fumadocs-mdx 14.2.14 → 14.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +21 -38
  2. package/dist/next/index.cjs +0 -938
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-mdx",
3
- "version": "14.2.14",
3
+ "version": "14.3.0",
4
4
  "description": "The built-in source for Fumadocs",
5
5
  "keywords": [
6
6
  "Docs",
@@ -17,44 +17,27 @@
17
17
  "dist"
18
18
  ],
19
19
  "type": "module",
20
- "main": "./dist/index.js",
21
20
  "types": "./dist/index.d.ts",
22
21
  "exports": {
22
+ ".": "./dist/index.js",
23
+ "./bin": "./dist/bin.js",
24
+ "./bun": "./dist/bun/index.js",
25
+ "./config": "./dist/config/index.js",
26
+ "./next": "./dist/next/index.js",
27
+ "./node/loader": "./dist/node/loader.js",
28
+ "./plugins/index-file": "./dist/plugins/index-file.js",
29
+ "./plugins/json-schema": "./dist/plugins/json-schema.js",
30
+ "./plugins/last-modified": "./dist/plugins/last-modified.js",
31
+ "./runtime/browser": "./dist/runtime/browser.js",
32
+ "./runtime/dynamic": "./dist/runtime/dynamic.js",
33
+ "./runtime/server": "./dist/runtime/server.js",
34
+ "./runtime/types": "./dist/runtime/types.js",
35
+ "./vite": "./dist/vite/index.js",
36
+ "./webpack/mdx": "./dist/webpack/mdx.js",
37
+ "./webpack/meta": "./dist/webpack/meta.js",
38
+ "./package.json": "./package.json",
23
39
  "./loader-mdx": "./loader-mdx.cjs",
24
- "./loader-meta": "./loader-meta.cjs",
25
- "./config": {
26
- "types": "./dist/config/index.d.ts",
27
- "import": "./dist/config/index.js"
28
- },
29
- "./next": {
30
- "types": "./dist/next/index.d.ts",
31
- "import": "./dist/next/index.js",
32
- "require": "./dist/next/index.cjs"
33
- },
34
- "./vite": {
35
- "types": "./dist/vite/index.d.ts",
36
- "import": "./dist/vite/index.js"
37
- },
38
- ".": {
39
- "types": "./dist/index.d.ts",
40
- "import": "./dist/index.js"
41
- },
42
- "./runtime/*": {
43
- "types": "./dist/runtime/*.d.ts",
44
- "import": "./dist/runtime/*.js"
45
- },
46
- "./node/loader": {
47
- "types": "./dist/node/loader.d.ts",
48
- "import": "./dist/node/loader.js"
49
- },
50
- "./bun": {
51
- "types": "./dist/bun/index.d.ts",
52
- "import": "./dist/bun/index.js"
53
- },
54
- "./plugins/*": {
55
- "types": "./dist/plugins/*.d.ts",
56
- "import": "./dist/plugins/*.js"
57
- }
40
+ "./loader-meta": "./loader-meta.cjs"
58
41
  },
59
42
  "publishConfig": {
60
43
  "access": "public"
@@ -93,10 +76,10 @@
93
76
  "remark-directive": "^4.0.0",
94
77
  "remark-mdx": "^3.1.1",
95
78
  "rolldown": "1.0.0-rc.15",
96
- "tsdown": "0.21.7",
79
+ "tsdown": "0.21.8",
97
80
  "vite": "^8.0.8",
98
81
  "webpack": "^5.106.1",
99
- "fumadocs-core": "16.7.15",
82
+ "fumadocs-core": "16.7.16",
100
83
  "tsconfig": "0.0.0"
101
84
  },
102
85
  "peerDependencies": {
@@ -1,938 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
24
- let node_path = require("node:path");
25
- node_path = __toESM(node_path);
26
- let node_url = require("node:url");
27
- let picomatch = require("picomatch");
28
- picomatch = __toESM(picomatch);
29
- let node_fs_promises = require("node:fs/promises");
30
- node_fs_promises = __toESM(node_fs_promises);
31
- let tinyglobby = require("tinyglobby");
32
- let path = require("path");
33
- path = __toESM(path);
34
- let crypto = require("crypto");
35
- let js_yaml = require("js-yaml");
36
- //#region src/config/preset.ts
37
- function pluginOption(def, options = []) {
38
- const list = def(Array.isArray(options) ? options : []).filter(Boolean);
39
- if (typeof options === "function") return options(list);
40
- return list;
41
- }
42
- /**
43
- * apply MDX processor presets
44
- */
45
- function applyMdxPreset(options = {}) {
46
- return async (environment = "bundler") => {
47
- if (options.preset === "minimal") return options;
48
- const plugins = await import("fumadocs-core/mdx-plugins");
49
- const { valueToExport = [], rehypeCodeOptions, remarkImageOptions, remarkHeadingOptions, remarkStructureOptions, remarkCodeTabOptions, remarkNpmOptions, ...mdxOptions } = options;
50
- const remarkPlugins = pluginOption((v) => [
51
- plugins.remarkGfm,
52
- [plugins.remarkHeading, {
53
- generateToc: false,
54
- ...remarkHeadingOptions
55
- }],
56
- remarkImageOptions !== false && [plugins.remarkImage, {
57
- ...remarkImageOptions,
58
- useImport: remarkImageOptions?.useImport ?? environment === "bundler"
59
- }],
60
- "remarkCodeTab" in plugins && remarkCodeTabOptions !== false && [plugins.remarkCodeTab, remarkCodeTabOptions],
61
- "remarkNpm" in plugins && remarkNpmOptions !== false && [plugins.remarkNpm, remarkNpmOptions],
62
- ...v,
63
- remarkStructureOptions !== false && [plugins.remarkStructure, {
64
- exportAs: "structuredData",
65
- ...remarkStructureOptions
66
- }],
67
- valueToExport.length > 0 && (() => {
68
- return (_, file) => {
69
- file.data["mdx-export"] ??= [];
70
- for (const name of valueToExport) {
71
- if (!(name in file.data)) continue;
72
- file.data["mdx-export"].push({
73
- name,
74
- value: file.data[name]
75
- });
76
- }
77
- };
78
- })
79
- ], mdxOptions.remarkPlugins);
80
- const rehypePlugins = pluginOption((v) => [
81
- rehypeCodeOptions !== false && [plugins.rehypeCode, rehypeCodeOptions],
82
- ...v,
83
- plugins.rehypeToc
84
- ], mdxOptions.rehypePlugins);
85
- return {
86
- ...mdxOptions,
87
- outputFormat: environment === "runtime" ? "function-body" : mdxOptions.outputFormat,
88
- remarkPlugins,
89
- rehypePlugins
90
- };
91
- };
92
- }
93
- //#endregion
94
- //#region src/config/build.ts
95
- const SupportedFormats = {
96
- doc: ["mdx", "md"],
97
- meta: ["json", "yaml"]
98
- };
99
- function buildCollection(name, collection, cwd) {
100
- if (collection.type === "docs") return {
101
- ...collection,
102
- type: "docs",
103
- get dir() {
104
- return this.docs.dir;
105
- },
106
- name,
107
- meta: buildCollection(name, collection.meta, cwd),
108
- docs: buildCollection(name, collection.docs, cwd),
109
- hasFile(filePath) {
110
- return this.docs.hasFile(filePath) || this.meta.hasFile(filePath);
111
- },
112
- isFileSupported(filePath) {
113
- return this.docs.isFileSupported(filePath) || this.meta.isFileSupported(filePath);
114
- },
115
- cwd
116
- };
117
- return {
118
- ...collection,
119
- ...buildPrimitiveCollection(name, collection, cwd)
120
- };
121
- }
122
- function buildPrimitiveCollection(name, config, cwd) {
123
- const supportedFormats = SupportedFormats[config.type];
124
- const patterns = config.files ?? [`**/*.{${supportedFormats.join(",")}}`];
125
- let matcher;
126
- return {
127
- dir: node_path.default.resolve(cwd, config.dir),
128
- cwd,
129
- name,
130
- patterns,
131
- isFileSupported(filePath) {
132
- return supportedFormats.some((format) => filePath.endsWith(`.${format}`));
133
- },
134
- hasFile(filePath) {
135
- if (!this.isFileSupported(filePath)) return false;
136
- const relativePath = node_path.default.relative(this.dir, filePath);
137
- if (relativePath.startsWith(`..${node_path.default.sep}`)) return false;
138
- return (matcher ??= (0, picomatch.default)(patterns))(relativePath);
139
- }
140
- };
141
- }
142
- function buildConfig(config, cwd = process.cwd()) {
143
- const collections = /* @__PURE__ */ new Map();
144
- const loaded = {};
145
- for (const [k, v] of Object.entries(config)) {
146
- if (!v) continue;
147
- if (typeof v === "object" && "type" in v) {
148
- if (v.type === "docs" || v.type === "doc" || v.type === "meta") {
149
- collections.set(k, buildCollection(k, v, cwd));
150
- continue;
151
- }
152
- }
153
- if (k === "default" && v) {
154
- Object.assign(loaded, v);
155
- continue;
156
- }
157
- throw new Error(`Unknown export "${k}", you can only export collections from source configuration file.`);
158
- }
159
- const mdxOptionsCache = /* @__PURE__ */ new Map();
160
- return {
161
- global: loaded,
162
- collections,
163
- workspaces: Object.fromEntries(Object.entries(loaded.workspaces ?? {}).map(([key, value]) => {
164
- return [key, {
165
- dir: value.dir,
166
- config: buildConfig(value.config, node_path.default.resolve(cwd, value.dir))
167
- }];
168
- })),
169
- getMDXOptions(collection, environment = "bundler") {
170
- const key = collection ? `${environment}:${collection.name}` : environment;
171
- const cached = mdxOptionsCache.get(key);
172
- if (cached) return cached;
173
- let result;
174
- if (collection?.mdxOptions) {
175
- const optionsFn = collection.mdxOptions;
176
- result = typeof optionsFn === "function" ? optionsFn(environment) : optionsFn;
177
- } else result = (async () => {
178
- const optionsFn = this.global.mdxOptions;
179
- return applyMdxPreset(typeof optionsFn === "function" ? await optionsFn() : optionsFn)(environment);
180
- })();
181
- mdxOptionsCache.set(key, result);
182
- return result;
183
- }
184
- };
185
- }
186
- //#endregion
187
- //#region src/config/load-from-file.ts
188
- async function compileConfig(core) {
189
- const { build } = await import("esbuild");
190
- const { configPath, outDir } = core.getOptions();
191
- if ((await build({
192
- entryPoints: [{
193
- in: configPath,
194
- out: "source.config"
195
- }],
196
- bundle: true,
197
- outdir: outDir,
198
- target: "node22",
199
- write: true,
200
- platform: "node",
201
- format: "esm",
202
- packages: "external",
203
- outExtension: { ".js": ".mjs" },
204
- allowOverwrite: true
205
- })).errors.length > 0) throw new Error("failed to compile configuration file");
206
- }
207
- /**
208
- * Load config
209
- *
210
- * @param build - By default, it assumes the config file has been compiled. Set this `true` to compile the config first.
211
- */
212
- async function loadConfig(core, build = false) {
213
- if (build) await compileConfig(core);
214
- const url = (0, node_url.pathToFileURL)(core.getCompiledConfigPath());
215
- url.searchParams.set("hash", Date.now().toString());
216
- return await import(url.href).then((loaded) => buildConfig(loaded));
217
- }
218
- //#endregion
219
- //#region src/utils/validation.ts
220
- var ValidationError = class extends Error {
221
- constructor(message, issues) {
222
- super(`${message}:\n${issues.map((issue) => ` ${issue.path}: ${issue.message}`).join("\n")}`);
223
- this.title = message;
224
- this.issues = issues;
225
- }
226
- async toStringFormatted() {
227
- const picocolorsModule = await import("picocolors");
228
- const picocolors = picocolorsModule.default ?? picocolorsModule;
229
- return [picocolors.bold(`[MDX] ${this.title}:`), ...this.issues.map((issue) => picocolors.redBright(`- ${picocolors.bold(issue.path?.join(".") ?? "*")}: ${issue.message}`))].join("\n");
230
- }
231
- };
232
- async function validate(schema, data, context, errorMessage) {
233
- if (typeof schema === "function" && !("~standard" in schema)) schema = schema(context);
234
- if ("~standard" in schema) {
235
- const result = await schema["~standard"].validate(data);
236
- if (result.issues) throw new ValidationError(errorMessage, result.issues);
237
- return result.value;
238
- }
239
- return data;
240
- }
241
- //#endregion
242
- //#region src/utils/codegen.ts
243
- /**
244
- * Code generator (one instance per file)
245
- */
246
- function createCodegen({ target = "default", outDir = "", jsExtension = false, globCache = /* @__PURE__ */ new Map() }) {
247
- let eagerImportId = 0;
248
- const banner = ["// @ts-nocheck"];
249
- if (target === "vite") banner.push("/// <reference types=\"vite/client\" />");
250
- return {
251
- options: {
252
- target,
253
- outDir
254
- },
255
- lines: [],
256
- addImport(statement) {
257
- this.lines.unshift(statement);
258
- },
259
- async pushAsync(insert) {
260
- for (const line of await Promise.all(insert)) {
261
- if (line === void 0) continue;
262
- this.lines.push(line);
263
- }
264
- },
265
- async generateGlobImport(patterns, options) {
266
- if (target === "vite") return this.generateViteGlobImport(patterns, options);
267
- return this.generateNodeGlobImport(patterns, options);
268
- },
269
- generateViteGlobImport(patterns, { base, ...rest }) {
270
- patterns = (typeof patterns === "string" ? [patterns] : patterns).map(normalizeViteGlobPath);
271
- return `import.meta.glob(${JSON.stringify(patterns)}, ${JSON.stringify({
272
- base: normalizeViteGlobPath(node_path.default.relative(outDir, base)),
273
- ...rest
274
- }, null, 2)})`;
275
- },
276
- async generateNodeGlobImport(patterns, { base, eager = false, query = {}, import: importName }) {
277
- const cacheKey = JSON.stringify({
278
- patterns,
279
- base
280
- });
281
- let files = globCache.get(cacheKey);
282
- if (!files) {
283
- files = (0, tinyglobby.glob)(patterns, { cwd: base });
284
- globCache.set(cacheKey, files);
285
- }
286
- let code = "{";
287
- for (const item of await files) {
288
- const fullPath = node_path.default.join(base, item);
289
- const searchParams = new URLSearchParams();
290
- for (const [k, v] of Object.entries(query)) if (v !== void 0) searchParams.set(k, v);
291
- const importPath = this.formatImportPath(fullPath) + "?" + searchParams.toString();
292
- if (eager) {
293
- const name = `__fd_glob_${eagerImportId++}`;
294
- this.lines.unshift(importName ? `import { ${importName} as ${name} } from ${JSON.stringify(importPath)}` : `import * as ${name} from ${JSON.stringify(importPath)}`);
295
- code += `${JSON.stringify(item)}: ${name}, `;
296
- } else {
297
- let line = `${JSON.stringify(item)}: () => import(${JSON.stringify(importPath)})`;
298
- if (importName) line += `.then(mod => mod.${importName})`;
299
- code += `${line}, `;
300
- }
301
- }
302
- code += "}";
303
- return code;
304
- },
305
- formatImportPath(file) {
306
- const ext = node_path.default.extname(file);
307
- let filename;
308
- if (ext === ".ts") {
309
- filename = file.substring(0, file.length - ext.length);
310
- if (jsExtension) filename += ".js";
311
- } else filename = file;
312
- const importPath = slash(node_path.default.relative(outDir, filename));
313
- return importPath.startsWith(".") ? importPath : `./${importPath}`;
314
- },
315
- toString() {
316
- return [...banner, ...this.lines].join("\n");
317
- }
318
- };
319
- }
320
- /**
321
- * convert into POSIX & relative file paths, such that Vite can accept it.
322
- */
323
- function normalizeViteGlobPath(file) {
324
- file = slash(file);
325
- if (file.startsWith("./")) return file;
326
- if (file.startsWith("/")) return `.${file}`;
327
- return `./${file}`;
328
- }
329
- function slash(path) {
330
- if (path.startsWith("\\\\?\\")) return path;
331
- return path.replaceAll("\\", "/");
332
- }
333
- function ident(code, tab = 1) {
334
- return code.split("\n").map((v) => " ".repeat(tab) + v).join("\n");
335
- }
336
- //#endregion
337
- //#region src/core.ts
338
- const _Defaults = {
339
- configPath: "source.config.ts",
340
- outDir: ".source"
341
- };
342
- async function getPlugins(pluginOptions) {
343
- const plugins = [];
344
- for await (const option of pluginOptions) {
345
- if (!option) continue;
346
- if (Array.isArray(option)) plugins.push(...await getPlugins(option));
347
- else plugins.push(option);
348
- }
349
- return plugins;
350
- }
351
- function createCore(options) {
352
- let config;
353
- let plugins;
354
- const workspaces = /* @__PURE__ */ new Map();
355
- async function transformMetadata({ collection, filePath, source }, data) {
356
- if (collection.schema) data = await validate(collection.schema, data, {
357
- path: filePath,
358
- source
359
- }, collection.type === "doc" ? `invalid frontmatter in ${filePath}` : `invalid data in ${filePath}`);
360
- return data;
361
- }
362
- return {
363
- cache: /* @__PURE__ */ new Map(),
364
- async init({ config: newConfig }) {
365
- config = await newConfig;
366
- this.cache.clear();
367
- workspaces.clear();
368
- plugins = await getPlugins([
369
- postprocessPlugin(),
370
- options.plugins,
371
- config.global.plugins
372
- ]);
373
- for (const plugin of plugins) {
374
- const out = await plugin.config?.call(this.getPluginContext(), config);
375
- if (out) config = out;
376
- }
377
- if (!options.workspace) await Promise.all(Object.entries(config.workspaces).map(async ([name, workspace]) => {
378
- const core = createCore({
379
- ...options,
380
- outDir: node_path.default.join(options.outDir, name),
381
- workspace: {
382
- name,
383
- parent: this,
384
- dir: workspace.dir
385
- }
386
- });
387
- await core.init({ config: workspace.config });
388
- workspaces.set(name, core);
389
- }));
390
- },
391
- getWorkspaces() {
392
- return workspaces;
393
- },
394
- getOptions() {
395
- return options;
396
- },
397
- getConfig() {
398
- return config;
399
- },
400
- getCompiledConfigPath() {
401
- return node_path.default.join(options.outDir, "source.config.mjs");
402
- },
403
- getPlugins() {
404
- return plugins;
405
- },
406
- getCollections() {
407
- return Array.from(config.collections.values());
408
- },
409
- getCollection(name) {
410
- return config.collections.get(name);
411
- },
412
- getPluginContext() {
413
- return { core: this };
414
- },
415
- async initServer(server) {
416
- const ctx = this.getPluginContext();
417
- for (const plugin of plugins) await plugin.configureServer?.call(ctx, server);
418
- for (const workspace of workspaces.values()) await workspace.initServer(server);
419
- },
420
- async emit(emitOptions = {}) {
421
- const { filterPlugin, filterWorkspace, write = false } = emitOptions;
422
- const start = performance.now();
423
- const ctx = this.getPluginContext();
424
- const added = /* @__PURE__ */ new Set();
425
- const out = {
426
- entries: [],
427
- workspaces: {}
428
- };
429
- for (const li of await Promise.all(plugins.map((plugin) => {
430
- if (filterPlugin && !filterPlugin(plugin) || !plugin.emit) return;
431
- return plugin.emit.call(ctx);
432
- }))) {
433
- if (!li) continue;
434
- for (const item of li) {
435
- if (added.has(item.path)) continue;
436
- out.entries.push(item);
437
- added.add(item.path);
438
- }
439
- }
440
- if (write) {
441
- await Promise.all(out.entries.map(async (entry) => {
442
- const file = node_path.default.join(options.outDir, entry.path);
443
- await node_fs_promises.default.mkdir(node_path.default.dirname(file), { recursive: true });
444
- await node_fs_promises.default.writeFile(file, entry.content);
445
- }));
446
- console.log(options.workspace ? `[MDX: ${options.workspace.name}] generated files in ${performance.now() - start}ms` : `[MDX] generated files in ${performance.now() - start}ms`);
447
- }
448
- for (const [name, workspace] of workspaces) {
449
- if (filterWorkspace && !filterWorkspace(name)) continue;
450
- out.workspaces[name] = (await workspace.emit(emitOptions)).entries;
451
- }
452
- return out;
453
- },
454
- async transformMeta(options, data) {
455
- const ctx = {
456
- ...this.getPluginContext(),
457
- ...options
458
- };
459
- data = await transformMetadata(options, data);
460
- for (const plugin of plugins) if (plugin.meta?.transform) data = await plugin.meta.transform.call(ctx, data) ?? data;
461
- return data;
462
- },
463
- async transformFrontmatter(options, data) {
464
- const ctx = {
465
- ...this.getPluginContext(),
466
- ...options
467
- };
468
- data = await transformMetadata(options, data);
469
- for (const plugin of plugins) if (plugin.doc?.frontmatter) data = await plugin.doc.frontmatter.call(ctx, data) ?? data;
470
- return data;
471
- },
472
- async transformVFile(options, file) {
473
- const ctx = {
474
- ...this.getPluginContext(),
475
- ...options
476
- };
477
- for (const plugin of plugins) if (plugin.doc?.vfile) file = await plugin.doc.vfile.call(ctx, file) ?? file;
478
- return file;
479
- }
480
- };
481
- }
482
- function postprocessPlugin() {
483
- const LinkReferenceTypes = `{
484
- /**
485
- * extracted references (e.g. hrefs, paths), useful for analyzing relationships between pages.
486
- */
487
- extractedReferences: import("fumadocs-mdx").ExtractedReference[];
488
- }`;
489
- return { "index-file": {
490
- generateTypeConfig() {
491
- const lines = [];
492
- lines.push("{");
493
- lines.push(" DocData: {");
494
- for (const collection of this.core.getCollections()) {
495
- let postprocessOptions;
496
- switch (collection.type) {
497
- case "doc":
498
- postprocessOptions = collection.postprocess;
499
- break;
500
- case "docs":
501
- postprocessOptions = collection.docs.postprocess;
502
- break;
503
- }
504
- if (postprocessOptions?.extractLinkReferences) lines.push(ident(`${collection.name}: ${LinkReferenceTypes},`, 2));
505
- }
506
- lines.push(" }");
507
- lines.push("}");
508
- return lines.join("\n");
509
- },
510
- serverOptions(options) {
511
- options.doc ??= {};
512
- options.doc.passthroughs ??= [];
513
- options.doc.passthroughs.push("extractedReferences");
514
- }
515
- } };
516
- }
517
- //#endregion
518
- //#region src/loaders/index.ts
519
- const metaLoaderGlob = /\.(json|yaml)(\?.+?)?$/;
520
- const mdxLoaderGlob = /\.mdx?(\?.+?)?$/;
521
- //#endregion
522
- //#region src/utils/fs-cache.ts
523
- const map = /* @__PURE__ */ new Map();
524
- function createFSCache() {
525
- return {
526
- read(file) {
527
- const fullPath = toFullPath(file);
528
- const cached = map.get(fullPath);
529
- if (cached) return cached;
530
- const read = node_fs_promises.default.readFile(fullPath, "utf-8");
531
- map.set(fullPath, read);
532
- return read;
533
- },
534
- delete(file) {
535
- map.delete(toFullPath(file));
536
- }
537
- };
538
- }
539
- /**
540
- * make file paths relative to cwd
541
- */
542
- function toFullPath(file) {
543
- if (node_path.default.isAbsolute(file)) return node_path.default.relative(process.cwd(), file);
544
- return file;
545
- }
546
- //#endregion
547
- //#region src/utils/fuma-matter.ts
548
- /**
549
- * Inspired by https://github.com/jonschlinkert/gray-matter
550
- */
551
- const regex = /^---\r?\n(.+?)\r?\n---\r?\n?/s;
552
- /**
553
- * parse frontmatter, it supports only yaml format
554
- */
555
- function fumaMatter(input) {
556
- const output = {
557
- matter: "",
558
- data: {},
559
- content: input
560
- };
561
- const match = regex.exec(input);
562
- if (!match) return output;
563
- output.matter = match[0];
564
- output.content = input.slice(match[0].length);
565
- output.data = (0, js_yaml.load)(match[1]) ?? {};
566
- return output;
567
- }
568
- //#endregion
569
- //#region src/plugins/index-file.ts
570
- const indexFileCache = createFSCache();
571
- function indexFile(options = {}) {
572
- const { target = "default", addJsExtension, browser = true, dynamic = true } = options;
573
- let dynamicCollections;
574
- function isDynamic(collection) {
575
- return collection.type === "docs" && collection.docs.dynamic || collection.type === "doc" && collection.dynamic;
576
- }
577
- function generateConfigs(core) {
578
- const serverOptions = {};
579
- const typeConfigs = ["import(\"fumadocs-mdx/runtime/types\").InternalTypeConfig"];
580
- const ctx = core.getPluginContext();
581
- for (const plugin of core.getPlugins()) {
582
- const indexFilePlugin = plugin["index-file"];
583
- if (!indexFilePlugin) continue;
584
- indexFilePlugin.serverOptions?.call(ctx, serverOptions);
585
- const config = indexFilePlugin.generateTypeConfig?.call(ctx);
586
- if (config) typeConfigs.push(config);
587
- }
588
- return {
589
- serverOptions,
590
- tc: typeConfigs.join(" & ")
591
- };
592
- }
593
- return {
594
- name: "index-file",
595
- config() {
596
- dynamicCollections = this.core.getCollections().filter(isDynamic);
597
- },
598
- configureServer(server) {
599
- if (!server.watcher) return;
600
- server.watcher.on("all", async (event, file) => {
601
- indexFileCache.delete(file);
602
- if (dynamicCollections.length === 0) {
603
- if (target === "vite") return;
604
- if (target === "default" && event === "change") return;
605
- }
606
- const updatedCollection = this.core.getCollections().find((collection) => collection.hasFile(file));
607
- if (!updatedCollection) return;
608
- if (!isDynamic(updatedCollection)) {
609
- if (target === "vite") return;
610
- if (target === "default" && event === "change") return;
611
- }
612
- await this.core.emit({
613
- filterPlugin: (plugin) => plugin.name === "index-file",
614
- filterWorkspace: () => false,
615
- write: true
616
- });
617
- });
618
- },
619
- async emit() {
620
- const globCache = /* @__PURE__ */ new Map();
621
- const { workspace, outDir } = this.core.getOptions();
622
- const { serverOptions, tc } = generateConfigs(this.core);
623
- const toEmitEntry = async (path$6, content) => {
624
- const codegen = createCodegen({
625
- target,
626
- outDir,
627
- jsExtension: addJsExtension,
628
- globCache
629
- });
630
- await content({
631
- core: this.core,
632
- codegen,
633
- serverOptions,
634
- tc,
635
- workspace: workspace?.name
636
- });
637
- return {
638
- path: path$6,
639
- content: codegen.toString()
640
- };
641
- };
642
- const out = [toEmitEntry("server.ts", generateServerIndexFile)];
643
- if (dynamic) out.push(toEmitEntry("dynamic.ts", generateDynamicIndexFile));
644
- if (browser) out.push(toEmitEntry("browser.ts", generateBrowserIndexFile));
645
- return await Promise.all(out);
646
- }
647
- };
648
- }
649
- async function generateServerIndexFile(ctx) {
650
- const { core, codegen, serverOptions, tc } = ctx;
651
- codegen.lines.push(`import { server } from 'fumadocs-mdx/runtime/server';`, `import type * as Config from '${codegen.formatImportPath(core.getOptions().configPath)}';`, "", `const create = server<typeof Config, ${tc}>(${JSON.stringify(serverOptions)});`);
652
- async function generateCollectionObject(collection) {
653
- const base = getBase(collection);
654
- switch (collection.type) {
655
- case "docs": {
656
- if (collection.docs.dynamic) return;
657
- if (collection.docs.async) {
658
- const [metaGlob, headGlob, bodyGlob] = await Promise.all([
659
- generateMetaCollectionGlob(ctx, collection.meta, true),
660
- generateDocCollectionFrontmatterGlob(ctx, collection.docs, true),
661
- generateDocCollectionGlob(ctx, collection.docs)
662
- ]);
663
- return `await create.docsLazy("${collection.name}", "${base}", ${metaGlob}, ${headGlob}, ${bodyGlob})`;
664
- }
665
- const [metaGlob, docGlob] = await Promise.all([generateMetaCollectionGlob(ctx, collection.meta, true), generateDocCollectionGlob(ctx, collection.docs, true)]);
666
- return `await create.docs("${collection.name}", "${base}", ${metaGlob}, ${docGlob})`;
667
- }
668
- case "doc":
669
- if (collection.dynamic) return;
670
- if (collection.async) {
671
- const [headGlob, bodyGlob] = await Promise.all([generateDocCollectionFrontmatterGlob(ctx, collection, true), generateDocCollectionGlob(ctx, collection)]);
672
- return `await create.docLazy("${collection.name}", "${base}", ${headGlob}, ${bodyGlob})`;
673
- }
674
- return `await create.doc("${collection.name}", "${base}", ${await generateDocCollectionGlob(ctx, collection, true)})`;
675
- case "meta": return `await create.meta("${collection.name}", "${base}", ${await generateMetaCollectionGlob(ctx, collection, true)})`;
676
- }
677
- }
678
- await codegen.pushAsync(core.getCollections().map(async (collection) => {
679
- const obj = await generateCollectionObject(collection);
680
- if (!obj) return;
681
- return `\nexport const ${collection.name} = ${obj};`;
682
- }));
683
- }
684
- async function generateDynamicIndexFile(ctx) {
685
- const { core, codegen, serverOptions, tc } = ctx;
686
- const { configPath, environment, outDir } = core.getOptions();
687
- const partialOptions = {
688
- configPath,
689
- environment,
690
- outDir
691
- };
692
- async function generateCollectionObjectEntry(collection, absolutePath) {
693
- const fullPath = path.default.relative(process.cwd(), absolutePath);
694
- const content = await indexFileCache.read(fullPath).catch(() => "");
695
- const parsed = fumaMatter(content);
696
- const data = await core.transformFrontmatter({
697
- collection,
698
- filePath: fullPath,
699
- source: content
700
- }, parsed.data);
701
- const hash = (0, crypto.createHash)("md5").update(content).digest("hex");
702
- const infoStr = [`absolutePath: path.resolve(${JSON.stringify(fullPath)})`];
703
- for (const [k, v] of Object.entries({
704
- info: {
705
- fullPath,
706
- path: path.default.relative(collection.dir, absolutePath)
707
- },
708
- data,
709
- hash
710
- })) infoStr.push(`${k}: ${JSON.stringify(v)}`);
711
- return `{ ${infoStr.join(", ")} }`;
712
- }
713
- async function generateCollectionObject(parent) {
714
- let collection;
715
- if (parent.type === "doc") collection = parent;
716
- else if (parent.type === "docs") collection = parent.docs;
717
- if (!collection || !collection.dynamic) return;
718
- const files = await (0, tinyglobby.glob)(collection.patterns, {
719
- cwd: collection.dir,
720
- absolute: true
721
- });
722
- const entries = await Promise.all(files.map((file) => generateCollectionObjectEntry(collection, file)));
723
- switch (parent.type) {
724
- case "docs": {
725
- const metaGlob = await generateMetaCollectionGlob(ctx, parent.meta, true);
726
- return `await create.docs("${parent.name}", "${getBase(parent)}", ${metaGlob}, [${entries.join(", ")}])`;
727
- }
728
- case "doc": return `await create.doc("${collection.name}", "${getBase(collection)}", [${entries.join(", ")}])`;
729
- }
730
- }
731
- const objects = await Promise.all(core.getCollections().map(async (collection) => {
732
- const obj = await generateCollectionObject(collection);
733
- if (!obj) return;
734
- return `\nexport const ${collection.name} = ${obj};`;
735
- }));
736
- const hasDynamicCollection = objects.some(Boolean);
737
- codegen.lines.push(`import { dynamic } from 'fumadocs-mdx/runtime/dynamic';`, ...hasDynamicCollection ? [`import path from 'node:path';`] : [], `import * as Config from '${codegen.formatImportPath(configPath)}';`, "", `const create = await dynamic<typeof Config, ${tc}>(Config, ${JSON.stringify(partialOptions)}, ${JSON.stringify(serverOptions)});`);
738
- codegen.lines.push(...objects.filter((obj) => obj !== void 0));
739
- }
740
- async function generateBrowserIndexFile(ctx) {
741
- const { core, codegen, tc } = ctx;
742
- codegen.lines.push(`import { browser } from 'fumadocs-mdx/runtime/browser';`, `import type * as Config from '${codegen.formatImportPath(core.getOptions().configPath)}';`, "", `const create = browser<typeof Config, ${tc}>();`);
743
- async function generateCollectionObject(collection) {
744
- switch (collection.type) {
745
- case "docs":
746
- if (collection.docs.dynamic) return;
747
- return generateCollectionObject(collection.docs);
748
- case "doc":
749
- if (collection.dynamic) return;
750
- return `create.doc("${collection.name}", ${await generateDocCollectionGlob(ctx, collection)})`;
751
- }
752
- }
753
- codegen.lines.push("const browserCollections = {");
754
- await codegen.pushAsync(core.getCollections().map(async (collection) => {
755
- const obj = await generateCollectionObject(collection);
756
- if (!obj) return;
757
- return ident(`${collection.name}: ${obj},`);
758
- }));
759
- codegen.lines.push("};", "export default browserCollections;");
760
- }
761
- function getBase(collection) {
762
- return slash(path.default.relative(process.cwd(), collection.dir));
763
- }
764
- function generateDocCollectionFrontmatterGlob({ codegen, workspace }, collection, eager = false) {
765
- return codegen.generateGlobImport(collection.patterns, {
766
- query: {
767
- collection: collection.name,
768
- only: "frontmatter",
769
- workspace
770
- },
771
- import: "frontmatter",
772
- base: collection.dir,
773
- eager
774
- });
775
- }
776
- function generateDocCollectionGlob({ codegen, workspace }, collection, eager = false) {
777
- return codegen.generateGlobImport(collection.patterns, {
778
- query: {
779
- collection: collection.name,
780
- workspace
781
- },
782
- base: collection.dir,
783
- eager
784
- });
785
- }
786
- function generateMetaCollectionGlob({ codegen, workspace }, collection, eager = false) {
787
- return codegen.generateGlobImport(collection.patterns, {
788
- query: {
789
- collection: collection.name,
790
- workspace
791
- },
792
- import: "default",
793
- base: collection.dir,
794
- eager
795
- });
796
- }
797
- //#endregion
798
- //#region src/next/index.ts
799
- const defaultPageExtensions = [
800
- "mdx",
801
- "md",
802
- "jsx",
803
- "js",
804
- "tsx",
805
- "ts"
806
- ];
807
- function createMDX(createOptions = {}) {
808
- const core = createNextCore(applyDefaults(createOptions));
809
- const isDev = process.env.NODE_ENV === "development";
810
- if (process.env._FUMADOCS_MDX !== "1") {
811
- process.env._FUMADOCS_MDX = "1";
812
- init(isDev, core);
813
- }
814
- return (nextConfig = {}) => {
815
- const { configPath, outDir } = core.getOptions();
816
- const loaderOptions = {
817
- configPath,
818
- outDir,
819
- absoluteCompiledConfigPath: node_path.resolve(core.getCompiledConfigPath()),
820
- isDev
821
- };
822
- const turbopack = {
823
- ...nextConfig.turbopack,
824
- rules: {
825
- ...nextConfig.turbopack?.rules,
826
- "*.{md,mdx}": {
827
- loaders: [{
828
- loader: "fumadocs-mdx/loader-mdx",
829
- options: loaderOptions
830
- }],
831
- as: "*.js"
832
- },
833
- "*.json": {
834
- loaders: [{
835
- loader: "fumadocs-mdx/loader-meta",
836
- options: loaderOptions
837
- }],
838
- as: "*.json"
839
- },
840
- "*.yaml": {
841
- loaders: [{
842
- loader: "fumadocs-mdx/loader-meta",
843
- options: loaderOptions
844
- }],
845
- as: "*.js"
846
- }
847
- }
848
- };
849
- return {
850
- ...nextConfig,
851
- turbopack,
852
- pageExtensions: nextConfig.pageExtensions ?? defaultPageExtensions,
853
- webpack: (config, options) => {
854
- config.resolve ||= {};
855
- config.module ||= {};
856
- config.module.rules ||= [];
857
- config.module.rules.push({
858
- test: mdxLoaderGlob,
859
- use: [options.defaultLoaders.babel, {
860
- loader: "fumadocs-mdx/loader-mdx",
861
- options: loaderOptions
862
- }]
863
- }, {
864
- test: metaLoaderGlob,
865
- enforce: "pre",
866
- use: [{
867
- loader: "fumadocs-mdx/loader-meta",
868
- options: loaderOptions
869
- }]
870
- });
871
- config.plugins ||= [];
872
- return nextConfig.webpack?.(config, options) ?? config;
873
- }
874
- };
875
- };
876
- }
877
- async function init(dev, core) {
878
- async function initOrReload() {
879
- await core.init({ config: loadConfig(core, true) });
880
- await core.emit({ write: true });
881
- }
882
- async function devServer() {
883
- const { FSWatcher } = await import("chokidar");
884
- const { configPath, outDir } = core.getOptions();
885
- const watcher = new FSWatcher({
886
- ignoreInitial: true,
887
- persistent: true,
888
- ignored: [outDir]
889
- });
890
- watcher.add(configPath);
891
- for (const collection of core.getCollections()) watcher.add(collection.dir);
892
- for (const workspace of core.getWorkspaces().values()) for (const collection of workspace.getCollections()) watcher.add(collection.dir);
893
- watcher.on("ready", () => {
894
- console.log("[MDX] started dev server");
895
- });
896
- const absoluteConfigPath = node_path.resolve(configPath);
897
- watcher.on("all", async (_event, file) => {
898
- if (node_path.resolve(file) === absoluteConfigPath) {
899
- watcher.removeAllListeners();
900
- await watcher.close();
901
- await initOrReload();
902
- console.log("[MDX] restarting dev server");
903
- await devServer();
904
- }
905
- });
906
- process.on("exit", () => {
907
- if (watcher.closed) return;
908
- console.log("[MDX] closing dev server");
909
- watcher.close();
910
- });
911
- await core.initServer({ watcher });
912
- }
913
- await initOrReload();
914
- if (dev) await devServer();
915
- }
916
- async function postInstall(options) {
917
- const core = createNextCore(applyDefaults(options));
918
- await core.init({ config: loadConfig(core, true) });
919
- await core.emit({ write: true });
920
- }
921
- function applyDefaults(options) {
922
- return {
923
- index: {},
924
- outDir: options.outDir ?? _Defaults.outDir,
925
- configPath: options.configPath ?? _Defaults.configPath
926
- };
927
- }
928
- function createNextCore(options) {
929
- return createCore({
930
- environment: "next",
931
- outDir: options.outDir,
932
- configPath: options.configPath,
933
- plugins: [options.index && indexFile(options.index)]
934
- });
935
- }
936
- //#endregion
937
- exports.createMDX = createMDX;
938
- exports.postInstall = postInstall;