@babel/how-to
@NicoloRibaudo NICOLÒ RIBAUDO Babel team
@NicoloRibaudo [email protected] @nicolo-ribaudo
@NicoloRibaudo 2 https://opencollective.com/babel
@babeljs https://babeljs.io @babel
@NicoloRibaudo 3 What is Babel?
@NicoloRibaudo 4 What is Babel? Babel is a JavaScript compiler
@NicoloRibaudo 5 [square] StackCheck const square = n => n ** 2; Ldar a0 ExpSmi [2], [0] Return
@NicoloRibaudo 6 [square] StackCheck const square = n => n ** 2; Ldar a0 ExpSmi [2], [0] Return
@NicoloRibaudo 7 It’s a JavaScript to JavaScript compiler
var square = function (n) { const square = n => n ** 2; return Math.pow(n, 2); };
@NicoloRibaudo 8 Compilers’ data structure: Abstract Syntax Tree (AST)
@NicoloRibaudo 9 A naive approach to compilation: Regular Expressions n => n ** 2
@NicoloRibaudo 10 A naive approach to compilation: Regular Expressions n => n ** 2
/(\w+)\s*\*\*\s*(\w+)/
Words or numbers Spaces
@NicoloRibaudo 11 A naive approach to compilation: Regular Expressions n => n ** 2 Math.pow($1, $2)
/(\w+)\s*\*\*\s*(\w+)/
Words or numbers Spaces
@NicoloRibaudo 12 A naive approach to compilation: Regular Expressions n => n ** 2 Math.pow($1, $2)
/(\w+)\s*\*\*\s*(\w+)/
Words or numbers Spaces n => Math.pow(n, 2)
@NicoloRibaudo 13 A naive approach to compilation: Regular Expressions ☹
@NicoloRibaudo 14 A naive approach to compilation: Regular Expressions
COMPLEXITY ☹ INACCURACY fn(a) ** (2 ** 3) "8" !== "2 ** 3"
@NicoloRibaudo 15 Abstract Syntax Tree n => n ** 2 ArrowFunctionExpression
params body
Identifier → n BinaryExpression → **
left right
Identifier → n NumericLiteral → 2
@NicoloRibaudo 16 Abstract Syntax Tree n => n ** 2 ArrowFunctionExpression
params body
Identifier → n BinaryExpression → **
left right
Identifier → n NumericLiteral → 2
@NicoloRibaudo 17 Abstract Syntax Tree n => n ** 2 ArrowFunctionExpression
params body
Identifier → n BinaryExpression → **
left right
Identifier → n NumericLiteral → 2
@NicoloRibaudo 18 Abstract Syntax Tree n => n ** 2 ArrowFunctionExpression
params body
Identifier → n BinaryExpression → **
left right
Identifier → n NumericLiteral → 2
@NicoloRibaudo 19 Abstract Syntax Tree n => n ** 2 ArrowFunctionExpression
params body
Identifier → n BinaryExpression → **
left right
Identifier → n NumericLiteral → 2
@NicoloRibaudo 20 Abstract Syntax Tree n => n ** 2 ArrowFunctionExpression
params body
Identifier → n BinaryExpression → **
left right
Identifier → n NumericLiteral → 2
@NicoloRibaudo 21 AST node replacement
BinaryExpression → **
left right
CallExpression ? ?
callee args
Math.pow ? ?
@NicoloRibaudo 22 AST node replacement
ArrowFunctionExpression n => Math.pow(n, 2)
params body
Identifier → n CallExpression
callee args
Math.pow Identifier → n NumericLiteral → 2
@NicoloRibaudo 23 AST node replacement
@NicoloRibaudo 24 AST node replacement
COMPLEXITY INACCURACY fn(a) ** (2 ** 3) "8" !== "2 ** 3"
? ** ? StringLiteral → "2 ** 3"
@NicoloRibaudo 25 Babel's AST { { "type": "BinaryExpression", "type": "CallExpression", "operator": "**", "callee": { /* ... */ }, "left": { "arguments": [{ "type": "Identifier", "type": "Identifier", "name": "n" "name": "n" }, }, { "right": { "type": "NumericLiteral", "type": "NumericLiteral", "value": 2 "value": 2 }] } } }
@NicoloRibaudo 26 A look inside Babel
@NicoloRibaudo 27 A look inside Babel
Input source code Original AST Transformed AST Output source code
@NicoloRibaudo 28 A look inside Babel
@babel/parser @babel/traverse @babel/generator
Input source code Original AST Transformed AST Output source code
@NicoloRibaudo 29 A look inside Babel
@babel/parser @babel/traverse @babel/generator
Input source code Original AST Transformed AST Output source code
@babel/core
@NicoloRibaudo 30 A look inside Babel: @babel/parser
@NicoloRibaudo 31 1. Lexical analysis
Transform the input source code into a list of tokens
var a = 7;
@NicoloRibaudo 32 1. Lexical analysis
Transform the input source code into a list of tokens
var a = 7;
1. Keyword var
@NicoloRibaudo 33 1. Lexical analysis
Transform the input source code into a list of tokens
var a = 7;
1. Keyword var 2. Identifier a
@NicoloRibaudo 34 1. Lexical analysis
Transform the input source code into a list of tokens
var a = 7;
1. Keyword var 2. Identifier a 3. Punctuator =
@NicoloRibaudo 35 1. Lexical analysis
Transform the input source code into a list of tokens
var a = 7;
1. Keyword var 2. Identifier a 3. Punctuator = 4. Literal 7
@NicoloRibaudo 36 1. Lexical analysis
Transform the input source code into a list of tokens
var a = 7;
1. Keyword var 2. Identifier a 3. Punctuator = 4. Literal 7 5. Punctuator ;
@NicoloRibaudo 37 1. Lexical analysis
Report errors about invalid literals or characters
@NicoloRibaudo 38 1. Lexical analysis
Report errors about invalid literals or characters
Unterminated comment /* var a = 7;
@NicoloRibaudo 39 1. Lexical analysis
Report errors about invalid literals or characters
Unterminated comment /* var a = 7;
Unexpected character '°' var a = 7°;
@NicoloRibaudo 40 1. Lexical analysis
Report errors about invalid literals or characters
Unterminated comment /* var a = 7;
Unexpected character '°' var a = 7°;
Expected number in radix 2 var a = 0b20;
@NicoloRibaudo 41 2. Syntax analysis
Transform the list of tokens into an AST
var a = 7;
@NicoloRibaudo 42 2. Syntax analysis
Transform the list of tokens into an AST
var a = 7;
VariableDeclaration → var
@NicoloRibaudo 43 2. Syntax analysis
Transform the list of tokens into an AST
var a = 7;
VariableDeclaration → var
declarations
VariableDeclarator
@NicoloRibaudo 44 2. Syntax analysis
Transform the list of tokens into an AST
var a = 7;
VariableDeclaration → var
declarations
VariableDeclarator
id
Identifier → a
@NicoloRibaudo 45 2. Syntax analysis
Transform the list of tokens into an AST
var a = 7;
VariableDeclaration → var
declarations
VariableDeclarator
id
Identifier → a
@NicoloRibaudo 46 2. Syntax analysis
Transform the list of tokens into an AST
var a = 7;
VariableDeclaration → var
declarations
VariableDeclarator
id init
Identifier → a NumericLiteral → 7
@NicoloRibaudo 47 2. Syntax analysis
Transform the list of tokens into an AST
var a = 7;
VariableDeclaration → var
declarations
VariableDeclarator
id init
Identifier → a NumericLiteral → 7
@NicoloRibaudo 48 2. Syntax analysis
Transform the list of tokens into an AST
var a = 7;
VariableDeclaration → var
declarations
VariableDeclarator
id init
Identifier → a NumericLiteral → 7
@NicoloRibaudo 49 2. Syntax analysis
Handle automatic semicolon insertion (ASI)
@NicoloRibaudo 50 2. Syntax analysis
Handle automatic semicolon insertion (ASI)
var a = foo foo.forEach(fn)
@NicoloRibaudo 51 2. Syntax analysis
Handle automatic semicolon insertion (ASI)
var a = foo var a = foo; foo.forEach(fn) foo.forEach(fn);
@NicoloRibaudo 52 2. Syntax analysis
Handle automatic semicolon insertion (ASI)
var a = foo var a = foo; foo.forEach(fn) foo.forEach(fn);
var a = foo [7].forEach(fn)
@NicoloRibaudo 53 2. Syntax analysis
Handle automatic semicolon insertion (ASI)
var a = foo var a = foo; foo.forEach(fn) foo.forEach(fn);
var a = foo var a = foo[7].forEach(n); [7].forEach(fn)
@NicoloRibaudo 54 2. Syntax analysis
Report errors about misplaced tokens
@NicoloRibaudo 55 2. Syntax analysis
Report errors about misplaced tokens
Unexpected token, expected ")" var a = double(7;
@NicoloRibaudo 56 2. Syntax analysis
Report errors about misplaced tokens
Unexpected token, expected ")" var a = double(7;
Unexpected keyword 'if' 1 + if;
@NicoloRibaudo 57 3. Semantic analysis
Check the AST respects all the static ECMAScript rules: early errors
@NicoloRibaudo 58 3. Semantic analysis
Check the AST respects all the static ECMAScript rules: early errors
Redefinition of __proto__ ({ __proto__: x, property __proto__: y, })
@NicoloRibaudo 59 3. Semantic analysis
Check that the AST respects all the static ECMAScript rules: early errors
Redefinition of __proto__ ({ __proto__: x, property __proto__: y, })
'with' in strict mode "use strict"; with (obj) {}
@NicoloRibaudo 60 3. Semantic analysis
Report errors about invalid variables, using a scope tracker
@NicoloRibaudo 61 3. Semantic analysis
Report errors about invalid variables, using a scope tracker
Identifier 'foo' has let foo = 2; already been declared let foo = 3;
@NicoloRibaudo 62 3. Semantic analysis
Report errors about invalid variables, using a scope tracker
Identifier 'foo' has let foo = 2; already been declared let foo = 3;
Export 'bar' is not { let bar = 2 } defined export { bar };
@NicoloRibaudo 63 A look inside Babel: @babel/traverse
@NicoloRibaudo 64 Declarative traversal
Algorithm: Depth-first search, in-order (enter) and out-order (exit)
traverse(ast, { CallExpression: { enter() { console.log("Function call!") } } })
@NicoloRibaudo 65 Declarative traversal
Algorithm: Depth-first search, in-order (enter) and out-order (exit)
traverse(ast, { CallExpression: { enter() { console.log("Function call!") } } })
@NicoloRibaudo 66 Declarative traversal
Algorithm: Depth-first search, in-order (enter) and out-order (exit)
traverse(ast, { CallExpression: { enter() { console.log("Function call!") } } })
@NicoloRibaudo 67 Declarative traversal
Algorithm: Depth-first search, in-order (enter) and out-order (exit)
traverse(ast, { CallExpression: { enter is the default enter() { traversal order console.log("Function call!") } } })
@NicoloRibaudo 68 Declarative traversal
Algorithm: Depth-first search, in-order (enter) and out-order (exit)
traverse(ast, { CallExpression() { enter is the default
traversal order console.log("Function call!")
} })
@NicoloRibaudo 69 Dynamic Abstract Syntax Tree
1 + fn(3) Example: 1 + [6, fn] ➔ Traverse 1 + fn(3) ➔ When we reach fn(3), during the "exit" phase, replace it with [6, fn]
@NicoloRibaudo 70 Dynamic Abstract Syntax Tree
IN 1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 CallExpression
Identifier → fn NumericLiteral → 3
@NicoloRibaudo 71 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
IN NumericLiteral → 1 CallExpression
Identifier → fn NumericLiteral → 3
@NicoloRibaudo 72 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
OUT NumericLiteral → 1 CallExpression
Identifier → fn NumericLiteral → 3
@NicoloRibaudo 73 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
IN NumericLiteral → 1 CallExpression
Identifier → fn NumericLiteral → 3
@NicoloRibaudo 74 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 CallExpression
IN Identifier → fn NumericLiteral → 3
@NicoloRibaudo 75 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 CallExpression
OUT Identifier → fn NumericLiteral → 3
@NicoloRibaudo 76 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 CallExpression
IN Identifier → fn NumericLiteral → 3
@NicoloRibaudo 77 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 CallExpression
OUT Identifier → fn NumericLiteral → 3
@NicoloRibaudo 78 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
OUT NumericLiteral → 1 CallExpression
Identifier → fn NumericLiteral → 3
@NicoloRibaudo 79 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
OUT NumericLiteral → 1 CallExpression Replace!
[6, fn] Identifier → fn NumericLiteral → 3
@NicoloRibaudo 80 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 ArrayExpression
NumericLiteral → 6 Identifier → fn
@NicoloRibaudo 81 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
IN NumericLiteral → 1 ArrayExpression
NumericLiteral → 6 Identifier → fn
@NicoloRibaudo 82 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 ArrayExpression
IN NumericLiteral → 6 Identifier → fn
@NicoloRibaudo 83 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 ArrayExpression
OUT NumericLiteral → 6 Identifier → fn
@NicoloRibaudo 84 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 ArrayExpression
IN NumericLiteral → 6 Identifier → fn
@NicoloRibaudo 85 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 ArrayExpression
OUT NumericLiteral → 6 Identifier → fn
@NicoloRibaudo 86 Dynamic Abstract Syntax Tree
1 + fn(3) BinaryExpression → + 1 + [6, fn]
OUT NumericLiteral → 1 ArrayExpression
NumericLiteral → 6 Identifier → fn
@NicoloRibaudo 87 Dynamic Abstract Syntax Tree
1 + fn(3) OUT BinaryExpression → + 1 + [6, fn]
NumericLiteral → 1 ArrayExpression
NumericLiteral → 6 Identifier → fn
@NicoloRibaudo 88 Scope analysis
let MAX = 5; export function isValid(val, big) { if (big) { const MAX = 10; return val < MAX; } MAX += 1; return val < MAX; }
@NicoloRibaudo 89 Scope analysis
1. Collect different scopes let MAX = 5; export function isValid(val, big) { if (big) { const MAX = 10; return val < MAX; } MAX += 1; return val < MAX; }
@NicoloRibaudo 90 Scope analysis
1. Collect different scopes let MAX = 5;
Program export function isValid(val, big) { if (big) { const MAX = 10; return val < MAX;
} MAX += 1; return val < MAX; }
@NicoloRibaudo 91 Scope analysis
1. Collect different scopes let MAX = 5;
Program export function isValid(val, big) { if (big) { const MAX = 10; return val < MAX; Function
} MAX += 1; return val < MAX; }
@NicoloRibaudo 92 Scope analysis
1. Collect different scopes let MAX = 5;
Program export function isValid(val, big) { if (big) { const MAX = 10; return val < MAX; Function
} MAX += 1; return val < MAX; Block }
@NicoloRibaudo 93 Scope analysis
2. Collect declarations let MAX = 5;
Program export function isValid(val, big) { if (big) { const MAX = 10; return val < MAX; Function
} MAX += 1; return val < MAX; Block }
@NicoloRibaudo 94 Scope analysis
2. Collect declarations let MAX = 5;
MAX Program export function isValid(val, big) { if (big) { const MAX = 10; return val < MAX; Function
} MAX += 1; return val < MAX; Block }
@NicoloRibaudo 95 Scope analysis
2. Collect declarations let MAX = 5;
MAX Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; Function
} MAX += 1; return val < MAX; Block }
@NicoloRibaudo 96 Scope analysis
2. Collect declarations let MAX = 5;
MAX Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val Function
} MAX += 1; return val < MAX; Block }
@NicoloRibaudo 97 Scope analysis
2. Collect declarations let MAX = 5;
MAX Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val Function
} big MAX += 1; return val < MAX; Block }
@NicoloRibaudo 98 Scope analysis
2. Collect declarations let MAX = 5;
MAX Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val Function
} big MAX += 1; return val < MAX; MAX Block }
@NicoloRibaudo 99 Scope analysis
3. Collect references let MAX = 5;
MAX Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val Function
} big MAX += 1; return val < MAX; MAX Block }
@NicoloRibaudo 100 Scope analysis
3. Collect references let MAX = 5;
MAX Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val Function
} big 1 MAX += 1; return val < MAX; MAX Block }
@NicoloRibaudo 101 Scope analysis
3. Collect references let MAX = 5;
MAX Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val 1 Function
} big 1 MAX += 1; return val < MAX; MAX Block }
@NicoloRibaudo 102 Scope analysis
3. Collect references let MAX = 5;
MAX Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val 1 Function
} big 1 MAX += 1; return val < MAX; MAX 1 Block }
@NicoloRibaudo 103 Scope analysis
3. Collect references let MAX = 5;
MAX 1 Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val 1 Function
} big 1 MAX += 1; return val < MAX; MAX 1 Block }
@NicoloRibaudo 104 Scope analysis
3. Collect references let MAX = 5;
MAX 1 Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val 2 Function
} big 1 MAX += 1; return val < MAX; MAX 1 Block }
@NicoloRibaudo 105 Scope analysis
3. Collect references let MAX = 5;
MAX 2 Program export function isValid(val, big) { if (big) { isValid const MAX = 10; return val < MAX; val 2 Function
} big 1 MAX += 1; return val < MAX; MAX 1 Block }
@NicoloRibaudo 106 Scope analysis
3. Collect references let MAX = 5;
MAX 2 Program export function isValid(val, big) { if (big) { isValid 1 const MAX = 10; return val < MAX; val 2 Function
} big 1 MAX += 1; return val < MAX; MAX 1 Block }
@NicoloRibaudo 107 Scope analysis
3. Collect references let MAX = 5;
MAX 2 Program export function isValid(val, big) { if (big) { isValid 1 const MAX = 10; return val < MAX; val 2 Function
} big 1 MAX += 1; return val < MAX; MAX 1 Block }
@NicoloRibaudo 108 Utilities
NODES (AST)
Search
Introspection
Evaluation
Insertion
Removal
Replacement
@NicoloRibaudo 109 Utilities
NODES (AST) BINDINGS (SCOPE)
Search Validation Introspection Tracking Evaluation Creation Insertion Renaming Removal
Replacement
@NicoloRibaudo 110 A look inside Babel: @babel/generator
@NicoloRibaudo 111 Transform AST to source code
Insert parentheses, comments and indentation where needed Fast and opinionated
@NicoloRibaudo 112 Transform AST to source code
Insert parentheses, comments and indentation where needed Fast and opinionated
⚠ IT'S NOT A PRETTY PRINTER ⚠
@NicoloRibaudo 113 A look inside Babel: @babel/core
@NicoloRibaudo 114 Babel's entrypoint
Used by Babel integrations
@babel/cli @babel/register babel-loader gulp-plugin-babel babelify Parcel
@NicoloRibaudo 115 Babel's entrypoint
Used by Babel integrations Merges configuration sources
@babel/cli babel.config.js @babel/register .babelrc babel-loader package.json gulp-plugin-babel programmatic options babelify Parcel
@NicoloRibaudo 116 Babel's entrypoint
Used by Babel integrations Merges configuration sources
@babel/cli babel.config.js @babel/register .babelrc babel-loader package.json gulp-plugin-babel programmatic options babelify Parcel Runs plugins and presets
@NicoloRibaudo 117 Bonus package: @babel/types
@NicoloRibaudo 118 Nodes validation
Is this node an expression? t.isExpression(node)
@NicoloRibaudo 119 Nodes validation
Is this node an expression? t.isExpression(node)
Is this node an identifier whose name is "test"? t.isIdentifier(node, { name: "test" })
@NicoloRibaudo 120 Nodes validation
Is this node an expression? t.isExpression(node)
Is this node an identifier whose name is "test"? t.isIdentifier(node, { name: "test" })
Is this node a sum whose left operand is the child node? t.isBinaryExpression(node, { operator: "+", left: child })
@NicoloRibaudo 121 Nodes building
Given a varId node, how to increment the value it represents by 2? { type: "AssignmentExpression", t.assignmentExpression( operator: "+=", "+=", right: varId, varId, left: { t.numericLiteral(2) type: "NumericLiteral", ); value: 2, }, }
@NicoloRibaudo 122 Nodes building
Given a varId node, how to increment the value it represents by 2? { type: "AssignmentExpression", t.assignmentExpression( operator: "+=", "+=", right: varId, varId, left: { t.numericLiteral(2) type: "NumericLiteral", ); value: 2, }, }
@NicoloRibaudo 123 Nodes building
Given a varId node, how to increment the value it represents by 2? { type: "AssignmentExpression", t.assignmentExpression( operator: "+=", "+=", right: varId, varId, left: { t.numericLiteral(2) type: "NumericLiteral", ); value: 2, }, }
@NicoloRibaudo 124 Nodes building
Given a varId node, how to increment the value it represents by 2? { type: "AssignmentExpression", t.assignmentExpression( operator: "+=", "+=", right: varId, varId, left: { t.numericLiteral(2) type: "NumericLiteral", ); value: 2, }, }
@NicoloRibaudo 125 Nodes building
Given a varId node, how to increment the value it represents by 2? { type: "AssignmentExpression", t.assignmentExpression( operator: "+=", "+=", right: varId, varId, left: { t.numericLiteral(2) type: "NumericLiteral", ); value: 2, }, }
@NicoloRibaudo 126 Nodes building
Given a varId node, how to increment the value it represents by 2? { type: "AssignmentExpression", t.assignmentExpression( operator: "+=", "+=", right: varId, varId, left: { t.numericLiteral(2) type: "NumericLiteral", ); value: 2, }, }
@NicoloRibaudo 127 Nodes building
Given a varId node, how to increment the value it represents by 2? { type: "AssignmentExpression", t.assignmentExpression( operator: "+=", "+=", right: varId, varId, left: { t.numericLiteral(2) type: "NumericLiteral", ); value: 2, }, }
@NicoloRibaudo 128 Bonus package: @babel/template
@NicoloRibaudo 129 High level AST building
Given a varId node referencing an array, how to increment each of its elements by 2 and then take only the values greater than 10?
t.assignmentExpression("=", varId, t.callExpression( t.memberExpression(t.callExpression( t.memberExpression(varId, t.identifier("map")), [t.arrowFunctionExpression([t.identifier("val")], t.binaryExpression("+", t.identifier("val"), t.numericLiteral(2)) )] ), t.identifier("filter")), [t.arrowFunctionExpression([t.identifier("val")], t.binaryExpression(">", t.identifier("val"), t.numericLiteral(10)) )] ) )
@NicoloRibaudo 130 High level AST building
Given a varId node referencing an array, how to increment each of its elements by 2 and then take only the values greater than 10?
template.expression.ast` ${varId} = ${varId} .map(val => val + 2) .filter(val => val > 10) `
@NicoloRibaudo 131 High level AST building
Different parsing goals
template.expression
template.statement
template.statements
template.program
@NicoloRibaudo 132 High level AST building
Different parsing goals Immediate usage...
ast = template.*.ast`${val} * 2` template.expression
template.statement … or deferred usage
template.statements build = template.*(`%%val%% * 2`) // ... template.program ast = build({ val })
@NicoloRibaudo 133 Plugins
@NicoloRibaudo 134 Everything is a plugin
ECMAScript features @babel/plugin-transform-classes
ECMAScript proposals @babel/plugin-proposal-optional-chaining
ECMAScript extensions @babel/plugin-transform-typescript @babel/plugin-transform-react-jsx
Optimization @babel/plugin-transform-runtime
@NicoloRibaudo 135 Everything is a plugin
babel-plugin-module-resolver
babel-plugin-macros babel-plugin-transform-define babel-plugin-emotion babel-plugin-inferno babel-plugin-add-module-exports babel-plugin-istanbul babel-plugin-react-css-modules babel-plugin-react-intl-auto babel-plugin-transform-async-to-promises
@NicoloRibaudo 136 How to create a plugin
@NicoloRibaudo 137 1. Create a function function myPlugin(babel, options) { return { name: "my-plugin", visitor: { CallExpression(path) { /* ... */ } }, manipulateOptions(babelOptions) {}, inherits: require("another-plugin"), }; }
@NicoloRibaudo 138 1. Create a function function myPlugin(babel, options) { return { The first parameter exposes all name: "my-plugin", the public API and utilities visitor: { // @babel/types CallExpression(path) { /* ... */ } const t = babel.types; }, manipulateOptions(babelOptions) {}, inherits: require("another-plugin"), }; }
@NicoloRibaudo 139 1. Create a function function myPlugin(babel, options) { return { The second parameter contains name: "my-plugin", the options for this plugin visitor: { defined in the user's config CallExpression(path) { /* ... */ } }, manipulateOptions(babelOptions) {}, inherits: require("another-plugin"), }; }
@NicoloRibaudo 140 2. Choose a name Required function myPlugin(babel, options) { return { Should match the plugin name: "my-plugin", package name visitor: { babel-plugin-my-plugin CallExpression(path) { /* ... */ } }, manipulateOptions(babelOptions) {}, inherits: require("another-plugin"), }; }
@NicoloRibaudo 141 3. Define traversal visitor Optional function myPlugin(babel, options) { return { name: "my-plugin", visitor: { CallExpression(path) { /* ... */ } }, manipulateOptions(babelOptions) {}, inherits: require("another-plugin"), }; }
@NicoloRibaudo 142 4. Modify Babel options Optional function myPlugin(babel, options) { return { It also handles options for name: "my-plugin", @babel/parser and visitor: { @babel/generator CallExpression(path) { /* ... */ } opts.parserOpts }, opts.generatorOprs manipulateOptions(babelOptions) {}, inherits: require("another-plugin"), }; }
@NicoloRibaudo 143 5. Extend another plugin Optional function myPlugin(babel, options) { return { name: "my-plugin", visitor: { CallExpression(path) { /* ... */ } }, manipulateOptions(babelOptions) {}, inherits: require("another-plugin"), }; }
@NicoloRibaudo 144 A node with superpowers: NodePath
@NicoloRibaudo 145 NodePath
Transformations need context and ergonomics for AST manipulation
@NicoloRibaudo 146 NodePath
Transformations need context and ergonomics for AST manipulation
@NicoloRibaudo 147 NodePath
Transformations need context and ergonomics for AST manipulation
Scope Tracker
Program
Block
@NicoloRibaudo 148 NodePath
Transformations need context and ergonomics for AST manipulation
Scope Tracker
Program
Block
@NicoloRibaudo 149 NodePath
@NicoloRibaudo 150 NodePath
path.node Get the original, unwrapped, node
@NicoloRibaudo 151 NodePath
path.node Get the original, unwrapped, node
path.parentPath Get the path of parent node … path.get("body.0.id") .. or of a child node
@NicoloRibaudo 152 NodePath
path.node Get the original, unwrapped, node
path.parentPath Get the path of parent node … path.get("body.0.id") .. or of a child node
path.scope Get the scope of the current path
@NicoloRibaudo 153 NodePath
path.node Get the original, unwrapped, node
path.parentPath Get the path of parent node … path.get("body.0.id") .. or of a child node
path.scope Get the scope of the current path
path.replaceWith(node) Replace the current node with another one … path.insertBefore(...nodes) … or just insert some new nodes before … path.insertAfter(...nodes) … or after
@NicoloRibaudo 154 NodePath
path.node Get the original, unwrapped, node
path.parentPath Get the path of parent node … path.get("body.0.id") .. or of a child node
path.scope Get the scope of the current path
path.replaceWith(node) Replace the current node with another one … path.insertBefore(...nodes) … or just insert some new nodes before … path.insertAfter(...nodes) … or after
path.toString() Call @babel/generator, useful when debugging
@NicoloRibaudo 155 Scope
@NicoloRibaudo 156 Scope
scope.buildUndefinedNode() Create a node which evaluates to undefined t.idenfier("undefined") is not safe, because users can have var undefined = 2;
@NicoloRibaudo 157 Scope
scope.buildUndefinedNode() Create a node which evaluates to undefined t.idenfier("undefined") is not safe, because users can have var undefined = 2; scope.generateUidIdentifier() Generate an identifier which is guaranteed not to conflict with existing variables
scope.push({ id }) Declare a variable in the current scope
@NicoloRibaudo 158 Scope
scope.buildUndefinedNode() Create a node which evaluates to undefined t.idenfier("undefined") is not safe, because users can have var undefined = 2; scope.generateUidIdentifier() Generate an identifier which is guaranteed not to conflict with existing variables
scope.push({ id }) Declare a variable in the current scope
scope.getBinding(name) Get information about the currently defined scope.hasBinding(name) bindings
@NicoloRibaudo 159 Case study Optional chaining proposal
obj?.prop
@NicoloRibaudo 160 Optional chaining
Optionally get properties from possibly null or undefined objects:
var street = user.address && user.address.street;
var street = user.address?.street;
@NicoloRibaudo 161 Optional chaining
Optionally get properties from possibly null or undefined objects:
var street = user.address && user.address.street;
var street = user.address?.street;
Also works with nested properties:
var _tmp = a.b && a.b[3].c(x); var result = _tmp && _tmp.d;
var result = a.b?.[3].c(x)?.d;
@NicoloRibaudo 162 @NicoloRibaudo 163 NICOLÒ RIBAUDO Babel team
@NicoloRibaudo [email protected] @nicolo-ribaudo
@NicoloRibaudo 164 Links
➔ Babel AST specification https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md
➔ @babel/types builders API https://babeljs.io/docs/en/babel-types
➔ Optional chaining proposal https://github.com/tc39/proposal-optional-chaining
➔ babel-plugin-tester https://github.com/babel-utils/babel-plugin-tester
➔ GitHub repository https://github.com/nicolo-ribaudo/conf-holyjs-moscow-2019
@NicoloRibaudo 165