fumadocs-mdx 11.7.0 → 11.7.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.
@@ -27,8 +27,32 @@ function toImportPath(file, config) {
27
27
  }
28
28
  return importPath.replaceAll(path.sep, "/");
29
29
  }
30
+ function ident(code, tab = 1) {
31
+ return code.split("\n").map((v) => " ".repeat(tab) + v).join("\n");
32
+ }
33
+
34
+ // src/utils/collections.ts
35
+ function getSupportedFormats(collection) {
36
+ return {
37
+ doc: ["mdx", "md"],
38
+ meta: ["json", "yaml"]
39
+ }[collection.type];
40
+ }
41
+ function getGlobPatterns(collection) {
42
+ if (collection.files) return collection.files;
43
+ return [`**/*.{${getSupportedFormats(collection).join(",")}}`];
44
+ }
45
+ function isFileSupported(filePath, collection) {
46
+ for (const format of getSupportedFormats(collection)) {
47
+ if (filePath.endsWith(`.${format}`)) return true;
48
+ }
49
+ return false;
50
+ }
30
51
 
31
52
  export {
32
53
  getImportCode,
33
- toImportPath
54
+ toImportPath,
55
+ ident,
56
+ getGlobPatterns,
57
+ isFileSupported
34
58
  };
@@ -76,13 +76,11 @@ function defineDocs(options) {
76
76
  docs: defineCollections({
77
77
  type: "doc",
78
78
  dir,
79
- files: ["**/*.{md,mdx}"],
80
79
  schema: frontmatterSchema,
81
80
  ...options?.docs
82
81
  }),
83
82
  meta: defineCollections({
84
83
  type: "meta",
85
- files: ["**/*.{json,yaml}"],
86
84
  dir,
87
85
  schema: metaSchema,
88
86
  ...options?.meta
@@ -26,13 +26,11 @@ function defineDocs(options) {
26
26
  docs: defineCollections({
27
27
  type: "doc",
28
28
  dir,
29
- files: ["**/*.{md,mdx}"],
30
29
  schema: frontmatterSchema,
31
30
  ...options?.docs
32
31
  }),
33
32
  meta: defineCollections({
34
33
  type: "meta",
35
- files: ["**/*.{json,yaml}"],
36
34
  dir,
37
35
  schema: metaSchema,
38
36
  ...options?.meta
@@ -309,16 +309,6 @@ var path4 = __toESM(require("path"), 1);
309
309
  var fs2 = __toESM(require("fs/promises"), 1);
310
310
  var import_tinyglobby = require("tinyglobby");
311
311
 
312
- // src/utils/get-type-from-path.ts
313
- var import_node_path = require("path");
314
- var docTypes = [".mdx", ".md"];
315
- var metaTypes = [".json", ".yaml"];
316
- function getTypeFromPath(path7) {
317
- const ext = (0, import_node_path.extname)(path7);
318
- if (docTypes.includes(ext)) return "doc";
319
- if (metaTypes.includes(ext)) return "meta";
320
- }
321
-
322
312
  // src/utils/schema.ts
323
313
  var import_zod = require("zod");
324
314
  var import_picocolors = __toESM(require("picocolors"), 1);
@@ -398,7 +388,7 @@ var fileCache = {
398
388
  var import_js_yaml2 = require("js-yaml");
399
389
 
400
390
  // src/utils/git-timestamp.ts
401
- var import_node_path2 = __toESM(require("path"), 1);
391
+ var import_node_path = __toESM(require("path"), 1);
402
392
  var import_tinyexec = require("tinyexec");
403
393
  var cache2 = /* @__PURE__ */ new Map();
404
394
  async function getGitTimestamp(file) {
@@ -407,7 +397,7 @@ async function getGitTimestamp(file) {
407
397
  try {
408
398
  const out = await (0, import_tinyexec.x)(
409
399
  "git",
410
- ["log", "-1", '--pretty="%ai"', import_node_path2.default.relative(process.cwd(), file)],
400
+ ["log", "-1", '--pretty="%ai"', import_node_path.default.relative(process.cwd(), file)],
411
401
  {
412
402
  throwOnError: true
413
403
  }
@@ -437,7 +427,7 @@ function fumaMatter(input) {
437
427
  }
438
428
 
439
429
  // src/utils/import-formatter.ts
440
- var import_node_path3 = __toESM(require("path"), 1);
430
+ var import_node_path2 = __toESM(require("path"), 1);
441
431
  function getImportCode(info) {
442
432
  const specifier = JSON.stringify(info.specifier);
443
433
  if (info.type === "default") return `import ${info.name} from ${specifier}`;
@@ -452,18 +442,36 @@ function getImportCode(info) {
452
442
  return `import ${specifier}`;
453
443
  }
454
444
  function toImportPath(file, config) {
455
- const ext = import_node_path3.default.extname(file);
445
+ const ext = import_node_path2.default.extname(file);
456
446
  const filename = ext === ".ts" ? file.substring(0, file.length - ext.length) : file;
457
447
  let importPath;
458
448
  if ("relativeTo" in config) {
459
- importPath = import_node_path3.default.relative(config.relativeTo, filename);
460
- if (!import_node_path3.default.isAbsolute(importPath) && !importPath.startsWith(".")) {
449
+ importPath = import_node_path2.default.relative(config.relativeTo, filename);
450
+ if (!import_node_path2.default.isAbsolute(importPath) && !importPath.startsWith(".")) {
461
451
  importPath = `./${importPath}`;
462
452
  }
463
453
  } else {
464
- importPath = import_node_path3.default.resolve(filename);
454
+ importPath = import_node_path2.default.resolve(filename);
465
455
  }
466
- return importPath.replaceAll(import_node_path3.default.sep, "/");
456
+ return importPath.replaceAll(import_node_path2.default.sep, "/");
457
+ }
458
+
459
+ // src/utils/collections.ts
460
+ function getSupportedFormats(collection) {
461
+ return {
462
+ doc: ["mdx", "md"],
463
+ meta: ["json", "yaml"]
464
+ }[collection.type];
465
+ }
466
+ function getGlobPatterns(collection) {
467
+ if (collection.files) return collection.files;
468
+ return [`**/*.{${getSupportedFormats(collection).join(",")}}`];
469
+ }
470
+ function isFileSupported(filePath, collection) {
471
+ for (const format of getSupportedFormats(collection)) {
472
+ if (filePath.endsWith(`.${format}`)) return true;
473
+ }
474
+ return false;
467
475
  }
468
476
 
469
477
  // src/map/generate.ts
@@ -598,14 +606,15 @@ async function generateJS(configPath, config, importPath, configHash = false) {
598
606
  async function getCollectionFiles(collection) {
599
607
  const files = /* @__PURE__ */ new Map();
600
608
  const dirs = Array.isArray(collection.dir) ? collection.dir : [collection.dir];
609
+ const patterns = getGlobPatterns(collection);
601
610
  await Promise.all(
602
611
  dirs.map(async (dir) => {
603
- const result = await (0, import_tinyglobby.glob)(collection.files ?? "**/*", {
612
+ const result = await (0, import_tinyglobby.glob)(patterns, {
604
613
  cwd: path4.resolve(dir),
605
614
  absolute: true
606
615
  });
607
616
  for (const item of result) {
608
- if (getTypeFromPath(item) !== collection.type) continue;
617
+ if (!isFileSupported(item, collection)) continue;
609
618
  files.set(item, {
610
619
  path: path4.relative(dir, item),
611
620
  absolutePath: item
@@ -616,16 +625,16 @@ async function getCollectionFiles(collection) {
616
625
  return Array.from(files.values());
617
626
  }
618
627
  function parseMetaEntry(file, content) {
619
- const extname3 = path4.extname(file);
628
+ const extname2 = path4.extname(file);
620
629
  try {
621
- if (extname3 === ".json") return JSON.parse(content);
622
- if (extname3 === ".yaml") return (0, import_js_yaml2.load)(content);
630
+ if (extname2 === ".json") return JSON.parse(content);
631
+ if (extname2 === ".yaml") return (0, import_js_yaml2.load)(content);
623
632
  } catch (e) {
624
633
  throw new Error(`Failed to parse meta file: ${file}.`, {
625
634
  cause: e
626
635
  });
627
636
  }
628
- throw new Error(`Unknown meta file format: ${extname3}, in ${file}.`);
637
+ throw new Error(`Unknown meta file format: ${extname2}, in ${file}.`);
629
638
  }
630
639
 
631
640
  // src/map/index.ts
@@ -683,7 +692,16 @@ async function start(dev, configPath, outDir) {
683
692
  }
684
693
 
685
694
  // src/next/create.ts
695
+ var import_node_fs = require("fs");
686
696
  var defaultPageExtensions = ["mdx", "md", "jsx", "js", "tsx", "ts"];
697
+ var isTurboExperimental;
698
+ try {
699
+ const content = (0, import_node_fs.readFileSync)("./node_modules/next/package.json").toString();
700
+ const version = JSON.parse(content).version;
701
+ isTurboExperimental = version.startsWith("15.0.") || version.startsWith("15.1.") || version.startsWith("15.2.");
702
+ } catch {
703
+ isTurboExperimental = false;
704
+ }
687
705
  function createMDX({
688
706
  configPath = findConfigFile(),
689
707
  outDir = ".source"
@@ -699,24 +717,25 @@ function createMDX({
699
717
  configPath,
700
718
  outDir
701
719
  };
702
- return {
703
- ...nextConfig,
704
- turbopack: {
705
- ...nextConfig?.turbopack,
706
- rules: {
707
- ...nextConfig?.turbopack?.rules,
708
- // @ts-expect-error -- safe
709
- "*.{md,mdx}": {
710
- loaders: [
711
- {
712
- loader: "fumadocs-mdx/loader-mdx",
713
- options: mdxLoaderOptions
714
- }
715
- ],
716
- as: "*.js"
717
- }
720
+ const turbo = {
721
+ ...nextConfig.experimental?.turbo,
722
+ ...nextConfig.turbopack,
723
+ rules: {
724
+ ...nextConfig.experimental?.turbo?.rules,
725
+ ...nextConfig.turbopack?.rules,
726
+ "*.{md,mdx}": {
727
+ loaders: [
728
+ {
729
+ loader: "fumadocs-mdx/loader-mdx",
730
+ options: mdxLoaderOptions
731
+ }
732
+ ],
733
+ as: "*.js"
718
734
  }
719
- },
735
+ }
736
+ };
737
+ const updated = {
738
+ ...nextConfig,
720
739
  pageExtensions: nextConfig.pageExtensions ?? defaultPageExtensions,
721
740
  webpack: (config, options) => {
722
741
  config.resolve ||= {};
@@ -736,6 +755,12 @@ function createMDX({
736
755
  return nextConfig.webpack?.(config, options) ?? config;
737
756
  }
738
757
  };
758
+ if (isTurboExperimental) {
759
+ updated.experimental = { ...updated.experimental, turbo };
760
+ } else {
761
+ updated.turbopack = turbo;
762
+ }
763
+ return updated;
739
764
  };
740
765
  }
741
766
 
@@ -5,9 +5,11 @@ import {
5
5
  loadConfig
6
6
  } from "../chunk-2KBRPMAM.js";
7
7
  import {
8
+ getGlobPatterns,
8
9
  getImportCode,
10
+ isFileSupported,
9
11
  toImportPath
10
- } from "../chunk-JPPCALFT.js";
12
+ } from "../chunk-OWZSTKKX.js";
11
13
  import "../chunk-GWR7KMRU.js";
12
14
  import {
13
15
  ValidationError,
@@ -26,16 +28,6 @@ import * as path from "path";
26
28
  import * as fs from "fs/promises";
27
29
  import { glob } from "tinyglobby";
28
30
 
29
- // src/utils/get-type-from-path.ts
30
- import { extname } from "path";
31
- var docTypes = [".mdx", ".md"];
32
- var metaTypes = [".json", ".yaml"];
33
- function getTypeFromPath(path4) {
34
- const ext = extname(path4);
35
- if (docTypes.includes(ext)) return "doc";
36
- if (metaTypes.includes(ext)) return "meta";
37
- }
38
-
39
31
  // src/map/file-cache.ts
40
32
  import { LRUCache } from "lru-cache";
41
33
  var map = new LRUCache({
@@ -189,14 +181,15 @@ async function generateJS(configPath, config, importPath, configHash = false) {
189
181
  async function getCollectionFiles(collection) {
190
182
  const files = /* @__PURE__ */ new Map();
191
183
  const dirs = Array.isArray(collection.dir) ? collection.dir : [collection.dir];
184
+ const patterns = getGlobPatterns(collection);
192
185
  await Promise.all(
193
186
  dirs.map(async (dir) => {
194
- const result = await glob(collection.files ?? "**/*", {
187
+ const result = await glob(patterns, {
195
188
  cwd: path.resolve(dir),
196
189
  absolute: true
197
190
  });
198
191
  for (const item of result) {
199
- if (getTypeFromPath(item) !== collection.type) continue;
192
+ if (!isFileSupported(item, collection)) continue;
200
193
  files.set(item, {
201
194
  path: path.relative(dir, item),
202
195
  absolutePath: item
@@ -207,16 +200,16 @@ async function getCollectionFiles(collection) {
207
200
  return Array.from(files.values());
208
201
  }
209
202
  function parseMetaEntry(file, content) {
210
- const extname3 = path.extname(file);
203
+ const extname2 = path.extname(file);
211
204
  try {
212
- if (extname3 === ".json") return JSON.parse(content);
213
- if (extname3 === ".yaml") return load(content);
205
+ if (extname2 === ".json") return JSON.parse(content);
206
+ if (extname2 === ".yaml") return load(content);
214
207
  } catch (e) {
215
208
  throw new Error(`Failed to parse meta file: ${file}.`, {
216
209
  cause: e
217
210
  });
218
211
  }
219
- throw new Error(`Unknown meta file format: ${extname3}, in ${file}.`);
212
+ throw new Error(`Unknown meta file format: ${extname2}, in ${file}.`);
220
213
  }
221
214
 
222
215
  // src/map/index.ts
@@ -274,7 +267,16 @@ async function start(dev, configPath, outDir) {
274
267
  }
275
268
 
276
269
  // src/next/create.ts
270
+ import { readFileSync } from "fs";
277
271
  var defaultPageExtensions = ["mdx", "md", "jsx", "js", "tsx", "ts"];
272
+ var isTurboExperimental;
273
+ try {
274
+ const content = readFileSync("./node_modules/next/package.json").toString();
275
+ const version = JSON.parse(content).version;
276
+ isTurboExperimental = version.startsWith("15.0.") || version.startsWith("15.1.") || version.startsWith("15.2.");
277
+ } catch {
278
+ isTurboExperimental = false;
279
+ }
278
280
  function createMDX({
279
281
  configPath = findConfigFile(),
280
282
  outDir = ".source"
@@ -290,24 +292,25 @@ function createMDX({
290
292
  configPath,
291
293
  outDir
292
294
  };
293
- return {
294
- ...nextConfig,
295
- turbopack: {
296
- ...nextConfig?.turbopack,
297
- rules: {
298
- ...nextConfig?.turbopack?.rules,
299
- // @ts-expect-error -- safe
300
- "*.{md,mdx}": {
301
- loaders: [
302
- {
303
- loader: "fumadocs-mdx/loader-mdx",
304
- options: mdxLoaderOptions
305
- }
306
- ],
307
- as: "*.js"
308
- }
295
+ const turbo = {
296
+ ...nextConfig.experimental?.turbo,
297
+ ...nextConfig.turbopack,
298
+ rules: {
299
+ ...nextConfig.experimental?.turbo?.rules,
300
+ ...nextConfig.turbopack?.rules,
301
+ "*.{md,mdx}": {
302
+ loaders: [
303
+ {
304
+ loader: "fumadocs-mdx/loader-mdx",
305
+ options: mdxLoaderOptions
306
+ }
307
+ ],
308
+ as: "*.js"
309
309
  }
310
- },
310
+ }
311
+ };
312
+ const updated = {
313
+ ...nextConfig,
311
314
  pageExtensions: nextConfig.pageExtensions ?? defaultPageExtensions,
312
315
  webpack: (config, options) => {
313
316
  config.resolve ||= {};
@@ -327,6 +330,12 @@ function createMDX({
327
330
  return nextConfig.webpack?.(config, options) ?? config;
328
331
  }
329
332
  };
333
+ if (isTurboExperimental) {
334
+ updated.experimental = { ...updated.experimental, turbo };
335
+ } else {
336
+ updated.turbopack = turbo;
337
+ }
338
+ return updated;
330
339
  };
331
340
  }
332
341
 
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/runtime/vite.ts
21
21
  var vite_exports = {};
22
22
  __export(vite_exports, {
23
+ createClientLoader: () => createClientLoader,
23
24
  fromConfig: () => fromConfig,
24
25
  toClientRenderer: () => toClientRenderer
25
26
  });
@@ -99,18 +100,51 @@ function fromConfig() {
99
100
  }
100
101
  };
101
102
  }
102
- function toClientRenderer(files, renderer) {
103
- const loader = {};
103
+ var loaderStore = /* @__PURE__ */ new Map();
104
+ function createClientLoader(files, options) {
105
+ const { id = "", component } = options;
106
+ const store = loaderStore.get(id) ?? {
107
+ preloaded: /* @__PURE__ */ new Map()
108
+ };
109
+ loaderStore.set(id, store);
110
+ let renderer;
111
+ return {
112
+ async preload(path) {
113
+ const loaded = await files[path]();
114
+ store.preloaded.set(path, loaded);
115
+ return loaded;
116
+ },
117
+ getComponent(path) {
118
+ renderer ??= toClientRenderer(files, component, {
119
+ cache: store.preloaded
120
+ });
121
+ return renderer[path];
122
+ }
123
+ };
124
+ }
125
+ function toClientRenderer(files, component, options = {}) {
126
+ const { cache } = options;
127
+ const renderer = {};
104
128
  for (const k in files) {
105
- loader[k] = (0, import_react.lazy)(async () => {
129
+ const OnDemand = (0, import_react.lazy)(async () => {
106
130
  const loaded = await files[k]();
107
- return { default: (props) => renderer(loaded, props) };
131
+ return { default: (props) => component(loaded, props) };
108
132
  });
133
+ if (cache) {
134
+ renderer[k] = (props) => {
135
+ const cached = cache.get(k);
136
+ if (!cached) return (0, import_react.createElement)(OnDemand, props);
137
+ return component(cached, props);
138
+ };
139
+ } else {
140
+ renderer[k] = OnDemand;
141
+ }
109
142
  }
110
- return loader;
143
+ return renderer;
111
144
  }
112
145
  // Annotate the CommonJS export names for ESM import in node:
113
146
  0 && (module.exports = {
147
+ createClientLoader,
114
148
  fromConfig,
115
149
  toClientRenderer
116
150
  });
@@ -1,5 +1,5 @@
1
1
  import { TableOfContents } from 'fumadocs-core/server';
2
- import { FC, ReactNode, LazyExoticComponent } from 'react';
2
+ import { FC, ReactNode } from 'react';
3
3
  import { MDXProps } from 'mdx/types';
4
4
  import { StructuredData } from 'fumadocs-core/mdx-plugins';
5
5
  import { D as DocCollection, M as MetaCollection, a as DocsCollection } from '../define-CCrinVBZ.cjs';
@@ -41,7 +41,31 @@ declare function fromConfig<Config>(): {
41
41
  metaData: MetaOut;
42
42
  }>>;
43
43
  };
44
- type ClientLoader<Props> = Record<string, LazyExoticComponent<(props: Props) => ReactNode>>;
45
- declare function toClientRenderer<Frontmatter, Props extends object = object>(files: Record<string, () => Promise<CompiledMDXFile<Frontmatter>>>, renderer: (loaded: CompiledMDXFile<Frontmatter>, props: Props) => ReactNode): ClientLoader<Props>;
44
+ interface ClientLoaderOptions<Frontmatter, Props> {
45
+ /**
46
+ * Loader ID (usually your collection name)
47
+ *
48
+ * The code splitting strategy of frameworks like Tanstack Start may duplicate `createClientLoader()` into different chunks.
49
+ *
50
+ * We use loader ID to share cache between multiple instances of client loader.
51
+ *
52
+ * @defaultValue ''
53
+ */
54
+ id?: string;
55
+ component: (loaded: CompiledMDXFile<Frontmatter>, props: Props) => ReactNode;
56
+ }
57
+ interface ClientLoader<Frontmatter, Props> {
58
+ preload: (path: string) => Promise<CompiledMDXFile<Frontmatter>>;
59
+ /**
60
+ * Get a component that renders content with `React.lazy`
61
+ */
62
+ getComponent: (path: string) => FC<Props>;
63
+ }
64
+ declare function createClientLoader<Frontmatter, Props = object>(files: Record<string, () => Promise<CompiledMDXFile<Frontmatter>>>, options: ClientLoaderOptions<Frontmatter, Props>): ClientLoader<Frontmatter, Props>;
65
+ interface ClientRendererOptions<Frontmatter> {
66
+ cache?: Map<string, CompiledMDXFile<Frontmatter>>;
67
+ }
68
+ type ClientRenderer<Props> = Record<string, FC<Props>>;
69
+ declare function toClientRenderer<Frontmatter, Props = object>(files: Record<string, () => Promise<CompiledMDXFile<Frontmatter>>>, component: (loaded: CompiledMDXFile<Frontmatter>, props: Props) => ReactNode, options?: ClientRendererOptions<Frontmatter>): ClientRenderer<Props>;
46
70
 
47
- export { type CompiledMDXFile, fromConfig, toClientRenderer };
71
+ export { type ClientLoader, type ClientLoaderOptions, type ClientRendererOptions, type CompiledMDXFile, createClientLoader, fromConfig, toClientRenderer };
@@ -1,5 +1,5 @@
1
1
  import { TableOfContents } from 'fumadocs-core/server';
2
- import { FC, ReactNode, LazyExoticComponent } from 'react';
2
+ import { FC, ReactNode } from 'react';
3
3
  import { MDXProps } from 'mdx/types';
4
4
  import { StructuredData } from 'fumadocs-core/mdx-plugins';
5
5
  import { D as DocCollection, M as MetaCollection, a as DocsCollection } from '../define-CCrinVBZ.js';
@@ -41,7 +41,31 @@ declare function fromConfig<Config>(): {
41
41
  metaData: MetaOut;
42
42
  }>>;
43
43
  };
44
- type ClientLoader<Props> = Record<string, LazyExoticComponent<(props: Props) => ReactNode>>;
45
- declare function toClientRenderer<Frontmatter, Props extends object = object>(files: Record<string, () => Promise<CompiledMDXFile<Frontmatter>>>, renderer: (loaded: CompiledMDXFile<Frontmatter>, props: Props) => ReactNode): ClientLoader<Props>;
44
+ interface ClientLoaderOptions<Frontmatter, Props> {
45
+ /**
46
+ * Loader ID (usually your collection name)
47
+ *
48
+ * The code splitting strategy of frameworks like Tanstack Start may duplicate `createClientLoader()` into different chunks.
49
+ *
50
+ * We use loader ID to share cache between multiple instances of client loader.
51
+ *
52
+ * @defaultValue ''
53
+ */
54
+ id?: string;
55
+ component: (loaded: CompiledMDXFile<Frontmatter>, props: Props) => ReactNode;
56
+ }
57
+ interface ClientLoader<Frontmatter, Props> {
58
+ preload: (path: string) => Promise<CompiledMDXFile<Frontmatter>>;
59
+ /**
60
+ * Get a component that renders content with `React.lazy`
61
+ */
62
+ getComponent: (path: string) => FC<Props>;
63
+ }
64
+ declare function createClientLoader<Frontmatter, Props = object>(files: Record<string, () => Promise<CompiledMDXFile<Frontmatter>>>, options: ClientLoaderOptions<Frontmatter, Props>): ClientLoader<Frontmatter, Props>;
65
+ interface ClientRendererOptions<Frontmatter> {
66
+ cache?: Map<string, CompiledMDXFile<Frontmatter>>;
67
+ }
68
+ type ClientRenderer<Props> = Record<string, FC<Props>>;
69
+ declare function toClientRenderer<Frontmatter, Props = object>(files: Record<string, () => Promise<CompiledMDXFile<Frontmatter>>>, component: (loaded: CompiledMDXFile<Frontmatter>, props: Props) => ReactNode, options?: ClientRendererOptions<Frontmatter>): ClientRenderer<Props>;
46
70
 
47
- export { type CompiledMDXFile, fromConfig, toClientRenderer };
71
+ export { type ClientLoader, type ClientLoaderOptions, type ClientRendererOptions, type CompiledMDXFile, createClientLoader, fromConfig, toClientRenderer };
@@ -1,5 +1,5 @@
1
1
  // src/runtime/vite.ts
2
- import { lazy } from "react";
2
+ import { createElement, lazy } from "react";
3
3
  function fromConfig() {
4
4
  function normalize(entries) {
5
5
  const out = {};
@@ -74,17 +74,50 @@ function fromConfig() {
74
74
  }
75
75
  };
76
76
  }
77
- function toClientRenderer(files, renderer) {
78
- const loader = {};
77
+ var loaderStore = /* @__PURE__ */ new Map();
78
+ function createClientLoader(files, options) {
79
+ const { id = "", component } = options;
80
+ const store = loaderStore.get(id) ?? {
81
+ preloaded: /* @__PURE__ */ new Map()
82
+ };
83
+ loaderStore.set(id, store);
84
+ let renderer;
85
+ return {
86
+ async preload(path) {
87
+ const loaded = await files[path]();
88
+ store.preloaded.set(path, loaded);
89
+ return loaded;
90
+ },
91
+ getComponent(path) {
92
+ renderer ??= toClientRenderer(files, component, {
93
+ cache: store.preloaded
94
+ });
95
+ return renderer[path];
96
+ }
97
+ };
98
+ }
99
+ function toClientRenderer(files, component, options = {}) {
100
+ const { cache } = options;
101
+ const renderer = {};
79
102
  for (const k in files) {
80
- loader[k] = lazy(async () => {
103
+ const OnDemand = lazy(async () => {
81
104
  const loaded = await files[k]();
82
- return { default: (props) => renderer(loaded, props) };
105
+ return { default: (props) => component(loaded, props) };
83
106
  });
107
+ if (cache) {
108
+ renderer[k] = (props) => {
109
+ const cached = cache.get(k);
110
+ if (!cached) return createElement(OnDemand, props);
111
+ return component(cached, props);
112
+ };
113
+ } else {
114
+ renderer[k] = OnDemand;
115
+ }
84
116
  }
85
- return loader;
117
+ return renderer;
86
118
  }
87
119
  export {
120
+ createClientLoader,
88
121
  fromConfig,
89
122
  toClientRenderer
90
123
  };
@@ -425,11 +425,28 @@ function toImportPath(file, config) {
425
425
  }
426
426
  return importPath.replaceAll(import_node_path.default.sep, "/");
427
427
  }
428
+ function ident(code, tab = 1) {
429
+ return code.split("\n").map((v) => " ".repeat(tab) + v).join("\n");
430
+ }
428
431
 
429
432
  // src/vite/index.ts
430
433
  var import_promises = __toESM(require("fs/promises"), 1);
431
434
  var import_node_path2 = __toESM(require("path"), 1);
432
435
  var import_js_yaml2 = require("js-yaml");
436
+
437
+ // src/utils/collections.ts
438
+ function getSupportedFormats(collection) {
439
+ return {
440
+ doc: ["mdx", "md"],
441
+ meta: ["json", "yaml"]
442
+ }[collection.type];
443
+ }
444
+ function getGlobPatterns(collection) {
445
+ if (collection.files) return collection.files;
446
+ return [`**/*.{${getSupportedFormats(collection).join(",")}}`];
447
+ }
448
+
449
+ // src/vite/index.ts
433
450
  var fileRegex = /\.(md|mdx)$/;
434
451
  var onlySchema = import_zod2.z.literal(["frontmatter", "all"]);
435
452
  function mdx(config, options = {}) {
@@ -455,76 +472,29 @@ function mdx(config, options = {}) {
455
472
  "",
456
473
  `export const create = fromConfig<typeof Config>();`
457
474
  ];
458
- function filesToGlob(files) {
459
- return files.map((file) => {
460
- if (file.startsWith("./")) return file;
461
- if (file.startsWith("/")) return `.${file}`;
462
- return `./${file}`;
463
- });
464
- }
465
- function docs(name, dir, collection) {
466
- const docFiles = collection.docs.files ? filesToGlob(collection.docs.files) : ["./**/*.{mdx,md}"];
467
- const metaFiles = collection.meta.files ? filesToGlob(collection.meta.files) : ["./**/*.{yaml,json}"];
468
- return `export const ${name} = create.docs('${name}', {
469
- doc: import.meta.glob(${JSON.stringify(docFiles)}, {
470
- query: {
471
- collection: '${name}',
472
- },
473
- base: '${dir}',
474
- }),
475
- meta: import.meta.glob(${JSON.stringify(metaFiles)}, {
476
- query: {
477
- collection: '${name}',
478
- },
479
- base: '${dir}',
480
- import: 'default',
481
- }),
475
+ function docs(name, collection) {
476
+ const args = [
477
+ ident(`doc: ${generateGlob(name, collection.docs)}`),
478
+ ident(`meta: ${generateGlob(name, collection.meta)}`)
479
+ ].join(",\n");
480
+ return `export const ${name} = create.docs("${name}", {
481
+ ${args}
482
482
  });`;
483
483
  }
484
- function doc(name, dir, collection) {
485
- const files = collection.files ? filesToGlob(collection.files) : ["./**/*.{mdx,md}"];
486
- return `export const ${name} = create.doc(
487
- '${name}',
488
- import.meta.glob(${JSON.stringify(files)}, {
489
- query: {
490
- collection: '${name}',
491
- },
492
- base: '${dir}',
493
- }),
494
- );`;
484
+ function doc(name, collection) {
485
+ return `export const ${name} = create.doc("${name}", ${generateGlob(name, collection)});`;
495
486
  }
496
- function meta(name, dir, collection) {
497
- const files = collection.files ? filesToGlob(collection.files) : ["./**/*.{yaml,json}"];
498
- return `export const ${name} = create.meta(
499
- '${name}',
500
- import.meta.glob(${JSON.stringify(files)}, {
501
- query: {
502
- collection: '${name}',
503
- },
504
- base: '${dir}',
505
- import: 'default',
506
- }),
507
- );`;
487
+ function meta(name, collection) {
488
+ return `export const ${name} = create.meta("${name}", ${generateGlob(name, collection)});`;
508
489
  }
509
490
  for (const [name, collection] of loaded.collections.entries()) {
510
- let dir = collection.dir;
511
- if (Array.isArray(dir) && dir.length === 1) {
512
- dir = dir[0];
513
- } else if (Array.isArray(dir)) {
514
- throw new Error(
515
- `[Fumadocs MDX] Vite Plugin doesn't support multiple \`dir\` for a collection at the moment.`
516
- );
517
- }
518
- if (!dir.startsWith("./") && !dir.startsWith("/")) {
519
- dir = "/" + dir;
520
- }
521
491
  lines.push("");
522
492
  if (collection.type === "docs") {
523
- lines.push(docs(name, dir, collection));
493
+ lines.push(docs(name, collection));
524
494
  } else if (collection.type === "meta") {
525
- lines.push(meta(name, dir, collection));
495
+ lines.push(meta(name, collection));
526
496
  } else {
527
- lines.push(doc(name, dir, collection));
497
+ lines.push(doc(name, collection));
528
498
  }
529
499
  }
530
500
  await import_promises.default.writeFile(import_node_path2.default.join(outdir, outFile), lines.join("\n"));
@@ -629,3 +599,37 @@ function mdx(config, options = {}) {
629
599
  }
630
600
  };
631
601
  }
602
+ function generateGlob(name, collection) {
603
+ const patterns = mapGlobPatterns(getGlobPatterns(collection));
604
+ const options = {
605
+ query: {
606
+ collection: name
607
+ },
608
+ base: getGlobBase(collection)
609
+ };
610
+ if (collection.type === "meta") {
611
+ options.import = "default";
612
+ }
613
+ return `import.meta.glob(${JSON.stringify(patterns)}, ${JSON.stringify(options, null, 2)})`;
614
+ }
615
+ function mapGlobPatterns(patterns) {
616
+ return patterns.map((file) => {
617
+ if (file.startsWith("./")) return file;
618
+ if (file.startsWith("/")) return `.${file}`;
619
+ return `./${file}`;
620
+ });
621
+ }
622
+ function getGlobBase(collection) {
623
+ let dir = collection.dir;
624
+ if (Array.isArray(dir)) {
625
+ if (dir.length !== 1)
626
+ throw new Error(
627
+ `[Fumadocs MDX] Vite Plugin doesn't support multiple \`dir\` for a collection at the moment.`
628
+ );
629
+ dir = dir[0];
630
+ }
631
+ if (!dir.startsWith("./") && !dir.startsWith("/")) {
632
+ return "/" + dir;
633
+ }
634
+ return dir;
635
+ }
@@ -3,8 +3,10 @@ import {
3
3
  countLines
4
4
  } from "../chunk-C5INPAZJ.js";
5
5
  import {
6
+ getGlobPatterns,
7
+ ident,
6
8
  toImportPath
7
- } from "../chunk-JPPCALFT.js";
9
+ } from "../chunk-OWZSTKKX.js";
8
10
  import {
9
11
  buildConfig
10
12
  } from "../chunk-GWR7KMRU.js";
@@ -48,76 +50,29 @@ function mdx(config, options = {}) {
48
50
  "",
49
51
  `export const create = fromConfig<typeof Config>();`
50
52
  ];
51
- function filesToGlob(files) {
52
- return files.map((file) => {
53
- if (file.startsWith("./")) return file;
54
- if (file.startsWith("/")) return `.${file}`;
55
- return `./${file}`;
56
- });
57
- }
58
- function docs(name, dir, collection) {
59
- const docFiles = collection.docs.files ? filesToGlob(collection.docs.files) : ["./**/*.{mdx,md}"];
60
- const metaFiles = collection.meta.files ? filesToGlob(collection.meta.files) : ["./**/*.{yaml,json}"];
61
- return `export const ${name} = create.docs('${name}', {
62
- doc: import.meta.glob(${JSON.stringify(docFiles)}, {
63
- query: {
64
- collection: '${name}',
65
- },
66
- base: '${dir}',
67
- }),
68
- meta: import.meta.glob(${JSON.stringify(metaFiles)}, {
69
- query: {
70
- collection: '${name}',
71
- },
72
- base: '${dir}',
73
- import: 'default',
74
- }),
53
+ function docs(name, collection) {
54
+ const args = [
55
+ ident(`doc: ${generateGlob(name, collection.docs)}`),
56
+ ident(`meta: ${generateGlob(name, collection.meta)}`)
57
+ ].join(",\n");
58
+ return `export const ${name} = create.docs("${name}", {
59
+ ${args}
75
60
  });`;
76
61
  }
77
- function doc(name, dir, collection) {
78
- const files = collection.files ? filesToGlob(collection.files) : ["./**/*.{mdx,md}"];
79
- return `export const ${name} = create.doc(
80
- '${name}',
81
- import.meta.glob(${JSON.stringify(files)}, {
82
- query: {
83
- collection: '${name}',
84
- },
85
- base: '${dir}',
86
- }),
87
- );`;
62
+ function doc(name, collection) {
63
+ return `export const ${name} = create.doc("${name}", ${generateGlob(name, collection)});`;
88
64
  }
89
- function meta(name, dir, collection) {
90
- const files = collection.files ? filesToGlob(collection.files) : ["./**/*.{yaml,json}"];
91
- return `export const ${name} = create.meta(
92
- '${name}',
93
- import.meta.glob(${JSON.stringify(files)}, {
94
- query: {
95
- collection: '${name}',
96
- },
97
- base: '${dir}',
98
- import: 'default',
99
- }),
100
- );`;
65
+ function meta(name, collection) {
66
+ return `export const ${name} = create.meta("${name}", ${generateGlob(name, collection)});`;
101
67
  }
102
68
  for (const [name, collection] of loaded.collections.entries()) {
103
- let dir = collection.dir;
104
- if (Array.isArray(dir) && dir.length === 1) {
105
- dir = dir[0];
106
- } else if (Array.isArray(dir)) {
107
- throw new Error(
108
- `[Fumadocs MDX] Vite Plugin doesn't support multiple \`dir\` for a collection at the moment.`
109
- );
110
- }
111
- if (!dir.startsWith("./") && !dir.startsWith("/")) {
112
- dir = "/" + dir;
113
- }
114
69
  lines.push("");
115
70
  if (collection.type === "docs") {
116
- lines.push(docs(name, dir, collection));
71
+ lines.push(docs(name, collection));
117
72
  } else if (collection.type === "meta") {
118
- lines.push(meta(name, dir, collection));
73
+ lines.push(meta(name, collection));
119
74
  } else {
120
- lines.push(doc(name, dir, collection));
75
+ lines.push(doc(name, collection));
121
76
  }
122
77
  }
123
78
  await fs.writeFile(path.join(outdir, outFile), lines.join("\n"));
@@ -222,6 +177,40 @@ function mdx(config, options = {}) {
222
177
  }
223
178
  };
224
179
  }
180
+ function generateGlob(name, collection) {
181
+ const patterns = mapGlobPatterns(getGlobPatterns(collection));
182
+ const options = {
183
+ query: {
184
+ collection: name
185
+ },
186
+ base: getGlobBase(collection)
187
+ };
188
+ if (collection.type === "meta") {
189
+ options.import = "default";
190
+ }
191
+ return `import.meta.glob(${JSON.stringify(patterns)}, ${JSON.stringify(options, null, 2)})`;
192
+ }
193
+ function mapGlobPatterns(patterns) {
194
+ return patterns.map((file) => {
195
+ if (file.startsWith("./")) return file;
196
+ if (file.startsWith("/")) return `.${file}`;
197
+ return `./${file}`;
198
+ });
199
+ }
200
+ function getGlobBase(collection) {
201
+ let dir = collection.dir;
202
+ if (Array.isArray(dir)) {
203
+ if (dir.length !== 1)
204
+ throw new Error(
205
+ `[Fumadocs MDX] Vite Plugin doesn't support multiple \`dir\` for a collection at the moment.`
206
+ );
207
+ dir = dir[0];
208
+ }
209
+ if (!dir.startsWith("./") && !dir.startsWith("/")) {
210
+ return "/" + dir;
211
+ }
212
+ return dir;
213
+ }
225
214
  export {
226
215
  mdx as default
227
216
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-mdx",
3
- "version": "11.7.0",
3
+ "version": "11.7.1",
4
4
  "description": "The built-in source for Fumadocs",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -62,16 +62,16 @@
62
62
  "tinyexec": "^1.0.1",
63
63
  "tinyglobby": "^0.2.14",
64
64
  "unist-util-visit": "^5.0.0",
65
- "zod": "^4.0.5"
65
+ "zod": "^4.0.10"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@types/js-yaml": "^4.0.9",
69
69
  "@types/mdast": "^4.0.3",
70
70
  "@types/mdx": "^2.0.13",
71
- "@types/node": "^24.0.15",
71
+ "@types/node": "^24.1.0",
72
72
  "@types/react": "^19.1.8",
73
73
  "mdast-util-mdx-jsx": "^3.2.0",
74
- "next": "^15.4.2",
74
+ "next": "^15.4.4",
75
75
  "react": "^19.1.0",
76
76
  "unified": "^11.0.5",
77
77
  "vfile": "^6.0.3",
@@ -79,7 +79,7 @@
79
79
  "webpack": "^5.100.2",
80
80
  "@fumadocs/mdx-remote": "1.4.0",
81
81
  "eslint-config-custom": "0.0.0",
82
- "fumadocs-core": "15.6.5",
82
+ "fumadocs-core": "15.6.6",
83
83
  "tsconfig": "0.0.0"
84
84
  },
85
85
  "peerDependencies": {