mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 10:55:57 +00:00
TypeScript: require explicit cache
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'fumadocs-typescript': major
|
||||
---
|
||||
|
||||
**Require async for `generator.generateDocumentation()`**
|
||||
|
||||
This is necessary to support async cache adapter.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'fumadocs-typescript': major
|
||||
---
|
||||
|
||||
**Remove deprecated APIs**
|
||||
|
||||
- removed standalone `generateDocumentation()` function, create a generator instead.
|
||||
- removed `generateFiles` & MDX generation APIs, use `remarkAutoTypeTable` instead.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
'fumadocs-typescript': major
|
||||
---
|
||||
|
||||
**Require explicit cache**
|
||||
|
||||
Previously, we enabled file system cache by default, but the directory is not customisable and only support Next.js.
|
||||
|
||||
Now, cache is disabled by default and require explicit declaration.
|
||||
|
||||
Update all your `createGenerator()` calls:
|
||||
|
||||
```ts
|
||||
import {
|
||||
createGenerator,
|
||||
createFileSystemGeneratorCache,
|
||||
} from 'fumadocs-typescript';
|
||||
|
||||
const generator = createGenerator({
|
||||
// add this!
|
||||
cache: createFileSystemGeneratorCache('.next/fumadocs-typescript'),
|
||||
});
|
||||
```
|
||||
@@ -4,8 +4,6 @@ pnpm-lock.yaml
|
||||
|
||||
packages/create-app/versions.json
|
||||
|
||||
apps/docs/shaders/*
|
||||
|
||||
**/test/out/**/*
|
||||
**/test/fixtures/**/*.output.*
|
||||
**/test/fixtures/**/*.out.*
|
||||
@@ -22,8 +20,6 @@ examples/openapi/content/docs/(api)/**/*.mdx
|
||||
examples/obsidian/content/docs/vault/**/*
|
||||
examples/obsidian/obsidian/Obsidian Vault
|
||||
|
||||
apps/docs/content/docs/ui/typescript.mdx
|
||||
|
||||
.content-collections/**/*
|
||||
|
||||
packages/mdx-remote/test/fixtures/**/*.js
|
||||
|
||||
@@ -53,10 +53,16 @@ Initialize the TypeScript compiler and add it as a MDX component.
|
||||
```tsx title="mdx-components.tsx"
|
||||
import defaultComponents from 'fumadocs-ui/mdx';
|
||||
import type { MDXComponents } from 'mdx/types';
|
||||
import { createGenerator } from 'fumadocs-typescript';
|
||||
import {
|
||||
createGenerator,
|
||||
createFileSystemGeneratorCache,
|
||||
} from 'fumadocs-typescript';
|
||||
import { AutoTypeTable } from 'fumadocs-typescript/ui';
|
||||
|
||||
const generator = createGenerator();
|
||||
const generator = createGenerator({
|
||||
// set a cache, necessary for serverless platform like Vercel
|
||||
cache: createFileSystemGeneratorCache('.next/fumadocs-typescript'),
|
||||
});
|
||||
|
||||
export function getMDXComponents(components?: MDXComponents): MDXComponents {
|
||||
return {
|
||||
@@ -139,14 +145,16 @@ Your `tsconfig.json` file in the current working directory will be loaded.
|
||||
You can change the compiler settings from [`createGenerator()`](/docs/ui/typescript).
|
||||
|
||||
```ts
|
||||
import { createGenerator } from 'fumadocs-typescript';
|
||||
import {
|
||||
createGenerator,
|
||||
createFileSystemGeneratorCache,
|
||||
} from 'fumadocs-typescript';
|
||||
|
||||
const generator = createGenerator({
|
||||
tsconfigPath: './tsconfig.json',
|
||||
// where to resolve relative paths (normally cwd)
|
||||
basePath: './',
|
||||
// disable file system cache
|
||||
cache: false,
|
||||
// other options...
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -18,10 +18,17 @@ It comes with the `AutoTypeTable` component. Learn more about [Auto Type Table](
|
||||
You can use it as a remark plugin:
|
||||
|
||||
```ts title="source.config.ts" tab="Fumadocs MDX"
|
||||
import { remarkAutoTypeTable, createGenerator } from 'fumadocs-typescript';
|
||||
import {
|
||||
remarkAutoTypeTable,
|
||||
createGenerator,
|
||||
createFileSystemGeneratorCache,
|
||||
} from 'fumadocs-typescript';
|
||||
import { defineConfig } from 'fumadocs-mdx/config';
|
||||
|
||||
const generator = createGenerator();
|
||||
const generator = createGenerator({
|
||||
// recommended: choose a directory for cache
|
||||
cache: createFileSystemGeneratorCache('.next/fumadocs-typescript'),
|
||||
});
|
||||
|
||||
export default defineConfig({
|
||||
mdxOptions: {
|
||||
@@ -31,10 +38,17 @@ export default defineConfig({
|
||||
```
|
||||
|
||||
```ts tab="MDX Compiler"
|
||||
import { remarkAutoTypeTable, createGenerator } from 'fumadocs-typescript';
|
||||
import {
|
||||
remarkAutoTypeTable,
|
||||
createGenerator,
|
||||
createFileSystemGeneratorCache,
|
||||
} from 'fumadocs-typescript';
|
||||
import { compile } from '@mdx-js/mdx';
|
||||
|
||||
const generator = createGenerator();
|
||||
const generator = createGenerator({
|
||||
// recommended: choose a directory for cache
|
||||
cache: createFileSystemGeneratorCache('.next/fumadocs-typescript'),
|
||||
});
|
||||
|
||||
await compile('...', {
|
||||
remarkPlugins: [[remarkAutoTypeTable, { generator }]],
|
||||
@@ -100,4 +114,4 @@ interface MyInterface {
|
||||
*/
|
||||
client: MyClient;
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
@@ -85,8 +85,15 @@ export default defineConfig({
|
||||
const { remarkTypeScriptToJavaScript } =
|
||||
await import('fumadocs-docgen/remark-ts2js');
|
||||
const { default: rehypeKatex } = await import('rehype-katex');
|
||||
const { remarkAutoTypeTable } = await import('fumadocs-typescript');
|
||||
const {
|
||||
remarkAutoTypeTable,
|
||||
createGenerator,
|
||||
createFileSystemGeneratorCache,
|
||||
} = await import('fumadocs-typescript');
|
||||
|
||||
const generator = createGenerator({
|
||||
cache: createFileSystemGeneratorCache('.next/fumadocs-typescript'),
|
||||
});
|
||||
return {
|
||||
remarkStructureOptions: {
|
||||
types: [...remarkStructureDefaultOptions.types, 'code'],
|
||||
@@ -117,7 +124,12 @@ export default defineConfig({
|
||||
remarkPlugins: [
|
||||
remarkSteps,
|
||||
remarkMath,
|
||||
remarkAutoTypeTable,
|
||||
[
|
||||
remarkAutoTypeTable,
|
||||
{
|
||||
generator,
|
||||
},
|
||||
],
|
||||
remarkTypeScriptToJavaScript,
|
||||
],
|
||||
rehypePlugins: (v) => [rehypeKatex, ...v],
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import { createHash } from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import type { Cache } from '@/cache/index';
|
||||
|
||||
export function createFileSystemGeneratorCache(dir: string): Cache {
|
||||
// call `path.resolve` so Vercel NFT will include the cache directory in production.
|
||||
dir = path.resolve(dir);
|
||||
const initDirPromise = fs.mkdir(dir, { recursive: true }).catch(() => {
|
||||
// it fails on Vercel as of 2025 12 May, we can skip it
|
||||
});
|
||||
|
||||
return {
|
||||
async write(input, data) {
|
||||
const hash = createHash('SHA256')
|
||||
.update(input)
|
||||
.digest('hex')
|
||||
.slice(0, 12);
|
||||
|
||||
await initDirPromise;
|
||||
await fs.writeFile(path.join(dir, `${hash}.json`), JSON.stringify(data));
|
||||
},
|
||||
async read(input) {
|
||||
const hash = createHash('SHA256')
|
||||
.update(input)
|
||||
.digest('hex')
|
||||
.slice(0, 12);
|
||||
|
||||
try {
|
||||
return JSON.parse(
|
||||
(await fs.readFile(path.join(dir, `${hash}.json`))).toString(),
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export interface Cache {
|
||||
read: (key: string) => unknown | undefined | Promise<unknown | undefined>;
|
||||
write: (key: string, value: unknown) => void | Promise<void>;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
export * from '@/lib/base';
|
||||
export * from '@/lib/mdx';
|
||||
export * from '@/lib/file';
|
||||
export { renderMarkdownToHast } from './markdown';
|
||||
export { createProject } from './create-project';
|
||||
export * from '@/lib/remark-auto-type-table';
|
||||
export * from './cache/fs-cache';
|
||||
export * from './cache';
|
||||
|
||||
@@ -6,15 +6,15 @@ import {
|
||||
type Type,
|
||||
} from 'ts-morph';
|
||||
import { createProject, type TypescriptConfig } from '@/create-project';
|
||||
import fs from 'node:fs';
|
||||
import fs from 'node:fs/promises';
|
||||
import {
|
||||
type BaseTypeTableProps,
|
||||
type GenerateTypeTableOptions,
|
||||
getTypeTableOutput,
|
||||
} from '@/lib/type-table';
|
||||
import { createCache } from '@/lib/cache';
|
||||
import path from 'node:path';
|
||||
import { getSimpleForm } from '@/lib/get-simple-form';
|
||||
import type { Cache } from '@/cache';
|
||||
|
||||
export interface GeneratedDoc {
|
||||
name: string;
|
||||
@@ -72,9 +72,9 @@ export interface GeneratorOptions extends TypescriptConfig {
|
||||
/**
|
||||
* cache results, note that some options are not marked as dependency.
|
||||
*
|
||||
* @defaultValue fs
|
||||
* @defaultValue false
|
||||
*/
|
||||
cache?: 'fs' | false;
|
||||
cache?: Cache | false;
|
||||
|
||||
project?: Project;
|
||||
}
|
||||
@@ -86,9 +86,7 @@ export function createGenerator(config?: GeneratorOptions | Project) {
|
||||
project: config,
|
||||
}
|
||||
: config;
|
||||
const cacheType =
|
||||
options?.cache ?? (process.env.NODE_ENV !== 'development' ? 'fs' : false);
|
||||
const cache = cacheType === 'fs' ? createCache() : null;
|
||||
const cache = options?.cache ? options.cache : null;
|
||||
let instance: Project | undefined;
|
||||
|
||||
function getProject() {
|
||||
@@ -97,7 +95,7 @@ export function createGenerator(config?: GeneratorOptions | Project) {
|
||||
}
|
||||
|
||||
return {
|
||||
generateDocumentation(
|
||||
async generateDocumentation(
|
||||
file: {
|
||||
path: string;
|
||||
content?: string;
|
||||
@@ -106,10 +104,12 @@ export function createGenerator(config?: GeneratorOptions | Project) {
|
||||
options?: GenerateOptions,
|
||||
) {
|
||||
const content =
|
||||
file.content ?? fs.readFileSync(path.resolve(file.path)).toString();
|
||||
file.content ?? (await fs.readFile(path.resolve(file.path))).toString();
|
||||
const cacheKey = `${file.path}:${name}:${content}`;
|
||||
if (cache) {
|
||||
const cached = cache.read(cacheKey) as GeneratedDoc[] | undefined;
|
||||
const cached = (await cache.read(cacheKey)) as
|
||||
| GeneratedDoc[]
|
||||
| undefined;
|
||||
if (cached) return cached;
|
||||
}
|
||||
const sourceFile = getProject().createSourceFile(file.path, content, {
|
||||
@@ -128,7 +128,7 @@ export function createGenerator(config?: GeneratorOptions | Project) {
|
||||
out.push(generate(getProject(), k, d[0], options));
|
||||
}
|
||||
|
||||
cache?.write(cacheKey, out);
|
||||
void cache?.write(cacheKey, out);
|
||||
return out;
|
||||
},
|
||||
generateTypeTable(
|
||||
@@ -140,28 +140,6 @@ export function createGenerator(config?: GeneratorOptions | Project) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate documentation for properties in an exported type/interface
|
||||
*
|
||||
* @deprecated use `createGenerator` instead
|
||||
*/
|
||||
export function generateDocumentation(
|
||||
file: string,
|
||||
name: string | undefined,
|
||||
content: string,
|
||||
options: GenerateOptions & {
|
||||
/**
|
||||
* Typescript configurations
|
||||
*/
|
||||
config?: TypescriptConfig;
|
||||
project?: Project;
|
||||
} = {},
|
||||
): GeneratedDoc[] {
|
||||
const gen = createGenerator(options.project ?? options.config);
|
||||
|
||||
return gen.generateDocumentation({ path: file, content }, name, options);
|
||||
}
|
||||
|
||||
function generate(
|
||||
program: Project,
|
||||
name: string,
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import fs from 'node:fs';
|
||||
import { createHash } from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
|
||||
export function createCache() {
|
||||
const dir = path.join(process.cwd(), '.next/fumadocs-typescript');
|
||||
try {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
} catch {
|
||||
// it fails on Vercel as of 12 May, we can skip it
|
||||
}
|
||||
|
||||
return {
|
||||
write(input: string, data: unknown) {
|
||||
const hash = createHash('SHA256')
|
||||
.update(input)
|
||||
.digest('hex')
|
||||
.slice(0, 12);
|
||||
|
||||
fs.writeFileSync(path.join(dir, `${hash}.json`), JSON.stringify(data));
|
||||
},
|
||||
read(input: string): unknown | undefined {
|
||||
const hash = createHash('SHA256')
|
||||
.update(input)
|
||||
.digest('hex')
|
||||
.slice(0, 12);
|
||||
|
||||
try {
|
||||
return JSON.parse(
|
||||
fs.readFileSync(path.join(dir, `${hash}.json`)).toString(),
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import * as path from 'node:path';
|
||||
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
||||
import { glob, type GlobOptions } from 'tinyglobby';
|
||||
import { generateMDX, type GenerateMDXOptions } from './mdx';
|
||||
import type { Generator } from '@/lib/base';
|
||||
|
||||
export interface GenerateFilesOptions {
|
||||
input: string | string[];
|
||||
/**
|
||||
* Output directory, or a function that returns the output path
|
||||
*/
|
||||
output: string | ((inputPath: string) => string);
|
||||
globOptions?: GlobOptions;
|
||||
options?: GenerateMDXOptions;
|
||||
|
||||
/**
|
||||
* @returns New content
|
||||
*/
|
||||
transformOutput?: (path: string, content: string) => string;
|
||||
}
|
||||
|
||||
export async function generateFiles(
|
||||
generator: Generator,
|
||||
options: GenerateFilesOptions,
|
||||
): Promise<void> {
|
||||
const files = await glob(options.input, options.globOptions);
|
||||
|
||||
const produce = files.map(async (file) => {
|
||||
const absolutePath = path.resolve(file);
|
||||
const outputPath =
|
||||
typeof options.output === 'function'
|
||||
? options.output(file)
|
||||
: path.resolve(
|
||||
options.output,
|
||||
`${path.basename(file, path.extname(file))}.mdx`,
|
||||
);
|
||||
|
||||
const content = (await readFile(absolutePath)).toString();
|
||||
let result = generateMDX(generator, content, {
|
||||
basePath: path.dirname(absolutePath),
|
||||
...options.options,
|
||||
});
|
||||
|
||||
if (options.transformOutput) {
|
||||
result = options.transformOutput(outputPath, result);
|
||||
}
|
||||
|
||||
await write(outputPath, result);
|
||||
console.log(`Generated: ${outputPath}`);
|
||||
});
|
||||
|
||||
await Promise.all(produce);
|
||||
}
|
||||
|
||||
async function write(file: string, content: string): Promise<void> {
|
||||
await mkdir(path.dirname(file), { recursive: true });
|
||||
await writeFile(file, content);
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import * as path from 'node:path';
|
||||
import {
|
||||
type DocEntry,
|
||||
type GeneratedDoc,
|
||||
type GenerateOptions,
|
||||
type Generator,
|
||||
} from './base';
|
||||
|
||||
interface Templates {
|
||||
block: (doc: GeneratedDoc, children: string) => string;
|
||||
property: (entry: DocEntry) => string;
|
||||
}
|
||||
|
||||
export interface GenerateMDXOptions extends GenerateOptions {
|
||||
/**
|
||||
* a root directory to resolve relative file paths
|
||||
*/
|
||||
basePath?: string;
|
||||
templates?: Partial<Templates>;
|
||||
}
|
||||
|
||||
// \r?\n is required for cross-platform compatibility
|
||||
const regex =
|
||||
/^---type-table---\r?\n(?<file>.+?)(?:#(?<name>.+))?\r?\n---end---$/gm;
|
||||
|
||||
const defaultTemplates: Templates = {
|
||||
block: (doc, c) => `### ${doc.name}
|
||||
|
||||
${doc.description}
|
||||
|
||||
<div className='*:border-b [&>*:last-child]:border-b-0'>${c}</div>`,
|
||||
|
||||
property: (c) => `<div className='text-sm text-fd-muted-foreground py-4'>
|
||||
|
||||
<div className="flex flex-row items-center gap-4">
|
||||
<code className="text-sm">${c.name}</code>
|
||||
<code className="text-fd-muted-foreground">{${JSON.stringify(c.simplifiedType)}}</code>
|
||||
</div>
|
||||
|
||||
Full Type: <code className="text-fd-muted-foreground">{${JSON.stringify(c.type)}}</code>
|
||||
|
||||
${c.description || 'No Description'}
|
||||
|
||||
${c.tags
|
||||
.map((tag) => `- ${tag.name}:\n${replaceJsDocLinks(tag.text)}`)
|
||||
.join('\n')}
|
||||
|
||||
</div>`,
|
||||
};
|
||||
|
||||
export function generateMDX(
|
||||
generator: Generator,
|
||||
source: string,
|
||||
{ basePath = './', templates: overrides, ...rest }: GenerateMDXOptions = {},
|
||||
): string {
|
||||
const templates = { ...defaultTemplates, ...overrides };
|
||||
|
||||
return source.replace(regex, (...args) => {
|
||||
const groups = args[args.length - 1] as {
|
||||
file: string;
|
||||
name: string | undefined;
|
||||
};
|
||||
const file = path.resolve(basePath, groups.file);
|
||||
const docs = generator.generateDocumentation(
|
||||
{ path: file },
|
||||
groups.name,
|
||||
rest,
|
||||
);
|
||||
|
||||
return docs
|
||||
.map((doc) =>
|
||||
templates.block(doc, doc.entries.map(templates.property).join('\n')),
|
||||
)
|
||||
.join('\n\n');
|
||||
});
|
||||
}
|
||||
|
||||
function replaceJsDocLinks(md: string): string {
|
||||
return md.replace(/{@link (?<link>[^}]*)}/g, '$1');
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
type GenerateTypeTableOptions,
|
||||
} from '@/lib/type-table';
|
||||
import { toEstree } from 'hast-util-to-estree';
|
||||
import { dirname } from 'node:path';
|
||||
import { type ParameterTag, parseTags } from '@/lib/parse-tags';
|
||||
|
||||
function objectBuilder() {
|
||||
@@ -62,11 +61,11 @@ async function buildTypeProp(
|
||||
): Promise<ObjectExpression> {
|
||||
async function onItem(entry: DocEntry) {
|
||||
const node = objectBuilder();
|
||||
const tags = parseTags(entry.tags);
|
||||
node.addJsxProperty('type', await renderType(entry.simplifiedType));
|
||||
node.addJsxProperty('typeDescription', await renderType(entry.type));
|
||||
node.addExpressionNode('required', valueToEstree(entry.required));
|
||||
|
||||
const tags = parseTags(entry.tags);
|
||||
if (tags.default)
|
||||
node.addJsxProperty('default', await renderType(tags.default));
|
||||
|
||||
@@ -144,6 +143,10 @@ export interface RemarkAutoTypeTableOptions {
|
||||
generator?: Generator;
|
||||
}
|
||||
|
||||
export interface TypeTableProps extends BaseTypeTableProps {
|
||||
cwd?: true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile `auto-type-table` into Fumadocs UI compatible TypeTable
|
||||
*
|
||||
@@ -162,68 +165,71 @@ export function remarkAutoTypeTable(
|
||||
|
||||
return async (tree, file) => {
|
||||
const queue: Promise<void>[] = [];
|
||||
const defaultBasePath = file.path ? dirname(file.path) : undefined;
|
||||
async function run(node: object, props: TypeTableProps) {
|
||||
let basePath = props.cwd ? file.cwd : generateOptions.basePath;
|
||||
if (file.dirname) {
|
||||
basePath ??= file.dirname;
|
||||
}
|
||||
|
||||
const output = await generator.generateTypeTable(props, {
|
||||
...generateOptions,
|
||||
basePath,
|
||||
});
|
||||
|
||||
const rendered = output.map(async (doc) => {
|
||||
return {
|
||||
type: 'mdxJsxFlowElement',
|
||||
name: outputName,
|
||||
attributes: [
|
||||
{
|
||||
type: 'mdxJsxAttribute',
|
||||
name: 'type',
|
||||
value: {
|
||||
type: 'mdxJsxAttributeValueExpression',
|
||||
value: remarkStringify ? JSON.stringify(doc, null, 2) : '',
|
||||
data: {
|
||||
estree: {
|
||||
type: 'Program',
|
||||
sourceType: 'module',
|
||||
body: [
|
||||
{
|
||||
type: 'ExpressionStatement',
|
||||
expression: await buildTypeProp(doc.entries, config),
|
||||
},
|
||||
],
|
||||
} satisfies Program,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
children: [],
|
||||
};
|
||||
});
|
||||
|
||||
Object.assign(node, {
|
||||
type: 'root',
|
||||
attributes: [],
|
||||
children: await Promise.all(rendered),
|
||||
} as Root);
|
||||
}
|
||||
|
||||
visit(tree, 'mdxJsxFlowElement', (node) => {
|
||||
if (node.name !== name) return;
|
||||
const props: Record<string, string> = {};
|
||||
const props: Record<string, string | null> = {};
|
||||
|
||||
for (const attr of node.attributes) {
|
||||
if (attr.type !== 'mdxJsxAttribute' || typeof attr.value !== 'string')
|
||||
if (
|
||||
attr.type !== 'mdxJsxAttribute' ||
|
||||
(typeof attr.value !== 'string' && attr.value !== null)
|
||||
)
|
||||
throw new Error(
|
||||
'`auto-type-table` does not support non-string attributes',
|
||||
'`auto-type-table` only support string & boolean attributes',
|
||||
);
|
||||
|
||||
props[attr.name] = attr.value;
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const output = await generator.generateTypeTable(
|
||||
props as BaseTypeTableProps,
|
||||
{
|
||||
...generateOptions,
|
||||
basePath: generateOptions.basePath ?? defaultBasePath,
|
||||
},
|
||||
);
|
||||
|
||||
const rendered = output.map(async (doc) => {
|
||||
return {
|
||||
type: 'mdxJsxFlowElement',
|
||||
name: outputName,
|
||||
attributes: [
|
||||
{
|
||||
type: 'mdxJsxAttribute',
|
||||
name: 'type',
|
||||
value: {
|
||||
type: 'mdxJsxAttributeValueExpression',
|
||||
value: remarkStringify ? JSON.stringify(doc, null, 2) : '',
|
||||
data: {
|
||||
estree: {
|
||||
type: 'Program',
|
||||
sourceType: 'module',
|
||||
body: [
|
||||
{
|
||||
type: 'ExpressionStatement',
|
||||
expression: await buildTypeProp(doc.entries, config),
|
||||
},
|
||||
],
|
||||
} satisfies Program,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
children: [],
|
||||
};
|
||||
});
|
||||
|
||||
Object.assign(node, {
|
||||
type: 'root',
|
||||
attributes: [],
|
||||
children: await Promise.all(rendered),
|
||||
} as Root);
|
||||
}
|
||||
|
||||
queue.push(run());
|
||||
queue.push(run(node, props));
|
||||
return 'skip';
|
||||
});
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ export async function getTypeTableOutput(
|
||||
content += `\nexport type ${typeName} = ${type}`;
|
||||
}
|
||||
|
||||
const output = gen.generateDocumentation(
|
||||
const output = await gen.generateDocumentation(
|
||||
{ path: file ?? 'temp.ts', content },
|
||||
typeName,
|
||||
options,
|
||||
|
||||
@@ -10,6 +10,11 @@ import { highlightHast } from 'fumadocs-core/highlight';
|
||||
|
||||
const shikiOptions = {
|
||||
lazy: true,
|
||||
langs: ['ts', 'tsx'],
|
||||
|
||||
// disable default transformers & meta parser
|
||||
transformers: [],
|
||||
parseMetaString: undefined,
|
||||
|
||||
themes: {
|
||||
light: 'github-light',
|
||||
|
||||
@@ -13,8 +13,8 @@ const generator = createGenerator({
|
||||
const relative = (s: string): string =>
|
||||
path.resolve(fileURLToPath(new URL(s, import.meta.url)));
|
||||
|
||||
test('class members', () => {
|
||||
const out = generator.generateDocumentation(
|
||||
test('class members', async () => {
|
||||
const out = await generator.generateDocumentation(
|
||||
{
|
||||
path: 'index.ts',
|
||||
content: `
|
||||
@@ -62,8 +62,8 @@ test('class members', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
test('interface members', () => {
|
||||
const out = generator.generateDocumentation(
|
||||
test('interface members', async () => {
|
||||
const out = await generator.generateDocumentation(
|
||||
{
|
||||
path: 'index.ts',
|
||||
content: `
|
||||
@@ -116,7 +116,9 @@ const project = createProject(tsconfig);
|
||||
|
||||
function getSimpleForms(fileName: string, sourceCode: string) {
|
||||
const out: string[] = [];
|
||||
const sourceFile = project.createSourceFile(fileName, sourceCode);
|
||||
const sourceFile = project.createSourceFile(fileName, sourceCode, {
|
||||
overwrite: true,
|
||||
});
|
||||
const checker = project.getTypeChecker();
|
||||
|
||||
function visit(node: Node) {
|
||||
|
||||
@@ -24,11 +24,13 @@ const generator = createGenerator(tsconfig);
|
||||
test('Run', async () => {
|
||||
const file = relative('./fixtures/test.ts');
|
||||
|
||||
const result = ['Test1', 'Test2', 'Test3', 'Test4'].flatMap((name) =>
|
||||
generator.generateDocumentation({ path: file }, name),
|
||||
const result = await Promise.all(
|
||||
['Test1', 'Test2', 'Test3', 'Test4'].map((name) => {
|
||||
return generator.generateDocumentation({ path: file }, name);
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(JSON.stringify(result, null, 2)).toMatchFileSnapshot(
|
||||
await expect(JSON.stringify(result.flat(), null, 2)).toMatchFileSnapshot(
|
||||
'./fixtures/test.output.json',
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user