fumadocs-mdx 10.0.2 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  var metaSchema = z.object({
4
4
  title: z.string().optional(),
5
5
  pages: z.array(z.string()).optional(),
6
+ description: z.string().optional(),
6
7
  root: z.boolean().optional(),
7
8
  defaultOpen: z.boolean().optional(),
8
9
  icon: z.string().optional()
@@ -20,20 +21,23 @@ var frontmatterSchema = z.object({
20
21
  function defineCollections(options) {
21
22
  return {
22
23
  _doc: "collections",
24
+ // @ts-expect-error -- internal type inferring
25
+ _type: void 0,
23
26
  ...options
24
27
  };
25
28
  }
26
29
  function defineDocs(options) {
30
+ const dir = options?.dir ?? "content/docs";
27
31
  return {
28
32
  docs: defineCollections({
29
33
  type: "doc",
30
- dir: "content/docs",
34
+ dir,
31
35
  schema: frontmatterSchema,
32
36
  ...options?.docs
33
37
  }),
34
38
  meta: defineCollections({
35
39
  type: "meta",
36
- dir: "content/docs",
40
+ dir,
37
41
  schema: metaSchema,
38
42
  ...options?.meta
39
43
  })
@@ -46,14 +50,23 @@ function defineConfig(config = {}) {
46
50
  // src/utils/mdx-options.ts
47
51
  import {
48
52
  rehypeCode,
53
+ rehypeToc,
49
54
  remarkGfm,
50
55
  remarkHeading,
51
56
  remarkImage,
52
57
  remarkStructure
53
58
  } from "fumadocs-core/mdx-plugins";
54
59
 
55
- // src/mdx-plugins/utils.ts
60
+ // src/mdx-plugins/remark-exports.ts
56
61
  import { valueToEstree } from "estree-util-value-to-estree";
62
+ function remarkMdxExport({ values }) {
63
+ return (tree, vfile) => {
64
+ for (const name of values) {
65
+ if (!(name in vfile.data)) return;
66
+ tree.children.unshift(getMdastExport(name, vfile.data[name]));
67
+ }
68
+ };
69
+ }
57
70
  function getMdastExport(name, value) {
58
71
  return {
59
72
  type: "mdxjsEsm",
@@ -88,16 +101,6 @@ function getMdastExport(name, value) {
88
101
  };
89
102
  }
90
103
 
91
- // src/mdx-plugins/remark-exports.ts
92
- function remarkMdxExport({ values }) {
93
- return (tree, vfile) => {
94
- for (const name of values) {
95
- if (!(name in vfile.data)) return;
96
- tree.children.unshift(getMdastExport(name, vfile.data[name]));
97
- }
98
- };
99
- }
100
-
101
104
  // src/utils/mdx-options.ts
102
105
  function pluginOption(def, options = []) {
103
106
  const list = def(Array.isArray(options) ? options : []).filter(
@@ -118,7 +121,6 @@ function getDefaultMDXOptions({
118
121
  }) {
119
122
  const mdxExports = [
120
123
  "structuredData",
121
- "toc",
122
124
  "frontmatter",
123
125
  "lastModified",
124
126
  ...valueToExport
@@ -126,7 +128,13 @@ function getDefaultMDXOptions({
126
128
  const remarkPlugins = pluginOption(
127
129
  (v) => [
128
130
  remarkGfm,
129
- [remarkHeading, remarkHeadingOptions],
131
+ [
132
+ remarkHeading,
133
+ {
134
+ generateToc: false,
135
+ ...remarkHeadingOptions
136
+ }
137
+ ],
130
138
  remarkImageOptions !== false && [remarkImage, remarkImageOptions],
131
139
  ...v,
132
140
  remarkStructureOptions !== false && [
@@ -140,7 +148,8 @@ function getDefaultMDXOptions({
140
148
  const rehypePlugins = pluginOption(
141
149
  (v) => [
142
150
  rehypeCodeOptions !== false && [rehypeCode, rehypeCodeOptions],
143
- ...v
151
+ ...v,
152
+ [rehypeToc]
144
153
  ],
145
154
  mdxOptions.rehypePlugins
146
155
  );
@@ -20,7 +20,10 @@ function validateConfig(config) {
20
20
  continue;
21
21
  }
22
22
  if (typeof v === "object" && "_doc" in v && v._doc === "collections") {
23
- out.collections.set(k, v);
23
+ out.collections.set(
24
+ k,
25
+ v
26
+ );
24
27
  continue;
25
28
  }
26
29
  if (k === "default") {
@@ -1,9 +1,11 @@
1
+ import { F as FileInfo, G as GlobalConfig, M as MarkdownProps, B as BaseCollectionEntry } from '../types-D11VoRzy.mjs';
2
+ export { C as CollectionEntry, D as DefaultMDXOptions, b as GetOutput, I as InferSchema, a as InferSchemaType, g as getDefaultMDXOptions } from '../types-D11VoRzy.mjs';
1
3
  import { z, ZodTypeAny } from 'zod';
2
- import { MDXProps } from 'mdx/types';
3
- import { StructureOptions, RemarkHeadingOptions, RemarkImageOptions, RehypeCodeOptions, StructuredData } from 'fumadocs-core/mdx-plugins';
4
- import { TableOfContents } from 'fumadocs-core/server';
5
4
  import { ProcessorOptions } from '@mdx-js/mdx';
6
- import { Pluggable } from 'unified';
5
+ import 'mdx/types';
6
+ import 'fumadocs-core/mdx-plugins';
7
+ import 'fumadocs-core/server';
8
+ import 'unified';
7
9
 
8
10
  interface MDXOptions extends ProcessorOptions {
9
11
  /**
@@ -24,21 +26,24 @@ interface MDXOptions extends ProcessorOptions {
24
26
  declare const metaSchema: z.ZodObject<{
25
27
  title: z.ZodOptional<z.ZodString>;
26
28
  pages: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
29
+ description: z.ZodOptional<z.ZodString>;
27
30
  root: z.ZodOptional<z.ZodBoolean>;
28
31
  defaultOpen: z.ZodOptional<z.ZodBoolean>;
29
32
  icon: z.ZodOptional<z.ZodString>;
30
33
  }, "strip", z.ZodTypeAny, {
34
+ root?: boolean | undefined;
31
35
  title?: string | undefined;
36
+ icon?: string | undefined;
32
37
  pages?: string[] | undefined;
33
- root?: boolean | undefined;
38
+ description?: string | undefined;
34
39
  defaultOpen?: boolean | undefined;
35
- icon?: string | undefined;
36
40
  }, {
41
+ root?: boolean | undefined;
37
42
  title?: string | undefined;
43
+ icon?: string | undefined;
38
44
  pages?: string[] | undefined;
39
- root?: boolean | undefined;
45
+ description?: string | undefined;
40
46
  defaultOpen?: boolean | undefined;
41
- icon?: string | undefined;
42
47
  }>;
43
48
  declare const frontmatterSchema: z.ZodObject<{
44
49
  title: z.ZodString;
@@ -68,7 +73,7 @@ interface TransformContext {
68
73
  */
69
74
  buildMDX: (source: string, options?: ProcessorOptions) => Promise<string>;
70
75
  }
71
- interface Collections<Schema extends ZodTypeAny = ZodTypeAny, Type extends SupportedType = SupportedType, Output extends BaseCollectionEntry = CollectionEntry<Type, z.output<Schema>>> {
76
+ interface BaseCollection<Schema> {
72
77
  /**
73
78
  * Directories to scan
74
79
  */
@@ -80,92 +85,63 @@ interface Collections<Schema extends ZodTypeAny = ZodTypeAny, Type extends Suppo
80
85
  */
81
86
  files?: string[];
82
87
  schema?: Schema | ((ctx: TransformContext) => Schema);
83
- /**
84
- * content type
85
- */
86
- type: Type;
88
+ }
89
+ interface MetaCollection<Schema extends ZodTypeAny = ZodTypeAny, TransformOutput = unknown> extends BaseCollection<Schema> {
90
+ type: 'meta';
87
91
  /**
88
92
  * Do transformation in runtime.
89
93
  *
90
94
  * This cannot be optimized by bundlers/loaders, avoid expensive calculations here.
91
95
  */
92
- transform?: (entry: CollectionEntry<Type, z.output<Schema>>, globalConfig?: GlobalConfig) => Output | Promise<Output>;
93
- mdxOptions?: Type extends 'doc' ? MDXOptions : never;
96
+ transform?: (entry: {
97
+ data: z.output<Schema>;
98
+ file: FileInfo;
99
+ }, globalConfig?: GlobalConfig) => TransformOutput | Promise<TransformOutput>;
94
100
  }
95
- declare function defineCollections<Schema extends ZodTypeAny, Type extends SupportedType, Output extends BaseCollectionEntry = CollectionEntry<Type, z.output<Schema>>>(options: Collections<Schema, Type, Output>): {
96
- _doc: 'collections';
97
- } & Collections<Schema, Type, Output>;
98
- declare function defineDocs<F extends ZodTypeAny = typeof frontmatterSchema, M extends ZodTypeAny = typeof metaSchema, DocsOut extends BaseCollectionEntry = CollectionEntry<'doc', z.output<F>>, MetaOut extends BaseCollectionEntry = CollectionEntry<'meta', z.output<M>>>(options?: {
99
- docs?: Partial<Collections<F, 'doc', DocsOut>>;
100
- meta?: Partial<Collections<M, 'meta', MetaOut>>;
101
- }): {
102
- docs: Collections<F, 'doc', DocsOut>;
103
- meta: Collections<M, 'meta', MetaOut>;
104
- };
105
- declare function defineConfig(config?: GlobalConfig): GlobalConfig;
106
-
107
- type ResolvePlugins = Pluggable[] | ((v: Pluggable[]) => Pluggable[]);
108
- type DefaultMDXOptions = Omit<NonNullable<ProcessorOptions>, 'rehypePlugins' | 'remarkPlugins' | '_ctx'> & {
109
- rehypePlugins?: ResolvePlugins;
110
- remarkPlugins?: ResolvePlugins;
111
- /**
112
- * Properties to export from `vfile.data`
113
- */
114
- valueToExport?: string[];
115
- remarkStructureOptions?: StructureOptions | false;
116
- remarkHeadingOptions?: RemarkHeadingOptions;
117
- remarkImageOptions?: RemarkImageOptions | false;
118
- rehypeCodeOptions?: RehypeCodeOptions | false;
119
- };
120
- declare function getDefaultMDXOptions({ valueToExport, rehypeCodeOptions, remarkImageOptions, remarkHeadingOptions, remarkStructureOptions, ...mdxOptions }: DefaultMDXOptions): ProcessorOptions;
121
-
122
- interface GlobalConfig {
123
- /**
124
- * Configure global MDX options
125
- */
126
- mdxOptions?: DefaultMDXOptions;
101
+ interface DocCollection<Schema extends ZodTypeAny = ZodTypeAny, Async extends boolean = boolean, TransformOutput = unknown> extends BaseCollection<Schema> {
102
+ type: 'doc';
127
103
  /**
128
- * Fetch last modified time with specified version control
129
- * @defaultValue 'none'
104
+ * Do transformation in runtime.
105
+ *
106
+ * This cannot be optimized by bundlers/loaders, avoid expensive calculations here.
130
107
  */
131
- lastModifiedTime?: 'git' | 'none';
108
+ transform?: (entry: {
109
+ data: z.output<Schema>;
110
+ file: FileInfo;
111
+ mdx: Async extends true ? MarkdownProps : never;
112
+ }, globalConfig?: GlobalConfig) => TransformOutput | Promise<TransformOutput>;
113
+ mdxOptions?: MDXOptions;
132
114
  /**
133
- * Generate manifest file on build mode
134
- *
135
- * @defaultValue false
115
+ * Load files with async
136
116
  */
137
- generateManifest?: boolean;
138
- }
139
- type InferSchema<C> = C extends Collections<infer Schema, any, any> ? Schema : never;
140
- type InferSchemaType<C> = z.output<InferSchema<C>>;
141
- type InferCollectionsProps<C> = SupportedTypes[C extends Collections<any, infer Type, any> ? Type : never];
142
- interface FileInfo {
143
- path: string;
144
- absolutePath: string;
117
+ async?: Async;
145
118
  }
146
- interface MarkdownProps {
147
- body: (props: MDXProps) => React.ReactElement;
148
- structuredData: StructuredData;
149
- toc: TableOfContents;
150
- _exports: Record<string, unknown>;
119
+ declare function defineCollections<T extends 'doc' | 'meta', Schema extends ZodTypeAny = ZodTypeAny, Async extends boolean = false, TransformOutput = unknown>(options: {
120
+ type: T;
121
+ } & (T extends 'doc' ? DocCollection<Schema, Async, TransformOutput> : MetaCollection<Schema, TransformOutput>)): {
122
+ _doc: 'collections';
123
+ type: T;
124
+ _type: {
125
+ async: Async;
126
+ transform: TransformOutput;
127
+ runtime: T extends 'doc' ? Async extends true ? z.infer<Schema> & BaseCollectionEntry & {
128
+ load: () => Promise<MarkdownProps>;
129
+ } : Omit<MarkdownProps, keyof z.infer<Schema>> & z.infer<Schema> & BaseCollectionEntry : typeof options extends MetaCollection ? z.infer<Schema> & BaseCollectionEntry : never;
130
+ };
131
+ };
132
+ declare function defineDocs<DocData extends ZodTypeAny = typeof frontmatterSchema, MetaData extends ZodTypeAny = typeof metaSchema, DocAsync extends boolean = false, DocOut = unknown, MetaOut = unknown>(options?: {
151
133
  /**
152
- * Only available when `lastModifiedTime` is enabled on MDX loader
134
+ * The directory to scan files
135
+ *
136
+ * @defaultValue '/content/docs'
153
137
  */
154
- lastModified?: Date;
155
- }
156
- interface SupportedTypes {
157
- meta: {};
158
- doc: MarkdownProps;
159
- }
160
- type SupportedType = keyof SupportedTypes;
161
- type CollectionEntry<Type extends SupportedType, Output> = Omit<SupportedTypes[Type], keyof Output> & Output & BaseCollectionEntry;
162
- interface BaseCollectionEntry {
163
- _file: FileInfo;
164
- }
165
- type EntryFromCollection<C> = C extends Collections<any, any, infer Output> ? Output : never;
166
- /**
167
- * Get output type of collections
168
- */
169
- type GetOutput<C> = EntryFromCollection<C>[];
138
+ dir?: string | string[];
139
+ docs?: Partial<DocCollection<DocData, DocAsync, DocOut>>;
140
+ meta?: Partial<MetaCollection<MetaData, MetaOut>>;
141
+ }): {
142
+ docs: ReturnType<typeof defineCollections<'doc', DocData, DocAsync, DocOut>>;
143
+ meta: ReturnType<typeof defineCollections<'meta', MetaData, false, MetaOut>>;
144
+ };
145
+ declare function defineConfig(config?: GlobalConfig): GlobalConfig;
170
146
 
171
- export { type BaseCollectionEntry, type CollectionEntry, type Collections, type DefaultMDXOptions, type EntryFromCollection, type FileInfo, type GetOutput, type GlobalConfig, type InferCollectionsProps, type InferSchema, type InferSchemaType, type SupportedType, type SupportedTypes, type TransformContext, defineCollections, defineConfig, defineDocs, frontmatterSchema, getDefaultMDXOptions, metaSchema };
147
+ export { type BaseCollection, BaseCollectionEntry, type DocCollection, FileInfo, GlobalConfig, MarkdownProps, type MetaCollection, type TransformContext, defineCollections, defineConfig, defineDocs, frontmatterSchema, metaSchema };
@@ -5,7 +5,7 @@ import {
5
5
  frontmatterSchema,
6
6
  getDefaultMDXOptions,
7
7
  metaSchema
8
- } from "../chunk-LRV535YP.mjs";
8
+ } from "../chunk-FXIX4XBC.mjs";
9
9
  export {
10
10
  defineCollections,
11
11
  defineConfig,
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PageData, MetaData, Source } from 'fumadocs-core/source';
2
- import { SupportedType, FileInfo, EntryFromCollection, Collections, CollectionEntry } from './config/index.mjs';
2
+ import { F as FileInfo, B as BaseCollectionEntry } from './types-D11VoRzy.mjs';
3
3
  import { MetaFile } from './loader-mdx.mjs';
4
4
  import 'zod';
5
5
  import 'mdx/types';
@@ -9,8 +9,9 @@ import '@mdx-js/mdx';
9
9
  import 'unified';
10
10
  import 'webpack';
11
11
 
12
- declare function toRuntime(type: SupportedType, file: Record<string, unknown>, info: FileInfo): EntryFromCollection<Collections>;
13
- declare function createMDXSource<Doc extends CollectionEntry<'doc', PageData>, Meta extends CollectionEntry<'meta', MetaData>>(docs: Doc[], meta: Meta[]): Source<{
12
+ declare function toRuntime(type: 'doc' | 'meta', file: Record<string, unknown>, info: FileInfo): unknown;
13
+ declare function toRuntimeAsync(frontmatter: Record<string, unknown>, load: () => Promise<Record<string, unknown>>, info: FileInfo): unknown;
14
+ declare function createMDXSource<Doc extends PageData & BaseCollectionEntry, Meta extends MetaData & BaseCollectionEntry>(docs: Doc[], meta: Meta[]): Source<{
14
15
  pageData: Doc;
15
16
  metaData: Meta;
16
17
  }>;
@@ -21,4 +22,4 @@ interface Manifest {
21
22
  })[];
22
23
  }
23
24
 
24
- export { type Manifest, createMDXSource, toRuntime };
25
+ export { type Manifest, createMDXSource, toRuntime, toRuntimeAsync };
package/dist/index.mjs CHANGED
@@ -1,7 +1,4 @@
1
1
  // src/runtime/resolve-files.ts
2
- import path from "node:path";
3
- var pageTypes = [".md", ".mdx"];
4
- var metaTypes = [".json", ".yaml"];
5
2
  function resolveFiles({
6
3
  docs,
7
4
  meta,
@@ -10,38 +7,23 @@ function resolveFiles({
10
7
  const outputs = [];
11
8
  for (const entry of docs) {
12
9
  if (!entry._file.path.startsWith(rootDir)) continue;
13
- const ext = path.extname(entry._file.path);
14
- if (pageTypes.includes(ext)) {
15
- outputs.push({
16
- type: "page",
17
- path: entry._file.path,
18
- data: entry
19
- });
20
- continue;
21
- }
22
- console.warn(
23
- `Unknown Type: ${ext} on ${entry._file.path}, expected: ${pageTypes.toString()}`
24
- );
10
+ outputs.push({
11
+ type: "page",
12
+ path: entry._file.path,
13
+ data: entry
14
+ });
25
15
  }
26
16
  for (const entry of meta) {
27
- if (!entry._file.path.startsWith(rootDir)) continue;
28
- const ext = path.extname(entry._file.path);
29
- if (metaTypes.includes(ext)) {
30
- outputs.push({
31
- type: "meta",
32
- path: entry._file.path,
33
- data: entry
34
- });
35
- continue;
36
- }
37
- console.warn(
38
- `Unknown Type: ${ext} on ${entry._file.path}, expected: ${metaTypes.toString()}`
39
- );
17
+ outputs.push({
18
+ type: "meta",
19
+ path: entry._file.path,
20
+ data: entry
21
+ });
40
22
  }
41
23
  return outputs;
42
24
  }
43
25
 
44
- // src/runtime/create.ts
26
+ // src/runtime/index.ts
45
27
  function toRuntime(type, file, info) {
46
28
  if (type === "doc") {
47
29
  const { default: body, frontmatter, ...exports } = file;
@@ -58,6 +40,19 @@ function toRuntime(type, file, info) {
58
40
  _file: info
59
41
  };
60
42
  }
43
+ function toRuntimeAsync(frontmatter, load, info) {
44
+ return {
45
+ async load() {
46
+ const { default: body, ...res } = await load();
47
+ return {
48
+ body,
49
+ ...res
50
+ };
51
+ },
52
+ ...frontmatter,
53
+ _file: info
54
+ };
55
+ }
61
56
  function createMDXSource(docs, meta) {
62
57
  return {
63
58
  files: (rootDir) => resolveFiles({
@@ -69,5 +64,6 @@ function createMDXSource(docs, meta) {
69
64
  }
70
65
  export {
71
66
  createMDXSource,
72
- toRuntime
67
+ toRuntime,
68
+ toRuntimeAsync
73
69
  };
@@ -1,39 +1,18 @@
1
+ import {
2
+ getDefaultMDXOptions
3
+ } from "./chunk-FXIX4XBC.mjs";
1
4
  import {
2
5
  getConfigHash,
3
6
  getManifestEntryPath,
4
7
  loadConfigCached
5
- } from "./chunk-VW6REBOK.mjs";
6
- import {
7
- getDefaultMDXOptions
8
- } from "./chunk-LRV535YP.mjs";
8
+ } from "./chunk-KD74ZWYJ.mjs";
9
9
 
10
10
  // src/loader-mdx.ts
11
- import path3 from "node:path";
11
+ import path2 from "node:path";
12
12
  import fs2 from "node:fs/promises";
13
13
  import { parse } from "node:querystring";
14
14
  import grayMatter from "gray-matter";
15
15
 
16
- // src/utils/find-collection.ts
17
- import path from "node:path";
18
- import micromatch from "micromatch";
19
- function findCollectionId(config, file, type) {
20
- const cached = config._runtime.files.get(file);
21
- if (cached) return cached;
22
- for (const [name, collection] of config.collections.entries()) {
23
- if (collection.type !== type) continue;
24
- const dirs = Array.isArray(collection.dir) ? collection.dir : [collection.dir];
25
- const isInDir = dirs.some((dir) => {
26
- const relative = path.relative(dir, path.dirname(file));
27
- return !relative.startsWith("..") && !path.isAbsolute(relative);
28
- });
29
- if (!isInDir) continue;
30
- const isIncluded = collection.files ? micromatch.isMatch(file, collection.files) : true;
31
- if (!isIncluded) continue;
32
- config._runtime.files.set(file, name);
33
- return name;
34
- }
35
- }
36
-
37
16
  // src/utils/build-mdx.ts
38
17
  import { createProcessor } from "@mdx-js/mdx";
39
18
  var cache = /* @__PURE__ */ new Map();
@@ -90,7 +69,7 @@ function formatError(file, error) {
90
69
  }
91
70
 
92
71
  // src/utils/git-timestamp.ts
93
- import path2 from "node:path";
72
+ import path from "node:path";
94
73
  import fs from "node:fs";
95
74
  import { spawn } from "cross-spawn";
96
75
  var cache2 = /* @__PURE__ */ new Map();
@@ -98,12 +77,12 @@ function getGitTimestamp(file) {
98
77
  const cachedTimestamp = cache2.get(file);
99
78
  if (cachedTimestamp) return Promise.resolve(cachedTimestamp);
100
79
  return new Promise((resolve, reject) => {
101
- const cwd = path2.dirname(file);
80
+ const cwd = path.dirname(file);
102
81
  if (!fs.existsSync(cwd)) {
103
82
  resolve(void 0);
104
83
  return;
105
84
  }
106
- const fileName = path2.basename(file);
85
+ const fileName = path.basename(file);
107
86
  const child = spawn("git", ["log", "-1", '--pretty="%ai"', fileName], {
108
87
  cwd
109
88
  });
@@ -136,8 +115,11 @@ async function loader(source, callback) {
136
115
  const query = getQuery(this.resourceQuery);
137
116
  const configHash = query.hash ?? await getConfigHash(_ctx.configPath);
138
117
  const config = await loadConfigCached(_ctx.configPath, configHash);
139
- const collectionId = query.collection ?? findCollectionId(config, filePath, "doc");
140
- const collection = collectionId !== void 0 ? config.collections.get(collectionId) : void 0;
118
+ const collectionId = query.collection;
119
+ let collection = collectionId !== void 0 ? config.collections.get(collectionId) : void 0;
120
+ if (collection && collection.type !== "doc") {
121
+ collection = void 0;
122
+ }
141
123
  const mdxOptions = collection?.mdxOptions ?? getDefaultMDXOptions(config.global?.mdxOptions ?? {});
142
124
  function getTransformContext() {
143
125
  return {
@@ -166,7 +148,7 @@ async function loader(source, callback) {
166
148
  }
167
149
  const props = matter.data._mdx ?? {};
168
150
  if (props.mirror) {
169
- const mirrorPath = path3.resolve(path3.dirname(filePath), props.mirror);
151
+ const mirrorPath = path2.resolve(path2.dirname(filePath), props.mirror);
170
152
  this.addDependency(mirrorPath);
171
153
  matter.content = await fs2.readFile(mirrorPath).then((res) => grayMatter(res.toString()).content);
172
154
  }
@@ -201,7 +183,7 @@ async function loader(source, callback) {
201
183
  }
202
184
  } catch (error) {
203
185
  if (!(error instanceof Error)) throw error;
204
- const fpath = path3.relative(context, filePath);
186
+ const fpath = path2.relative(context, filePath);
205
187
  error.message = `${fpath}:${error.name}: ${error.message}`;
206
188
  callback(error);
207
189
  }
@@ -1,13 +1,21 @@
1
1
  import { NextConfig } from 'next';
2
2
 
3
+ /**
4
+ * Start a MDX server that builds index and manifest files.
5
+ *
6
+ * In development mode, it starts a file watcher to auto-update output as your input changes.
7
+ */
8
+ declare function start(dev: boolean, configPath: string, outDir: string): Promise<void>;
9
+
3
10
  interface CreateMDXOptions {
4
11
  /**
5
12
  * Path to source configuration file
6
13
  */
7
14
  configPath?: string;
8
15
  }
16
+
9
17
  declare function createMDX({ configPath, }?: CreateMDXOptions): (nextConfig?: NextConfig) => NextConfig;
10
18
 
11
19
  declare function postInstall(configPath?: string): Promise<void>;
12
20
 
13
- export { type CreateMDXOptions, createMDX, postInstall };
21
+ export { type CreateMDXOptions, createMDX, postInstall, start };
@@ -5,7 +5,7 @@ import {
5
5
  loadConfig,
6
6
  loadConfigCached,
7
7
  writeManifest
8
- } from "../chunk-VW6REBOK.mjs";
8
+ } from "../chunk-KD74ZWYJ.mjs";
9
9
 
10
10
  // src/next/create.ts
11
11
  import path3 from "node:path";
@@ -13,82 +13,90 @@ import path3 from "node:path";
13
13
  // src/map/index.ts
14
14
  import path2 from "node:path";
15
15
  import fs from "node:fs";
16
-
17
- // src/map/watcher.ts
18
- import { watch } from "chokidar";
19
- function watcher(configPath, config) {
20
- const deps = [configPath];
21
- for (const collection of config.collections.values()) {
22
- if (Array.isArray(collection.dir)) deps.push(...collection.dir);
23
- else deps.push(collection.dir);
24
- }
25
- return watch(deps, {
26
- ignoreInitial: true
27
- });
28
- }
16
+ import { readFile, writeFile } from "node:fs/promises";
17
+ import grayMatter from "gray-matter";
29
18
 
30
19
  // src/map/generate.ts
31
- import path from "node:path";
20
+ import * as path from "node:path";
32
21
  import fg from "fast-glob";
33
- async function generateJS(configPath, config, outputPath, hash) {
22
+ async function generateJS(configPath, config, outputPath, hash, getFrontmatter) {
34
23
  const outDir2 = path.dirname(outputPath);
35
- const imports = ['import { toRuntime } from "fumadocs-mdx"'];
24
+ const imports = [
25
+ {
26
+ type: "named",
27
+ names: ["toRuntime", "toRuntimeAsync"],
28
+ specifier: "fumadocs-mdx"
29
+ }
30
+ ];
36
31
  const importedCollections = /* @__PURE__ */ new Set();
37
- const sources = [];
38
- let importId = 0;
39
32
  config._runtime.files.clear();
40
- await Promise.all(
41
- Array.from(config.collections.entries()).map(async ([name, collection]) => {
42
- const entries = [];
43
- const dirs = Array.isArray(collection.dir) ? collection.dir : [collection.dir];
44
- await Promise.all(
45
- dirs.map(async (dir) => {
46
- const included = await fg(collection.files ?? ["**/*"], {
47
- cwd: dir,
48
- absolute: true
49
- });
50
- for (const file of included) {
51
- if (getTypeFromPath(file) !== collection.type) continue;
52
- if (config._runtime.files.has(file)) {
53
- console.warn(
54
- `[MDX] Files cannot exist in multiple collections: ${file} (${config._runtime.files.get(file) ?? ""})`
55
- );
56
- continue;
57
- }
58
- config._runtime.files.set(file, name);
59
- const importName = `file_${(importId++).toString()}`;
60
- imports.push(
61
- `import * as ${importName} from ${JSON.stringify(`${toImportPath(file, outDir2)}?collection=${name}&hash=${hash}`)}`
62
- );
63
- const info = {
64
- path: path.relative(dir, file),
65
- absolutePath: file
66
- };
67
- entries.push(
68
- `toRuntime("${collection.type}", ${importName}, ${JSON.stringify(info)})`
69
- );
70
- }
71
- })
72
- );
73
- if (collection.transform) {
74
- if (config.global) importedCollections.add("default");
75
- importedCollections.add(name);
76
- sources.push(
77
- `export const ${name} = await Promise.all([${entries.join(",")}].map(v => c_${name}.transform(v, c_default)))`
78
- );
79
- } else {
80
- sources.push(`export const ${name} = [${entries.join(",")}]`);
33
+ const entries = Array.from(config.collections.entries());
34
+ const declares = entries.map(async ([k, collection]) => {
35
+ const files = await getCollectionFiles(collection);
36
+ const items = files.map(async (file, i) => {
37
+ config._runtime.files.set(file.absolutePath, k);
38
+ if (collection.type === "doc" && collection.async) {
39
+ const importPath = `${toImportPath(file.absolutePath, outDir2)}?hash=${hash}&collection=${k}`;
40
+ const frontmatter = await getFrontmatter(file.absolutePath);
41
+ return `toRuntimeAsync(${JSON.stringify(frontmatter)}, () => import(${JSON.stringify(importPath)}), ${JSON.stringify(file)})`;
81
42
  }
43
+ const importName = `${k}_${i.toString()}`;
44
+ imports.push({
45
+ type: "namespace",
46
+ name: importName,
47
+ specifier: `${toImportPath(file.absolutePath, outDir2)}?collection=${k}&hash=${hash}`
48
+ });
49
+ return `toRuntime("${collection.type}", ${importName}, ${JSON.stringify(file)})`;
50
+ });
51
+ const resolvedItems = await Promise.all(items);
52
+ if (collection.transform) {
53
+ if (config.global) importedCollections.add("default");
54
+ importedCollections.add(k);
55
+ }
56
+ return collection.transform ? `export const ${k} = await Promise.all([${resolvedItems.join(", ")}].map(v => c_${k}.transform(v, ${config.global ? "c_default" : "undefined"})));` : `export const ${k} = [${resolvedItems.join(", ")}];`;
57
+ });
58
+ const resolvedDeclares = await Promise.all(declares);
59
+ if (importedCollections.size > 0) {
60
+ imports.push({
61
+ type: "named",
62
+ names: Array.from(importedCollections.values()).sort().map((v) => [v, `c_${v}`]),
63
+ specifier: toImportPath(configPath, outDir2)
64
+ });
65
+ }
66
+ return [...imports.map(getImportCode), ...resolvedDeclares].join("\n");
67
+ }
68
+ async function getCollectionFiles(collection) {
69
+ const files = /* @__PURE__ */ new Map();
70
+ const dirs = Array.isArray(collection.dir) ? collection.dir : [collection.dir];
71
+ await Promise.all(
72
+ dirs.map(async (dir) => {
73
+ const result = await fg(collection.files ?? "**/*", {
74
+ cwd: dir,
75
+ absolute: true
76
+ });
77
+ result.forEach((item) => {
78
+ if (getTypeFromPath(item) !== collection.type) return;
79
+ files.set(item, {
80
+ path: path.relative(dir, item),
81
+ absolutePath: item
82
+ });
83
+ });
82
84
  })
83
85
  );
84
- if (importedCollections.size > 0) {
85
- imports.push(
86
- `import { ${Array.from(importedCollections.values()).map((v) => `${v} as c_${v}`).join(
87
- ", "
88
- )} } from ${JSON.stringify(toImportPath(configPath, outDir2))}`
86
+ return Array.from(files.values());
87
+ }
88
+ function getImportCode(info) {
89
+ const specifier = JSON.stringify(info.specifier);
90
+ if (info.type === "default") return `import ${info.name} from ${specifier}`;
91
+ if (info.type === "namespace")
92
+ return `import * as ${info.name} from ${specifier}`;
93
+ if (info.type === "named") {
94
+ const names = info.names.map(
95
+ (name) => Array.isArray(name) ? `${name[0]} as ${name[1]}` : name
89
96
  );
97
+ return `import { ${names.join(", ")} } from ${specifier}`;
90
98
  }
91
- return [...imports, ...sources].join("\n");
99
+ return `import ${specifier}`;
92
100
  }
93
101
  function toImportPath(file, dir) {
94
102
  let importPath = path.relative(dir, file);
@@ -119,10 +127,28 @@ async function start(dev, configPath, outDir2) {
119
127
  const manifestPath = path2.resolve(outDir2, "manifest.json");
120
128
  const jsOut = path2.resolve(outDir2, `index.js`);
121
129
  const typeOut = path2.resolve(outDir2, `index.d.ts`);
130
+ const frontmatterCache = /* @__PURE__ */ new Map();
131
+ let hookUpdate = false;
132
+ const readFrontmatter = async (file) => {
133
+ const cached = frontmatterCache.get(file);
134
+ if (cached) return cached;
135
+ hookUpdate = true;
136
+ return grayMatter({
137
+ content: await readFile(file).then((res) => res.toString())
138
+ }).data;
139
+ };
140
+ fs.mkdirSync(outDir2, { recursive: true });
141
+ fs.writeFileSync(
142
+ jsOut,
143
+ await generateJS(configPath, config, jsOut, configHash, readFrontmatter)
144
+ );
145
+ fs.writeFileSync(typeOut, generateTypes(configPath, config, typeOut));
146
+ console.log("[MDX] initialized map file");
122
147
  if (dev) {
148
+ const { watcher } = await import("../watcher-DL2NRJWG.mjs");
123
149
  const instance = watcher(configPath, config);
124
150
  instance.on("ready", () => {
125
- console.log("[MDX] started dev server");
151
+ if (!instance._readyEmitted) console.log("[MDX] started dev server");
126
152
  });
127
153
  instance.on("all", (event, file) => {
128
154
  const onUpdate = async () => {
@@ -130,13 +156,20 @@ async function start(dev, configPath, outDir2) {
130
156
  if (isConfigFile) {
131
157
  configHash = await getConfigHash(configPath);
132
158
  config = await loadConfigCached(configPath, configHash);
133
- fs.writeFileSync(typeOut, generateTypes(configPath, config, typeOut));
159
+ await writeFile(typeOut, generateTypes(configPath, config, typeOut));
134
160
  console.log("[MDX] Updated map types");
135
161
  }
136
- if (isConfigFile || event !== "change") {
137
- fs.writeFileSync(
162
+ if (isConfigFile || event !== "change" || hookUpdate) {
163
+ if (event === "change") frontmatterCache.delete(file);
164
+ await writeFile(
138
165
  jsOut,
139
- await generateJS(configPath, config, jsOut, configHash)
166
+ await generateJS(
167
+ configPath,
168
+ config,
169
+ jsOut,
170
+ configHash,
171
+ readFrontmatter
172
+ )
140
173
  );
141
174
  console.log("[MDX] Updated map file");
142
175
  }
@@ -148,13 +181,6 @@ async function start(dev, configPath, outDir2) {
148
181
  void instance.close();
149
182
  });
150
183
  }
151
- fs.mkdirSync(outDir2, { recursive: true });
152
- fs.writeFileSync(
153
- jsOut,
154
- await generateJS(configPath, config, jsOut, configHash)
155
- );
156
- fs.writeFileSync(typeOut, generateTypes(configPath, config, typeOut));
157
- console.log("[MDX] initialized map file");
158
184
  if (config.global?.generateManifest && !dev) {
159
185
  process.on("exit", () => {
160
186
  console.log("[MDX] writing manifest");
@@ -171,7 +197,8 @@ function createMDX({
171
197
  } = {}) {
172
198
  const isDev = process.argv.includes("dev");
173
199
  const isBuild = process.argv.includes("build");
174
- if (isDev || isBuild) {
200
+ if ((isDev || isBuild) && process.env._FUMADOCS_MDX !== "1") {
201
+ process.env._FUMADOCS_MDX = "1";
175
202
  void start(isDev, configPath, outDir);
176
203
  }
177
204
  return (nextConfig = {}) => {
@@ -235,5 +262,6 @@ async function postInstall(configPath = findConfigFile()) {
235
262
  }
236
263
  export {
237
264
  createMDX,
238
- postInstall
265
+ postInstall,
266
+ start
239
267
  };
@@ -0,0 +1,77 @@
1
+ import { AnyZodObject, z } from 'zod';
2
+ import { MDXProps } from 'mdx/types';
3
+ import { StructureOptions, RemarkHeadingOptions, RemarkImageOptions, RehypeCodeOptions, StructuredData } from 'fumadocs-core/mdx-plugins';
4
+ import { TableOfContents } from 'fumadocs-core/server';
5
+ import { ProcessorOptions } from '@mdx-js/mdx';
6
+ import { Pluggable } from 'unified';
7
+
8
+ type ResolvePlugins = Pluggable[] | ((v: Pluggable[]) => Pluggable[]);
9
+ type DefaultMDXOptions = Omit<NonNullable<ProcessorOptions>, 'rehypePlugins' | 'remarkPlugins' | '_ctx'> & {
10
+ rehypePlugins?: ResolvePlugins;
11
+ remarkPlugins?: ResolvePlugins;
12
+ /**
13
+ * Properties to export from `vfile.data`
14
+ */
15
+ valueToExport?: string[];
16
+ remarkStructureOptions?: StructureOptions | false;
17
+ remarkHeadingOptions?: RemarkHeadingOptions;
18
+ remarkImageOptions?: RemarkImageOptions | false;
19
+ rehypeCodeOptions?: RehypeCodeOptions | false;
20
+ };
21
+ declare function getDefaultMDXOptions({ valueToExport, rehypeCodeOptions, remarkImageOptions, remarkHeadingOptions, remarkStructureOptions, ...mdxOptions }: DefaultMDXOptions): ProcessorOptions;
22
+
23
+ interface GlobalConfig {
24
+ /**
25
+ * Configure global MDX options
26
+ */
27
+ mdxOptions?: DefaultMDXOptions;
28
+ /**
29
+ * Fetch last modified time with specified version control
30
+ * @defaultValue 'none'
31
+ */
32
+ lastModifiedTime?: 'git' | 'none';
33
+ /**
34
+ * Generate manifest file on build mode
35
+ *
36
+ * @defaultValue false
37
+ */
38
+ generateManifest?: boolean;
39
+ }
40
+ type InferSchema<CollectionOut> = CollectionOut extends {
41
+ _type: {
42
+ schema: infer T;
43
+ };
44
+ } ? T : never;
45
+ type InferSchemaType<C> = InferSchema<C> extends AnyZodObject ? z.output<InferSchema<C>> : never;
46
+ interface FileInfo {
47
+ path: string;
48
+ absolutePath: string;
49
+ }
50
+ interface MarkdownProps {
51
+ body: (props: MDXProps) => React.ReactElement;
52
+ structuredData: StructuredData;
53
+ toc: TableOfContents;
54
+ _exports: Record<string, unknown>;
55
+ /**
56
+ * Only available when `lastModifiedTime` is enabled on MDX loader
57
+ */
58
+ lastModified?: Date;
59
+ }
60
+ type CollectionEntry<CollectionOut extends {
61
+ _type: {
62
+ runtime: unknown;
63
+ };
64
+ }> = CollectionOut['_type']['runtime'];
65
+ interface BaseCollectionEntry {
66
+ _file: FileInfo;
67
+ }
68
+ /**
69
+ * Get output type of collections
70
+ */
71
+ type GetOutput<C extends {
72
+ _type: {
73
+ runtime: unknown;
74
+ };
75
+ }> = CollectionEntry<C>[];
76
+
77
+ export { type BaseCollectionEntry as B, type CollectionEntry as C, type DefaultMDXOptions as D, type FileInfo as F, type GlobalConfig as G, type InferSchema as I, type MarkdownProps as M, type InferSchemaType as a, type GetOutput as b, getDefaultMDXOptions as g };
@@ -0,0 +1,16 @@
1
+ // src/map/watcher.ts
2
+ import { watch } from "chokidar";
3
+ function watcher(configPath, config) {
4
+ const deps = [configPath];
5
+ for (const collection of config.collections.values()) {
6
+ if (Array.isArray(collection.dir)) deps.push(...collection.dir);
7
+ else deps.push(collection.dir);
8
+ }
9
+ return watch(deps, {
10
+ ignoreInitial: true,
11
+ persistent: true
12
+ });
13
+ }
14
+ export {
15
+ watcher
16
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-mdx",
3
- "version": "10.0.2",
3
+ "version": "11.0.0",
4
4
  "description": "The built-in source for Fumadocs",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -27,16 +27,6 @@
27
27
  },
28
28
  "main": "./dist/index.mjs",
29
29
  "types": "./dist/index.mts",
30
- "typesVersions": {
31
- "*": {
32
- "config": [
33
- "./dist/config/index.d.mts"
34
- ],
35
- "next": [
36
- "./dist/next/index.d.mts"
37
- ]
38
- }
39
- },
40
30
  "bin": "./bin.mjs",
41
31
  "files": [
42
32
  "dist/*",
@@ -44,10 +34,10 @@
44
34
  "bin.mjs"
45
35
  ],
46
36
  "dependencies": {
47
- "@mdx-js/mdx": "^3.0.1",
48
- "chokidar": "^3.6.0",
37
+ "@mdx-js/mdx": "^3.1.0",
38
+ "chokidar": "^4.0.1",
49
39
  "cross-spawn": "^7.0.3",
50
- "esbuild": "^0.23.1",
40
+ "esbuild": "^0.24.0",
51
41
  "estree-util-value-to-estree": "^3.1.2",
52
42
  "fast-glob": "^3.3.1",
53
43
  "gray-matter": "^4.0.3",
@@ -59,17 +49,18 @@
59
49
  "@types/mdast": "^4.0.3",
60
50
  "@types/mdx": "^2.0.13",
61
51
  "@types/micromatch": "^4.0.9",
62
- "@types/react": "^18.3.5",
63
- "next": "^14.2.8",
52
+ "@types/react": "^18.3.11",
53
+ "next": "15.0.0",
64
54
  "unified": "^11.0.5",
65
- "webpack": "^5.90.3",
55
+ "vfile": "^6.0.3",
56
+ "webpack": "^5.95.0",
66
57
  "eslint-config-custom": "0.0.0",
67
- "fumadocs-core": "13.4.8",
58
+ "fumadocs-core": "14.0.1",
68
59
  "tsconfig": "0.0.0"
69
60
  },
70
61
  "peerDependencies": {
71
- "fumadocs-core": "^13.2.1",
72
- "next": ">= 14.1.0"
62
+ "fumadocs-core": "^14.0.0",
63
+ "next": "14.x.x || 15.x.x"
73
64
  },
74
65
  "publishConfig": {
75
66
  "access": "public"