fumadocs-core 15.0.4 → 15.0.6

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.
@@ -21,35 +21,31 @@ function remarkHeading({
21
21
  return (root, file) => {
22
22
  const toc = [];
23
23
  slugger.reset();
24
- if (file.data.frontmatter) {
25
- const frontmatter = file.data.frontmatter;
26
- if (frontmatter._openapi?.toc) {
27
- toc.push(...frontmatter._openapi.toc);
28
- }
29
- }
30
24
  visit(root, "heading", (heading) => {
31
25
  heading.data ||= {};
32
26
  heading.data.hProperties ||= {};
27
+ let id = heading.data.hProperties.id;
33
28
  const lastNode = heading.children.at(-1);
34
- if (!heading.data.hProperties.id && lastNode?.type === "text" && customId) {
29
+ if (!id && lastNode?.type === "text" && customId) {
35
30
  const match = regex.exec(lastNode.value);
36
31
  if (match?.[1]) {
37
- heading.data.hProperties.id = match[1];
32
+ id = match[1];
38
33
  lastNode.value = lastNode.value.slice(0, match.index);
39
34
  }
40
35
  }
41
- const value = flattenNode(heading);
42
- let id = heading.data.hProperties.id;
36
+ let flattened;
43
37
  if (!id) {
44
- id = defaultSlug ? defaultSlug(root, heading, value) : slugger.slug(value);
38
+ flattened ??= flattenNode(heading);
39
+ id = defaultSlug ? defaultSlug(root, heading, flattened) : slugger.slug(flattened);
45
40
  }
46
41
  heading.data.hProperties.id = id;
47
- if (generateToc)
42
+ if (generateToc) {
48
43
  toc.push({
49
- title: value,
44
+ title: flattened ?? flattenNode(heading),
50
45
  url: `#${id}`,
51
46
  depth: heading.depth
52
47
  });
48
+ }
53
49
  return "skip";
54
50
  });
55
51
  if (generateToc) file.data.toc = toc;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  flattenNode,
3
3
  remarkHeading
4
- } from "../chunk-4MNUWZIW.js";
4
+ } from "../chunk-IYQ35KI2.js";
5
5
  import {
6
6
  resolvePath,
7
7
  slash
@@ -290,7 +290,7 @@ function transformerTab() {
290
290
  }
291
291
 
292
292
  // src/mdx-plugins/remark-image.ts
293
- import path from "node:path";
293
+ import * as path from "node:path";
294
294
  import { visit } from "unist-util-visit";
295
295
  import sizeOf from "image-size";
296
296
  var VALID_BLUR_EXT = [".jpeg", ".png", ".webp", ".avif", ".jpg"];
@@ -308,8 +308,8 @@ function remarkImage({
308
308
  if (!src.startsWith("/")) return src;
309
309
  const to = path.join(publicDir, src);
310
310
  if (file.dirname) {
311
- const relative = slash(path.relative(file.dirname, to));
312
- return relative.startsWith("./") ? relative : `./${relative}`;
311
+ const relative2 = slash(path.relative(file.dirname, to));
312
+ return relative2.startsWith("./") ? relative2 : `./${relative2}`;
313
313
  }
314
314
  return slash(to);
315
315
  }
@@ -396,30 +396,29 @@ function remarkImage({
396
396
  }
397
397
  });
398
398
  await Promise.all(promises);
399
- if (importsToInject.length > 0) {
400
- const imports = importsToInject.map(
401
- ({ variableName, importPath }) => ({
402
- type: "mdxjsEsm",
403
- data: {
404
- estree: {
405
- body: [
406
- {
407
- type: "ImportDeclaration",
408
- source: { type: "Literal", value: importPath },
409
- specifiers: [
410
- {
411
- type: "ImportDefaultSpecifier",
412
- local: { type: "Identifier", name: variableName }
413
- }
414
- ]
415
- }
416
- ]
417
- }
399
+ if (importsToInject.length === 0) return;
400
+ const imports = importsToInject.map(
401
+ ({ variableName, importPath }) => ({
402
+ type: "mdxjsEsm",
403
+ data: {
404
+ estree: {
405
+ body: [
406
+ {
407
+ type: "ImportDeclaration",
408
+ source: { type: "Literal", value: importPath },
409
+ specifiers: [
410
+ {
411
+ type: "ImportDefaultSpecifier",
412
+ local: { type: "Identifier", name: variableName }
413
+ }
414
+ ]
415
+ }
416
+ ]
418
417
  }
419
- })
420
- );
421
- tree.children.unshift(...imports);
422
- }
418
+ }
419
+ })
420
+ );
421
+ tree.children.unshift(...imports);
423
422
  };
424
423
  }
425
424
  async function getImageSize(src, dir) {
@@ -571,12 +570,23 @@ function visit4(node, tagNames, handler) {
571
570
  }
572
571
 
573
572
  // src/mdx-plugins/rehype-toc.ts
573
+ var TocOnlyTag = "[toc]";
574
+ var NoTocTag = "[!toc]";
574
575
  function rehypeToc({ exportToc = true } = {}) {
575
576
  return (tree) => {
576
577
  const output = [];
577
578
  visit4(tree, ["h1", "h2", "h3", "h4", "h5", "h6"], (element) => {
578
579
  const id = element.properties.id;
579
580
  if (!id) return "skip";
581
+ let isTocOnly = false;
582
+ const last = element.children.at(-1);
583
+ if (last?.type === "text" && last.value.endsWith(TocOnlyTag)) {
584
+ isTocOnly = true;
585
+ last.value = last.value.substring(0, last.value.length - TocOnlyTag.length).trimEnd();
586
+ } else if (last?.type === "text" && last.value.endsWith(NoTocTag)) {
587
+ last.value = last.value.substring(0, last.value.length - NoTocTag.length).trimEnd();
588
+ return "skip";
589
+ }
580
590
  const estree = toEstree(element, {
581
591
  elementAttributeNameCase: "react",
582
592
  stylePropertyNameCase: "dom"
@@ -587,6 +597,12 @@ function rehypeToc({ exportToc = true } = {}) {
587
597
  depth: Number(element.tagName.slice(1)),
588
598
  url: `#${id}`
589
599
  });
600
+ if (isTocOnly) {
601
+ Object.assign(element, {
602
+ type: "comment",
603
+ value: ""
604
+ });
605
+ }
590
606
  return "skip";
591
607
  });
592
608
  const declaration = {
@@ -143,12 +143,13 @@ function pageToIndex(page) {
143
143
  "Cannot find structured data from page, please define the page to index function."
144
144
  );
145
145
  }
146
+ const structuredData = page.data.structuredData;
146
147
  return {
147
- title: page.data.title,
148
+ title: page.data.title ?? page.file.name,
148
149
  description: "description" in page.data ? page.data.description : void 0,
149
150
  url: page.url,
150
151
  id: page.url,
151
- structuredData: page.data.structuredData
152
+ structuredData
152
153
  };
153
154
  }
154
155
  function createFromSource(source, pageToIndexFn = pageToIndex, options = {}) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  remarkHeading
3
- } from "../chunk-4MNUWZIW.js";
3
+ } from "../chunk-IYQ35KI2.js";
4
4
  import {
5
5
  createStyleTransformer,
6
6
  highlight
@@ -140,7 +140,7 @@ interface MetaData {
140
140
  }
141
141
  interface PageData {
142
142
  icon?: string | undefined;
143
- title: string;
143
+ title?: string;
144
144
  }
145
145
  type InferPageType<Utils extends LoaderOutput<any>> = Utils extends LoaderOutput<infer Config> ? Page<Config['source']['pageData']> : never;
146
146
  type InferMetaType<Utils extends LoaderOutput<any>> = Utils extends LoaderOutput<infer Config> ? Meta<Config['source']['metaData']> : never;
@@ -123,7 +123,8 @@ function buildFolderNode(folder, isGlobalRoot, ctx) {
123
123
  }
124
124
  const node = {
125
125
  type: "folder",
126
- name: metadata?.title ?? index?.name ?? pathToName(folder.file.name, true),
126
+ name: metadata?.title ?? index?.name ?? // resolve folder groups like (group_name)
127
+ pathToName(group.exec(folder.file.name)?.[1] ?? folder.file.name),
127
128
  icon: ctx.options.resolveIcon?.(metadata?.icon) ?? index?.icon,
128
129
  root: metadata?.root,
129
130
  defaultOpen: metadata?.defaultOpen,
@@ -142,7 +143,7 @@ function buildFileNode(file, ctx) {
142
143
  const localized = findLocalizedFile(file.file.flattenedPath, "page", ctx) ?? file;
143
144
  const item = {
144
145
  type: "page",
145
- name: localized.data.data.title,
146
+ name: localized.data.data.title ?? pathToName(localized.file.name),
146
147
  icon: ctx.options.resolveIcon?.(localized.data.data.icon),
147
148
  url: ctx.options.getUrl(localized.data.slugs, ctx.lang),
148
149
  $ref: !ctx.options.noRef ? {
@@ -187,10 +188,9 @@ function findLocalizedFile(path, format, ctx) {
187
188
  if (!ctx.lang) return;
188
189
  return ctx.storage.read(`${path}.${ctx.lang}`, format);
189
190
  }
190
- function pathToName(name, resolveGroup = false) {
191
- const resolved = resolveGroup ? group.exec(name)?.[1] ?? name : name;
191
+ function pathToName(name) {
192
192
  const result = [];
193
- for (const c of resolved) {
193
+ for (const c of name) {
194
194
  if (result.length === 0) result.push(c.toLocaleUpperCase());
195
195
  else if (c === "-") result.push(" ");
196
196
  else result.push(c);
@@ -328,33 +328,31 @@ function loadFiles(files, options) {
328
328
  }
329
329
 
330
330
  // src/source/loader.ts
331
- function indexPages(storage, getUrl, languages = []) {
332
- const i18n = /* @__PURE__ */ new Map();
331
+ function indexPages(storage, getUrl, i18n) {
332
+ const defaultLanguage = i18n?.defaultLanguage ?? "";
333
+ const map = /* @__PURE__ */ new Map();
333
334
  const pages = /* @__PURE__ */ new Map();
334
335
  const metas = /* @__PURE__ */ new Map();
335
- const defaultMap = /* @__PURE__ */ new Map();
336
- i18n.set("", defaultMap);
337
- for (const file of storage.list()) {
338
- if (file.format === "meta") metas.set(file.file.path, fileToMeta(file));
339
- if (file.format === "page") {
340
- const page = fileToPage(file, getUrl, file.file.locale);
341
- pages.set(file.file.path, page);
342
- if (file.file.locale) continue;
343
- defaultMap.set(page.slugs.join("/"), page);
344
- for (const lang of languages) {
345
- const langMap = i18n.get(lang) ?? /* @__PURE__ */ new Map();
336
+ for (const item of storage.list()) {
337
+ if (item.format === "meta") metas.set(item.file.path, fileToMeta(item));
338
+ if (item.format === "page") {
339
+ const page = fileToPage(item, getUrl, item.file.locale);
340
+ pages.set(item.file.path, page);
341
+ if (item.file.locale) continue;
342
+ map.set(`${defaultLanguage}.${page.slugs.join("/")}`, page);
343
+ if (!i18n) continue;
344
+ for (const lang of i18n.languages) {
346
345
  const localized = storage.read(
347
- `${file.file.flattenedPath}.${lang}`,
346
+ `${item.file.flattenedPath}.${lang}`,
348
347
  "page"
349
348
  );
350
- const localizedPage = fileToPage(localized ?? file, getUrl, lang);
351
- langMap.set(localizedPage.slugs.join("/"), localizedPage);
352
- i18n.set(lang, langMap);
349
+ const localizedPage = fileToPage(localized ?? item, getUrl, lang);
350
+ map.set(`${lang}.${localizedPage.slugs.join("/")}`, localizedPage);
353
351
  }
354
352
  }
355
353
  }
356
354
  return {
357
- i18n,
355
+ pages: map,
358
356
  pathToPage: pages,
359
357
  pathToMeta: metas
360
358
  };
@@ -398,7 +396,7 @@ function createOutput(options) {
398
396
  getSlugs: slugsFn
399
397
  }
400
398
  );
401
- const walker = indexPages(storage, getUrl, options.i18n?.languages);
399
+ const walker = indexPages(storage, getUrl, options.i18n);
402
400
  const builder = createPageTreeBuilder();
403
401
  const pageTree = options.i18n === void 0 ? builder.build({
404
402
  storage,
@@ -416,21 +414,25 @@ function createOutput(options) {
416
414
  _i18n: options.i18n,
417
415
  pageTree,
418
416
  getPages(language = options.i18n?.defaultLanguage ?? "") {
419
- return Array.from(walker.i18n.get(language)?.values() ?? []);
417
+ const pages = [];
418
+ for (const key of walker.pages.keys()) {
419
+ if (key.startsWith(`${language}.`)) pages.push(walker.pages.get(key));
420
+ }
421
+ return pages;
420
422
  },
421
423
  getLanguages() {
422
424
  const list = [];
423
- for (const [language, pages] of walker.i18n) {
424
- if (language === "") continue;
425
+ if (!options.i18n) return list;
426
+ for (const language of options.i18n.languages) {
425
427
  list.push({
426
428
  language,
427
- pages: Array.from(pages.values())
429
+ pages: this.getPages(language)
428
430
  });
429
431
  }
430
432
  return list;
431
433
  },
432
434
  getPage(slugs = [], language = options.i18n?.defaultLanguage ?? "") {
433
- return walker.i18n.get(language)?.get(slugs.join("/"));
435
+ return walker.pages.get(`${language}.${slugs.join("/")}`);
434
436
  },
435
437
  getNodeMeta(node) {
436
438
  if (!node.$ref?.metaFile) return;
@@ -456,7 +458,7 @@ function createOutput(options) {
456
458
  }))
457
459
  );
458
460
  }
459
- return Array.from(walker.i18n.get("")?.values() ?? []).map((page) => ({
461
+ return this.getPages().map((page) => ({
460
462
  [slug ?? "slug"]: page.slugs
461
463
  }));
462
464
  }
@@ -59,7 +59,7 @@ function useShiki(code, {
59
59
  return /* @__PURE__ */ jsx(Pre, { children: /* @__PURE__ */ jsx(Code, { children: code }) });
60
60
  });
61
61
  if (typeof window === "undefined") {
62
- return use(highlight(code, shikiOptions));
62
+ return highlight(code, shikiOptions);
63
63
  }
64
64
  if (!currentTask.current || currentTask.current.key !== key) {
65
65
  if (currentTask.current) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-core",
3
- "version": "15.0.4",
3
+ "version": "15.0.6",
4
4
  "description": "The library for building a documentation website in Next.js",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -77,10 +77,10 @@
77
77
  "dist/*"
78
78
  ],
79
79
  "dependencies": {
80
- "@formatjs/intl-localematcher": "^0.5.10",
80
+ "@formatjs/intl-localematcher": "^0.6.0",
81
81
  "@orama/orama": "^2.1.1",
82
- "@shikijs/rehype": "^2.3.1",
83
- "@shikijs/transformers": "^2.3.1",
82
+ "@shikijs/rehype": "^2.3.2",
83
+ "@shikijs/transformers": "^2.3.2",
84
84
  "github-slugger": "^2.0.0",
85
85
  "hast-util-to-estree": "^3.1.1",
86
86
  "hast-util-to-jsx-runtime": "^2.3.2",
@@ -90,7 +90,7 @@
90
90
  "remark": "^15.0.0",
91
91
  "remark-gfm": "^4.0.0",
92
92
  "scroll-into-view-if-needed": "^3.1.0",
93
- "shiki": "^2.3.1",
93
+ "shiki": "^2.3.2",
94
94
  "unist-util-visit": "^5.0.0"
95
95
  },
96
96
  "devDependencies": {