fumadocs-core 15.2.6 → 15.2.8

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.
@@ -33,7 +33,7 @@ function remarkHeading({
33
33
  lastNode.value = lastNode.value.slice(0, match.index);
34
34
  }
35
35
  }
36
- let flattened;
36
+ let flattened = null;
37
37
  if (!id) {
38
38
  flattened ??= flattenNode(heading);
39
39
  id = defaultSlug ? defaultSlug(root, heading, flattened) : slugger.slug(flattened);
@@ -4,8 +4,9 @@ import { RehypeShikiOptions } from '@shikijs/rehype';
4
4
  import { Processor, Transformer } from 'unified';
5
5
  import { ShikiTransformer } from 'shiki';
6
6
  import { Root as Root$1 } from 'mdast';
7
- export { a as StructureOptions, S as StructuredData, r as remarkStructure, s as structure } from '../remark-structure-1kvQbrfH.js';
7
+ export { a as StructureOptions, S as StructuredData, r as remarkStructure, s as structure } from '../remark-structure-FIjTA11P.js';
8
8
  export { R as RemarkHeadingOptions, r as remarkHeading } from '../remark-heading-BPCoYwjn.js';
9
+ import 'mdast-util-mdx-jsx';
9
10
 
10
11
  interface CodeBlockIcon {
11
12
  viewBox: string;
@@ -113,4 +114,23 @@ declare function rehypeToc(this: Processor, { exportToc }?: RehypeTocOptions): T
113
114
 
114
115
  declare function remarkCodeTab(): Transformer<Root$1, Root$1>;
115
116
 
116
- export { type CodeBlockIcon, type RehypeCodeOptions, type RehypeTocOptions, type RemarkAdmonitionOptions, type RemarkImageOptions, rehypeCode, rehypeCodeDefaultOptions, rehypeToc, remarkAdmonition, remarkCodeTab, remarkImage, transformerIcon, transformerTab };
117
+ interface RemarkStepsOptions {
118
+ /**
119
+ * Class name for steps container
120
+ *
121
+ * @defaultValue fd-steps
122
+ */
123
+ steps?: string;
124
+ /**
125
+ * Class name for step container
126
+ *
127
+ * @defaultValue fd-step
128
+ */
129
+ step?: string;
130
+ }
131
+ /**
132
+ * Convert headings in the format of `1. Hello World` into steps.
133
+ */
134
+ declare function remarkSteps({ steps, step, }?: RemarkStepsOptions): Transformer<Root$1, Root$1>;
135
+
136
+ export { type CodeBlockIcon, type RehypeCodeOptions, type RehypeTocOptions, type RemarkAdmonitionOptions, type RemarkImageOptions, type RemarkStepsOptions, rehypeCode, rehypeCodeDefaultOptions, rehypeToc, remarkAdmonition, remarkCodeTab, remarkImage, remarkSteps, transformerIcon, transformerTab };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  flattenNode,
3
3
  remarkHeading
4
- } from "../chunk-IYQ35KI2.js";
4
+ } from "../chunk-Y2774T3B.js";
5
5
  import {
6
6
  joinPath,
7
7
  slash
@@ -185,10 +185,6 @@ var metaValues = [
185
185
  {
186
186
  name: "tab",
187
187
  regex: /tab="(?<value>[^"]+)"/
188
- },
189
- {
190
- name: "tab",
191
- regex: /tab/
192
188
  }
193
189
  ];
194
190
  var rehypeCodeDefaultOptions = {
@@ -281,7 +277,7 @@ function transformerTab() {
281
277
  data: {
282
278
  _codeblock: true
283
279
  },
284
- attributes: value !== "" ? [{ type: "mdxJsxAttribute", name: "value", value }] : [],
280
+ attributes: [{ type: "mdxJsxAttribute", name: "value", value }],
285
281
  children: root.children
286
282
  }
287
283
  ]
@@ -447,8 +443,23 @@ import remarkGfm from "remark-gfm";
447
443
  import { visit as visit2 } from "unist-util-visit";
448
444
  var slugger = new Slugger();
449
445
  function remarkStructure({
450
- types = ["paragraph", "blockquote", "heading", "tableCell"]
446
+ types = [
447
+ "heading",
448
+ "paragraph",
449
+ "blockquote",
450
+ "tableCell",
451
+ "mdxJsxFlowElement"
452
+ ],
453
+ allowedMdxAttributes = () => true
451
454
  } = {}) {
455
+ if (Array.isArray(allowedMdxAttributes)) {
456
+ const arr = allowedMdxAttributes;
457
+ allowedMdxAttributes = (_node, attribute) => attribute.type === "mdxJsxAttribute" && arr.includes(attribute.name);
458
+ }
459
+ if (Array.isArray(types)) {
460
+ const arr = types;
461
+ types = (node) => arr.includes(node.type);
462
+ }
452
463
  return (node, file) => {
453
464
  slugger.reset();
454
465
  const data = { contents: [], headings: [] };
@@ -462,24 +473,25 @@ function remarkStructure({
462
473
  }
463
474
  visit2(node, (element) => {
464
475
  if (element.type === "root") return;
476
+ if (!types(element)) return;
465
477
  if (element.type === "heading") {
466
478
  element.data ||= {};
467
479
  element.data.hProperties ||= {};
468
480
  const properties = element.data.hProperties;
469
- const content = flattenNode(element).trim();
470
- const id = properties.id ?? slugger.slug(content);
481
+ const content2 = flattenNode(element).trim();
482
+ const id = properties.id ?? slugger.slug(content2);
471
483
  data.headings.push({
472
484
  id,
473
- content
485
+ content: content2
474
486
  });
475
487
  lastHeading = id;
476
488
  return "skip";
477
489
  }
478
490
  if (element.data?._string) {
479
- for (const content of element.data._string) {
491
+ for (const content2 of element.data._string) {
480
492
  data.contents.push({
481
493
  heading: lastHeading,
482
- content
494
+ content: content2
483
495
  });
484
496
  }
485
497
  return "skip";
@@ -493,6 +505,8 @@ function remarkStructure({
493
505
  ...element.attributes.flatMap((attribute) => {
494
506
  const valueStr = typeof attribute.value === "string" ? attribute.value : attribute.value?.value;
495
507
  if (!valueStr) return [];
508
+ if (allowedMdxAttributes && !allowedMdxAttributes(element, attribute))
509
+ return [];
496
510
  return {
497
511
  heading: lastHeading,
498
512
  content: attribute.type === "mdxJsxAttribute" ? `${attribute.name}: ${valueStr}` : valueStr
@@ -501,15 +515,13 @@ function remarkStructure({
501
515
  );
502
516
  return;
503
517
  }
504
- if (types.includes(element.type)) {
505
- const content = flattenNode(element).trim();
506
- if (content.length === 0) return;
507
- data.contents.push({
508
- heading: lastHeading,
509
- content
510
- });
511
- return "skip";
512
- }
518
+ const content = flattenNode(element).trim();
519
+ if (content.length === 0) return;
520
+ data.contents.push({
521
+ heading: lastHeading,
522
+ content
523
+ });
524
+ return "skip";
513
525
  });
514
526
  file.data.structuredData = data;
515
527
  };
@@ -787,32 +799,123 @@ function toTab(nodes) {
787
799
  function remarkCodeTab() {
788
800
  return (tree) => {
789
801
  visit5(tree, (node) => {
802
+ if (!("children" in node)) return;
790
803
  if (node.type === "mdxJsxFlowElement" && node.name === "Tabs") return;
791
- if ("children" in node) {
792
- let start = -1;
793
- let i = 0;
794
- while (i < node.children.length) {
795
- const child = node.children[i];
796
- const isSwitcher = child.type === "code" && child.meta && child.meta.match(TabRegex);
797
- if (isSwitcher && start === -1) {
798
- start = i;
799
- }
800
- const isLast = i === node.children.length - 1;
801
- if (start !== -1 && (isLast || !isSwitcher)) {
802
- const end = isSwitcher ? i + 1 : i;
803
- const targets = node.children.slice(start, end);
804
- node.children.splice(
805
- start,
806
- end - start,
807
- toTab(targets)
808
- );
809
- if (isLast) break;
810
- i = start;
811
- start = -1;
812
- }
813
- i++;
804
+ 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;
811
+ }
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;
824
+ }
825
+ i++;
826
+ }
827
+ });
828
+ };
829
+ }
830
+
831
+ // src/mdx-plugins/remark-steps.ts
832
+ import { visit as visit6 } from "unist-util-visit";
833
+ var StepRegex = /^(\d+)\.\s(.+)$/;
834
+ function remarkSteps({
835
+ steps = "fd-steps",
836
+ step = "fd-step"
837
+ } = {}) {
838
+ function convertToSteps(nodes) {
839
+ const depth = nodes[0].depth;
840
+ const children = [];
841
+ for (const node of nodes) {
842
+ if (node.type === "heading" && node.depth === depth) {
843
+ children.push({
844
+ type: "mdxJsxFlowElement",
845
+ name: "div",
846
+ attributes: [
847
+ {
848
+ type: "mdxJsxAttribute",
849
+ name: "className",
850
+ value: step
851
+ }
852
+ ],
853
+ children: [node]
854
+ });
855
+ } else {
856
+ children[children.length - 1].children.push(node);
857
+ }
858
+ }
859
+ return {
860
+ type: "mdxJsxFlowElement",
861
+ name: "div",
862
+ attributes: [
863
+ {
864
+ type: "mdxJsxAttribute",
865
+ name: "className",
866
+ value: steps
867
+ }
868
+ ],
869
+ data: {
870
+ _fd_step: true
871
+ },
872
+ children
873
+ };
874
+ }
875
+ return (tree) => {
876
+ visit6(tree, (parent) => {
877
+ if (!("children" in parent) || parent.type === "heading") return;
878
+ if (parent.data && "_fd_step" in parent.data) return "skip";
879
+ let startIdx = -1;
880
+ let lastNumber = 0;
881
+ let i = 0;
882
+ const onEnd = () => {
883
+ if (startIdx === -1) return;
884
+ const item = {};
885
+ const nodes = parent.children.splice(
886
+ startIdx,
887
+ i - startIdx,
888
+ item
889
+ );
890
+ Object.assign(item, convertToSteps(nodes));
891
+ i = startIdx + 1;
892
+ startIdx = -1;
893
+ };
894
+ for (; i < parent.children.length; i++) {
895
+ const node = parent.children[i];
896
+ if (node.type !== "heading") continue;
897
+ if (startIdx !== -1) {
898
+ const startDepth = parent.children[startIdx].depth;
899
+ if (node.depth > startDepth) continue;
900
+ else if (node.depth < startDepth) onEnd();
901
+ }
902
+ const head = node.children.filter((c) => c.type === "text").at(0);
903
+ if (!head) {
904
+ onEnd();
905
+ continue;
906
+ }
907
+ const match = StepRegex.exec(head.value);
908
+ if (!match) {
909
+ onEnd();
910
+ continue;
814
911
  }
912
+ const num = Number(match[1]);
913
+ head.value = match[2];
914
+ if (startIdx !== -1 && num !== lastNumber + 1) onEnd();
915
+ if (startIdx === -1) startIdx = i;
916
+ lastNumber = num;
815
917
  }
918
+ onEnd();
816
919
  });
817
920
  };
818
921
  }
@@ -825,6 +928,7 @@ export {
825
928
  default2 as remarkGfm,
826
929
  remarkHeading,
827
930
  remarkImage,
931
+ remarkSteps,
828
932
  remarkStructure,
829
933
  structure,
830
934
  transformerIcon,
@@ -1,5 +1,6 @@
1
- import { Root } from 'mdast';
1
+ import { Nodes, Root } from 'mdast';
2
2
  import { Transformer, PluggableList } from 'unified';
3
+ import { MdxJsxAttribute, MdxJsxExpressionAttribute } from 'mdast-util-mdx-jsx';
3
4
 
4
5
  interface Heading {
5
6
  id: string;
@@ -20,9 +21,10 @@ interface StructureOptions {
20
21
  /**
21
22
  * Types to be scanned as content.
22
23
  *
23
- * @defaultValue ['paragraph', 'blockquote', 'tableCell']
24
+ * @defaultValue ['heading', 'paragraph', 'blockquote', 'tableCell', 'mdxJsxFlowElement']
24
25
  */
25
- types?: string[];
26
+ types?: string[] | ((node: Nodes) => boolean);
27
+ allowedMdxAttributes?: string[] | ((node: Nodes, attribute: MdxJsxAttribute | MdxJsxExpressionAttribute) => boolean);
26
28
  }
27
29
  declare module 'mdast' {
28
30
  interface Data {
@@ -37,7 +39,7 @@ declare module 'mdast' {
37
39
  /**
38
40
  * Attach structured data to VFile, you can access via `vfile.data.structuredData`.
39
41
  */
40
- declare function remarkStructure({ types, }?: StructureOptions): Transformer<Root, Root>;
42
+ declare function remarkStructure({ types, allowedMdxAttributes, }?: StructureOptions): Transformer<Root, Root>;
41
43
  /**
42
44
  * Extract data from markdown/mdx content
43
45
  */
@@ -1,7 +1,8 @@
1
1
  import { SearchClient, SearchIndex } from 'algoliasearch';
2
- import { S as StructuredData } from '../remark-structure-1kvQbrfH.js';
2
+ import { S as StructuredData } from '../remark-structure-FIjTA11P.js';
3
3
  import 'mdast';
4
4
  import 'unified';
5
+ import 'mdast-util-mdx-jsx';
5
6
 
6
7
  interface DocumentRecord {
7
8
  /**
@@ -2,10 +2,11 @@ import { S as SortedResult } from '../types-Ch8gnVgO.js';
2
2
  import { AnyOrama } from '@orama/orama';
3
3
  import { SearchOptions } from '@algolia/client-search';
4
4
  import { SearchIndex } from 'algoliasearch/lite';
5
- import '../remark-structure-1kvQbrfH.js';
5
+ import '../remark-structure-FIjTA11P.js';
6
6
  import { OramaClient, ClientSearchParams } from '@oramacloud/client';
7
7
  import 'mdast';
8
8
  import 'unified';
9
+ import 'mdast-util-mdx-jsx';
9
10
 
10
11
  interface FetchOptions {
11
12
  /**
@@ -1,8 +1,9 @@
1
1
  import { CloudManager } from '@oramacloud/client';
2
- import { S as StructuredData } from '../remark-structure-1kvQbrfH.js';
2
+ import { S as StructuredData } from '../remark-structure-FIjTA11P.js';
3
3
  import '../remark-heading-BPCoYwjn.js';
4
4
  import 'mdast';
5
5
  import 'unified';
6
+ import 'mdast-util-mdx-jsx';
6
7
 
7
8
  interface SyncOptions {
8
9
  /**
@@ -1,10 +1,11 @@
1
1
  import { TypedDocument, Orama, Language, RawData, create, SearchParams } from '@orama/orama';
2
- import { S as StructuredData } from '../remark-structure-1kvQbrfH.js';
2
+ import { S as StructuredData } from '../remark-structure-FIjTA11P.js';
3
3
  import { S as SortedResult } from '../types-Ch8gnVgO.js';
4
4
  import { I as I18nConfig } from '../config-inq6kP6y.js';
5
5
  import { LoaderOutput, LoaderConfig, InferPageType } from '../source/index.js';
6
6
  import 'mdast';
7
7
  import 'unified';
8
+ import 'mdast-util-mdx-jsx';
8
9
  import 'react';
9
10
  import '../page-tree-BG3wP0gU.js';
10
11
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  remarkHeading
3
- } from "../chunk-IYQ35KI2.js";
3
+ } from "../chunk-Y2774T3B.js";
4
4
  import "../chunk-MLKGABMK.js";
5
5
 
6
6
  // src/server/get-toc.ts
@@ -151,7 +151,7 @@ function build(ctx) {
151
151
  const root = ctx.storage.root();
152
152
  const folder = buildFolderNode(root, true, ctx);
153
153
  return {
154
- $id: "root",
154
+ $id: ctx.locale ? ctx.locale : "root",
155
155
  name: folder.name,
156
156
  children: folder.children
157
157
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-core",
3
- "version": "15.2.6",
3
+ "version": "15.2.8",
4
4
  "description": "The library for building a documentation website in Next.js",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -78,9 +78,9 @@
78
78
  ],
79
79
  "dependencies": {
80
80
  "@formatjs/intl-localematcher": "^0.6.1",
81
- "@orama/orama": "^3.1.4",
82
- "@shikijs/rehype": "^3.2.1",
83
- "@shikijs/transformers": "^3.2.1",
81
+ "@orama/orama": "^3.1.6",
82
+ "@shikijs/rehype": "^3.2.2",
83
+ "@shikijs/transformers": "^3.2.2",
84
84
  "github-slugger": "^2.0.0",
85
85
  "hast-util-to-estree": "^3.1.3",
86
86
  "hast-util-to-jsx-runtime": "^2.3.6",
@@ -90,26 +90,26 @@
90
90
  "remark": "^15.0.0",
91
91
  "remark-gfm": "^4.0.1",
92
92
  "scroll-into-view-if-needed": "^3.1.0",
93
- "shiki": "^3.2.1",
93
+ "shiki": "^3.2.2",
94
94
  "unist-util-visit": "^5.0.0"
95
95
  },
96
96
  "devDependencies": {
97
97
  "@algolia/client-search": "4.24.0",
98
98
  "@mdx-js/mdx": "^3.1.0",
99
99
  "@oramacloud/client": "^2.1.4",
100
- "@tanstack/react-router": "^1.115.2",
100
+ "@tanstack/react-router": "^1.116.0",
101
101
  "@types/estree-jsx": "^1.0.5",
102
102
  "@types/hast": "^3.0.4",
103
103
  "@types/mdast": "^4.0.3",
104
104
  "@types/negotiator": "^0.6.3",
105
- "@types/node": "22.14.0",
106
- "@types/react": "^19.1.0",
107
- "@types/react-dom": "^19.1.1",
105
+ "@types/node": "22.14.1",
106
+ "@types/react": "^19.1.2",
107
+ "@types/react-dom": "^19.1.2",
108
108
  "algoliasearch": "4.24.0",
109
109
  "mdast-util-mdx-jsx": "^3.2.0",
110
110
  "mdast-util-mdxjs-esm": "^2.0.1",
111
- "next": "^15.2.4",
112
- "react-router": "^7.5.0",
111
+ "next": "^15.3.1",
112
+ "react-router": "^7.5.1",
113
113
  "remark-mdx": "^3.1.0",
114
114
  "remark-rehype": "^11.1.2",
115
115
  "typescript": "^5.8.3",