
今天,我将与您分享有关如何编写babel插件的分步指南。您可以使用此知识来自动执行编辑,重构或代码生成。
什么是通天塔?
Babel是一个JavaScript编译器,主要用于将ECMAScript 2015+代码向后转换为与当前和较旧的浏览器或环境向后兼容。Babel使用插件系统来转换代码,因此任何人都可以编写自己的插件。
在开始编写插件之前,我们需要找出什么是AST(抽象语法树)。
什么是AST?
我不确定我能比在Web上出色的材料中所做的更好地解释这一点:
总而言之,AST是代码的树形结构。对于JavaScript,AST JavaScript遵循estree规范。
AST是代码的结构和含义。这样,AST允许像babel这样的编译器理解代码并在其中进行特定的有意义的转换。
, AST, .
babel ?
, babel :
import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';
const code = 'const n = 1';
const ast = parse(code);
traverse(ast, {
enter(path) {
if (path.isIdentifier({ name: 'n' })) {
path.node.name = 'x';
}
},
});
const output = generate(ast, code);
console.log(output.code);
@babel/core
. @babel/parser
, @babel/traverse
, @babel/generator
@babel/core
.
AST, AST .
-> AST -> AST ->
, babel API :
import babel from '@babel/core';
const code = 'const n = 1';
const output = babel.transformSync(code, {
plugins: [
function myCustomPlugin() {
return {
visitor: {
Identifier(path) {
if (path.isIdentifier({ name: 'n' })) {
path.node.name = 'x';
}
},
},
};
},
],
});
console.log(output.code);
babel n
x
, ?
myCustomPlugin
. npm babel !
: ", babel , ...". , .
1.
:
:
function greet(name) {
return 'Hello ' + name;
}
console.log(greet('tanhauhau'));
:
function teerg(eman) {
return 'H' + 'e' + 'l' + 'l' + 'o' + ' ' + eman;
}
console.log(teerg('t' + 'a' + 'n' + 'h' + 'a' + 'u' + 'h' + 'a' + 'u'));
console.log
, , . ( !)
2. AST
babel AST explorer, AST:

AST, , .
, :
- Identifier
- StringLiteral .
3. AST
babel AST explorer, :

AST .
'H' + 'e' + 'l' + 'l' + 'o' + ' ' + eman
(BinaryExpression
) (StringLiteral
)
4.
:
function myCustomPlugin() {
return {
visitor: {
Identifier(path) {
},
},
};
}
visitor.
, babel AST . visitor
, , babel .
visitor
:
function myCustomPlugin() {
return {
visitor: {
Identifier(path) {
console.log('');
},
StringLiteral(path) {
console.log(' ');
},
},
};
}
,
babel :
, Identifer(path) {}
. path
node
, ?
babel, path
, , , , .. path
, replaceWith
, insertBefore
, remove
, AST .
path
babel Jamie Kyle
.
babel AST explorer, name
, , :
Identifier(path) {
path.node.name = path.node.name
.split('')
.reverse()
.join('');
}
:
function teerg(eman) {
return 'Hello ' + eman;
}
elosnoc.gol(teerg('tanhauhau'));
console.log . ?
AST:

console.log
MemberExpression
, c object
— "console"
property
— "log"
, MemberExpression
, :
if (
!(
path.parentPath.isMemberExpression() &&
path.parentPath
.get('object')
.isIdentifier({ name: 'console' }) &&
path.parentPath.get('property').isIdentifier({ name: 'log' })
)
) {
path.node.name = path.node.name
.split('')
.reverse()
.join('');
}
}
, !
function teerg(eman) {
return 'Hello ' + eman;
}
console.log(teerg('tanhauhau'));
, Identifier
console.log
MemberExpression
? Identifier.name === 'console' || Identifier.name === 'log'
?
, console
or log
:
const log = 1;
isMemberExpression
isIdentifier
? @babel/types isXxxx
, anyTypeAnnotation
isAnyTypeAnnotation
. ,
BinaryExpression
.
AST, [@babel/types](https://babeljs.io/docs/en/babel-types)
:
StringLiteral(path) {
const newNode = path.node.value
.split('')
.map(c => babel.types.stringLiteral(c))
.reduce((prev, curr) => {
return babel.types.binaryExpression('+', prev, curr);
});
path.replaceWith(newNode);
}
StringLiteral
, path.node.value
, c StringLiteral
BinaryExpression
. StringLiteral
.
… ! , :
RangeError: Maximum call stack size exceeded
?
, StringLiteral
StringLiteral
, StringLiteral
. StringLiteral
StringLiteral
, babel , , .
, babel StringLiteral
newNode
, ?
path.skip()
:
StringLiteral(path) {
const newNode = path.node.value
.split('')
.map(c => babel.types.stringLiteral(c))
.reduce((prev, curr) => {
return babel.types.binaryExpression('+', prev, curr);
});
path.replaceWith(newNode);
path.skip();
}
… !
babel:
const babel = require('@babel/core');
const code = `
function greet(name) {
return 'Hello ' + name;
}
console.log(greet('tanhauhau')); // Hello tanhauhau
`;
const output = babel.transformSync(code, {
plugins: [
function myCustomPlugin() {
return {
visitor: {
StringLiteral(path) {
const concat = path.node.value
.split('')
.map(c => babel.types.stringLiteral(c))
.reduce((prev, curr) => {
return babel.types.binaryExpression('+', prev, curr);
});
path.replaceWith(concat);
path.skip();
},
Identifier(path) {
if (
!(
path.parentPath.isMemberExpression() &&
path.parentPath
.get('object')
.isIdentifier({ name: 'console' }) &&
path.parentPath.get('property').isIdentifier({ name: 'log' })
)
) {
path.node.name = path.node.name
.split('')
.reverse()
.join('');
}
},
},
};
},
],
});
console.log(output.code);
, :
- AST
- AST
, babel GitHub babel.
https://github.com/babel/babel, babel-plugin-transform-*
babel-plugin-proposal-*
, -, babel nullish coalescing operator, optional chaining .