fumadocs-mdx 11.5.3 → 11.5.5

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.
@@ -30,130 +30,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  ));
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
- // src/mdx-plugins/remark-exports.ts
34
- function remarkMdxExport({ values }) {
35
- return (tree, vfile) => {
36
- for (const name of values) {
37
- if (!(name in vfile.data)) return;
38
- tree.children.unshift(getMdastExport(name, vfile.data[name]));
39
- }
40
- };
41
- }
42
- function getMdastExport(name, value) {
43
- return {
44
- type: "mdxjsEsm",
45
- value: "",
46
- data: {
47
- estree: {
48
- type: "Program",
49
- sourceType: "module",
50
- body: [
51
- {
52
- type: "ExportNamedDeclaration",
53
- specifiers: [],
54
- source: null,
55
- declaration: {
56
- type: "VariableDeclaration",
57
- kind: "let",
58
- declarations: [
59
- {
60
- type: "VariableDeclarator",
61
- id: {
62
- type: "Identifier",
63
- name
64
- },
65
- init: (0, import_estree_util_value_to_estree.valueToEstree)(value)
66
- }
67
- ]
68
- }
69
- }
70
- ]
71
- }
72
- }
73
- };
74
- }
75
- var import_estree_util_value_to_estree;
76
- var init_remark_exports = __esm({
77
- "src/mdx-plugins/remark-exports.ts"() {
78
- "use strict";
79
- import_estree_util_value_to_estree = require("estree-util-value-to-estree");
80
- }
81
- });
82
-
83
- // src/utils/mdx-options.ts
84
- var mdx_options_exports = {};
85
- __export(mdx_options_exports, {
86
- getDefaultMDXOptions: () => getDefaultMDXOptions
87
- });
88
- function pluginOption(def, options = []) {
89
- const list = def(Array.isArray(options) ? options : []).filter(
90
- Boolean
91
- );
92
- if (typeof options === "function") {
93
- return options(list);
94
- }
95
- return list;
96
- }
97
- function getDefaultMDXOptions({
98
- valueToExport = [],
99
- rehypeCodeOptions,
100
- remarkImageOptions,
101
- remarkHeadingOptions,
102
- remarkStructureOptions,
103
- remarkCodeTabOptions,
104
- ...mdxOptions
105
- }) {
106
- const mdxExports = [
107
- "structuredData",
108
- "frontmatter",
109
- "lastModified",
110
- ...valueToExport
111
- ];
112
- const remarkPlugins = pluginOption(
113
- (v) => [
114
- plugins.remarkGfm,
115
- [
116
- plugins.remarkHeading,
117
- {
118
- generateToc: false,
119
- ...remarkHeadingOptions
120
- }
121
- ],
122
- remarkImageOptions !== false && [plugins.remarkImage, remarkImageOptions],
123
- // Fumadocs 14 compatibility
124
- "remarkCodeTab" in plugins && remarkCodeTabOptions !== false && plugins.remarkCodeTab,
125
- ...v,
126
- remarkStructureOptions !== false && [
127
- plugins.remarkStructure,
128
- remarkStructureOptions
129
- ],
130
- [remarkMdxExport, { values: mdxExports }]
131
- ],
132
- mdxOptions.remarkPlugins
133
- );
134
- const rehypePlugins = pluginOption(
135
- (v) => [
136
- rehypeCodeOptions !== false && [plugins.rehypeCode, rehypeCodeOptions],
137
- ...v,
138
- [plugins.rehypeToc]
139
- ],
140
- mdxOptions.rehypePlugins
141
- );
142
- return {
143
- ...mdxOptions,
144
- remarkPlugins,
145
- rehypePlugins
146
- };
147
- }
148
- var plugins;
149
- var init_mdx_options = __esm({
150
- "src/utils/mdx-options.ts"() {
151
- "use strict";
152
- plugins = __toESM(require("fumadocs-core/mdx-plugins"), 1);
153
- init_remark_exports();
154
- }
155
- });
156
-
157
33
  // src/map/watcher.ts
158
34
  var watcher_exports = {};
159
35
  __export(watcher_exports, {
@@ -199,8 +75,7 @@ module.exports = __toCommonJS(next_exports);
199
75
  var import_node_path2 = __toESM(require("path"), 1);
200
76
 
201
77
  // src/utils/config.ts
202
- var import_node_crypto = require("crypto");
203
- var fs = __toESM(require("fs"), 1);
78
+ var fs = __toESM(require("fs/promises"), 1);
204
79
  var path = __toESM(require("path"), 1);
205
80
  var import_node_url = require("url");
206
81
 
@@ -231,23 +106,11 @@ function buildConfig(config) {
231
106
  null
232
107
  ];
233
108
  }
234
- let cachedMdxOptions;
235
109
  return [
236
110
  null,
237
111
  {
238
112
  global: globalConfig,
239
113
  collections,
240
- async getDefaultMDXOptions() {
241
- if (cachedMdxOptions) return cachedMdxOptions;
242
- const { getDefaultMDXOptions: getDefaultMDXOptions2 } = await Promise.resolve().then(() => (init_mdx_options(), mdx_options_exports));
243
- const mdxOptions = globalConfig?.mdxOptions ?? {};
244
- if (typeof mdxOptions === "function") {
245
- cachedMdxOptions = getDefaultMDXOptions2(await mdxOptions());
246
- } else {
247
- cachedMdxOptions = getDefaultMDXOptions2(mdxOptions);
248
- }
249
- return cachedMdxOptions;
250
- },
251
114
  _runtime: {
252
115
  files: /* @__PURE__ */ new Map()
253
116
  }
@@ -259,13 +122,13 @@ function buildConfig(config) {
259
122
  function findConfigFile() {
260
123
  return path.resolve("source.config.ts");
261
124
  }
262
- var cache = /* @__PURE__ */ new Map();
263
- async function compileConfig(configPath) {
125
+ var cache = null;
126
+ async function compileConfig(configPath, outDir2) {
264
127
  const { build } = await import("esbuild");
265
128
  const transformed = await build({
266
129
  entryPoints: [{ in: configPath, out: "source.config" }],
267
130
  bundle: true,
268
- outdir: ".source",
131
+ outdir: outDir2,
269
132
  target: "node18",
270
133
  write: true,
271
134
  platform: "node",
@@ -281,13 +144,12 @@ async function compileConfig(configPath) {
281
144
  }
282
145
  }
283
146
  async function loadConfig(configPath, hash, build = false) {
284
- const cached = cache.get(configPath);
285
- if (cached && cached.hash === hash) {
286
- return await cached.config;
147
+ if (cache && cache.hash === hash) {
148
+ return await cache.config;
287
149
  }
288
- if (build) await compileConfig(configPath);
150
+ if (build) await compileConfig(configPath, ".source");
289
151
  const url = (0, import_node_url.pathToFileURL)(path.resolve(".source/source.config.mjs"));
290
- const config = import(`${url.href}?hash=${configPath}`).then((loaded) => {
152
+ const config = import(`${url.href}?hash=${hash}`).then((loaded) => {
291
153
  const [err, config2] = buildConfig(
292
154
  // every call to `loadConfig` will cause the previous cache to be ignored
293
155
  loaded
@@ -295,24 +157,24 @@ async function loadConfig(configPath, hash, build = false) {
295
157
  if (err !== null) throw new Error(err);
296
158
  return config2;
297
159
  });
298
- cache.set(configPath, { config, hash });
160
+ cache = { config, hash };
299
161
  return await config;
300
162
  }
301
163
  async function getConfigHash(configPath) {
302
- const hash = (0, import_node_crypto.createHash)("md5");
303
- const rs = fs.createReadStream(configPath);
304
- for await (const chunk of rs) {
305
- hash.update(chunk);
164
+ const stats = await fs.stat(configPath).catch(() => void 0);
165
+ if (stats) {
166
+ return stats.mtime.getTime().toString();
306
167
  }
307
- return hash.digest("hex");
168
+ throw new Error("Cannot find config file");
308
169
  }
309
170
 
310
171
  // src/map/index.ts
311
172
  var path3 = __toESM(require("path"), 1);
312
- var fs3 = __toESM(require("fs/promises"), 1);
173
+ var fs4 = __toESM(require("fs/promises"), 1);
313
174
 
314
175
  // src/map/generate.ts
315
176
  var path2 = __toESM(require("path"), 1);
177
+ var fs3 = __toESM(require("fs/promises"), 1);
316
178
  var import_fast_glob = __toESM(require("fast-glob"), 1);
317
179
 
318
180
  // src/utils/get-type-from-path.ts
@@ -325,8 +187,103 @@ function getTypeFromPath(path6) {
325
187
  if (metaTypes.includes(ext)) return "meta";
326
188
  }
327
189
 
190
+ // src/utils/schema.ts
191
+ var import_zod = require("zod");
192
+ var metaSchema = import_zod.z.object({
193
+ title: import_zod.z.string().optional(),
194
+ pages: import_zod.z.array(import_zod.z.string()).optional(),
195
+ description: import_zod.z.string().optional(),
196
+ root: import_zod.z.boolean().optional(),
197
+ defaultOpen: import_zod.z.boolean().optional(),
198
+ icon: import_zod.z.string().optional()
199
+ });
200
+ var frontmatterSchema = import_zod.z.object({
201
+ title: import_zod.z.string(),
202
+ description: import_zod.z.string().optional(),
203
+ icon: import_zod.z.string().optional(),
204
+ full: import_zod.z.boolean().optional(),
205
+ // Fumadocs OpenAPI generated
206
+ _openapi: import_zod.z.object({}).passthrough().optional()
207
+ });
208
+ async function validate(schema, data, context, errorMessage) {
209
+ if (typeof schema === "function" && !("~standard" in schema)) {
210
+ schema = schema(context);
211
+ }
212
+ if ("~standard" in schema) {
213
+ const result = await schema["~standard"].validate(
214
+ data
215
+ );
216
+ if (result.issues) {
217
+ throw new Error(formatError(errorMessage, result.issues));
218
+ }
219
+ return result.value;
220
+ }
221
+ return data;
222
+ }
223
+ function formatError(message, issues) {
224
+ return `${message}:
225
+ ${issues.map((issue) => ` ${issue.path}: ${issue.message}`).join("\n")}`;
226
+ }
227
+
228
+ // src/map/file-cache.ts
229
+ var map = /* @__PURE__ */ new Map();
230
+ var fileCache = {
231
+ read(namespace, path6) {
232
+ return map.get(`${namespace}.${path6}}`);
233
+ },
234
+ write(namespace, path6, data) {
235
+ map.set(`${namespace}.${path6}}`, data);
236
+ },
237
+ removeCache(path6) {
238
+ for (const key of map.keys()) {
239
+ const keyPath = key.slice(key.indexOf(".") + 1);
240
+ if (keyPath === path6) map.delete(key);
241
+ }
242
+ }
243
+ };
244
+
245
+ // src/utils/read-frontmatter.ts
246
+ var fs2 = __toESM(require("fs"), 1);
247
+ var import_gray_matter = __toESM(require("gray-matter"), 1);
248
+ async function readFrontmatter(file) {
249
+ const readStream = fs2.createReadStream(file, {
250
+ highWaterMark: 250
251
+ });
252
+ return new Promise((res, rej) => {
253
+ let idx = 0;
254
+ let str = "";
255
+ readStream.on("data", (_chunk) => {
256
+ const chunk = _chunk.toString();
257
+ if (idx === 0 && !chunk.startsWith("---")) {
258
+ res({});
259
+ readStream.close();
260
+ return;
261
+ }
262
+ str += chunk;
263
+ idx++;
264
+ if (str.includes("\n---")) {
265
+ res(
266
+ (0, import_gray_matter.default)({
267
+ content: str
268
+ }).data
269
+ );
270
+ readStream.close();
271
+ }
272
+ });
273
+ readStream.on("end", () => res({}));
274
+ readStream.on("error", (e) => rej(e));
275
+ });
276
+ }
277
+
328
278
  // src/map/generate.ts
329
- async function generateJS(configPath, config, outputPath, configHash, getFrontmatter) {
279
+ async function readFrontmatterWithCache(file) {
280
+ const cached = fileCache.read("read-frontmatter", file);
281
+ if (cached) return cached;
282
+ const res = await readFrontmatter(file);
283
+ fileCache.write("read-frontmatter", file, res);
284
+ return res;
285
+ }
286
+ async function generateJS(configPath, config, outputPath, configHash) {
330
287
  const outDir2 = path2.dirname(outputPath);
331
288
  let asyncInit = false;
332
289
  const lines = [
@@ -346,7 +303,27 @@ async function generateJS(configPath, config, outputPath, configHash, getFrontma
346
303
  async function getEntries(collectionName, collection, files) {
347
304
  const items = files.map(async (file, i) => {
348
305
  config._runtime.files.set(file.absolutePath, collectionName);
349
- const importId = `${collectionName}_${collection.type}_${i}`;
306
+ if (collection.type === "meta") {
307
+ const cached = fileCache.read("generate-js", file.absolutePath);
308
+ if (cached) return cached;
309
+ const source = (await fs3.readFile(file.absolutePath)).toString();
310
+ let data = JSON.parse(source);
311
+ if (collection?.schema) {
312
+ data = await validate(
313
+ collection.schema,
314
+ data,
315
+ {
316
+ source,
317
+ path: file.absolutePath
318
+ },
319
+ `invalid data in ${file.absolutePath}:`
320
+ );
321
+ }
322
+ const entry = `{ info: ${JSON.stringify(file)}, data: ${JSON.stringify(data)} }`;
323
+ fileCache.write("generate-js", file.absolutePath, entry);
324
+ return entry;
325
+ }
326
+ const importId = `${collectionName}_${i}`;
350
327
  lines.unshift(
351
328
  getImportCode({
352
329
  type: "namespace",
@@ -372,10 +349,9 @@ async function generateJS(configPath, config, outputPath, configHash, getFrontma
372
349
  asyncInit = true;
373
350
  }
374
351
  const entries2 = files.map(async (file) => {
375
- const frontmatter = await getFrontmatter(file.absolutePath);
376
352
  return JSON.stringify({
377
353
  info: file,
378
- data: frontmatter
354
+ data: await readFrontmatterWithCache(file.absolutePath)
379
355
  });
380
356
  });
381
357
  return Promise.all(entries2);
@@ -384,19 +360,16 @@ async function generateJS(configPath, config, outputPath, configHash, getFrontma
384
360
  if (collection.type === "docs") {
385
361
  const docs = await getCollectionFiles(collection.docs);
386
362
  const metas = await getCollectionFiles(collection.meta);
363
+ const metaEntries = (await getEntries(k, collection.meta, metas)).join(
364
+ ", "
365
+ );
387
366
  if (collection.docs.async) {
388
367
  const docsEntries2 = (await getAsyncEntries(docs)).join(", ");
389
- const metaEntries2 = (await getEntries(k, collection.meta, metas)).join(
390
- ", "
391
- );
392
- return `export const ${k} = _runtimeAsync.docs<typeof _source.${k}>([${docsEntries2}], [${metaEntries2}], "${k}", _sourceConfig)`;
368
+ return `export const ${k} = _runtimeAsync.docs<typeof _source.${k}>([${docsEntries2}], [${metaEntries}], "${k}", _sourceConfig)`;
393
369
  }
394
370
  const docsEntries = (await getEntries(k, collection.docs, docs)).join(
395
371
  ", "
396
372
  );
397
- const metaEntries = (await getEntries(k, collection.meta, metas)).join(
398
- ", "
399
- );
400
373
  return `export const ${k} = _runtime.docs<typeof _source.${k}>([${docsEntries}], [${metaEntries}])`;
401
374
  }
402
375
  const files = await getCollectionFiles(collection);
@@ -457,72 +430,23 @@ function toImportPath(file, dir) {
457
430
  return importPath.replaceAll(path2.sep, "/");
458
431
  }
459
432
 
460
- // src/utils/read-frontmatter.ts
461
- var fs2 = __toESM(require("fs"), 1);
462
- var import_gray_matter = __toESM(require("gray-matter"), 1);
463
- async function readFrontmatter(file) {
464
- const readStream = fs2.createReadStream(file, {
465
- highWaterMark: 250
466
- });
467
- return new Promise((res, rej) => {
468
- let idx = 0;
469
- let str = "";
470
- readStream.on("data", (_chunk) => {
471
- const chunk = _chunk.toString();
472
- if (idx === 0 && !chunk.startsWith("---")) {
473
- res({});
474
- readStream.close();
475
- return;
476
- }
477
- str += chunk;
478
- idx++;
479
- if (str.includes("\n---")) {
480
- res(
481
- (0, import_gray_matter.default)({
482
- content: str
483
- }).data
484
- );
485
- readStream.close();
486
- }
487
- });
488
- readStream.on("end", () => res({}));
489
- readStream.on("error", (e) => rej(e));
490
- });
491
- }
492
-
493
433
  // src/map/index.ts
494
434
  async function start(dev, configPath, outDir2) {
495
- void fs3.rm(path3.resolve(outDir2, `index.js`), { force: true });
496
- void fs3.rm(path3.resolve(outDir2, `index.d.ts`), { force: true });
497
- await fs3.mkdir(outDir2, { recursive: true });
435
+ void fs4.rm(path3.resolve(outDir2, `index.js`), { force: true });
436
+ void fs4.rm(path3.resolve(outDir2, `index.d.ts`), { force: true });
437
+ await fs4.mkdir(outDir2, { recursive: true });
498
438
  let configHash = await getConfigHash(configPath);
499
439
  let config = await loadConfig(configPath, configHash, true);
500
440
  const outPath = path3.resolve(outDir2, `index.ts`);
501
- const frontmatterCache = /* @__PURE__ */ new Map();
502
- let hookUpdate = false;
503
- async function readFrontmatterWithCache(file) {
504
- hookUpdate = true;
505
- const cached = frontmatterCache.get(file);
506
- if (cached) return cached;
507
- const res = await readFrontmatter(file);
508
- frontmatterCache.set(file, res);
509
- return res;
510
- }
511
441
  async function updateMapFile() {
512
- await fs3.writeFile(
442
+ console.time(`[MDX] update map file`);
443
+ await fs4.writeFile(
513
444
  outPath,
514
- await generateJS(
515
- configPath,
516
- config,
517
- outPath,
518
- configHash,
519
- readFrontmatterWithCache
520
- )
445
+ await generateJS(configPath, config, outPath, configHash)
521
446
  );
447
+ console.timeEnd(`[MDX] update map file`);
522
448
  }
523
- console.time(`[MDX] initialize map file`);
524
449
  await updateMapFile();
525
- console.timeEnd(`[MDX] initialize map file`);
526
450
  if (dev) {
527
451
  const { watcher: watcher2 } = await Promise.resolve().then(() => (init_watcher(), watcher_exports));
528
452
  const instance = watcher2(configPath, config);
@@ -531,17 +455,15 @@ async function start(dev, configPath, outDir2) {
531
455
  });
532
456
  instance.on("all", (event, file) => {
533
457
  if (typeof file !== "string") return;
458
+ const absolutePath = path3.resolve(file);
534
459
  const onUpdate = async () => {
535
- const isConfigFile = path3.resolve(file) === configPath;
460
+ const isConfigFile = absolutePath === configPath;
536
461
  if (isConfigFile) {
537
462
  configHash = await getConfigHash(configPath);
538
463
  config = await loadConfig(configPath, configHash, true);
539
464
  }
540
- if (isConfigFile || event !== "change" || hookUpdate) {
541
- if (event === "change") frontmatterCache.delete(file);
542
- await updateMapFile();
543
- console.log("[MDX] Updated map file");
544
- }
465
+ if (event === "change") fileCache.removeCache(absolutePath);
466
+ await updateMapFile();
545
467
  };
546
468
  void onUpdate();
547
469
  });
@@ -615,20 +537,19 @@ function createMDX({
615
537
 
616
538
  // src/postinstall.ts
617
539
  var path5 = __toESM(require("path"), 1);
618
- var fs4 = __toESM(require("fs"), 1);
540
+ var fs5 = __toESM(require("fs"), 1);
619
541
  async function postInstall(configPath = findConfigFile()) {
620
542
  const jsOut = path5.resolve(".source/index.ts");
621
543
  const hash = await getConfigHash(configPath);
622
544
  const config = await loadConfig(configPath, hash, true);
623
- fs4.mkdirSync(path5.dirname(jsOut), { recursive: true });
624
- fs4.writeFileSync(
545
+ fs5.mkdirSync(path5.dirname(jsOut), { recursive: true });
546
+ fs5.writeFileSync(
625
547
  jsOut,
626
548
  await generateJS(
627
549
  configPath,
628
550
  config,
629
551
  path5.resolve(".source/index.ts"),
630
- hash,
631
- readFrontmatter
552
+ hash
632
553
  )
633
554
  );
634
555
  console.log("[MDX] types generated");