MDX: Improve types

This commit is contained in:
Fuma Nama
2025-02-08 17:32:27 +08:00
parent c57141789e
commit be3acf448c
7 changed files with 188 additions and 130 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-mdx': patch
---
Improve types
+3 -1
View File
@@ -1,3 +1,5 @@
import { defineDocs } from 'fumadocs-mdx/config';
export const { docs, meta } = defineDocs();
export const { docs, meta } = defineDocs({
dir: 'content/docs',
});
+3 -1
View File
@@ -1,3 +1,5 @@
import { defineDocs } from 'fumadocs-mdx/config';
export const { docs, meta } = defineDocs();
export const { docs, meta } = defineDocs({
dir: 'content/docs',
});
+37 -12
View File
@@ -50,10 +50,16 @@ export interface DocCollection<
async?: Async;
}
export interface DocsCollection {
export interface DocsCollection<
DocSchema extends StandardSchemaV1 = StandardSchemaV1,
MetaSchema extends StandardSchemaV1 = StandardSchemaV1,
Async extends boolean = boolean,
> {
type: 'docs';
docs: DocCollection;
meta: MetaCollection;
dir: string | string[];
docs: DocCollection<DocSchema, Async>;
meta: MetaCollection<MetaSchema>;
}
export function defineCollections<
@@ -80,9 +86,9 @@ export function defineCollections<
}
export function defineDocs<
DocData extends StandardSchemaV1 = typeof frontmatterSchema,
MetaData extends StandardSchemaV1 = typeof metaSchema,
DocAsync extends boolean = false,
DocSchema extends StandardSchemaV1 = typeof frontmatterSchema,
MetaSchema extends StandardSchemaV1 = typeof metaSchema,
Async extends boolean = false,
>(options?: {
/**
* The directory to scan files
@@ -91,27 +97,46 @@ export function defineDocs<
*/
dir?: string | string[];
docs?: Omit<DocCollection<DocData, DocAsync>, 'dir' | 'type'>;
meta?: Omit<MetaCollection<MetaData>, 'dir' | 'type'>;
docs?: Omit<DocCollection<DocSchema, Async>, 'dir' | 'type'>;
meta?: Omit<MetaCollection<MetaSchema>, 'dir' | 'type'>;
}): {
type: 'docs';
docs: ReturnType<typeof defineCollections<'doc', DocData, DocAsync>>;
meta: ReturnType<typeof defineCollections<'meta', MetaData, false>>;
docs: {
type: 'doc';
_type: {
schema: DocSchema;
async: Async;
};
};
meta: {
type: 'meta';
_type: {
schema: MetaSchema;
};
};
} {
if (!options)
console.warn(
'[`source.config.ts`] Deprecated: please pass options to `defineDocs()` and specify a `dir`.',
);
const dir = options?.dir ?? 'content/docs';
return {
type: 'docs',
// @ts-expect-error -- internal type inferring
docs: defineCollections({
type: 'doc',
dir,
schema: frontmatterSchema as unknown as DocData,
schema: frontmatterSchema,
...options?.docs,
}),
// @ts-expect-error -- internal type inferring
meta: defineCollections({
type: 'meta',
dir,
schema: metaSchema as unknown as MetaData,
schema: metaSchema,
...options?.meta,
}),
};
+8 -53
View File
@@ -1,68 +1,23 @@
import { type BaseCollectionEntry, type MarkdownProps } from '@/config/types';
import { createCompiler, type MDXOptions } from '@fumadocs/mdx-remote';
import * as fs from 'node:fs/promises';
import type { LoadedConfig } from '@/utils/config';
import { remarkInclude } from '@/mdx-plugins/remark-include';
import type { defineCollections } from '@/config/define';
import {
remarkStructure,
type StructuredData,
} from 'fumadocs-core/mdx-plugins';
import type { StandardSchemaV1 } from '@standard-schema/spec';
import { _runtime, createMDXSource, type RuntimeFile } from '@/runtime/index';
import type { MetaData, PageData, Source } from 'fumadocs-core/source';
export interface RuntimeAsync {
doc: <C>(
files: RuntimeFile[],
collection: string,
config: LoadedConfig,
) => C extends ReturnType<
typeof defineCollections<'doc', infer Schema extends StandardSchemaV1, true>
>
? (StandardSchemaV1.InferOutput<Schema> &
BaseCollectionEntry & {
load: () => Promise<MarkdownProps>;
})[]
: never;
docs: <Docs>(
docs: RuntimeFile[],
metas: RuntimeFile[],
collection: string,
config: LoadedConfig,
) => Docs extends {
type: 'docs';
docs: unknown;
meta: unknown;
}
? {
docs: ReturnType<typeof _runtimeAsync.doc<Docs['docs']>>;
meta: ReturnType<typeof _runtime.meta<Docs['meta']>>;
toFumadocsSource: () => Source<{
pageData: ReturnType<
typeof _runtimeAsync.doc<Docs['docs']>
>[number] extends PageData & BaseCollectionEntry
? ReturnType<typeof _runtimeAsync.doc<Docs['docs']>>[number]
: never;
metaData: ReturnType<
typeof _runtime.meta<Docs['meta']>
>[number] extends MetaData & BaseCollectionEntry
? ReturnType<typeof _runtime.meta<Docs['meta']>>[number]
: never;
}>;
}
: never;
}
import { createMDXSource, _runtime } from '@/runtime/index';
import type { RuntimeAsync } from '@/runtime/types';
async function initCompiler(config: LoadedConfig, collection: string) {
let col = config.collections.get(collection);
if (col?.type === 'docs') col = col.docs;
let mdxOptions: MDXOptions | undefined;
let mdxOptions: MDXOptions;
const col = config.collections.get(collection);
if (col?.type === 'doc') mdxOptions = col.mdxOptions as MDXOptions;
else if (col?.type === 'docs')
mdxOptions = col.docs?.mdxOptions as MDXOptions;
if (col?.type === 'doc' && col.mdxOptions) {
mdxOptions = col.mdxOptions as MDXOptions;
} else {
if (!mdxOptions) {
const options =
typeof config.global?.mdxOptions === 'function'
? await config.global.mdxOptions()
+3 -63
View File
@@ -3,71 +3,11 @@ import {
type PageData,
type Source,
} from 'fumadocs-core/source';
import {
type BaseCollectionEntry,
type defineCollections,
type FileInfo,
type MarkdownProps,
} from '@/config';
import { type BaseCollectionEntry } from '@/config';
import type { VirtualFile } from 'fumadocs-core/source';
import type { StandardSchemaV1 } from '@standard-schema/spec';
import type { Runtime } from '@/runtime/types';
export interface RuntimeFile {
info: FileInfo;
data: Record<string, unknown>;
}
export interface Runtime {
doc: <C>(
files: RuntimeFile[],
) => C extends ReturnType<
typeof defineCollections<
'doc',
infer Schema extends StandardSchemaV1,
false
>
>
? (Omit<MarkdownProps, keyof StandardSchemaV1.InferOutput<Schema>> &
StandardSchemaV1.InferOutput<Schema> &
BaseCollectionEntry)[]
: never;
meta: <C>(
files: RuntimeFile[],
) => C extends ReturnType<
typeof defineCollections<
'meta',
infer Schema extends StandardSchemaV1,
false
>
>
? (StandardSchemaV1.InferOutput<Schema> & BaseCollectionEntry)[]
: never;
docs: <Docs>(
docs: RuntimeFile[],
metas: RuntimeFile[],
) => Docs extends {
type: 'docs';
docs: unknown;
meta: unknown;
}
? {
docs: ReturnType<typeof _runtime.doc<Docs['docs']>>;
meta: ReturnType<typeof _runtime.meta<Docs['meta']>>;
toFumadocsSource: () => Source<{
pageData: ReturnType<
typeof _runtime.doc<Docs['docs']>
>[number] extends PageData & BaseCollectionEntry
? ReturnType<typeof _runtime.doc<Docs['docs']>>[number]
: never;
metaData: ReturnType<
typeof _runtime.meta<Docs['meta']>
>[number] extends MetaData & BaseCollectionEntry
? ReturnType<typeof _runtime.meta<Docs['meta']>>[number]
: never;
}>;
}
: never;
}
export type { RuntimeFile } from '@/runtime/types';
export const _runtime: Runtime = {
doc(files) {
+129
View File
@@ -0,0 +1,129 @@
import type { BaseCollectionEntry, FileInfo, MarkdownProps } from '@/config';
import type { StandardSchemaV1 } from '@standard-schema/spec';
import type { MetaData, PageData, Source } from 'fumadocs-core/source';
import type { LoadedConfig } from '@/utils/config';
export interface RuntimeFile {
info: FileInfo;
data: Record<string, unknown>;
}
type DocOut<Schema extends StandardSchemaV1> = Omit<
MarkdownProps,
keyof StandardSchemaV1.InferOutput<Schema>
> &
StandardSchemaV1.InferOutput<Schema> &
BaseCollectionEntry;
type MetaOut<Schema extends StandardSchemaV1> =
StandardSchemaV1.InferOutput<Schema> & BaseCollectionEntry;
export interface Runtime {
doc: <C>(files: RuntimeFile[]) => C extends {
type: 'doc';
_type: {
schema: infer Schema extends StandardSchemaV1;
};
}
? DocOut<Schema>[]
: never;
meta: <C>(files: RuntimeFile[]) => C extends {
type: 'meta';
_type: {
schema: infer Schema extends StandardSchemaV1;
};
}
? MetaOut<Schema>[]
: never;
docs: <Docs>(
docs: RuntimeFile[],
metas: RuntimeFile[],
) => Docs extends {
type: 'docs';
docs: {
type: 'doc';
_type: {
schema: infer DocSchema extends StandardSchemaV1;
};
};
meta: {
type: 'meta';
_type: {
schema: infer MetaSchema extends StandardSchemaV1;
};
};
}
? {
docs: DocOut<DocSchema>[];
meta: MetaOut<MetaSchema>[];
toFumadocsSource: () => Source<{
pageData: DocOut<DocSchema> extends PageData
? DocOut<DocSchema>
: never;
metaData: MetaOut<MetaSchema> extends MetaData
? MetaOut<MetaSchema>
: never;
}>;
}
: never;
}
type AsyncDocOut<Schema extends StandardSchemaV1> =
StandardSchemaV1.InferOutput<Schema> &
BaseCollectionEntry & {
load: () => Promise<MarkdownProps>;
};
export interface RuntimeAsync {
doc: <C>(
files: RuntimeFile[],
collection: string,
config: LoadedConfig,
) => C extends {
type: 'doc';
_type: {
schema: infer Schema extends StandardSchemaV1;
};
}
? AsyncDocOut<Schema>[]
: never;
docs: <Docs>(
docs: RuntimeFile[],
metas: RuntimeFile[],
collection: string,
config: LoadedConfig,
) => Docs extends {
type: 'docs';
docs: {
type: 'doc';
_type: {
schema: infer DocSchema extends StandardSchemaV1;
};
};
meta: {
type: 'meta';
_type: {
schema: infer MetaSchema extends StandardSchemaV1;
};
};
}
? {
docs: AsyncDocOut<DocSchema>[];
meta: MetaOut<MetaSchema>[];
toFumadocsSource: () => Source<{
pageData: AsyncDocOut<DocSchema> extends PageData
? AsyncDocOut<DocSchema>
: never;
metaData: MetaOut<MetaSchema> extends MetaData
? MetaOut<MetaSchema>
: never;
}>;
}
: never;
}