Babel 替换条件确定的 if 判断或条件表达式代码
需求
如果 if 判断或条件表达式条件确定(必为真或假),那么我们可以仅保留进入的分支的代码,以简化代码
下面是代码样例(encode.js)
if (1 > 2) {
console.log('a');
}
else {
console.log('b');
}
if (0) {
console.log('c');
}
if (1) {
console.log('111');
}
if (2) {
let cc = 'cc';
console.log('d -- ' + cc);
}
if (2 > 3) {
console.log('f');
}
else if (5 % 2 === 0) {
console.log('g');
}
else if (!![]) {
console.log('h');
}
else {
console.log('i')
}
var j = 2 >= 3 ? 'j11' : 'j22';
console.log(![] ? '---' : '~~~');
处理后代码(decode.js)
console.log('b');
if (0) {
console.log('c');
}
console.log('111');
if (2) {
let cc = 'cc';
console.log('d -- ' + cc);
}
console.log('h');
var j = 'j22';
console.log('~~~');
思路
也可使用 path.evaluateTruthy()
方法判断 if 条件真假
注意:
- 被处理的 if-else 判断中不能有 var 以外的声明
- 特殊情况 else-if ,如:if else-if else-if else
- 特殊情况 is-else 不含大括号,如: if () xxx else xxx
编写 babel 插件
完整插件代码如下
// decrypt.js
const fs = require('fs');
var util = require('util');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const types = require('@babel/types');
const generator = require('@babel/generator').default;
// 程序启动时间
var time_start = new Date().getTime()
// 读取文件
process.argv.length > 2 ? encode_file = process.argv[2] : encode_file = 'encode.js';
process.argv.length > 3 ? decode_file = process.argv[3] : decode_file = 'decode.js';
let jscode = fs.readFileSync(encode_file, { encoding: 'utf-8' });
console.log(util.format('Reading the file [%s] is complete.', encode_file))
// 转换为 ast 树
let ast = parser.parse(jscode);
const visitor =
{
IfStatement(path) {
let consequent_path = path.get('consequent');
let alternate_path = path.get('alternate');
let test_path = path.get('test');
if (!types.isBlockStatement(consequent_path)) {
consequent_path.replaceWith(types.blockStatement([consequent_path]));
}
if (alternate_path && !types.isBlockStatement(alternate_path)) {
alternate_path.replaceWith(types.blockStatement([alternate_path]));
}
let replace_path;
let { confident, value } = test_path.evaluate();
if (!confident) {
return;
}
if (value) {
replace_path = consequent_path;
}
else {
if (!alternate_path) {
path.remove();
path.scope.crawl();
return
}
replace_path = alternate_path;
}
for (let statement of replace_path.node.body) {
if (types.isVariableDeclaration(statement) && statement.kind !== 'var') {
return;
}
}
path.replaceWithMultiple(replace_path.node.body);
path.scope.crawl();
},
ConditionalExpression(path) {
let consequent_path = path.get('consequent');
let alternate_path = path.get('alternate');
let test_path = path.get('test');
let { confident, value } = test_path.evaluate();
if (!confident) {
return;
}
if (value) {
path.replaceWith(consequent_path);
}
else {
path.replaceWith(alternate_path);
}
path.scope.crawl();
}
}
//调用插件,处理待处理 js ast 树
traverse(ast, visitor);
console.log('AST traverse completed.')
// 生成处理后的 js
let { code } = generator(ast);
console.log('AST generator completed.')
fs.writeFile(decode_file, code, (err) => { });
console.log(util.format('The javascript code in [%s] has been processed.', encode_file))
console.log(util.format('The processing result has been saved to [%s].', decode_file))
// 程序结束时间
var time_end = new Date().getTime()
console.log(util.format('The program runs to completion, time-consuming: %s s', (time_end - time_start) / 1000))