MDX: fix meta file validation on Bun

This commit is contained in:
Fuma Nama
2025-11-14 22:15:43 +08:00
parent 7b450d60c6
commit 97722c6d52
6 changed files with 60 additions and 40 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-mdx': patch
---
fix meta file validation on Bun
+1
View File
@@ -50,6 +50,7 @@ export const blog = loader(
type: 'page',
data: post,
path: post.info.path,
absolutePath: post.info.fullPath,
})),
} as Source<{
metaData: MetaData;
+1 -1
View File
@@ -76,7 +76,7 @@ async function getFiles(source: AnySource) {
files.push({
data: page.data,
url: page.url,
path: page.absolutePath,
path: page.data.info.fullPath,
content: await page.data.getText('raw'),
});
}
+1 -1
View File
@@ -2,5 +2,5 @@ import { createMdxPlugin } from 'fumadocs-mdx/bun';
import { postInstall } from 'fumadocs-mdx/next';
const configPath = 'source.script.ts';
await postInstall(configPath);
await postInstall({ configPath });
Bun.plugin(createMdxPlugin({ configPath }));
+3 -3
View File
@@ -48,7 +48,7 @@ export interface Loader {
* 1. Bun doesn't allow `null` in loaders.
* 2. Bun requires sync result to support dynamic require().
*/
loadSync?: (source: string, input: LoaderInput) => Bun.OnLoadResult;
load?: (source: string, input: LoaderInput) => Awaitable<Bun.OnLoadResult>;
};
}
@@ -192,8 +192,8 @@ export function toBun(loader: Loader) {
},
};
if (loader.bun?.loadSync) {
return loader.bun.loadSync(readFileSync(filePath).toString(), input);
if (loader.bun?.load) {
return loader.bun.load(readFileSync(filePath).toString(), input);
}
const result = loader.load(input);
+49 -35
View File
@@ -1,4 +1,4 @@
import type { Loader } from '@/loaders/adapter';
import type { Loader, LoaderInput } from '@/loaders/adapter';
import type { ConfigLoader } from '@/loaders/config';
import { dump, load } from 'js-yaml';
import { validate } from '@/utils/validation';
@@ -35,29 +35,16 @@ export function createMetaLoader(
throw new Error('Unknown file type ' + filePath);
}
function stringifyOutput(filePath: string, data: unknown) {
if (filePath.endsWith('.json')) {
return resolveJson === 'json'
? JSON.stringify(data)
: `export default ${JSON.stringify(data)}`;
} else {
return resolveYaml === 'yaml'
? dump(data)
: `export default ${JSON.stringify(data)}`;
}
}
function onMeta(source: string, { filePath, query }: LoaderInput) {
const parsed = querySchema.safeParse(query);
if (!parsed.success || !parsed.data.collection) return null;
const collectionName = parsed.data.collection;
return {
test: metaLoaderGlob,
async load({ filePath, query, getSource }) {
const parsed = querySchema.parse(query);
if (!parsed.collection) return null;
return async (): Promise<unknown> => {
const config = await configLoader.getConfig();
const collection = config.getCollection(collectionName);
let metaCollection: MetaCollectionItem | undefined;
const collection = (await configLoader.getConfig()).getCollection(
parsed.collection,
);
let metaCollection: MetaCollectionItem;
switch (collection?.type) {
case 'meta':
metaCollection = collection;
@@ -65,14 +52,11 @@ export function createMetaLoader(
case 'docs':
metaCollection = collection.meta;
break;
default:
return null;
}
const source = await getSource();
let data = parse(filePath, source);
if (metaCollection.schema) {
data = await validate(
const data = parse(filePath, source);
if (metaCollection?.schema) {
return validate(
metaCollection.schema,
data,
{ path: filePath, source },
@@ -80,16 +64,46 @@ export function createMetaLoader(
);
}
return {
code: stringifyOutput(filePath, data),
};
return data;
};
}
return {
test: metaLoaderGlob,
async load(input) {
const result = onMeta(await input.getSource(), input);
if (result === null) return null;
const data = await result();
if (input.filePath.endsWith('.json')) {
return {
code:
resolveJson === 'json'
? JSON.stringify(data)
: `export default ${JSON.stringify(data)}`,
};
} else {
return {
code:
resolveYaml === 'yaml'
? dump(data)
: `export default ${JSON.stringify(data)}`,
};
}
},
bun: {
loadSync(source, { filePath }) {
return {
load(source, input) {
const result = onMeta(source, input);
if (result === null)
return {
loader: 'object',
exports: parse(input.filePath, source),
};
return result().then((data) => ({
loader: 'object',
exports: parse(filePath, source) as Record<string, unknown>,
};
exports: { default: data },
}));
},
},
};