mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
OpenAPI: Support generating virtual pages for Source API
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-openapi': minor
|
||||
---
|
||||
|
||||
Support generating virtual pages for Source API
|
||||
@@ -12,7 +12,6 @@ export {
|
||||
loadFiles,
|
||||
type PageFile,
|
||||
type MetaFile,
|
||||
type LoadOptions,
|
||||
type ContentStorage,
|
||||
type Transformer,
|
||||
} from './load-files';
|
||||
|
||||
@@ -5,11 +5,6 @@ import { basename, dirname, joinPath, slash, splitPath } from '@/source/path';
|
||||
import type { I18nConfig } from '@/i18n';
|
||||
import type { LoaderPlugin } from '@/source/plugins';
|
||||
|
||||
export interface LoadOptions {
|
||||
buildFile: (file: VirtualFile) => MetaFile | PageFile;
|
||||
plugins?: LoaderPlugin[];
|
||||
}
|
||||
|
||||
export type ContentStorage<
|
||||
Page extends PageData = PageData,
|
||||
Meta extends MetaData = MetaData,
|
||||
@@ -71,10 +66,10 @@ const parsers = {
|
||||
*/
|
||||
export function loadFiles(
|
||||
files: VirtualFile[],
|
||||
options: LoadOptions,
|
||||
buildFile: (file: VirtualFile) => MetaFile | PageFile,
|
||||
plugins: LoaderPlugin[],
|
||||
i18n: I18nConfig,
|
||||
): Record<string, ContentStorage> {
|
||||
const { buildFile, plugins = [] } = options;
|
||||
const parser = parsers[i18n.parser ?? 'dot'];
|
||||
const storages: Record<string, ContentStorage> = {};
|
||||
const normalized = files.map((file) =>
|
||||
|
||||
@@ -310,7 +310,7 @@ function resolveConfig({
|
||||
plugins: buildPlugins([
|
||||
slugsPlugin(slugs),
|
||||
icon && iconPlugin(icon),
|
||||
compatPlugin(base, base.pageTree),
|
||||
compatPlugin(base),
|
||||
...plugins,
|
||||
]),
|
||||
};
|
||||
@@ -323,10 +323,14 @@ function resolveConfig({
|
||||
return config;
|
||||
}
|
||||
|
||||
function createOutput(
|
||||
config: ResolvedLoaderConfig,
|
||||
): LoaderOutput<LoaderConfig> {
|
||||
const { source, baseUrl = '/', url: urlFn, i18n } = config;
|
||||
function createOutput({
|
||||
source,
|
||||
baseUrl = '/',
|
||||
url: urlFn,
|
||||
i18n,
|
||||
plugins = [],
|
||||
pageTree: pageTreeConfig,
|
||||
}: ResolvedLoaderConfig): LoaderOutput<LoaderConfig> {
|
||||
const getUrl: UrlFn = urlFn
|
||||
? (...args) => normalizeUrl(urlFn(...args))
|
||||
: createGetUrl(baseUrl, i18n);
|
||||
@@ -335,27 +339,25 @@ function createOutput(
|
||||
|
||||
const storages = loadFiles(
|
||||
files,
|
||||
{
|
||||
buildFile(file) {
|
||||
if (file.type === 'page') {
|
||||
return {
|
||||
format: 'page',
|
||||
path: file.path,
|
||||
slugs: file.slugs,
|
||||
data: file.data,
|
||||
absolutePath: file.absolutePath ?? '',
|
||||
} as PageFile;
|
||||
}
|
||||
|
||||
(file) => {
|
||||
if (file.type === 'page') {
|
||||
return {
|
||||
format: 'meta',
|
||||
format: 'page',
|
||||
path: file.path,
|
||||
absolutePath: file.absolutePath ?? '',
|
||||
slugs: file.slugs,
|
||||
data: file.data,
|
||||
} as MetaFile;
|
||||
},
|
||||
plugins: config.plugins,
|
||||
absolutePath: file.absolutePath ?? '',
|
||||
} as PageFile;
|
||||
}
|
||||
|
||||
return {
|
||||
format: 'meta',
|
||||
path: file.path,
|
||||
absolutePath: file.absolutePath ?? '',
|
||||
data: file.data,
|
||||
} as MetaFile;
|
||||
},
|
||||
plugins,
|
||||
i18n ?? {
|
||||
defaultLanguage,
|
||||
parser: 'none',
|
||||
@@ -372,8 +374,8 @@ function createOutput(
|
||||
get pageTree() {
|
||||
pageTree ??= builder.buildI18n({
|
||||
storages,
|
||||
plugins: config.plugins,
|
||||
...config.pageTree,
|
||||
plugins,
|
||||
...pageTreeConfig,
|
||||
});
|
||||
|
||||
return i18n
|
||||
|
||||
@@ -17,8 +17,8 @@ export interface LegacyLoaderOptions {
|
||||
}
|
||||
|
||||
export interface LegacyPageTreeOptions<
|
||||
Page extends PageData,
|
||||
Meta extends MetaData,
|
||||
Page extends PageData = PageData,
|
||||
Meta extends MetaData = MetaData,
|
||||
> {
|
||||
/**
|
||||
* @deprecated use `plugins` instead
|
||||
@@ -48,20 +48,18 @@ export interface LegacyPageTreeOptions<
|
||||
/**
|
||||
* legacy features compatibility layer
|
||||
*/
|
||||
export function compatPlugin<Page extends PageData, Meta extends MetaData>(
|
||||
loader: LegacyLoaderOptions,
|
||||
pageTreeOptions?: LegacyPageTreeOptions<Page, Meta>,
|
||||
): LoaderPlugin<Page, Meta> {
|
||||
export function compatPlugin({
|
||||
pageTree,
|
||||
transformers,
|
||||
}: LegacyLoaderOptions & { pageTree?: LegacyPageTreeOptions }): LoaderPlugin {
|
||||
return {
|
||||
name: 'fumadocs:compat',
|
||||
config(config) {
|
||||
const plugins = (
|
||||
config.plugins ? [...config.plugins] : []
|
||||
) as LoaderPlugin<Page, Meta>[];
|
||||
const plugins = config.plugins ? [...config.plugins] : [];
|
||||
|
||||
if (pageTreeOptions) {
|
||||
if (pageTree) {
|
||||
const { attachFile, attachSeparator, attachFolder, transformers } =
|
||||
pageTreeOptions;
|
||||
pageTree;
|
||||
|
||||
for (const transformer of transformers ?? []) {
|
||||
plugins.push(fromPageTreeTransformer(transformer));
|
||||
@@ -103,15 +101,15 @@ export function compatPlugin<Page extends PageData, Meta extends MetaData>(
|
||||
);
|
||||
}
|
||||
|
||||
if (loader.transformers) {
|
||||
for (const transformer of loader.transformers) {
|
||||
if (transformers) {
|
||||
for (const transformer of transformers) {
|
||||
plugins.push(fromStorageTransformer(transformer));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...config,
|
||||
plugins: plugins as LoaderPlugin[],
|
||||
plugins,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,15 +2,48 @@ import { MethodLabel } from '@/ui/components/method-label';
|
||||
import type * as PageTree from 'fumadocs-core/page-tree';
|
||||
import type {
|
||||
LoaderPlugin,
|
||||
PageData,
|
||||
PageFile,
|
||||
PageTreeTransformer,
|
||||
Source,
|
||||
SourceConfig,
|
||||
} from 'fumadocs-core/source';
|
||||
import type { OpenAPIServer } from '@/server/create';
|
||||
import {
|
||||
type OutputEntry,
|
||||
type SchemaToPagesOptions,
|
||||
serverToPages,
|
||||
} from '@/utils/schema-to-pages';
|
||||
|
||||
export type OpenAPIPluginOptions<Page extends PageData = PageData> =
|
||||
SchemaToPagesOptions & {
|
||||
from: OpenAPIServer;
|
||||
baseDir?: string;
|
||||
|
||||
onPage: (raw: OutputEntry) => Page;
|
||||
};
|
||||
|
||||
export function openapiPlugin(): LoaderPlugin;
|
||||
|
||||
export function openapiPlugin<S extends SourceConfig | Source<SourceConfig>>(
|
||||
options?: OpenAPIPluginOptions<
|
||||
S extends Source<infer Config>
|
||||
? Config['pageData']
|
||||
: S extends SourceConfig
|
||||
? S['pageData']
|
||||
: never
|
||||
>,
|
||||
): Promise<LoaderPlugin>;
|
||||
|
||||
/**
|
||||
* Fumadocs Source API integration, pass this to `plugins` array in `loader()`.
|
||||
*/
|
||||
export function openapiPlugin(): LoaderPlugin {
|
||||
return {
|
||||
export function openapiPlugin(
|
||||
autoPages?: OpenAPIPluginOptions,
|
||||
): LoaderPlugin | Promise<LoaderPlugin> {
|
||||
const basePlugin: LoaderPlugin = {
|
||||
name: 'fumadocs:openapi',
|
||||
enforce: 'pre',
|
||||
transformPageTree: {
|
||||
file(node, filePath) {
|
||||
if (!filePath) return node;
|
||||
@@ -43,6 +76,30 @@ export function openapiPlugin(): LoaderPlugin {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (autoPages) {
|
||||
const { from, onPage, baseDir = '' } = autoPages;
|
||||
|
||||
return serverToPages(from, autoPages).then((entries) => {
|
||||
const pages = Object.values(entries).flat();
|
||||
|
||||
basePlugin.transformStorage = ({ storage }) => {
|
||||
for (const page of pages) {
|
||||
storage.write(`${baseDir}/${page.path}`, {
|
||||
format: 'page',
|
||||
data: onPage(page),
|
||||
path: page.path,
|
||||
absolutePath: '',
|
||||
slugs: undefined as unknown as string[],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return basePlugin;
|
||||
});
|
||||
}
|
||||
|
||||
return basePlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user