最终效果
<DocsModule title="类型">
<Button>默认按钮</Button>
<Button type="primary">主要按钮</Button>
<Button type="success">成功按钮</Button>
<Button type="danger">危险按钮</Button>
</DocsModule>
转换为 ↓↓↓↓ ( 自动生成 , 再也不用手动写这些重复的代码了)
<DocsModule
title="类型"
jsxCode={`
<Button>默认按钮</Button>
<Button type="primary">主要按钮</Button>
<Button type="success">成功按钮</Button>
<Button type="danger">危险按钮</Button>
`}
>
<Button>默认按钮</Button>
<Button type="primary">主要按钮</Button>
<Button type="success">成功按钮</Button>
<Button type="danger">危险按钮</Button>
</DocsModule>
jsxCode 中格式比较乱, 无所谓,
组件内部会使用 prettier 格式化代码,
还有 highlight.js 给代码高亮
组件中还可以封装 代码前内容
和 代码后内容
属性
用来自定义前后的内容
测试 行高亮
<DocsModule
title="类型"
codeLineHighLight={[
{ line: 1, type: "info" },
{ line: 2, type: "info" },
{ line: 5, type: "error" },
{ line: 6, type: "error" },
]}
>
<Button>默认按钮</Button>
<Button type="primary">主要按钮</Button>
<Button type="success">成功按钮</Button>
<Button type="info">信息按钮</Button>
<Button type="warning">警告按钮</Button>
<Button type="danger">危险按钮</Button>
</DocsModule>
安装依赖
"devDependencies": {
"@babel/core": "^7.22.9",
"@babel/generator": "^7.22.9",
"@babel/parser": "^7.22.7",
"@babel/plugin-syntax-jsx": "^7.22.5",
"@babel/preset-env": "^7.22.9",
"@babel/preset-typescript": "^7.22.5",
"@babel/traverse": "^7.22.8",
"@babel/types": "^7.22.5",
"@types/babel__generator": "^7.6.4",
"@types/babel__traverse": "^7.20.1",
},
vite.config.ts
// 导入
import * as babelParser from '@babel/parser';
import babelTraverse from '@babel/traverse';
import * as babelTypes from '@babel/types';
import babelGenerator from '@babel/generator';
//插件
plugins: [
{
name: '自动获取 jsx 内容,填写到 jsxCode',
enforce: 'pre',
transform(code, id) {
// 只处理自己的 pages 目录代码
if (!id.includes("node_modules") && id.includes("/examples/src/pages/")) {
const ast = babelParser.parse(code, {
plugins: ["jsx", "typescript"],
sourceType: "module",
});
babelTraverse(ast, {
JSXOpeningElement(path) {
// DocsModule 是自己封装的 组件
if (path.node.name.type == "JSXIdentifier" && path.node.name.name === "DocsModule") {
const attributes = path.node.attributes;
const hasJsxCodeAttr = attributes.find(
item =>
item.type === 'JSXAttribute' &&
babelTypes.isJSXIdentifier(item.name, { name: 'jsxCode' }) // 忘了判断 code,用的很少,就懒得搞了.
);
// 如果已经自定义了 jsxCode 属性, 则跳过本次处理
if (hasJsxCodeAttr) {
return;
}
// 开始开始拼装 属性, 并赋值给 组件
if (path.parent.type == "JSXElement") {
const children = path.parent.children.filter((node) => node.type === "JSXElement");
const childrenStr = children.map((child) => babelGenerator(child).code).join("");
const jsxCodeAttribute = babelTypes.jsxAttribute(
// 组件中会给 jsxCode 在前后 加上 <> </> , 因为 jsx 不允许一级结构存在多个组件 (组件中用了 prettier 格式化代码,所以会报错)
// 多个组件就赋值给 jsxCode
babelTypes.jsxIdentifier(children.length > 1 ? "jsxCode" : "code"),
babelTypes.jsxExpressionContainer(
// 定义模版字符串
babelTypes.templateLiteral([babelTypes.templateElement({ raw: childrenStr }, true)], [])
)
);
// 向原始属性列表中添加新的 jsxCode 属性
attributes.push(jsxCodeAttribute);
}
}
},
});
// 生成转换后的代码
let transformedCode: string = babelGenerator(ast).code;
// fix: ast处理后末尾出现了分号
// 莫名其妙的多了分号, 感觉是 babel 的 bug, 或者是 边缘情况, babel 给误判了. (不是标准的js代码, 是纯jsx)
transformedCode = transformedCode.replaceAll(">;", ">")
return {
code: transformedCode,
map: null
};
}
// 不是 pages 目录的代码 就直接返回原code
return {
code: code,
map: null
};
},
},
// 其他插件↓
],