@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

@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 ’ 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. 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- @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--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