webpack和Lint等很多的工具和库的核心都是通过Abstract Syntax Tree抽象语法树这个概念来实现对代码的检查、分析等操作的。通过了解抽象语法树这个概念,你也可以随手编写类似的工具
这些工具的原理都是通过JavaScript Parser把代码转化为一颗抽象语法树(AST),这颗树定义了代码的结构,通过操纵这颗树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更等操作
在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。
Javascript的语法是为了给开发者更好的编程而设计的,但是不适合程序的理解。所以需要转化为AST来更适合程序分析,浏览器编译器一般会把源码转化为AST来进行进一步的分析等其他操作。

JavaScript Parser,把js源码转化为抽象语法树的解析器。
浏览器会把js源码通过解析器转为抽象语法树,再进一步转化为字节码或直接生成机器码。
一般来说每个js引擎都会有自己的抽象语法树格式,Chrome的v8引擎,firefox的SpiderMonkey引擎等等,MDN提供了详细SpiderMonkey AST format的详细说明,算是业界的标准。
let esprima = require('esprima');
var estraverse = require('estraverse');
var escodegen = require("escodegen");
let code = 'function ast(){}';
let ast = esprima.parse(code);
console.log(ast);
estraverse.traverse(ast,{
enter(node){
node.name += '_ext';
},
leave(node){
// console.log(node.type);
}
});
let generated = escodegen.generate(ast);
console.log(generated);
let babel = require('babel-core');
let types = require('babel-types');
const code = `const sum = (a,b)=>a+b`;
// babel-eslint
const visitor = {
ArrowFunctionExpression:{
enter(path) {
let node = path.node;
let expression = node.body;
let params = node.params;
let returnStatement = types.returnStatement(expression);
let block = types.blockStatement([
returnStatement
]);
let func = types.functionExpression(null,params, block,false, false);
path.replaceWith(func);
}
}
}
const result = babel.transform(code,{
plugins:[
{visitor}
]
});
console.log(result.code);
预计算简单表达式的插件
const result = 1 + 2;
const result = 3;
let babel = require('babel-core');
let types = require('babel-types');
let visitor = {
BinaryExpression(path){
let node = path.node;
if(!isNaN(node.left.value) && !isNaN(node.right.value)){
let result = eval(node.left.value + node.operator + node.right.value);
path.replaceWith(types.numericLiteral(result));
if(path.parentPath&& path.parentPath.node.type == 'BinaryExpression'){
visitor.BinaryExpression.call(null,path.parentPath);
}
}
}
}
module.exports = function(babel){
return {
visitor
}
}
const babel = require('babel-core');
const result = babel.transform('const sum = 1+2+3',{
plugins:[
require('./plugin')
]
});
console.log(result.code);
import { flatten,concat } from "lodash"
转换为
import flatten from "lodash/flatten";
import concat from "lodash/flatten";
{
"presets":["react","stage-0","env"],
"plugins":[
["extract", {"library":"lodash"}],
["transform-runtime", {}]
]
}
编译顺序为首先
plugins从左往右,然后presets从右往左
let babel = require('babel-core');
let types = require('babel-types');
const visitor = {
ImportDeclaration:{
enter(path,ref={opts:{}}){
const specifiers = path.node.specifiers;
const source = path.node.source;
if(ref.opts.library == source.value && !types.isImportDefaultSpecifier(specifiers[0])){
const declarations = specifiers.map((specifier,index)=>{
return types.ImportDeclaration(
[types.importDefaultSpecifier(specifier.local)],
types.stringLiteral(`${source.value}/${specifier.local.name}`)
)
});
path.replaceWithMultiple(declarations);
}
}
}
}
module.exports = function(babel){
return {
visitor
}
}