fumadocs-mdx 12.0.3 → 13.0.1

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.
Files changed (80) hide show
  1. package/dist/bin.cjs +1116 -359
  2. package/dist/bin.js +4 -4
  3. package/dist/build-mdx-CCNr86q6.d.ts +53 -0
  4. package/dist/build-mdx-D-r3_eQL.d.cts +53 -0
  5. package/dist/bun/index.cjs +196 -52
  6. package/dist/bun/index.d.cts +8 -3
  7. package/dist/bun/index.d.ts +8 -3
  8. package/dist/bun/index.js +19 -10
  9. package/dist/{chunk-QAUWMR5D.js → chunk-3J3WL7WN.js} +23 -5
  10. package/dist/{chunk-6Y5JDZHD.js → chunk-CXA4JO4Z.js} +1 -21
  11. package/dist/chunk-EELYB2XC.js +207 -0
  12. package/dist/{chunk-46UPKP5R.js → chunk-II3H5ZVZ.js} +5 -5
  13. package/dist/{chunk-LGYVNESJ.js → chunk-JVZFH6ND.js} +6 -22
  14. package/dist/{chunk-LMG6UWCL.js → chunk-K5ZLPEIQ.js} +56 -16
  15. package/dist/{chunk-OMAMTKDE.js → chunk-KILFIBVW.js} +3 -12
  16. package/dist/chunk-NVRDCY6Z.js +30 -0
  17. package/dist/{chunk-RMDXSZYE.js → chunk-XQ5O7IPO.js} +31 -24
  18. package/dist/chunk-XZY2AWJI.js +81 -0
  19. package/dist/{chunk-VXEBLM4X.js → chunk-YVCR6FUH.js} +1 -1
  20. package/dist/config/index.cjs +56 -16
  21. package/dist/config/index.d.cts +2 -1
  22. package/dist/config/index.d.ts +2 -1
  23. package/dist/config/index.js +1 -1
  24. package/dist/{define-DJbJduHy.d.ts → core-B6j6Fxse.d.cts} +89 -2
  25. package/dist/{define-DJbJduHy.d.cts → core-B6j6Fxse.d.ts} +89 -2
  26. package/dist/index.cjs +0 -109
  27. package/dist/index.d.cts +75 -9
  28. package/dist/index.d.ts +75 -9
  29. package/dist/index.js +0 -11
  30. package/dist/{load-UUXLUBHL.js → load-MNG3CLET.js} +1 -3
  31. package/dist/next/index.cjs +298 -234
  32. package/dist/next/index.d.cts +2 -11
  33. package/dist/next/index.d.ts +2 -11
  34. package/dist/next/index.js +177 -141
  35. package/dist/node/loader.cjs +228 -85
  36. package/dist/node/loader.js +19 -9
  37. package/dist/plugins/json-schema.cjs +162 -0
  38. package/dist/plugins/json-schema.d.cts +24 -0
  39. package/dist/plugins/json-schema.d.ts +24 -0
  40. package/dist/plugins/json-schema.js +78 -0
  41. package/dist/runtime/next/async.cjs +108 -70
  42. package/dist/runtime/next/async.d.cts +9 -6
  43. package/dist/runtime/next/async.d.ts +9 -6
  44. package/dist/runtime/next/async.js +8 -18
  45. package/dist/runtime/next/index.cjs +25 -14
  46. package/dist/runtime/next/index.d.cts +11 -8
  47. package/dist/runtime/next/index.d.ts +11 -8
  48. package/dist/runtime/next/index.js +2 -2
  49. package/dist/runtime/vite/browser.cjs +7 -3
  50. package/dist/runtime/vite/browser.d.cts +56 -7
  51. package/dist/runtime/vite/browser.d.ts +56 -7
  52. package/dist/runtime/vite/browser.js +2 -1
  53. package/dist/runtime/vite/server.cjs +40 -34
  54. package/dist/runtime/vite/server.d.cts +13 -10
  55. package/dist/runtime/vite/server.d.ts +13 -10
  56. package/dist/runtime/vite/server.js +8 -23
  57. package/dist/{types-TeHjsmja.d.ts → types-AGzTfBmf.d.ts} +3 -10
  58. package/dist/{types-BRx1QsIJ.d.cts → types-DKGMoay5.d.cts} +3 -10
  59. package/dist/vite/index.cjs +443 -249
  60. package/dist/vite/index.d.cts +23 -10
  61. package/dist/vite/index.d.ts +23 -10
  62. package/dist/vite/index.js +213 -36
  63. package/dist/{loader-mdx.cjs → webpack/index.cjs} +268 -82
  64. package/dist/{loader-mdx.d.ts → webpack/index.d.cts} +1 -0
  65. package/dist/{loader-mdx.d.cts → webpack/index.d.ts} +1 -0
  66. package/dist/webpack/index.js +44 -0
  67. package/loader-mdx.cjs +1 -1
  68. package/package.json +30 -16
  69. package/dist/browser-BupUnhpC.d.ts +0 -98
  70. package/dist/browser-R0x9IPaQ.d.cts +0 -98
  71. package/dist/chunk-ADR6R7HM.js +0 -29
  72. package/dist/chunk-IQAEAI4P.js +0 -66
  73. package/dist/chunk-XMFLD5J6.js +0 -30
  74. package/dist/chunk-ZLCSVXCD.js +0 -10
  75. package/dist/chunk-ZX7TM4AR.js +0 -127
  76. package/dist/loader-mdx.js +0 -25
  77. package/dist/postinstall-SCSXM4IM.js +0 -10
  78. package/dist/shared-CfiiRctw.d.ts +0 -70
  79. package/dist/shared-fFqiuWJC.d.cts +0 -70
  80. package/dist/watcher-HGOH3APP.js +0 -22
@@ -173,6 +173,10 @@ var init_build = __esm({
173
173
  });
174
174
 
175
175
  // src/loaders/config/load.ts
176
+ var load_exports = {};
177
+ __export(load_exports, {
178
+ loadConfig: () => loadConfig
179
+ });
176
180
  async function compileConfig(configPath, outDir) {
177
181
  const { build } = await import("esbuild");
178
182
  const transformed = await build({
@@ -193,30 +197,22 @@ async function compileConfig(configPath, outDir) {
193
197
  throw new Error("failed to compile configuration file");
194
198
  }
195
199
  }
196
- async function loadConfig(configPath, outDir, hash, build = false) {
197
- if (cache3 && cache3.hash === hash) {
198
- return await cache3.config;
199
- }
200
+ async function loadConfig(configPath, outDir, build = false) {
200
201
  if (build) await compileConfig(configPath, outDir);
201
- const url = (0, import_node_url2.pathToFileURL)(path7.resolve(outDir, "source.config.mjs"));
202
- const config = import(`${url.href}?hash=${hash}`).then((loaded) => {
203
- return buildConfig(
204
- // every call to `loadConfig` will cause the previous cache to be ignored
205
- loaded
206
- );
207
- });
208
- if (hash) cache3 = { config, hash };
202
+ const url = (0, import_node_url3.pathToFileURL)(path11.resolve(outDir, "source.config.mjs"));
203
+ url.searchParams.set("hash", Date.now().toString());
204
+ const config = import(url.href).then(
205
+ (loaded) => buildConfig(loaded)
206
+ );
209
207
  return await config;
210
208
  }
211
- var fs4, path7, import_node_url2, cache3;
209
+ var path11, import_node_url3;
212
210
  var init_load = __esm({
213
211
  "src/loaders/config/load.ts"() {
214
212
  "use strict";
215
- fs4 = __toESM(require("fs/promises"), 1);
216
- path7 = __toESM(require("path"), 1);
217
- import_node_url2 = require("url");
213
+ path11 = __toESM(require("path"), 1);
214
+ import_node_url3 = require("url");
218
215
  init_build();
219
- cache3 = null;
220
216
  }
221
217
  });
222
218
 
@@ -270,142 +266,9 @@ async function validate(schema, data, context, errorMessage) {
270
266
  }
271
267
 
272
268
  // src/vite/index.ts
273
- var fs6 = __toESM(require("fs/promises"), 1);
274
- var path9 = __toESM(require("path"), 1);
269
+ var path12 = __toESM(require("path"), 1);
275
270
  var import_js_yaml2 = require("js-yaml");
276
271
 
277
- // src/utils/import-formatter.ts
278
- var import_node_path = __toESM(require("path"), 1);
279
- function toImportPath(file, config) {
280
- const ext = import_node_path.default.extname(file);
281
- let filename;
282
- if (ext === ".ts" && config.jsExtension) {
283
- filename = file.substring(0, file.length - ext.length) + ".js";
284
- } else if (ext === ".ts") {
285
- filename = file.substring(0, file.length - ext.length);
286
- } else {
287
- filename = file;
288
- }
289
- let importPath;
290
- if ("relativeTo" in config) {
291
- importPath = import_node_path.default.relative(config.relativeTo, filename);
292
- if (!import_node_path.default.isAbsolute(importPath) && !importPath.startsWith(".")) {
293
- importPath = `./${importPath}`;
294
- }
295
- } else {
296
- importPath = import_node_path.default.resolve(filename);
297
- }
298
- return importPath.replaceAll(import_node_path.default.sep, "/");
299
- }
300
- function ident(code, tab = 1) {
301
- return code.split("\n").map((v) => " ".repeat(tab) + v).join("\n");
302
- }
303
-
304
- // src/vite/generate-glob.ts
305
- function generateGlob(name, patterns, globOptions) {
306
- const options = {
307
- ...globOptions,
308
- query: {
309
- ...globOptions?.query,
310
- collection: name
311
- }
312
- };
313
- return `import.meta.glob(${JSON.stringify(mapGlobPatterns(patterns))}, ${JSON.stringify(options, null, 2)})`;
314
- }
315
- function mapGlobPatterns(patterns) {
316
- return patterns.map(enforceRelative);
317
- }
318
- function enforceRelative(file) {
319
- if (file.startsWith("./")) return file;
320
- if (file.startsWith("/")) return `.${file}`;
321
- return `./${file}`;
322
- }
323
- function getGlobBase(collection) {
324
- let dir = collection.dir;
325
- if (Array.isArray(dir)) {
326
- if (dir.length !== 1)
327
- throw new Error(
328
- `[Fumadocs MDX] Vite Plugin doesn't support multiple \`dir\` for a collection at the moment.`
329
- );
330
- dir = dir[0];
331
- }
332
- return enforceRelative(dir);
333
- }
334
-
335
- // src/utils/collections.ts
336
- function getSupportedFormats(collection) {
337
- return {
338
- doc: ["mdx", "md"],
339
- meta: ["json", "yaml"]
340
- }[collection.type];
341
- }
342
- function getGlobPatterns(collection) {
343
- if (collection.files) return collection.files;
344
- return [`**/*.{${getSupportedFormats(collection).join(",")}}`];
345
- }
346
-
347
- // src/vite/generate.ts
348
- function docs(name, collection) {
349
- const obj = [
350
- ident(`doc: ${doc(name, collection.docs)}`),
351
- ident(`meta: ${meta(name, collection.meta)}`)
352
- ].join(",\n");
353
- return `{
354
- ${obj}
355
- }`;
356
- }
357
- function doc(name, collection) {
358
- const patterns = getGlobPatterns(collection);
359
- const base = getGlobBase(collection);
360
- const docGlob = generateGlob(name, patterns, {
361
- base
362
- });
363
- if (collection.async) {
364
- const headBlob = generateGlob(name, patterns, {
365
- query: {
366
- only: "frontmatter"
367
- },
368
- import: "frontmatter",
369
- base
370
- });
371
- return `create.docLazy("${name}", "${base}", ${headBlob}, ${docGlob})`;
372
- }
373
- return `create.doc("${name}", "${base}", ${docGlob})`;
374
- }
375
- function meta(name, collection) {
376
- const patterns = getGlobPatterns(collection);
377
- const base = getGlobBase(collection);
378
- return `create.meta("${name}", "${base}", ${generateGlob(name, patterns, {
379
- import: "default",
380
- base
381
- })})`;
382
- }
383
- function entry(configPath, config, outDir, jsExtension) {
384
- const lines = [
385
- '/// <reference types="vite/client" />',
386
- `import { fromConfig } from 'fumadocs-mdx/runtime/vite';`,
387
- `import type * as Config from '${toImportPath(configPath, {
388
- relativeTo: outDir,
389
- jsExtension
390
- })}';`,
391
- "",
392
- `export const create = fromConfig<typeof Config>();`
393
- ];
394
- for (const [name, collection] of config.collections.entries()) {
395
- let body;
396
- if (collection.type === "docs") {
397
- body = docs(name, collection);
398
- } else if (collection.type === "meta") {
399
- body = meta(name, collection);
400
- } else {
401
- body = doc(name, collection);
402
- }
403
- lines.push("");
404
- lines.push(`export const ${name} = ${body};`);
405
- }
406
- return lines.join("\n");
407
- }
408
-
409
272
  // src/utils/fuma-matter.ts
410
273
  var import_js_yaml = require("js-yaml");
411
274
  var regex = /^---\r?\n(.+?)\r?\n---\r?\n/s;
@@ -423,7 +286,7 @@ function fumaMatter(input) {
423
286
  }
424
287
 
425
288
  // src/utils/git-timestamp.ts
426
- var import_node_path2 = __toESM(require("path"), 1);
289
+ var import_node_path = __toESM(require("path"), 1);
427
290
  var import_tinyexec = require("tinyexec");
428
291
  var cache = /* @__PURE__ */ new Map();
429
292
  async function getGitTimestamp(file) {
@@ -432,7 +295,7 @@ async function getGitTimestamp(file) {
432
295
  try {
433
296
  const out = await (0, import_tinyexec.x)(
434
297
  "git",
435
- ["log", "-1", '--pretty="%ai"', import_node_path2.default.relative(process.cwd(), file)],
298
+ ["log", "-1", '--pretty="%ai"', import_node_path.default.relative(process.cwd(), file)],
436
299
  {
437
300
  throwOnError: true
438
301
  }
@@ -450,12 +313,56 @@ var import_mdx = require("@mdx-js/mdx");
450
313
 
451
314
  // src/loaders/mdx/remark-include.ts
452
315
  var import_unified = require("unified");
453
- var import_unist_util_visit = require("unist-util-visit");
454
- var path3 = __toESM(require("path"), 1);
316
+ var import_unist_util_visit2 = require("unist-util-visit");
317
+ var path2 = __toESM(require("path"), 1);
455
318
  var fs = __toESM(require("fs/promises"), 1);
456
- var import_remark_parse = __toESM(require("remark-parse"), 1);
457
- var import_remark_mdx = __toESM(require("remark-mdx"), 1);
458
319
  var import_mdx_plugins = require("fumadocs-core/mdx-plugins");
320
+
321
+ // src/loaders/mdx/remark-unravel.ts
322
+ var import_unist_util_visit = require("unist-util-visit");
323
+ function remarkMarkAndUnravel() {
324
+ return (tree) => {
325
+ (0, import_unist_util_visit.visit)(tree, function(node, index, parent) {
326
+ let offset = -1;
327
+ let all = true;
328
+ let oneOrMore = false;
329
+ if (parent && typeof index === "number" && node.type === "paragraph") {
330
+ const children = node.children;
331
+ while (++offset < children.length) {
332
+ const child = children[offset];
333
+ if (child.type === "mdxJsxTextElement" || child.type === "mdxTextExpression") {
334
+ oneOrMore = true;
335
+ } else if (child.type === "text" && child.value.trim().length === 0) {
336
+ } else {
337
+ all = false;
338
+ break;
339
+ }
340
+ }
341
+ if (all && oneOrMore) {
342
+ offset = -1;
343
+ const newChildren = [];
344
+ while (++offset < children.length) {
345
+ const child = children[offset];
346
+ if (child.type === "mdxJsxTextElement") {
347
+ child.type = "mdxJsxFlowElement";
348
+ }
349
+ if (child.type === "mdxTextExpression") {
350
+ child.type = "mdxFlowExpression";
351
+ }
352
+ if (child.type === "text" && /^[\t\r\n ]+$/.test(String(child.value))) {
353
+ } else {
354
+ newChildren.push(child);
355
+ }
356
+ }
357
+ parent.children.splice(index, 1, ...newChildren);
358
+ return index;
359
+ }
360
+ }
361
+ });
362
+ };
363
+ }
364
+
365
+ // src/loaders/mdx/remark-include.ts
459
366
  var ElementLikeTypes = [
460
367
  "mdxJsxFlowElement",
461
368
  "mdxJsxTextElement",
@@ -495,7 +402,7 @@ function parseSpecifier(specifier) {
495
402
  function extractSection(root, section) {
496
403
  let nodes;
497
404
  let capturingHeadingContent = false;
498
- (0, import_unist_util_visit.visit)(root, (node) => {
405
+ (0, import_unist_util_visit2.visit)(root, (node) => {
499
406
  if (node.type === "heading") {
500
407
  if (capturingHeadingContent) {
501
408
  return false;
@@ -527,7 +434,7 @@ function extractSection(root, section) {
527
434
  }
528
435
  function remarkInclude() {
529
436
  const TagName = "include";
530
- async function embedContent(file, heading, params, data) {
437
+ const embedContent = async (file, heading, params, data) => {
531
438
  let content;
532
439
  try {
533
440
  content = (await fs.readFile(file)).toString();
@@ -538,7 +445,7 @@ ${e instanceof Error ? e.message : String(e)}`,
538
445
  { cause: e }
539
446
  );
540
447
  }
541
- const ext = path3.extname(file);
448
+ const ext = path2.extname(file);
542
449
  data._compiler?.addDependency(file);
543
450
  if (params.lang || ext !== ".md" && ext !== ".mdx") {
544
451
  const lang = params.lang ?? ext.slice(1);
@@ -550,18 +457,17 @@ ${e instanceof Error ? e.message : String(e)}`,
550
457
  data: {}
551
458
  };
552
459
  }
553
- const parser = (data._getProcessor ?? getDefaultProcessor)(
554
- ext === ".mdx" ? "mdx" : "md"
555
- );
460
+ const parser = data._getProcessor ? data._getProcessor(ext === ".mdx" ? "mdx" : "md") : this;
556
461
  const parsed = fumaMatter(content);
557
462
  let mdast = parser.parse({
558
463
  path: file,
559
464
  value: parsed.content,
560
465
  data: { frontmatter: parsed.data }
561
466
  });
467
+ const baseProcessor = (0, import_unified.unified)().use(remarkMarkAndUnravel);
562
468
  if (heading) {
563
469
  const extracted = extractSection(
564
- await (0, import_unified.unified)().use(import_mdx_plugins.remarkHeading).run(mdast),
470
+ await baseProcessor.use(import_mdx_plugins.remarkHeading).run(mdast),
565
471
  heading
566
472
  );
567
473
  if (!extracted)
@@ -569,20 +475,22 @@ ${e instanceof Error ? e.message : String(e)}`,
569
475
  `Cannot find section ${heading} in ${file}, make sure you have encapsulated the section in a <section id="${heading}"> tag, or a :::section directive with remark-directive configured.`
570
476
  );
571
477
  mdast = extracted;
478
+ } else {
479
+ mdast = await baseProcessor.run(mdast);
572
480
  }
573
- await update(mdast, path3.dirname(file), data);
481
+ await update(mdast, path2.dirname(file), data);
574
482
  return mdast;
575
- }
483
+ };
576
484
  async function update(tree, directory, data) {
577
485
  const queue = [];
578
- (0, import_unist_util_visit.visit)(tree, ElementLikeTypes, (_node, _, parent) => {
486
+ (0, import_unist_util_visit2.visit)(tree, ElementLikeTypes, (_node, _, parent) => {
579
487
  const node = _node;
580
488
  if (node.name !== TagName) return;
581
489
  const specifier = flattenNode(node);
582
490
  if (specifier.length === 0) return "skip";
583
491
  const attributes = parseElementAttributes(node);
584
492
  const { file: relativePath, section } = parseSpecifier(specifier);
585
- const file = path3.resolve(
493
+ const file = path2.resolve(
586
494
  "cwd" in attributes ? process.cwd() : directory,
587
495
  relativePath
588
496
  );
@@ -599,27 +507,31 @@ ${e instanceof Error ? e.message : String(e)}`,
599
507
  await Promise.all(queue);
600
508
  }
601
509
  return async (tree, file) => {
602
- await update(tree, path3.dirname(file.path), file.data);
510
+ await update(tree, path2.dirname(file.path), file.data);
603
511
  };
604
512
  }
605
- function getDefaultProcessor(format) {
606
- const mdProcessor = (0, import_unified.unified)().use(import_remark_parse.default);
607
- if (format === "md") return mdProcessor;
608
- return mdProcessor.use(import_remark_mdx.default);
609
- }
610
513
 
611
514
  // src/loaders/mdx/remark-postprocess.ts
612
- var import_unist_util_visit2 = require("unist-util-visit");
515
+ var import_unist_util_visit3 = require("unist-util-visit");
613
516
  var import_mdast_util_to_markdown = require("mdast-util-to-markdown");
614
517
  var import_estree_util_value_to_estree = require("estree-util-value-to-estree");
518
+ var import_unist_util_remove_position = require("unist-util-remove-position");
519
+ var import_remark_mdx = __toESM(require("remark-mdx"), 1);
615
520
  function remarkPostprocess({
521
+ _format,
616
522
  includeProcessedMarkdown = false,
523
+ includeMDAST = false,
617
524
  valueToExport = []
618
- } = {}) {
525
+ }) {
526
+ let _stringifyProcessor;
527
+ const getStringifyProcessor = () => {
528
+ if (_format === "mdx") return this;
529
+ return _stringifyProcessor ??= this().use(import_remark_mdx.default).freeze();
530
+ };
619
531
  return (tree, file) => {
620
532
  let title;
621
533
  const urls = [];
622
- (0, import_unist_util_visit2.visit)(tree, ["heading", "link"], (node) => {
534
+ (0, import_unist_util_visit3.visit)(tree, ["heading", "link"], (node) => {
623
535
  if (node.type === "heading" && node.depth === 1) {
624
536
  title = flattenNode2(node);
625
537
  }
@@ -635,12 +547,19 @@ function remarkPostprocess({
635
547
  }
636
548
  file.data.extractedReferences = urls;
637
549
  if (includeProcessedMarkdown) {
550
+ const processor = getStringifyProcessor();
638
551
  file.data._markdown = (0, import_mdast_util_to_markdown.toMarkdown)(tree, {
639
- ...this.data("settings"),
552
+ ...processor.data("settings"),
640
553
  // from https://github.com/remarkjs/remark/blob/main/packages/remark-stringify/lib/index.js
641
- extensions: this.data("toMarkdownExtensions") || []
554
+ extensions: processor.data("toMarkdownExtensions") || []
642
555
  });
643
556
  }
557
+ if (includeMDAST) {
558
+ const options = includeMDAST === true ? {} : includeMDAST;
559
+ file.data._mdast = JSON.stringify(
560
+ options.removePosition ? (0, import_unist_util_remove_position.removePosition)(structuredClone(tree)) : tree
561
+ );
562
+ }
644
563
  for (const { name, value } of file.data["mdx-export"] ?? []) {
645
564
  tree.children.unshift(getMdastExport(name, value));
646
565
  }
@@ -708,6 +627,7 @@ async function buildMDX(cacheKey, source, options) {
708
627
  [
709
628
  remarkPostprocess,
710
629
  {
630
+ _format: format,
711
631
  ...options.postprocess,
712
632
  valueToExport: [
713
633
  ...options.postprocess?.valueToExport ?? [],
@@ -715,7 +635,8 @@ async function buildMDX(cacheKey, source, options) {
715
635
  "extractedReferences",
716
636
  "frontmatter",
717
637
  "lastModified",
718
- "_markdown"
638
+ "_markdown",
639
+ "_mdast"
719
640
  ]
720
641
  }
721
642
  ]
@@ -743,14 +664,11 @@ async function buildMDX(cacheKey, source, options) {
743
664
  // src/loaders/mdx/index.ts
744
665
  var import_zod = require("zod");
745
666
  var import_promises = __toESM(require("fs/promises"), 1);
746
- var import_node_path3 = __toESM(require("path"), 1);
667
+ var import_node_path2 = __toESM(require("path"), 1);
747
668
  var import_node_crypto = require("crypto");
748
669
  var querySchema = import_zod.z.object({
749
670
  only: import_zod.z.literal(["frontmatter", "all"]).default("all"),
750
- collection: import_zod.z.string().optional(),
751
- hash: import_zod.z.string().describe(
752
- "the hash of config, used for revalidation on Turbopack/Webpack."
753
- ).optional()
671
+ collection: import_zod.z.string().optional()
754
672
  }).loose();
755
673
  var cacheEntry = import_zod.z.object({
756
674
  code: import_zod.z.string(),
@@ -767,14 +685,25 @@ function createMdxLoader(configLoader) {
767
685
  }) => {
768
686
  const matter = fumaMatter(value);
769
687
  const parsed = querySchema.parse(query);
770
- const loaded = await configLoader.getConfig(parsed.hash);
771
- const cacheDir = isDevelopment ? void 0 : loaded.global.experimentalBuildCache;
772
- const cacheKey = `${parsed.hash}_${parsed.collection ?? "global"}_${generateCacheHash(filePath)}`;
773
- if (cacheDir) {
774
- const cached = await import_promises.default.readFile(import_node_path3.default.join(cacheDir, cacheKey)).then((content) => cacheEntry.parse(JSON.parse(content.toString()))).catch(() => null);
688
+ const config = await configLoader.getConfig();
689
+ let after;
690
+ if (!isDevelopment && config.global.experimentalBuildCache) {
691
+ const cacheDir = config.global.experimentalBuildCache;
692
+ const cacheKey = `${parsed.hash}_${parsed.collection ?? "global"}_${generateCacheHash(filePath)}`;
693
+ const cached = await import_promises.default.readFile(import_node_path2.default.join(cacheDir, cacheKey)).then((content) => cacheEntry.parse(JSON.parse(content.toString()))).catch(() => null);
775
694
  if (cached && cached.hash === generateCacheHash(value)) return cached;
695
+ after = async () => {
696
+ await import_promises.default.mkdir(cacheDir, { recursive: true });
697
+ await import_promises.default.writeFile(
698
+ import_node_path2.default.join(cacheDir, cacheKey),
699
+ JSON.stringify({
700
+ ...out,
701
+ hash: generateCacheHash(value)
702
+ })
703
+ );
704
+ };
776
705
  }
777
- const collection = parsed.collection ? loaded.collections.get(parsed.collection) : void 0;
706
+ const collection = parsed.collection ? config.collections.get(parsed.collection) : void 0;
778
707
  let docCollection;
779
708
  switch (collection?.type) {
780
709
  case "doc":
@@ -802,16 +731,16 @@ function createMdxLoader(configLoader) {
802
731
  };
803
732
  }
804
733
  const data = {};
805
- if (loaded.global.lastModifiedTime === "git") {
734
+ if (config.global.lastModifiedTime === "git") {
806
735
  data.lastModified = (await getGitTimestamp(filePath))?.getTime();
807
736
  }
808
737
  const lineOffset = isDevelopment ? countLines(matter.matter) : 0;
809
738
  const compiled = await buildMDX(
810
- `${parsed.hash ?? ""}:${parsed.collection ?? "global"}`,
739
+ `${getConfigHash(config)}:${parsed.collection ?? "global"}`,
811
740
  "\n".repeat(lineOffset) + matter.content,
812
741
  {
813
742
  development: isDevelopment,
814
- ...docCollection?.mdxOptions ?? await loaded.getDefaultMDXOptions(),
743
+ ...docCollection?.mdxOptions ?? await config.getDefaultMDXOptions(),
815
744
  postprocess: docCollection?.postprocess,
816
745
  data,
817
746
  filePath,
@@ -823,19 +752,18 @@ function createMdxLoader(configLoader) {
823
752
  code: String(compiled.value),
824
753
  map: compiled.map
825
754
  };
826
- if (cacheDir) {
827
- await import_promises.default.mkdir(cacheDir, { recursive: true });
828
- await import_promises.default.writeFile(
829
- import_node_path3.default.join(cacheDir, cacheKey),
830
- JSON.stringify({
831
- ...out,
832
- hash: generateCacheHash(value)
833
- })
834
- );
835
- }
755
+ await after?.();
836
756
  return out;
837
757
  };
838
758
  }
759
+ var hashes = /* @__PURE__ */ new WeakMap();
760
+ function getConfigHash(config) {
761
+ let hash = hashes.get(config);
762
+ if (hash) return hash;
763
+ hash = Date.now().toString();
764
+ hashes.set(config, hash);
765
+ return hash;
766
+ }
839
767
  function generateCacheHash(input) {
840
768
  return (0, import_node_crypto.createHash)("md5").update(input).digest("hex");
841
769
  }
@@ -848,23 +776,17 @@ function countLines(s) {
848
776
  }
849
777
 
850
778
  // src/loaders/config/index.ts
851
- var import_node_path4 = __toESM(require("path"), 1);
779
+ var import_node_path3 = __toESM(require("path"), 1);
780
+ var import_promises2 = __toESM(require("fs/promises"), 1);
852
781
  function findConfigFile() {
853
- return import_node_path4.default.resolve("source.config.ts");
854
- }
855
- function resolvedConfig(loaded) {
856
- return {
857
- getConfig() {
858
- return loaded;
859
- }
860
- };
782
+ return import_node_path3.default.resolve("source.config.ts");
861
783
  }
862
784
 
863
785
  // src/loaders/adapter.ts
864
786
  var import_node_url = require("url");
865
- var import_promises2 = __toESM(require("fs/promises"), 1);
787
+ var import_promises3 = __toESM(require("fs/promises"), 1);
866
788
  var import_node_querystring = require("querystring");
867
- var import_node_path5 = __toESM(require("path"), 1);
789
+ var import_node_path4 = __toESM(require("path"), 1);
868
790
  function toVite(loader) {
869
791
  return async function(file, query, value) {
870
792
  const result = await loader({
@@ -885,37 +807,277 @@ function toVite(loader) {
885
807
  };
886
808
  }
887
809
 
888
- // src/vite/postinstall.ts
889
- init_load();
890
- var import_promises3 = __toESM(require("fs/promises"), 1);
810
+ // src/utils/import-formatter.ts
811
+ var import_node_path5 = __toESM(require("path"), 1);
812
+ function toImportPath(file, config) {
813
+ const ext = import_node_path5.default.extname(file);
814
+ let filename;
815
+ if (ext === ".ts" && config.jsExtension) {
816
+ filename = file.substring(0, file.length - ext.length) + ".js";
817
+ } else if (ext === ".ts") {
818
+ filename = file.substring(0, file.length - ext.length);
819
+ } else {
820
+ filename = file;
821
+ }
822
+ let importPath;
823
+ if ("relativeTo" in config) {
824
+ importPath = import_node_path5.default.relative(config.relativeTo, filename);
825
+ if (!import_node_path5.default.isAbsolute(importPath) && !importPath.startsWith(".")) {
826
+ importPath = `./${importPath}`;
827
+ }
828
+ } else {
829
+ importPath = import_node_path5.default.resolve(filename);
830
+ }
831
+ return importPath.replaceAll(import_node_path5.default.sep, "/");
832
+ }
833
+ function ident(code, tab = 1) {
834
+ return code.split("\n").map((v) => " ".repeat(tab) + v).join("\n");
835
+ }
836
+
837
+ // src/utils/collections.ts
838
+ var import_picomatch = __toESM(require("picomatch"), 1);
839
+ var import_tinyglobby = require("tinyglobby");
891
840
  var import_node_path6 = __toESM(require("path"), 1);
892
- async function postInstall(configPath = findConfigFile(), outDir, addJsExtension = false) {
893
- const config = await loadConfig(configPath, "node_modules", void 0, true);
894
- const outFile = "source.generated.ts";
895
- if (outDir) {
896
- await import_promises3.default.mkdir(outDir, { recursive: true });
841
+ var SupportedFormats = {
842
+ doc: ["mdx", "md"],
843
+ meta: ["json", "yaml"]
844
+ };
845
+ function getGlobPatterns(collection) {
846
+ if (collection.files) return collection.files;
847
+ return [`**/*.{${SupportedFormats[collection.type].join(",")}}`];
848
+ }
849
+
850
+ // src/utils/glob-import.ts
851
+ var import_tinyglobby2 = require("tinyglobby");
852
+ var import_node_path7 = __toESM(require("path"), 1);
853
+ var import_node_url2 = require("url");
854
+ function generateGlobImport(patterns, options) {
855
+ let code = "{";
856
+ const result = (0, import_tinyglobby2.globSync)(patterns, {
857
+ cwd: options.base
858
+ });
859
+ for (const item of result) {
860
+ const fullPath = import_node_path7.default.join(options.base, item);
861
+ const url = (0, import_node_url2.pathToFileURL)(fullPath);
862
+ for (const [k, v] of Object.entries(options.query ?? {})) {
863
+ url.searchParams.set(k, v);
864
+ }
865
+ let line = `${JSON.stringify(item)}: () => import(${JSON.stringify(url.href)})`;
866
+ if (options.import) {
867
+ line += `.then(mod => mod[${JSON.stringify(options.import)}])`;
868
+ }
869
+ code += `${line}, `;
897
870
  }
898
- await import_promises3.default.writeFile(
899
- outDir ? import_node_path6.default.join(outDir, outFile) : outFile,
900
- entry(configPath, config, outDir ?? process.cwd(), addJsExtension)
901
- );
902
- console.log("[MDX] types generated");
871
+ code += "}";
872
+ return code;
873
+ }
874
+
875
+ // src/plugins/vite.ts
876
+ var import_node_path8 = __toESM(require("path"), 1);
877
+ function vite(options) {
878
+ let config;
879
+ return {
880
+ config(v) {
881
+ config = v;
882
+ },
883
+ emit() {
884
+ return [
885
+ {
886
+ path: "index.ts",
887
+ content: indexFile(this.configPath, this.outDir, config, options)
888
+ }
889
+ ];
890
+ }
891
+ };
892
+ }
893
+ function indexFile(configPath, outDir, config, options) {
894
+ const { addJsExtension = false, runtime } = options;
895
+ const lines = [
896
+ '/// <reference types="vite/client" />',
897
+ `import { fromConfig } from 'fumadocs-mdx/runtime/vite';`,
898
+ `import type * as Config from '${toImportPath(configPath, {
899
+ relativeTo: outDir,
900
+ jsExtension: addJsExtension
901
+ })}';`,
902
+ "",
903
+ `export const create = fromConfig<typeof Config>();`
904
+ ];
905
+ function docs(name, collection) {
906
+ const obj = [
907
+ ident(`doc: ${doc(name, collection.docs)}`),
908
+ ident(`meta: ${meta(name, collection.meta)}`)
909
+ ].join(",\n");
910
+ return `{
911
+ ${obj}
912
+ }`;
913
+ }
914
+ function doc(name, collection) {
915
+ const patterns = getGlobPatterns(collection);
916
+ const base = getGlobBase(collection);
917
+ const docGlob = generateGlob(patterns, {
918
+ query: {
919
+ collection: name
920
+ },
921
+ base
922
+ });
923
+ if (collection.async) {
924
+ const headBlob = generateGlob(patterns, {
925
+ query: {
926
+ only: "frontmatter",
927
+ collection: name
928
+ },
929
+ import: "frontmatter",
930
+ base
931
+ });
932
+ return `create.docLazy("${name}", "${base}", ${headBlob}, ${docGlob})`;
933
+ }
934
+ return `create.doc("${name}", "${base}", ${docGlob})`;
935
+ }
936
+ function meta(name, collection) {
937
+ const patterns = getGlobPatterns(collection);
938
+ const base = getGlobBase(collection);
939
+ return `create.meta("${name}", "${base}", ${generateGlob(patterns, {
940
+ import: "default",
941
+ base,
942
+ query: {
943
+ collection: name
944
+ }
945
+ })})`;
946
+ }
947
+ function generateGlob(patterns, options2) {
948
+ patterns = mapGlobPatterns(patterns);
949
+ if (runtime === "node" || runtime === "bun") {
950
+ return generateGlobImport(patterns, options2);
951
+ } else {
952
+ return `import.meta.glob(${JSON.stringify(patterns)}, ${JSON.stringify(
953
+ {
954
+ ...options2,
955
+ base: import_node_path8.default.relative(outDir, options2.base)
956
+ },
957
+ null,
958
+ 2
959
+ )})`;
960
+ }
961
+ }
962
+ for (const [name, collection] of config.collections.entries()) {
963
+ let body;
964
+ if (collection.type === "docs") {
965
+ body = docs(name, collection);
966
+ } else if (collection.type === "meta") {
967
+ body = meta(name, collection);
968
+ } else {
969
+ body = doc(name, collection);
970
+ }
971
+ lines.push("");
972
+ lines.push(`export const ${name} = ${body};`);
973
+ }
974
+ return lines.join("\n");
975
+ }
976
+ function mapGlobPatterns(patterns) {
977
+ return patterns.map(enforceRelative);
978
+ }
979
+ function enforceRelative(file) {
980
+ if (file.startsWith("./")) return file;
981
+ if (file.startsWith("/")) return `.${file}`;
982
+ return `./${file}`;
983
+ }
984
+ function getGlobBase(collection) {
985
+ let dir = collection.dir;
986
+ if (Array.isArray(dir)) {
987
+ if (dir.length !== 1)
988
+ throw new Error(
989
+ `[Fumadocs MDX] Vite Plugin doesn't support multiple \`dir\` for a collection at the moment.`
990
+ );
991
+ dir = dir[0];
992
+ }
993
+ return enforceRelative(dir);
994
+ }
995
+
996
+ // src/core.ts
997
+ var import_node_path9 = __toESM(require("path"), 1);
998
+ var import_promises4 = __toESM(require("fs/promises"), 1);
999
+ function createCore(options, defaultPlugins = []) {
1000
+ let config;
1001
+ let plugins2;
1002
+ return {
1003
+ _options: options,
1004
+ getPluginContext() {
1005
+ return {
1006
+ core: this,
1007
+ ...options
1008
+ };
1009
+ },
1010
+ /**
1011
+ * Convenient cache store, reset when config changes
1012
+ */
1013
+ cache: /* @__PURE__ */ new Map(),
1014
+ async init({ config: newConfig }) {
1015
+ config = await newConfig;
1016
+ this.cache.clear();
1017
+ plugins2 = [];
1018
+ for await (const option of [
1019
+ ...defaultPlugins,
1020
+ ...config.global.plugins ?? []
1021
+ ]) {
1022
+ if (!option) continue;
1023
+ if (Array.isArray(option)) plugins2.push(...option);
1024
+ else plugins2.push(option);
1025
+ }
1026
+ for (const plugin of plugins2) {
1027
+ const out = await plugin.config?.call(this.getPluginContext(), config);
1028
+ if (out) config = out;
1029
+ }
1030
+ return this;
1031
+ },
1032
+ getConfig() {
1033
+ return config;
1034
+ },
1035
+ creatConfigLoader() {
1036
+ return {
1037
+ getConfig() {
1038
+ return config;
1039
+ }
1040
+ };
1041
+ },
1042
+ async initServer(server) {
1043
+ for (const plugin of plugins2) {
1044
+ await plugin.configureServer?.call(this.getPluginContext(), server);
1045
+ }
1046
+ },
1047
+ async emitAndWrite({
1048
+ filterPlugin = () => true
1049
+ } = {}) {
1050
+ const start = performance.now();
1051
+ const out = await Promise.all(
1052
+ plugins2.map((plugin) => {
1053
+ if (!filterPlugin(plugin) || !plugin.emit) return [];
1054
+ return plugin.emit.call(this.getPluginContext());
1055
+ })
1056
+ );
1057
+ await Promise.all(
1058
+ out.flat().map(async (entry) => {
1059
+ const file = import_node_path9.default.join(options.outDir, entry.path);
1060
+ await import_promises4.default.mkdir(import_node_path9.default.dirname(file), { recursive: true });
1061
+ await import_promises4.default.writeFile(file, entry.content);
1062
+ })
1063
+ );
1064
+ console.log(`[MDX] generated files in ${performance.now() - start}ms`);
1065
+ }
1066
+ };
903
1067
  }
904
1068
 
905
1069
  // src/vite/index.ts
906
1070
  var FumadocsDeps = ["fumadocs-core", "fumadocs-ui", "fumadocs-openapi"];
907
- function mdx(config, options = {}) {
908
- const {
909
- generateIndexFile = true,
910
- updateViteConfig = true,
911
- configPath = "source.config.ts"
912
- } = options;
913
- const loaded = buildConfig(config);
914
- const mdxLoader = toVite(createMdxLoader(resolvedConfig(loaded)));
915
- async function transformMeta(path10, query, value) {
916
- const isJson = path10.endsWith(".json");
1071
+ async function mdx(config, pluginOptions = {}) {
1072
+ const options = applyDefaults(pluginOptions);
1073
+ const core = await createViteCore(options).init({
1074
+ config: buildConfig(config)
1075
+ });
1076
+ const mdxLoader = toVite(createMdxLoader(core.creatConfigLoader()));
1077
+ async function transformMeta(path13, query, value) {
1078
+ const isJson = path13.endsWith(".json");
917
1079
  const parsed = (0, import_node_querystring2.parse)(query);
918
- const collection = parsed.collection ? loaded.collections.get(parsed.collection) : void 0;
1080
+ const collection = parsed.collection ? core.getConfig().collections.get(parsed.collection) : void 0;
919
1081
  if (!collection) return null;
920
1082
  let schema;
921
1083
  switch (collection.type) {
@@ -936,8 +1098,8 @@ function mdx(config, options = {}) {
936
1098
  const out = await validate(
937
1099
  schema,
938
1100
  data,
939
- { path: path10, source: value },
940
- `invalid data in ${path10}`
1101
+ { path: path13, source: value },
1102
+ `invalid data in ${path13}`
941
1103
  );
942
1104
  return {
943
1105
  code: isJson ? JSON.stringify(out) : `export default ${JSON.stringify(out)}`,
@@ -949,7 +1111,7 @@ function mdx(config, options = {}) {
949
1111
  // needed, otherwise other plugins will be executed before our `transform`.
950
1112
  enforce: "pre",
951
1113
  config(config2) {
952
- if (!updateViteConfig) return config2;
1114
+ if (!options.updateViteConfig) return config2;
953
1115
  return (0, import_vite.mergeConfig)(config2, {
954
1116
  optimizeDeps: {
955
1117
  exclude: FumadocsDeps
@@ -961,16 +1123,16 @@ function mdx(config, options = {}) {
961
1123
  });
962
1124
  },
963
1125
  async buildStart() {
964
- if (!generateIndexFile) return;
965
- const { out = "source.generated.ts", addJsExtension } = typeof generateIndexFile === "object" ? generateIndexFile : {};
966
- console.log("[Fumadocs MDX] Generating index files");
967
- const dir = path9.dirname(out);
968
- await fs6.mkdir(dir, { recursive: true });
969
- await fs6.writeFile(out, entry(configPath, loaded, dir, addJsExtension));
1126
+ await core.emitAndWrite();
1127
+ },
1128
+ async configureServer(server) {
1129
+ await core.initServer({
1130
+ watcher: server.watcher
1131
+ });
970
1132
  },
971
1133
  async transform(value, id) {
972
1134
  const [file, query = ""] = id.split("?");
973
- const ext = path9.extname(file);
1135
+ const ext = path12.extname(file);
974
1136
  try {
975
1137
  if ([".yaml", ".json"].includes(ext))
976
1138
  return await transformMeta(file, query, value);
@@ -985,6 +1147,38 @@ function mdx(config, options = {}) {
985
1147
  }
986
1148
  };
987
1149
  }
1150
+ async function postInstall(configPath = findConfigFile(), pluginOptions = {}) {
1151
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_load(), load_exports));
1152
+ const options = applyDefaults(pluginOptions);
1153
+ const core = await createViteCore(options).init({
1154
+ config: loadConfig2(configPath, options.outDir, true)
1155
+ });
1156
+ await core.emitAndWrite();
1157
+ }
1158
+ function createViteCore({
1159
+ configPath,
1160
+ outDir,
1161
+ generateIndexFile
1162
+ }) {
1163
+ return createCore(
1164
+ {
1165
+ environment: "vite",
1166
+ configPath,
1167
+ outDir
1168
+ },
1169
+ [
1170
+ generateIndexFile !== false && vite(typeof generateIndexFile === "object" ? generateIndexFile : {})
1171
+ ]
1172
+ );
1173
+ }
1174
+ function applyDefaults(options) {
1175
+ return {
1176
+ updateViteConfig: options.updateViteConfig ?? true,
1177
+ generateIndexFile: options.generateIndexFile ?? true,
1178
+ configPath: options.configPath ?? "source.config.ts",
1179
+ outDir: options.outDir ?? ".source"
1180
+ };
1181
+ }
988
1182
  // Annotate the CommonJS export names for ESM import in node:
989
1183
  0 && (module.exports = {
990
1184
  postInstall