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