A talk I gave in October 2016, discussing my solution for shipping native ES2015 to users of netflix.com. It lays out the concept of progressive transpilation, and goes through the hurdles I have run across so far.
3. 3
• Const & Let
• Arrow Functions
• Destructuring
• Backtick/Template Strings
• Default Parameters
💧
Where we are today:
Limited ES2015
4. 4
What are we trying to do?
Goals
👼 Best experience for our users.
🤓 Best experience for our developers.
5. 5
🤓 Senior UI Engineer @ Netflix
😅 Squad lead for the Web Core team
😍 Travel
😭 Clever code
Who?
Jacques Favreau
formComplete = function(){
var u = user.getUser();
var ct = 0 + (typeof(u.nickname)==='string' && u.nickname.length>0) +
(typeof(u.websiteUrl)==='string' && u.websiteUrl.length>0) +
(typeof(u.linkedin)==='string' && u.linkedin.length>0) +
(typeof(u.locationString)==='string' && u.locationString.length>0) +
(typeof(u.title)==='string' && u.title.length>0) +
(typeof(u.bio)==='string' && u.bio.length>0) +
(typeof(u.imageUrl)==='string' && u.imageUrl.length>0) +
(typeof(u.resume)==='string' && u.resume.length>0) +
(u.employerSharing && true);
return Math.ceil(ct/0.09);
};
6. 6
What’s the future look like?
1.Are we stuck?
2.Rethinking what we
ship, and why
3.ES2015 in the browser
4.Balancing UX and DX
now and in the future
8. 8
Where we are today
Heavy on DX
Transpiled to ES5
Polyfill ES2015
Modern
Transpiled to ES5
Polyfill ES2015
Polyfill ES5
Legacy
No
Support
Unsupported
10. 10
Where we are today
Heavy on DX
Transpiled to ES5
Polyfill ES2015
Modern
Transpiled to ES5
Polyfill ES2015
Polyfill ES5
Legacy
No
Support
Unsupported
31. 31
Sidebar:
Look for other win opportunities
ES2015
Modern
Legacy
No
Support
Unsupported
Transpiled to ES5
Polyfill ES2015
Polyfill ES5
Trailing
Transpiled to ES5
Polyfill ES2015
Polyfill ES5
Shims
41. 41
const babelConfig = {
modern: {
presets: []
},
trailing: {
presets: ['es2015']
}
};
return gulp.src(glob)
// Run babel with the modern config
.pipe(babel(babelConfig.modern))
// Save the modern JS version.
.pipe(gulp.dest('generated/modern/'))
// Run Babel with trailing config.
.pipe(babel(babelConfig.trailing))
// Save the trailing JS version.
.pipe(gulp.dest('generated/trailing/'));
Just don’t use the
ES2015 preset.
Solutions to issue #1:
Workflows that work
49. 49
We unabashedly use the User Agent string
“[A]n ever-growing pack of lies[…]“
- Patrick H. Lauke
Mozilla/5.0 (Windows NT 10.0)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/42.0.2311.135 Safari/537.36
Edge/12.10136
Solution to Issue #3:
Our old friend, UA
50. 50
On first load, it’s all we have
¯_( )_/¯
Solution to Issue #3:
Our old friend, UA
51. 51
Select browsers with ES2015 support.
kangax.github.io/compat-table/es6/
At this time, our ES2015 browsers are:
- Chrome 51
- Firefox 49
- Edge 14
- Safari 10
Solution to Issue #3:
Our old friend, UA
53. 53
Solution to issue #4:
Test for lying browsers
Execute a bit of Javascript
and test for lies.
54. 54
✓ unicode
✓ proxies
✓ symbols
✓ subclassable built-ins
✓ promises
✓ math + number + string APIs
✓ array + object APIs
✓ binary and octal literals
Solution to issue #4:
Test for lying browsers
✓ arrows
✓ classes
✓ enhanced object literals
✓ template strings
✓ destructuring
✓ default + rest + spread
✓ let + const
✓ iterators + for..of
✓ generators
55. 55
class ಠ_ಠ extends Array{
constructor(j = 'a', ...c) {
const q = (({u: e}) => {
return { [`s${c}`]: Symbol(j) };
})({});
super(j, q, ...c);
}
}
new Promise((f) => {
const a = function* (){
return "u{20BB7}".match(/./u)[0].length === 2 || true;
};
for(let vre of a()){
const [uw, as, he, re] = [new Set(), new WeakSet(), new Map(), new WeakMap()];
break;
}
f(new Proxy({}, {get: (han, h) => h in han ? han[h] : "42".repeat(0o10)}));
}).then(bi => new ಠ_ಠ(bi.rd));
Solution to issue #4:
Test for lying browsers
Execute a bit of Javascript
and test for lies.
56. 56
eval(
'class ಠ_ಠ extends Array{constructor(j="a",...c){const q=(({u:e})=>{return{[' +
'`s${c}`]:Symbol(j)}})({});super(j,q,...c)}}new Promise(f=>{const a=function' +
'*(){return"u{20BB7}".match(/./u)[0].length===2||true};for(let vre of a()){' +
'const[uw,as,he,re]=[new Set,new WeakSet,new Map,new WeakMap];break} f(new ' +
'Proxy({},{get:(han,h)=>h in han?han[h]:"42".repeat(8)}))}).then(bi=>new ಠ_ಠ(bi.rd));'
);
Execute a bit of Javascript
and test for lies.
Solution to issue #4:
Test for lying browsers
57. 57
try {
eval(
'class ಠ_ಠ extends Array{constructor(j="a",...c){const q=(({u:e})=>{return{[' +
'`s${c}`]:Symbol(j)}})({});super(j,q,...c)}}new Promise(f=>{const a=function' +
'*(){return"u{20BB7}".match(/./u)[0].length===2||true};for(let vre of a()){' +
'const[uw,as,he,re]=[new Set,new WeakSet,new Map,new WeakMap];break} f(new ' +
'Proxy({},{get:(han,h)=>h in han?han[h]:"42".repeat(8)}))}).then(bi=>new ಠ_ಠ(bi.rd));'
);
} catch (e) {
}
Execute a bit of Javascript
and test for lies.
Solution to issue #4:
Test for lying browsers
58. 58
try {
eval(
'class ಠ_ಠ extends Array{constructor(j="a",...c){const q=(({u:e})=>{return{[' +
'`s${c}`]:Symbol(j)}})({});super(j,q,...c)}}new Promise(f=>{const a=function' +
'*(){return"u{20BB7}".match(/./u)[0].length===2||true};for(let vre of a()){' +
'const[uw,as,he,re]=[new Set,new WeakSet,new Map,new WeakMap];break} f(new ' +
'Proxy({},{get:(han,h)=>h in han?han[h]:"42".repeat(8)}))}).then(bi=>new ಠ_ಠ(bi.rd));'
);
} catch (e) {
document.cookie = 'esSupportLevel=5; expires=' +
(new Date((new Date()).getTime() + 2678400000)).toGMTString() +
'; path=/ ;domain=netflix.com';
if(location.reload){location.reload(true);}else{location.href = location.href;}
}
Execute a bit of Javascript
and test for lies.
Solution to issue #4:
Test for lying browsers
59. 59
<script>
try {
eval(
'class ಠ_ಠ extends Array{constructor(j="a",...c){const q=(({u:e})=>{return{[' +
'`s${c}`]:Symbol(j)}})({});super(j,q,...c)}}new Promise(f=>{const a=function' +
'*(){return"u{20BB7}".match(/./u)[0].length===2||true};for(let vre of a()){' +
'const[uw,as,he,re]=[new Set,new WeakSet,new Map,new WeakMap];break} f(new ' +
'Proxy({},{get:(han,h)=>h in han?han[h]:"42".repeat(8)}))}).then(bi=>new ಠ_ಠ(bi.rd));'
);
} catch (e) {
document.cookie = 'esSupportLevel=5; expires=' +
(new Date((new Date()).getTime() + 2678400000)).toGMTString() +
'; path=/ ;domain=netflix.com';
if(location.reload){location.reload(true);}else{location.href = location.href;}
}
</script>
Execute a bit of Javascript
and test for lies.
Solution to issue #4:
Test for lying browsers
60. 60
What we’re doing (for now)
1. Build Babel as a transpiler.
2. Minifiers Babel plugins > babel-minify
3. Targeting User Agent to target supporting browsers
4. Lies Evaluated ES2015 to test; cookie fallback