Migrate remarkDynamicContent and remarkInstall to docs generator

This commit is contained in:
fuma-nama
2024-04-15 21:12:13 +08:00
parent cfe7e085be
commit f75287dfeb
40 changed files with 672 additions and 337 deletions
+49
View File
@@ -0,0 +1,49 @@
---
"fumadocs-core": major
"fumadocs-typescript": major
---
**Introduce `fumadocs-docgen` package.**
Offer a better authoring experience for advanced use cases.
- Move `remark-dynamic-content` and `remark-install` plugins to the new package `fumadocs-docgen`.
- Support Typescript generator by default
**Usage**
Add the `remarkDocGen` plugin to your remark plugins.
```ts
import { remarkDocGen, fileGenerator } from "fumadocs-docgen";
remark().use(remarkDocGen, { generators: [fileGenerator()] })
```
Generate docs with code blocks.
````mdx
```json doc-gen:<generator>
{
// options
}
```
````
**Migrate**
For `remarkDynamicContent`, enable `fileGenerator` and use this syntax:
````mdx
```json doc-gen:file
{
"file": "./path/to/my-file.txt"
}
```
````
For `remarkInstall`, it remains the same:
```ts
import { remarkInstall } from "fumadocs-docgen"
```
-6
View File
@@ -1,6 +0,0 @@
---
"fumadocs-typescript": minor
"fumadocs-core": minor
---
Introduce Remark Docs Generation plugin
+14 -4
View File
@@ -97,8 +97,13 @@ Make sure to wrap your content in the [Page](/docs/ui/blocks/page) component, th
<Accordions>
<Accordion title="Example with Fumadocs MDX">
```tsx title="app/docs/[[...slug]]/page.tsx"
|reference:../../examples/next-mdx/app/docs/[[...slug]]/page.tsx|
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/docs/[[...slug]]/page.tsx",
"codeblock": {
"meta": "title=\"app/docs/[[...slug]]/page.tsx\""
}
}
```
</Accordion>
@@ -111,8 +116,13 @@ By default, it uses the built-in document search based on Flexsearch. [Learn to
<Accordions>
<Accordion title="Example with Fumadocs MDX">
```tsx title="app/api/search/route.ts"
|reference:../../examples/next-mdx/app/api/search/route.ts|
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/api/search/route.ts",
"codeblock": {
"meta": "title=\"app/api/search/route.ts\""
}
}
```
</Accordion>
+6 -7
View File
@@ -1,14 +1,14 @@
import createBundleAnalyzer from '@next/bundle-analyzer';
import createMDX from 'fumadocs-mdx/config';
import { rehypeCodeDefaultOptions } from 'fumadocs-core/mdx-plugins';
import {
remarkDynamicContent,
remarkInstall,
rehypeCodeDefaultOptions,
remarkDocGen,
} from 'fumadocs-core/mdx-plugins';
remarkInstall,
fileGenerator,
typescriptGenerator,
} from 'fumadocs-docgen';
import { transformerTwoslash } from 'fumadocs-twoslash';
import rehypeKatex from 'rehype-katex';
import { typescriptGen } from 'fumadocs-typescript';
import remarkMath from 'remark-math';
const withAnalyzer = createBundleAnalyzer({
@@ -63,9 +63,8 @@ const withMDX = createMDX({
lastModifiedTime: 'git',
remarkPlugins: [
remarkMath,
remarkDynamicContent,
[remarkInstall, { Tabs: 'InstallTabs' }],
[remarkDocGen, { generators: [typescriptGen()] }],
[remarkDocGen, { generators: [typescriptGenerator(), fileGenerator()] }],
],
rehypePlugins: (v) => [rehypeKatex, ...v],
},
+1
View File
@@ -16,6 +16,7 @@
"algoliasearch": "^4.23.3",
"class-variance-authority": "^0.7.0",
"fumadocs-core": "workspace:*",
"fumadocs-docgen": "workspace:^",
"fumadocs-mdx": "workspace:*",
"fumadocs-twoslash": "workspace:^",
"fumadocs-typescript": "workspace:^",
-3
View File
@@ -4,8 +4,5 @@ export {
} from 'remark-gfm';
export * from './rehype-code';
export * from './remark-image';
export * from './remark-dynamic-content';
export * from './remark-structure';
export * from './remark-heading';
export * from './remark-install';
export * from './remark-doc-gen';
@@ -1,37 +0,0 @@
import { type Code, type Root } from 'mdast';
import { type Transformer } from 'unified';
import { visit } from 'unist-util-visit';
export interface RemarkDocGenerator {
name: string;
/**
* Transform codeblocks to another mdast element
*/
run: (node: Code, options: unknown) => unknown;
}
export interface RemarkDocGenOptions {
generators?: RemarkDocGenerator[];
}
const metaRegex = /doc-gen:(?<name>.+)/;
export function remarkDocGen({
generators = [],
}: RemarkDocGenOptions): Transformer<Root, Root> {
return (tree) => {
visit(tree, 'code', (c) => {
if (c.lang !== 'json' || !c.meta) return;
const matches = metaRegex.exec(c.meta);
if (!matches) return;
const name = matches[1];
const gen = generators.find((g) => g.name === name);
const result = gen?.run(c, JSON.parse(c.value));
if (result) Object.assign(c, result);
});
};
}
@@ -1,61 +0,0 @@
import fs from 'node:fs';
import path from 'node:path';
import type { Root } from 'mdast';
import type { Transformer } from 'unified';
import { visit } from 'unist-util-visit';
const regex = /^\|reference:(?<path>.+)\|/;
export interface RemarkDynamicContentOptions {
/** @defaultValue true */
trim?: boolean;
/**
* Resolve reference files relative to `vfile.path`
* @defaultValue false
*/
relative?: boolean;
/**
* Filter specific element types
* @defaultValue ['text','code']
* */
visit?: string[];
}
/**
* Copy content from referenced file
*
* @example
* |reference:../path/to/file.ts|
*/
export function remarkDynamicContent(
options: RemarkDynamicContentOptions = {},
): Transformer<Root, Root> {
const {
trim = true,
relative = false,
visit: filter = ['text', 'code'],
} = options;
return (tree, file) => {
const cwd = file.cwd;
visit(tree, filter, (node) => {
const canReplace = 'value' in node && typeof node.value === 'string';
if (!canReplace) return;
const result = regex.exec(node.value);
if (result) {
const dest = relative
? path.resolve(cwd, path.dirname(file.path), result[1])
: path.resolve(cwd, result[1]);
let value = fs.readFileSync(dest).toString();
if (trim) value = value.trim();
node.value = value;
}
});
};
}
-5
View File
@@ -1,5 +0,0 @@
## Hello World
```md
|reference:./fixtures/remark-dynamic-content.md|
```
@@ -1,9 +0,0 @@
## Hello World
````md
## Hello World
```md
|reference:./fixtures/remark-dynamic-content.md|
```
````
@@ -1,5 +0,0 @@
## Hello World
```md
|reference:./remark-dynamic-content.md|
```
@@ -1,9 +0,0 @@
## Hello World
````md
## Hello World
```md
|reference:./fixtures/remark-dynamic-content.md|
```
````
+1 -50
View File
@@ -2,47 +2,11 @@ import { expect, test } from 'vitest';
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { remark } from 'remark';
import {
remarkDynamicContent,
remarkHeading,
remarkInstall,
remarkStructure,
} from '@/mdx-plugins';
import { remarkHeading, remarkStructure } from '@/mdx-plugins';
import { fileURLToPath } from 'node:url';
import { createProcessor } from '@mdx-js/mdx';
const cwd = path.dirname(fileURLToPath(import.meta.url));
test('Remark Dynamic Content', async () => {
const content = readFileSync(
path.resolve(cwd, './fixtures/remark-dynamic-content.md'),
);
const result = await remark()
.use(remarkDynamicContent)
.process({ value: content, cwd });
expect(result.toString()).toMatchFileSnapshot(
path.resolve(cwd, './fixtures/remark-dynamic-content.output.md'),
);
});
test('Remark Dynamic Content - Relative', async () => {
const file = path.resolve(
cwd,
'./fixtures/remark-dynamic-content.relative.md',
);
const content = readFileSync(file);
const result = await remark()
.use(remarkDynamicContent, { relative: true })
.process({ path: file, value: content });
expect(result.toString()).toMatchFileSnapshot(
path.resolve(cwd, './fixtures/remark-dynamic-content.relative.output.md'),
);
});
test('Remark Heading', async () => {
const file = path.resolve(cwd, './fixtures/remark-heading.md');
const content = readFileSync(file);
@@ -54,19 +18,6 @@ test('Remark Heading', async () => {
);
});
test('Remark Install', async () => {
const file = path.resolve(cwd, './fixtures/remark-install.md');
const content = readFileSync(file);
const result = await createProcessor({
remarkPlugins: [remarkInstall],
}).process(content);
expect(result.toString()).toMatchFileSnapshot(
path.resolve(cwd, './fixtures/remark-install.output.js'),
);
});
test('Remark Structure', async () => {
const content = readFileSync(
path.resolve(cwd, './fixtures/remark-structure.md'),
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
extends: ['custom/library'],
ignorePatterns: ['*.test.ts'],
};
+3
View File
@@ -0,0 +1,3 @@
# Fumadocs Doc Gen
Remark plugins & Docs Generator utilities.
+50
View File
@@ -0,0 +1,50 @@
{
"name": "fumadocs-docgen",
"version": "1.0.0",
"description": "Useful remark utilities and plugins",
"keywords": [
"NextJs",
"fumadocs",
"remark",
"mdx",
"Docs"
],
"homepage": "https://fumadocs.vercel.app",
"repository": "github:fuma-nama/fumadocs",
"license": "MIT",
"author": "Fuma Nama",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist/*"
],
"scripts": {
"build": "tsup",
"clean": "rimraf dist",
"dev": "tsup --watch",
"lint": "eslint .",
"types:check": "tsc --noEmit"
},
"dependencies": {
"estree-util-value-to-estree": "^3.1.1",
"fumadocs-typescript": "workspace:^",
"hast-util-to-estree": "^3.1.0",
"npm-to-yarn": "^2.2.1",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@mdx-js/mdx": "^3.0.1",
"@types/estree": "^1.0.5",
"@types/hast": "^3.0.4",
"@types/mdast": "^4.0.3",
"@types/node": "18.17.5",
"eslint-config-custom": "workspace:*",
"remark": "^15.0.0",
"tsconfig": "workspace:*",
"unified": "^11.0.4"
},
"publishConfig": {
"access": "public"
}
}
+66
View File
@@ -0,0 +1,66 @@
import fs from 'node:fs';
import path from 'node:path';
import type { Code, Paragraph } from 'mdast';
import type { DocGenerator } from './remark-docgen';
export interface FileGeneratorOptions {
/** @defaultValue true */
trim?: boolean;
/**
* Resolve reference files relative to `vfile.path`
*
* @defaultValue false
*/
relative?: boolean;
}
export interface FileGeneratorInput {
file: string;
/**
* Turn file content into a code block
*
* @defaultValue false
*/
codeblock?: CodeBlock | boolean;
}
interface CodeBlock {
lang?: string;
meta?: string;
}
export function fileGenerator({
relative = false,
trim = true,
}: FileGeneratorOptions = {}): DocGenerator {
return {
name: 'file',
run(input, ctx) {
const { file, codeblock = false } = input as FileGeneratorInput;
const dest = relative
? path.resolve(ctx.cwd, path.dirname(ctx.path), file)
: path.resolve(ctx.cwd, file);
let value = fs.readFileSync(dest).toString();
if (trim) value = value.trim();
if (codeblock === false) {
return {
type: 'paragraph',
children: [{ type: 'text', value }],
} as Paragraph;
}
const codeOptions = codeblock === true ? {} : codeblock;
return {
type: 'code',
lang: codeOptions.lang ?? path.extname(dest).slice(1),
meta: codeOptions.meta,
value,
} as Code;
},
};
}
+5
View File
@@ -0,0 +1,5 @@
export * from './remark-docgen';
export * from './utils';
export * from './file-generator';
export * from './typescript-generator';
export * from './remark-install';
+49
View File
@@ -0,0 +1,49 @@
import { type BlockContent, type Code, type Root } from 'mdast';
import { type Transformer } from 'unified';
import { visit } from 'unist-util-visit';
export interface DocGenerator {
name: string;
/**
* Transform codeblocks to another mdast element
*/
run: (input: unknown, context: Context) => object | undefined;
}
interface Context {
node: Code;
path: string;
cwd: string;
}
export interface RemarkDocGenOptions {
generators?: DocGenerator[];
}
const metaRegex = /doc-gen:(?<name>.+)/;
export function remarkDocGen({
generators = [],
}: RemarkDocGenOptions): Transformer<Root, Root> {
return (tree, file) => {
visit(tree, 'code', (code, index, parent) => {
if (code.lang !== 'json' || !code.meta) return;
const matches = metaRegex.exec(code.meta);
if (!matches) return;
const name = matches[1];
const gen = generators.find((g) => g.name === name);
const result = gen?.run(JSON.parse(code.value), {
cwd: file.cwd,
path: file.path,
node: code,
});
if (result && typeof index === 'number' && parent) {
parent.children[index] = result as BlockContent;
}
});
};
}
@@ -2,6 +2,7 @@ import type { Code, Root } from 'mdast';
import type { Transformer } from 'unified';
import { visit } from 'unist-util-visit';
import convert from 'npm-to-yarn';
import { createElement } from './utils';
interface PackageManager {
name: string;
@@ -47,35 +48,18 @@ export function remarkInstall({
? node.value
: `npm install ${node.value}`;
const insert = {
type: 'mdxJsxFlowElement',
name: Tabs,
attributes: [
{
type: 'mdxJsxAttribute',
name: 'items',
value: {
type: 'mdxJsxAttributeValueExpression',
data: {
estree: {
body: [
{
type: 'ExpressionStatement',
expression: {
type: 'ArrayExpression',
elements: packageManagers.map(({ name }) => ({
type: 'Literal',
value: name,
})),
},
},
],
},
},
},
const insert = createElement(
Tabs,
{
items: {
type: 'ArrayExpression',
elements: packageManagers.map(({ name }) => ({
type: 'Literal',
value: name,
})),
},
],
children: packageManagers.map(({ command, name }) => ({
},
packageManagers.map(({ command, name }) => ({
type: 'mdxJsxFlowElement',
name: Tab,
attributes: [{ type: 'mdxJsxAttribute', name: 'value', value: name }],
@@ -83,11 +67,12 @@ export function remarkInstall({
{
type: 'code',
lang: 'bash',
meta: node.meta,
value: command(value),
} satisfies Code,
],
})),
};
);
Object.assign(node, insert);
});
@@ -0,0 +1,97 @@
import * as path from 'node:path';
import { valueToEstree } from 'estree-util-value-to-estree';
import type { ExpressionStatement, ObjectExpression, Property } from 'estree';
import { toEstree as hastToEstree } from 'hast-util-to-estree';
import {
type GenerateDocumentationOptions,
generateDocumentation,
renderMarkdownToHast,
type DocEntry,
} from 'fumadocs-typescript';
import { createElement } from './utils';
import type { DocGenerator } from './remark-docgen';
export type TypescriptGeneratorOptions = GenerateDocumentationOptions;
export interface TypescriptGeneratorInput {
file: string;
name: string;
/**
* Component name which accepts the `type` property
*
* @defaultValue 'TypeTable'
*/
component?: string;
}
/**
* Docs generator for Typescript
*
* @param options - configuration
*/
export function typescriptGenerator(
options?: TypescriptGeneratorOptions,
): DocGenerator {
function mapProperty(entry: DocEntry): Property {
const value = valueToEstree({
type: entry.type,
description: '',
default: entry.tags.default || entry.tags.defaultValue,
}) as ObjectExpression;
if (entry.description) {
const hast = hastToEstree(renderMarkdownToHast(entry.description), {
elementAttributeNameCase: 'react',
}).body[0] as ExpressionStatement;
value.properties.push({
type: 'Property',
method: false,
shorthand: false,
computed: false,
key: {
type: 'Identifier',
name: 'description',
},
kind: 'init',
value: hast.expression,
});
}
return {
type: 'Property',
method: false,
shorthand: false,
computed: false,
key: {
type: 'Identifier',
name: entry.name,
},
kind: 'init',
value,
};
}
return {
name: 'typescript',
run(input, ctx) {
const {
file,
name,
component = 'TypeTable',
} = input as TypescriptGeneratorInput;
const dest = path.resolve(ctx.cwd, file);
const doc = generateDocumentation(dest, name, options);
if (!doc) throw new Error(`Failed to find type ${name} in ${dest}`);
return createElement(component, {
type: {
type: 'ObjectExpression',
properties: doc.entries.map(mapProperty),
},
});
},
};
}
+34
View File
@@ -0,0 +1,34 @@
import type { Expression, Program } from 'estree';
export function createElement(
name: string,
properties: Record<string, Expression>,
children?: unknown,
): object {
const element: Record<string, unknown> = {
type: 'mdxJsxFlowElement',
name,
attributes: Object.entries(properties).map(([key, prop]) => ({
type: 'mdxJsxAttribute',
name: key,
value: {
type: 'mdxJsxAttributeValueExpression',
data: {
estree: {
type: 'Program',
body: [
{
type: 'ExpressionStatement',
expression: prop,
},
],
} as Program,
},
},
})),
};
if (children) element.children = children;
return element;
}
+28
View File
@@ -0,0 +1,28 @@
## Hello World
```json doc-gen:file
{
"file": "./fixtures/sample.txt"
}
```
## Code Block
```json doc-gen:file
{
"file": "./fixtures/sample.ts",
"codeblock": true
}
```
## Code Block with config
```json doc-gen:file
{
"file": "./fixtures/sample.txt",
"codeblock": {
"lang": "md",
"meta": "title=\"Hello World\""
}
}
```
+23
View File
@@ -0,0 +1,23 @@
## Hello World
Hello World
This is some text
## Code Block
```ts
export interface Test1 {
name: string;
description: string;
isGood: boolean;
}
```
## Code Block with config
```md title="Hello World"
Hello World
This is some text
```
+8
View File
@@ -0,0 +1,8 @@
## Hello World
```json doc-gen:file
{
"file": "./sample.txt",
"codeblock": true
}
```
@@ -0,0 +1,7 @@
## Hello World
```txt
Hello World
This is some text
```
+5
View File
@@ -0,0 +1,5 @@
export interface Test1 {
name: string;
description: string;
isGood: boolean;
}
+3
View File
@@ -0,0 +1,3 @@
Hello World
This is some text
+8
View File
@@ -0,0 +1,8 @@
## Normal
```json doc-gen:typescript
{
"file": "./fixtures/sample.ts",
"name": "Test1"
}
```
+43
View File
@@ -0,0 +1,43 @@
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
function _createMdxContent(props) {
const _components = {
h2: "h2",
...props.components
}, {TypeTable} = _components;
if (!TypeTable) _missingMdxReference("TypeTable", true);
return _jsxs(_Fragment, {
children: [_jsx(_components.h2, {
children: "Normal"
}), "\n", _jsx(TypeTable, {
type: {
name: {
"type": "string",
"description": "",
"default": undefined
},
description: {
"type": "string",
"description": "",
"default": undefined
},
isGood: {
"type": "boolean",
"description": "",
"default": undefined
}
}
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
function _missingMdxReference(id, component) {
throw new Error("Expected " + (component ? "component" : "object") + " `" + id + "` to be defined: you likely forgot to import, pass, or provide it.");
}
+77
View File
@@ -0,0 +1,77 @@
import { expect, test } from 'vitest';
import { remarkDocGen } from '@/remark-docgen';
import { createProcessor } from '@mdx-js/mdx';
import { fileGenerator } from '@/file-generator';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import { readFileSync } from 'node:fs';
import { remarkInstall } from '@/remark-install';
import { remark } from 'remark';
import { typescriptGenerator } from '@/typescript-generator';
const cwd = path.dirname(fileURLToPath(import.meta.url));
test('File Generator', async () => {
const content = readFileSync(path.resolve(cwd, './fixtures/file-gen.md'));
const processor = remark().use(remarkDocGen, {
generators: [fileGenerator()],
});
const result = await processor.process({ cwd, value: content });
expect(result.toString()).toMatchFileSnapshot(
path.resolve(cwd, './fixtures/file-gen.output.md'),
);
});
test('File Generator - Relative', async () => {
const file = path.resolve(cwd, './fixtures/file-gen.relative.md');
const content = readFileSync(file);
const result = await remark()
.use(remarkDocGen, {
generators: [fileGenerator({ relative: true })],
})
.process({ path: file, value: content, cwd });
expect(result.toString()).toMatchFileSnapshot(
path.resolve(cwd, './fixtures/file-gen.relative.output.md'),
);
});
test('Remark Install', async () => {
const file = path.resolve(cwd, './fixtures/remark-install.md');
const content = readFileSync(file);
const result = await createProcessor({
remarkPlugins: [remarkInstall],
}).process(content);
expect(result.toString()).toMatchFileSnapshot(
path.resolve(cwd, './fixtures/remark-install.output.js'),
);
});
test('Typescript Generator', async () => {
const file = path.resolve(cwd, './fixtures/typescript-gen.md');
const content = readFileSync(file);
const tsconfig = {
tsconfigPath: path.resolve(cwd, '../tsconfig.json'),
basePath: path.resolve(cwd, '../'),
};
const result = await createProcessor({
remarkPlugins: [
[
remarkDocGen,
{ generators: [typescriptGenerator({ config: tsconfig })] },
],
],
}).process({ value: content, cwd });
expect(result.toString()).toMatchFileSnapshot(
path.resolve(cwd, './fixtures/typescript-gen.output.js'),
);
});
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "tsconfig/react-library.json",
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
+9
View File
@@ -0,0 +1,9 @@
import { defineConfig } from 'tsup';
export default defineConfig({
external: [],
dts: true,
target: 'es6',
format: 'esm',
entry: ['src/index.ts'],
});
+9
View File
@@ -0,0 +1,9 @@
import { defineProject } from 'vitest/config';
export default defineProject({
resolve: {
alias: {
'@/': new URL('./src/', import.meta.url).pathname,
},
},
});
-3
View File
@@ -42,9 +42,7 @@
"types:check": "tsc --noEmit"
},
"dependencies": {
"estree-util-value-to-estree": "^3.1.1",
"fast-glob": "^3.3.1",
"hast-util-to-estree": "^3.1.0",
"hast-util-to-jsx-runtime": "^2.3.0",
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-gfm": "^3.0.0",
@@ -57,7 +55,6 @@
"@types/react": "18.2.67",
"@types/react-dom": "18.2.22",
"eslint-config-custom": "workspace:*",
"fumadocs-core": "workspace:*",
"fumadocs-ui": "workspace:*",
"tsconfig": "workspace:*"
},
+1 -1
View File
@@ -1,4 +1,4 @@
export * from './generate/base';
export * from './generate/mdx';
export * from './generate/file';
export * from './plugins/remark-typescript-gen';
export { renderMarkdownToHast } from './markdown';
@@ -1,97 +0,0 @@
import { valueToEstree } from 'estree-util-value-to-estree';
import type { RemarkDocGenerator } from 'fumadocs-core/mdx-plugins';
import type { ExpressionStatement, ObjectExpression, Property } from 'estree';
import { toEstree as hastToEstree } from 'hast-util-to-estree';
import { renderMarkdownToHast } from '@/markdown';
import {
type GenerateDocumentationOptions,
generateDocumentation,
type DocEntry,
} from '../generate/base';
export interface GeneratorOptions {
file: string;
name: string;
}
export function typescriptGen(
config?: GenerateDocumentationOptions,
): RemarkDocGenerator {
function mapProperty(entry: DocEntry): Property {
const value = valueToEstree({
type: entry.type,
description: '',
default: entry.tags.default || entry.tags.defaultValue,
}) as ObjectExpression;
if (entry.description) {
const hast = hastToEstree(renderMarkdownToHast(entry.description), {
elementAttributeNameCase: 'react',
}).body[0] as ExpressionStatement;
value.properties = value.properties.map((p) => {
if (
p.type === 'Property' &&
p.key.type === 'Literal' &&
p.key.value === 'description'
) {
return {
...p,
value: hast.expression as unknown,
};
}
return p;
}) as ObjectExpression['properties'];
}
return {
type: 'Property',
method: false,
shorthand: false,
computed: false,
key: {
type: 'Identifier',
name: entry.name,
},
kind: 'init',
value,
};
}
return {
name: 'typescript',
run(_, options) {
const { file, name } = options as GeneratorOptions;
const doc = generateDocumentation(file, name, config);
if (!doc) throw new Error(`Failed to find type ${name} in ${file}`);
return {
type: 'mdxJsxFlowElement',
name: 'TypeTable',
attributes: [
{
type: 'mdxJsxAttribute',
name: 'type',
value: {
type: 'mdxJsxAttributeValueExpression',
data: {
estree: {
type: 'Program',
body: [
{
type: 'ExpressionStatement',
expression: {
type: 'ObjectExpression',
properties: doc.entries.map(mapProperty),
},
} as ExpressionStatement,
],
},
},
},
},
],
};
},
};
}
+49 -11
View File
@@ -70,6 +70,9 @@ importers:
fumadocs-core:
specifier: workspace:*
version: link:../../packages/core
fumadocs-docgen:
specifier: workspace:^
version: link:../../packages/doc-gen
fumadocs-mdx:
specifier: workspace:*
version: link:../../packages/mdx
@@ -455,6 +458,52 @@ importers:
specifier: ^5.4.5
version: 5.4.5
packages/doc-gen:
dependencies:
estree-util-value-to-estree:
specifier: ^3.1.1
version: 3.1.1
fumadocs-typescript:
specifier: workspace:^
version: link:../typescript
hast-util-to-estree:
specifier: ^3.1.0
version: 3.1.0
npm-to-yarn:
specifier: ^2.2.1
version: 2.2.1
unist-util-visit:
specifier: ^5.0.0
version: 5.0.0
devDependencies:
'@mdx-js/mdx':
specifier: ^3.0.1
version: 3.0.1
'@types/estree':
specifier: ^1.0.5
version: 1.0.5
'@types/hast':
specifier: ^3.0.4
version: 3.0.4
'@types/mdast':
specifier: ^4.0.3
version: 4.0.3
'@types/node':
specifier: 18.17.5
version: 18.17.5
eslint-config-custom:
specifier: workspace:*
version: link:../eslint-config-custom
remark:
specifier: ^15.0.0
version: 15.0.1
tsconfig:
specifier: workspace:*
version: link:../tsconfig
unified:
specifier: ^11.0.4
version: 11.0.4
packages/eslint-config-custom: {}
packages/mdx:
@@ -575,15 +624,9 @@ importers:
packages/typescript:
dependencies:
estree-util-value-to-estree:
specifier: ^3.1.1
version: 3.1.1
fast-glob:
specifier: ^3.3.1
version: 3.3.2
hast-util-to-estree:
specifier: ^3.1.0
version: 3.1.0
hast-util-to-jsx-runtime:
specifier: ^2.3.0
version: 2.3.0
@@ -618,9 +661,6 @@ importers:
eslint-config-custom:
specifier: workspace:*
version: link:../eslint-config-custom
fumadocs-core:
specifier: workspace:*
version: link:../core
fumadocs-ui:
specifier: workspace:*
version: link:../ui
@@ -9564,7 +9604,6 @@ packages:
'@types/mdast': 4.0.3
mdast-util-to-markdown: 2.1.0
unified: 11.0.4
dev: false
/[email protected]:
resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==}
@@ -9575,7 +9614,6 @@ packages:
unified: 11.0.4
transitivePeerDependencies:
- supports-color
dev: false
/[email protected]:
resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==}