BabelJS - James Kyle at Modern Web UI

Preview:

Citation preview

@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

Recommended