- Install Prerequisites
- Setup a new project
- Import your first module
- Import styles
- [Setup hot module swapping]
- Create an ES6 exporting module and modlet
- Create a test with dependency injection
- Import a global script in a CJS modlet
- Build a production app
- Build a progressive loading production app
- Export modules to other formats
- Install NodeJS 6.
> mkdir myhub
> cd myhub
> npm init
Create myhub.html with:
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
Hello World!
</body>
</html>
Install and run local fileserver:
> npm install http-server -g
> http-server
Open http://127.0.0.1:8080/myhub.html.
> npm install steal --save
> npm install steal-tools --save-dev
> npm install jquery --save
Create myhub.js with the following:
import $ from "jquery";
$("body").html("<h1>Goodbye script tags!</h1>");
Update myhub.html with:
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
Hello World!
<script src="./node_modules/steal/steal.js"></script>
</body>
</html>
Update package.json to:
{
"name": "myhub",
"version": "1.0.0",
"description": "",
"main": "myhub.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"jquery": "^3.0.0",
"steal": "^0.16.21"
},
"devDependencies": {
"steal-tools": "^0.16.5"
}
}
Reload myhub.html to see your changes.
Create myhub.less with:
body h1 {
color: #2193C4;
}
Import it with the following updated myhub.js:
import $ from "jquery";
import "./myhub.less";
$("body").html("<h1>Goodbye script tags!</h1>");
Review module identifiers and module names.
Install bootstrap with:
> npm install bootstrap --save
Update the myhub.html to use bootstrap with:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script src="./node_modules/steal/steal.js"></script>
</body>
</html>
Import it and use it with the following updated myhub.js:
import $ from "jquery";
import "./myhub.less";
import "bootstrap/dist/css/bootstrap.css";
$("body").append(
"<div class='container'>"+
"<h1>Goodbye script tags!</h1>"+
"</div>");
Create repos/repos.html with:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="repos"/>
<script src="../node_modules/steal/steal.js" main="@empty">
import repos from "myhub/repos/repos";
repos("#repos");
</script>
</body>
</html>
Create repos/repos.js with:
import $ from "jquery";
import "bootstrap/dist/css/bootstrap.css";
export default function(selector){
$(selector).html("Loading...")
$.ajax({
url: "https://api.github.com/users/justinbmeyer/repos",
jsonp: "callback",
dataType: "jsonp",
success: function( response ) {
var defs = response.data.map(function(repo){
return `<dt><a href="${repo.url}">${repo.name}</a></dt><dd>${repo.description}</dt>`
});
$(selector).html("<dl class='dl-horizontal'>"+defs.join("")+"</dl>");
}
});
}
Create repos/repos-test.html with:
<title>myhub/repos/repos</title>
<script src="../node_modules/steal/steal.js"
main="myhub/repos/repos-test"></script>
<div id="qunit-fixture"></div>
Install steal-qunit
with:
> npm install steal-qunit --save-dev
Create repos/repos-test.js with:
import QUnit from "steal-qunit";
import repos from "./repos";
QUnit.module("myhub/repos/");
QUnit.test("basics", function(){
stop();
var fixtureEl = document.getElementById("qunit-fixture");
repos(fixtureEl);
QUnit.equal(
fixtureEl.innerHTML,
"Loading...", "starts with loading");
var interval = setInterval(function(){
var dl = fixtureEl.getElementsByTagName("dl");
if(dl.length === 1) {
QUnit.ok(true, "inserted a dl");
QUnit.start();
clearInterval(interval);
}
},100);
});
Update myhub.js to:
import $ from "jquery";
import "./myhub.less";
import "bootstrap/dist/css/bootstrap.css";
import repos from "./repos/repos";
$("body").append(
"<div class='container'>"+
"<h1>Goodbye script tags!</h1>"+
"<div id='repos'/>"+
"</div>");
repos('#repos');
Update repos/repos-test.js with:
import QUnit from "steal-qunit";
import repos from "./repos";
import clone from 'steal-clone';
import $ from 'jquery';
QUnit.module("myhub/repos/");
QUnit.test("basics", function(){
QUnit.stop();
var fixtureEl = document.getElementById("qunit-fixture");
repos(fixtureEl);
QUnit.equal(
fixtureEl.innerHTML,
"Loading...", "starts with loading");
var interval = setInterval(function(){
var dl = fixtureEl.getElementsByTagName("dl");
if(dl.length === 1) {
QUnit.ok(true, "inserted a dl");
QUnit.start();
clearInterval(interval);
}
},100);
});
QUnit.asyncTest("basics with dependency injection", function(){
var jQuery = function(selector){
return $(selector)
};
jQuery.ajax = function(options){
setTimeout(function(){
options.success({
data: [{
url: "http://stealjs.com",
name: "StealJS",
description: "Futuristic Module Loader"
}]
});
QUnit.equal(
$("#qunit-fixture").html(),
'<dl class="dl-horizontal">'+
'<dt><a href="http://stealjs.com">StealJS</a></dt><dd>Futuristic Module Loader</dd>'+
'</dl>',
"updated with request");
QUnit.start();
},1);
};
clone({
"jquery": {"default": jQuery}
}).import("myhub/repos/repos").then(function(module){
var repos = module["default"];
var fixtureEl = document.getElementById("qunit-fixture");
repos(fixtureEl);
});
});
Run:
> npm install justifiedGallery --save
Create puppies/puppies.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class='container'>
<div id="puppies"/>
</div>
<script src="../node_modules/steal/steal.js" main="@empty">
var puppies = require("myhub/puppies/puppies");
puppies("#puppies");
</script>
</body>
</html>
Create puppies/puppies.js:
require("justifiedGallery");
module.exports = function(selector) {
};
Change package.json to:
{
"name": "myhub",
"version": "1.0.0",
"description": "",
"main": "myhub.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"bootstrap": "^3.3.6",
"jquery": "^3.0.0",
"justifiedGallery": "^3.6.2",
"steal": "^0.16.21"
},
"devDependencies": {
"steal-qunit": "^0.1.1",
"steal-tools": "^0.16.5"
},
"system": {
"map": {
"justifiedGallery": "justifiedGallery/src/js/justifiedGallery"
},
"meta": {
"justifiedGallery/src/js/justifiedGallery": {
"format": "global",
"deps": ["jquery","justifiedGallery/src/less/justifiedGallery.less"]
}
}
}
}
Change puppies/puppies.js to:
require("justifiedGallery");
var $ = require("jquery");
module.exports = function(selector) {
$(selector).html("Loading...");
$.ajax({
url: 'https://api.flickr.com/services/feeds/photos_public.gne',
dataType: 'jsonp',
jsonpCallback: "jsonFlickrFeed",
data: {
"tags": "puppy",
"format": "json"
},
success: function(response) {
var html = response.items.map(function(item, index) {
return '<a href="'+item.link+'">'+
'<img alt="'+item.title+'" src="'+item.media.m+'"/>'+
'</a>'
}).join("");
$(selector).html(html).justifiedGallery();
}
});
};
Update myhub.js to:
import $ from "jquery";
import "./myhub.less";
import "bootstrap/dist/css/bootstrap.css";
import repos from "./repos/repos";
import puppies from "./puppies/puppies";
$("body").append(`
<div class='container'>
<h1>Goodbye script tags!</h1>
<a href="#repos">Repos</a> <a href="#puppies">Puppies</a>
<div id='main'/>
</div>`);
var modules = {
repos: repos,
puppies: puppies,
"": function(selector){
$(selector).html("Welcome home");
}
}
var updatePage = function(){
var hash = window.location.hash.substr(1);
modules[hash]("#main");
};
$(window).on("hashchange", updatePage);
updatePage();
Run:
> ./node_modules/.bin/steal-tools
Create index.html with:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script src="./node_modules/steal/steal.production.js"
main="myhub/myhub"></script>
</body>
</html>
Update index.html to:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="./dist/bundles/myhub/myhub.css" rel="stylesheet">
</head>
<body>
<script>
steal = {
instantiated: {
"bundles/myhub/myhub.css!$css" : null
}
}
</script>
<script src="./node_modules/steal/steal.production.js"
main="myhub/myhub"></script>
</body>
</html>
Run:
> ./node_modules/.bin/steal-tools build --bundleSteal
Update index.html to:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="./dist/bundles/myhub/myhub.css" rel="stylesheet">
</head>
<body>
<script>
steal = {
instantiated: {
"bundles/myhub/myhub.css!$css" : null
}
}
</script>
<script src="./dist/bundles/myhub/myhub.js"></script>
</body>
</html>
Update myhub.js to:
import $ from "jquery";
import "./myhub.less";
import "bootstrap/dist/css/bootstrap.css";
$("body").append(`
<div class='container'>
<h1>Goodbye script tags!</h1>
<a href="#repos">Repos</a> <a href="#puppies">Puppies</a>
<div id='main'/>
</div>`);
var updatePage = function(){
var hash = window.location.hash.substr(1);
if(!hash) {
$("#main").html("Welcome home");
} else {
System.import("myhub/"+hash+"/"+hash).then(function(moduleOrPlugin){
var plugin = typeof moduleOrPlugin === "function" ?
moduleOrPlugin : moduleOrPlugin["default"];
plugin("#main");
});
}
};
$(window).on("hashchange", updatePage);
updatePage();
Update package.json to:
{
"name": "myhub",
"version": "1.0.0",
"description": "",
"main": "myhub.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"bootstrap": "^3.3.6",
"jquery": "^3.0.0",
"jqueryui": "^1.11.1",
"justifiedGallery": "^3.6.2",
"steal": "^0.16.21",
"steal-tools": "^0.16.4"
},
"devDependencies": {
"steal-qunit": "^0.1.1"
},
"system": {
"map": {
"justifiedGallery": "justifiedGallery/src/js/justifiedGallery"
},
"meta": {
"justifiedGallery/src/js/justifiedGallery": {
"format": "global",
"deps": ["jquery","justifiedGallery/src/less/justifiedGallery.less"]
}
},
"bundle": [
"myhub/puppies/puppies",
"myhub/repos/repos"
]
}
}
Run:
> ./node_modules/.bin/steal-tools build --bundleSteal
Create build.js:
var stealTools = require("steal-tools");
stealTools.build({
config: __dirname+"/package.json!npm"
}, {
bundleSteal: true
});
Run the build script with:
> node build.js
Create export.js with:
var stealTools = require("steal-tools");
stealTools.export({
system: {
main: "myhub/repos/repos",
config: __dirname+"/package.json!npm"
},
options: {
verbose: true
},
outputs: {
"+amd": {},
"+global-js": {
exports: {
"myhub/repos/repos":"repos",
"jquery": "jQuery"
},
dest: __dirname+"/dist/global/repos.js"
}
}
});
Run:
> node export.js
Create repos/repos-standalone.html with:
<div id='git-repos'/>
<script src="//code.jquery.com/jquery-3.0.0.js"></script>
<script src="../dist/global/repos.js"></script>
<script>
repos("#git-repos");
</script>
Tell the user to go to
http://127.0.0.1:8080/repos/repos-test.html
to run the initial test, just before the "Create test with dependency injection" heading.EDIT: This is a more general issue, that in a step by step guide with several places to see changes, the user should be prompted to see what's changed before moving on. So between e.g. "Build app and switch to production" and "Preload CSS" it seems pointless to have two versions of index.html unless the reader is guided to see an intermediate step between them.