fumadocs-core 16.15.0 → 16.15.2

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.
@@ -1,8 +1,10 @@
1
1
  import { r as normalizeUrl } from "./url-BVHvi3_K.js";
2
2
  import { c as visit } from "./utils-Dn9VIXRN.js";
3
+ import { t as isEqualShallow } from "./is-equal-LdLqRs0o.js";
3
4
  import { a as normalize, i as joinPath, n as dirname, r as extname, t as basename } from "./path-1H5VzYiT.js";
4
5
  import { slugsPlugin } from "./source/plugins/slugs.js";
5
6
  import { t as iconPlugin } from "./icon-BILaoXeg.js";
7
+ import { cache } from "react";
6
8
  //#region src/source/storage/file-system.ts
7
9
  /**
8
10
  * In memory file system.
@@ -83,11 +85,8 @@ function multiple(sources) {
83
85
  })) };
84
86
  return out;
85
87
  }
86
- function source(config) {
87
- return { files: [...config.pages, ...config.metas] };
88
- }
89
88
  /**
90
- * update a source object in-place.
89
+ * update a **static** source object in-place.
91
90
  */
92
91
  function update(source) {
93
92
  return {
@@ -160,10 +159,11 @@ function createContentStorageBuilder(loaderConfig) {
160
159
  function scan(type, source) {
161
160
  for (const inputFile of source.files) {
162
161
  let file;
162
+ const path = normalize(source.baseDir ? `${source.baseDir}/${inputFile.path}` : inputFile.path);
163
163
  if (inputFile.type === "page") file = {
164
164
  format: "page",
165
165
  type,
166
- path: normalize(inputFile.path),
166
+ path,
167
167
  slugs: inputFile.slugs,
168
168
  data: inputFile.data,
169
169
  absolutePath: inputFile.absolutePath
@@ -171,11 +171,11 @@ function createContentStorageBuilder(loaderConfig) {
171
171
  else file = {
172
172
  format: "meta",
173
173
  type,
174
- path: normalize(inputFile.path),
174
+ path,
175
175
  absolutePath: inputFile.absolutePath,
176
176
  data: inputFile.data
177
177
  };
178
- const [storageKey, locale = i18n ? i18n.defaultLanguage : EmptyLang] = parser(file.path);
178
+ const [storageKey, locale = i18n ? i18n.defaultLanguage : EmptyLang] = parser(path);
179
179
  const entry = [storageKey, file];
180
180
  if (Array.isArray(locale)) for (const item of locale) pushMapList(fileMap, item, entry);
181
181
  else pushMapList(fileMap, locale, entry);
@@ -820,4 +820,86 @@ function buildPlugins(plugins, sort = true) {
820
820
  return flatten;
821
821
  }
822
822
  //#endregion
823
- export { multiple as a, FileSystem as c, isStaticSource as i, loader as n, source as o, isDynamicSource as r, update as s, createGetUrl as t };
823
+ //#region src/source/dynamic.ts
824
+ /** content loader API for static & dynamic content sources, with in-memory cache. */
825
+ function dynamicLoader(input, options) {
826
+ let cachedLoader;
827
+ const memoryCache = /* @__PURE__ */ new Map();
828
+ async function resolveSources() {
829
+ if (isStaticSource(input) || isDynamicSource(input)) return resolveSource(input);
830
+ const entries = await Promise.all(Object.entries(input).map(async ([k, v]) => [k, await resolveSource(v)]));
831
+ return Object.fromEntries(entries);
832
+ }
833
+ function resolveSource(v) {
834
+ if (isStaticSource(v)) return v;
835
+ const mapFiles = (files) => ({
836
+ baseDir: v.baseDir,
837
+ files,
838
+ configureStatic: v.configureStatic
839
+ });
840
+ if (!v.cache || v.cache === "memory") {
841
+ const cached = memoryCache.get(v);
842
+ if (cached && (cached.expires === void 0 || Date.now() < cached.expires)) return cached.value;
843
+ const value = Promise.resolve(v.files()).then(mapFiles).catch((e) => {
844
+ if (memoryCache.get(v)?.value === value) memoryCache.delete(v);
845
+ throw e;
846
+ });
847
+ memoryCache.set(v, {
848
+ value,
849
+ expires: v.staleTime !== void 0 ? Date.now() + v.staleTime : void 0
850
+ });
851
+ return value;
852
+ }
853
+ return Promise.resolve(v.files()).then(mapFiles);
854
+ }
855
+ const dynamicLoader = {
856
+ get: cache(async () => {
857
+ const resolved = await resolveSources();
858
+ if (cachedLoader && isEqual(cachedLoader.input, resolved)) return cachedLoader.value;
859
+ cachedLoader = {
860
+ input: resolved,
861
+ value: loader(resolved, options)
862
+ };
863
+ return cachedLoader.value;
864
+ }),
865
+ $inferPage: void 0,
866
+ $inferMeta: void 0,
867
+ async revalidate(name) {
868
+ dynamicLoader.invalidate(name);
869
+ if (name === void 0) await resolveSources();
870
+ else if (!isStaticSource(input) && !isDynamicSource(input)) await resolveSource(input[name]);
871
+ },
872
+ invalidate(name) {
873
+ if (isStaticSource(input)) return;
874
+ if (name === void 0) {
875
+ memoryCache.clear();
876
+ if (isDynamicSource(input)) input.invalidate?.();
877
+ else for (const v of Object.values(input)) if (isDynamicSource(v)) v.invalidate?.();
878
+ return;
879
+ }
880
+ if (isDynamicSource(input)) return;
881
+ const s = input[name];
882
+ if (!isDynamicSource(s)) return;
883
+ memoryCache.delete(s);
884
+ s.invalidate?.();
885
+ }
886
+ };
887
+ if (isDynamicSource(input) || isStaticSource(input)) {
888
+ input.configureDynamic?.({ loader: dynamicLoader });
889
+ if (isDynamicSource(input)) input.configure?.(dynamicLoader, {});
890
+ } else for (const [k, v] of Object.entries(input)) {
891
+ v.configureDynamic?.({
892
+ loader: dynamicLoader,
893
+ source: k
894
+ });
895
+ if (isDynamicSource(v)) v.configure?.(dynamicLoader, { source: k });
896
+ }
897
+ return dynamicLoader;
898
+ }
899
+ function isEqual(a, b) {
900
+ if (isStaticSource(a) && isStaticSource(b)) return isEqualShallow(a.files, b.files);
901
+ if (!isStaticSource(a) && !isStaticSource(b)) return Object.keys(b).every((k) => isEqualShallow(a[k].files, b[k].files));
902
+ return false;
903
+ }
904
+ //#endregion
905
+ export { update as a, multiple as i, createGetUrl as n, FileSystem as o, loader as r, dynamicLoader as t };
@@ -1,4 +1,3 @@
1
- import { t as getNegotiator } from "../negotiation-DAsKgSvF.js";
2
1
  import { NextResponse } from "next/server.js";
3
2
  //#region ../../node_modules/.pnpm/@formatjs+fast-memoize@3.1.7/node_modules/@formatjs/fast-memoize/index.js
4
3
  function memoize(fn, options) {
@@ -3738,6 +3737,70 @@ function match(requestedLocales, availableLocales, defaultLocale, opts) {
3738
3737
  return ResolveLocale(availableLocales, CanonicalizeLocaleList(requestedLocales), { localeMatcher: opts?.algorithm || "best fit" }, [], {}, () => defaultLocale).locale;
3739
3738
  }
3740
3739
  //#endregion
3740
+ //#region src/utils/accept-language.ts
3741
+ function parseAcceptLanguage(header) {
3742
+ const specs = [];
3743
+ for (const section of header.split(",")) {
3744
+ const [rawTag, ...params] = section.split(";");
3745
+ const full = rawTag.trim().toLowerCase();
3746
+ if (full.length === 0) continue;
3747
+ let quality = 1;
3748
+ for (const param of params) {
3749
+ const separator = param.indexOf("=");
3750
+ if (separator === -1 || param.slice(0, separator).trim().toLowerCase() !== "q") continue;
3751
+ const parsed = Number.parseFloat(param.slice(separator + 1));
3752
+ if (!Number.isNaN(parsed)) quality = parsed;
3753
+ }
3754
+ const dash = full.indexOf("-");
3755
+ specs.push({
3756
+ prefix: dash === -1 ? full : full.slice(0, dash),
3757
+ full,
3758
+ quality,
3759
+ order: specs.length
3760
+ });
3761
+ }
3762
+ return specs;
3763
+ }
3764
+ function matchLanguage(language, specs) {
3765
+ const full = language.trim().toLowerCase();
3766
+ const dash = full.indexOf("-");
3767
+ const prefix = dash === -1 ? full : full.slice(0, dash);
3768
+ const best = {
3769
+ language,
3770
+ quality: 0,
3771
+ specificity: 0,
3772
+ order: -1
3773
+ };
3774
+ for (const spec of specs) {
3775
+ let specificity;
3776
+ if (spec.full === full) specificity = 4;
3777
+ else if (spec.prefix === full) specificity = 2;
3778
+ else if (spec.full === prefix) specificity = 1;
3779
+ else if (spec.full === "*") specificity = 0;
3780
+ else continue;
3781
+ if ((specificity - best.specificity || spec.quality - best.quality || spec.order - best.order) > 0) {
3782
+ best.quality = spec.quality;
3783
+ best.specificity = specificity;
3784
+ best.order = spec.order;
3785
+ }
3786
+ }
3787
+ if (best.quality > 0) return best;
3788
+ }
3789
+ /**
3790
+ * Filter `available` down to the languages the `Accept-Language` header accepts, ordered by
3791
+ * client preference. A missing (`null`) header accepts everything.
3792
+ */
3793
+ function negotiateLanguages(header, available) {
3794
+ const specs = parseAcceptLanguage(header ?? "*");
3795
+ const matches = [];
3796
+ for (const language of available) {
3797
+ const match = matchLanguage(language, specs);
3798
+ if (match) matches.push(match);
3799
+ }
3800
+ matches.sort((a, b) => b.quality - a.quality || b.specificity - a.specificity || a.order - b.order);
3801
+ return matches.map((match) => match.language);
3802
+ }
3803
+ //#endregion
3741
3804
  //#region src/i18n/middleware.ts
3742
3805
  const DefaultFormatter = {
3743
3806
  get(url) {
@@ -3773,7 +3836,7 @@ function createI18nMiddleware({ languages, defaultLanguage, format = DefaultForm
3773
3836
  if (pathLocale && !languages.includes(pathLocale)) pathLocale = void 0;
3774
3837
  if (!pathLocale) {
3775
3838
  if (hideLocale === "default-locale") return NextResponse.rewrite(formatter.add(url, defaultLanguage));
3776
- const preferred = match(getNegotiator(request).languages(languages), languages, defaultLanguage);
3839
+ const preferred = match(negotiateLanguages(request.headers.get("accept-language"), languages), languages, defaultLanguage);
3777
3840
  if (hideLocale === "always") {
3778
3841
  const locale = request.cookies.get(cookieName)?.value ?? preferred;
3779
3842
  return NextResponse.rewrite(formatter.add(url, locale));
@@ -333,18 +333,33 @@ type SourceUnion<Config extends SourceConfig = SourceConfig> = StaticSource<Conf
333
333
  * @deprecated use `StaticSource<Config>` instead
334
334
  */
335
335
  type Source<Config extends SourceConfig = SourceConfig> = StaticSource<Config>;
336
- interface StaticSource<Config extends SourceConfig = SourceConfig> {
337
- files: VirtualFile<Config>[];
336
+ interface GenericSourceOptions {
337
+ /** the base directory for generated virtual files */
338
+ baseDir?: string;
338
339
  /**
339
- * called when the source is attached to a new static loader, before loader output is accessible.
340
+ * when the source is attached to a new static loader, before loader output is accessible.
341
+ *
342
+ * can be called multiple times when attached to a dynamic loader.
340
343
  **/
341
344
  configureStatic?: (opts: {
342
345
  loader: LoaderOutput;
343
346
  source?: string;
344
347
  }) => void;
348
+ /**
349
+ * when the source is attached to a dynamic loader.
350
+ *
351
+ * called at most once for each source object.
352
+ **/
353
+ configureDynamic?: (opts: {
354
+ loader: DynamicLoader;
355
+ source?: string;
356
+ }) => void;
357
+ }
358
+ interface StaticSource<Config extends SourceConfig = SourceConfig> extends GenericSourceOptions {
359
+ files: VirtualFile<Config>[];
345
360
  }
346
361
  /** one dynamic source object can only be used by one dynamic loader */
347
- interface DynamicSource<Config extends SourceConfig = SourceConfig> {
362
+ type DynamicSource<Config extends SourceConfig = SourceConfig> = GenericSourceOptions & {
348
363
  /**
349
364
  * - `memory`: the dynamic loader's in-memory cache handles caching.
350
365
  * - `custom`: the source handles caching itself. When the newer result of `files()` is (shallowly) different from the previous result, the source is considered revalidated, and all associated properties will be re-computed.
@@ -353,20 +368,19 @@ interface DynamicSource<Config extends SourceConfig = SourceConfig> {
353
368
  **/
354
369
  cache?: 'memory' | 'custom';
355
370
  files: () => Awaitable<VirtualFile<Config>[]>;
371
+ /** @deprecated use `configureDynamic` instead */
356
372
  configure?: (loader: DynamicLoader, opts: {
357
373
  source?: string;
358
374
  }) => void;
359
- /**
360
- * called when the source is attached to a new static loader, before loader output is accessible.
361
- *
362
- * it can be called multiple times, when the parent dynamic loader creates a different static loader.
363
- **/
364
- configureStatic?: (opts: {
365
- loader: LoaderOutput;
366
- source?: string;
367
- }) => void;
375
+ /** when the source is invalidated */
368
376
  invalidate?: () => void;
369
- }
377
+ } & ({
378
+ cache?: 'memory';
379
+ /** enable time-based revalidation, the previous result will be stale after the specified duration (ms) */
380
+ staleTime?: number;
381
+ } | {
382
+ cache: 'custom';
383
+ });
370
384
  type SourceConfig = {
371
385
  pageData: PageData;
372
386
  metaData: MetaData;
@@ -423,13 +437,6 @@ declare function multiple<T extends Record<string, StaticSource>>(sources: T): T
423
437
  type: k;
424
438
  };
425
439
  }> : never; } : never;
426
- declare function source<Page extends PageData, Meta extends MetaData>(config: {
427
- pages: VirtualPage<Page>[];
428
- metas: VirtualMeta<Meta>[];
429
- }): StaticSource<{
430
- pageData: Page;
431
- metaData: Meta;
432
- }>;
433
440
  interface SourceUpdater<Config extends SourceConfig> {
434
441
  files: <Page extends PageData, Meta extends MetaData>(fn: (files: VirtualFile<Config>[]) => (VirtualPage<Page> | VirtualMeta<Meta>)[]) => SourceUpdater<{
435
442
  pageData: Page;
@@ -446,7 +453,7 @@ interface SourceUpdater<Config extends SourceConfig> {
446
453
  build: () => StaticSource<Config>;
447
454
  }
448
455
  /**
449
- * update a source object in-place.
456
+ * update a **static** source object in-place.
450
457
  */
451
458
  declare function update<Config extends SourceConfig>(source: StaticSource<Config>): SourceUpdater<Config>;
452
459
  declare namespace path_d_exports {
@@ -503,4 +510,4 @@ declare function llms<C extends LoaderConfig = LoaderConfig>(loader: LoaderOutpu
503
510
  indexNode(node: Node, lang?: string): string;
504
511
  };
505
512
  //#endregion
506
- export { SlugFn as A, ContentStorageMetaFile as B, LoaderPluginOption as C, createGetUrl as D, ResolvedLoaderConfig as E, PageTreeBuilder as F, FileSystem as H, PageTreeBuilderContext as I, PageTreeOptions as L, getSlugs as M, slugsFromData as N, loader as O, slugsPlugin as P, PageTreeTransformer as R, LoaderPlugin as S, Page as T, ContentStoragePageFile as V, InferMetaType as _, MetaData as a, LoaderOptions as b, SourceUnion as c, multiple as d, source as f, dynamicLoader as g, DynamicLoaderConfig as h, DynamicSource as i, SlugsPluginOptions as j, types_d_exports as k, StaticSource as l, DynamicLoader as m, llms as n, PageData as o, update as p, path_d_exports as r, Source as s, LLMsConfig as t, VirtualFile as u, InferPageType as v, Meta as w, LoaderOutput as x, LoaderConfig as y, ContentStorage as z };
513
+ export { SlugsPluginOptions as A, ContentStoragePageFile as B, Meta as C, loader as D, createGetUrl as E, PageTreeBuilderContext as F, PageTreeOptions as I, PageTreeTransformer as L, slugsFromData as M, slugsPlugin as N, types_d_exports as O, PageTreeBuilder as P, ContentStorage as R, LoaderPluginOption as S, ResolvedLoaderConfig as T, FileSystem as V, InferPageType as _, MetaData as a, LoaderOutput as b, SourceUnion as c, multiple as d, update as f, InferMetaType as g, dynamicLoader as h, DynamicSource as i, getSlugs as j, SlugFn as k, StaticSource as l, DynamicLoaderConfig as m, llms as n, PageData as o, DynamicLoader as p, path_d_exports as r, Source as s, LLMsConfig as t, VirtualFile as u, LoaderConfig as v, Page as w, LoaderPlugin as x, LoaderOptions as y, ContentStorageMetaFile as z };
@@ -1,6 +1,4 @@
1
- import Negotiator from "negotiator";
2
1
  //#region src/negotiation/index.d.ts
3
- declare function getNegotiator(request: Request): Negotiator;
4
2
  /**
5
3
  * Rewrite incoming path matching the `source` pattern into the `destination` pattern.
6
4
  *
@@ -16,4 +14,4 @@ declare function isMarkdownPreferred(request: Request, options?: {
16
14
  markdownMediaTypes?: string[];
17
15
  }): boolean;
18
16
  //#endregion
19
- export { getNegotiator, isMarkdownPreferred, rewritePath };
17
+ export { isMarkdownPreferred, rewritePath };