create-bfe-cttq
Version:
CTTQ大前端脚手架项目
201 lines (186 loc) • 9.97 kB
JavaScript
const path = require("path");
const fs = require("fs");
const recast = require("recast");
const ejs = require("ejs")
const { FindImport, FindNode } = require("../cli/recast/FilterDeclaration");
module.exports = async function(api, answers) {
if (!answers.capability || answers.capability.length <= 0) {
return;
}
if (answers.capability.includes("monitor")) {
api.extendPackage("monitor/package.json");
}
if (answers.capability.includes("sensors")) {
api.extendPackage("sensors/package.json");
}
if ("PC" == answers.type) {
api.onBeforeCreate((files) => {
Object.keys(files).forEach(file => {
if (path.basename(file) != "main.js") {
return;
}
let ast = recast.parse(files[file]);
// 找到最后一个ImportDeclaration节点
let lastImportIndex = -1;
const lastImport = ast.program.body.filter(node =>
recast.types.namedTypes.ImportDeclaration.check(node)
).pop();
if (lastImport) {
lastImportIndex = ast.program.body.indexOf(lastImport);
}
let vueVariableName = "";
let vueVariable;
// 设置vue实例变量
recast.visit(ast, {
visitExpressionStatement(path) {
const node = path.node;
if (recast.types.namedTypes.CallExpression.check(node.expression) && recast.types.namedTypes.MemberExpression.check(node.expression.callee) && recast.types.namedTypes.NewExpression.check(node.expression.callee.object) && recast.types.namedTypes.Identifier.check(node.expression.callee.object.callee) && node.expression.callee.object.callee.name == "Vue") {
return recast.types.builders.variableDeclaration(
"const",
[
recast.types.builders.variableDeclarator(recast.types.builders.identifier("appVue"),recast.parse(recast.print(node).code).program.body[0].expression)
]
);
}
return false;
}
});
vueVariable = ast.program.body.filter(node => {
if (recast.types.namedTypes.VariableDeclaration.check(node) && node.declarations && node.declarations.length > 0) {
for (const variable of node.declarations) {
if (recast.types.namedTypes.VariableDeclarator.check(variable) && recast.types.namedTypes.CallExpression.check(variable.init) && recast.types.namedTypes.MemberExpression.check(variable.init.callee) && variable.init.callee.object && variable.init.callee.object.callee && variable.init.callee.object.callee.name == "Vue") {
vueVariableName = variable.id.name;
return true;
}
}
}
}
).pop();
// 引入监控
if (answers.capability.includes("monitor")) {
let mainContext = ejs.render(fs.readFileSync(api.templatePathAt("monitor/main.js"), 'utf-8'), {
name: answers.name,
userInfo: `${vueVariableName}.$store.state`
});
let mainAST = recast.parse(mainContext);
let imports = FindImport(mainAST);
if (imports && imports.length > 0) {
ast.program.body.splice(lastImportIndex + 1, 0, ...imports);
}
let otherNode = FindNode(mainAST, {exclude: ["ImportDeclaration"]});
if (otherNode && otherNode.length > 0) {
const vueVariableIndex = ast.program.body.indexOf(vueVariable);
ast.program.body.splice(vueVariableIndex + 1, 0, ...otherNode);
}
}
// 引入埋点
if (answers.capability.includes("sensors")) {
let mainContext = ejs.render(fs.readFileSync(api.templatePathAt("sensors/main.js"), 'utf-8'), {
name: answers.name,
description: answers.description,
userInfo: "newValue"
});
let mainAST = recast.parse(mainContext);
let imports = FindImport(mainAST);
if (imports && imports.length > 0) {
ast.program.body.splice(lastImportIndex + 1, 0, ...imports);
for (const importDec of imports) {
mainAST.program.body.splice(mainAST.program.body.indexOf(importDec), 1);
}
}
let otherNode = FindNode(mainAST, {exclude: ["ImportDeclaration"]});
mainContext = `${vueVariableName}.$store.watch((state) => {
return (state.user || {}).userInfo;
}, (newValue) => {
if (newValue) {
${recast.print(mainAST).code}
}
})`
otherNode = recast.parse(mainContext).program.body;
if (otherNode && otherNode.length > 0) {
const vueVariableIndex = ast.program.body.indexOf(vueVariable);
ast.program.body.splice(vueVariableIndex + 1, 0, ...otherNode);
}
}
// 将AST转换回代码字符串
files[file] = recast.prettyPrint(ast, {tabWidth: 4, useTabs: true}).code.replace(/ {4}/g, "\t") + "\n";
});
})
}
if ("APP" == answers.type) {
api.onBeforeCreate((files) => {
Object.keys(files).forEach(file => {
if (path.basename(file) != "main.js") {
return;
}
let ast = recast.parse(files[file]);
// 找到最后一个ImportDeclaration节点
let lastImportIndex = -1;
const lastImport = ast.program.body.filter(node =>
recast.types.namedTypes.ImportDeclaration.check(node)
).pop();
if (lastImport) {
lastImportIndex = ast.program.body.indexOf(lastImport);
}
// 引入监控
let monitorCode = "";
if (answers.capability.includes("monitor")) {
let mainContext = ejs.render(fs.readFileSync(api.templatePathAt("monitor/main.js"), 'utf-8'), {
name: answers.name,
userInfo: `app.$store.state`
});
let mainAST = recast.parse(mainContext);
let imports = FindImport(mainAST);
if (imports && imports.length > 0) {
ast.program.body.splice(lastImportIndex + 1, 0, ...imports);
for (const importDec of imports) {
mainAST.program.body.splice(mainAST.program.body.indexOf(importDec), 1);
}
}
monitorCode = recast.print(mainAST).code;
}
// 引入埋点
let sensorsCode = "";
if (answers.capability.includes("sensors")) {
let mainContext = ejs.render(fs.readFileSync(api.templatePathAt("sensors/main.js"), 'utf-8'), {
name: answers.name,
description: answers.description,
userInfo: "newValue"
});
let mainAST = recast.parse(mainContext);
let imports = FindImport(mainAST);
if (imports && imports.length > 0) {
ast.program.body.splice(lastImportIndex + 1, 0, ...imports);
for (const importDec of imports) {
mainAST.program.body.splice(mainAST.program.body.indexOf(importDec), 1);
}
}
sensorsCode = `app.$store.watch((state) => {
return (state.userInfo || {}).person;
}, (newValue) => {
if (newValue) {
${recast.print(mainAST).code}
}
});`
}
if (monitorCode || sensorsCode) {
let injectAst = recast.parse(`Object.defineProperty(window, "$app", {
get() {return this.__app;},
set(app) {
this.__app = app;
${monitorCode}
${sensorsCode}
}})
`).program.body;
ast.program.body.splice(ast.program.body.length, 0, ...injectAst);
// 将AST转换回代码字符串
files[file] = recast.prettyPrint(ast, {tabWidth: 4, useTabs: true}).code.replace(/ {4}/g, "\t") + "\n";
}
});
})
}
return;
}
module.exports.apply = {
after: ["base", "cttq-app", "cttq-mobile"],
}