├── .gitignore
├── tests
├── benchmark
│ ├── src
│ │ ├── evens.js
│ │ ├── odds.js
│ │ └── main.js
│ └── run.js
└── unit
│ ├── src
│ ├── circular-init
│ │ ├── a.js
│ │ ├── b.js
│ │ └── c.js
│ ├── live
│ │ ├── b.js
│ │ └── a.js
│ ├── fixtures
│ │ ├── bar.js
│ │ ├── foo.js
│ │ └── baz.js
│ └── others
│ │ ├── odds.js
│ │ └── evens.js
│ ├── system.js
│ ├── manual.html
│ └── system-live-test.js
├── index.js
├── Gruntfile.js
├── LICENSE
├── dist
├── system-polyfill.min.js
└── system-polyfill.js
├── package.json
├── server.js
├── README.md
└── client.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | tests/benchmark/cjs
3 | tests/benchmark/system
4 | tests/benchmark/bundle
5 | tests/unit/build
6 |
--------------------------------------------------------------------------------
/tests/benchmark/src/evens.js:
--------------------------------------------------------------------------------
1 | /* jshint esnext:true */
2 |
3 | export default function (n) {
4 | return n % 2 === 0;
5 | }
6 |
--------------------------------------------------------------------------------
/tests/unit/src/circular-init/a.js:
--------------------------------------------------------------------------------
1 | import B from './b';
2 | import C from './c';
3 |
4 | var b = new B();
5 | var c = new C();
6 |
--------------------------------------------------------------------------------
/tests/benchmark/src/odds.js:
--------------------------------------------------------------------------------
1 | /* jshint esnext:true */
2 |
3 | import isEven from "./evens";
4 |
5 | export default function (n) {
6 | return !isEven(n);
7 | }
8 |
--------------------------------------------------------------------------------
/tests/unit/src/circular-init/b.js:
--------------------------------------------------------------------------------
1 | import C from './c';
2 |
3 | function B() {
4 | console.log('B');
5 | }
6 |
7 | B.prototype.createC = function() {
8 | return new C();
9 | };
10 |
11 | export default B;
12 |
--------------------------------------------------------------------------------
/tests/unit/src/live/b.js:
--------------------------------------------------------------------------------
1 | /* jshint esnext:true */
2 |
3 | export var counter = 0;
4 |
5 | export function increment () {
6 | counter++;
7 | }
8 |
9 | export function decrement () {
10 | counter--;
11 | }
12 |
--------------------------------------------------------------------------------
/tests/unit/src/fixtures/bar.js:
--------------------------------------------------------------------------------
1 | import foo from "./foo";
2 |
3 | export default function (a, b) {
4 | return foo(a, b);
5 | };
6 |
7 | export function multi () {
8 | return foo.apply(this, arguments);
9 | }
10 |
--------------------------------------------------------------------------------
/tests/unit/src/fixtures/foo.js:
--------------------------------------------------------------------------------
1 |
2 | var foo = { something: 1};
3 |
4 | export default function () {
5 | var n = 0;
6 | Array.prototype.slice(arguments).forEach(function (v) {
7 | n += v;
8 | });
9 | return n;
10 | };
11 |
--------------------------------------------------------------------------------
/tests/unit/src/circular-init/c.js:
--------------------------------------------------------------------------------
1 | import B from './b';
2 |
3 | function C() {
4 | B.call(this);
5 | console.log('C');
6 | }
7 |
8 | C.prototype = Object.create(B.prototype);
9 | C.prototype.constructor = C;
10 |
11 | export default C;
12 |
--------------------------------------------------------------------------------
/tests/benchmark/src/main.js:
--------------------------------------------------------------------------------
1 | /* jslint esnext: true */
2 |
3 | import isOdd from "./odds";
4 |
5 | export function checkForOdd(n) {
6 | return isOdd(n);
7 | }
8 |
9 | // this is needed for `bundle` output
10 | if (typeof global !== 'undefined') {
11 | global.checkForOdd = checkForOdd;
12 | }
13 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* jslint node:true */
2 |
3 | if (global.System) {
4 | throw new Error("Conflicts with the global `System` definition, use `es6-micro-loader/lib/system` instead.");
5 | }
6 |
7 | global.Promise = global.Promise || require("es6-promise").Promise;
8 | module.exports = global.System = require("./server").System;
9 |
--------------------------------------------------------------------------------
/tests/unit/src/live/a.js:
--------------------------------------------------------------------------------
1 | /* jshint esnext:true */
2 |
3 | import { counter, increment, decrement } from "./b";
4 |
5 | export function up () {
6 | increment();
7 | return counter;
8 | }
9 |
10 | export function down () {
11 | decrement();
12 | return counter;
13 | }
14 |
15 | export function current () {
16 | return counter;
17 | }
18 |
--------------------------------------------------------------------------------
/tests/unit/src/fixtures/baz.js:
--------------------------------------------------------------------------------
1 | import simple from "./bar";
2 | import {multi} from "./bar";
3 |
4 | class Baz {
5 |
6 | constructor(config) {
7 | this.config = config;
8 | }
9 |
10 | simple(a, b) {
11 | let r = simple(a, b);
12 | return r;
13 | }
14 |
15 | multi() {
16 | return multi.apply(this, arguments);
17 | }
18 |
19 | };
20 |
21 | export default Baz;
22 |
--------------------------------------------------------------------------------
/tests/unit/src/others/odds.js:
--------------------------------------------------------------------------------
1 | /* jshint esnext:true */
2 |
3 | import { isEven } from './evens';
4 |
5 | export function nextOdd(n) {
6 | return isEven(n) ? n + 1 : n + 2;
7 | }
8 |
9 | /**
10 | * We go through these gymnastics to eager-bind to isEven. This is done to
11 | * ensure that both this module and the 'evens' module eagerly use something
12 | * from the other.
13 | */
14 | export var isOdd = (function(isEven) {
15 | return function(n) {
16 | return !isEven(n);
17 | };
18 | })(isEven);
19 |
--------------------------------------------------------------------------------
/tests/unit/src/others/evens.js:
--------------------------------------------------------------------------------
1 | /* jshint esnext:true */
2 |
3 | import { nextOdd } from './odds';
4 |
5 | /**
6 | * We go through these gymnastics to eager-bind to nextOdd. This is done to
7 | * ensure that both this module and the 'odds' module eagerly use something
8 | * from the other.
9 | */
10 | export var nextEven = (function() {
11 | return function(n) {
12 | var no = nextOdd(n);
13 | return (no === n + 2) ?
14 | no - 1 : no;
15 | };
16 | })(nextOdd);
17 |
18 | export function isEven(n) {
19 | return n % 2 === 0;
20 | }
21 |
--------------------------------------------------------------------------------
/tests/unit/system.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, beforeEach */
2 | 'use strict';
3 |
4 | var System = require('../../'),
5 | expect = require('chai').expect;
6 |
7 | describe('System', function() {
8 |
9 | it('.import() should be a function', function() {
10 | expect(System.import).to.be.a('function');
11 | });
12 |
13 | it('.has() should be a function', function() {
14 | expect(System.has).to.be.a('function');
15 | });
16 |
17 | it('.get() should be a function', function() {
18 | expect(System.get).to.be.a('function');
19 | });
20 |
21 | it('.register() should be a function', function() {
22 | expect(System.register).to.be.a('function');
23 | });
24 |
25 | });
26 |
--------------------------------------------------------------------------------
/tests/unit/manual.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | using System polyfill thru micro-loader
6 |
7 |
8 |
9 | Look at the browser's console for details.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 |
3 | var libpath = require('path');
4 |
5 | grunt.initConfig({
6 | pkg: grunt.file.readJSON('package.json'),
7 | jshint: {
8 | all: ['server.js', 'client.js', 'tests/*.js']
9 | },
10 | copy: {
11 | dist: {
12 | files: [
13 | {
14 | 'dist/system-polyfill.js': ['client.js']
15 | }
16 | ]
17 | }
18 | },
19 | uglify: {
20 | dist: {
21 | files: [
22 | {
23 | 'dist/system-polyfill.min.js': ['dist/system-polyfill.js']
24 | }
25 | ]
26 | }
27 | }
28 | });
29 |
30 | grunt.loadNpmTasks('grunt-contrib-jshint');
31 | grunt.loadNpmTasks('grunt-contrib-uglify');
32 | grunt.loadNpmTasks('grunt-contrib-copy');
33 |
34 | grunt.registerTask('build', ['copy', 'uglify']);
35 | grunt.registerTask('default', ['jshint', 'build']);
36 | };
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Caridy Patiño
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/tests/benchmark/run.js:
--------------------------------------------------------------------------------
1 | var Benchmark = require("benchmark");
2 | var System = require("../../"); // es6-micro-loader
3 |
4 | System.import("tests/benchmark/system/main").then(function (systemMod) {
5 |
6 | var suite = new Benchmark.Suite(),
7 | a = 0, aFn = require("./cjs/main").checkForOdd,
8 | b = 0, bFn = systemMod.checkForOdd,
9 | c = 0, cFn = require("./bundle/main") && global.checkForOdd;
10 |
11 | // add tests
12 | suite.add("cjs", function() {
13 | // commonjs modules which use require() and defineProperties() to preserve ES semantics
14 | aFn(a++);
15 | })
16 | .add("system", function() {
17 | // System.register() modules which preserve ES semantics
18 | bFn(b++);
19 | })
20 | .add("bundle", function() {
21 | // using a global variable, and a rollup without preserving bindings or any other ES Semantics
22 | cFn(c++);
23 | })
24 | // add listeners
25 | .on("cycle", function(event) {
26 | console.log(String(event.target));
27 | })
28 | .on("complete", function() {
29 | console.log("Fastest is " + this.filter("fastest").pluck("name"));
30 | })
31 | // run async
32 | .run({ "async": true });
33 |
34 | });
35 |
--------------------------------------------------------------------------------
/tests/unit/system-live-test.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, beforeEach */
2 | 'use strict';
3 |
4 | var System = require('../../'),
5 | expect = require('chai').expect;
6 |
7 | describe('System', function() {
8 |
9 | it('.import() should be a function', function() {
10 | expect(System.import).to.be.a('function');
11 | });
12 |
13 | describe('import("tests/unit/build/live/a")', function() {
14 |
15 | it('should support a live counter', function(next) {
16 | var p = System.import("tests/unit/build/live/a");
17 | p.then(function (a) {
18 | expect(a.up).to.be.a('function');
19 | expect(a.down).to.be.a('function');
20 | expect(a.current).to.be.a('function');
21 | expect(a.current()).to.be.a('number').equal(0);
22 | // testing live bindings
23 | a.up();
24 | a.up();
25 | expect(a.current()).to.be.a('number').equal(2);
26 | a.down();
27 | expect(a.current()).to.be.a('number').equal(1);
28 | next();
29 | }).catch(next);
30 | });
31 |
32 | it('should maintain the state after second import() call', function(next) {
33 | var p = System.import("tests/unit/build/live/a");
34 | p.then(function (a) {
35 | expect(a.up).to.be.a('function');
36 | expect(a.down).to.be.a('function');
37 | expect(a.current).to.be.a('function');
38 | expect(a.current()).to.be.a('number').equal(1);
39 | next();
40 | }).catch(next);
41 | });
42 |
43 | });
44 |
45 | });
46 |
--------------------------------------------------------------------------------
/dist/system-polyfill.min.js:
--------------------------------------------------------------------------------
1 | !function(a){"use strict";function b(a,b){if("/"===a.charAt(0)&&(a=a.slice(1)),"."!==a.charAt(0))return a;for(var c=a.split("/");"."===c[0]||".."===c[0];)".."===c.shift()&&b.pop();return b.concat(c).join("/")}function c(a){var b=m[a];return b&&!l[a]&&(l[a]=!0,b.execute()),b&&b.proxy}function d(a,b){n[a]=b}function e(a){return n[a]||c(a)}function f(a){return!!n[a]||!!m[a]}function g(a,b){var c=document.createElement("script");c.async&&(c.async=!1),k?c.onreadystatechange=function(){/loaded|complete/.test(this.readyState)&&(this.onreadystatechange=null,b())}:c.onload=c.onerror=b,c.setAttribute("src",a),j.appendChild(c)}function h(a){return new Promise(function(b,c){g((o.baseURL||"/")+a+".js",function(){i&&(o.register(a,i[0],i[1]),i=void 0);var d=m[a];return d?void Promise.all(d.deps.map(function(a){return n[a]||m[a]?Promise.resolve():h(a)})).then(b,c):void c(new Error("Error loading module "+a))})})}var i,j=document.getElementsByTagName("head")[0],k=/MSIE/.test(navigator.userAgent),l=Object.create(null),m=Object.create(null),n=Object.create(null),o={set:d,get:e,has:f,"import":function(a){return new Promise(function(c){var d=b(a,[]),f=e(d);return f?c(f):h(a).then(function(){return e(d)})})},register:function(a,c,d){if(Array.isArray(a))return i=[],void i.push.apply(i,arguments);var f,g,h=Object.create(null),j=Object.create(null);m[a]=f={proxy:h,values:j,deps:c.map(function(c){return b(c,a.split("/").slice(0,-1))}),dependants:[],update:function(a,b){g.setters[f.deps.indexOf(a)](b)},execute:function(){f.deps.map(function(b){var c=n[b];c?f.update(b,c):(c=e(b)&&m[b].values,c&&(m[b].dependants.push(a),f.update(b,c)))}),g.execute()}},g=d(function(b,c){return j[b]=c,f.lock=!0,f.dependants.forEach(function(b){m[b]&&!m[b].lock&&m[b].update(a,j)}),f.lock=!1,Object.getOwnPropertyDescriptor(h,b)||Object.defineProperty(h,b,{enumerable:!0,get:function(){return j[b]}}),c})}};a.System=o}(window);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "es6-micro-loader",
3 | "version": "0.2.1",
4 | "description": "Simplistic ES6 Module Loader for modules transpiled into System.register() format",
5 | "author": "Caridy Patino ",
6 | "homepage": "https://github.com/caridy/es6-micro-loader",
7 | "keywords": [
8 | "es6",
9 | "module",
10 | "loader",
11 | "system",
12 | "register"
13 | ],
14 | "bugs": "https://github.com/caridy/es6-micro-loader/issues",
15 | "main": "index.js",
16 | "files": [
17 | "index.js",
18 | "server.js",
19 | "dist/"
20 | ],
21 | "scripts": {
22 | "test": "npm run test-compile; ./node_modules/.bin/_mocha tests/unit/*.js --reporter spec",
23 | "test-compile": "cd tests/unit/src/; ../../../node_modules/.bin/compile-modules convert -f es6-module-transpiler-system-formatter **/*.js -o ../build",
24 | "benchmark-compile-cjs": "cd tests/benchmark/src/; ../../../node_modules/.bin/compile-modules convert -f commonjs *.js -o ../cjs",
25 | "benchmark-compile-bundle": "cd tests/benchmark/src/; ../../../node_modules/.bin/compile-modules convert -f bundle *.js -o ../bundle/main.js",
26 | "benchmark-compile-system": "cd tests/benchmark/src/; ../../../node_modules/.bin/compile-modules convert -f es6-module-transpiler-system-formatter *.js -o ../system",
27 | "benchmark": "npm run benchmark-compile-bundle; npm run benchmark-compile-cjs; npm run benchmark-compile-system; node tests/benchmark/run.js"
28 | },
29 | "license": "MIT",
30 | "dependencies": {
31 | "es6-promise": "^1.0.0"
32 | },
33 | "devDependencies": {
34 | "grunt": "^0.4.5",
35 | "grunt-cli": "0.1.*",
36 | "grunt-contrib-copy": "^0.5.0",
37 | "grunt-contrib-uglify": "^0.5.1",
38 | "grunt-contrib-jshint": "^0.10.0",
39 | "benchmark": "^1.0.0",
40 | "es6-module-transpiler": "~0.8.2",
41 | "es6-module-transpiler-system-formatter": "~0.2.0",
42 | "chai": "*",
43 | "mocha": "*",
44 | "xunit-file": "*",
45 | "chai-as-promised": "*"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /* jslint node: true */
2 |
3 | var path = require('path');
4 |
5 | var seen = Object.create(null);
6 | var internalRegistry = Object.create(null);
7 | var externalRegistry = Object.create(null);
8 |
9 | function ensuredExecute (name) {
10 | var mod = internalRegistry[name];
11 | if (mod && !seen[name]) {
12 | seen[name] = true;
13 | // one time operation to execute the module body
14 | mod.execute();
15 | }
16 | return mod && mod.proxy;
17 | }
18 | function set (name, values) {
19 | externalRegistry[name] = values;
20 | }
21 | function get (name) {
22 | return externalRegistry[name] || ensuredExecute(name);
23 | }
24 | function has (name) {
25 | return !!externalRegistry[name] || !!internalRegistry[name];
26 | }
27 |
28 |
29 |
30 | // exporting the System object
31 | exports.System = {
32 | set: set,
33 | get: get,
34 | has: has,
35 | import: function(name) {
36 | return new Promise(function (resolve, reject) {
37 | var mod = patchedRequire(path.resolve(name));
38 | return mod ? resolve(mod) : reject(new Error('Could not find module ' + name));
39 | });
40 | },
41 | register: function(name, deps, wrapper) {
42 | var mod,
43 | externalDeps = [];
44 | // creating a new module entry that will be added to the cache later on
45 | mod = {
46 | name: name,
47 | setters: null,
48 | proxy: Object.create(null),
49 | deps: deps.map(function(dep) {
50 | if (dep.charAt(0) !== '.') {
51 | externalDeps.push(dep);
52 | return dep;
53 | }
54 | var parts = dep.split('/'),
55 | parentBase = name.split('/').slice(0, -1);
56 | while (parts[0] === '.' || parts[0] === '..') {
57 | if (parts.shift() === '..') {
58 | parentBase.pop();
59 | }
60 | }
61 | return parentBase.concat(parts).join('/');
62 | }),
63 | externalDeps: externalDeps,
64 | // other modules that depends on this so we can push updates into those modules
65 | dependants: [],
66 | // method used to push updates of dependencies into the module body
67 | update: function(moduleName, moduleObj) {
68 | mod.setters[mod.deps.indexOf(moduleName)](moduleObj);
69 | },
70 | execute: wrapper
71 | };
72 | newEntry = mod;
73 | }
74 | };
75 |
76 |
77 |
78 | var newEntry;
79 | var m = require('module');
80 | var Module = require('module').Module;
81 | var originalLoader = require('module')._load;
82 |
83 | // monkey patching `require()` during a brief period of time
84 | function patchedRequire(name) {
85 | m._load = function patchedLoader(request, parent, isMain) {
86 | var values, filename, cachedModule, metaModule, esModule;
87 | newEntry = undefined;
88 | values = originalLoader.apply(this, arguments);
89 | if (newEntry) {
90 | filename = Module._resolveFilename(request, parent);
91 | cachedModule = Module._cache[filename];
92 | if (cachedModule && !cachedModule._esModule) {
93 | cachedModule._esModule = esModule = newEntry;
94 | esModule.address = filename;
95 | esModule.basePath = request.slice(0, -esModule.name.length);
96 | esModule.parent = parent;
97 | // collecting execute() and setters[]
98 | metaModule = esModule.execute(function(identifier, value) {
99 | values[identifier] = value;
100 | esModule.lock = true; // locking down the updates on the module to avoid infinite loop
101 | esModule.dependants.forEach(function(dependant) {
102 | if (!dependant.lock) {
103 | dependant.update(esModule.name, values);
104 | }
105 | });
106 | esModule.lock = false;
107 | if (!Object.getOwnPropertyDescriptor(esModule.proxy, identifier)) {
108 | Object.defineProperty(esModule.proxy, identifier, {
109 | enumerable: true,
110 | get: function() {
111 | return values[identifier];
112 | }
113 | });
114 | }
115 | return value;
116 | });
117 | esModule.setters = metaModule.setters;
118 | esModule.deps.forEach(function(dep) {
119 | var imports = externalRegistry[dep],
120 | depRequest, depFilename, depModule;
121 | if (!imports) {
122 | if (~esModule.externalDeps.indexOf(dep)) {
123 | imports = require(Module._resolveFilename(dep, cachedModule));
124 | } else {
125 | depRequest = path.resolve(path.join(esModule.basePath, dep));
126 | imports = require(depRequest);
127 | depFilename = Module._resolveFilename(depRequest, cachedModule);
128 | depModule = Module._cache[depFilename]._esModule;
129 | if (depModule) {
130 | depModule.dependants.push(esModule);
131 | }
132 | }
133 | }
134 | esModule.update(dep, imports);
135 | });
136 | // executing the module body
137 | metaModule.execute();
138 | }
139 | }
140 | return values;
141 | };
142 | var mod = require(name);
143 | // removing the patch
144 | m._load = originalLoader;
145 | return mod;
146 | }
147 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | es6-micro-loader
2 | ================
3 |
4 | ES6 System Loader Polyfill
5 |
6 | ## Overview
7 |
8 | This package implements a `System` polyfill that works on a browser, and in nodejs. The reason why we call this a micro loader is because it sides with the developers, implementing only a very tiny part of the specs to maintain the semantic of the ES modules.
9 |
10 | If you're looking for a full implementation of the ES6 Loader specs, you should check [SystemJS][] instead. Modules transpiled into `System.register()` using [es6-module-transpiler-system-formatter][] or [traceur-compiler][] should work fine with both implementations.
11 |
12 | The browser version of the loader is meant to be used to call `System.import('module-name')` to access ES6 modules included in the page after loading the transpiled modules using script tags. While, the nodejs version, is meant to be used by calling `System.import('path/to/module-name')`.
13 |
14 | ## Disclaimer
15 |
16 | This format is experimental, and it is a living creature, we will continue to tweak it until we fill it is good enough, and then we will change it again :p
17 |
18 | ## Usage
19 |
20 | ### In a browser
21 |
22 | The browser polyfill for `System` is available in this package in `dist/system-polyfill.js` and the corresponding minified version of it. This polyfill has to be included in the page before calling `System.import()` and before loading any transpiled module.
23 |
24 | _note: it provides basic network fetching mechanism to fetch modules using `script` tags by relying on `System.baseURL` configuration, which defaults to `/`, to construct the url to fetch the transpiled script in a non-blocking fashion. recommendation is to have all modules available locally somehow, right after including the polyfill in the page and not rely on the basic fetching routine that this polyfill provides.
25 |
26 | _note: you might also need to load a Promise polyfill. we recommend to use `es6-promise` or `bluebird`._
27 |
28 | Once `System` is available in a page, you can load the transpiled modules, where no order is required. E.g.:
29 |
30 | ```html
31 |
32 |
33 |
34 |
35 | ```
36 |
37 | then you can simply use the imperative form to import any of the available modules, e.g:
38 |
39 | ```javascript
40 | System.has('named/foo'); // -> true
41 | System.has('named/wrong'); // -> false
42 | System.import('named/foo').then(function (foo) {
43 | foo.init(); // do something
44 | }).catch(function (err) {
45 | console.log(err);
46 | });
47 | ```
48 |
49 | ### In nodejs
50 |
51 | Install the npm package to access the `System` polyfill:
52 |
53 | ```
54 | npm install es6-micro-loader --save
55 | ```
56 |
57 | This package exports the `System` polyfill, so you can invoke `System.import()`, `System.get()` or `System.has()`:
58 |
59 | ```javascript
60 | var System = require('es6-micro-loader');
61 | System.import("global/path/to/module");
62 | ```
63 |
64 | _note: the difference between a relative path, which is used when using `require()` in nodejs, and the global path to the module, which is what `System.import()` is expecting, is important. what is the "global/path/to/module" in the previous example? it is effectible the path from the root of the application (aka `process.env.PWD`) to the file in question. If you build a file into `build/foo.js`, then you use `build/foo` from any script in your application, independently of the `__dirname` or `__filename` from where you want to import `foo`._
65 |
66 |
67 | # Benchmark
68 |
69 | For the server side, you could do the same by transpiling the modules into CommonJS using [es6-module-transpiler][] and using `require('./path/to/module-name')`, and even try to use them thru [browserify], although, `System.register()` form provides order of magnituds better performance than CommonJS output, due to the nature of the live bindings in ES6 Modules. You can run benchmark tests in this project:
70 |
71 | ```bash
72 | git clone https://github.com/caridy/es6-micro-loader.git
73 | cd es6-micro-loader
74 | npm install
75 | npm run benchmark
76 | ```
77 |
78 | Benchmark results on the latest version:
79 |
80 | ```bash
81 | cjs x 2,847,803 ops/sec ±0.90% (92 runs sampled)
82 | system x 39,078,259 ops/sec ±0.78% (94 runs sampled)
83 | bundle x 50,916,706 ops/sec ±1.21% (82 runs sampled)
84 | Fastest is bundle
85 | ```
86 |
87 | You can look into the benchmark code here: https://github.com/caridy/es6-micro-loader/tree/master/tests/benchmark
88 |
89 | [es6-module-transpiler-system-formatter]: https://github.com/caridy/es6-module-transpiler-system-formatter
90 | [SystemJS]: https://github.com/systemjs/systemjs
91 | [es6-module-transpiler]: https://github.com/square/es6-module-transpiler
92 | [traceur-compiler]: https://github.com/google/traceur-compiler
93 | [browserify]: http://browserify.org/
94 |
95 |
96 | ## Contributing
97 |
98 | 1. Fork it
99 | 2. Create your feature branch (`git checkout -b my-new-feature`)
100 | 3. Commit your changes (`git commit -am 'Add some feature'`)
101 | 4. Push to the branch (`git push origin my-new-feature`)
102 | 5. Create new Pull Request
103 |
104 | Thanks, and enjoy living in the ES6 future!
105 |
--------------------------------------------------------------------------------
/client.js:
--------------------------------------------------------------------------------
1 | (function(exports) {
2 |
3 | 'use strict';
4 |
5 | var headEl = document.getElementsByTagName('head')[0],
6 | ie = /MSIE/.test(navigator.userAgent);
7 |
8 | /*
9 | normalizeName() is inspired by Ember's loader:
10 | https://github.com/emberjs/ember.js/blob/0591740685ee2c444f2cfdbcebad0bebd89d1303/packages/loader/lib/main.js#L39-L53
11 | */
12 | function normalizeName(child, parentBase) {
13 | if (child.charAt(0) === '/') {
14 | child = child.slice(1);
15 | }
16 | if (child.charAt(0) !== '.') {
17 | return child;
18 | }
19 | var parts = child.split('/');
20 | while (parts[0] === '.' || parts[0] === '..') {
21 | if (parts.shift() === '..') {
22 | parentBase.pop();
23 | }
24 | }
25 | return parentBase.concat(parts).join('/');
26 | }
27 |
28 | var seen = Object.create(null);
29 | var internalRegistry = Object.create(null);
30 | var externalRegistry = Object.create(null);
31 | var anonymousEntry;
32 |
33 | function ensuredExecute(name) {
34 | var mod = internalRegistry[name];
35 | if (mod && !seen[name]) {
36 | seen[name] = true;
37 | // one time operation to execute the module body
38 | mod.execute();
39 | }
40 | return mod && mod.proxy;
41 | }
42 |
43 | function set(name, values) {
44 | externalRegistry[name] = values;
45 | }
46 |
47 | function get(name) {
48 | return externalRegistry[name] || ensuredExecute(name);
49 | }
50 |
51 | function has(name) {
52 | return !!externalRegistry[name] || !!internalRegistry[name];
53 | }
54 |
55 | function createScriptNode(src, callback) {
56 | var node = document.createElement('script');
57 | // use async=false for ordered async?
58 | // parallel-load-serial-execute http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
59 | if (node.async) {
60 | node.async = false;
61 | }
62 | if (ie) {
63 | node.onreadystatechange = function() {
64 | if (/loaded|complete/.test(this.readyState)) {
65 | this.onreadystatechange = null;
66 | callback();
67 | }
68 | };
69 | } else {
70 | node.onload = node.onerror = callback;
71 | }
72 | node.setAttribute('src', src);
73 | headEl.appendChild(node);
74 | }
75 |
76 | function load(name) {
77 | return new Promise(function(resolve, reject) {
78 | createScriptNode((System.baseURL || '/') + name + '.js', function(err) {
79 | if (anonymousEntry) {
80 | System.register(name, anonymousEntry[0], anonymousEntry[1]);
81 | anonymousEntry = undefined;
82 | }
83 | var mod = internalRegistry[name];
84 | if (!mod) {
85 | reject(new Error('Error loading module ' + name));
86 | return;
87 | }
88 | Promise.all(mod.deps.map(function (dep) {
89 | if (externalRegistry[dep] || internalRegistry[dep]) {
90 | return Promise.resolve();
91 | }
92 | return load(dep);
93 | })).then(resolve, reject);
94 | });
95 | });
96 | }
97 |
98 |
99 | var System = {
100 | set: set,
101 | get: get,
102 | has: has,
103 | import: function(name) {
104 | return new Promise(function(resolve, reject) {
105 | var normalizedName = normalizeName(name, []);
106 | var mod = get(normalizedName);
107 | return mod ? resolve(mod) : load(name).then(function () {
108 | return get(normalizedName);
109 | });
110 | });
111 | },
112 | register: function(name, deps, wrapper) {
113 | if (Array.isArray(name)) {
114 | // anounymous module
115 | anonymousEntry = [];
116 | anonymousEntry.push.apply(anonymousEntry, arguments);
117 | return; // breaking to let the script tag to name it.
118 | }
119 | var proxy = Object.create(null),
120 | values = Object.create(null),
121 | mod, meta;
122 | // creating a new entry in the internal registry
123 | internalRegistry[name] = mod = {
124 | // live bindings
125 | proxy: proxy,
126 | // exported values
127 | values: values,
128 | // normalized deps
129 | deps: deps.map(function(dep) {
130 | return normalizeName(dep, name.split('/').slice(0, -1));
131 | }),
132 | // other modules that depends on this so we can push updates into those modules
133 | dependants: [],
134 | // method used to push updates of deps into the module body
135 | update: function(moduleName, moduleObj) {
136 | meta.setters[mod.deps.indexOf(moduleName)](moduleObj);
137 | },
138 | execute: function() {
139 | mod.deps.map(function(dep) {
140 | var imports = externalRegistry[dep];
141 | if (imports) {
142 | mod.update(dep, imports);
143 | } else {
144 | imports = get(dep) && internalRegistry[dep].values; // optimization to pass plain values instead of bindings
145 | if (imports) {
146 | internalRegistry[dep].dependants.push(name);
147 | mod.update(dep, imports);
148 | }
149 | }
150 | });
151 | meta.execute();
152 | }
153 | };
154 | // collecting execute() and setters[]
155 | meta = wrapper(function(identifier, value) {
156 | values[identifier] = value;
157 | mod.lock = true; // locking down the updates on the module to avoid infinite loop
158 | mod.dependants.forEach(function(moduleName) {
159 | if (internalRegistry[moduleName] && !internalRegistry[moduleName].lock) {
160 | internalRegistry[moduleName].update(name, values);
161 | }
162 | });
163 | mod.lock = false;
164 | if (!Object.getOwnPropertyDescriptor(proxy, identifier)) {
165 | Object.defineProperty(proxy, identifier, {
166 | enumerable: true,
167 | get: function() {
168 | return values[identifier];
169 | }
170 | });
171 | }
172 | return value;
173 | });
174 | }
175 | };
176 |
177 | // exporting the System object
178 | exports.System = System;
179 |
180 | })(window);
181 |
--------------------------------------------------------------------------------
/dist/system-polyfill.js:
--------------------------------------------------------------------------------
1 | (function(exports) {
2 |
3 | 'use strict';
4 |
5 | var headEl = document.getElementsByTagName('head')[0],
6 | ie = /MSIE/.test(navigator.userAgent);
7 |
8 | /*
9 | normalizeName() is inspired by Ember's loader:
10 | https://github.com/emberjs/ember.js/blob/0591740685ee2c444f2cfdbcebad0bebd89d1303/packages/loader/lib/main.js#L39-L53
11 | */
12 | function normalizeName(child, parentBase) {
13 | if (child.charAt(0) === '/') {
14 | child = child.slice(1);
15 | }
16 | if (child.charAt(0) !== '.') {
17 | return child;
18 | }
19 | var parts = child.split('/');
20 | while (parts[0] === '.' || parts[0] === '..') {
21 | if (parts.shift() === '..') {
22 | parentBase.pop();
23 | }
24 | }
25 | return parentBase.concat(parts).join('/');
26 | }
27 |
28 | var seen = Object.create(null);
29 | var internalRegistry = Object.create(null);
30 | var externalRegistry = Object.create(null);
31 | var anonymousEntry;
32 |
33 | function ensuredExecute(name) {
34 | var mod = internalRegistry[name];
35 | if (mod && !seen[name]) {
36 | seen[name] = true;
37 | // one time operation to execute the module body
38 | mod.execute();
39 | }
40 | return mod && mod.proxy;
41 | }
42 |
43 | function set(name, values) {
44 | externalRegistry[name] = values;
45 | }
46 |
47 | function get(name) {
48 | return externalRegistry[name] || ensuredExecute(name);
49 | }
50 |
51 | function has(name) {
52 | return !!externalRegistry[name] || !!internalRegistry[name];
53 | }
54 |
55 | function createScriptNode(src, callback) {
56 | var node = document.createElement('script');
57 | // use async=false for ordered async?
58 | // parallel-load-serial-execute http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
59 | if (node.async) {
60 | node.async = false;
61 | }
62 | if (ie) {
63 | node.onreadystatechange = function() {
64 | if (/loaded|complete/.test(this.readyState)) {
65 | this.onreadystatechange = null;
66 | callback();
67 | }
68 | };
69 | } else {
70 | node.onload = node.onerror = callback;
71 | }
72 | node.setAttribute('src', src);
73 | headEl.appendChild(node);
74 | }
75 |
76 | function load(name) {
77 | return new Promise(function(resolve, reject) {
78 | createScriptNode((System.baseURL || '/') + name + '.js', function(err) {
79 | if (anonymousEntry) {
80 | System.register(name, anonymousEntry[0], anonymousEntry[1]);
81 | anonymousEntry = undefined;
82 | }
83 | var mod = internalRegistry[name];
84 | if (!mod) {
85 | reject(new Error('Error loading module ' + name));
86 | return;
87 | }
88 | Promise.all(mod.deps.map(function (dep) {
89 | if (externalRegistry[dep] || internalRegistry[dep]) {
90 | return Promise.resolve();
91 | }
92 | return load(dep);
93 | })).then(resolve, reject);
94 | });
95 | });
96 | }
97 |
98 |
99 | var System = {
100 | set: set,
101 | get: get,
102 | has: has,
103 | import: function(name) {
104 | return new Promise(function(resolve, reject) {
105 | var normalizedName = normalizeName(name, []);
106 | var mod = get(normalizedName);
107 | return mod ? resolve(mod) : load(name).then(function () {
108 | return get(normalizedName);
109 | });
110 | });
111 | },
112 | register: function(name, deps, wrapper) {
113 | if (Array.isArray(name)) {
114 | // anounymous module
115 | anonymousEntry = [];
116 | anonymousEntry.push.apply(anonymousEntry, arguments);
117 | return; // breaking to let the script tag to name it.
118 | }
119 | var proxy = Object.create(null),
120 | values = Object.create(null),
121 | mod, meta;
122 | // creating a new entry in the internal registry
123 | internalRegistry[name] = mod = {
124 | // live bindings
125 | proxy: proxy,
126 | // exported values
127 | values: values,
128 | // normalized deps
129 | deps: deps.map(function(dep) {
130 | return normalizeName(dep, name.split('/').slice(0, -1));
131 | }),
132 | // other modules that depends on this so we can push updates into those modules
133 | dependants: [],
134 | // method used to push updates of deps into the module body
135 | update: function(moduleName, moduleObj) {
136 | meta.setters[mod.deps.indexOf(moduleName)](moduleObj);
137 | },
138 | execute: function() {
139 | mod.deps.map(function(dep) {
140 | var imports = externalRegistry[dep];
141 | if (imports) {
142 | mod.update(dep, imports);
143 | } else {
144 | imports = get(dep) && internalRegistry[dep].values; // optimization to pass plain values instead of bindings
145 | if (imports) {
146 | internalRegistry[dep].dependants.push(name);
147 | mod.update(dep, imports);
148 | }
149 | }
150 | });
151 | meta.execute();
152 | }
153 | };
154 | // collecting execute() and setters[]
155 | meta = wrapper(function(identifier, value) {
156 | values[identifier] = value;
157 | mod.lock = true; // locking down the updates on the module to avoid infinite loop
158 | mod.dependants.forEach(function(moduleName) {
159 | if (internalRegistry[moduleName] && !internalRegistry[moduleName].lock) {
160 | internalRegistry[moduleName].update(name, values);
161 | }
162 | });
163 | mod.lock = false;
164 | if (!Object.getOwnPropertyDescriptor(proxy, identifier)) {
165 | Object.defineProperty(proxy, identifier, {
166 | enumerable: true,
167 | get: function() {
168 | return values[identifier];
169 | }
170 | });
171 | }
172 | return value;
173 | });
174 | }
175 | };
176 |
177 | // exporting the System object
178 | exports.System = System;
179 |
180 | })(window);
181 |
--------------------------------------------------------------------------------