fumadocs-core 14.0.2 → 14.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,59 @@
1
+ // src/server/shiki.ts
2
+ import {
3
+ getSingletonHighlighter
4
+ } from "shiki";
5
+ import { toJsxRuntime } from "hast-util-to-jsx-runtime";
6
+ import { Fragment } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { createOnigurumaEngine } from "shiki/engine/oniguruma";
9
+ function createStyleTransformer() {
10
+ return {
11
+ name: "rehype-code:styles",
12
+ line(hast) {
13
+ if (hast.children.length === 0) {
14
+ hast.children.push({
15
+ type: "text",
16
+ value: " "
17
+ });
18
+ }
19
+ }
20
+ };
21
+ }
22
+ var defaultThemes = {
23
+ light: "github-light",
24
+ dark: "github-dark"
25
+ };
26
+ async function highlight(code, options) {
27
+ const { lang, components, engine, ...rest } = options;
28
+ let themes = { themes: defaultThemes };
29
+ if ("theme" in options && options.theme) {
30
+ themes = { theme: options.theme };
31
+ } else if ("themes" in options && options.themes) {
32
+ themes = { themes: options.themes };
33
+ }
34
+ const highlighter = await getSingletonHighlighter({
35
+ langs: [lang],
36
+ engine: engine ?? createOnigurumaEngine(() => import("shiki/wasm")),
37
+ themes: "theme" in themes ? [themes.theme] : Object.values(themes.themes).filter((v) => v !== void 0)
38
+ });
39
+ const hast = highlighter.codeToHast(code, {
40
+ lang,
41
+ ...rest,
42
+ ...themes,
43
+ transformers: [createStyleTransformer(), ...rest.transformers ?? []],
44
+ defaultColor: "themes" in themes ? false : void 0
45
+ });
46
+ return toJsxRuntime(hast, {
47
+ jsx,
48
+ jsxs,
49
+ development: false,
50
+ components,
51
+ Fragment
52
+ });
53
+ }
54
+
55
+ export {
56
+ createStyleTransformer,
57
+ defaultThemes,
58
+ highlight
59
+ };
@@ -0,0 +1,19 @@
1
+ // src/utils/use-on-change.ts
2
+ import { useState } from "react";
3
+ function isDifferent(a, b) {
4
+ if (Array.isArray(a) && Array.isArray(b)) {
5
+ return b.length !== a.length || a.some((v, i) => isDifferent(v, b[i]));
6
+ }
7
+ return a !== b;
8
+ }
9
+ function useOnChange(value, onChange, isUpdated = isDifferent) {
10
+ const [prev, setPrev] = useState(value);
11
+ if (isUpdated(prev, value)) {
12
+ onChange(value, prev);
13
+ setPrev(value);
14
+ }
15
+ }
16
+
17
+ export {
18
+ useOnChange
19
+ };
@@ -0,0 +1,33 @@
1
+ // src/utils/path.ts
2
+ function splitPath(path) {
3
+ return path.split("/").filter((p) => p.length > 0);
4
+ }
5
+ function resolvePath(from, join) {
6
+ const v1 = splitPath(from), v2 = splitPath(join);
7
+ while (v2.length > 0) {
8
+ switch (v2[0]) {
9
+ case "..":
10
+ v1.pop();
11
+ break;
12
+ case ".":
13
+ break;
14
+ default:
15
+ v1.push(v2[0]);
16
+ }
17
+ v2.shift();
18
+ }
19
+ return v1.join("/");
20
+ }
21
+ function slash(path) {
22
+ const isExtendedLengthPath = path.startsWith("\\\\?\\");
23
+ if (isExtendedLengthPath) {
24
+ return path;
25
+ }
26
+ return path.replaceAll("\\", "/");
27
+ }
28
+
29
+ export {
30
+ splitPath,
31
+ resolvePath,
32
+ slash
33
+ };
@@ -52,7 +52,7 @@ declare function transformerTab(): ShikiTransformer;
52
52
 
53
53
  interface RemarkImageOptions {
54
54
  /**
55
- * Directory to resolve absolute image paths
55
+ * Directory or base URL to resolve absolute image paths
56
56
  */
57
57
  publicDir?: string;
58
58
  /**
@@ -3,8 +3,13 @@ import {
3
3
  remarkHeading
4
4
  } from "../chunk-4MNUWZIW.js";
5
5
  import {
6
+ resolvePath,
6
7
  slash
7
- } from "../chunk-UWEEHUJV.js";
8
+ } from "../chunk-SHGL6VBO.js";
9
+ import {
10
+ createStyleTransformer,
11
+ defaultThemes
12
+ } from "../chunk-7CSWJQ5H.js";
8
13
  import "../chunk-MLKGABMK.js";
9
14
 
10
15
  // src/mdx-plugins/index.ts
@@ -14,11 +19,254 @@ import {
14
19
 
15
20
  // src/mdx-plugins/rehype-code.ts
16
21
  import rehypeShikiFromHighlighter from "@shikijs/rehype/core";
17
- import {
18
- transformerNotationDiff,
19
- transformerNotationHighlight,
20
- transformerNotationWordHighlight
21
- } from "@shikijs/transformers";
22
+
23
+ // ../../node_modules/.pnpm/shiki-transformers@1.0.0_shiki@1.22.2/node_modules/shiki-transformers/dist/index.js
24
+ var matchers = [
25
+ [/^(<!--)(.+)(-->)$/, true],
26
+ [/^(\/\*)(.+)(\*\/)$/, true],
27
+ [/^(\/\/|["']|;{1,2}|%{1,2}|--|#)(.+)$/, false]
28
+ ];
29
+ function parseComments(lines, jsx) {
30
+ const out = [];
31
+ for (const line of lines) {
32
+ const elements = line.children;
33
+ const start = jsx ? elements.length - 2 : elements.length - 1;
34
+ for (let i = Math.max(start, 0); i < elements.length; i++) {
35
+ const token = elements[i];
36
+ if (token.type !== "element")
37
+ continue;
38
+ const isLast = i === elements.length - 1;
39
+ const match = matchToken(token, isLast);
40
+ if (!match) continue;
41
+ if (jsx && !isLast && i !== 0) {
42
+ const left = elements[i - 1];
43
+ const right = elements[i + 1];
44
+ out.push({
45
+ info: match,
46
+ line,
47
+ token,
48
+ jsxIntercept: isValue(left, "{") && isValue(right, "}")
49
+ });
50
+ } else {
51
+ out.push({
52
+ info: match,
53
+ line,
54
+ token,
55
+ jsxIntercept: false
56
+ });
57
+ }
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+ function isValue(element, value) {
63
+ if (element.type !== "element") return false;
64
+ const text = element.children[0];
65
+ if (text.type !== "text")
66
+ return false;
67
+ return text.value.trim() === value;
68
+ }
69
+ function matchToken(token, last) {
70
+ const text = token.children[0];
71
+ if (text.type !== "text")
72
+ return;
73
+ for (const [matcher, lastOnly] of matchers) {
74
+ if (!lastOnly && !last) continue;
75
+ let trimmed = text.value.trimStart();
76
+ const spaceFront = text.value.length - trimmed.length;
77
+ trimmed = trimmed.trimEnd();
78
+ const spaceEnd = text.value.length - trimmed.length - spaceFront;
79
+ const result = matcher.exec(trimmed);
80
+ if (!result) continue;
81
+ return [
82
+ " ".repeat(spaceFront) + result[1],
83
+ result[2],
84
+ result[3] ? result[3] + " ".repeat(spaceEnd) : void 0
85
+ ];
86
+ }
87
+ }
88
+ function createCommentNotationTransformer(name, regex, onMatch) {
89
+ return {
90
+ name,
91
+ code(code) {
92
+ const lines = code.children.filter((i) => i.type === "element");
93
+ const linesToRemove = [];
94
+ code.data ??= {};
95
+ const data = code.data;
96
+ const parsed = data._shiki_notation ??= parseComments(lines, ["jsx", "tsx"].includes(this.options.lang));
97
+ for (const comment of parsed) {
98
+ if (comment.info[1].length === 0) continue;
99
+ const isLineCommentOnly = comment.line.children.length === (comment.jsxIntercept ? 3 : 1);
100
+ let lineIdx = lines.indexOf(comment.line);
101
+ if (isLineCommentOnly) lineIdx++;
102
+ comment.info[1] = comment.info[1].replace(regex, (...match) => {
103
+ if (onMatch.call(this, match, comment.line, comment.token, lines, lineIdx)) {
104
+ return "";
105
+ }
106
+ return match[0];
107
+ });
108
+ const isEmpty = comment.info[1].trim().length === 0;
109
+ if (isEmpty) comment.info[1] = "";
110
+ if (isEmpty && isLineCommentOnly) {
111
+ linesToRemove.push(comment.line);
112
+ } else if (isEmpty && comment.jsxIntercept) {
113
+ comment.line.children.splice(comment.line.children.indexOf(comment.token) - 1, 3);
114
+ } else if (isEmpty) {
115
+ comment.line.children.splice(comment.line.children.indexOf(comment.token), 1);
116
+ } else {
117
+ const head = comment.token.children[0];
118
+ if (head.type === "text") {
119
+ head.value = comment.info.join("");
120
+ }
121
+ }
122
+ }
123
+ for (const line of linesToRemove)
124
+ code.children.splice(code.children.indexOf(line), 1);
125
+ }
126
+ };
127
+ }
128
+ function escapeRegExp(str) {
129
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
130
+ }
131
+ function transformerNotationMap(options = {}, name = "@shikijs/transformers:notation-map") {
132
+ const {
133
+ classMap = {},
134
+ classActivePre = void 0
135
+ } = options;
136
+ return createCommentNotationTransformer(
137
+ name,
138
+ new RegExp(`\\s*\\[!code (${Object.keys(classMap).map(escapeRegExp).join("|")})(:\\d+)?\\]`),
139
+ function([_, match, range = ":1"], _line, _comment, lines, index) {
140
+ const lineNum = Number.parseInt(range.slice(1), 10);
141
+ lines.slice(index, index + lineNum).forEach((line) => {
142
+ this.addClassToHast(line, classMap[match]);
143
+ });
144
+ if (classActivePre)
145
+ this.addClassToHast(this.pre, classActivePre);
146
+ return true;
147
+ }
148
+ );
149
+ }
150
+ function transformerNotationDiff(options = {}) {
151
+ const {
152
+ classLineAdd = "diff add",
153
+ classLineRemove = "diff remove",
154
+ classActivePre = "has-diff"
155
+ } = options;
156
+ return transformerNotationMap(
157
+ {
158
+ classMap: {
159
+ "++": classLineAdd,
160
+ "--": classLineRemove
161
+ },
162
+ classActivePre
163
+ },
164
+ "@shikijs/transformers:notation-diff"
165
+ );
166
+ }
167
+ function transformerNotationHighlight(options = {}) {
168
+ const {
169
+ classActiveLine = "highlighted",
170
+ classActivePre = "has-highlighted"
171
+ } = options;
172
+ return transformerNotationMap(
173
+ {
174
+ classMap: {
175
+ highlight: classActiveLine,
176
+ hl: classActiveLine
177
+ },
178
+ classActivePre
179
+ },
180
+ "@shikijs/transformers:notation-highlight"
181
+ );
182
+ }
183
+ function highlightWordInLine(line, ignoredElement, word, className) {
184
+ const content = getTextContent(line);
185
+ let index = content.indexOf(word);
186
+ while (index !== -1) {
187
+ highlightRange.call(this, line.children, ignoredElement, index, word.length, className);
188
+ index = content.indexOf(word, index + 1);
189
+ }
190
+ }
191
+ function getTextContent(element) {
192
+ if (element.type === "text")
193
+ return element.value;
194
+ if (element.type === "element" && element.tagName === "span")
195
+ return element.children.map(getTextContent).join("");
196
+ return "";
197
+ }
198
+ function highlightRange(elements, ignoredElement, index, len, className) {
199
+ let currentIdx = 0;
200
+ for (let i = 0; i < elements.length; i++) {
201
+ const element = elements[i];
202
+ if (element.type !== "element" || element.tagName !== "span" || element === ignoredElement)
203
+ continue;
204
+ const textNode = element.children[0];
205
+ if (textNode.type !== "text")
206
+ continue;
207
+ if (hasOverlap([currentIdx, currentIdx + textNode.value.length - 1], [index, index + len])) {
208
+ const start = Math.max(0, index - currentIdx);
209
+ const length = len - Math.max(0, currentIdx - index);
210
+ if (length === 0)
211
+ continue;
212
+ const separated = separateToken(element, textNode, start, length);
213
+ this.addClassToHast(separated[1], className);
214
+ const output = separated.filter(Boolean);
215
+ elements.splice(i, 1, ...output);
216
+ i += output.length - 1;
217
+ }
218
+ currentIdx += textNode.value.length;
219
+ }
220
+ }
221
+ function hasOverlap(range1, range2) {
222
+ return range1[0] <= range2[1] && range1[1] >= range2[0];
223
+ }
224
+ function separateToken(span, textNode, index, len) {
225
+ const text = textNode.value;
226
+ const createNode = (value) => inheritElement(span, {
227
+ children: [
228
+ {
229
+ type: "text",
230
+ value
231
+ }
232
+ ]
233
+ });
234
+ return [
235
+ index > 0 ? createNode(text.slice(0, index)) : void 0,
236
+ createNode(text.slice(index, index + len)),
237
+ index + len < text.length ? createNode(text.slice(index + len)) : void 0
238
+ ];
239
+ }
240
+ function inheritElement(original, overrides) {
241
+ return {
242
+ ...original,
243
+ properties: {
244
+ ...original.properties
245
+ },
246
+ ...overrides
247
+ };
248
+ }
249
+ function transformerNotationWordHighlight(options = {}) {
250
+ const {
251
+ classActiveWord = "highlighted-word",
252
+ classActivePre = void 0
253
+ } = options;
254
+ return createCommentNotationTransformer(
255
+ "@shikijs/transformers:notation-highlight-word",
256
+ /\s*\[!code word:((?:\\.|[^:\]])+)(:\d+)?\]/,
257
+ function([_, word, range], _line, comment, lines, index) {
258
+ const lineNum = range ? Number.parseInt(range.slice(1), 10) : lines.length;
259
+ word = word.replace(/\\(.)/g, "$1");
260
+ lines.slice(index, index + lineNum).forEach((line) => highlightWordInLine.call(this, line, comment, word, classActiveWord));
261
+ if (classActivePre)
262
+ this.addClassToHast(this.pre, classActivePre);
263
+ return true;
264
+ }
265
+ );
266
+ }
267
+ var symbol = Symbol("highlighted-lines");
268
+
269
+ // src/mdx-plugins/rehype-code.ts
22
270
  import {
23
271
  getSingletonHighlighter,
24
272
  bundledLanguages
@@ -185,14 +433,12 @@ var metaValues = [
185
433
  }
186
434
  ];
187
435
  var rehypeCodeDefaultOptions = {
188
- themes: {
189
- light: "github-light",
190
- dark: "github-dark"
191
- },
436
+ themes: defaultThemes,
437
+ defaultColor: false,
192
438
  defaultLanguage: "plaintext",
193
439
  experimentalJSEngine: false,
194
- defaultColor: false,
195
440
  transformers: [
441
+ createStyleTransformer(),
196
442
  transformerNotationHighlight(),
197
443
  transformerNotationWordHighlight(),
198
444
  transformerNotationDiff()
@@ -229,14 +475,6 @@ function rehypeCode(options = {}) {
229
475
  meta.__raw = codeOptions.filterMetaString(meta.__raw ?? "");
230
476
  }
231
477
  return code.replace(/\n$/, "");
232
- },
233
- line(hast) {
234
- if (hast.children.length === 0) {
235
- hast.children.push({
236
- type: "text",
237
- value: " "
238
- });
239
- }
240
478
  }
241
479
  },
242
480
  ...codeOptions.transformers
@@ -313,57 +551,58 @@ function remarkImage({
313
551
  const promises = [];
314
552
  function getImportPath(src) {
315
553
  if (!src.startsWith("/")) return src;
316
- if (file.path) {
317
- const relative = path.relative(
318
- path.dirname(file.path),
319
- path.join(publicDir, src)
320
- );
554
+ const to = path.join(publicDir, src);
555
+ if (file.dirname) {
556
+ const relative = slash(path.relative(file.dirname, to));
321
557
  return relative.startsWith("./") ? relative : `./${relative}`;
322
558
  }
323
- return path.join(publicDir, src);
559
+ return slash(to);
324
560
  }
325
561
  visit(tree, "image", (node) => {
326
- const src = decodeURI(node.url);
327
- if (!src) return;
328
- const isExternal = EXTERNAL_URL_REGEX.test(src);
562
+ const url = decodeURI(node.url);
563
+ if (!url) return;
564
+ const isExternal = EXTERNAL_URL_REGEX.test(url);
329
565
  if (isExternal && external || !useImport) {
330
- promises.push(
331
- getImageSize(src, publicDir).then((size) => {
332
- if (!size.width || !size.height) return;
333
- Object.assign(node, {
334
- type: "mdxJsxFlowElement",
335
- name: "img",
336
- attributes: [
337
- {
338
- type: "mdxJsxAttribute",
339
- name: "alt",
340
- value: node.alt ?? "image"
341
- },
342
- {
343
- type: "mdxJsxAttribute",
344
- name: "src",
345
- value: src
346
- },
347
- {
348
- type: "mdxJsxAttribute",
349
- name: "width",
350
- value: size.width.toString()
351
- },
352
- {
353
- type: "mdxJsxAttribute",
354
- name: "height",
355
- value: size.height.toString()
356
- }
357
- ]
358
- });
359
- })
360
- );
566
+ const task = getImageSize(url, publicDir).then((size) => {
567
+ if (!size.width || !size.height) return;
568
+ Object.assign(node, {
569
+ type: "mdxJsxFlowElement",
570
+ name: "img",
571
+ attributes: [
572
+ {
573
+ type: "mdxJsxAttribute",
574
+ name: "alt",
575
+ value: node.alt ?? "image"
576
+ },
577
+ {
578
+ type: "mdxJsxAttribute",
579
+ name: "src",
580
+ value: url
581
+ },
582
+ {
583
+ type: "mdxJsxAttribute",
584
+ name: "width",
585
+ value: size.width.toString()
586
+ },
587
+ {
588
+ type: "mdxJsxAttribute",
589
+ name: "height",
590
+ value: size.height.toString()
591
+ }
592
+ ]
593
+ });
594
+ }).catch(() => {
595
+ console.error(
596
+ `[Remark Image] Failed obtain image size for ${url} with public directory ${publicDir}`
597
+ );
598
+ });
599
+ promises.push(task);
361
600
  } else if (!isExternal) {
362
601
  const variableName = `__img${importsToInject.length.toString()}`;
363
- const hasBlur = placeholder === "blur" && VALID_BLUR_EXT.some((ext) => src.endsWith(ext));
602
+ const hasBlur = placeholder === "blur" && VALID_BLUR_EXT.some((ext) => url.endsWith(ext));
364
603
  importsToInject.push({
365
604
  variableName,
366
- importPath: slash(getImportPath(src))
605
+ importPath: getImportPath(url)
367
606
  });
368
607
  Object.assign(node, {
369
608
  type: "mdxJsxFlowElement",
@@ -428,17 +667,20 @@ function remarkImage({
428
667
  }
429
668
  };
430
669
  }
431
- function resolveSrc(src, dir) {
432
- return src.startsWith("/") || !path.isAbsolute(src) ? path.join(dir, src) : src;
433
- }
434
670
  async function getImageSize(src, dir) {
671
+ const isRelative = src.startsWith("/") || !path.isAbsolute(src);
672
+ let url;
435
673
  if (EXTERNAL_URL_REGEX.test(src)) {
436
- const res = await fetch(src);
437
- return sizeOf(
438
- await res.arrayBuffer().then((buffer) => new Uint8Array(buffer))
439
- );
674
+ url = src;
675
+ } else if (EXTERNAL_URL_REGEX.test(dir) && isRelative) {
676
+ const base = new URL(dir);
677
+ base.pathname = resolvePath(base.pathname, src);
678
+ url = base.toString();
679
+ } else {
680
+ return sizeOf(isRelative ? path.join(dir, src) : src);
440
681
  }
441
- return sizeOf(resolveSrc(src, dir));
682
+ const buffer = await fetch(url).then((res) => res.arrayBuffer());
683
+ return sizeOf(new Uint8Array(buffer));
442
684
  }
443
685
 
444
686
  // src/mdx-plugins/remark-structure.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useOnChange
3
- } from "../chunk-I5BWASD6.js";
3
+ } from "../chunk-EMWGTXSW.js";
4
4
  import "../chunk-MLKGABMK.js";
5
5
 
6
6
  // src/search/client.ts
@@ -5,8 +5,12 @@ export { S as SortedResult } from '../types-Ch8gnVgO.js';
5
5
  import { Metadata } from 'next';
6
6
  import { NextRequest } from 'next/server';
7
7
  import { LoaderOutput, LoaderConfig, InferPageType } from '../source/index.js';
8
+ export { H as HighlightOptions, c as createStyleTransformer, d as defaultThemes, h as highlight } from '../shiki-FJwEmGMA.js';
8
9
  import 'react';
9
10
  import '../config-inq6kP6y.js';
11
+ import 'shiki';
12
+ import 'shiki/themes';
13
+ import 'hast-util-to-jsx-runtime';
10
14
 
11
15
  /**
12
16
  * Flatten tree to an array of page nodes
@@ -1,6 +1,11 @@
1
1
  import {
2
2
  remarkHeading
3
3
  } from "../chunk-4MNUWZIW.js";
4
+ import {
5
+ createStyleTransformer,
6
+ defaultThemes,
7
+ highlight
8
+ } from "../chunk-7CSWJQ5H.js";
4
9
  import "../chunk-MLKGABMK.js";
5
10
 
6
11
  // src/server/get-toc.ts
@@ -147,9 +152,12 @@ function createMetadataImage(options) {
147
152
  export {
148
153
  page_tree_exports as PageTree,
149
154
  createMetadataImage,
155
+ createStyleTransformer,
156
+ defaultThemes,
150
157
  findNeighbour,
151
158
  flattenTree,
152
159
  getGithubLastEdit,
153
160
  getTableOfContents,
161
+ highlight,
154
162
  separatePageTree
155
163
  };
@@ -0,0 +1,16 @@
1
+ import { ShikiTransformer, CodeToHastOptionsCommon, BundledLanguage, HighlighterCoreOptions, CodeOptionsThemes, CodeOptionsMeta } from 'shiki';
2
+ import { BundledTheme } from 'shiki/themes';
3
+ import { Components } from 'hast-util-to-jsx-runtime';
4
+ import { ReactNode } from 'react';
5
+
6
+ declare function createStyleTransformer(): ShikiTransformer;
7
+ declare const defaultThemes: {
8
+ light: string;
9
+ dark: string;
10
+ };
11
+ type HighlightOptions = CodeToHastOptionsCommon<BundledLanguage> & Pick<HighlighterCoreOptions, 'engine'> & Partial<CodeOptionsThemes<BundledTheme>> & CodeOptionsMeta & {
12
+ components?: Partial<Components>;
13
+ };
14
+ declare function highlight(code: string, options: HighlightOptions): Promise<ReactNode>;
15
+
16
+ export { type HighlightOptions as H, createStyleTransformer as c, defaultThemes as d, highlight as h };
package/dist/sidebar.js CHANGED
@@ -3,7 +3,6 @@ import "./chunk-MLKGABMK.js";
3
3
 
4
4
  // src/sidebar.tsx
5
5
  import {
6
- useCallback,
7
6
  createContext,
8
7
  useContext,
9
8
  useEffect,
@@ -38,9 +37,9 @@ function SidebarTrigger({
38
37
  {
39
38
  "aria-label": "Toggle Sidebar",
40
39
  "data-open": open,
41
- onClick: useCallback(() => {
40
+ onClick: () => {
42
41
  setOpen(!open);
43
- }, [open, setOpen]),
42
+ },
44
43
  ...props
45
44
  }
46
45
  );
@@ -2,75 +2,14 @@ import {
2
2
  removeUndefined
3
3
  } from "../chunk-2V6SCS43.js";
4
4
  import {
5
- slash
6
- } from "../chunk-UWEEHUJV.js";
5
+ resolvePath,
6
+ slash,
7
+ splitPath
8
+ } from "../chunk-SHGL6VBO.js";
7
9
  import {
8
10
  __export
9
11
  } from "../chunk-MLKGABMK.js";
10
12
 
11
- // src/source/path.ts
12
- function parseFilePath(path) {
13
- const segments = splitPath(slash(path));
14
- const dirname = segments.slice(0, -1).join("/");
15
- const base = segments.at(-1) ?? "";
16
- const dotIdx = base.lastIndexOf(".");
17
- const nameWithLocale = dotIdx !== -1 ? base.slice(0, dotIdx) : base;
18
- const flattenedPath = [dirname, nameWithLocale].filter((p) => p.length > 0).join("/");
19
- const [name, locale] = getLocale(nameWithLocale);
20
- return {
21
- dirname,
22
- name,
23
- flattenedPath,
24
- locale,
25
- path: segments.join("/")
26
- };
27
- }
28
- function parseFolderPath(path) {
29
- const segments = splitPath(slash(path));
30
- const base = segments.at(-1) ?? "";
31
- const [name, locale] = getLocale(base);
32
- const flattenedPath = segments.join("/");
33
- return {
34
- dirname: segments.slice(0, -1).join("/"),
35
- name,
36
- flattenedPath,
37
- locale,
38
- path: flattenedPath
39
- };
40
- }
41
- function getLocale(name) {
42
- const sep = name.lastIndexOf(".");
43
- if (sep === -1) return [name];
44
- const locale = name.slice(sep + 1);
45
- if (/\d+/.exec(locale)) return [name];
46
- return [name.slice(0, sep), locale];
47
- }
48
- function normalizePath(path) {
49
- const segments = splitPath(slash(path));
50
- if (segments[0] === "." || segments[0] === "..")
51
- throw new Error("It must not start with './' or '../'");
52
- return segments.join("/");
53
- }
54
- function splitPath(path) {
55
- return path.split("/").filter((p) => p.length > 0);
56
- }
57
- function resolvePath(from, join) {
58
- const v1 = splitPath(from), v2 = splitPath(join);
59
- while (v2.length > 0) {
60
- switch (v2[0]) {
61
- case "..":
62
- v1.pop();
63
- break;
64
- case ".":
65
- break;
66
- default:
67
- v1.push(v2[0]);
68
- }
69
- v2.shift();
70
- }
71
- return v1.join("/");
72
- }
73
-
74
13
  // src/source/page-tree-builder.ts
75
14
  var group = /^\((?<name>.+)\)$/;
76
15
  var link = /^(?:\[(?<icon>[^\]]+)])?\[(?<name>[^\]]+)]\((?<url>[^)]+)\)$/;
@@ -249,6 +188,50 @@ function pathToName(name, resolveGroup = false) {
249
188
  return result.join("");
250
189
  }
251
190
 
191
+ // src/source/path.ts
192
+ function parseFilePath(path) {
193
+ const segments = splitPath(slash(path));
194
+ const dirname = segments.slice(0, -1).join("/");
195
+ const base = segments.at(-1) ?? "";
196
+ const dotIdx = base.lastIndexOf(".");
197
+ const nameWithLocale = dotIdx !== -1 ? base.slice(0, dotIdx) : base;
198
+ const flattenedPath = [dirname, nameWithLocale].filter((p) => p.length > 0).join("/");
199
+ const [name, locale] = getLocale(nameWithLocale);
200
+ return {
201
+ dirname,
202
+ name,
203
+ flattenedPath,
204
+ locale,
205
+ path: segments.join("/")
206
+ };
207
+ }
208
+ function parseFolderPath(path) {
209
+ const segments = splitPath(slash(path));
210
+ const base = segments.at(-1) ?? "";
211
+ const [name, locale] = getLocale(base);
212
+ const flattenedPath = segments.join("/");
213
+ return {
214
+ dirname: segments.slice(0, -1).join("/"),
215
+ name,
216
+ flattenedPath,
217
+ locale,
218
+ path: flattenedPath
219
+ };
220
+ }
221
+ function getLocale(name) {
222
+ const sep = name.lastIndexOf(".");
223
+ if (sep === -1) return [name];
224
+ const locale = name.slice(sep + 1);
225
+ if (/\d+/.exec(locale)) return [name];
226
+ return [name.slice(0, sep), locale];
227
+ }
228
+ function normalizePath(path) {
229
+ const segments = splitPath(slash(path));
230
+ if (segments[0] === "." || segments[0] === "..")
231
+ throw new Error("It must not start with './' or '../'");
232
+ return segments.join("/");
233
+ }
234
+
252
235
  // src/source/file-system.ts
253
236
  var file_system_exports = {};
254
237
  __export(file_system_exports, {
package/dist/toc.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  useOnChange
4
- } from "./chunk-I5BWASD6.js";
4
+ } from "./chunk-EMWGTXSW.js";
5
5
  import "./chunk-MLKGABMK.js";
6
6
 
7
7
  // src/toc.tsx
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useOnChange
3
- } from "../chunk-I5BWASD6.js";
3
+ } from "../chunk-EMWGTXSW.js";
4
4
  import "../chunk-MLKGABMK.js";
5
5
  export {
6
6
  useOnChange
@@ -0,0 +1,11 @@
1
+ import { ReactNode, DependencyList } from 'react';
2
+ import { H as HighlightOptions } from '../shiki-FJwEmGMA.js';
3
+ import 'shiki';
4
+ import 'shiki/themes';
5
+ import 'hast-util-to-jsx-runtime';
6
+
7
+ declare function useShiki(code: string, options: HighlightOptions & {
8
+ defaultValue?: ReactNode;
9
+ }, deps?: DependencyList): ReactNode;
10
+
11
+ export { useShiki };
@@ -0,0 +1,38 @@
1
+ "use client";
2
+ import {
3
+ highlight
4
+ } from "../chunk-7CSWJQ5H.js";
5
+ import "../chunk-MLKGABMK.js";
6
+
7
+ // src/utils/use-shiki.tsx
8
+ import {
9
+ useEffect,
10
+ useState
11
+ } from "react";
12
+ import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
13
+ import { jsx } from "react/jsx-runtime";
14
+ var jsEngine;
15
+ function useShiki(code, options, deps) {
16
+ const [out, setOut] = useState(() => {
17
+ if (options.defaultValue) return options.defaultValue;
18
+ const { pre: Pre = "pre", code: Code = "code" } = options.components ?? {};
19
+ return /* @__PURE__ */ jsx(Pre, { children: /* @__PURE__ */ jsx(Code, { children: code }) });
20
+ });
21
+ if (!options.engine && !jsEngine) {
22
+ jsEngine = createJavaScriptRegexEngine();
23
+ }
24
+ useEffect(
25
+ () => {
26
+ void highlight(code, {
27
+ ...options,
28
+ engine: options.engine ?? jsEngine
29
+ }).then(setOut);
30
+ },
31
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- custom deps
32
+ deps ?? [code, options.lang]
33
+ );
34
+ return out;
35
+ }
36
+ export {
37
+ useShiki
38
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-core",
3
- "version": "14.0.2",
3
+ "version": "14.1.1",
4
4
  "description": "The library for building a documentation website in Next.js",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -48,6 +48,10 @@
48
48
  "import": "./dist/utils/use-on-change.js",
49
49
  "types": "./dist/utils/use-on-change.d.ts"
50
50
  },
51
+ "./utils/use-shiki": {
52
+ "import": "./dist/utils/use-shiki.js",
53
+ "types": "./dist/utils/use-shiki.d.ts"
54
+ },
51
55
  "./link": {
52
56
  "import": "./dist/link.js",
53
57
  "types": "./dist/link.d.ts"
@@ -65,19 +69,19 @@
65
69
  "dist/*"
66
70
  ],
67
71
  "dependencies": {
68
- "@formatjs/intl-localematcher": "^0.5.5",
72
+ "@formatjs/intl-localematcher": "^0.5.6",
69
73
  "@orama/orama": "^3.0.1",
70
- "@shikijs/rehype": "^1.22.0",
71
- "@shikijs/transformers": "^1.22.0",
74
+ "@shikijs/rehype": "^1.22.2",
72
75
  "github-slugger": "^2.0.0",
73
76
  "hast-util-to-estree": "^3.1.0",
77
+ "hast-util-to-jsx-runtime": "^2.3.2",
74
78
  "image-size": "^1.1.1",
75
79
  "negotiator": "^1.0.0",
76
80
  "react-remove-scroll": "^2.6.0",
77
81
  "remark": "^15.0.0",
78
82
  "remark-gfm": "^4.0.0",
79
83
  "scroll-into-view-if-needed": "^3.1.0",
80
- "shiki": "^1.22.0",
84
+ "shiki": "^1.22.2",
81
85
  "unist-util-visit": "^5.0.0"
82
86
  },
83
87
  "devDependencies": {
@@ -87,8 +91,8 @@
87
91
  "@types/hast": "^3.0.4",
88
92
  "@types/mdast": "^4.0.3",
89
93
  "@types/negotiator": "^0.6.3",
90
- "@types/node": "22.7.8",
91
- "@types/react": "^18.3.11",
94
+ "@types/node": "22.8.1",
95
+ "@types/react": "^18.3.12",
92
96
  "@types/react-dom": "^18.3.1",
93
97
  "algoliasearch": "4.24.0",
94
98
  "mdast-util-mdx-jsx": "^3.1.3",
@@ -96,16 +100,31 @@
96
100
  "next": "^15.0.0",
97
101
  "remark-mdx": "^3.1.0",
98
102
  "remark-rehype": "^11.1.1",
103
+ "shiki-transformers": "^1.0.0",
99
104
  "unified": "^11.0.5",
100
105
  "eslint-config-custom": "0.0.0",
101
106
  "tsconfig": "0.0.0"
102
107
  },
103
- "optionalDependencies": {
108
+ "peerDependencies": {
104
109
  "algoliasearch": "4.24.0",
105
- "next": "15.0.0",
110
+ "next": "14.x.x || 15.x.x",
106
111
  "react": ">= 18",
107
112
  "react-dom": ">= 18"
108
113
  },
114
+ "peerDependenciesMeta": {
115
+ "algoliasearch": {
116
+ "optional": true
117
+ },
118
+ "next": {
119
+ "optional": true
120
+ },
121
+ "react": {
122
+ "optional": true
123
+ },
124
+ "react-dom": {
125
+ "optional": true
126
+ }
127
+ },
109
128
  "publishConfig": {
110
129
  "access": "public"
111
130
  },
@@ -1,13 +0,0 @@
1
- // src/utils/use-on-change.ts
2
- import { useState } from "react";
3
- function useOnChange(value, onChange, isUpdated = (prev, current) => prev !== current) {
4
- const [prev, setPrev] = useState(value);
5
- if (isUpdated(prev, value)) {
6
- onChange(value, prev);
7
- setPrev(value);
8
- }
9
- }
10
-
11
- export {
12
- useOnChange
13
- };
@@ -1,12 +0,0 @@
1
- // src/utils/slash.ts
2
- function slash(path) {
3
- const isExtendedLengthPath = path.startsWith("\\\\?\\");
4
- if (isExtendedLengthPath) {
5
- return path;
6
- }
7
- return path.replaceAll("\\", "/");
8
- }
9
-
10
- export {
11
- slash
12
- };