fumadocs-core 15.7.0 → 15.7.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.
@@ -26,7 +26,13 @@ interface I18nConfig<Languages extends string = string> {
26
26
  *
27
27
  * @defaultValue 'dot'
28
28
  */
29
- parser?: 'dot' | 'dir';
29
+ parser?: 'dot' | 'dir' | 'none';
30
+ /**
31
+ * the fallback language when the page has no translations available for a given locale.
32
+ *
33
+ * Default to ``defaultLanguage`, no fallback when set to `null`.
34
+ */
35
+ fallbackLanguage?: Languages | null;
30
36
  }
31
37
  declare function defineI18n<Languages extends string>(config: I18nConfig<Languages>): I18nConfig<Languages>;
32
38
 
@@ -43,21 +43,12 @@ type Transformer = (context: {
43
43
  storage: ContentStorage;
44
44
  options: LoadOptions;
45
45
  }) => void;
46
- declare const parsers: {
47
- dir(path: string): [string, string?];
48
- dot(path: string): [string, string?];
49
- none(path: string): [string, string?];
50
- };
51
46
  /**
52
47
  * @returns a map of locale and its content storage.
53
48
  *
54
49
  * in the storage, locale codes are removed from file paths, hence the same file will have same file paths in every storage.
55
50
  */
56
- declare function loadFiles(files: VirtualFile[], options: LoadOptions, i18n: {
57
- parser: keyof typeof parsers;
58
- languages: string[];
59
- defaultLanguage: string;
60
- }): Record<string, ContentStorage>;
51
+ declare function loadFiles(files: VirtualFile[], options: LoadOptions, i18n: I18nConfig): Record<string, ContentStorage>;
61
52
 
62
53
  interface FileInfo {
63
54
  /**
@@ -112,7 +103,7 @@ interface LoaderOptions<T extends SourceConfig = SourceConfig, I18n extends I18n
112
103
  icon?: NonNullable<BaseOptions['resolveIcon']>;
113
104
  slugs?: (info: FileInfo) => string[];
114
105
  url?: UrlFn;
115
- source: Source<T>;
106
+ source: Source<T> | Source<T>[];
116
107
  transformers?: Transformer[];
117
108
  /**
118
109
  * Additional options for page tree builder
@@ -265,7 +256,7 @@ interface LegacyTransformerOptions<Page extends PageData, Meta extends MetaData>
265
256
 
266
257
  interface PageTreeBuilderContext<Page extends PageData = PageData, Meta extends MetaData = MetaData> {
267
258
  /**
268
- * @internal
259
+ * @internal resolve paths without extensions
269
260
  */
270
261
  resolveName: (name: string, format: 'meta' | 'page') => string;
271
262
  options: BaseOptions<Page, Meta>;
@@ -292,17 +283,17 @@ interface BaseOptions<Page extends PageData = PageData, Meta extends MetaData =
292
283
  noRef?: boolean;
293
284
  transformers?: PageTreeTransformer<Page, Meta>[];
294
285
  resolveIcon?: (icon: string | undefined) => ReactNode | undefined;
286
+ /**
287
+ * generate fallback page tree
288
+ *
289
+ * @defaultValue true
290
+ */
291
+ generateFallback?: boolean;
295
292
  }
296
293
  interface PageTreeBuilder<Page extends PageData = PageData, Meta extends MetaData = MetaData> {
297
294
  build: (options: BaseOptions<Page, Meta> & {
298
295
  id?: string;
299
296
  storage: ContentStorage<Page, Meta>;
300
- /**
301
- * generate fallback page tree
302
- *
303
- * @defaultValue true
304
- */
305
- generateFallback?: boolean;
306
297
  }) => Root;
307
298
  /**
308
299
  * Build page tree and fallback to the default language if the localized page doesn't exist
@@ -252,7 +252,7 @@ function build(id, ctx) {
252
252
  return root;
253
253
  }
254
254
  function createPageTreeBuilder(getUrl) {
255
- function getTransformers(options, generateFallback = true) {
255
+ function getTransformers(options, generateFallback) {
256
256
  const transformers = [legacyTransformer(options)];
257
257
  if (options.transformers) {
258
258
  transformers.push(...options.transformers);
@@ -275,25 +275,21 @@ function createPageTreeBuilder(getUrl) {
275
275
  };
276
276
  }
277
277
  return {
278
- build({ storage, id, generateFallback, ...options }) {
279
- const resolve = createFlattenPathResolver(storage);
280
- return build(id ?? "root", {
281
- transformers: getTransformers(options, generateFallback),
282
- options,
283
- builder: this,
284
- storage,
285
- getUrl,
286
- resolveName(name, format) {
287
- return resolve(name, format) ?? name;
288
- }
289
- });
278
+ build({ storage, id, ...options }) {
279
+ const key = "";
280
+ return this.buildI18n({
281
+ id,
282
+ storages: { [key]: storage },
283
+ ...options
284
+ })[key];
290
285
  },
291
- buildI18n({ id, storages, ...options }) {
292
- const transformers = getTransformers(options);
286
+ buildI18n({ id, storages, generateFallback = true, ...options }) {
287
+ const transformers = getTransformers(options, generateFallback);
293
288
  const out = {};
294
289
  for (const [locale, storage] of Object.entries(storages)) {
295
290
  const resolve = createFlattenPathResolver(storage);
296
- out[locale] = build(id ?? (locale.length === 0 ? "root" : locale), {
291
+ const branch = locale.length === 0 ? "root" : locale;
292
+ out[locale] = build(id ? `${id}-${branch}` : branch, {
297
293
  transformers,
298
294
  builder: this,
299
295
  options,
@@ -401,7 +397,7 @@ var parsers = {
401
397
  };
402
398
  function loadFiles(files, options, i18n) {
403
399
  const { buildFile, transformers = [] } = options;
404
- const parser = parsers[i18n.parser];
400
+ const parser = parsers[i18n.parser ?? "dot"];
405
401
  const storages = {};
406
402
  const normalized = files.map(
407
403
  (file) => buildFile({
@@ -409,8 +405,16 @@ function loadFiles(files, options, i18n) {
409
405
  path: normalizePath(file.path)
410
406
  })
411
407
  );
412
- function scan(lang, fallback) {
413
- const storage = new FileSystem(fallback);
408
+ const fallbackLang = i18n.fallbackLanguage !== null ? i18n.fallbackLanguage ?? i18n.defaultLanguage : null;
409
+ function scan(lang) {
410
+ if (storages[lang]) return;
411
+ let storage;
412
+ if (fallbackLang && fallbackLang !== lang) {
413
+ scan(fallbackLang);
414
+ storage = new FileSystem(storages[fallbackLang]);
415
+ } else {
416
+ storage = new FileSystem();
417
+ }
414
418
  for (const item of normalized) {
415
419
  const [path, locale = i18n.defaultLanguage] = parser(item.path);
416
420
  if (locale === lang) storage.write(path, item);
@@ -421,12 +425,9 @@ function loadFiles(files, options, i18n) {
421
425
  options
422
426
  });
423
427
  }
424
- return storage;
425
- }
426
- storages[i18n.defaultLanguage] = scan(i18n.defaultLanguage);
427
- for (const lang of i18n.languages) {
428
- storages[lang] ??= scan(lang, storages[i18n.defaultLanguage]);
428
+ storages[lang] = storage;
429
429
  }
430
+ for (const lang of i18n.languages) scan(lang);
430
431
  return storages;
431
432
  }
432
433
  function normalizePath(path) {
@@ -479,6 +480,17 @@ function createGetUrl(baseUrl, i18n) {
479
480
  function loader(options) {
480
481
  return createOutput(options);
481
482
  }
483
+ function loadSource(source) {
484
+ const out = [];
485
+ for (const item of Array.isArray(source) ? source : [source]) {
486
+ if (typeof item.files === "function") {
487
+ out.push(...item.files());
488
+ } else {
489
+ out.push(...item.files);
490
+ }
491
+ }
492
+ return out;
493
+ }
482
494
  function createOutput(options) {
483
495
  if (!options.url && !options.baseUrl) {
484
496
  console.warn("`loader()` now requires a `baseUrl` option to be defined.");
@@ -492,14 +504,15 @@ function createOutput(options) {
492
504
  transformers = []
493
505
  } = options;
494
506
  const defaultLanguage = i18n?.defaultLanguage ?? "";
495
- const files = typeof source.files === "function" ? source.files() : source.files;
507
+ const files = loadSource(source);
496
508
  const transformerSlugs = ({ storage }) => {
497
509
  const indexFiles = /* @__PURE__ */ new Set();
498
510
  const taken = /* @__PURE__ */ new Set();
511
+ const autoIndex = slugsFn === void 0;
499
512
  for (const path of storage.getFiles()) {
500
513
  const file = storage.read(path);
501
514
  if (!file || file.format !== "page" || file.slugs) continue;
502
- if (isIndex(path) && !slugsFn) {
515
+ if (isIndex(path) && autoIndex) {
503
516
  indexFiles.add(path);
504
517
  continue;
505
518
  }
@@ -537,10 +550,7 @@ function createOutput(options) {
537
550
  },
538
551
  transformers: [transformerSlugs, ...transformers]
539
552
  },
540
- i18n ? {
541
- ...i18n,
542
- parser: i18n.parser ?? "dot"
543
- } : {
553
+ i18n ?? {
544
554
  defaultLanguage,
545
555
  parser: "none",
546
556
  languages: [defaultLanguage]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-core",
3
- "version": "15.7.0",
3
+ "version": "15.7.1",
4
4
  "description": "The library for building a documentation website in Next.js",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -110,7 +110,7 @@
110
110
  },
111
111
  "devDependencies": {
112
112
  "@mdx-js/mdx": "^3.1.0",
113
- "@mixedbread/sdk": "^0.25.0",
113
+ "@mixedbread/sdk": "^0.26.0",
114
114
  "@oramacloud/client": "^2.1.4",
115
115
  "@tanstack/react-router": "^1.131.27",
116
116
  "@types/estree-jsx": "^1.0.5",