perf(mdx): require collection query param at regex matching

This commit is contained in:
Fuma Nama
2026-06-27 23:03:58 +08:00
parent 593302d01c
commit baacf1a67d
11 changed files with 39 additions and 34 deletions
+8
View File
@@ -0,0 +1,8 @@
---
packages:
npm:fumadocs-mdx: patch
---
## Require `collection` query param at regex matching
Instead of passing through all JSON/YAML files, the meta loader now requires `collection` query param to be triggered.
+3 -2
View File
@@ -6,6 +6,7 @@ import { _Defaults, type CoreOptions, createCore } from '@/core';
import { createIntegratedConfigLoader } from '@/loaders/config';
import { createMetaLoader } from '@/loaders/meta';
import { toBun } from '@/loaders/adapter';
import { mdxLoaderGlob, metaLoaderGlob } from '@/loaders';
export interface MdxPluginOptions extends Partial<CoreOptions> {
/**
@@ -37,8 +38,8 @@ export function createMdxPlugin(options: MdxPluginOptions = {}): BunPlugin {
});
const configLoader = createIntegratedConfigLoader(core);
toBun(createMdxLoader(configLoader))(build);
if (!disableMetaFile) toBun(createMetaLoader(configLoader))(build);
toBun(mdxLoaderGlob, createMdxLoader(configLoader))(build);
if (!disableMetaFile) toBun(metaLoaderGlob, createMetaLoader(configLoader))(build);
},
};
}
+5 -17
View File
@@ -2,7 +2,7 @@ import type { CompilerOptions } from '@/loaders/mdx/build-mdx';
import type { LoadFnOutput, LoadHook } from 'node:module';
import { fileURLToPath } from 'node:url';
import fs from 'node:fs/promises';
import type { HookFilter, TransformPluginContext } from 'rolldown';
import type { TransformPluginContext } from 'rolldown';
import type { Environment, TransformResult } from 'vite';
import { parse } from 'node:querystring';
import { ValidationError } from '@/utils/validation';
@@ -33,13 +33,6 @@ export interface LoaderOutput {
type Awaitable<T> = T | Promise<T>;
export interface Loader {
/**
* Filter file paths, the input can be either a file URL or file path.
*
* Must take resource query into consideration.
*/
test?: RegExp;
/**
* Transform input into JavaScript.
*
@@ -58,9 +51,9 @@ export interface Loader {
};
}
export function toNode(loader: Loader): LoadHook {
export function toNode(test: RegExp | undefined, loader: Loader): LoadHook {
return async (url, _context, nextLoad): Promise<LoadFnOutput> => {
if (url.startsWith('file:///') && (!loader.test || loader.test.test(url))) {
if (url.startsWith('file:///') && (!test || test.test(url))) {
const parsedUrl = new URL(url);
const filePath = fileURLToPath(parsedUrl);
@@ -90,8 +83,6 @@ export function toNode(loader: Loader): LoadHook {
}
export interface ViteLoader {
filter: HookFilter;
transform: (
this: TransformPluginContext & { environment?: Environment },
value: string,
@@ -101,9 +92,6 @@ export interface ViteLoader {
export function toVite(loader: Loader): ViteLoader {
return {
filter: {
id: loader.test,
},
async transform(value, id) {
const [file, query = ''] = id.split('?', 2);
@@ -182,7 +170,7 @@ export function toWebpack(loader: Loader): WebpackLoader {
};
}
export function toBun(loader: Loader) {
export function toBun(test: RegExp | undefined, loader: Loader) {
function toResult(output: LoaderOutput | null): Bun.OnLoadResult {
// it errors, treat this as an exception
if (!output) return;
@@ -195,7 +183,7 @@ export function toBun(loader: Loader) {
return (build: Bun.PluginBuilder) => {
// avoid using async here, because it will cause dynamic require() to fail
build.onLoad({ filter: loader.test ?? /.+/ }, (args) => {
build.onLoad({ filter: test ?? /.+/ }, (args) => {
const [filePath, query = ''] = args.path.split('?', 2);
const input: LoaderInput = {
async getSource() {
+3 -1
View File
@@ -1,2 +1,4 @@
export const metaLoaderGlob = /\.(json|yaml)(\?.+?)?$/;
export const metaLoaderGlob = /\.(json|yaml)\?.*collection=/;
export const metaLoaderFileGlob = /\.(json|yaml)$/;
export const metaLoaderQueryGlob = /[?&]collection=/;
export const mdxLoaderGlob = /\.mdx?(\?.+?)?$/;
-2
View File
@@ -6,7 +6,6 @@ import fs from 'node:fs/promises';
import path from 'node:path';
import { createHash } from 'node:crypto';
import type { ConfigLoader } from '@/loaders/config';
import { mdxLoaderGlob } from '..';
const querySchema = z.looseObject({
only: z.literal(['frontmatter', 'all']).default('all'),
@@ -24,7 +23,6 @@ type CacheEntry = z.infer<typeof cacheEntry>;
export function createMdxLoader({ getCore }: ConfigLoader): Loader {
return {
test: mdxLoaderGlob,
async load({ getSource, development: isDevelopment, query, compiler, filePath }) {
let core = await getCore();
const value = await getSource();
-2
View File
@@ -2,7 +2,6 @@ import type { Loader, LoaderInput } from '@/loaders/adapter';
import type { ConfigLoader } from '@/loaders/config';
import { load } from 'js-yaml';
import { z } from 'zod';
import { metaLoaderGlob } from '.';
import type { MetaCollectionItem } from '@/config/build';
const querySchema = z.looseObject({
@@ -71,7 +70,6 @@ export function createMetaLoader(
}
return {
test: metaLoaderGlob,
async load(input) {
const result = onMeta(await input.getSource(), input);
if (result === null) return null;
+9 -2
View File
@@ -5,7 +5,7 @@ import type { TurbopackLoaderOptions, TurbopackOptions } from 'next/dist/server/
import * as path from 'node:path';
import { loadConfig } from '@/config/load-from-file';
import { _Defaults, type Core, createCore } from '@/core';
import { mdxLoaderGlob, metaLoaderGlob } from '@/loaders';
import { mdxLoaderGlob, metaLoaderFileGlob, metaLoaderQueryGlob } from '@/loaders';
import type { IndexFilePluginOptions } from '@/plugins/index-file';
import indexFile from '@/plugins/index-file';
@@ -60,6 +60,9 @@ export function createMDX(createOptions: CreateMDXOptions = {}) {
as: '*.js',
},
'*.json': {
condition: {
query: metaLoaderQueryGlob,
},
loaders: [
{
loader: 'fumadocs-mdx/webpack/meta',
@@ -69,6 +72,9 @@ export function createMDX(createOptions: CreateMDXOptions = {}) {
as: '*.json',
},
'*.yaml': {
condition: {
query: metaLoaderQueryGlob,
},
loaders: [
{
loader: 'fumadocs-mdx/webpack/meta',
@@ -102,7 +108,8 @@ export function createMDX(createOptions: CreateMDXOptions = {}) {
],
},
{
test: metaLoaderGlob,
test: metaLoaderFileGlob,
resourceQuery: metaLoaderQueryGlob,
enforce: 'pre',
use: [
{
+3 -2
View File
@@ -5,6 +5,7 @@ import { createStandaloneConfigLoader } from '@/loaders/config';
import { toNode } from '@/loaders/adapter';
import { createMdxLoader } from '@/loaders/mdx';
import { createMetaLoader } from '@/loaders/meta';
import { mdxLoaderGlob, metaLoaderGlob } from '@/loaders';
let cachedLoaders: LoadHook[] | undefined;
@@ -22,9 +23,9 @@ export const initialize: InitializeHook<NodeLoaderOptions> = (options) => {
mode: 'production',
});
cachedLoaders = [toNode(createMdxLoader(configLoader))];
cachedLoaders = [toNode(mdxLoaderGlob, createMdxLoader(configLoader))];
if (!options.disableMetaFile) {
cachedLoaders.push(toNode(createMetaLoader(configLoader)));
cachedLoaders.push(toNode(metaLoaderGlob, createMetaLoader(configLoader)));
}
};
+3 -2
View File
@@ -4,6 +4,7 @@ import { toNode } from '@/loaders/adapter';
import { createStandaloneConfigLoader } from '@/loaders/config';
import type { LoadHook } from 'node:module';
import { createMetaLoader } from '@/loaders/meta';
import { mdxLoaderGlob, metaLoaderGlob } from '@/loaders';
const core = createCore({
environment: 'node',
@@ -17,8 +18,8 @@ const configLoader = createStandaloneConfigLoader({
mode: 'production',
});
const mdxLoader = toNode(createMdxLoader(configLoader));
const metaLoader = toNode(createMetaLoader(configLoader));
const mdxLoader = toNode(mdxLoaderGlob, createMdxLoader(configLoader));
const metaLoader = toNode(metaLoaderGlob, createMetaLoader(configLoader));
/**
* @deprecated use the `register()` function from `fumadocs-mdx/node` instead.
+2 -2
View File
@@ -52,14 +52,14 @@ export default async function mdx(
{
name: 'fumadocs-mdx:mdx',
transform: {
filter: mdxLoader.filter,
filter: { id: mdxLoaderGlob },
handler: mdxLoader.transform,
},
},
{
name: 'fumadocs-mdx:meta',
transform: {
filter: metaLoader.filter,
filter: { id: metaLoaderGlob },
handler: metaLoader.transform,
},
},
+3 -2
View File
@@ -8,6 +8,7 @@ import { createIntegratedConfigLoader } from '@/loaders/config';
import { createMetaLoader } from '@/loaders/meta';
import indexFile, { IndexFilePluginOptions } from '@/plugins/index-file';
import path from 'node:path';
import { mdxLoaderGlob, metaLoaderGlob } from '@/loaders';
export interface PluginOptions {
/**
@@ -77,7 +78,7 @@ export default function mdx(
);
mdxPlugin.transform = {
filter: mdxLoader.filter,
filter: { id: mdxLoaderGlob },
order: 'pre',
handler(code, id) {
// Vite RSC will pass the compiled MDX file's client module with ID `virtual:vite-rsc/client-references/group/facade:xxx.mdx`.
@@ -89,7 +90,7 @@ export default function mdx(
},
};
metaPlugin.transform = {
filter: metaLoader.filter,
filter: { id: metaLoaderGlob },
order: 'pre',
handler(code, id) {
if (!forcedConfig) this.addWatchFile(options.configPath);