fumadocs-core 10.1.3 → 11.0.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.
@@ -52,33 +52,6 @@ interface RemarkImageOptions {
52
52
  */
53
53
  declare function remarkImage({ placeholder, }?: RemarkImageOptions): Transformer<Root$1, Root$1>;
54
54
 
55
- interface RemarkDynamicContentOptions {
56
- /**
57
- * @defaultValue `vfile.cwd`
58
- * @deprecated use the `cwd` option from remark instead
59
- * */
60
- cwd?: string;
61
- /** @defaultValue true */
62
- trim?: boolean;
63
- /**
64
- * Resolve reference files relative to `vfile.path`
65
- * @defaultValue false
66
- */
67
- relative?: boolean;
68
- /**
69
- * Filter specific element types
70
- * @defaultValue ['text','code']
71
- * */
72
- visit?: string[];
73
- }
74
- /**
75
- * Copy content from referenced file
76
- *
77
- * @example
78
- * |reference:../path/to/file.ts|
79
- */
80
- declare function remarkDynamicContent(options?: RemarkDynamicContentOptions): Transformer<Root$1, Root$1>;
81
-
82
55
  declare module 'mdast' {
83
56
  interface HeadingData extends Data {
84
57
  hProperties?: {
@@ -93,29 +66,4 @@ declare module 'mdast' {
93
66
  */
94
67
  declare function remarkHeading(): Transformer<Root$1, Root$1>;
95
68
 
96
- interface PackageManager {
97
- name: string;
98
- /**
99
- * Convert from npm to another package manager
100
- */
101
- command: (command: string) => string;
102
- }
103
- type RemarkInstallOptions = Partial<{
104
- Tabs: string;
105
- Tab: string;
106
- packageManagers: PackageManager[];
107
- }>;
108
- /**
109
- * It generates the following structure from a code block with `package-install` as language
110
- *
111
- * @example
112
- * ```tsx
113
- * <Tabs items={["npm", "pnpm", "yarn", "bun"]}>
114
- * <Tab value="pnpm">...</Tab>
115
- * ...
116
- * </Tabs>
117
- * ```
118
- */
119
- declare function remarkInstall({ Tab, Tabs, packageManagers, }?: RemarkInstallOptions): Transformer<Root$1, Root$1>;
120
-
121
- export { type CodeBlockIcon, type RehypeCodeOptions, type RemarkDynamicContentOptions, type RemarkImageOptions, type RemarkInstallOptions, rehypeCode, rehypeCodeDefaultOptions, remarkDynamicContent, remarkHeading, remarkImage, remarkInstall, transformerIcon };
69
+ export { type CodeBlockIcon, type RehypeCodeOptions, type RemarkImageOptions, rehypeCode, rehypeCodeDefaultOptions, remarkHeading, remarkImage, transformerIcon };
@@ -450,42 +450,12 @@ function remarkImage({
450
450
  };
451
451
  }
452
452
 
453
- // src/mdx-plugins/remark-dynamic-content.ts
454
- import fs from "fs";
455
- import path2 from "path";
456
- import { visit as visit3 } from "unist-util-visit";
457
- var regex = new RegExp("^\\|reference:(?<path>.+)\\|");
458
- function remarkDynamicContent(options = {}) {
459
- const {
460
- trim = true,
461
- relative = false,
462
- visit: filter = ["text", "code"]
463
- } = options;
464
- return (tree, file) => {
465
- var _a;
466
- const cwd = (_a = options.cwd) != null ? _a : file.cwd;
467
- visit3(tree, filter, (node) => {
468
- const canReplace = "value" in node && typeof node.value === "string";
469
- if (!canReplace)
470
- return;
471
- const result = regex.exec(node.value);
472
- if (result) {
473
- const dest = relative ? path2.resolve(cwd, path2.dirname(file.path), result[1]) : path2.resolve(cwd, result[1]);
474
- let value = fs.readFileSync(dest).toString();
475
- if (trim)
476
- value = value.trim();
477
- node.value = value;
478
- }
479
- });
480
- };
481
- }
482
-
483
453
  // src/mdx-plugins/remark-structure.ts
484
454
  import Slugger from "github-slugger";
485
455
  import { remark } from "remark";
486
456
  import remarkGfm from "remark-gfm";
487
457
  import remarkMdx from "remark-mdx";
488
- import { visit as visit4 } from "unist-util-visit";
458
+ import { visit as visit3 } from "unist-util-visit";
489
459
  var slugger = new Slugger();
490
460
  function remarkStructure({
491
461
  types = ["paragraph", "blockquote", "heading"]
@@ -494,7 +464,7 @@ function remarkStructure({
494
464
  slugger.reset();
495
465
  const data = { contents: [], headings: [] };
496
466
  let lastHeading = "";
497
- visit4(node, types, (element) => {
467
+ visit3(node, types, (element) => {
498
468
  var _a, _b;
499
469
  if (element.type === "root")
500
470
  return;
@@ -526,78 +496,13 @@ function structure(content, remarkPlugins = [], options = {}) {
526
496
  const result = remark().use(remarkGfm).use(remarkMdx).use(remarkPlugins).use(remarkStructure, options).processSync(content);
527
497
  return result.data.structuredData;
528
498
  }
529
-
530
- // src/mdx-plugins/remark-install.ts
531
- import { visit as visit5 } from "unist-util-visit";
532
- import convert from "npm-to-yarn";
533
- function remarkInstall({
534
- Tab = "Tab",
535
- Tabs = "Tabs",
536
- packageManagers = [
537
- { command: (cmd) => convert(cmd, "npm"), name: "npm" },
538
- { command: (cmd) => convert(cmd, "pnpm"), name: "pnpm" },
539
- { command: (cmd) => convert(cmd, "yarn"), name: "yarn" },
540
- { command: (cmd) => convert(cmd, "bun"), name: "bun" }
541
- ]
542
- } = {}) {
543
- return (tree) => {
544
- visit5(tree, "code", (node) => {
545
- if (node.lang !== "package-install")
546
- return "skip";
547
- const value = node.value.startsWith("npm") ? node.value : `npm install ${node.value}`;
548
- const insert = {
549
- type: "mdxJsxFlowElement",
550
- name: Tabs,
551
- attributes: [
552
- {
553
- type: "mdxJsxAttribute",
554
- name: "items",
555
- value: {
556
- type: "mdxJsxAttributeValueExpression",
557
- data: {
558
- estree: {
559
- body: [
560
- {
561
- type: "ExpressionStatement",
562
- expression: {
563
- type: "ArrayExpression",
564
- elements: packageManagers.map(({ name }) => ({
565
- type: "Literal",
566
- value: name
567
- }))
568
- }
569
- }
570
- ]
571
- }
572
- }
573
- }
574
- }
575
- ],
576
- children: packageManagers.map(({ command, name }) => ({
577
- type: "mdxJsxFlowElement",
578
- name: Tab,
579
- attributes: [{ type: "mdxJsxAttribute", name: "value", value: name }],
580
- children: [
581
- {
582
- type: "code",
583
- lang: "bash",
584
- value: command(value)
585
- }
586
- ]
587
- }))
588
- };
589
- Object.assign(node, insert);
590
- });
591
- };
592
- }
593
499
  export {
594
500
  rehypeCode,
595
501
  rehypeCodeDefaultOptions,
596
- remarkDynamicContent,
597
502
  default2 as remarkGfm,
598
503
  remarkHeading,
599
504
  remarkImage,
600
- remarkInstall,
601
505
  remarkStructure,
602
- structure
506
+ structure,
507
+ transformerIcon
603
508
  };
@@ -1,18 +1,8 @@
1
- import { T as TableOfContents } from '../get-toc-YF_TdazL.js';
2
- export { a as TOCItemType, g as getTableOfContents } from '../get-toc-YF_TdazL.js';
1
+ export { a as TOCItemType, T as TableOfContents, g as getTableOfContents } from '../get-toc-YF_TdazL.js';
3
2
  import { N as Node, I as Item, R as Root } from '../page-tree-mLHMYx2B.js';
4
3
  export { p as PageTree } from '../page-tree-mLHMYx2B.js';
5
4
  import 'react';
6
5
 
7
- /**
8
- * Parse TOC from portable text (Sanity)
9
- *
10
- * @param value - Blocks
11
- * @param slugFn - A function that generates slug from title
12
- * @deprecated Sanity is no longer supported
13
- */
14
- declare function getTableOfContentsFromPortableText(value: any): TableOfContents;
15
-
16
6
  /**
17
7
  * Flatten tree to an array of page nodes
18
8
  */
@@ -55,4 +45,4 @@ interface GetGithubLastCommitOptions {
55
45
  */
56
46
  declare function getGithubLastEdit({ repo, token, owner, path, options, params: customParams, }: GetGithubLastCommitOptions): Promise<Date | null>;
57
47
 
58
- export { type GetGithubLastCommitOptions, TableOfContents, findNeighbour, flattenTree, getGithubLastEdit, getTableOfContentsFromPortableText, separatePageTree };
48
+ export { type GetGithubLastCommitOptions, findNeighbour, flattenTree, getGithubLastEdit, separatePageTree };
@@ -16,50 +16,6 @@ function getTableOfContents(content) {
16
16
  });
17
17
  }
18
18
 
19
- // src/server/get-toc-sanity.ts
20
- import Slugger from "github-slugger";
21
- var slugger = new Slugger();
22
- function getTableOfContentsFromPortableText(value) {
23
- if (!Array.isArray(value)) {
24
- throw new Error("Invalid body type");
25
- }
26
- slugger.reset();
27
- const result = [];
28
- for (const block of value) {
29
- dfs(block, result);
30
- }
31
- return result;
32
- }
33
- function dfs(block, list) {
34
- var _a;
35
- if (block.style && block.style.length === 2 && block.style.startsWith("h")) {
36
- const depth = Number(block.style[1]);
37
- if (Number.isNaN(depth))
38
- return;
39
- const text = flattenNode(block);
40
- list.push({
41
- title: text,
42
- url: slugger.slug(text),
43
- depth
44
- });
45
- return;
46
- }
47
- (_a = block.children) == null ? void 0 : _a.forEach((child) => {
48
- dfs(child, list);
49
- });
50
- }
51
- function flattenNode(block) {
52
- var _a, _b;
53
- let text = "";
54
- if (block._type === "span") {
55
- return (_a = block.text) != null ? _a : "";
56
- }
57
- (_b = block.children) == null ? void 0 : _b.forEach((child) => {
58
- text += flattenNode(child);
59
- });
60
- return text;
61
- }
62
-
63
19
  // src/server/page-tree-utils.ts
64
20
  function flattenTree(tree) {
65
21
  return tree.flatMap((node) => {
@@ -143,6 +99,5 @@ export {
143
99
  flattenTree,
144
100
  getGithubLastEdit,
145
101
  getTableOfContents,
146
- getTableOfContentsFromPortableText,
147
102
  separatePageTree
148
103
  };
@@ -20,24 +20,108 @@ interface FileInfo {
20
20
  declare function parseFilePath(path: string): FileInfo;
21
21
  declare function parseFolderPath(path: string): FileInfo;
22
22
 
23
+ interface File {
24
+ file: FileInfo;
25
+ format: 'meta' | 'page';
26
+ data: Record<string, unknown>;
27
+ }
28
+ interface Folder {
29
+ file: FileInfo;
30
+ children: (File | Folder)[];
31
+ }
32
+ /**
33
+ * A virtual file system that solves inconsistent behaviours
34
+ *
35
+ * Some source providers may not provide the full file structure, this will cause inconsistent outputs for page builder and other transformers
36
+ */
37
+ declare class Storage {
38
+ files: Map<string, File>;
39
+ folders: Map<string, Folder>;
40
+ private rootFolder;
41
+ constructor();
42
+ /**
43
+ * @param path - flattened path
44
+ */
45
+ read(path: string, format: string): File | undefined;
46
+ readDir(path: string): Folder | undefined;
47
+ root(): Folder;
48
+ write(path: string, format: 'meta' | 'page', data: Record<string, unknown>): void;
49
+ list(): File[];
50
+ makeDir(path: string): void;
51
+ }
52
+
53
+ type fileSystem_File = File;
54
+ type fileSystem_Folder = Folder;
55
+ type fileSystem_Storage = Storage;
56
+ declare const fileSystem_Storage: typeof Storage;
57
+ declare namespace fileSystem {
58
+ export { type fileSystem_File as File, type fileSystem_Folder as Folder, fileSystem_Storage as Storage };
59
+ }
60
+
61
+ interface BuildPageTreeOptionsWithI18n {
62
+ languages: string[];
63
+ }
64
+ interface PageTreeBuilder {
65
+ build: () => Root;
66
+ /**
67
+ * Build page tree and fallback to the default language if the page doesn't exist
68
+ */
69
+ buildI18n: (options?: Partial<BuildPageTreeOptionsWithI18n>) => Record<string, Root>;
70
+ }
71
+ interface CreatePageTreeBuilderOptions {
72
+ storage: Storage;
73
+ resolveIcon?: (icon: string) => ReactElement | undefined;
74
+ }
75
+ declare function createPageTreeBuilder({ storage, resolveIcon, }: CreatePageTreeBuilderOptions): PageTreeBuilder;
76
+
23
77
  interface LoadOptions {
24
- files: VirtualFile[];
25
78
  transformers?: Transformer[];
26
- rootDir: string;
79
+ rootDir?: string;
27
80
  getSlugs: (info: FileInfo) => string[];
28
81
  getUrl: (slugs: string[], locale?: string) => string;
29
82
  }
30
83
  interface VirtualFile {
84
+ /**
85
+ * Relative path
86
+ *
87
+ * @example `docs/page.mdx`
88
+ */
31
89
  path: string;
32
90
  type: 'page' | 'meta';
33
91
  data: unknown;
34
92
  }
35
93
  type Transformer = (context: {
36
94
  storage: Storage;
37
- getSlugs: (info: FileInfo) => string[];
38
- getUrl: (slugs: string[], locale?: string) => string;
95
+ options: LoadOptions;
39
96
  }) => void;
40
97
 
98
+ interface MetaData {
99
+ icon?: string;
100
+ title?: string;
101
+ root?: boolean;
102
+ pages?: string[];
103
+ defaultOpen?: boolean;
104
+ }
105
+ interface PageData {
106
+ icon?: string;
107
+ title: string;
108
+ }
109
+ type InferPageType<Utils extends LoaderOutput<any>> = Utils extends LoaderOutput<infer Config> ? Page<Config['source']['pageData']> : never;
110
+ type InferMetaType<Utils extends LoaderOutput<any>> = Utils extends LoaderOutput<infer Config> ? Meta<Config['source']['metaData']> : never;
111
+ /**
112
+ * @internal
113
+ */
114
+ interface FileData {
115
+ meta: {
116
+ data: MetaData;
117
+ };
118
+ file: {
119
+ url: string;
120
+ slugs: string[];
121
+ data: PageData;
122
+ };
123
+ }
124
+
41
125
  interface LoaderConfig {
42
126
  source: SourceConfig;
43
127
  i18n: boolean;
@@ -69,9 +153,19 @@ interface Source<Config extends SourceConfig> {
69
153
  _config?: Config;
70
154
  files: VirtualFile[] | ((rootDir: string) => VirtualFile[]);
71
155
  }
156
+ interface Page<Data = PageData> {
157
+ file: FileInfo;
158
+ slugs: string[];
159
+ url: string;
160
+ data: Data;
161
+ }
162
+ interface Meta<Data = MetaData> {
163
+ file: FileInfo;
164
+ data: Data;
165
+ }
72
166
  interface LoaderOutput<Config extends LoaderConfig> {
73
167
  pageTree: Config['i18n'] extends true ? Record<string, Root> : Root;
74
- files: (Meta<Config['source']['metaData']> | Page<Config['source']['pageData']>)[];
168
+ files: File[];
75
169
  /**
76
170
  * Get list of pages from language, empty if language hasn't specified
77
171
  *
@@ -91,89 +185,4 @@ declare function loader<Options extends LoaderOptions>(options: Options): Loader
91
185
  i18n: Options['languages'] extends string[] ? true : false;
92
186
  }>;
93
187
 
94
- interface MetaData {
95
- icon?: string;
96
- title?: string;
97
- root?: boolean;
98
- pages?: string[];
99
- defaultOpen?: boolean;
100
- }
101
- interface PageData {
102
- icon?: string;
103
- title: string;
104
- }
105
- type InferPageType<Utils extends LoaderOutput<any>> = Utils extends LoaderOutput<infer Config> ? Page<Config['source']['pageData']> : never;
106
- type InferMetaType<Utils extends LoaderOutput<any>> = Utils extends LoaderOutput<infer Config> ? Meta<Config['source']['metaData']> : never;
107
-
108
- interface Meta<Data extends MetaData = MetaData> {
109
- type: 'meta';
110
- file: FileInfo;
111
- data: Data;
112
- }
113
- interface Page<Data extends PageData = PageData> {
114
- type: 'page';
115
- file: FileInfo;
116
- slugs: string[];
117
- url: string;
118
- data: Data;
119
- }
120
- interface Folder<MD extends MetaData = MetaData, PD extends PageData = PageData> {
121
- type: 'folder';
122
- file: FileInfo;
123
- children: (Meta<MD> | Page<PD> | Folder<MD, PD>)[];
124
- }
125
- /**
126
- * A virtual file system that solves inconsistent behaviours
127
- *
128
- * Some source providers may not provide the full file structure, this will cause inconsistent outputs for page builder and other transformers
129
- */
130
- declare class Storage {
131
- files: Map<string, Page<PageData> | Meta<MetaData>>;
132
- folders: Map<string, Folder<MetaData, PageData>>;
133
- private rootFolder;
134
- constructor();
135
- /**
136
- * @param path - flattened path
137
- */
138
- private read;
139
- /**
140
- * @param path - flattened path
141
- */
142
- readPage(path: string): Page | undefined;
143
- /**
144
- * @param path - flattened path
145
- */
146
- readMeta(path: string): Meta | undefined;
147
- readDir(path: string): Folder | undefined;
148
- root(): Folder;
149
- write(path: string, file: Omit<Page, 'file'> | Omit<Meta, 'file'>): void;
150
- list(): (Page | Meta)[];
151
- makeDir(path: string): void;
152
- }
153
-
154
- type fileSystem_Folder<MD extends MetaData = MetaData, PD extends PageData = PageData> = Folder<MD, PD>;
155
- type fileSystem_Meta<Data extends MetaData = MetaData> = Meta<Data>;
156
- type fileSystem_Page<Data extends PageData = PageData> = Page<Data>;
157
- type fileSystem_Storage = Storage;
158
- declare const fileSystem_Storage: typeof Storage;
159
- declare namespace fileSystem {
160
- export { type fileSystem_Folder as Folder, type fileSystem_Meta as Meta, type fileSystem_Page as Page, fileSystem_Storage as Storage };
161
- }
162
-
163
- interface BuildPageTreeOptionsWithI18n {
164
- languages: string[];
165
- }
166
- interface PageTreeBuilder {
167
- build: () => Root;
168
- /**
169
- * Build page tree and fallback to the default language if the page doesn't exist
170
- */
171
- buildI18n: (options?: Partial<BuildPageTreeOptionsWithI18n>) => Record<string, Root>;
172
- }
173
- interface CreatePageTreeBuilderOptions {
174
- storage: Storage;
175
- resolveIcon?: (icon: string) => ReactElement | undefined;
176
- }
177
- declare function createPageTreeBuilder({ storage, resolveIcon, }: CreatePageTreeBuilderOptions): PageTreeBuilder;
178
-
179
- export { type BuildPageTreeOptionsWithI18n, type CreatePageTreeBuilderOptions, type FileInfo, fileSystem as FileSystem, type InferMetaType, type InferPageType, type LoadOptions, type LoaderConfig, type LoaderOptions, type LoaderOutput, type MetaData, type PageData, type PageTreeBuilder, type Source, type SourceConfig, type Transformer, type VirtualFile, createGetUrl, createPageTreeBuilder, getSlugs, loader, parseFilePath, parseFolderPath };
188
+ export { type BuildPageTreeOptionsWithI18n, type CreatePageTreeBuilderOptions, type FileData, type FileInfo, fileSystem as FileSystem, type InferMetaType, type InferPageType, type LoadOptions, type LoaderConfig, type LoaderOptions, type LoaderOutput, type Meta, type MetaData, type Page, type PageData, type PageTreeBuilder, type Source, type SourceConfig, type Transformer, type VirtualFile, createGetUrl, createPageTreeBuilder, getSlugs, loader, parseFilePath, parseFolderPath };
@@ -2,15 +2,14 @@ import {
2
2
  slash
3
3
  } from "../chunk-UWEEHUJV.js";
4
4
  import {
5
- __export,
6
- __spreadValues
5
+ __export
7
6
  } from "../chunk-WEAGW6MQ.js";
8
7
 
9
8
  // src/source/path.ts
10
9
  import { parse } from "path";
11
- function parseFilePath(path2) {
12
- const slashedPath = slash(path2);
13
- const parsed = parse(slashedPath);
10
+ function parseFilePath(path) {
11
+ const normalized = normalizePath(path);
12
+ const parsed = parse(normalized);
14
13
  const flattenedPath = joinPaths([parsed.dir, parsed.name]);
15
14
  const [name, locale] = parsed.name.split(".");
16
15
  return {
@@ -18,26 +17,32 @@ function parseFilePath(path2) {
18
17
  name,
19
18
  flattenedPath,
20
19
  locale,
21
- path: slashedPath
20
+ path: normalized
22
21
  };
23
22
  }
24
- function parseFolderPath(path2) {
25
- const slashedPath = slash(path2);
26
- const parsed = parse(slashedPath);
23
+ function parseFolderPath(path) {
24
+ const normalized = normalizePath(path);
25
+ const parsed = parse(normalized);
27
26
  const [name, locale] = parsed.base.split(".");
28
27
  return {
29
28
  dirname: parsed.dir,
30
29
  name,
31
- flattenedPath: slashedPath,
30
+ flattenedPath: normalized,
32
31
  locale,
33
- path: slashedPath
32
+ path: normalized
34
33
  };
35
34
  }
36
- function splitPath(path2) {
37
- return path2.split("/").filter((p) => p.length > 0);
35
+ function normalizePath(path) {
36
+ const segments = splitPath(slash(path));
37
+ if (segments[0] === "." || segments[0] === "..")
38
+ throw new Error("It must not start with './' or '../'");
39
+ return joinPaths(segments);
40
+ }
41
+ function splitPath(path) {
42
+ return path.split("/").filter((p) => p.length > 0);
38
43
  }
39
44
  function joinPaths(paths, slashMode = "none") {
40
- const joined = paths.flatMap((path2) => splitPath(path2)).join("/");
45
+ const joined = paths.flatMap((path) => splitPath(path)).join("/");
41
46
  switch (slashMode) {
42
47
  case "leading":
43
48
  return `/${joined}`;
@@ -58,9 +63,7 @@ function buildAll(nodes, ctx, skipIndex) {
58
63
  for (const node of [...nodes].sort(
59
64
  (a, b) => a.file.name.localeCompare(b.file.name)
60
65
  )) {
61
- if (node.type === "page") {
62
- if (node.file.locale)
63
- continue;
66
+ if ("data" in node && node.format === "page" && !node.file.locale) {
64
67
  const treeNode = buildFileNode(node, ctx);
65
68
  if (node.file.name === "index") {
66
69
  if (!skipIndex)
@@ -69,20 +72,12 @@ function buildAll(nodes, ctx, skipIndex) {
69
72
  }
70
73
  output.push(treeNode);
71
74
  }
72
- if (node.type === "folder") {
75
+ if ("children" in node) {
73
76
  output.push(buildFolderNode(node, false, ctx));
74
77
  }
75
78
  }
76
79
  return output;
77
80
  }
78
- function getFolderMeta(folder, ctx) {
79
- var _a;
80
- let meta = ctx.storage.readMeta(joinPaths([folder.file.path, "meta"]));
81
- if (ctx.lang) {
82
- meta = (_a = ctx.storage.readMeta(joinPaths([folder.file.path, `meta.${ctx.lang}`]))) != null ? _a : meta;
83
- }
84
- return meta;
85
- }
86
81
  function resolveFolderItem(folder, item, ctx, addedNodePaths) {
87
82
  var _a, _b, _c;
88
83
  if (item === rest)
@@ -108,15 +103,15 @@ function resolveFolderItem(folder, item, ctx, addedNodePaths) {
108
103
  ];
109
104
  }
110
105
  const extractResult = extractor.exec(item);
111
- const path2 = joinPaths([
106
+ const path = joinPaths([
112
107
  folder.file.path,
113
108
  (_b = (_a = extractResult == null ? void 0 : extractResult.groups) == null ? void 0 : _a.name) != null ? _b : item
114
109
  ]);
115
- const itemNode = (_c = ctx.storage.readDir(path2)) != null ? _c : ctx.storage.readPage(path2);
110
+ const itemNode = (_c = ctx.storage.readDir(path)) != null ? _c : ctx.storage.read(path, "page");
116
111
  if (!itemNode)
117
112
  return [];
118
113
  addedNodePaths.add(itemNode.file.path);
119
- if (itemNode.type === "folder") {
114
+ if ("children" in itemNode) {
120
115
  const node = buildFolderNode(itemNode, false, ctx);
121
116
  return extractResult ? node.children : [node];
122
117
  }
@@ -124,18 +119,22 @@ function resolveFolderItem(folder, item, ctx, addedNodePaths) {
124
119
  }
125
120
  function buildFolderNode(folder, defaultIsRoot, ctx) {
126
121
  var _a, _b, _c, _d, _e;
127
- const indexFile = ctx.storage.readPage(
128
- joinPaths([folder.file.flattenedPath, "index"])
122
+ const metaPath = joinPaths([folder.file.path, "meta"]);
123
+ let meta = ctx.storage.read(metaPath, "meta");
124
+ meta = (_a = findLocalizedFile(metaPath, "meta", ctx)) != null ? _a : meta;
125
+ const indexFile = ctx.storage.read(
126
+ joinPaths([folder.file.flattenedPath, "index"]),
127
+ "page"
129
128
  );
129
+ const metadata = meta == null ? void 0 : meta.data.data;
130
130
  const index = indexFile ? buildFileNode(indexFile, ctx) : void 0;
131
- const meta = (_a = getFolderMeta(folder, ctx)) == null ? void 0 : _a.data;
132
131
  let children;
133
132
  if (!meta) {
134
133
  children = buildAll(folder.children, ctx, !defaultIsRoot);
135
134
  } else {
136
- const isRoot = (_b = meta.root) != null ? _b : defaultIsRoot;
135
+ const isRoot = (_b = metadata == null ? void 0 : metadata.root) != null ? _b : defaultIsRoot;
137
136
  const addedNodePaths = /* @__PURE__ */ new Set();
138
- const resolved = (_c = meta.pages) == null ? void 0 : _c.flatMap((item) => {
137
+ const resolved = (_c = metadata == null ? void 0 : metadata.pages) == null ? void 0 : _c.flatMap((item) => {
139
138
  return resolveFolderItem(folder, item, ctx, addedNodePaths);
140
139
  });
141
140
  const restNodes = buildAll(
@@ -153,28 +152,23 @@ function buildFolderNode(folder, defaultIsRoot, ctx) {
153
152
  }
154
153
  return removeUndefined({
155
154
  type: "folder",
156
- name: (_e = (_d = meta == null ? void 0 : meta.title) != null ? _d : index == null ? void 0 : index.name) != null ? _e : pathToName(folder.file.name),
157
- icon: ctx.resolveIcon(meta == null ? void 0 : meta.icon),
158
- root: meta == null ? void 0 : meta.root,
159
- defaultOpen: meta == null ? void 0 : meta.defaultOpen,
155
+ name: (_e = (_d = metadata == null ? void 0 : metadata.title) != null ? _d : index == null ? void 0 : index.name) != null ? _e : pathToName(folder.file.name),
156
+ icon: ctx.resolveIcon(metadata == null ? void 0 : metadata.icon),
157
+ root: metadata == null ? void 0 : metadata.root,
158
+ defaultOpen: metadata == null ? void 0 : metadata.defaultOpen,
160
159
  index,
161
160
  children
162
161
  });
163
162
  }
164
- function buildFileNode(page, ctx) {
165
- let localePage = page;
166
- if (ctx.lang) {
167
- const result = ctx.storage.readPage(
168
- `${page.file.flattenedPath}.${ctx.lang}`
169
- );
170
- if (result)
171
- localePage = result;
172
- }
163
+ function buildFileNode(file, ctx) {
164
+ var _a;
165
+ const localized = (_a = findLocalizedFile(file.file.flattenedPath, "page", ctx)) != null ? _a : file;
166
+ const data = localized.data;
173
167
  return removeUndefined({
174
168
  type: "page",
175
- name: localePage.data.title,
176
- icon: ctx.resolveIcon(localePage.data.icon),
177
- url: localePage.url
169
+ name: data.data.title,
170
+ icon: ctx.resolveIcon(data.data.icon),
171
+ url: data.url
178
172
  });
179
173
  }
180
174
  function build(ctx) {
@@ -214,8 +208,13 @@ function createPageTreeBuilder({
214
208
  }
215
209
  };
216
210
  }
217
- function pathToName(path2) {
218
- return path2.slice(0, 1).toUpperCase() + path2.slice(1);
211
+ function findLocalizedFile(path, format, ctx) {
212
+ if (!ctx.lang)
213
+ return;
214
+ return ctx.storage.read(`${path}.${ctx.lang}`, format);
215
+ }
216
+ function pathToName(path) {
217
+ return path.slice(0, 1).toUpperCase() + path.slice(1);
219
218
  }
220
219
  function removeUndefined(value) {
221
220
  const obj = value;
@@ -226,9 +225,6 @@ function removeUndefined(value) {
226
225
  return value;
227
226
  }
228
227
 
229
- // src/source/load.ts
230
- import * as path from "path";
231
-
232
228
  // src/source/file-system.ts
233
229
  var file_system_exports = {};
234
230
  __export(file_system_exports, {
@@ -239,7 +235,6 @@ var Storage = class {
239
235
  this.files = /* @__PURE__ */ new Map();
240
236
  this.folders = /* @__PURE__ */ new Map();
241
237
  this.rootFolder = {
242
- type: "folder",
243
238
  file: parseFolderPath(""),
244
239
  children: []
245
240
  };
@@ -248,54 +243,37 @@ var Storage = class {
248
243
  /**
249
244
  * @param path - flattened path
250
245
  */
251
- read(path2, type) {
252
- return this.files.get(`${path2}.${type}`);
246
+ read(path, format) {
247
+ return this.files.get(`${path}.${format}`);
253
248
  }
254
- /**
255
- * @param path - flattened path
256
- */
257
- readPage(path2) {
258
- const result = this.read(path2, "page");
259
- if ((result == null ? void 0 : result.type) !== "page")
260
- return;
261
- return result;
262
- }
263
- /**
264
- * @param path - flattened path
265
- */
266
- readMeta(path2) {
267
- const result = this.read(path2, "meta");
268
- if ((result == null ? void 0 : result.type) !== "meta")
269
- return;
270
- return result;
271
- }
272
- readDir(path2) {
273
- return this.folders.get(path2);
249
+ readDir(path) {
250
+ return this.folders.get(path);
274
251
  }
275
252
  root() {
276
253
  return this.rootFolder;
277
254
  }
278
- write(path2, file) {
255
+ write(path, format, data) {
279
256
  var _a;
280
- const node = __spreadValues({
281
- file: parseFilePath(path2)
282
- }, file);
257
+ const node = {
258
+ format,
259
+ file: parseFilePath(path),
260
+ data
261
+ };
283
262
  this.makeDir(node.file.dirname);
284
263
  (_a = this.readDir(node.file.dirname)) == null ? void 0 : _a.children.push(node);
285
- this.files.set(`${node.file.flattenedPath}.${node.type}`, node);
264
+ this.files.set(`${node.file.flattenedPath}.${node.format}`, node);
286
265
  }
287
266
  list() {
288
267
  return [...this.files.values()];
289
268
  }
290
- makeDir(path2) {
269
+ makeDir(path) {
291
270
  var _a;
292
- const segments = splitPath(path2);
271
+ const segments = splitPath(path);
293
272
  for (let i = 0; i < segments.length; i++) {
294
273
  const segment = segments.slice(0, i + 1).join("/");
295
274
  if (this.folders.has(segment))
296
275
  continue;
297
276
  const folder = {
298
- type: "folder",
299
277
  file: parseFolderPath(segment),
300
278
  children: []
301
279
  };
@@ -305,65 +283,64 @@ var Storage = class {
305
283
  }
306
284
  };
307
285
 
308
- // src/source/load.ts
309
- function load(options) {
286
+ // src/source/load-files.ts
287
+ function loadFiles(files, options) {
288
+ var _a;
310
289
  const { transformers = [] } = options;
311
- const storage = buildStorage(options);
312
- for (const transformer of transformers) {
313
- transformer({
314
- storage,
315
- getUrl: options.getUrl,
316
- getSlugs: options.getSlugs
317
- });
318
- }
319
- return { storage };
320
- }
321
- function buildStorage(options) {
322
290
  const storage = new Storage();
323
- for (const file of options.files) {
324
- const relativePath = path.join(
325
- path.relative(options.rootDir, path.join("./", path.dirname(file.path))),
326
- path.basename(file.path)
327
- );
328
- if (relativePath.startsWith("..") || path.isAbsolute(relativePath))
291
+ const rootDir = normalizePath((_a = options.rootDir) != null ? _a : "");
292
+ for (const file of files) {
293
+ const normalizedPath = normalizePath(file.path);
294
+ if (!normalizedPath.startsWith(rootDir))
329
295
  continue;
296
+ const relativePath = normalizedPath.slice(rootDir.length);
330
297
  if (file.type === "page") {
331
298
  const parsedPath = parseFilePath(relativePath);
332
299
  const slugs = options.getSlugs(parsedPath);
333
- storage.write(relativePath, {
300
+ storage.write(relativePath, file.type, {
334
301
  slugs,
335
302
  url: options.getUrl(slugs, parsedPath.locale),
336
- type: file.type,
337
303
  data: file.data
338
304
  });
339
305
  }
340
306
  if (file.type === "meta") {
341
- storage.write(relativePath, {
342
- type: file.type,
307
+ storage.write(relativePath, file.type, {
343
308
  data: file.data
344
309
  });
345
310
  }
346
311
  }
312
+ for (const transformer of transformers) {
313
+ transformer({
314
+ storage,
315
+ options
316
+ });
317
+ }
347
318
  return storage;
348
319
  }
349
320
 
350
321
  // src/source/loader.ts
351
- function groupByLanguages(storage, languages) {
352
- var _a, _b, _c;
353
- const langMap = /* @__PURE__ */ new Map();
354
- langMap.set("", []);
355
- for (const node of storage.list()) {
356
- if (node.type !== "page" || node.file.locale)
322
+ function buildPageMap(storage, languages) {
323
+ var _a;
324
+ const map = /* @__PURE__ */ new Map();
325
+ const defaultMap = /* @__PURE__ */ new Map();
326
+ map.set("", defaultMap);
327
+ for (const file of storage.list()) {
328
+ if (file.format !== "page" || file.file.locale)
357
329
  continue;
358
- (_a = langMap.get("")) == null ? void 0 : _a.push(node);
330
+ const page = fileToPage(file);
331
+ defaultMap.set(page.slugs.join("/"), page);
359
332
  for (const lang of languages) {
360
- const list = (_b = langMap.get(lang)) != null ? _b : [];
361
- const page = (_c = storage.readPage(`${node.file.flattenedPath}.${lang}`)) != null ? _c : node;
362
- list.push(page);
363
- langMap.set(lang, list);
333
+ const langMap = (_a = map.get(lang)) != null ? _a : /* @__PURE__ */ new Map();
334
+ const localized = storage.read(
335
+ `${file.file.flattenedPath}.${lang}`,
336
+ "page"
337
+ );
338
+ const localizedPage = localized ? fileToPage(localized) : page;
339
+ langMap.set(localizedPage.slugs.join("/"), localizedPage);
340
+ map.set(lang, langMap);
364
341
  }
365
342
  }
366
- return langMap;
343
+ return map;
367
344
  }
368
345
  function createGetUrl(baseUrl) {
369
346
  return (slugs, locale) => {
@@ -387,36 +364,46 @@ function createOutput({
387
364
  rootDir = "",
388
365
  transformers,
389
366
  baseUrl = "/",
390
- slugs = getSlugs,
391
- url = createGetUrl(baseUrl)
367
+ slugs: slugsFn = getSlugs,
368
+ url: urlFn = createGetUrl(baseUrl)
392
369
  }) {
393
- const result = load({
394
- files: typeof source.files === "function" ? source.files(rootDir) : source.files,
395
- transformers,
396
- rootDir,
397
- getSlugs: slugs,
398
- getUrl: url
399
- });
400
- const i18nMap = groupByLanguages(result.storage, languages != null ? languages : []);
370
+ const storage = loadFiles(
371
+ typeof source.files === "function" ? source.files(rootDir) : source.files,
372
+ {
373
+ transformers,
374
+ rootDir,
375
+ getSlugs: slugsFn,
376
+ getUrl: urlFn
377
+ }
378
+ );
379
+ const i18nMap = buildPageMap(storage, languages != null ? languages : []);
401
380
  const builder = createPageTreeBuilder({
402
- storage: result.storage,
381
+ storage,
403
382
  resolveIcon: icon
404
383
  });
405
384
  const pageTree = languages === void 0 ? builder.build() : builder.buildI18n({ languages });
406
385
  return {
407
386
  pageTree,
408
- files: result.storage.list(),
387
+ files: storage.list(),
409
388
  getPages(language = "") {
410
- var _a;
411
- return (_a = i18nMap.get(language)) != null ? _a : [];
389
+ var _a, _b;
390
+ return Array.from((_b = (_a = i18nMap.get(language)) == null ? void 0 : _a.values()) != null ? _b : []);
412
391
  },
413
- getPage(slugs_ = [], language = "") {
392
+ getPage(slugs = [], language = "") {
414
393
  var _a;
415
- const path2 = slugs_.join("/");
416
- return (_a = i18nMap.get(language)) == null ? void 0 : _a.find((page) => page.slugs.join("/") === path2);
394
+ return (_a = i18nMap.get(language)) == null ? void 0 : _a.get(slugs.join("/"));
417
395
  }
418
396
  };
419
397
  }
398
+ function fileToPage(file) {
399
+ const data = file.data;
400
+ return {
401
+ file: file.file,
402
+ url: data.url,
403
+ slugs: data.slugs,
404
+ data: data.data
405
+ };
406
+ }
420
407
  export {
421
408
  file_system_exports as FileSystem,
422
409
  createGetUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-core",
3
- "version": "10.1.3",
3
+ "version": "11.0.0",
4
4
  "description": "The library for building a documentation website in Next.js",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -109,8 +109,8 @@
109
109
  ],
110
110
  "dependencies": {
111
111
  "@formatjs/intl-localematcher": "^0.5.4",
112
- "@shikijs/rehype": "^1.2.1",
113
- "@shikijs/transformers": "^1.2.1",
112
+ "@shikijs/rehype": "^1.3.0",
113
+ "@shikijs/transformers": "^1.3.0",
114
114
  "flexsearch": "0.7.21",
115
115
  "github-slugger": "^2.0.0",
116
116
  "hast-util-to-estree": "^3.1.0",
@@ -121,12 +121,12 @@
121
121
  "remark-gfm": "^4.0.0",
122
122
  "remark-mdx": "^3.0.1",
123
123
  "scroll-into-view-if-needed": "^3.1.0",
124
- "shiki": "^1.2.1",
124
+ "shiki": "^1.3.0",
125
125
  "swr": "^2.2.5",
126
126
  "unist-util-visit": "^5.0.0"
127
127
  },
128
128
  "devDependencies": {
129
- "@algolia/client-search": "^4.23.2",
129
+ "@algolia/client-search": "^4.23.3",
130
130
  "@mdx-js/mdx": "^3.0.1",
131
131
  "@types/flexsearch": "0.7.6",
132
132
  "@types/hast": "^3.0.4",
@@ -135,8 +135,8 @@
135
135
  "@types/node": "18.17.5",
136
136
  "@types/react": "^18.2.67",
137
137
  "@types/react-dom": "^18.2.22",
138
- "algoliasearch": "^4.23.2",
139
- "next": "^14.1.4",
138
+ "algoliasearch": "^4.23.3",
139
+ "next": "^14.2.1",
140
140
  "unified": "^11.0.4",
141
141
  "eslint-config-custom": "0.0.0",
142
142
  "tsconfig": "0.0.0"