mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 06:31:49 +00:00
Core: Fix type inference for pageTree.attachFile in loader()
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-core': patch
|
||||
---
|
||||
|
||||
Fix type inference for `pageTree.attachFile` in `loader()`
|
||||
@@ -23,4 +23,11 @@ export interface I18nConfig {
|
||||
* @defaultValue 'never'
|
||||
*/
|
||||
hideLocale?: 'always' | 'default-locale' | 'never';
|
||||
|
||||
/**
|
||||
* Used by `loader()`, specify the way to parse i18n file structure.
|
||||
*
|
||||
* @defaultValue 'dot'
|
||||
*/
|
||||
parser?: 'dot' | 'dir';
|
||||
}
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
import type { MetaData, PageData } from '@/source/types';
|
||||
import {
|
||||
parseFilePath,
|
||||
parseFolderPath,
|
||||
type FileInfo,
|
||||
type FolderInfo,
|
||||
parseFilePath,
|
||||
parseFolderPath,
|
||||
} from './path';
|
||||
import { joinPath, splitPath } from '@/utils/path';
|
||||
|
||||
export interface MetaFile {
|
||||
export interface MetaFile<Data extends MetaData = MetaData> {
|
||||
file: FileInfo;
|
||||
format: 'meta';
|
||||
data: MetaData;
|
||||
data: Data;
|
||||
}
|
||||
|
||||
export interface PageFile {
|
||||
export interface PageFile<Data extends PageData = PageData> {
|
||||
file: FileInfo;
|
||||
format: 'page';
|
||||
data: {
|
||||
slugs: string[];
|
||||
data: PageData;
|
||||
data: Data;
|
||||
};
|
||||
}
|
||||
|
||||
export type File = MetaFile | PageFile;
|
||||
type File = MetaFile | PageFile;
|
||||
|
||||
export interface Folder {
|
||||
export interface Folder<
|
||||
Page extends PageData = PageData,
|
||||
Meta extends MetaData = MetaData,
|
||||
> {
|
||||
file: FolderInfo;
|
||||
children: (File | Folder)[];
|
||||
children: (MetaFile<Meta> | PageFile<Page> | Folder<Page, Meta>)[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type * as PageTree from '@/server/page-tree';
|
||||
import type { I18nConfig } from '@/i18n';
|
||||
import {
|
||||
type I18nLoadOptions,
|
||||
loadFiles,
|
||||
loadFilesI18n,
|
||||
type LoadOptions,
|
||||
@@ -9,8 +8,10 @@ import {
|
||||
type VirtualFile,
|
||||
} from './load-files';
|
||||
import type { MetaData, PageData, UrlFn } from './types';
|
||||
import type { BuildPageTreeOptions } from './page-tree-builder';
|
||||
import { createPageTreeBuilder } from './page-tree-builder';
|
||||
import {
|
||||
type BaseOptions as BasePageTreeBuilderOptions,
|
||||
createPageTreeBuilder,
|
||||
} from './page-tree-builder';
|
||||
import { type FileInfo } from './path';
|
||||
import type { MetaFile, PageFile, Storage } from './file-system';
|
||||
import { joinPath } from '@/utils/path';
|
||||
@@ -25,28 +26,28 @@ export interface SourceConfig {
|
||||
metaData: MetaData;
|
||||
}
|
||||
|
||||
export interface LoaderOptions {
|
||||
export interface LoaderOptions<
|
||||
T extends SourceConfig = SourceConfig,
|
||||
I18n extends I18nConfig | undefined = I18nConfig | undefined,
|
||||
> {
|
||||
baseUrl: string;
|
||||
|
||||
icon?: NonNullable<BuildPageTreeOptions['resolveIcon']>;
|
||||
icon?: NonNullable<BasePageTreeBuilderOptions['resolveIcon']>;
|
||||
slugs?: LoadOptions['getSlugs'];
|
||||
url?: UrlFn;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Inevitable
|
||||
source: Source<any>;
|
||||
source: Source<T>;
|
||||
transformers?: Transformer[];
|
||||
|
||||
/**
|
||||
* Additional options for page tree builder
|
||||
*/
|
||||
pageTree?: Partial<Omit<BuildPageTreeOptions, 'storage' | 'getUrl'>>;
|
||||
pageTree?: Partial<BasePageTreeBuilderOptions<T['pageData'], T['metaData']>>;
|
||||
|
||||
/**
|
||||
* Configure i18n
|
||||
*/
|
||||
i18n?: I18nConfig & {
|
||||
parser?: I18nLoadOptions['i18n']['parser'];
|
||||
};
|
||||
i18n?: I18n;
|
||||
}
|
||||
|
||||
export interface Source<Config extends SourceConfig> {
|
||||
@@ -231,15 +232,17 @@ export function getSlugs(info: FileInfo): string[] {
|
||||
);
|
||||
}
|
||||
|
||||
type InferSourceConfig<T> = T extends Source<infer Config> ? Config : never;
|
||||
|
||||
export function loader<Options extends LoaderOptions>(
|
||||
options: Options,
|
||||
export function loader<
|
||||
Config extends SourceConfig,
|
||||
I18n extends I18nConfig | undefined = undefined,
|
||||
>(
|
||||
options: LoaderOptions<Config, I18n>,
|
||||
): LoaderOutput<{
|
||||
source: InferSourceConfig<Options['source']>;
|
||||
i18n: Options['i18n'] extends I18nConfig ? true : false;
|
||||
source: Config;
|
||||
i18n: I18n extends I18nConfig ? true : false;
|
||||
}> {
|
||||
return createOutput(options) as ReturnType<typeof loader<Options>>;
|
||||
// @ts-expect-error -- forced type cast
|
||||
return createOutput(options);
|
||||
}
|
||||
|
||||
function createOutput(options: LoaderOptions): LoaderOutput<LoaderConfig> {
|
||||
@@ -270,7 +273,7 @@ function createOutput(options: LoaderOptions): LoaderOutput<LoaderConfig> {
|
||||
};
|
||||
|
||||
const walker = indexPages(storages, getUrl, options.i18n);
|
||||
const builder = createPageTreeBuilder();
|
||||
const builder = createPageTreeBuilder(getUrl);
|
||||
let pageTree: LoaderOutput<LoaderConfig>['pageTree'] | undefined;
|
||||
|
||||
return {
|
||||
@@ -280,7 +283,6 @@ function createOutput(options: LoaderOptions): LoaderOutput<LoaderConfig> {
|
||||
pageTree ??= builder.buildI18n({
|
||||
storages,
|
||||
resolveIcon: options.icon,
|
||||
getUrl,
|
||||
i18n: options.i18n,
|
||||
...options.pageTree,
|
||||
}) as unknown as LoaderOutput<LoaderConfig>['pageTree'];
|
||||
@@ -288,7 +290,6 @@ function createOutput(options: LoaderOptions): LoaderOutput<LoaderConfig> {
|
||||
pageTree ??= builder.build({
|
||||
storage: storages[''],
|
||||
resolveIcon: options.icon,
|
||||
getUrl,
|
||||
...options.pageTree,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import type { I18nConfig } from '@/i18n';
|
||||
import type * as PageTree from '../server/page-tree';
|
||||
import type { File, Folder, MetaFile, PageFile, Storage } from './file-system';
|
||||
import type { Folder, MetaFile, PageFile, Storage } from './file-system';
|
||||
import { joinPath } from '@/utils/path';
|
||||
import { type UrlFn } from './types';
|
||||
import type { MetaData, PageData, UrlFn } from './types';
|
||||
|
||||
interface PageTreeBuilderContext {
|
||||
storage: Storage;
|
||||
@@ -12,10 +12,14 @@ interface PageTreeBuilderContext {
|
||||
localeStorage?: Storage;
|
||||
|
||||
builder: PageTreeBuilder;
|
||||
options: Options;
|
||||
options: BaseOptions;
|
||||
getUrl: UrlFn;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
export interface BaseOptions<
|
||||
Page extends PageData = PageData,
|
||||
Meta extends MetaData = MetaData,
|
||||
> {
|
||||
/**
|
||||
* Remove references to the file path of original nodes (`$ref`)
|
||||
*
|
||||
@@ -23,23 +27,22 @@ interface Options {
|
||||
*/
|
||||
noRef?: boolean;
|
||||
|
||||
attachFile?: (node: PageTree.Item, file?: PageFile) => PageTree.Item;
|
||||
attachFile?: (node: PageTree.Item, file?: PageFile<Page>) => PageTree.Item;
|
||||
attachFolder?: (
|
||||
node: PageTree.Folder,
|
||||
folder: Folder,
|
||||
meta?: MetaFile,
|
||||
folder: Folder<Page, Meta>,
|
||||
meta?: MetaFile<Meta>,
|
||||
) => PageTree.Folder;
|
||||
attachSeparator?: (node: PageTree.Separator) => PageTree.Separator;
|
||||
|
||||
getUrl: UrlFn;
|
||||
resolveIcon?: (icon: string | undefined) => ReactElement | undefined;
|
||||
}
|
||||
|
||||
export interface BuildPageTreeOptions extends Options {
|
||||
export interface BuildPageTreeOptions extends BaseOptions {
|
||||
storage: Storage;
|
||||
}
|
||||
|
||||
export interface BuildPageTreeOptionsWithI18n extends Options {
|
||||
export interface BuildPageTreeOptionsWithI18n extends BaseOptions {
|
||||
storages: Record<string, Storage>;
|
||||
i18n: I18nConfig;
|
||||
}
|
||||
@@ -63,7 +66,7 @@ const restReversed = 'z...a' as const;
|
||||
const extractPrefix = '...';
|
||||
const excludePrefix = '!';
|
||||
|
||||
function isPageFile(node: Folder | File): node is PageFile {
|
||||
function isPageFile(node: Folder | PageFile | MetaFile): node is PageFile {
|
||||
return 'data' in node && node.format === 'page';
|
||||
}
|
||||
|
||||
@@ -74,7 +77,7 @@ function isPageFile(node: Folder | File): node is PageFile {
|
||||
* @returns Nodes with specified locale in context (sorted)
|
||||
*/
|
||||
function buildAll(
|
||||
nodes: (Folder | File)[],
|
||||
nodes: (Folder | MetaFile | PageFile)[],
|
||||
ctx: PageTreeBuilderContext,
|
||||
skipIndex: boolean,
|
||||
): PageTree.Node[] {
|
||||
@@ -262,7 +265,7 @@ function buildFileNode(
|
||||
name: file.data.data.title ?? pathToName(file.file.name),
|
||||
description: file.data.data.description,
|
||||
icon: ctx.options.resolveIcon?.(file.data.data.icon),
|
||||
url: ctx.options.getUrl(file.data.slugs, ctx.locale),
|
||||
url: ctx.getUrl(file.data.slugs, ctx.locale),
|
||||
$ref: !ctx.options.noRef
|
||||
? {
|
||||
file: file.file.path,
|
||||
@@ -284,19 +287,21 @@ function build(ctx: PageTreeBuilderContext): PageTree.Root {
|
||||
};
|
||||
}
|
||||
|
||||
export function createPageTreeBuilder(): PageTreeBuilder {
|
||||
export function createPageTreeBuilder(getUrl: UrlFn): PageTreeBuilder {
|
||||
return {
|
||||
build(options) {
|
||||
return build({
|
||||
options,
|
||||
builder: this,
|
||||
storage: options.storage,
|
||||
getUrl,
|
||||
});
|
||||
},
|
||||
buildI18n({ i18n, ...options }) {
|
||||
const entries = i18n.languages.map<[string, PageTree.Root]>((lang) => {
|
||||
const tree = build({
|
||||
options,
|
||||
getUrl,
|
||||
builder: this,
|
||||
locale: lang,
|
||||
storage: options.storages[i18n.defaultLanguage],
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
".source/**",
|
||||
"!.contentlayer/.cache/**",
|
||||
".next/**",
|
||||
"content/docs/openapi/(generated)",
|
||||
"content/docs/openapi/\\(generated\\)",
|
||||
"!.next/cache/**",
|
||||
"dist/**",
|
||||
"out",
|
||||
|
||||
Reference in New Issue
Block a user