insertCode.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. const parser = require('@babel/parser');
  2. const traverse = require('@babel/traverse');
  3. const generate = require('@babel/generator');
  4. const t = require('@babel/types');
  5. const fs = require('fs');
  6. const path = require('path');
  7. const prettier = require('prettier');
  8. const chalk = require('chalk');
  9. const parseCode = code => {
  10. return parser.parse(code, {
  11. sourceType: 'module',
  12. plugins: ['typescript', 'jsx'],
  13. }).program.body[0];
  14. };
  15. /**
  16. * 生成代码
  17. * @param {*} ast
  18. */
  19. function generateCode(ast) {
  20. const newCode = generate.default(ast, {}).code;
  21. return prettier.format(newCode, {
  22. // format same as ant-design-pro
  23. singleQuote: true,
  24. trailingComma: 'es5',
  25. printWidth: 100,
  26. parser: 'typescript',
  27. });
  28. }
  29. const SettingCodeString = `
  30. <SettingDrawer
  31. settings={settings}
  32. onSettingChange={config =>
  33. dispatch({
  34. type: 'settings/changeSetting',
  35. payload: config,
  36. })
  37. }
  38. />
  39. `;
  40. const mapAst = (configPath, callBack) => {
  41. const ast = parser.parse(fs.readFileSync(configPath, 'utf-8'), {
  42. sourceType: 'module',
  43. plugins: ['typescript', 'jsx'],
  44. });
  45. // 查询当前配置文件是否导出 routes 属性
  46. traverse.default(ast, {
  47. Program({ node }) {
  48. const { body } = node;
  49. callBack(body);
  50. },
  51. });
  52. return generateCode(ast);
  53. };
  54. const insertBasicLayout = configPath => {
  55. return mapAst(configPath, body => {
  56. const index = body.findIndex(item => {
  57. return item.type !== 'ImportDeclaration';
  58. });
  59. body.forEach(item => {
  60. // 从包中导出 SettingDrawer
  61. if (item.type === 'ImportDeclaration') {
  62. if (item.source.value === '@ant-design/pro-layout') {
  63. item.specifiers.push(parseCode(`SettingDrawer`).expression);
  64. }
  65. }
  66. if (item.type === 'VariableDeclaration') {
  67. const {
  68. id,
  69. init: { body },
  70. } = item.declarations[0];
  71. // 给 BasicLayout 中插入 button 和 设置抽屉
  72. if (id.name === `BasicLayout`) {
  73. body.body.forEach(node => {
  74. if (node.type === 'ReturnStatement') {
  75. const JSXFragment = parseCode(`<></>`).expression;
  76. JSXFragment.children.push({ ...node.argument });
  77. JSXFragment.children.push(parseCode(SettingCodeString).expression);
  78. node.argument = JSXFragment;
  79. }
  80. });
  81. }
  82. }
  83. });
  84. });
  85. };
  86. const insertBlankLayout = configPath => {
  87. return mapAst(configPath, body => {
  88. const index = body.findIndex(item => {
  89. return item.type !== 'ImportDeclaration';
  90. });
  91. // 从组件中导入 CopyBlock
  92. body.splice(
  93. index,
  94. 0,
  95. parseCode(`import CopyBlock from '@/components/CopyBlock';
  96. `),
  97. );
  98. body.forEach(item => {
  99. if (item.type === 'VariableDeclaration') {
  100. const { id, init } = item.declarations[0];
  101. // 给 BasicLayout 中插入 button 和 设置抽屉
  102. if (id.name === `Layout`) {
  103. const JSXFragment = parseCode(`<></>`).expression;
  104. JSXFragment.children.push({ ...init.body });
  105. JSXFragment.children.push(parseCode(` <CopyBlock id={Date.now()}/>`).expression);
  106. init.body = JSXFragment;
  107. }
  108. }
  109. });
  110. });
  111. };
  112. const insertRightContent = configPath => {
  113. return mapAst(configPath, body => {
  114. const index = body.findIndex(item => {
  115. return item.type !== 'ImportDeclaration';
  116. });
  117. // 从组件中导入 CopyBlock
  118. body.splice(index, 0, parseCode(`import NoticeIconView from './NoticeIconView';`));
  119. body.forEach(item => {
  120. if (item.type === 'ClassDeclaration') {
  121. const classBody = item.body.body[0].body;
  122. classBody.body.forEach(node => {
  123. if (node.type === 'ReturnStatement') {
  124. const index = node.argument.children.findIndex(item => {
  125. if (item.type === 'JSXElement') {
  126. if (item.openingElement.name.name === 'Avatar') {
  127. return true;
  128. }
  129. }
  130. });
  131. node.argument.children.splice(index, 1, parseCode(`<Avatar menu />`).expression);
  132. node.argument.children.splice(index, 0, parseCode(`<NoticeIconView />`).expression);
  133. }
  134. });
  135. }
  136. });
  137. });
  138. };
  139. module.exports = () => {
  140. const basicLayoutPath = path.join(__dirname, '../src/layouts/BasicLayout.tsx');
  141. fs.writeFileSync(basicLayoutPath, insertBasicLayout(basicLayoutPath));
  142. console.log(`insert ${chalk.hex('#1890ff')('BasicLayout')} success`);
  143. const rightContentPath = path.join(__dirname, '../src/components/GlobalHeader/RightContent.tsx');
  144. fs.writeFileSync(rightContentPath, insertRightContent(rightContentPath));
  145. console.log(`insert ${chalk.hex('#1890ff')('RightContent')} success`);
  146. const blankLayoutPath = path.join(__dirname, '../src/layouts/BlankLayout.tsx');
  147. fs.writeFileSync(blankLayoutPath, insertBlankLayout(blankLayoutPath));
  148. console.log(`insert ${chalk.hex('#1890ff')('blankLayoutPath')} success`);
  149. };