Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@
"estree-util-value-to-estree": "^3.5.0",
"expressive-code": "^0.41.7",
"expressive-code-twoslash": "^0.4.0",
"gray-matter": "^4.0.3",
"hastscript": "^9.0.1",
"magic-string": "^0.30.21",
"mdast-util-find-and-replace": "^3.0.2",
Expand Down
77 changes: 0 additions & 77 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions src/config/frontmatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { readFileSync } from "node:fs";
import type { Literal } from "mdast";
import remarkFrontmatter from "remark-frontmatter";
import remarkParse from "remark-parse";
import { parse as parseToml } from "toml";
import { type PluggableList, unified } from "unified";
import { parse as parseYaml } from "yaml";

export type FrontmatterOptions = {
remarkPlugins?: PluggableList;
};

function getFrontmatterPlugins(plugins: PluggableList): PluggableList {
return plugins.flatMap((plugin): PluggableList => {
if (typeof plugin === "object" && !Array.isArray(plugin)) {
return getFrontmatterPlugins(plugin.plugins ?? []);
}
return (Array.isArray(plugin) ? plugin[0] : plugin) === remarkFrontmatter
? [plugin]
: [];
});
}

/**
* Extracts and parses the leading frontmatter block of a markdown source.
*
* Uses the MDX pipeline's YAML default and any configured `remark-frontmatter`
* overrides, including explicitly enabled TOML.
*/
export function parseFrontmatter(
source: string,
options: FrontmatterOptions = {},
): Record<string, unknown> {
const processor = unified()
.use(remarkParse)
.use(remarkFrontmatter)
.use(getFrontmatterPlugins(options.remarkPlugins ?? []));
const node = processor
.parse(source)
.children.find((child) => ["yaml", "toml"].includes(child.type)) as
| Literal
| undefined;
if (!node) return {};

const parse = node.type === "toml" ? parseToml : parseYaml;
const data = parse(node.value as string);
return data && typeof data === "object" && !Array.isArray(data)
? (data as Record<string, unknown>)
: {};
}

export function readFrontmatter(
filePath: string,
options?: FrontmatterOptions,
): Record<string, unknown> {
return parseFrontmatter(readFileSync(filePath, "utf8"), options);
}
2 changes: 1 addition & 1 deletion src/config/llms-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export async function getLlmDocuments(
): Promise<LlmDocument[]> {
if (!config.llms) return [];

const routes = await getRoutesIndex(root);
const routes = await getRoutesIndex(root, config.markdown);

return Promise.all(
routes.map(async (route): Promise<LlmDocument | null> => {
Expand Down
12 changes: 7 additions & 5 deletions src/config/routes-index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { readdir, readFile } from "node:fs/promises";
import { extname, join, relative } from "node:path";

import matter from "gray-matter";

import { type FrontmatterOptions, parseFrontmatter } from "./frontmatter.js";
import type { SolidBaseResolvedConfig } from "./index.js";
import {
buildSolidBaseRoutePath,
Expand Down Expand Up @@ -213,7 +212,10 @@ export function isDefaultLocaleRoute(
);
}

export async function getRoutesIndex(root: string): Promise<RouteIndexEntry[]> {
export async function getRoutesIndex(
root: string,
frontmatterOptions?: FrontmatterOptions,
): Promise<RouteIndexEntry[]> {
const routesDir = getRoutesDir(root);
const markdownFiles = await collectMarkdownFiles(routesDir);

Expand All @@ -222,15 +224,15 @@ export async function getRoutesIndex(root: string): Promise<RouteIndexEntry[]> {
if (isNotFoundRoute(routesDir, filePath)) return null;

const source = await readFile(filePath, "utf8");
const { data } = matter(source);
const frontmatter = parseFrontmatter(source, frontmatterOptions);
const routePath = toRoutePath(routesDir, filePath);

return {
filePath,
routePath,
markdownPath: toMarkdownPath(routePath),
source,
frontmatter: data as Record<string, unknown>,
frontmatter,
} satisfies RouteIndexEntry;
}),
).then((routes) => routes.filter((route) => route !== null));
Expand Down
11 changes: 8 additions & 3 deletions src/config/sidebar.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { lstatSync, readdirSync } from "node:fs";
import path from "node:path";
import matter from "gray-matter";

import { type FrontmatterOptions, readFrontmatter } from "./frontmatter.js";

export type SidebarConfig<Item = SidebarItem> =
| Item[]
Expand Down Expand Up @@ -28,6 +29,8 @@ export type SidebarItemWithMeta<T = {}> = SidebarItem<T> & {
};

export interface FilesystemSidebarOptions {
/** Match the site's markdown frontmatter configuration. */
frontmatter?: FrontmatterOptions;
filter?: (item: SidebarItemWithMeta) => boolean;
sort?: (a: SidebarItemWithMeta, b: SidebarItemWithMeta) => number;
transform?: (item: SidebarItemWithMeta) => SidebarItemWithMeta;
Expand All @@ -42,6 +45,7 @@ export function createFilesystemSidebar<Item = SidebarItem>(
const collator = new Intl.Collator(undefined, { numeric: true });

const resolvedOptions: Required<FilesystemSidebarOptions> = {
frontmatter: {},
filter: (item) => {
return (
item.matterData?.excludeFromSidebar !== true &&
Expand Down Expand Up @@ -101,7 +105,7 @@ function traverse(
if (title.includes("[...")) return;

if (lstatSync(filePath).isFile()) {
const matterData = getMatterData(filePath);
const matterData = getMatterData(filePath, options.frontmatter);

return {
title: matterData.sidebarTitle ?? matterData.title ?? title,
Expand Down Expand Up @@ -136,8 +140,9 @@ function stripExtension(filePath: string): string {

function getMatterData(
filePath: string,
options: FrontmatterOptions,
): { sidebarTitle?: string; title?: string } & {} {
return matter.read(filePath).data;
return readFrontmatter(filePath, options);
}

function formatTitle(filePath: string): string {
Expand Down
2 changes: 1 addition & 1 deletion src/config/sitemap-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,6 @@ export async function getSitemapEntries(
const hostname = getSitemapHostname(config);
if (!hostname) return [];

const routes = await getRoutesIndex(root);
const routes = await getRoutesIndex(root, config.markdown);
return buildSitemapEntries(hostname, config, routes);
}
Loading