
рдЖрдЬ рдореИрдВ рдЖрдкрдХреЗ рд╕рд╛рде рдПрдХ рдХрджрдо-рджрд░-рдЪрд░рдг рдорд╛рд░реНрдЧрджрд░реНрд╢рд┐рдХрд╛ рд╕рд╛рдЭрд╛ рдХрд░реВрдБрдЧрд╛ рдХрд┐ рдЖрдк рдЕрдкрдиреЗ рдмреЗрдмреЗрд▓ рдкреНрд▓рдЧрдЗрди рдХреЛ рдХреИрд╕реЗ рд▓рд┐рдЦреЗрдВ ред рдЖрдк рдЗрд╕ рдЬреНрдЮрд╛рди рдХрд╛ рдЙрдкрдпреЛрдЧ рд╕рдВрдкрд╛рджрди, рд░реАрдлрд╝реИрдХреНрдЯрд░рд┐рдВрдЧ рдпрд╛ рдХреЛрдб рдЬреЗрдирд░реЗрд╢рди рдХреЛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдХреЛрд▓рд╛рд╣рд▓ рдХреНрдпрд╛ рд╣реИ?
рдмреИрдмреЗрд▓ рдПрдХ рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрдВрдкрд╛рдЗрд▓рд░ рд╣реИ рдЬрд┐рд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдореБрдЦреНрдпрддрдГ ECMAScript 2015+ рдХреЛрдб рдХреЛ рд╡рд░реНрддрдорд╛рди рдФрд░ рдкреБрд░рд╛рдиреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдпрд╛ рд╡рд╛рддрд╛рд╡рд░рдг рдХреЗ рд╕рд╛рде рдкреАрдЫреЗ рдХреА рдУрд░ рдХрдиреНрд╡рд░реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдХреЛрдмреЗрд▓ рдХреЛрдб рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреНрд▓рдЧрдЗрди рд╕рд┐рд╕реНрдЯрдо рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдХреЛрдИ рднреА рдЕрдкрдирд╛ рд╕реНрд╡рдпрдВ рдХрд╛ рдкреНрд▓рдЧрдЗрди рд▓рд┐рдЦ рд╕рдХрддрд╛ рд╣реИред
, AST( ).
AST?
, :
, 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 .