Skip to content

Commit 3a2c837

Browse files
committed
TypeScript & DocGen: Improve cache
1 parent 0c35da7 commit 3a2c837

File tree

16 files changed

+128
-122
lines changed

16 files changed

+128
-122
lines changed

.changeset/happy-ducks-brake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'fumadocs-docgen': minor
3+
---
4+
5+
Improve caching

.changeset/long-seahorses-tap.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'fumadocs-typescript': minor
3+
---
4+
5+
Disable cache on program-level

packages/create-app/versions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"fumadocs-core":"13.4.0","fumadocs-ui":"13.4.0","fumadocs-mdx":"9.0.3","@fumadocs/content-collections":"1.1.3"}
1+
{"fumadocs-core":"13.4.1","fumadocs-ui":"13.4.1","fumadocs-mdx":"9.0.4","@fumadocs/content-collections":"1.1.3"}

packages/doc-gen/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"fumadocs-typescript": "workspace:^",
3232
"hast-util-to-estree": "^3.1.0",
3333
"npm-to-yarn": "^3.0.0",
34-
"unist-util-visit": "^5.0.0"
34+
"unist-util-visit": "^5.0.0",
35+
"zod": "^3.23.8"
3536
},
3637
"devDependencies": {
3738
"@mdx-js/mdx": "^3.0.1",
@@ -42,7 +43,9 @@
4243
"eslint-config-custom": "workspace:*",
4344
"remark": "^15.0.0",
4445
"tsconfig": "workspace:*",
45-
"unified": "^11.0.5"
46+
"typescript": "^5.5.4",
47+
"unified": "^11.0.5",
48+
"vfile": "^6.0.3"
4649
},
4750
"publishConfig": {
4851
"access": "public"

packages/doc-gen/src/file-generator.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fs from 'node:fs';
22
import path from 'node:path';
33
import type { Code, Paragraph } from 'mdast';
4+
import { z } from 'zod';
45
import type { DocGenerator } from './remark-docgen';
56

67
export interface FileGeneratorOptions {
@@ -15,21 +16,26 @@ export interface FileGeneratorOptions {
1516
relative?: boolean;
1617
}
1718

18-
export interface FileGeneratorInput {
19-
file: string;
19+
export type FileGeneratorInput = z.output<typeof fileGeneratorSchema>;
20+
21+
export const fileGeneratorSchema = z.object({
22+
file: z.string(),
2023

2124
/**
2225
* Turn file content into a code block
2326
*
2427
* @defaultValue false
2528
*/
26-
codeblock?: CodeBlock | boolean;
27-
}
28-
29-
interface CodeBlock {
30-
lang?: string;
31-
meta?: string;
32-
}
29+
codeblock: z
30+
.union([
31+
z.object({
32+
lang: z.string().optional(),
33+
meta: z.string().optional(),
34+
}),
35+
z.boolean(),
36+
])
37+
.default(false),
38+
});
3339

3440
export function fileGenerator({
3541
relative = false,
@@ -38,7 +44,7 @@ export function fileGenerator({
3844
return {
3945
name: 'file',
4046
run(input, ctx) {
41-
const { file, codeblock = false } = input as FileGeneratorInput;
47+
const { file, codeblock = false } = fileGeneratorSchema.parse(input);
4248

4349
const dest = relative
4450
? path.resolve(ctx.cwd, path.dirname(ctx.path), file)

packages/doc-gen/src/remark-docgen.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type BlockContent, type Code, type Root } from 'mdast';
22
import { type Transformer } from 'unified';
33
import { visit } from 'unist-util-visit';
4+
import type { VFile } from 'vfile';
45

56
export interface DocGenerator {
67
name: string;
@@ -9,6 +10,8 @@ export interface DocGenerator {
910
* Transform codeblocks to another mdast element
1011
*/
1112
run: (input: unknown, context: Context) => object | undefined;
13+
14+
onFile?: (tree: Root, file: VFile) => void;
1215
}
1316

1417
interface Context {
@@ -27,6 +30,8 @@ export function remarkDocGen({
2730
generators = [],
2831
}: RemarkDocGenOptions): Transformer<Root, Root> {
2932
return (tree, file) => {
33+
generators.forEach((gen) => gen.onFile?.(tree, file));
34+
3035
visit(tree, 'code', (code, index, parent) => {
3136
if (code.lang !== 'json' || !code.meta) return;
3237

packages/doc-gen/src/typescript-generator.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,31 @@ import {
88
renderMarkdownToHast,
99
type DocEntry,
1010
} from 'fumadocs-typescript';
11+
import { getProgram } from 'fumadocs-typescript';
12+
import type { Program } from 'typescript';
13+
import { z } from 'zod';
1114
import { createElement, expressionToAttribute } from './utils';
1215
import type { DocGenerator } from './remark-docgen';
1316

1417
export type TypescriptGeneratorOptions = GenerateDocumentationOptions;
1518

16-
export interface TypescriptGeneratorInput {
17-
file: string;
18-
name: string;
19+
export type TypescriptGeneratorInput = z.output<
20+
typeof typescriptGeneratorSchema
21+
>;
22+
23+
export const typescriptGeneratorSchema = z.object({
24+
file: z.string({ description: 'Target TypeScript file name' }),
25+
name: z.string({ description: 'Exported type name' }),
1926

2027
/**
2128
* Component name which accepts the `type` property
2229
*
2330
* @defaultValue 'TypeTable'
2431
*/
25-
component?: string;
26-
}
32+
component: z
33+
.string({ description: 'Component name which accepts the `type` property' })
34+
.default('TypeTable'),
35+
});
2736

2837
export interface VirtualTypeTableProps {
2938
type: Record<
@@ -47,8 +56,16 @@ export interface VirtualTypeTableProps {
4756
* @param options - configuration
4857
*/
4958
export function typescriptGenerator(
50-
options?: TypescriptGeneratorOptions,
59+
options: TypescriptGeneratorOptions = {},
5160
): DocGenerator {
61+
let program: Program | undefined;
62+
63+
function loadProgram(): Program {
64+
return options.config && 'program' in options.config
65+
? options.config.program
66+
: getProgram(options.config);
67+
}
68+
5269
function mapProperty(entry: DocEntry): Property {
5370
const value = valueToEstree({
5471
type: entry.type,
@@ -91,15 +108,23 @@ export function typescriptGenerator(
91108

92109
return {
93110
name: 'typescript',
111+
onFile() {
112+
if (process.env.NODE_ENV === 'development' || !program) {
113+
program = loadProgram();
114+
}
115+
},
94116
run(input, ctx) {
95117
const {
96118
file,
97119
name,
98120
component = 'TypeTable',
99-
} = input as TypescriptGeneratorInput;
121+
} = typescriptGeneratorSchema.parse(input);
100122
const dest = path.resolve(ctx.cwd, file);
101123

102-
const doc = generateDocumentation(dest, name, options);
124+
const doc = generateDocumentation(dest, name, {
125+
...options,
126+
config: { program: program ?? loadProgram() },
127+
});
103128
if (!doc) throw new Error(`Failed to find type ${name} in ${dest}`);
104129

105130
return createElement(component, [

packages/mdx/src/map/generate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export async function generateJS(
3737
if (getTypeFromPath(file) !== collection.type) continue;
3838
if (config._runtime.files.has(file)) {
3939
console.warn(
40-
`[MDX] Files cannot exist in multiple collections: ${file}`,
40+
`[MDX] Files cannot exist in multiple collections: ${file} (${config._runtime.files.get(file) ?? ''})`,
4141
);
4242
continue;
4343
}

packages/openapi/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,8 @@
5353
"dependencies": {
5454
"@apidevtools/json-schema-ref-parser": "^11.7.0",
5555
"@fumari/json-schema-to-typescript": "^1.1.0",
56-
"@mdx-js/mdx": "^3.0.1",
5756
"@radix-ui/react-select": "^2.1.1",
5857
"@radix-ui/react-slot": "^1.1.0",
59-
"@types/mdx": "^2.0.13",
6058
"class-variance-authority": "^0.7.0",
6159
"fast-glob": "^3.3.1",
6260
"fumadocs-core": "workspace:*",
@@ -67,7 +65,8 @@
6765
"lucide-react": "^0.437.0",
6866
"openapi-sampler": "^1.5.1",
6967
"react-hook-form": "^7.53.0",
70-
"remark": "^15.0.0",
68+
"remark": "^15.0.1",
69+
"remark-rehype": "^11.1.0",
7170
"shiki": "^1.15.2",
7271
"swr": "^2.2.5"
7372
},
Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
1-
import { createElement, type ReactElement } from 'react';
2-
import { createProcessor } from '@mdx-js/mdx';
3-
import { rehypeCode, remarkGfm } from 'fumadocs-core/mdx-plugins';
1+
import { type ReactElement } from 'react';
2+
import { rehypeCode, remarkGfm, remarkImage } from 'fumadocs-core/mdx-plugins';
43
import defaultMdxComponents from 'fumadocs-ui/mdx';
4+
import { remark } from 'remark';
5+
import remarkRehype from 'remark-rehype';
6+
import { toJsxRuntime, type Jsx } from 'hast-util-to-jsx-runtime';
7+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
58

6-
const processor = createProcessor({
7-
remarkPlugins: [remarkGfm],
8-
rehypePlugins: [rehypeCode],
9-
outputFormat: 'function-body',
10-
development: process.env.NODE_ENV === 'development',
11-
});
9+
const processor = remark()
10+
.use(remarkGfm)
11+
.use(remarkImage, { useImport: false })
12+
.use(remarkRehype)
13+
.use(rehypeCode);
1214

1315
export async function Markdown({
1416
text,
1517
}: {
1618
text: string;
1719
}): Promise<ReactElement> {
18-
const result = await processor.process({ value: text });
19-
const jsxRuntime =
20-
process.env.NODE_ENV === 'development'
21-
? await import('react/jsx-dev-runtime')
22-
: await import('react/jsx-runtime');
20+
const nodes = processor.parse({ value: text });
21+
const hast = await processor.run(nodes);
2322

24-
const fullScope = {
25-
opts: jsxRuntime,
26-
};
27-
const keys = Object.keys(fullScope);
28-
const values = Object.values(fullScope);
29-
const hydrateFn = Reflect.construct(
30-
Function,
31-
keys.concat(String(result.value)),
32-
);
33-
34-
const rendered = hydrateFn.apply(hydrateFn, values) as {
35-
default: React.ElementType;
36-
};
37-
38-
return createElement(rendered.default, {
23+
return toJsxRuntime(hast, {
24+
development: false,
25+
jsx: jsx as Jsx,
26+
jsxs: jsxs as Jsx,
27+
Fragment,
28+
// @ts-expect-error -- safe to use
3929
components: defaultMdxComponents,
4030
});
4131
}

0 commit comments

Comments
 (0)