Skip to content

Commit

Permalink
Docgen: Use oxc for ts2js remark plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
fuma-nama committed Dec 19, 2024
1 parent 91033bf commit 6d3c7d2
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 96 deletions.
5 changes: 5 additions & 0 deletions .changeset/tiny-pans-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'fumadocs-docgen': patch
---

Use `oxc` for `ts2js` remark plugins
2 changes: 1 addition & 1 deletion packages/doc-gen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
"types:check": "tsc --noEmit"
},
"dependencies": {
"@swc/wasm-typescript": "^1.10.1",
"estree-util-value-to-estree": "^3.2.1",
"fumadocs-typescript": "workspace:^",
"hast-util-to-estree": "^3.1.0",
"npm-to-yarn": "^3.0.0",
"oxc-transform": "^0.42.0",
"ts-morph": "^24.0.0",
"unist-util-visit": "^5.0.0",
"zod": "^3.24.1"
Expand Down
183 changes: 96 additions & 87 deletions packages/doc-gen/src/remark-ts2js.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Transformer } from 'unified';
import { type Code, Root } from 'mdast';
import { visit } from 'unist-util-visit';
import { transform, Options as SWCOptions } from '@swc/wasm-typescript';
import { createElement, expressionToAttribute } from '@/utils';
import oxc from 'oxc-transform';

export interface TypeScriptToJavaScriptOptions {
swc?: SWCOptions;
/**
* Persist Tab value (Fumadocs UI only)
*
Expand All @@ -16,105 +15,115 @@ export interface TypeScriptToJavaScriptOptions {
id: string;
}
| false;

/**
* Transform all TypeScript codeblocks by default, without a trigger
*/
disableTrigger?: boolean;
}

export function remarkTs2js({
swc = {},
/**
* A remark plugin to transform TypeScript codeblocks into two tabs of codeblocks with its JS variant.
*
* Add `ts2js` to enable transformation:
* ````md
* ```tsx ts2js
* import { ReactNode } from "react";
*
* export default function Layout({ children }: { children: ReactNode }) {
* return <div>{children}</div>
* }
* ```
* ````
*/
export function remarkTypeScriptToJavaScript({
persist = false,
disableTrigger = false,
}: TypeScriptToJavaScriptOptions = {}): Transformer<Root> {
return async (tree, file) => {
const queue: Promise<void>[] = [];

return (tree, file) => {
visit(tree, 'code', (node) => {
if (node.lang !== 'ts' && node.lang !== 'tsx') return;

const task = transform(node.value, {
filename: `${file.path}.${node.lang}`,
transform: {
importExportAssignConfig: 'Preserve',
verbatimModuleSyntax: true,
if (!disableTrigger && !node.meta?.includes('ts2js')) return;

const result = oxc.transform(
`${file.path ?? 'test'}.${node.lang}`,
node.value,
{
sourcemap: false,
jsx: 'preserve',
},
sourceMap: false,
...swc,
})
.then((output) => {
const insert = createElement(
'Tabs',
[
...(typeof persist === 'object'
? [
{
type: 'mdxJsxAttribute',
name: 'groupId',
value: persist.id,
},
{
type: 'mdxJsxAttribute',
name: 'persist',
value: null,
},
]
: []),
expressionToAttribute('items', {
type: 'ArrayExpression',
elements: ['TypeScript', 'JavaScript'].map((name) => ({
type: 'Literal',
value: name,
})),
}),
],
[
);

const insert = createElement(
'Tabs',
[
...(typeof persist === 'object'
? [
{
type: 'mdxJsxAttribute',
name: 'groupId',
value: persist.id,
},
{
type: 'mdxJsxAttribute',
name: 'persist',
value: null,
},
]
: []),
expressionToAttribute('items', {
type: 'ArrayExpression',
elements: ['TypeScript', 'JavaScript'].map((name) => ({
type: 'Literal',
value: name,
})),
}),
],
[
{
type: 'mdxJsxFlowElement',
name: 'Tab',
attributes: [
{
type: 'mdxJsxFlowElement',
name: 'Tab',
attributes: [
{
type: 'mdxJsxAttribute',
name: 'value',
value: 'TypeScript',
},
],
children: [
{
type: 'code',
lang: node.lang,
meta: node.meta,
value: node.value,
} satisfies Code,
],
type: 'mdxJsxAttribute',
name: 'value',
value: 'TypeScript',
},
],
children: [
{
type: 'code',
lang: node.lang,
meta: node.meta,
value: node.value,
} satisfies Code,
],
},
{
type: 'mdxJsxFlowElement',
name: 'Tab',
attributes: [
{
type: 'mdxJsxFlowElement',
name: 'Tab',
attributes: [
{
type: 'mdxJsxAttribute',
name: 'value',
value: 'JavaScript',
},
],
children: [
{
type: 'code',
lang: 'jsx',
meta: node.meta,
value: output.code,
} satisfies Code,
],
type: 'mdxJsxAttribute',
name: 'value',
value: 'JavaScript',
},
],
);

Object.assign(node, insert);
})
.catch((e) => {
// ignore node
console.error(e);
});
children: [
{
type: 'code',
lang: 'jsx',
meta: node.meta,
value: result.code,
} satisfies Code,
],
},
],
);

queue.push(task);
Object.assign(node, insert);
return 'skip';
});

await Promise.all(queue);
};
}
89 changes: 81 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6d3c7d2

Please sign in to comment.