最终效果
<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 给代码高亮
组件中还可以封装 代码前内容
和 代码后内容
属性
用来自定义前后的内容
测试 行高亮
<DocsModuletitle="类型"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 格式化代码,所以会报错)// 多个组件就赋值给 jsxCodebabelTypes.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 目录的代码 就直接返回原codereturn {code: code,map: null};},},// 其他插件↓
],