原理
使用typescript把源代碼讀成ast,然后修改ast結(jié)構(gòu),然后將ast重新還原成js代碼,最后將js代碼重新寫入文件。
具體步驟
將需要修改的代碼以字符串的形式讀進(jìn)來
var file = fs.readFileSync(path, 'utf-8');
使用ts提供的一系列工具函數(shù)來生成和修改ast
const ts = require('typescript')
關(guān)鍵的轉(zhuǎn)換代碼
function transformTS(source, transformer) {
const sourceFile = ts.createSourceFile(
'test.ts', source, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS
);
const printer = ts.createPrinter();
const result = ts.transform(
sourceFile, [transformer]
);
const transformedSourceFile = result.transformed[0];
const newContent = printer.printFile(transformedSourceFile)
result.dispose()
return newContent
}
這個函數(shù)主要是接受一個源代碼字符串,可以是任意的js代碼或者ts代碼,還接受一個transformer,用來對生成的ast(抽象語法樹)進(jìn)行遍歷,根據(jù)需要在對應(yīng)的位置插入或修改。
一個transformer是一個函數(shù),要寫成下面這樣的格式提供給ts.transform函數(shù)
const transformer = (context) => {
return (rootNode) => {
function visit(node) {
node = ts.visitEachChild(node, visit, context);
if (ts.isArrowFunction(node)) {
console.log(node.body.statements);
let newLine = ts.createExpressionStatement(
ts.createCall(
ts.createPropertyAccess(
ts.createIdentifier('router'), 'get'), undefined,
[
ts.createLiteral(`/${namespace}/${pageName}`),
ts.createPropertyAccess(ts.createIdentifier('controller'),
ts.createPropertyAccess(ts.createIdentifier(namespace), pageName))
]))
node.body.statements = node.body.statements.concat(newLine);
}
return node;
}
return ts.visitNode(rootNode, visit);
}
}
這個函數(shù)主要是遍歷ast的node節(jié)點,對于匹配的節(jié)點進(jìn)行編輯操作,這個例子中遍歷節(jié)點,在ts.isClassDelaration(node)這里判斷是不是類聲明,如果是的話就給這個類添加一個新的成員方法node.members = node.members.concat(createControllerMethod(pageName))。
將代碼轉(zhuǎn)換之后并沒有執(zhí)行寫入操作,需要將修改后的代碼重新寫入
fs.writeFileSync('app/router.ts', result);