Steal Advanced
Topics
- Module Loading Past / Present / Future
- ES6 Module Syntax and API
- The Dream
-
ES6 Module Polyfills and Tools
- ES6-Module-Loader
- Traceur
- SystemJS
- StealJS
- Transpile
Module Loading - Past, Present, and Future
Yesterday: Static Loading
<script src='jquery.js'></script>
<script src='login.js'></script>
<script src='tabs.js'></script>
<script src='app.js'></script>
// app.js
new Login("#login");
new Tabs("#apps");
Yesterday: Dynamic Loading
var script =
document.createElement("script");
script.src = "homepage.js";
script.onload = function(){
new Homepage("#main");
};
document.head.appendChild(script);
Today – Static Loading - AMD
<script src="./config.js"></script>
<script src="./require.js"
data-main="app"></script>
// app.js
define(["login","tabs"], function(Login, Tabs){
new Login("#login");
new Tabs("#apps");
});
// config.js
var require = {
shim: {"jquery": { exports: ["jQuery"]} }
};
Today – Dynamic Loading - AMD
require(["homepage"],function(){
new Homepage("#main");
});
Today – Static - CommonJS / Browserify
<script src="site.js"></script>
// app.js
var Login = require("login"),
Tabs = require("tabs");
new Login("#login");
new Tabs("#apps");
// package.json
"browserify": {
"transform" : ["browserify-shim"]
},
"browserify-shim":{
"./jquery.js": {exports: "$"}
}
$ browserify app.js > site.js
Compile
Compromises
CommonJS
+ npm
+ you have to build
+ locate conventions
RequireJS
+ No build required
+ Progressive loading
- you have to build
- progressive loading
- syntax
- locate conventions
Tomorrow – Static Loading
<script type="module" src="app.js"></script>
// app.js
import Login from "login";
import Tabs from "tabs";
new Login("#login");
new Tabs("#apps");
Tomorrow – Dynamic Loading
System.import("homepage").then(
function(Homepage){
new Homepage("#main");
},
function(err){ … });
ES6 Module Syntax and API
Loader
MyLoader = new Loader({
normalize: ( name, parentName, parentAddress ) => name
locate: ( load ) => address
fetch: ( load ) => source
translate: ( load ) => source
instantiate: ( load ) => {deps, execute}
});
MyLoader.import("jquery");
MyLoader = new Loader({
normalize: function( name ){
if(name.indexOf("bb/") == 0) {
return "backbone/"+name.substr(3);
}
return name;
},
...
});
MyLoader.import("bb/view");
MyLoader = new Loader({
instantiate: function(load){
if(load.name.indexOf("jquery.ui") >= 0) {
return {
deps: ["jquery"],
execute: function(){
window.jQuery = MyLoader.get("jquery");
eval(load.source);
delete window.jQuery;
}
};
}
}
});
MyLoader.import("jquery.ui.tabs");
Syntax
// import
import { add, sum as total } from "math";
// export
var add = function(a){
return a + 1;
};
export { add };
export function sum( a, b ) {
return a + b;
};
Syntax
import "jquery";
import $ from "jquery";
import { $ } from "jquery";
import { $ as jQuery } from "jquery";
export var x = 42;
export function foo() {};
export q = {};
export default 42;
export default function foo() {};
export { encrypt };
export { decrypt as dec };
export { encrypt as en } from 'crypto';
export * from 'crypto';
System
var promise = System.import('lodash/map');
promise.then(function(map) {
map([1,2,3], (x) => x*x);
}, function(error){
console.log("can’t load map!",error);
});
System – baseURL + paths
System.baseURL = '/js/lib';
System.import('module');
// loads "/js/lib/module.js"
System.paths['lodash/*'] = 'util/lodash/*.js'
System.import('lodash/map')
// loads "/js/lib/util/lowdash/map.js"
ES6, CJS, AMD, and Global formats
// main.js
import A from "moduleA";
console.log(A.B.C.name)
//moduleA.js
module.exports = {
B: require("moduleB")
};
//moduleB.js
define(["c"],function(moduleC){
return {C: moduleC}
});
//moduleC
C = {name: "C"}
ES6 Sugar
// JS Classes
class Character {
constructor(x) {
this.x = x; }
}
// Arrow Syntax
var square = x => x * x;
// Default parameters
function f(list, indexA = 0, indexB = list.length) {
return [list, indexA, indexB];
}
// Array comprehensions
var array = [for (x of [0, 1, 2]) for (y of [0, 1]) x + '' + y];
Hooks
Normalize
- Turns a module identifier into a module name
- Module name serves as the key in a "database" of loaded modules.
- Are relative to the parent name, not address.
System.normalize("./list.stache!", "restaurant/list");
// -> "restaurant/list/list.stache!can/view/stache/system
Locate
System.locate({ name: "restaurant/list/list" });
// -> http://localhost:8080/src/restaurant/list/list.js
- Turns module names into a url.
- Used by the bower plugin to load bower dependencies from bower_components/
Fetch
System.locate({
name: "restaurant/list/list",
address: "http://localhost:8080/src/restaurant/list/list.js"
});
// -> "import can from 'can';\n..."
- Fetch the source from an address.
- By default XHR
- localStorage
- web sockets
- service workers
Translate
System.translate({
name: "restaurant/list/list.stache!can/view/stache/system",
address: ...,
source: "<can-import from='homepage'/>{{#if foo}}..."
});
/*
"define(['can/view/stache/', 'homepage'], function(stache){
return stache([{ h1: ... }]);
});
*/
- Transform source into more source.
- Useful for compiling non-JavaScript to JavaScript.
Instantiate
System.locate({ name: "restaurant/list/list" });
// -> "import can from 'can';\n..."
- Determine a module's dependencies
- Create a module's factory
- Useful when the module is not JavaScript.
Plugins
Modules that export hooks
exports.locate = function(name){
};
exports.fetch = function(load){
};
exports.translate = function(load){
};
exports.instantiate = function(load){
};
API
Create a less plugin
- Create a folder in examples/
- Add a styles.less with some styles
- Add an index.html that imports the less.
- Grab less-engine from stealjs/ext/less-engine.js
- Create a plugin!
Steal's startup process
Get script options
<script src="node_modules/steal/steal.js" data-env="production"
load-bundles></script>
Apply to config
var urlOptions = getUrlOptions();
System.config(urlOptions);
Determine production bundles
If loadBundles=true
<script src="node_modules/steal/steal.production.js" main="app"></script>
System.bundles = {"bundles/app": ["package.json!npm","app"]};
Import steal.dev
Only in development
Import System.configMain
- Crawl for package dependencies
- Set the configuration for each
- everything in package.json "system"
- Apply the npm extension
- Overrides normalize, locate
Import System.main
StealJS: Advanced
By Matthew Phillips
StealJS: Advanced
The gory internals of Steal.
- 667