MDX: Stabilize Vite plugin support

This commit is contained in:
Fuma Nama
2025-07-19 19:18:46 +08:00
parent ac33c3c877
commit e5cfa27e5b
23 changed files with 417 additions and 301 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-mdx': minor
---
Stabilize Vite plugin support
+30 -23
View File
@@ -7,36 +7,49 @@ import {
DocsTitle,
} from 'fumadocs-ui/page';
import { source } from '@/source';
import { type PageTree } from 'fumadocs-core/server';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import { executeMdxSync } from '@fumadocs/mdx-remote/client';
import type { PageTree } from 'fumadocs-core/server';
import { createCompiler } from '@fumadocs/mdx-remote';
import * as path from 'node:path';
const compiler = createCompiler({
development: false,
});
import { docs } from '../../content/docs';
import { toClientRenderer } from 'fumadocs-mdx/runtime/vite';
export async function loader({ params }: Route.LoaderArgs) {
const slugs = params['*'].split('/').filter((v) => v.length > 0);
const page = source.getPage(slugs);
if (!page) throw new Response('Not found', { status: 404 });
const compiled = await compiler.compileFile({
path: path.resolve('content/docs', page.path),
value: page.data.content,
});
return {
page,
compiled: compiled.toString(),
tree: source.pageTree,
};
}
const clientLoader = toClientRenderer(
docs.doc,
(
{ toc, default: Mdx },
{
description,
title,
}: {
title?: string;
description?: string;
},
) => {
return (
<DocsPage toc={toc}>
<DocsTitle>{title}</DocsTitle>
<DocsDescription>{description}</DocsDescription>
<DocsBody>
<Mdx components={{ ...defaultMdxComponents }} />
</DocsBody>
</DocsPage>
);
},
);
export default function Page(props: Route.ComponentProps) {
const { page, compiled, tree } = props.loaderData;
const { default: Mdx, toc } = executeMdxSync(compiled);
const { tree, page } = props.loaderData;
const Content = clientLoader[page.path];
return (
<DocsLayout
@@ -47,13 +60,7 @@ export default function Page(props: Route.ComponentProps) {
>
<title>{page.data.title}</title>
<meta name="description" content={page.data.description} />
<DocsPage toc={toc}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<Mdx components={defaultMdxComponents} />
</DocsBody>
</DocsPage>
<Content {...page.data} />
</DocsLayout>
);
}
+3 -10
View File
@@ -1,16 +1,9 @@
import type { Route } from './+types/search';
import { createSearchAPI } from 'fumadocs-core/search/server';
import { createFromSource } from 'fumadocs-core/search/server';
import { source } from '@/source';
import { structure } from 'fumadocs-core/mdx-plugins';
const server = createSearchAPI('advanced', {
indexes: source.getPages().map((page) => ({
id: page.url,
url: page.url,
title: page.data.title ?? '',
description: page.data.description,
structuredData: structure(page.data.content),
})),
const server = createFromSource(source, {
language: 'english',
});
export async function loader({ request }: Route.LoaderArgs) {
+3 -56
View File
@@ -1,60 +1,7 @@
import {
loader,
type MetaData,
type PageData,
type Source,
type VirtualFile,
} from 'fumadocs-core/source';
import matter from 'gray-matter';
import * as path from 'node:path';
const files = Object.entries(
import.meta.glob<true, 'raw'>('/content/docs/**/*', {
eager: true,
query: '?raw',
import: 'default',
}),
);
const virtualFiles: VirtualFile[] = files.flatMap(([file, content]) => {
const ext = path.extname(file);
const virtualPath = path.relative(
'content/docs',
path.join(process.cwd(), file),
);
if (ext === '.mdx' || ext === '.md') {
const parsed = matter(content);
return {
type: 'page',
path: virtualPath,
data: {
...parsed.data,
content: parsed.content,
},
};
}
if (ext === '.json') {
return {
type: 'meta',
path: virtualPath,
data: JSON.parse(content),
};
}
return [];
});
import { loader } from 'fumadocs-core/source';
import { create, docs } from '../content/docs';
export const source = loader({
source: {
files: virtualFiles,
} as Source<{
pageData: PageData & {
content: string;
};
metaData: MetaData;
}>,
source: await create.sourceAsync(docs.doc, docs.meta),
baseUrl: '/docs',
});
@@ -0,0 +1,19 @@
import { fromConfig } from 'fumadocs-mdx/runtime/vite';
import type * as Config from '../../source.config';
export const create = fromConfig<typeof Config>();
export const docs = create.docs('docs', {
doc: import.meta.glob('/content/docs/**/*.mdx', {
query: {
collection: 'docs',
},
base: '/content/docs',
}),
meta: import.meta.glob('/content/docs/**/*.json', {
query: {
collection: 'docs',
},
base: '/content/docs',
}),
});
+2 -4
View File
@@ -9,17 +9,15 @@
"typecheck": "react-router typegen && tsc"
},
"dependencies": {
"@fumadocs/mdx-remote": "workspace:*",
"@react-router/node": "^7.6.3",
"@react-router/serve": "^7.6.3",
"fumadocs-core": "workspace:*",
"fumadocs-mdx": "workspace:*",
"fumadocs-ui": "workspace:*",
"gray-matter": "^4.0.3",
"isbot": "^5.1.28",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router": "^7.6.3",
"shiki": "^3.8.0"
"react-router": "^7.6.3"
},
"devDependencies": {
"@react-router/dev": "^7.6.3",
@@ -1,9 +1,5 @@
import type { Config } from '@react-router/dev/config';
import { source } from './app/source';
export default {
ssr: true,
async prerender({ getStaticPaths }) {
return [...getStaticPaths(), ...source.getPages().map((page) => page.url)];
},
} satisfies Config;
+7
View File
@@ -0,0 +1,7 @@
import { defineConfig, defineDocs } from 'fumadocs-mdx/config';
export const docs = defineDocs({
dir: 'content/docs',
});
export default defineConfig();
+3 -6
View File
@@ -2,12 +2,9 @@ import { reactRouter } from '@react-router/dev/vite';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import mdx from 'fumadocs-mdx/vite';
import * as MdxConfig from './source.config';
export default defineConfig({
build: {
rollupOptions: {
external: ['shiki'],
},
},
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
plugins: [mdx(MdxConfig), tailwindcss(), reactRouter(), tsconfigPaths()],
});
+9
View File
@@ -32,6 +32,10 @@
"import": "./dist/runtime/async.js",
"types": "./dist/runtime/async.d.ts"
},
"./runtime/vite": {
"import": "./dist/runtime/vite.js",
"types": "./dist/runtime/vite.d.ts"
},
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -68,6 +72,7 @@
"zod": "^4.0.5"
},
"devDependencies": {
"react": "^19.1.0",
"@fumadocs/mdx-remote": "workspace:*",
"@types/js-yaml": "^4.0.9",
"@types/mdast": "^4.0.3",
@@ -84,12 +89,16 @@
"webpack": "^5.100.1"
},
"peerDependencies": {
"react": "*",
"@fumadocs/mdx-remote": "^1.2.0",
"fumadocs-core": "^14.0.0 || ^15.0.0",
"next": "^15.3.0",
"vite": "6.x.x"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"next": {
"optional": true
},
+2 -2
View File
@@ -1,8 +1,8 @@
import type { GlobalConfig } from '@/config/types';
import type { LoadedConfig } from '@/utils/config';
import type {
DocsCollection,
DocCollection,
DocsCollection,
GlobalConfig,
MetaCollection,
} from '@/config/define';
+35 -50
View File
@@ -1,7 +1,7 @@
import { type MDXOptions } from '@/utils/build-mdx';
import { type GlobalConfig } from '@/config/types';
import { frontmatterSchema, metaSchema } from '@/utils/schema';
import type { StandardSchemaV1 } from '@standard-schema/spec';
import type { DefaultMDXOptions } from '@/utils/mdx-options';
export type CollectionSchema<Schema extends StandardSchemaV1, Context> =
| Schema
@@ -51,68 +51,53 @@ export interface DocsCollection<
Async extends boolean = boolean,
> {
type: 'docs';
/**
* The directory to scan files
*
* @defaultValue 'content/docs'
*/
dir: string | string[];
docs: DocCollection<DocSchema, Async>;
meta: MetaCollection<MetaSchema>;
}
export function defineCollections<
T extends 'doc' | 'meta',
Schema extends StandardSchemaV1 = StandardSchemaV1<unknown, any>,
Async extends boolean = false,
>(
options: { type: T } & (T extends 'doc'
? DocCollection<Schema, Async>
: MetaCollection<Schema>),
): {
type: T;
export interface GlobalConfig {
/**
* Configure global MDX options
*/
mdxOptions?:
| DefaultMDXOptions
| (() => DefaultMDXOptions | Promise<DefaultMDXOptions>);
_type: {
async: Async;
schema: Schema;
};
} {
return {
// @ts-expect-error -- internal type inferring
_type: undefined,
...options,
};
/**
* Fetch last modified time with specified version control
* @defaultValue 'none'
*/
lastModifiedTime?: 'git' | 'none';
}
export function defineCollections<
Schema extends StandardSchemaV1 = StandardSchemaV1,
Async extends boolean = false,
>(options: DocCollection<Schema, Async>): DocCollection<Schema, Async>;
export function defineCollections<
Schema extends StandardSchemaV1 = StandardSchemaV1,
>(options: MetaCollection<Schema>): MetaCollection<Schema>;
export function defineCollections(
options: DocCollection | MetaCollection,
): DocCollection | MetaCollection {
return options as any;
}
export function defineDocs<
DocSchema extends StandardSchemaV1 = typeof frontmatterSchema,
MetaSchema extends StandardSchemaV1 = typeof metaSchema,
Async extends boolean = false,
>(options?: {
/**
* The directory to scan files
*
* @defaultValue 'content/docs'
*/
dir?: string | string[];
docs?: Omit<DocCollection<DocSchema, Async>, 'dir' | 'type'>;
meta?: Omit<MetaCollection<MetaSchema>, 'dir' | 'type'>;
}): {
type: 'docs';
docs: {
type: 'doc';
_type: {
schema: DocSchema;
async: Async;
};
};
meta: {
type: 'meta';
_type: {
schema: MetaSchema;
async: false;
};
};
} {
>(
options?: Partial<DocsCollection<DocSchema, MetaSchema, Async>>,
): DocsCollection<DocSchema, MetaSchema, Async> {
if (!options)
console.warn(
'[`source.config.ts`] Deprecated: please pass options to `defineDocs()` and specify a `dir`.',
-1
View File
@@ -1,4 +1,3 @@
export * from './types';
export * from './define';
export {
loadDefaultOptions,
-44
View File
@@ -1,44 +0,0 @@
import type { MDXProps } from 'mdx/types';
import type { StructuredData } from 'fumadocs-core/mdx-plugins';
import type { TableOfContents } from 'fumadocs-core/server';
import { type DefaultMDXOptions } from '@/utils/mdx-options';
import type { FC } from 'react';
export interface GlobalConfig {
/**
* Configure global MDX options
*/
mdxOptions?:
| DefaultMDXOptions
| (() => DefaultMDXOptions | Promise<DefaultMDXOptions>);
/**
* Fetch last modified time with specified version control
* @defaultValue 'none'
*/
lastModifiedTime?: 'git' | 'none';
}
export interface FileInfo {
path: string;
absolutePath: string;
}
export interface MarkdownProps {
body: FC<MDXProps>;
structuredData: StructuredData;
toc: TableOfContents;
_exports: Record<string, unknown>;
/**
* Only available when `lastModifiedTime` is enabled on MDX loader
*/
lastModified?: Date;
}
export interface BaseCollectionEntry {
/**
* Raw file path of collection entry, including absolute path (not normalized).
*/
_file: FileInfo;
}
+1 -2
View File
@@ -2,12 +2,11 @@ import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import { glob } from 'tinyglobby';
import { getTypeFromPath } from '@/utils/get-type-from-path';
import type { FileInfo } from '@/config/types';
import type { LoadedConfig } from '@/utils/config';
import type { DocCollection, MetaCollection } from '@/config';
import { validate } from '@/utils/schema';
import { fileCache } from '@/map/file-cache';
import type { AsyncRuntimeFile } from '@/runtime/types';
import type { AsyncRuntimeFile, FileInfo } from '@/runtime/types';
import { load } from 'js-yaml';
import { getGitTimestamp } from '@/utils/git-timestamp';
import { fumaMatter } from '@/utils/fuma-matter';
+1 -2
View File
@@ -4,8 +4,7 @@ import {
type PageData,
type Source,
} from 'fumadocs-core/source';
import { type BaseCollectionEntry, type FileInfo } from '@/config';
import type { Runtime } from '@/runtime/types';
import type { BaseCollectionEntry, FileInfo, Runtime } from '@/runtime/types';
import fs from 'node:fs';
const cache = new Map<string, string>();
+38 -58
View File
@@ -1,7 +1,35 @@
import type { BaseCollectionEntry, FileInfo, MarkdownProps } from '@/config';
import type { StandardSchemaV1 } from '@standard-schema/spec';
import type { MetaData, PageData, Source } from 'fumadocs-core/source';
import type { LoadedConfig } from '@/utils/config';
import type { FC } from 'react';
import type { MDXProps } from 'mdx/types';
import type { StructuredData } from 'fumadocs-core/mdx-plugins';
import type { TableOfContents } from 'fumadocs-core/server';
import type { DocCollection, DocsCollection, MetaCollection } from '@/config';
export interface BaseCollectionEntry {
/**
* Raw file path of collection entry, including absolute path (not normalized).
*/
_file: FileInfo;
}
export interface FileInfo {
path: string;
absolutePath: string;
}
export interface MarkdownProps {
body: FC<MDXProps>;
structuredData: StructuredData;
toc: TableOfContents;
_exports: Record<string, unknown>;
/**
* Only available when `lastModifiedTime` is enabled on MDX loader
*/
lastModified?: Date;
}
export interface RuntimeFile {
info: FileInfo;
@@ -31,43 +59,16 @@ type MetaOut<Schema extends StandardSchemaV1> =
StandardSchemaV1.InferOutput<Schema> & BaseCollectionEntry;
export interface Runtime {
doc: <C>(files: RuntimeFile[]) => C extends {
type: 'doc';
_type: {
schema: infer Schema extends StandardSchemaV1;
};
}
? DocOut<Schema>[]
: never;
meta: <C>(files: RuntimeFile[]) => C extends {
type: 'meta';
_type: {
schema: infer Schema extends StandardSchemaV1;
};
}
? MetaOut<Schema>[]
: never;
doc: <C>(
files: RuntimeFile[],
) => C extends DocCollection<infer Schema, false> ? DocOut<Schema>[] : never;
meta: <C>(
files: RuntimeFile[],
) => C extends MetaCollection<infer Schema> ? MetaOut<Schema>[] : never;
docs: <C>(
docs: RuntimeFile[],
metas: RuntimeFile[],
) => C extends {
type: 'docs';
docs: {
type: 'doc';
_type: {
schema: infer DocSchema extends StandardSchemaV1;
};
};
meta: {
type: 'meta';
_type: {
schema: infer MetaSchema extends StandardSchemaV1;
};
};
}
) => C extends DocsCollection<infer DocSchema, infer MetaSchema, false>
? {
docs: DocOut<DocSchema>[];
meta: MetaOut<MetaSchema>[];
@@ -96,12 +97,7 @@ export interface RuntimeAsync {
files: AsyncRuntimeFile[],
collection: string,
config: LoadedConfig,
) => C extends {
type: 'doc';
_type: {
schema: infer Schema extends StandardSchemaV1;
};
}
) => C extends DocCollection<infer Schema, true>
? AsyncDocOut<Schema>[]
: never;
docs: <C>(
@@ -109,23 +105,7 @@ export interface RuntimeAsync {
metas: RuntimeFile[],
collection: string,
config: LoadedConfig,
) => C extends {
type: 'docs';
docs: {
type: 'doc';
_type: {
schema: infer DocSchema extends StandardSchemaV1;
};
};
meta: {
type: 'meta';
_type: {
schema: infer MetaSchema extends StandardSchemaV1;
};
};
}
) => C extends DocsCollection<infer DocSchema, infer MetaSchema, true>
? {
docs: AsyncDocOut<DocSchema>[];
meta: MetaOut<MetaSchema>[];
+198
View File
@@ -0,0 +1,198 @@
import type { TableOfContents } from 'fumadocs-core/server';
import { type FC, lazy, type LazyExoticComponent, type ReactNode } from 'react';
import type { MDXProps } from 'mdx/types';
import type { StructuredData } from 'fumadocs-core/mdx-plugins';
import type { DocCollection, DocsCollection, MetaCollection } from '@/config';
import type { StandardSchemaV1 } from '@standard-schema/spec';
import type {
MetaData,
PageData,
Source,
VirtualFile,
} from 'fumadocs-core/source';
export type CompiledMDXFile<Frontmatter> = {
frontmatter: Frontmatter;
toc: TableOfContents;
default: FC<MDXProps>;
structuredData: StructuredData;
} & Record<string, unknown>;
type MDXFileToPageData<Frontmatter> = Frontmatter & {
toc: TableOfContents;
default: FC<MDXProps>;
structuredData: StructuredData;
_exports: Record<string, unknown>;
};
type AttachGlobValue<GlobValue, Attach> =
GlobValue extends () => Promise<unknown> ? () => Promise<Attach> : Attach;
export function fromConfig<Config>(): {
doc: <Name extends keyof Config, GlobValue>(
name: Name,
glob: Record<string, GlobValue>,
) => Config[Name] extends DocCollection<infer Schema>
? Record<
string,
AttachGlobValue<
GlobValue,
CompiledMDXFile<StandardSchemaV1.InferOutput<Schema>>
>
>
: never;
meta: <Name extends keyof Config, GlobValue>(
name: Name,
glob: Record<string, GlobValue>,
) => Config[Name] extends MetaCollection<infer Schema>
? AttachGlobValue<GlobValue, StandardSchemaV1.InferOutput<Schema>>
: never;
docs: <Name extends keyof Config, DocGlobValue, MetaGlobValue>(
name: Name,
options: {
meta: Record<string, MetaGlobValue>;
doc: Record<string, DocGlobValue>;
},
) => Config[Name] extends DocsCollection<infer DocSchema, infer MetaSchema>
? {
doc: Record<
string,
AttachGlobValue<
DocGlobValue,
CompiledMDXFile<StandardSchemaV1.InferOutput<DocSchema>>
>
>;
meta: Record<
string,
AttachGlobValue<
MetaGlobValue,
StandardSchemaV1.InferOutput<MetaSchema>
>
>;
}
: never;
source: <DocOut extends PageData, MetaOut extends MetaData>(
doc: Record<string, CompiledMDXFile<DocOut>>,
meta: Record<string, MetaOut>,
) => Source<{
pageData: MDXFileToPageData<DocOut>;
metaData: MetaOut;
}>;
sourceAsync: <DocOut extends PageData, MetaOut extends MetaData>(
doc: Record<string, () => Promise<CompiledMDXFile<DocOut>>>,
meta: Record<string, () => Promise<MetaOut>>,
) => Promise<
Source<{
pageData: MDXFileToPageData<DocOut>;
metaData: MetaOut;
}>
>;
} {
function normalize(entries: Record<string, unknown>) {
const out: Record<string, unknown> = {};
for (const k in entries) {
const mappedK = k.startsWith('./') ? k.slice(2) : k;
out[mappedK] = entries[k];
}
return out;
}
function mapPageData<Frontmatter>(
entry: CompiledMDXFile<Frontmatter>,
): MDXFileToPageData<Frontmatter> {
const { toc, structuredData } = entry;
return {
...entry.frontmatter,
default: entry.default,
toc,
structuredData,
_exports: entry,
};
}
return {
doc(_, glob) {
return normalize(glob) as any;
},
meta(_, glob) {
return normalize(glob) as any;
},
docs(_, { doc, meta }) {
return {
doc: normalize(doc),
meta: normalize(meta),
} as any;
},
source(doc, meta) {
const virtualFiles: VirtualFile[] = [];
for (const [file, content] of Object.entries(doc)) {
virtualFiles.push({
type: 'page',
path: file,
data: mapPageData(content),
});
}
for (const [file, content] of Object.entries(meta)) {
virtualFiles.push({
type: 'meta',
path: file,
data: content,
});
}
return {
files: virtualFiles,
};
},
async sourceAsync(doc, meta) {
const virtualFiles: VirtualFile[] = [];
for (const [file, content] of Object.entries(doc)) {
virtualFiles.push({
type: 'page',
path: file,
data: mapPageData(await content()),
});
}
for (const [file, content] of Object.entries(meta)) {
virtualFiles.push({
type: 'meta',
path: file,
data: await content(),
});
}
return {
files: virtualFiles,
};
},
};
}
type ClientLoader<Props> = Record<
string,
LazyExoticComponent<(props: Props) => ReactNode>
>;
export function toClientRenderer<Frontmatter, Props extends object = object>(
files: Record<string, () => Promise<CompiledMDXFile<Frontmatter>>>,
renderer: (loaded: CompiledMDXFile<Frontmatter>, props: Props) => ReactNode,
): ClientLoader<Props> {
const loader: ClientLoader<Props> = {};
for (const k in files) {
loader[k] = lazy(async () => {
const loaded = await files[k]();
return { default: (props) => renderer(loaded, props) };
});
}
return loader;
}
+1 -1
View File
@@ -3,9 +3,9 @@ import * as path from 'node:path';
import type {
DocCollection,
DocsCollection,
GlobalConfig,
MetaCollection,
} from '@/config/define';
import { type GlobalConfig } from '@/config/types';
import type { ProcessorOptions } from '@mdx-js/mdx';
import { pathToFileURL } from 'node:url';
import { buildConfig } from '@/config/build';
+49 -24
View File
@@ -4,18 +4,14 @@ import { buildMDX } from '@/utils/build-mdx';
import { parse } from 'node:querystring';
import { countLines } from '@/utils/count-lines';
import { fumaMatter } from '@/utils/fuma-matter';
import { loadDefaultOptions } from '@/utils/mdx-options';
export interface CreateMDXOptions {
configPath?: string;
}
import { validate, ValidationError } from '@/utils/schema';
import { z } from 'zod';
const fileRegex = /\.(md|mdx)$/;
export default function unstable_mdx(
config: Record<string, unknown>,
_options: CreateMDXOptions = {},
): Plugin {
const onlySchema = z.literal(['frontmatter', 'all']);
export default function mdx(config: Record<string, unknown>): Plugin {
const [err, loaded] = buildConfig(config);
if (err || !loaded) {
throw new Error(err);
@@ -23,6 +19,8 @@ export default function unstable_mdx(
return {
name: 'fumadocs-mdx',
// needed, otherwise other plugins will be executed before our `transform`.
enforce: 'pre',
// TODO: need a way to generate .source folder that works for non-RSC based frameworks, currently, we need to dynamic import MDX files using `import.meta.glob`.
// at the moment, RR and Tanstack Start has no stable support for RSC yet.
@@ -32,35 +30,64 @@ export default function unstable_mdx(
const matter = fumaMatter(value);
const isDevelopment = this.environment.mode === 'dev';
const { collection: collectionId, raw } = parse(query) as {
const parsed = parse(query) as {
collection?: string;
raw?: string;
only?: string;
};
const collection = collectionId
? loaded.collections.get(collectionId)
const collection = parsed.collection
? loaded.collections.get(parsed.collection)
: undefined;
const only = parsed.only ? onlySchema.parse(parsed.only) : 'all';
// ensure the line number is correct in dev mode
const lineOffset = '\n'.repeat(
isDevelopment ? countLines(matter.matter) : 0,
);
let schema;
let mdxOptions;
switch (collection?.type) {
case 'doc':
mdxOptions = collection.mdxOptions;
schema = collection.schema;
break;
case 'docs':
mdxOptions = collection.docs.mdxOptions;
schema = collection.docs.schema;
break;
}
if (schema) {
try {
matter.data = await validate(
schema,
matter.data,
{
source: value,
path,
},
`invalid frontmatter in ${path}`,
);
} catch (e) {
if (e instanceof ValidationError) {
throw new Error(e.toStringFormatted());
}
throw e;
}
}
if (only === 'frontmatter') {
return {
code: `export const frontmatter = ${JSON.stringify(matter.data)}`,
};
}
const { loadDefaultOptions } = await import('@/utils/mdx-options');
mdxOptions ??= await loadDefaultOptions(loaded);
// ensure the line number is correct in dev mode
const lineOffset = isDevelopment ? countLines(matter.matter) : 0;
const file = await buildMDX(
collectionId ?? 'global',
lineOffset + matter.content,
parsed.collection ?? 'global',
'\n'.repeat(lineOffset) + matter.content,
{
development: isDevelopment,
...mdxOptions,
@@ -75,10 +102,8 @@ export default function unstable_mdx(
);
return {
code:
typeof raw === 'string'
? `export default ${JSON.stringify(file.value)}`
: String(file.value),
code: String(file.value),
map: file.map,
};
},
};
+1 -1
View File
@@ -63,10 +63,10 @@ for (const { name, collection } of cases) {
const out = await generateJS(
path.join(file, './fixtures/config.ts'),
{
// @ts-expect-error -- test file
_runtime: {
files: new Map(),
},
// @ts-expect-error -- test file
collections: new Map([['docs', collection]]),
},
{
+1 -1
View File
@@ -4,7 +4,7 @@ export default defineConfig({
entry: [
'./src/{index,loader-mdx}.ts',
'./src/{config,next,vite}/index.ts',
'./src/runtime/async.ts',
'./src/runtime/{async,vite}.ts',
],
format: ['esm', 'cjs'],
external: ['next', 'typescript'],
+9 -12
View File
@@ -279,7 +279,7 @@ importers:
version: 0.2.2(@content-collections/[email protected]([email protected]))([email protected])([email protected]([email protected]))([email protected])
'@content-collections/next':
specifier: ^0.2.6
version: 0.2.6(@content-collections/[email protected]([email protected]))([email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))
version: 0.2.6(@content-collections/[email protected]([email protected]))([email protected](@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected]))
'@types/mdx':
specifier: ^2.0.13
version: 2.0.13
@@ -476,9 +476,6 @@ importers:
examples/react-router:
dependencies:
'@fumadocs/mdx-remote':
specifier: workspace:*
version: link:../../packages/mdx-remote
'@react-router/node':
specifier: ^7.6.3
version: 7.6.3([email protected]([email protected]([email protected]))([email protected]))([email protected])
@@ -488,12 +485,12 @@ importers:
fumadocs-core:
specifier: workspace:*
version: link:../../packages/core
fumadocs-mdx:
specifier: workspace:*
version: link:../../packages/mdx
fumadocs-ui:
specifier: workspace:*
version: link:../../packages/ui
gray-matter:
specifier: ^4.0.3
version: 4.0.3
isbot:
specifier: ^5.1.28
version: 5.1.28
@@ -506,9 +503,6 @@ importers:
react-router:
specifier: ^7.6.3
version: 7.6.3([email protected]([email protected]))([email protected])
shiki:
specifier: ^3.8.0
version: 3.8.0
devDependencies:
'@react-router/dev':
specifier: ^7.6.3
@@ -932,7 +926,7 @@ importers:
version: 0.2.2(@content-collections/[email protected]([email protected]))([email protected])([email protected]([email protected]))([email protected])
'@content-collections/next':
specifier: ^0.2.6
version: 0.2.6(@content-collections/[email protected]([email protected]))([email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))
version: 0.2.6(@content-collections/[email protected]([email protected]))([email protected](@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected]))
'@react-router/dev':
specifier: ^7.6.3
version: 7.6.3(@react-router/[email protected]([email protected]([email protected]([email protected]))([email protected]))([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected]([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))([email protected])
@@ -1141,6 +1135,9 @@ importers:
next:
specifier: ^15.4.1
version: 15.4.1(@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected])
react:
specifier: ^19.1.0
version: 19.1.0
tsconfig:
specifier: workspace:*
version: link:../tsconfig
@@ -10556,7 +10553,7 @@ snapshots:
- acorn
- supports-color
'@content-collections/[email protected](@content-collections/[email protected]([email protected]))([email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))':
'@content-collections/[email protected](@content-collections/[email protected]([email protected]))([email protected](@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected]))':
dependencies:
'@content-collections/core': 0.10.0([email protected])
'@content-collections/integrations': 0.2.1(@content-collections/[email protected]([email protected]))