https://zhuanlan.zhihu.com/p/340615580
示例:
为生成router.js代码写一个单元测试
import { Router } from '../tranform/vue/router';
import { PageParser } from '../index';
const babelParser = require('@babel/parser');
const generate = require('@babel/generator').default;
const traverse = require('@babel/traverse').default; // 访问ast对象,查找和替换使用, https://babeljs.io/docs/en/babel-traverse
describe('测试router', () => {
const routerDsl = [{
pageid: 'form', // nouse
title: `form.name`,
pageName: 'form',// nouse
meta: {
icon: 'fin-icon-s-home',
breadcrumb: false,
},
children: [
{
pageid: 'detail', // nouse
title: `form.detail`,
pageName: 'detail',// nouse
meta: {
icon: 'fin-icon-s-home',
breadcrumb: false,
},
},
],
}];
const pageDsl = {
fileName: 'index',
id: 'form',
shareData: [],
action: {},
components: [],
apiConfigs: [],
apis: [],
};
const pageParser = new PageParser(pageDsl)
const router = new Router(pageParser, routerDsl);
test('test generateRouterCode', () => {
const res = router.toString();
console.log('res', res);
console.log('success');
})
})
生成router.js源码:
import { ct } from '../../utils/createType';
import { PageParser } from '../../parser/page';
import generate from '@babel/generator';
const babelParser = require('@babel/parser');
const traverse = require('@babel/traverse').default; // 访问ast对象,查找和替换使用, https://babeljs.io/docs/en/babel-traverse
export class Router {
private parser: any;
private routerConf: any;
private baseCode: string = `
import basicLayout from '@/layouts/basic-layout';
const sort = 1;
const routers = [];
export default routers;
`;
constructor(parser: any, routerConf: any) {
this.parser = parser;
this.routerConf = routerConf;
}
/**
* 从main.dsl中获取模块的router配置,如果main.dsl没有,返回默认配置
* @returns object
*/
private getRouterConfig() {
// mainParser: pageid代表模块名称,parser:id代表模块名称
const curRouterConfig = this.routerConf.find((item) => item.pageid === this.parser.dsl.id);
if (curRouterConfig) {
return curRouterConfig;
}
const routerName: string = this.parser.dsl.fileName || 'index';
const moduleName: string = this.parser.dsl.id || 'module';
// 如果mainDal中没有配置,就从page.dsl中取fileName,id为默认值
return {
title: routerName,
pageid: moduleName,
pageName: routerName,
meta: {
icon: 'fin-icon-s-home',
breadcrumb: false,
},
children: [],
}
}
/**
* 获取所有需要导入的组件名称
* @params router 路由配置
* @returns array
*/
// private getComponents(router: any[]) {
// // 获取所有需要导入的组件名称
// const comArr: any[] = [];
// const getCom = (router) => {
// router.forEach((item) => {
// const { pageName, children=[] } = item;
// if (pageName) {
// comArr.push(pageName);
// }
// if (children.length) {
// // 递归获取组件名称
// getCom(children);
// }
// });
// }
// getCom(router);
// return comArr;
// }
/**
* 生成children:[]数组中内容
* @params arr children数组
* @returns array
*/
private genChildConf(arr) {
const routerConfig = this.getRouterConfig();
const {
pageid: moduleName,
pageName: routerName,
} = routerConfig;
const childrenConf = [];
arr.forEach((item, index) => {
const {
pageid: childId=`${moduleName}${index}`,
pageName: childName=`${routerName}${index}`,
title:subTitle = `${routerName}${index}`,
meta={},
children:sub = [],
} = item;
meta.title = subTitle;
let path: string = '';
// 一级菜单的路径默认为模块名
if (childId === moduleName) {
path = `/${moduleName}`;
} else {
path = `/${moduleName}/${childId}`;
}
const importStr = `/* webpackChunkName: ${moduleName} */ '../view/${childName}.vue'`;
childrenConf.push(
ct.objectExpression([
ct.objectProperty(ct.identifier('path'),ct._convert(path)),
ct.objectProperty(ct.identifier('component'),
ct.arrowFunctionExpression(
[],
ct.callExpression(
ct.identifier('import'),
[ct.identifier(importStr)]
)
)
),
ct.objectProperty(ct.identifier('meta'),ct._convert(meta)),
// 如果有children递归调用
ct.objectProperty(ct.identifier('children'), this.genChildConf(sub)),
])
);
});
return ct.arrayExpression(childrenConf);
}
/**
* 生成routers = []中内容
* @params router 路由配置
* @returns string
*/
private generateRouterArr() {
const routerConfig = this.getRouterConfig();
const {
title = '' ,
pageid: moduleName,
pageName: routerName,
meta = {},
} = routerConfig;
// path固定为模块名
const path = `/${moduleName}`;
// title放入meta中
meta.title = title;
const children = [{
title,
pageid: moduleName,
pageName: routerName,
meta,
children: routerConfig.children,
}];
return ct.variableDeclaration('const', [
ct.variableDeclarator(
ct.identifier('routers'),
ct.arrayExpression([
ct.objectExpression([
ct.objectProperty(ct.identifier('sort'),ct.identifier('sort')),
ct.objectProperty(ct.identifier('path'),ct._convert(path)),
ct.objectProperty(ct.identifier('component'),ct.identifier('basicLayout')), // 默认为模板工程的basiclayout
ct.objectProperty(ct.identifier('meta'),ct._convert(meta)),
ct.objectProperty(ct.identifier('children'), this.genChildConf(children)),
])
])
)
]
);
}
/**
* 生成模块router.js
* @returns string
*/
private generateRouterCode() {
const routerConfig = this.getRouterConfig();
const {
pageid: moduleName,
sort = 1,
} = routerConfig;
const ast = babelParser.parse(this.baseCode, { sourceType: 'module' });
// // import basiclayout
// const body_0 = ct.importDeclaration([
// ct.importDefaultSpecifier(ct.identifier('basicLayout'))
// ], ct.stringLiteral('@/layouts/basic-layout'));
// const body_1 = ct._define('const', 'sort', sort);
// // 声明moduleName,固定为main.dsl中的pageid
// const body_2 = ct._define('const', 'module', moduleName);
// 拼接ast
// ast.program.body = [
// body_0, body_1, body_2,
// this.generateRouterArr(),
// ct.exportDefaultDeclaration(ct.identifier('routers')),
// ];
const routerArr = this.generateRouterArr();
const astVistor = {
enter(path) {
if (ct.isVariableDeclaration(path)) {
switch (path.node.declarations[0].id.name) {
case 'sort':
path.node.declarations[0].init.value = sort;
break;
// case 'module':
// path.node.declarations[0].init.value = moduleName;
// break;
case 'routers':
path.node.declarations = routerArr.declarations;
break;
default: break;
}
}
},
}
// 遍历ast
traverse(ast, astVistor);
// 生成code
const newScript = generate(ast, {
jsescOption: { "minimal" : true } // 支持中文字符编码
}, this.baseCode);
return newScript.code;
}
public toString() {
if (this.parser instanceof PageParser) {
return this.generateRouterCode();
}
return '';
}
}