Skip to content
Bradford Powell edited this page Jul 21, 2017 · 85 revisions

Example App

Install Prerequisites

Window Setup

  1. Install NodeJS 6.
  2. Install chocolatey, python, windows sdk, and visual studio as described here.

Linux / Mac Setup

  1. Install NodeJS 6.

Setup a new project

Create a new project folder

> mkdir myhub
> cd myhub
> npm init

Create and host dummy page

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.

Install steal, steal-tools, and jquery

> npm install steal --save
> npm install steal-tools --save-dev
> npm install jquery --save

Import your first module

Create the module

Create myhub.js with the following:

import $ from "jquery";

$("body").html("<h1>Goodbye script tags!</h1>");

Point your page at steal.js

Update myhub.html with:

<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    Hello World!
    <script src="./node_modules/steal/steal.js"></script>
  </body>
</html>

Point package.json to the right main.

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.

Import styles

Install steal-less

Processing of css and less require steal-css and steal-less packages, respectively. The latter depends on the former, so installing it pulls in both.

> npm install steal-less --save

Create and import a less file

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 and import bootstrap

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 an ES6 exporting module and modlet

Create the demo page

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 the module

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 the test page

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>

Create the test

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);
});

Use the module

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');

Create test with dependency injection

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);
    });
});

Import a global script in a CJS modlet

Install the global script

Run:

> npm install justifiedGallery --save

Create the modlet

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) {
    
};

Configure justifiedGallery to load

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"]
         }
     }
  }
}

Use justifiedGallery

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();
		}
	});
};

Build a production app

Update app to change pages

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();

Build the app and switch to production

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>

Preload css

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>

Bundle steal.js

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>

Build a progressive loading production app

Make the app progressively load

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 bundles to build

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",
    "justifiedGallery": "^3.6.2",
    "steal": "^0.16.21"
  },
  "devDependencies": {
    "steal-tools": "^0.16.5",
    "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

Make a build script

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

Export modules to other formats

Create an export script

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

Test the standalone module

Create repos/repos-standalone.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">
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  </head>
  <body>
    <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>
  </body>
</html>