@thejameskyle
git.io/babel-handbook
git.io/i18n
Babel Sucks
B S
What is Babel?
A general purpose JavaScript compiler.
code()
0101010
1010100
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
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
{
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

BabelJS - James Kyle at Modern Web UI