babeljs - james kyle at modern web ui
TRANSCRIPT
@thejameskyle
git.io/babel-handbook
git.io/i18n
Babel Sucks
B S
What is Babel?
A general purpose JavaScript compiler.
code()
01010101010100
High Level
Low Level
Static Analysis
Abstract Syntax Tree (AST)
function square(n) { return n * n;}
- FunctionDeclaration: - id: - Identifier: - name: square - params [1] - Identifier - name: n - body:
{ type: "FunctionDeclaration", id: { type: "Identifier", name: "square" }, params: [{ type: "Identifier", name: "n" }], body: { type: "BlockStatement", body: [{ type: "ReturnStatement", argument: { type: "BinaryExpression", operator: "*", left: { type: "Identifier",
{ type: "FunctionDeclaration", id: {...}, params: [...], body: {...}}
{ type: "Identifier", name: ...}
{ type: "BinaryExpression", operator: ..., left: {...}, right: {...}}
interface Node { type: string;}
{ type: "FunctionDeclaration", id: {...}, params: [...], body: {...}}
Parsing
Transforming
Generating
Parsing:
1. Lexical Analysis
2. Syntactic Analysis
Lexical Analysis
n * n;
[ { type: { ... }, value: "n" }, { type: { ... }, value: "*" }, { type: { ... }, value: "n" }]
{ type: { label: 'name', keyword: undefined, beforeExpr: false, startsExpr: true, rightAssociative: false, isLoop: false, isAssign: false, prefix: false, postfix: false, binop: null, updateContext: null }, ...}
Syntactic Analysis
Parsing
Transforming
Generating
Parsing
Transformating
Generating
Traversal
{ type: "FunctionDeclaration", id: {...}, params: [...], body: {...}}
{ type: "FunctionDeclaration", id: { type: "Identifier", name: "square" }, params: [...], body: {...}}
{ type: "FunctionDeclaration", id: {...}, params: [{ type: "Identifier", name: "n" }], body: {...}}
{ type: "FunctionDeclaration", id: {...}, params: [], body: { type: "BlockStatement", body: [...] }}
{ type: "BlockStatement", body: [{ type: "ReturnStatement", argument: {...} }]}
{ type: "ReturnStatement", argument: { type: "BinaryExpression", operator: "*", left: {...}, right: {...} }}
{ type: "BinaryExpression", operator: "*", left: {...}, right: {...}}
{ type: "BinaryExpression", operator: "*", left: { type: "Identifier", name: "n" }, right: {...}}
{ type: "BinaryExpression", operator: "*", left: {...}, right: { type: "Identifier", name: "n" }}
Visitors
const MyVisitor = { Identifier() { console.log("Called!"); }};
function square(n) { return n * n;}
function square(n) { return n * n;}
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
- FunctionDeclaration - Identifier (id) - Identifier (params[0]) - BlockStatement (body) - ReturnStatement (body) - BinaryExpression (argument) - Identifier (left) - Identifier (right)
const MyVisitor = { Identifier: { enter() { console.log("Entered!"); }, exit() { console.log("Exited!"); } }};
Paths
{ type: "FunctionDeclaration", id: { type: "Identifier", name: "square" }, ...}
{ parent: { type: "FunctionDeclaration", id: {...}, .... }, node: { type: "Identifier", name: "square" }}
{ parent: { type: "FunctionDeclaration", id: {...}, .... }, node: { type: "Identifier", name: "square" }}
Paths in visitors
const MyVisitor = { Identifier(path) { console.log("Visiting: " + path.node.name); }};
Scopes
// global scope
function scopeOne() { // scope 1
function scopeTwo() { // scope 2 }}
var global = "I am in the global scope";
function scopeOne() { var one = "I am in scope one";
function scopeTwo() { var two = "I am in scope two"; }}
function scopeOne() { var one = "I am in scope one";
function scopeTwo() { one = "I updating a ref in scope one"; }}
function scopeOne() { var one = "I am in scope one";
function scopeTwo() { var one = "I am creating a new `one`"; }}
Scopes
{ path: path, block: path.node, parentBlock: path.parent, parent: parentScope, bindings: [...]}
Bindings
function scopeOnce() { var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() { ref; // This is a reference to a binding }}
{ identifier: node, scope: scope, path: path, kind: 'var',
referenced: true, references: 3, referencePaths: [path, path, path],
constant: false, constantViolations: [path]}
The many modules of Babel
Babel Types
defineType("BinaryExpression", { builder: ["operator", "left", "right"], fields: { operator: { validate: assertValueType("string") }, left: { validate: assertNodeType("Expression") }, right: { validate: assertNodeType("Expression") } }, visitor: ["left", "right"], aliases: ["Binary", "Expression"]});
defineType("BinaryExpression", { builder: ["operator", "left", "right"], fields: { operator: { validate: assertValueType("string") }, left: { validate: assertNodeType("Expression") }, right: { validate: assertNodeType("Expression") } }, visitor: ["left", "right"], aliases: ["Binary", "Expression"]});
t.binaryExpression( "*", t.identifier("a"), t.identifier("b"));
{ type: "BinaryExpression", operator: "*", left: { type: "Identifier", name: "a" }, right: { type: "Identifier", name: "b" }}
a * b
Babel Types
That's Babel.
Babel Sucks
Babel doesn't do anything in the least efficient way possible.
function babel(code) { return code;}
function babel(code) { return code}
const babel = code => code
Plugins!
Babel is only as good as the ecosystem built around it
You.
Writing your first Babel Plugin.
export default function(babel) { // plugin contents}
export default function(babel) { var t = babel.types; // plugin contents};
export default function(babel) { var t = babel.types; return { visitor: { // visitor contents } };};
foo === bar;
{ type: "BinaryExpression", operator: "===", left: { type: "Identifier", name: "foo" }, right: { type: "Identifier", name: "bar" }}
export default function(babel) { var t = babel.types; return { visitor: { // visitor contents } };};
export default function(babel) { var t = babel.types; return { visitor: { BinaryExpression(path) { // ... } } };};
export default function(babel) { var t = babel.types; return { visitor: { BinaryExpression(path) { if (path.node.operator !== "===") { return; } // ... } } };};
export default function(babel) { var t = babel.types; return { visitor: { BinaryExpression(path) { if (path.node.operator !== "===") { return; } path.node.left = t.identifier("sebmck"); } } };};
sebmck === bar;
export default function(babel) { var t = babel.types; return { visitor: { BinaryExpression(path) { if (path.node.operator !== "===") { return; } path.node.left = t.identifier("sebmck"); path.node.right = t.identifier("dork"); } } };};
sebmck === dork;
export default function(babel) { var t = babel.types; return { visitor: { BinaryExpression(path) { if (path.node.operator !== "===") { return; } path.node.left = t.identifier("sebmck"); path.node.right = t.identifier("dork"); } } };};
Fin