fumadocs-core 15.2.8 → 16.0.3

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 (92) hide show
  1. package/README.md +1 -1
  2. package/dist/algolia-IZEDLPHE.js +58 -0
  3. package/dist/breadcrumb.d.ts +7 -5
  4. package/dist/breadcrumb.js +46 -52
  5. package/dist/builder-feW_xVjc.d.ts +296 -0
  6. package/dist/{chunk-FVY6EZ3N.js → chunk-BBP7MIO4.js} +12 -14
  7. package/dist/{chunk-ORHEEQVY.js → chunk-EMWGTXSW.js} +0 -7
  8. package/dist/chunk-FAEPKD7U.js +20 -0
  9. package/dist/{chunk-NNKVN7WA.js → chunk-H2GMUTQG.js} +4 -2
  10. package/dist/chunk-IZPLHEX4.js +113 -0
  11. package/dist/chunk-OTD7MV33.js +53 -0
  12. package/dist/chunk-PFNP6PEB.js +11 -0
  13. package/dist/{chunk-Y2774T3B.js → chunk-QMATWJ5F.js} +6 -7
  14. package/dist/chunk-U67V476Y.js +35 -0
  15. package/dist/{chunk-BUCUQ3WX.js → chunk-XN2LKXFZ.js} +39 -34
  16. package/dist/{chunk-WFUH5VBX.js → chunk-XOFXGHS4.js} +26 -10
  17. package/dist/chunk-XZSI7AHE.js +67 -0
  18. package/dist/chunk-YVVDKJ2H.js +34 -0
  19. package/dist/chunk-ZMWYLUDP.js +21 -0
  20. package/dist/content/github.d.ts +34 -0
  21. package/dist/content/github.js +43 -0
  22. package/dist/content/index.d.ts +16 -0
  23. package/dist/content/index.js +30 -0
  24. package/dist/{get-toc-Cr2URuiP.d.ts → content/toc.d.ts} +6 -10
  25. package/dist/content/toc.js +21 -0
  26. package/dist/{page-tree-BG3wP0gU.d.ts → definitions-BRsJlZ6m.d.ts} +10 -15
  27. package/dist/dynamic-link.js +3 -3
  28. package/dist/fetch-2XFMBLBA.js +22 -0
  29. package/dist/framework/index.d.ts +1 -1
  30. package/dist/framework/index.js +2 -2
  31. package/dist/framework/next.js +2 -2
  32. package/dist/framework/react-router.js +2 -2
  33. package/dist/framework/tanstack.js +2 -2
  34. package/dist/framework/waku.d.ts +8 -0
  35. package/dist/framework/waku.js +51 -0
  36. package/dist/hide-if-empty.d.ts +18 -0
  37. package/dist/hide-if-empty.js +83 -0
  38. package/dist/highlight/client.d.ts +8 -5
  39. package/dist/highlight/client.js +9 -93
  40. package/dist/highlight/index.d.ts +20 -5
  41. package/dist/highlight/index.js +10 -6
  42. package/dist/i18n/index.d.ts +35 -8
  43. package/dist/i18n/index.js +5 -69
  44. package/dist/i18n/middleware.d.ts +12 -0
  45. package/dist/i18n/middleware.js +63 -0
  46. package/dist/link.js +3 -3
  47. package/dist/mdx-plugins/index.d.ts +124 -18
  48. package/dist/mdx-plugins/index.js +605 -203
  49. package/dist/mixedbread-RAHDVXGJ.js +118 -0
  50. package/dist/negotiation/index.d.ts +19 -0
  51. package/dist/negotiation/index.js +11 -0
  52. package/dist/{orama-cloud-USLSOSXS.js → orama-cloud-WEGQE5A6.js} +37 -27
  53. package/dist/page-tree/index.d.ts +32 -0
  54. package/dist/page-tree/index.js +15 -0
  55. package/dist/remark-code-tab-DmyIyi6m.d.ts +57 -0
  56. package/dist/{remark-structure-FIjTA11P.d.ts → remark-structure-DkCXCzpD.d.ts} +13 -2
  57. package/dist/search/algolia.d.ts +9 -7
  58. package/dist/search/algolia.js +31 -17
  59. package/dist/search/client.d.ts +88 -17
  60. package/dist/search/client.js +71 -50
  61. package/dist/search/index.d.ts +26 -0
  62. package/dist/search/index.js +7 -0
  63. package/dist/search/orama-cloud.d.ts +7 -5
  64. package/dist/search/orama-cloud.js +18 -10
  65. package/dist/search/server.d.ts +33 -25
  66. package/dist/search/server.js +109 -47
  67. package/dist/source/index.d.ts +33 -254
  68. package/dist/source/index.js +532 -353
  69. package/dist/source/plugins/lucide-icons.d.ts +14 -0
  70. package/dist/source/plugins/lucide-icons.js +23 -0
  71. package/dist/static-A2YJ5TXV.js +62 -0
  72. package/dist/toc.d.ts +11 -7
  73. package/dist/toc.js +6 -5
  74. package/dist/utils/use-effect-event.d.ts +4 -3
  75. package/dist/utils/use-effect-event.js +9 -6
  76. package/dist/utils/use-media-query.d.ts +3 -0
  77. package/dist/utils/use-media-query.js +23 -0
  78. package/dist/utils/use-on-change.js +2 -2
  79. package/package.json +92 -40
  80. package/dist/algolia-NTWLS6J3.js +0 -49
  81. package/dist/chunk-KAOEMCTI.js +0 -17
  82. package/dist/chunk-MLKGABMK.js +0 -9
  83. package/dist/chunk-XMCPKVJQ.js +0 -34
  84. package/dist/config-inq6kP6y.d.ts +0 -26
  85. package/dist/fetch-W5EHIBOE.js +0 -21
  86. package/dist/remark-heading-BPCoYwjn.d.ts +0 -31
  87. package/dist/server/index.d.ts +0 -117
  88. package/dist/server/index.js +0 -202
  89. package/dist/sidebar.d.ts +0 -33
  90. package/dist/sidebar.js +0 -89
  91. package/dist/static-VESU2S64.js +0 -61
  92. package/dist/types-Ch8gnVgO.d.ts +0 -8
@@ -1,17 +1,12 @@
1
1
  import {
2
- flattenNode,
3
- remarkHeading
4
- } from "../chunk-Y2774T3B.js";
5
- import {
6
- joinPath,
7
- slash
8
- } from "../chunk-XMCPKVJQ.js";
9
- import {
10
- createStyleTransformer,
11
2
  defaultThemes,
12
3
  getHighlighter
13
- } from "../chunk-BUCUQ3WX.js";
14
- import "../chunk-MLKGABMK.js";
4
+ } from "../chunk-XN2LKXFZ.js";
5
+ import {
6
+ flattenNode,
7
+ remarkHeading
8
+ } from "../chunk-QMATWJ5F.js";
9
+ import "../chunk-U67V476Y.js";
15
10
 
16
11
  // src/mdx-plugins/index.ts
17
12
  import {
@@ -22,6 +17,7 @@ import {
22
17
  import rehypeShikiFromHighlighter from "@shikijs/rehype/core";
23
18
  import {
24
19
  transformerNotationDiff,
20
+ transformerNotationFocus,
25
21
  transformerNotationHighlight,
26
22
  transformerNotationWordHighlight
27
23
  } from "@shikijs/transformers";
@@ -165,36 +161,101 @@ function transformerIcon(options = {}) {
165
161
  const iconName = lang in shortcuts ? shortcuts[lang] : lang;
166
162
  const icon = iconName in icons ? icons[iconName] : defaultIcon;
167
163
  if (icon) {
168
- pre.properties.icon = `<svg viewBox="${icon.viewBox}"><path d="${icon.d}" fill="${icon.fill}" /></svg>`;
164
+ pre.properties.icon = typeof icon === "string" ? icon : `<svg viewBox="${icon.viewBox}"><path d="${icon.d}" fill="${icon.fill}" /></svg>`;
169
165
  }
170
166
  return pre;
171
167
  }
172
168
  };
173
169
  }
174
170
 
175
- // src/mdx-plugins/rehype-code.ts
176
- var metaValues = [
177
- {
178
- name: "title",
179
- regex: /title="(?<value>[^"]*)"/
180
- },
181
- {
182
- name: "custom",
183
- regex: /custom="(?<value>[^"]+)"/
184
- },
185
- {
186
- name: "tab",
187
- regex: /tab="(?<value>[^"]+)"/
171
+ // src/mdx-plugins/codeblock-utils.ts
172
+ function generateCodeBlockTabs({
173
+ persist = false,
174
+ defaultValue,
175
+ triggers,
176
+ tabs,
177
+ ...options
178
+ }) {
179
+ const attributes = [];
180
+ if (options.attributes) attributes.push(...options.attributes);
181
+ if (defaultValue) {
182
+ attributes.push({
183
+ type: "mdxJsxAttribute",
184
+ name: "defaultValue",
185
+ value: defaultValue
186
+ });
187
+ }
188
+ if (typeof persist === "object") {
189
+ attributes.push(
190
+ {
191
+ type: "mdxJsxAttribute",
192
+ name: "groupId",
193
+ value: persist.id
194
+ },
195
+ {
196
+ type: "mdxJsxAttribute",
197
+ name: "persist",
198
+ value: null
199
+ }
200
+ );
201
+ }
202
+ const children = [
203
+ {
204
+ type: "mdxJsxFlowElement",
205
+ name: "CodeBlockTabsList",
206
+ attributes: [],
207
+ children: triggers.map(
208
+ (trigger) => ({
209
+ type: "mdxJsxFlowElement",
210
+ attributes: [
211
+ { type: "mdxJsxAttribute", name: "value", value: trigger.value }
212
+ ],
213
+ name: "CodeBlockTabsTrigger",
214
+ children: trigger.children
215
+ })
216
+ )
217
+ }
218
+ ];
219
+ for (const tab of tabs) {
220
+ children.push({
221
+ type: "mdxJsxFlowElement",
222
+ name: "CodeBlockTab",
223
+ attributes: [
224
+ { type: "mdxJsxAttribute", name: "value", value: tab.value }
225
+ ],
226
+ children: tab.children
227
+ });
188
228
  }
189
- ];
229
+ return {
230
+ type: "mdxJsxFlowElement",
231
+ name: "CodeBlockTabs",
232
+ attributes,
233
+ children
234
+ };
235
+ }
236
+ function parseCodeBlockAttributes(meta, allowedNames) {
237
+ let str = meta;
238
+ const StringRegex = /(?<=^|\s)(?<name>\w+)(?:=(?:"([^"]*)"|'([^']*)'))?/g;
239
+ const attributes = {};
240
+ str = str.replaceAll(StringRegex, (match, name, value_1, value_2) => {
241
+ if (allowedNames && !allowedNames.includes(name)) return match;
242
+ attributes[name] = value_1 ?? value_2 ?? null;
243
+ return "";
244
+ });
245
+ return {
246
+ rest: str,
247
+ attributes
248
+ };
249
+ }
250
+
251
+ // src/mdx-plugins/rehype-code.ts
190
252
  var rehypeCodeDefaultOptions = {
191
253
  lazy: true,
192
254
  themes: defaultThemes,
193
255
  defaultColor: false,
194
256
  defaultLanguage: "plaintext",
195
- experimentalJSEngine: false,
257
+ engine: "js",
196
258
  transformers: [
197
- createStyleTransformer(),
198
259
  transformerNotationHighlight({
199
260
  matchAlgorithm: "v3"
200
261
  }),
@@ -203,21 +264,27 @@ var rehypeCodeDefaultOptions = {
203
264
  }),
204
265
  transformerNotationDiff({
205
266
  matchAlgorithm: "v3"
267
+ }),
268
+ transformerNotationFocus({
269
+ matchAlgorithm: "v3"
206
270
  })
207
271
  ],
208
272
  parseMetaString(meta) {
209
- const map = {};
210
- for (const value of metaValues) {
211
- meta = meta.replace(value.regex, (_, ...args) => {
212
- const first = args.at(0);
213
- map[value.name] = typeof first === "string" ? first : "";
214
- return "";
215
- });
216
- }
217
- map.__parsed_raw = meta;
218
- return map;
273
+ const parsed = parseCodeBlockAttributes(meta, ["title", "tab"]);
274
+ const data = parsed.attributes;
275
+ parsed.rest = parseLineNumber(parsed.rest, data);
276
+ data.__parsed_raw = parsed.rest;
277
+ return data;
219
278
  }
220
279
  };
280
+ function parseLineNumber(str, data) {
281
+ return str.replace(/lineNumbers=(\d+)|lineNumbers/, (_, ...args) => {
282
+ data["data-line-numbers"] = true;
283
+ if (args[0] !== void 0)
284
+ data["data-line-numbers-start"] = Number(args[0]);
285
+ return "";
286
+ });
287
+ }
221
288
  function rehypeCode(_options = {}) {
222
289
  const options = {
223
290
  ...rehypeCodeDefaultOptions,
@@ -243,13 +310,11 @@ function rehypeCode(_options = {}) {
243
310
  if (options.tab !== false) {
244
311
  transformers.push(transformerTab());
245
312
  }
246
- const highlighter = getHighlighter(
247
- options.experimentalJSEngine ? "js" : "oniguruma",
248
- {
249
- themes: "themes" in options ? Object.values(options.themes).filter(Boolean) : [options.theme],
250
- langs: options.langs ?? (options.lazy ? [] : Object.keys(bundledLanguages))
251
- }
252
- );
313
+ const highlighter = getHighlighter(options.engine ?? "js", {
314
+ themes: "themes" in options ? Object.values(options.themes).filter(Boolean) : [options.theme],
315
+ langs: options.langs ?? (options.lazy ? ["ts", "tsx"] : Object.keys(bundledLanguages)),
316
+ langAlias: options.langAlias
317
+ });
253
318
  const transformer = highlighter.then(
254
319
  (loaded) => rehypeShikiFromHighlighter(loaded, {
255
320
  ...options,
@@ -268,6 +333,9 @@ function transformerTab() {
268
333
  root(root) {
269
334
  const value = this.options.meta?.tab;
270
335
  if (typeof value !== "string") return root;
336
+ console.warn(
337
+ '[Fumadocs] For `tab="value" in codeblocks, please use `remarkCodeTab` plugin instead.'
338
+ );
271
339
  return {
272
340
  type: "root",
273
341
  children: [
@@ -287,78 +355,38 @@ function transformerTab() {
287
355
  }
288
356
 
289
357
  // src/mdx-plugins/remark-image.ts
290
- import * as path from "node:path";
358
+ import * as path from "path";
291
359
  import { visit } from "unist-util-visit";
292
360
  import { imageSize } from "image-size";
293
361
  import { imageSizeFromFile } from "image-size/fromFile";
362
+ import { fileURLToPath } from "url";
294
363
  var VALID_BLUR_EXT = [".jpeg", ".png", ".webp", ".avif", ".jpg"];
295
364
  var EXTERNAL_URL_REGEX = /^https?:\/\//;
296
365
  function remarkImage({
297
366
  placeholder = "blur",
298
367
  external = true,
299
368
  useImport = true,
369
+ onError = "error",
300
370
  publicDir = path.join(process.cwd(), "public")
301
371
  } = {}) {
302
372
  return async (tree, file) => {
303
373
  const importsToInject = [];
304
374
  const promises = [];
305
- function getImportPath(src) {
306
- if (!src.startsWith("/")) return src;
307
- const to = path.join(publicDir, src);
308
- if (file.dirname) {
309
- const relative2 = slash(path.relative(file.dirname, to));
310
- return relative2.startsWith("./") ? relative2 : `./${relative2}`;
311
- }
312
- return slash(to);
313
- }
314
- visit(tree, "image", (node) => {
315
- const url = decodeURI(node.url);
316
- if (!url) return;
317
- const isExternal = EXTERNAL_URL_REGEX.test(url);
318
- if (isExternal && external || !useImport) {
319
- const task = getImageSize(url, publicDir).then((size) => {
320
- if (!size.width || !size.height) return;
321
- Object.assign(node, {
322
- type: "mdxJsxFlowElement",
323
- name: "img",
324
- attributes: [
325
- {
326
- type: "mdxJsxAttribute",
327
- name: "alt",
328
- value: node.alt ?? "image"
329
- },
330
- {
331
- type: "mdxJsxAttribute",
332
- name: "src",
333
- value: url
334
- },
335
- {
336
- type: "mdxJsxAttribute",
337
- name: "width",
338
- value: size.width.toString()
339
- },
340
- {
341
- type: "mdxJsxAttribute",
342
- name: "height",
343
- value: size.height.toString()
344
- }
345
- ]
346
- });
347
- }).catch((e) => {
348
- console.error(
349
- `[Remark Image] Failed obtain image size for ${url} with public directory ${publicDir}`
375
+ async function onImage(src, node) {
376
+ if (src.type === "file" && useImport) {
377
+ const variableName = `__img${importsToInject.length}`;
378
+ const hasBlur = placeholder === "blur" && VALID_BLUR_EXT.some((ext) => src.file.endsWith(ext));
379
+ if (!file.dirname) {
380
+ throw new Error(
381
+ "When `useImport` is enabled, you must specify `dirname` in the VFile passed to compiler."
350
382
  );
351
- throw e;
352
- });
353
- promises.push(task);
354
- } else if (!isExternal) {
355
- const variableName = `__img${importsToInject.length.toString()}`;
356
- const hasBlur = placeholder === "blur" && VALID_BLUR_EXT.some((ext) => url.endsWith(ext));
383
+ }
357
384
  importsToInject.push({
358
385
  variableName,
359
- importPath: getImportPath(url)
386
+ importPath: getImportPath(src.file, file.dirname)
360
387
  });
361
- Object.assign(node, {
388
+ const out = {
389
+ children: [],
362
390
  type: "mdxJsxFlowElement",
363
391
  name: "img",
364
392
  attributes: [
@@ -367,11 +395,6 @@ function remarkImage({
367
395
  name: "alt",
368
396
  value: node.alt ?? "image"
369
397
  },
370
- hasBlur && {
371
- type: "mdxJsxAttribute",
372
- name: "placeholder",
373
- value: "blur"
374
- },
375
398
  {
376
399
  type: "mdxJsxAttribute",
377
400
  name: "src",
@@ -385,14 +408,83 @@ function remarkImage({
385
408
  type: "ExpressionStatement",
386
409
  expression: { type: "Identifier", name: variableName }
387
410
  }
388
- ]
411
+ ],
412
+ type: "Program",
413
+ sourceType: "script"
389
414
  }
390
415
  }
391
416
  }
392
417
  }
393
- ].filter(Boolean)
394
- });
418
+ ]
419
+ };
420
+ if (hasBlur) {
421
+ out.attributes.push({
422
+ type: "mdxJsxAttribute",
423
+ name: "placeholder",
424
+ value: "blur"
425
+ });
426
+ }
427
+ return out;
395
428
  }
429
+ if (src.type === "url" && !external) return;
430
+ const size = await getImageSize(src).catch((e) => {
431
+ throw new Error(
432
+ `[Remark Image] Failed obtain image size for ${node.url} (public directory configured as ${publicDir})`,
433
+ {
434
+ cause: e
435
+ }
436
+ );
437
+ });
438
+ return {
439
+ type: "mdxJsxFlowElement",
440
+ name: "img",
441
+ attributes: [
442
+ {
443
+ type: "mdxJsxAttribute",
444
+ name: "alt",
445
+ value: node.alt ?? "image"
446
+ },
447
+ {
448
+ type: "mdxJsxAttribute",
449
+ name: "src",
450
+ // `src` doesn't support file paths, we can use `node.url` for files and let the underlying framework handle it
451
+ value: src.type === "url" ? src.url.toString() : node.url
452
+ },
453
+ {
454
+ type: "mdxJsxAttribute",
455
+ name: "width",
456
+ value: size.width.toString()
457
+ },
458
+ {
459
+ type: "mdxJsxAttribute",
460
+ name: "height",
461
+ value: size.height.toString()
462
+ }
463
+ ],
464
+ children: []
465
+ };
466
+ }
467
+ visit(tree, "image", (node) => {
468
+ const src = parseSrc(decodeURI(node.url), publicDir, file.dirname);
469
+ if (!src) return;
470
+ const task = onImage(src, node).catch((e) => {
471
+ if (onError === "ignore" || node.url.endsWith(".svg")) {
472
+ return;
473
+ }
474
+ if (onError === "hide") {
475
+ return {
476
+ type: "mdxJsxFlowElement",
477
+ name: null,
478
+ attributes: [],
479
+ children: []
480
+ };
481
+ }
482
+ if (onError === "error") throw e;
483
+ onError(e);
484
+ }).then((res) => {
485
+ if (res) Object.assign(node, res);
486
+ });
487
+ promises.push(task);
396
488
  });
397
489
  await Promise.all(promises);
398
490
  if (importsToInject.length === 0) return;
@@ -420,20 +512,53 @@ function remarkImage({
420
512
  tree.children.unshift(...imports);
421
513
  };
422
514
  }
423
- async function getImageSize(src, dir) {
424
- const isRelative = src.startsWith("/") || !path.isAbsolute(src);
425
- let url;
515
+ function getImportPath(file, dir) {
516
+ const relative2 = path.relative(dir, file).replaceAll(path.sep, "/");
517
+ return relative2.startsWith("../") ? relative2 : `./${relative2}`;
518
+ }
519
+ function parseSrc(src, publicDir, dir) {
520
+ if (src.startsWith("file:///"))
521
+ return { type: "file", file: fileURLToPath(src) };
426
522
  if (EXTERNAL_URL_REGEX.test(src)) {
427
- url = src;
428
- } else if (EXTERNAL_URL_REGEX.test(dir) && isRelative) {
429
- const base = new URL(dir);
430
- base.pathname = joinPath(base.pathname, src);
431
- url = base.toString();
432
- } else {
433
- return imageSizeFromFile(isRelative ? path.join(dir, src) : src);
523
+ return {
524
+ type: "url",
525
+ url: new URL(src)
526
+ };
527
+ }
528
+ if (src.startsWith("/")) {
529
+ if (EXTERNAL_URL_REGEX.test(publicDir)) {
530
+ const url = new URL(publicDir);
531
+ const segs = [...url.pathname.split("/"), ...src.split("/")].filter(
532
+ (v) => v.length > 0
533
+ );
534
+ url.pathname = `/${segs.join("/")}`;
535
+ return { type: "url", url };
536
+ }
537
+ return {
538
+ type: "file",
539
+ file: path.join(publicDir, src)
540
+ };
541
+ }
542
+ if (!dir) {
543
+ console.warn(
544
+ `[Remark Image] found relative path ${src} but missing 'dirname' in VFile, this image will be skipped for now.`
545
+ );
546
+ return;
434
547
  }
435
- const buffer = await fetch(url).then((res) => res.arrayBuffer());
436
- return imageSize(new Uint8Array(buffer));
548
+ return {
549
+ type: "file",
550
+ file: path.join(dir, src)
551
+ };
552
+ }
553
+ async function getImageSize(src) {
554
+ if (src.type === "file") return imageSizeFromFile(src.file);
555
+ const res = await fetch(src.url);
556
+ if (!res.ok) {
557
+ throw new Error(
558
+ `[Remark Image] Failed to fetch ${src.url} (${res.status}): ${await res.text()}`
559
+ );
560
+ }
561
+ return imageSize(new Uint8Array(await res.arrayBuffer()));
437
562
  }
438
563
 
439
564
  // src/mdx-plugins/remark-structure.ts
@@ -441,7 +566,6 @@ import Slugger from "github-slugger";
441
566
  import { remark } from "remark";
442
567
  import remarkGfm from "remark-gfm";
443
568
  import { visit as visit2 } from "unist-util-visit";
444
- var slugger = new Slugger();
445
569
  function remarkStructure({
446
570
  types = [
447
571
  "heading",
@@ -450,8 +574,12 @@ function remarkStructure({
450
574
  "tableCell",
451
575
  "mdxJsxFlowElement"
452
576
  ],
453
- allowedMdxAttributes = () => true
577
+ allowedMdxAttributes = (node) => {
578
+ if (!node.name) return false;
579
+ return ["TypeTable", "Callout"].includes(node.name);
580
+ }
454
581
  } = {}) {
582
+ const slugger = new Slugger();
455
583
  if (Array.isArray(allowedMdxAttributes)) {
456
584
  const arr = allowedMdxAttributes;
457
585
  allowedMdxAttributes = (_node, attribute) => attribute.type === "mdxJsxAttribute" && arr.includes(attribute.name);
@@ -463,7 +591,7 @@ function remarkStructure({
463
591
  return (node, file) => {
464
592
  slugger.reset();
465
593
  const data = { contents: [], headings: [] };
466
- let lastHeading = "";
594
+ let lastHeading;
467
595
  if (file.data.frontmatter) {
468
596
  const frontmatter = file.data.frontmatter;
469
597
  if (frontmatter._openapi?.structuredData) {
@@ -498,18 +626,14 @@ function remarkStructure({
498
626
  }
499
627
  if (element.type === "mdxJsxFlowElement" && element.name) {
500
628
  data.contents.push(
501
- {
502
- heading: lastHeading,
503
- content: element.name
504
- },
505
629
  ...element.attributes.flatMap((attribute) => {
506
- const valueStr = typeof attribute.value === "string" ? attribute.value : attribute.value?.value;
507
- if (!valueStr) return [];
630
+ const value = typeof attribute.value === "string" ? attribute.value : attribute.value?.value;
631
+ if (!value || value.length === 0) return [];
508
632
  if (allowedMdxAttributes && !allowedMdxAttributes(element, attribute))
509
633
  return [];
510
634
  return {
511
635
  heading: lastHeading,
512
- content: attribute.type === "mdxJsxAttribute" ? `${attribute.name}: ${valueStr}` : valueStr
636
+ content: attribute.type === "mdxJsxAttribute" ? `${attribute.name}: ${value}` : value
513
637
  };
514
638
  })
515
639
  );
@@ -620,7 +744,7 @@ function rehypeToc({ exportToc = true } = {}) {
620
744
  const output = [];
621
745
  visit4(tree, ["h1", "h2", "h3", "h4", "h5", "h6"], (element) => {
622
746
  const id = element.properties.id;
623
- if (!id) return "skip";
747
+ if (typeof id !== "string") return "skip";
624
748
  let isTocOnly = false;
625
749
  const last = element.children.at(-1);
626
750
  if (last?.type === "text" && last.value.endsWith(TocOnlyTag)) {
@@ -740,91 +864,215 @@ function rehypeToc({ exportToc = true } = {}) {
740
864
 
741
865
  // src/mdx-plugins/remark-code-tab.ts
742
866
  import { visit as visit5 } from "unist-util-visit";
743
- var TabRegex = /tab="(.+?)"/;
744
- function toTab(nodes) {
745
- const names = nodes.map((node, i) => {
746
- let title = `Tab ${i + 1}`;
747
- node.meta = node.meta?.replace(TabRegex, (_, value) => {
748
- title = value;
749
- return "";
750
- });
751
- return title;
752
- });
753
- const itemsArr = {
754
- type: "ExpressionStatement",
755
- expression: {
756
- type: "ArrayExpression",
757
- elements: names.map((name) => ({
758
- type: "Literal",
759
- value: name
760
- }))
761
- }
762
- };
763
- return {
764
- type: "mdxJsxFlowElement",
765
- name: "Tabs",
766
- attributes: [
767
- {
768
- type: "mdxJsxAttribute",
769
- name: "items",
770
- value: {
771
- type: "mdxJsxAttributeValueExpression",
772
- data: {
773
- estree: {
774
- type: "Program",
775
- sourceType: "module",
776
- comments: [],
777
- body: [itemsArr]
867
+ var Tabs = {
868
+ convert(processor, nodes, withMdx = false, withParent = true) {
869
+ const tabs = Array.from(processTabValue(nodes).entries());
870
+ if (!withMdx) {
871
+ const children2 = tabs.map(([name, codes]) => {
872
+ return {
873
+ type: "mdxJsxFlowElement",
874
+ name: "Tab",
875
+ attributes: [
876
+ {
877
+ type: "mdxJsxAttribute",
878
+ name: "value",
879
+ value: name
778
880
  }
779
- }
780
- }
781
- }
782
- ],
783
- children: nodes.map((node, i) => {
881
+ ],
882
+ children: codes
883
+ };
884
+ });
885
+ if (!withParent) return createFragment(children2);
784
886
  return {
785
887
  type: "mdxJsxFlowElement",
786
- name: "Tab",
888
+ name: "Tabs",
787
889
  attributes: [
788
890
  {
789
891
  type: "mdxJsxAttribute",
790
- name: "value",
791
- value: names[i]
892
+ name: "items",
893
+ value: {
894
+ type: "mdxJsxAttributeValueExpression",
895
+ value: tabs.map(([name]) => name).join(", "),
896
+ data: {
897
+ estree: {
898
+ type: "Program",
899
+ sourceType: "module",
900
+ comments: [],
901
+ body: [
902
+ {
903
+ type: "ExpressionStatement",
904
+ expression: {
905
+ type: "ArrayExpression",
906
+ elements: tabs.map(([name]) => ({
907
+ type: "Literal",
908
+ value: name
909
+ }))
910
+ }
911
+ }
912
+ ]
913
+ }
914
+ }
915
+ }
792
916
  }
793
917
  ],
794
- children: [node]
918
+ children: children2
795
919
  };
796
- })
797
- };
798
- }
799
- function remarkCodeTab() {
920
+ }
921
+ const children = [
922
+ {
923
+ type: "mdxJsxFlowElement",
924
+ name: "TabsList",
925
+ attributes: [],
926
+ children: tabs.map(([name]) => ({
927
+ type: "mdxJsxFlowElement",
928
+ name: "TabsTrigger",
929
+ attributes: [
930
+ {
931
+ type: "mdxJsxAttribute",
932
+ name: "value",
933
+ value: name
934
+ }
935
+ ],
936
+ children: [mdxToAst(processor, name)]
937
+ }))
938
+ },
939
+ ...tabs.map(
940
+ ([name, codes]) => ({
941
+ type: "mdxJsxFlowElement",
942
+ name: "TabsContent",
943
+ attributes: [
944
+ {
945
+ type: "mdxJsxAttribute",
946
+ name: "value",
947
+ value: name
948
+ }
949
+ ],
950
+ children: codes
951
+ })
952
+ )
953
+ ];
954
+ if (!withParent) return createFragment(children);
955
+ return {
956
+ type: "mdxJsxFlowElement",
957
+ name: "Tabs",
958
+ attributes: [
959
+ {
960
+ type: "mdxJsxAttribute",
961
+ name: "defaultValue",
962
+ value: tabs[0][0]
963
+ }
964
+ ],
965
+ children
966
+ };
967
+ }
968
+ };
969
+ var CodeBlockTabs = {
970
+ convert(processor, nodes, withMdx = false, withParent = true) {
971
+ const tabs = Array.from(processTabValue(nodes).entries());
972
+ const node = generateCodeBlockTabs({
973
+ defaultValue: tabs[0][0],
974
+ triggers: tabs.map(([name]) => ({
975
+ value: name,
976
+ children: [
977
+ withMdx ? mdxToAst(processor, name) : {
978
+ type: "text",
979
+ value: name
980
+ }
981
+ ]
982
+ })),
983
+ tabs: tabs.map(([name, codes]) => ({
984
+ value: name,
985
+ children: codes
986
+ }))
987
+ });
988
+ if (!withParent) return createFragment(node.children);
989
+ return node;
990
+ }
991
+ };
992
+ var Types = {
993
+ CodeBlockTabs,
994
+ Tabs
995
+ };
996
+ function remarkCodeTab(options = {}) {
997
+ const { parseMdx = false, Tabs: Tabs2 = "CodeBlockTabs" } = options;
800
998
  return (tree) => {
999
+ const ignored = /* @__PURE__ */ new WeakSet();
801
1000
  visit5(tree, (node) => {
802
- if (!("children" in node)) return;
803
- if (node.type === "mdxJsxFlowElement" && node.name === "Tabs") return;
1001
+ if (!("children" in node) || ignored.has(node)) return "skip";
1002
+ let localTabs = Tabs2;
1003
+ let localParseMdx = parseMdx;
1004
+ let withParent = true;
1005
+ if (node.type === "mdxJsxFlowElement" && node.name && node.name in Types) {
1006
+ withParent = false;
1007
+ localTabs = node.name;
1008
+ if (node.name === "Tabs" && localParseMdx) {
1009
+ localParseMdx = node.attributes.every(
1010
+ (attribute) => attribute.type !== "mdxJsxAttribute" || attribute.name !== "items"
1011
+ );
1012
+ }
1013
+ }
804
1014
  let start = -1;
805
- let i = 0;
806
- while (i < node.children.length) {
807
- const child = node.children[i];
808
- const isSwitcher = child.type === "code" && child.meta && child.meta.match(TabRegex);
809
- if (isSwitcher && start === -1) {
810
- start = i;
1015
+ let end = 0;
1016
+ const close = () => {
1017
+ if (start === -1 || start === end) return;
1018
+ const replacement = Types[localTabs].convert(
1019
+ this,
1020
+ node.children.slice(start, end),
1021
+ localParseMdx,
1022
+ withParent
1023
+ );
1024
+ ignored.add(replacement);
1025
+ node.children.splice(start, end - start, replacement);
1026
+ end = start;
1027
+ start = -1;
1028
+ };
1029
+ for (; end < node.children.length; end++) {
1030
+ const child = node.children[end];
1031
+ if (child.type !== "code" || !child.meta) {
1032
+ close();
1033
+ continue;
811
1034
  }
812
- const isLast = i === node.children.length - 1;
813
- if (start !== -1 && (isLast || !isSwitcher)) {
814
- const end = isSwitcher ? i + 1 : i;
815
- const targets = node.children.slice(start, end);
816
- node.children.splice(
817
- start,
818
- end - start,
819
- toTab(targets)
820
- );
821
- if (isLast) break;
822
- i = start;
823
- start = -1;
1035
+ const meta = parseCodeBlockAttributes(child.meta, ["tab"]);
1036
+ if (!meta.attributes.tab) {
1037
+ close();
1038
+ continue;
824
1039
  }
825
- i++;
1040
+ if (start === -1) start = end;
1041
+ child.meta = meta.rest;
1042
+ child.data ??= {};
1043
+ child.data.tab = meta.attributes.tab;
826
1044
  }
1045
+ close();
1046
+ });
1047
+ };
1048
+ }
1049
+ function processTabValue(nodes) {
1050
+ const out = /* @__PURE__ */ new Map();
1051
+ for (let i = 0; i < nodes.length; i++) {
1052
+ const node = nodes[i];
1053
+ const name = node.data?.tab ?? `Tab ${i + 1}`;
1054
+ const li = out.get(name) ?? [];
1055
+ li.push(node);
1056
+ out.set(name, li);
1057
+ }
1058
+ return out;
1059
+ }
1060
+ function mdxToAst(processor, name) {
1061
+ const node = processor.parse(name);
1062
+ if (node.type === "root") {
1063
+ node.children = node.children.flatMap((child) => {
1064
+ if (child.type === "paragraph") return child.children;
1065
+ return child;
827
1066
  });
1067
+ }
1068
+ return node;
1069
+ }
1070
+ function createFragment(children) {
1071
+ return {
1072
+ type: "mdxJsxFlowElement",
1073
+ name: null,
1074
+ attributes: [],
1075
+ children
828
1076
  };
829
1077
  }
830
1078
 
@@ -877,7 +1125,6 @@ function remarkSteps({
877
1125
  if (!("children" in parent) || parent.type === "heading") return;
878
1126
  if (parent.data && "_fd_step" in parent.data) return "skip";
879
1127
  let startIdx = -1;
880
- let lastNumber = 0;
881
1128
  let i = 0;
882
1129
  const onEnd = () => {
883
1130
  if (startIdx === -1) return;
@@ -909,17 +1156,169 @@ function remarkSteps({
909
1156
  onEnd();
910
1157
  continue;
911
1158
  }
912
- const num = Number(match[1]);
913
1159
  head.value = match[2];
914
- if (startIdx !== -1 && num !== lastNumber + 1) onEnd();
915
1160
  if (startIdx === -1) startIdx = i;
916
- lastNumber = num;
917
1161
  }
918
1162
  onEnd();
919
1163
  });
920
1164
  };
921
1165
  }
1166
+
1167
+ // src/mdx-plugins/remark-npm.ts
1168
+ import { visit as visit7 } from "unist-util-visit";
1169
+ import convert from "npm-to-yarn";
1170
+ var aliases = ["npm", "package-install"];
1171
+ function remarkNpm({
1172
+ persist = false,
1173
+ packageManagers = [
1174
+ { command: (cmd) => convert(cmd, "npm"), name: "npm" },
1175
+ { command: (cmd) => convert(cmd, "pnpm"), name: "pnpm" },
1176
+ { command: (cmd) => convert(cmd, "yarn"), name: "yarn" },
1177
+ { command: (cmd) => convert(cmd, "bun"), name: "bun" }
1178
+ ]
1179
+ } = {}) {
1180
+ return (tree) => {
1181
+ visit7(tree, "code", (node) => {
1182
+ if (!node.lang || !aliases.includes(node.lang)) return;
1183
+ let code = node.value;
1184
+ if (node.lang === "package-install" && !code.startsWith("npm") && !code.startsWith("npx")) {
1185
+ code = `npm install ${code}`;
1186
+ }
1187
+ const options = {
1188
+ persist,
1189
+ tabs: [],
1190
+ triggers: []
1191
+ };
1192
+ for (const manager of packageManagers) {
1193
+ const value = manager.value ?? manager.name;
1194
+ const command = manager.command(code);
1195
+ if (!command || command.length === 0) continue;
1196
+ options.defaultValue ??= value;
1197
+ options.triggers.push({
1198
+ value,
1199
+ children: [{ type: "text", value: manager.name }]
1200
+ });
1201
+ options.tabs.push({
1202
+ value,
1203
+ children: [
1204
+ {
1205
+ type: "code",
1206
+ lang: "bash",
1207
+ meta: node.meta,
1208
+ value: command
1209
+ }
1210
+ ]
1211
+ });
1212
+ }
1213
+ Object.assign(node, generateCodeBlockTabs(options));
1214
+ return "skip";
1215
+ });
1216
+ };
1217
+ }
1218
+
1219
+ // src/mdx-plugins/remark-mdx-files.ts
1220
+ import { visit as visit8 } from "unist-util-visit";
1221
+ function parseFileTree(code) {
1222
+ const lines = code.split(/\r?\n/);
1223
+ const stack = /* @__PURE__ */ new Map();
1224
+ for (const line of lines) {
1225
+ let depth = 0;
1226
+ let name = line;
1227
+ let match;
1228
+ while (match = /(?:├──|│|└──)\s*/.exec(name)) {
1229
+ name = name.slice(match[0].length);
1230
+ depth++;
1231
+ }
1232
+ if (!name) continue;
1233
+ const node = name.endsWith("/") ? { type: "folder", name, children: [], depth } : { type: "file", name, depth };
1234
+ let parent;
1235
+ for (let i = depth - 1; i >= 0 && !parent; i--) {
1236
+ parent = stack.get(i);
1237
+ }
1238
+ stack.set(depth, node);
1239
+ if (!parent) continue;
1240
+ if (parent.type === "file") {
1241
+ Object.assign(parent, {
1242
+ type: "folder",
1243
+ children: []
1244
+ });
1245
+ }
1246
+ parent.children.push(node);
1247
+ }
1248
+ return stack.get(0);
1249
+ }
1250
+ function defaultToMDX(node, depth = 0) {
1251
+ if (depth === 0) {
1252
+ return {
1253
+ type: "mdxJsxFlowElement",
1254
+ name: "Files",
1255
+ attributes: [],
1256
+ children: [defaultToMDX(node, depth + 1)]
1257
+ };
1258
+ }
1259
+ const attributes = [
1260
+ { type: "mdxJsxAttribute", name: "name", value: node.name }
1261
+ ];
1262
+ if (node.type === "file") {
1263
+ return {
1264
+ type: "mdxJsxFlowElement",
1265
+ attributes,
1266
+ children: [],
1267
+ name: "File"
1268
+ };
1269
+ }
1270
+ attributes.push({
1271
+ type: "mdxJsxAttribute",
1272
+ name: "defaultOpen",
1273
+ value: null
1274
+ });
1275
+ return {
1276
+ type: "mdxJsxFlowElement",
1277
+ attributes,
1278
+ name: "Folder",
1279
+ children: node.children.map((item) => defaultToMDX(item, depth + 1))
1280
+ };
1281
+ }
1282
+ function remarkMdxFiles(options = {}) {
1283
+ const { lang = "files", toMdx = defaultToMDX } = options;
1284
+ return (tree) => {
1285
+ visit8(tree, "code", (node) => {
1286
+ if (node.lang !== lang || !node.value) return;
1287
+ const fileTree = parseFileTree(node.value);
1288
+ if (!fileTree) return;
1289
+ Object.assign(node, toMdx(fileTree));
1290
+ });
1291
+ };
1292
+ }
1293
+
1294
+ // src/mdx-plugins/remark-mdx-mermaid.ts
1295
+ import { visit as visit9 } from "unist-util-visit";
1296
+ function toMDX(code) {
1297
+ return {
1298
+ type: "mdxJsxFlowElement",
1299
+ name: "Mermaid",
1300
+ attributes: [
1301
+ {
1302
+ type: "mdxJsxAttribute",
1303
+ name: "chart",
1304
+ value: code.trim()
1305
+ }
1306
+ ],
1307
+ children: []
1308
+ };
1309
+ }
1310
+ function remarkMdxMermaid(options = {}) {
1311
+ const { lang = "mermaid" } = options;
1312
+ return (tree) => {
1313
+ visit9(tree, "code", (node) => {
1314
+ if (node.lang !== lang || !node.value) return;
1315
+ Object.assign(node, toMDX(node.value));
1316
+ });
1317
+ };
1318
+ }
922
1319
  export {
1320
+ generateCodeBlockTabs,
1321
+ parseCodeBlockAttributes,
923
1322
  rehypeCode,
924
1323
  rehypeCodeDefaultOptions,
925
1324
  rehypeToc,
@@ -928,6 +1327,9 @@ export {
928
1327
  default2 as remarkGfm,
929
1328
  remarkHeading,
930
1329
  remarkImage,
1330
+ remarkMdxFiles,
1331
+ remarkMdxMermaid,
1332
+ remarkNpm,
931
1333
  remarkSteps,
932
1334
  remarkStructure,
933
1335
  structure,