David Bashford
UI/Node Engineer
Babel is a JavaScript transpiler (compiler) that turns bleeding edge JavaScript into JavaScript a browser can execute.
In the Ember UI (asoc/sa-ui), babel is run whenever ember-cli is run on the codebase
With `ember test`, `ember exam`, `ember serve`, before any of the UI code is used, it is first transpiled by babel
ember-cil reports on the performance of the babel compilation when the ember task is complete
In e2e, it is used twice. Once to allow the e2e framework code...
...which runs in node.js, not in the browser...
to be in modern JavaScript.
This isn't for the tests themselves, which execute in the browser
https://github.rsa.lab.emc.com/asoc/nw-e2e/blob/master/index.js#L1-L3
It is also used by the framework to compile the tests, 1 file at a time, from the `test-src` directory to the `test-lib` directory. This happens before the tests run.
So Babel turns JavaScript with cool modern features into JavaScript that functions in all the browsers.
But who is responsible for inventing the cool modern features?
"TC39 is the committee that evolves JavaScript. Its members are companies. TC39 meets regularly, its meetings are attended by delegates that members send and by invited experts."
The companies that wish to, send representatives to participate in committee meetings.
That is who evolves JavaScript...
but how is JavaScript evolved?
They go through a multi-stage process that can, if they are not rejected, take years to result in a approved update to the specification
Language Proposals can be made by anyone
all major semantics, syntax and API are covered, but TODOs, placeholders and editorial issues are expected
Committee approvals take place
Spec editors approve as well
No TODOs, complete specification text
Tests written
Compatible implementations exist
Just minor updates since then
What language features are we using that Babel transpiles for us?
var add = function(a, b) {
if (b === undefined) {
b = 5;
}
return a + b;
};
add(1, 10) // 11
add(1) // 6
var add = function(a, b = 5) {
return a + b;
};
add(1, 10) // 11
add(1) // 6
var add = function(obj) {
return obj.a + obj.b;
};
add({ a: 1, b: 2 }) // 3
var add = function({ a, b }) {
return a + b;
};
add({ a: 1, b: 2 }) // 3
var add = function(arr) {
return arr[0] + arr[1];
};
add([1, 2]) // 3
var add = function([a, b]) {
return a + b;
};
add([1, 2]) // 3
var add = function({ a: left, b: right }) {
return left + right;
};
add({ a: 1, b: 2 }) // 3
var add = function({ a: left, b: right = 5}) {
return left + right;
};
add({ a: 1 }) // 6
var foo = [1, 2, 3];
var bar = [4, 5, 6];
var baz = [...foo, ...bar];
// [1, 2, 3, 4, 5, 6]
var foo = [1, 2, 3];
var bar = [4, 5, 6];
var baz = foo.concat(bar);
// [1, 2, 3, 4, 5, 6]
var foo = [1, 2, 3];
var bar = [4, 5, 6];
var baz = [
0,
...foo,
...bar,
7,
8,
9
];
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
var foo = { a: 1, b: 2, c: 3 };
var bar = { c: 4, d: 5 };
Object.assign(foo, bar);
// foo = { a: 1, b: 2, c: 4, d: 5 }
var foo = { a: 1, b: 2, c: 3 };
var bar = { c: 4, d: 5 };
var merge = function(obj1, obj2) {
for (var attr in obj2) {
obj1[attr] = obj2[attr];
}
};
merge(foo, bar);
// foo = { a: 1, b: 2, c: 4, d: 5 }
var foo = { a: 1, b: 2, c: 3 };
var bar = { c: 4, d: 5 };
var baz = {
...foo,
...bar
};
// foo = { a: 1, b: 2, c: 4, d: 5 }
var foo = { a: 1, b: 2, c: 3 };
var bar = { c: 4, d: 5 };
var baz = {
...foo,
...bar
e: 7,
r: 19
};
// foo = {
// a: 1, b: 2, c: 4, d: 5, e: 7, r: 19
// }
var foo = 1;
var bar = 2;
var obj = {
foo: foo,
bar: bar
};
var foo = 1;
var bar = 2;
var obj = {
foo,
bar
};
var sum = function(...nums) {
return nums.reduce(function(a, b) {
return a + b;
});
}
var sum = (...nums) => {
return nums.reduce((a, b) => {
return a + b;
});
}
var sum = (...nums) => nums.reduce((a, b) => a + b);
Arrow functions also preserve the calling context for `this`, but we don't have time to talk about JavaScript context.
And those are just some of the ones we use a bunch, there's also...
JSON Strings
Async Generator Iterators
https://babeljs.io/docs/en/babel-plugin-proposal-async-generator-functions
Optional Catch Binding
Regex Unicode Escapes
https://github.com/tc39/proposal-regexp-unicode-property-escapes
Block Scoped Functions
https://babeljs.io/docs/en/babel-plugin-transform-block-scoped-functions
Computed Properties
https://babeljs.io/docs/en/next/babel-plugin-transform-computed-properties.html
Regex dotall
Allow Duplicate Object Keys
https://babeljs.io/docs/en/babel-plugin-transform-duplicate-keys
Exponential Operator
https://babeljs.io/docs/en/babel-plugin-transform-exponentiation-operator
Transform Literals
Member Expression Keywords
https://babeljs.io/docs/en/babel-plugin-transform-member-expression-literals
Named Regex Capture Groups
Un-Reserved words
https://babeljs.io/docs/en/babel-plugin-transform-reserved-words
Let/Const block scoping
https://babeljs.io/docs/en/babel-plugin-transform-block-scoping
Parameter Rest
https://babeljs.io/docs/en/babel-plugin-transform-parameters
Unicode-aware Regex
https://babeljs.io/docs/en/babel-plugin-transform-unicode-regex
Template Literals
https://babeljs.io/docs/en/babel-plugin-transform-template-literals
Those are all the things that, right now, we are transpiling.
If Babel encounters these new features in your JavaScript, it will transform your JavaScript into something else that performs the desired task, but works in all browsers
We use it
Babel also has a polyfill
It adds even more
A polyfill is a piece of code used to provide modern functionality on older browsers that do not natively support it.
Polyfills add the missing feature by including it in the browser, not by changing your code
`Array.includes` doesn't exist in many browsers, but you can add `Array.includes` by altering the Array prototype
if(!Array.prototype.includes){
Array.prototype.includes = function(search){
return !!~this.indexOf(search);
}
}
Make this the first code executed, and Array.includes becomes available to all code that follows
Babel's polyfill comes from a library called "core-js". Babel 6 uses core-js 2, which adds a bunch more language features.
String.startsWith/endsWith
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/string/startsWith
Promise (we've accidentally used this)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Between transpiling and polyfilling, Babel is busy
But Babel gets us to the point where our modern code works in all browsers
For instance, browsers like IE11
We don't need to support IE 11 anymore
But 3 years ago, 80% of these features needed to be transpiled or polyfilled for other browsers we do support
3 years ago...
How are we looking now?
Everything is supported!!!
Well, almost...
We may have some spread/rest issues in Safari
And then there's the feature we may use more than any other and hasn't been mentioned yet
TC39 is struggling with decorators. 5 years after starting, decorators are still at stage 2.
The expected implementation has changed multiple times. It may still be awhile.
With the exception of decorators, and potentially spread/rest...
...we can stop transpiling...
...remove the polyfill...
..and realize the promise Babel makes, to be able to use modern JavaScript until the browsers catch up, at which point remove Babel and It Just Works
And it does Just Work, mostly, and I've got the branch builds to prove it
Smaller JS payloads
Less JS to evaluate and run
Faster native implementations of features
Is that it?
The point of Babel is to allow us to stay on the bleeding edge
Clearly the edge we are on stopped bleeding awhile ago
In the meantime TC39 has mostly been dormant...
...just kidding, there's all kinds of new 💩!
We haven't updated anything in 3 years and in 3 years all sorts of cool stuff has happened
100000000000
100_000_000_000
Function sent
Export namespaces
Export default from
New Set/Map functions to match Array options
Observables
We are using other polyfills of lesser note, check the core-js site for details
Ember UI: PR is ready to be submit
e2e: within a few weeks
By David Bashford