├── .npmignore ├── test ├── fixtures │ ├── conditional-tree │ │ ├── ENV.js │ │ ├── mock.js │ │ ├── conditions.js │ │ ├── config.dev.js │ │ ├── interpolated-2.js │ │ ├── interpolate-1-dep.js │ │ ├── pkg │ │ │ ├── env-condition.js │ │ │ └── env-condition-browser.js │ │ ├── interpolated-1.js │ │ └── custom-conditions.js │ ├── test-tree │ │ ├── some │ │ ├── text.txt │ │ ├── jquery.js │ │ ├── amd-meta-dep.js │ │ ├── cjs-globals.js │ │ ├── file.json │ │ ├── cjs-4.js │ │ ├── cjs-5.js │ │ ├── lodash │ │ │ └── lodash.js │ │ ├── sameName │ │ │ ├── 1 │ │ │ │ └── amd.js │ │ │ └── 2 │ │ │ │ └── amd.js │ │ ├── Buffer.js │ │ ├── cjs-in-12.js │ │ ├── cjs-in-13.js │ │ ├── amd-6a.js │ │ ├── amd-7.js │ │ ├── cjs space.js │ │ ├── cjs-resolve.js │ │ ├── second.js │ │ ├── amd-6b.js │ │ ├── amd-3.js │ │ ├── cjs-2.js │ │ ├── cjs-3.js │ │ ├── amd-2.js │ │ ├── runtime.js │ │ ├── amd-8.js │ │ ├── global.js │ │ ├── component.jsx │ │ ├── amd-5a.js │ │ ├── amd-5b.js │ │ ├── cjs-1.js │ │ ├── amd.js │ │ ├── global-inner.js │ │ ├── amd-12.js │ │ ├── global-outer.js │ │ ├── amd-4.js │ │ ├── sfx-format-01.js │ │ ├── jsx.js │ │ ├── first.js │ │ ├── amd-10.js │ │ ├── amd-9.js │ │ ├── class-def.js │ │ ├── cjs.js │ │ ├── third.js │ │ ├── amd-1.js │ │ ├── json-plugin.js │ │ ├── amd-11.js │ │ ├── plugin.js │ │ ├── text-plugin.js │ │ ├── umd.js │ │ ├── example │ │ ├── example.js │ │ ├── example.map │ │ ├── sfx-format-02.js │ │ ├── babel.js │ │ └── register.js │ ├── circular-tree │ │ ├── a.js │ │ ├── b.js │ │ └── c.js │ ├── error-tree │ │ └── a.js │ ├── es-tree │ │ ├── b.js │ │ ├── a.js │ │ ├── global-dep-loader.js │ │ └── global-dep.js │ ├── sourcemaps-expectations │ │ ├── babel.chain.multi.json │ │ ├── babel.chain.single.json │ │ ├── traceur.chain.multi.json │ │ ├── traceur.chain.single.json │ │ ├── babel.tree.single.json │ │ ├── traceur.tree.single.json │ │ ├── babel.tree.multi.json │ │ └── traceur.tree.multi.json │ ├── test-cache-tree │ │ └── simple.js │ ├── conditional-tree.config.js │ └── test-tree.config.js ├── jquery.js ├── mocha.opts ├── setup.js ├── test-circular.js ├── utils.js ├── errors.js ├── test-sfx.html ├── test-sfx-amd.html ├── anon-compile.js ├── conditional-canonicals.js ├── static-optimize.js ├── canonicals.js ├── test-load-config.js ├── test-post-order.js ├── conditional-builds.js ├── test-build.js ├── test-build.html ├── test-build-cache.js ├── sourcemaps.js └── arithmetic.js ├── index.js ├── templates ├── sfx-amd.js ├── sfx-esm.js ├── sfx-global.js ├── minify.sh ├── sfx-cjs.js ├── sfx-umd.js ├── global-helpers.min.js ├── global-helpers.js ├── sfx-core.min.js └── sfx-core.js ├── .gitignore ├── .travis.yml ├── compilers ├── register.js ├── global.js ├── cjs.js ├── compiler.js ├── json.js ├── amd.js └── esm.js ├── LICENSE ├── package.json ├── lib ├── get-deferred-imports.js ├── sourcemaps.js ├── output.js ├── rollup.js └── arithmetic.js ├── docs └── api.md └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/ENV.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/jquery.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; -------------------------------------------------------------------------------- /test/fixtures/test-tree/some: -------------------------------------------------------------------------------- 1 | 'asdf'; 2 | -------------------------------------------------------------------------------- /test/fixtures/circular-tree/a.js: -------------------------------------------------------------------------------- 1 | import 'b.js'; -------------------------------------------------------------------------------- /test/fixtures/error-tree/a.js: -------------------------------------------------------------------------------- 1 | import 'asdfe'; -------------------------------------------------------------------------------- /test/fixtures/es-tree/b.js: -------------------------------------------------------------------------------- 1 | export var b = 'b'; -------------------------------------------------------------------------------- /test/fixtures/circular-tree/b.js: -------------------------------------------------------------------------------- 1 | import 'c.js'; 2 | -------------------------------------------------------------------------------- /test/fixtures/circular-tree/c.js: -------------------------------------------------------------------------------- 1 | import 'a.js'; 2 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/text.txt: -------------------------------------------------------------------------------- 1 | This is some text -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/builder'); 2 | -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/mock.js: -------------------------------------------------------------------------------- 1 | console.log('mock'); -------------------------------------------------------------------------------- /test/fixtures/test-tree/jquery.js: -------------------------------------------------------------------------------- 1 | var jquery = {}; 2 | -------------------------------------------------------------------------------- /test/fixtures/sourcemaps-expectations/babel.chain.multi.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/sourcemaps-expectations/babel.chain.single.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/sourcemaps-expectations/traceur.chain.multi.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/sourcemaps-expectations/traceur.chain.single.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-meta-dep.js: -------------------------------------------------------------------------------- 1 | window.meta = 'dep'; -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs-globals.js: -------------------------------------------------------------------------------- 1 | module.exports = Buffer; -------------------------------------------------------------------------------- /test/fixtures/test-tree/file.json: -------------------------------------------------------------------------------- 1 | { 2 | "some": "json" 3 | } -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/conditions.js: -------------------------------------------------------------------------------- 1 | export var test = '1'; -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/config.dev.js: -------------------------------------------------------------------------------- 1 | export default 'dev'; -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/interpolated-2.js: -------------------------------------------------------------------------------- 1 | export default 2; 2 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs-4.js: -------------------------------------------------------------------------------- 1 | module.exports = { name: 'cjs4' }; 2 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs-5.js: -------------------------------------------------------------------------------- 1 | module.exports = { name: 'cjs5' }; 2 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/lodash/lodash.js: -------------------------------------------------------------------------------- 1 | export var lodash = 'lodash'; -------------------------------------------------------------------------------- /test/fixtures/test-tree/sameName/1/amd.js: -------------------------------------------------------------------------------- 1 | define('a1', () => {}); 2 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/sameName/2/amd.js: -------------------------------------------------------------------------------- 1 | define('a2', () => {}); 2 | -------------------------------------------------------------------------------- /test/fixtures/es-tree/a.js: -------------------------------------------------------------------------------- 1 | export {b} from './b.js'; 2 | export var a = 'a'; -------------------------------------------------------------------------------- /test/fixtures/test-tree/Buffer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = 'Buffer'; -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require test/setup.js 2 | --ui tdd 3 | --timeout 120000 4 | -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/interpolate-1-dep.js: -------------------------------------------------------------------------------- 1 | module.exports = '1-dep'; 2 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs-in-12.js: -------------------------------------------------------------------------------- 1 | module.exports = { name: 'cjs-in-12' }; 2 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs-in-13.js: -------------------------------------------------------------------------------- 1 | module.exports = { name: 'cjs-in-13' }; 2 | -------------------------------------------------------------------------------- /test/fixtures/es-tree/global-dep-loader.js: -------------------------------------------------------------------------------- 1 | export * from '../test-tree/global-inner.js'; -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-6a.js: -------------------------------------------------------------------------------- 1 | define(function(require) { 2 | this.p = 'a'; 3 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-7.js: -------------------------------------------------------------------------------- 1 | var factory = { amd: 'object' }; 2 | define(factory); -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs space.js: -------------------------------------------------------------------------------- 1 | module.exports = { name: 'cjs space' }; 2 | 3 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs-resolve.js: -------------------------------------------------------------------------------- 1 | module.exports = require.resolve('./first.js'); -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/pkg/env-condition.js: -------------------------------------------------------------------------------- 1 | module.exports = 'environment condition'; -------------------------------------------------------------------------------- /templates/sfx-amd.js: -------------------------------------------------------------------------------- 1 | (function(factory) { 2 | define(${JSON.stringify(deps)}, factory); 3 | }); -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | global.Promise = require('bluebird'); 2 | global.assert = require('chai').assert; -------------------------------------------------------------------------------- /templates/sfx-esm.js: -------------------------------------------------------------------------------- 1 | (function(factory) { 2 | main = factory(); 3 | }); 4 | 5 | export default main; -------------------------------------------------------------------------------- /test/fixtures/test-tree/second.js: -------------------------------------------------------------------------------- 1 | import './third.js'; 2 | import './cjs.js'; 3 | export var q = 4; 4 | -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/pkg/env-condition-browser.js: -------------------------------------------------------------------------------- 1 | module.exports = 'environment condition browser'; -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-6b.js: -------------------------------------------------------------------------------- 1 | define(['require', 'exports'], function() { 2 | this.p = 'b'; 3 | }); -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/interpolated-1.js: -------------------------------------------------------------------------------- 1 | require('./interpolate-1-dep.js'); 2 | module.exports = '1'; 3 | -------------------------------------------------------------------------------- /test/fixtures/test-cache-tree/simple.js: -------------------------------------------------------------------------------- 1 | define([], function(module) { 2 | console.log('I hate caches'); 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/es-tree/global-dep.js: -------------------------------------------------------------------------------- 1 | // export * from '../test-tree/global-inner.js'; 2 | import './global-dep-loader.js'; -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-3.js: -------------------------------------------------------------------------------- 1 | define(function(req, exports, module) { 2 | module.exports = req('./first.js'); 3 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs-2.js: -------------------------------------------------------------------------------- 1 | var shared1 = require('./cjs-in-12.js'); 2 | 3 | module.exports = { name: 'cjs2' }; 4 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs-3.js: -------------------------------------------------------------------------------- 1 | var shared1 = require('./cjs-in-13.js'); 2 | 3 | module.exports = { name: 'cjs3' }; 4 | 5 | -------------------------------------------------------------------------------- /templates/sfx-global.js: -------------------------------------------------------------------------------- 1 | (function(factory) { 2 | ${globalName ? globalName + ' = ' : ''}factory(${globalDeps.join(', ')}); 3 | }); -------------------------------------------------------------------------------- /test/fixtures/conditional-tree/custom-conditions.js: -------------------------------------------------------------------------------- 1 | export * from './config.#{ENV|environment}.js'; 2 | import './mock.js#?ENV|mock'; -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-2.js: -------------------------------------------------------------------------------- 1 | 2 | /* asdf 3 | 4 | define({ amd: 2 }); // ? 5 | 6 | */ 7 | 8 | define(window.m = { amd: '2' }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/runtime.js: -------------------------------------------------------------------------------- 1 | export class test extends Object { 2 | constructor() { 3 | super(); 4 | q; 5 | } 6 | } -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-8.js: -------------------------------------------------------------------------------- 1 | "deps ./amd-meta-dep.js"; 2 | if (!window.meta) 3 | define(function() { 4 | return window.meta; 5 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/global.js: -------------------------------------------------------------------------------- 1 | "exports myjquery.test"; 2 | "globals.myjquery ./jquery.js"; 3 | 4 | this.myjquery.test = 'output'; 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | test/output 4 | .DS_Store 5 | test/output.js 6 | test/output.js.map 7 | dist 8 | .idea/ 9 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/component.jsx: -------------------------------------------------------------------------------- 1 | var React = {}; 2 | React.createElement = function() {;}; 3 | export var component =
; 4 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-5a.js: -------------------------------------------------------------------------------- 1 | function factory() { 2 | window.jquery = '1'; 3 | return { jquery: '1' }; 4 | } 5 | 6 | define('jquery', [], factory); -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-5b.js: -------------------------------------------------------------------------------- 1 | function factory(first) { 2 | return { jquery: '1', first: first }; 3 | } 4 | 5 | define('jquery', ['./first.js'], factory); -------------------------------------------------------------------------------- /templates/minify.sh: -------------------------------------------------------------------------------- 1 | uglifyjs sfx-core.js -cm --screw-ie8 | sed 's/.$//' > sfx-core.min.js 2 | uglifyjs global-helpers.js -cm --screw-ie8 > global-helpers.min.js 3 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs-1.js: -------------------------------------------------------------------------------- 1 | var shared1 = require('./cjs-in-12.js'); 2 | var shared2 = require('./cjs-in-13.js'); 3 | 4 | module.exports = { name: 'cjs1' }; 5 | -------------------------------------------------------------------------------- /templates/sfx-cjs.js: -------------------------------------------------------------------------------- 1 | (function(factory) { 2 | module.exports = factory(${deps.map(function(dep) { 3 | return 'require("' + dep + '")'; 4 | }).join(', ')}); 5 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd.js: -------------------------------------------------------------------------------- 1 | define(['./global.js', './some.js!./plugin.js', './text.txt!./text-plugin.js'], function(a, b, c) { 2 | return { is: 'amd', text: c }; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/global-inner.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var p = 5; 3 | }); 4 | if (true) { 5 | let q = 5; 6 | } 7 | 8 | { 9 | let r = 10; 10 | var r = 10; 11 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | git: 2 | depth: 1 3 | language: node_js 4 | node_js: 5 | # - '4' 6 | - '6' 7 | 8 | before_install: 9 | - npm install 10 | script: 11 | - npm run test 12 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-12.js: -------------------------------------------------------------------------------- 1 | define(function() { 2 | 3 | var a = 'a'; 4 | 5 | function internal() { 6 | define(a, [], {}); 7 | } 8 | 9 | return { a: a }; 10 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/global-outer.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | p = 6; 3 | }); 4 | var p = 5; 5 | 6 | if (false) 7 | var window = 5; 8 | 9 | function q(preprocessedTokens, eval) { 10 | eval(); 11 | } -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-4.js: -------------------------------------------------------------------------------- 1 | function factory(second) { 2 | var define = 'asdf'; 3 | return second; 4 | } 5 | 6 | if (false) 7 | System.import('x'); 8 | 9 | 10 | define(['./second.js'], factory) -------------------------------------------------------------------------------- /test/fixtures/test-tree/sfx-format-01.js: -------------------------------------------------------------------------------- 1 | import comp from './component.jsx!jsx.js'; 2 | export function basic() { 3 | return 1 + 1; 4 | } 5 | export var component = comp; 6 | export default { 7 | 'hello': 'world' 8 | }; -------------------------------------------------------------------------------- /test/fixtures/sourcemaps-expectations/babel.tree.single.json: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["test/fixtures/test-tree/amd-2.js"],"names":["window","m","amd"],"mappings":";;AACA;;;;;;AAMA;AAAA,oBAAOA,OAAOC,CAAP,GAAW,EAAEC,KAAK,GAAP,EAAlB;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA","file":"output.js"} -------------------------------------------------------------------------------- /test/fixtures/sourcemaps-expectations/traceur.tree.single.json: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["test/fixtures/test-tree/amd-2.js"],"names":["window","m","amd"],"mappings":";;AACA;;;;;;AAMA;AAAA,oBAAOA,OAAOC,CAAP,GAAW,EAAEC,KAAK,GAAP,EAAlB;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA","file":"output.js"} -------------------------------------------------------------------------------- /test/fixtures/test-tree/jsx.js: -------------------------------------------------------------------------------- 1 | var babel = require('babel'); 2 | 3 | exports.translate = function(load) { 4 | var output = babel.transform(load.source, { modules: 'system', sourceMaps: true }); 5 | load.source = output.code; 6 | load.metadata.sourceMap = output.map; 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/first.js: -------------------------------------------------------------------------------- 1 | import 'jquery-cdn'; 2 | import '@empty'; 3 | import { q } from "./second.js"; 4 | import './amd.js'; 5 | import './component.jsx!./jsx.js'; 6 | import {some} from './file.json'; 7 | 8 | export var j = some; 9 | 10 | export var p = 5; 11 | q; -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-10.js: -------------------------------------------------------------------------------- 1 | define('a', { 2 | a: 'a' 3 | }); 4 | 5 | define('b', { 6 | b: 'b' 7 | }); 8 | 9 | define(['c'], function(c) { 10 | return c; 11 | }); 12 | 13 | define('c', ['b'], function(b) { 14 | return { 15 | b: b, 16 | c: 'c' 17 | }; 18 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-9.js: -------------------------------------------------------------------------------- 1 | define(['c'], function(c) { 2 | return c; 3 | }); 4 | 5 | define('a', { 6 | a: 'a' 7 | }); 8 | 9 | define('b', { 10 | b: 'b' 11 | }); 12 | 13 | define('c', ['b'], function(b) { 14 | return { 15 | b: b, 16 | c: 'c' 17 | }; 18 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/class-def.js: -------------------------------------------------------------------------------- 1 | class Foo { 2 | constructor() { 3 | this.bar = 'bar'; 4 | } 5 | static get baz() { return 'baz'; } 6 | } 7 | 8 | const foo = new Foo(); 9 | 10 | module.exports = { 11 | foo: foo, 12 | barbaz: `${foo.bar}${Foo.baz}`, 13 | }; 14 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/cjs.js: -------------------------------------------------------------------------------- 1 | console.log(__filename); 2 | 3 | (function(require) { 4 | if (typeof require != 'undefined') 5 | exports.cjs = true; 6 | 7 | if (false) 8 | require('some' + 'expression'); 9 | })(require); 10 | 11 | exports.env = process.env.NODE_ENV; 12 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/third.js: -------------------------------------------------------------------------------- 1 | System.register(['./second.js'], function($__export) { 2 | return { 3 | setters: [function() {}], 4 | execute: function() { 5 | $__export('some', 'exports'); 6 | $__export('pi', 'π'); 7 | $__export('name', __moduleName); 8 | } 9 | }; 10 | }); -------------------------------------------------------------------------------- /test/fixtures/conditional-tree.config.js: -------------------------------------------------------------------------------- 1 | System.config({ 2 | map: { 3 | condition: 'conditions.js' 4 | }, 5 | packages: { 6 | 'pkg': { 7 | map: { 8 | './env-condition': { 9 | 'browser': './env-condition-browser' 10 | } 11 | } 12 | } 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-1.js: -------------------------------------------------------------------------------- 1 | define(['./first.js', './second.js', 'require', 'module'], function(first, second, require, module) { 2 | 3 | module.exports = { 4 | first: first, 5 | second: require('./second.js'), 6 | utfChar: '\u221e' 7 | }; 8 | 9 | if (DEBUG) { 10 | console.log('debug mode'); 11 | } 12 | 13 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/json-plugin.js: -------------------------------------------------------------------------------- 1 | /* 2 | JSON plugin 3 | */ 4 | 5 | define({ 6 | translate: function(load) { 7 | if (this.builder) 8 | return 'module.exports = ' + JSON.stringify(JSON.parse(load.source)); 9 | }, 10 | instantiate: function(load) { 11 | if (!this.builder) 12 | return JSON.parse(load.source); 13 | } 14 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/amd-11.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var define = System.amdDefine; 3 | define('a', {a: 'a'}); 4 | define('b', {b: 'b'}); 5 | define("amd-10.js", ["c"], function(c) { 6 | return c; 7 | }); 8 | define('c', ['b'], function(b) { 9 | return { 10 | b: b, 11 | c: 'c' 12 | }; 13 | }); 14 | 15 | })(); 16 | 17 | define(['amd-10.js'], function(m) { 18 | return m; 19 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/plugin.js: -------------------------------------------------------------------------------- 1 | //exports.build = false; 2 | 3 | define(function() { 4 | return { 5 | fetch: function() { 6 | return ''; 7 | }, 8 | listAssets: function(loads) { 9 | return loads.map(function(load) { 10 | return { 11 | url: load.address, 12 | contentType: 'text/plain' 13 | }; 14 | }); 15 | } 16 | }; 17 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/text-plugin.js: -------------------------------------------------------------------------------- 1 | exports.translate = function(load) { 2 | return 'module.exports = "' + load.source 3 | .replace(/(["\\])/g, '\\$1') 4 | .replace(/[\f]/g, "\\f") 5 | .replace(/[\b]/g, "\\b") 6 | .replace(/[\n]/g, "\\n") 7 | .replace(/[\t]/g, "\\t") 8 | .replace(/[\r]/g, "\\r") 9 | .replace(/[\u2028]/g, "\\u2028") 10 | .replace(/[\u2029]/g, "\\u2029") 11 | + '";'; 12 | } -------------------------------------------------------------------------------- /test/test-circular.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | 3 | var builder = new Builder('test/fixtures/circular-tree'); 4 | 5 | suite('Test circular tree', function(err) { 6 | test('Circular tree', function() { 7 | builder.reset(); 8 | 9 | return builder.bundle('a.js') 10 | .then(function(out) { 11 | assert(out.entryPoints.length == 1 && out.entryPoints[0] == 'a.js'); 12 | }); 13 | }); 14 | }); -------------------------------------------------------------------------------- /compilers/register.js: -------------------------------------------------------------------------------- 1 | var compiler = require('./compiler'); 2 | 3 | exports.compile = function (load, opts, loader) { 4 | return compiler.compile(load, opts, [require('babel-plugin-transform-system-register').default, { 5 | moduleName: !opts.anonymous && load.name, 6 | map: function (dep) { 7 | return opts.normalize ? load.depMap[dep] : dep; 8 | }, 9 | systemGlobal: opts.systemGlobal 10 | }]); 11 | }; 12 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/umd.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['require', 'exports', 'cjs.js'], factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory(require, exports, module); 6 | } else { 7 | root.wAnalytics = factory(); 8 | } 9 | }(this, function(require, exports) { 10 | require('cjs.js'); 11 | exports.umd = 'detection'; 12 | })); -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | var builder = new Builder(); 3 | 4 | builder.config({ 5 | paths: { 6 | 'npm:': 'node_modules/' 7 | }, 8 | map: { 9 | x: 'npm:x' 10 | } 11 | }); 12 | 13 | var utils = require('../lib/utils.js'); 14 | 15 | suite('Aliasing', function() { 16 | test('getAlias', function(done) { 17 | assert.equal(utils.getAlias(builder.loader, 'npm:x'), 'x'); 18 | done(); 19 | }); 20 | }); -------------------------------------------------------------------------------- /test/errors.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | var builder = new Builder('test/fixtures/error-tree'); 3 | 4 | suite('Errors', function() { 5 | test('Non-existing file', function() { 6 | return builder.bundle('asdf') 7 | .catch(function(e) { 8 | assert(e.toString().indexOf('ENOENT') != -1); 9 | }); 10 | }); 11 | 12 | test('Non-existing dependency', function() { 13 | return builder.bundle('a.js') 14 | .catch(function(e) { 15 | assert(e.stack.toString().indexOf('a.js') != -1); 16 | }); 17 | }); 18 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/example: -------------------------------------------------------------------------------- 1 | module Sayings { 2 | export class Greeter { 3 | greeting: string; 4 | constructor(message: string) { 5 | this.greeting = message; 6 | } 7 | greet() { 8 | return "Hello, " + this.greeting; 9 | } 10 | } 11 | } 12 | var greeter = new Sayings.Greeter("world"); 13 | 14 | var button = document.createElement('button'); 15 | button.innerText = "Say Hello"; 16 | button.onclick = function() { 17 | alert(greeter.greet()); 18 | }; 19 | 20 | document.body.appendChild(button); 21 | -------------------------------------------------------------------------------- /templates/sfx-umd.js: -------------------------------------------------------------------------------- 1 | (function(factory) { 2 | if (typeof define == 'function' && define.amd) 3 | define(${JSON.stringify(deps)}, factory); 4 | else if (typeof module == 'object' && module.exports && typeof require == 'function') 5 | module.exports = factory(${deps.map(function(dep) { 6 | return 'require("' + dep + '")'; 7 | }).join(', ')}); 8 | else 9 | ${ deps.length && !globalDeps.length 10 | ? 'throw new Error("Module must be loaded as AMD or CommonJS")' 11 | : (globalName ? globalName + ' = ' : '') + 'factory(' + (globalDeps.length ? globalDeps.join(', ') : '') + ')'}; 12 | }); -------------------------------------------------------------------------------- /test/fixtures/test-tree/example.js: -------------------------------------------------------------------------------- 1 | var Sayings; 2 | (function (Sayings) { 3 | var Greeter = (function () { 4 | function Greeter(message) { 5 | this.greeting = message; 6 | } 7 | Greeter.prototype.greet = function () { 8 | return "Hello, " + this.greeting; 9 | }; 10 | return Greeter; 11 | })(); 12 | Sayings.Greeter = Greeter; 13 | })(Sayings || (Sayings = {})); 14 | var greeter = new Sayings.Greeter("world"); 15 | var button = document.createElement('button'); 16 | button.innerText = "Say Hello"; 17 | button.onclick = function () { 18 | alert(greeter.greet()); 19 | }; 20 | document.body.appendChild(button); 21 | //# sourceMappingURL=example.map -------------------------------------------------------------------------------- /test/fixtures/test-tree/example.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"example.js","sourceRoot":"","sources":["example"],"names":["Sayings","Sayings.Greeter","Sayings.Greeter.constructor","Sayings.Greeter.greet"],"mappings":"AAAA,IAAO,OAAO,CAUb;AAVD,WAAO,OAAO,EAAC,CAAC;IACZA;QAEIC,iBAAYA,OAAeA;YACvBC,IAAIA,CAACA,QAAQA,GAAGA,OAAOA,CAACA;QAC5BA,CAACA;QACDD,uBAAKA,GAALA;YACIE,MAAMA,CAACA,SAASA,GAAGA,IAAIA,CAACA,QAAQA,CAACA;QACrCA,CAACA;QACLF,cAACA;IAADA,CAACA,AARDD,IAQCA;IARYA,eAAOA,UAQnBA,CAAAA;AACLA,CAACA,EAVM,OAAO,KAAP,OAAO,QAUb;AACD,IAAI,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAE3C,IAAI,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;AAC9C,MAAM,CAAC,SAAS,GAAG,WAAW,CAAC;AAC/B,MAAM,CAAC,OAAO,GAAG;IACb,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;AAC3B,CAAC,CAAC;AAEF,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC"} -------------------------------------------------------------------------------- /test/fixtures/test-tree/sfx-format-02.js: -------------------------------------------------------------------------------- 1 | System.register(["./component.jsx!jsx.js"], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | function basic() { 5 | return 1 + 1; 6 | } 7 | exports_1("basic", basic); 8 | var component_jsx_jsx_js_1, component; 9 | return { 10 | setters: [ 11 | function (component_jsx_jsx_js_1_1) { 12 | component_jsx_jsx_js_1 = component_jsx_jsx_js_1_1; 13 | } 14 | ], 15 | execute: function () { 16 | exports_1("component", component = component_jsx_jsx_js_1.default); 17 | exports_1("default", { 18 | 'hello': 'world' 19 | }); 20 | } 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /test/fixtures/test-tree.config.js: -------------------------------------------------------------------------------- 1 | System.config({ 2 | paths: { 3 | '*': './test/fixtures/test-tree/*', 4 | '*.jade': './test/dummy/*.jade', 5 | 'babel': './node_modules/babel/node_modules/babel-core/browser.js', 6 | 'babel-helpers': './node_modules/babel/node_modules/babel-core/external-helpers.js', 7 | 'traceur': './node_modules/traceur/bin/traceur.js', 8 | 'traceur-runtime': './node_modules/traceur/bin/traceur-runtime.js', 9 | 'typescript': './node_modules/typescript/lib/typescript.js' 10 | }, 11 | map: { 12 | lodash: 'lodash/lodash.js' 13 | }, 14 | meta: { 15 | 'babel': { 16 | format: 'global' 17 | }, 18 | 'jquery-cdn': { 19 | build: false 20 | }, 21 | 'cjs-globals.js': { 22 | globals: { 23 | Buffer: 'Buffer.js' 24 | } 25 | }, 26 | '*.json': { 27 | loader: 'json-plugin.js' 28 | } 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /test/test-sfx.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 11 | 12 | -------------------------------------------------------------------------------- /compilers/global.js: -------------------------------------------------------------------------------- 1 | var compiler = require('./compiler'); 2 | 3 | exports.compile = function (load, opts, loader) { 4 | var deps = opts.normalize ? load.deps.map(function(dep) { return load.depMap[dep]; }) : load.deps; 5 | 6 | // send normalized globals into the transformer 7 | var normalizedGlobals; 8 | if (load.metadata.globals) { 9 | normalizedGlobals = {}; 10 | for (var g in load.metadata.globals) 11 | normalizedGlobals[g] = opts.normalize ? load.depMap[load.metadata.globals[g]] : load.metadata.globals[g]; 12 | } 13 | 14 | return compiler.compile(load, opts, [require('babel-plugin-transform-global-system-wrapper').default, { 15 | deps: deps, 16 | exportName: load.metadata.exports, 17 | globals: normalizedGlobals, 18 | moduleName: !opts.anonymous && load.name, 19 | systemGlobal: opts.systemGlobal, 20 | esModule: load.metadata.esModule 21 | }]); 22 | }; 23 | 24 | exports.sfx = function(loader) { 25 | return require('fs').readFileSync(require('path').resolve(__dirname, '../templates/global-helpers.min.js')).toString(); 26 | }; 27 | -------------------------------------------------------------------------------- /templates/global-helpers.min.js: -------------------------------------------------------------------------------- 1 | !function(e){function r(e,r){for(var n=e.split(".");n.length;)r=r[n.shift()];return r}function n(n){if("string"==typeof n)return r(n,e);if(!(n instanceof Array))throw new Error("Global exports must be a string or array.");for(var t={},o=0;o 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 42 | 43 | -------------------------------------------------------------------------------- /compilers/cjs.js: -------------------------------------------------------------------------------- 1 | var compiler = require('./compiler'); 2 | 3 | exports.compile = function (load, opts, loader) { 4 | 5 | opts.moduleId = !opts.anonymous && load.name; 6 | 7 | var deps = opts.normalize ? load.deps.map(function (dep) { return load.depMap[dep]; }) : load.deps; 8 | 9 | // send normalized globals into the transformer 10 | var normalizedGlobals; 11 | if (load.metadata.globals) { 12 | normalizedGlobals = {}; 13 | for (var g in load.metadata.globals) 14 | normalizedGlobals[g] = opts.normalize ? load.depMap[load.metadata.globals[g]] : load.metadata.globals[g]; 15 | } 16 | 17 | // remove loader base url from path 18 | var path; 19 | if (opts.static) { 20 | path = load.path; 21 | if (path.substr(0, loader.baseURL.length) == loader.baseURL) 22 | path = path.substr(loader.baseURL.length); 23 | } 24 | 25 | return compiler.compile(load, opts, [require('babel-plugin-transform-cjs-system-wrapper').default, { 26 | deps: deps, 27 | globals: normalizedGlobals, 28 | optimize: opts.production, 29 | map: function(dep) { 30 | return opts.normalize ? load.depMap[dep] : dep; 31 | }, 32 | path: path, 33 | static: opts.static, 34 | systemGlobal: opts.systemGlobal, 35 | esModule: load.metadata.esModule 36 | }]); 37 | }; 38 | -------------------------------------------------------------------------------- /test/anon-compile.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | var builder = new Builder(); 3 | 4 | builder.loadConfigSync('./test/fixtures/test-tree.config.js'); 5 | 6 | builder.config({ transpiler: 'babel' }); 7 | 8 | suite('Anonymous Compilation', function() { 9 | test('AMD', function(done) { 10 | builder.compile('amd.js').then(function(output) { 11 | assert.match(output.source, /System\.registerDynamic\(\['\.\/global.js', '\.\/some.js!\.\/plugin.js', '\.\/text.txt!\.\/text-plugin.js'\]/); 12 | }) 13 | .then(done, done); 14 | }); 15 | 16 | test('CJS', function(done) { 17 | builder.compile('cjs.js').then(function(output) { 18 | assert.match(output.source, /System\.registerDynamic\(\[\]/); 19 | }) 20 | .then(done, done); 21 | }); 22 | 23 | test('Global', function(done) { 24 | builder.compile('global.js').then(function(output) { 25 | assert.match(output.source, /System\.registerDynamic\(\["\.\/jquery\.js"/); 26 | }) 27 | .then(done, done); 28 | }); 29 | 30 | test('Register', function(done) { 31 | builder.compile('third.js').then(function(output) { 32 | assert.match(output.source, /System\.register\(\['\.\/second.js'\]/); 33 | }) 34 | .then(done, done); 35 | }); 36 | 37 | test('ES', function(done) { 38 | builder.compile('first.js').then(function(output) { 39 | assert.match(output.source, /System\.register\(\[/); 40 | }) 41 | .then(done, done); 42 | }); 43 | }); -------------------------------------------------------------------------------- /test/conditional-canonicals.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | 3 | var builder = new Builder('test/fixtures/conditional-tree'); 4 | builder.loadConfigSync('test/fixtures/conditional-tree.config.js'); 5 | 6 | var baseURL = builder.loader.baseURL; 7 | 8 | builder.config({ defaultJSExtensions: true }); 9 | 10 | suite('Conditional Canonical Names', function() { 11 | test('Package environment canonical', function() { 12 | assert.equal(builder.getCanonicalName(baseURL + 'pkg#:env-condition'), 'pkg#:env-condition'); 13 | }); 14 | test('Interpolation', function() { 15 | assert.equal(builder.getCanonicalName(baseURL + 'interpolated-#{' + baseURL + 'conditions.js|test}.js'), 'interpolated-#{conditions.js|test}.js'); 16 | }); 17 | test('Plugin interpolation', function() { 18 | assert.equal(builder.getCanonicalName(baseURL + 'pkg/test-#{' + baseURL + 'conditions.js|test}.js!plugin-#{conditions.js|another}.js'), 'pkg/test-#{conditions.js|test}.js!plugin-#{conditions.js|another}.js'); 19 | }); 20 | test('Boolean conditional', function() { 21 | assert.equal(builder.getCanonicalName(baseURL + 'pkg/lib/test#?~' + baseURL + 'bool|exp'), 'pkg/lib/test#?~bool|exp'); 22 | }); 23 | test('Boolean conditional with plugin', function() { 24 | builder.config({ 25 | paths: { 26 | a: 'asdf', // only if we add .js this catches 27 | condition: 'conditions.js', 28 | p: 'plugin' 29 | } 30 | }) 31 | assert.equal(builder.getCanonicalName(baseURL + 'asdf.js' + '!' + baseURL + 'plugin.js#?' + baseURL + 'conditions.js'), 'asdf.js!p#?condition'); 32 | }) 33 | }); -------------------------------------------------------------------------------- /compilers/compiler.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var babel = require('babel-core'); 3 | 4 | function pathToUrl(p) { 5 | return p.replace(/\\/g, '/'); 6 | } 7 | 8 | exports.compile = function (load, opts, plugin) { 9 | var sourceRoot = pathToUrl(path.dirname(load.path) + '/'); 10 | var options = { 11 | babelrc: false, 12 | compact: false, 13 | sourceType: 'script', 14 | filename: pathToUrl(load.path), 15 | filenameRelative: path.basename(load.path), 16 | inputSourceMap: load.metadata.sourceMap, 17 | moduleId: opts.moduleId, 18 | sourceFileName: load.path && path.basename(load.path), 19 | sourceMaps: !!opts.sourceMaps, 20 | sourceRoot: sourceRoot, 21 | plugins: [plugin] 22 | }; 23 | 24 | var source = load.metadata.originalSource || load.source; 25 | 26 | var output; 27 | if (load.metadata.ast) { 28 | output = babel.transformFromAst(load.metadata.ast, source, options); 29 | } else { 30 | output = babel.transform(source, options); 31 | } 32 | 33 | var sourceMap = output.map; 34 | if (sourceMap && !sourceMap.sourceRoot) // if input source map doesn't have sourceRoot - add it 35 | sourceMap.sourceRoot = sourceRoot; 36 | 37 | return Promise.resolve({ 38 | source: output.code, 39 | sourceMap: sourceMap 40 | }); 41 | }; 42 | 43 | exports.compileAst = function (load, plugin) { 44 | return babel.transform(load.source, { 45 | babelrc: false, 46 | compact: false, 47 | filename: load.path, 48 | inputSourceMap: load.metadata.sourceMap, 49 | ast: true, 50 | plugins: [plugin] 51 | }); 52 | }; 53 | -------------------------------------------------------------------------------- /test/static-optimize.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | var builder = new Builder(); 3 | 4 | var minify = false; 5 | 6 | builder.loadConfigSync('./test/fixtures/test-tree.config.js'); 7 | 8 | builder.config({ 9 | transpiler: false, 10 | meta: { 11 | 'a.js': { 12 | format: 'esm' 13 | }, 14 | 'b.js': { 15 | format: 'esm' 16 | }, 17 | 'global-dep.js': { 18 | format: 'esm' 19 | }, 20 | 'global-dep-loader.js': { 21 | format: 'esm' 22 | } 23 | }, 24 | paths: { 25 | '*': './test/fixtures/es-tree/*', 26 | 'global': './test/fixtures/test-tree/global-inner.js' 27 | } 28 | }); 29 | 30 | suite('SFX Optimizations', function() { 31 | if (process.versions.node && process.versions.node.substr(0, 5) !== '0.10.') 32 | test('All ES6 rollup optimization', function(done) { 33 | builder.buildStatic('a.js', 'test/output/es-sfx.js', { runtime: false, minify: minify, format: 'esm', rollup: true }) 34 | .then(function(output) { 35 | assert(output.source, 'var b = \'b\';\n\nvar a = \'a\';\n\nexport { a, b };'); 36 | done(); 37 | }, done) 38 | }); 39 | 40 | if (process.versions.node && process.versions.node.substr(0, 5) !== '0.10.') 41 | test('ES6 rollup with a global dep', function(done) { 42 | builder.buildStatic('global-dep.js', 'test/output/es-sfx.js', { runtime: false, minify: minify, format: 'esm', rollup: true }) 43 | .then(function(output) { 44 | assert(output.source.indexOf('System.registerDynamic("b"') != -1 && output.source.indexOf(', [\'b\'') != -1); 45 | done(); 46 | }, done); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "systemjs-builder", 3 | "version": "0.16.15", 4 | "description": "SystemJS Build Tool", 5 | "main": "index.js", 6 | "dependencies": { 7 | "babel-core": "^6.24.1", 8 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 9 | "babel-plugin-transform-amd-system-wrapper": "^0.3.7", 10 | "babel-plugin-transform-cjs-system-wrapper": "^0.6.2", 11 | "babel-plugin-transform-es2015-modules-systemjs": "^6.6.5", 12 | "babel-plugin-transform-global-system-wrapper": "^0.3.4", 13 | "babel-plugin-transform-system-register": "^0.0.1", 14 | "bluebird": "^3.3.4", 15 | "data-uri-to-buffer": "0.0.4", 16 | "es6-template-strings": "^2.0.0", 17 | "glob": "^7.0.3", 18 | "mkdirp": "^0.5.1", 19 | "rollup": "^0.58.2", 20 | "source-map": "^0.5.3", 21 | "systemjs": "^0.19.46", 22 | "terser": "3.8.1", 23 | "traceur": "0.0.105" 24 | }, 25 | "devDependencies": { 26 | "babel": "^5.8.38", 27 | "chai": "^3.0.0", 28 | "mocha": "^2.2.5", 29 | "mocha-phantomjs": "4.1.0", 30 | "phantomjs": "2.1.7", 31 | "systemjs-plugin-babel": "0.0.19", 32 | "typescript": "1.6.2", 33 | "unexpected": "^9.11.0", 34 | "when": "^3.7.5" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "http://github.com/systemjs/builder.git" 39 | }, 40 | "scripts": { 41 | "test": "mocha", 42 | "update-expectations": "UPDATE_EXPECTATIONS=1 mocha" 43 | }, 44 | "author": "", 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/systemjs/builder/issues" 48 | }, 49 | "homepage": "https://github.com/systemjs/builder" 50 | } 51 | -------------------------------------------------------------------------------- /test/canonicals.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | var builder = new Builder(); 3 | 4 | builder.loadConfigSync('./test/fixtures/test-tree.config.js'); 5 | 6 | builder.config({ transpiler: 'babel' }); 7 | 8 | var baseURL = builder.loader.baseURL; 9 | 10 | suite('Canonical Names', function() { 11 | test('Simple canonical', function() { 12 | assert.equal(builder.getCanonicalName('amd.js'), 'amd.js'); 13 | }); 14 | 15 | test('Wildcard', function() { 16 | assert.equal(builder.getCanonicalName(baseURL + 'test/fixtures/test-tree/asdf'), 'asdf'); 17 | }); 18 | 19 | test('Exact beats wildcard', function() { 20 | assert.equal(builder.getCanonicalName(baseURL + 'node_modules/babel/node_modules/babel-core/browser.js'), 'babel'); 21 | }); 22 | 23 | test('Wildcard extensions', function() { 24 | assert.equal(builder.getCanonicalName(baseURL + 'test/dummy/file.jade'), 'file.jade'); 25 | }); 26 | 27 | test('Wildcard extensions with a plugin', function() { 28 | builder.loader.defaultJSExtensions = true; 29 | assert.equal(builder.getCanonicalName('cjs'), 'cjs'); 30 | assert.equal(builder.getCanonicalName(baseURL + 'test/dummy/file.jade!' + baseURL + 'test/fixtures/test-tree/jade.js'), 'file.jade!jade.js'); 31 | }); 32 | 33 | test('Trailing / canonical', function() { 34 | builder.loader.defaultJSExtensions = false; 35 | builder.config({ 36 | paths: { 37 | 'trailing/': 'src/' 38 | } 39 | }); 40 | assert.equal(builder.getCanonicalName(baseURL + 'src/asdf'), 'trailing/asdf'); 41 | assert.equal(builder.getCanonicalName(baseURL + 'src/'), 'trailing/'); 42 | assert.equal(builder.getCanonicalName(baseURL + 'src'), 'trailing'); 43 | }) 44 | }); -------------------------------------------------------------------------------- /test/fixtures/sourcemaps-expectations/babel.tree.multi.json: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["test/fixtures/test-tree/third.js","test/fixtures/test-tree/cjs.js","test/fixtures/test-tree/second.js","test/fixtures/test-tree/jquery.js","test/fixtures/test-tree/global.js","test/fixtures/test-tree/text.txt","test/fixtures/test-tree/amd.js","test/fixtures/test-tree/unknown","test/fixtures/test-tree/file.json","test/fixtures/test-tree/first.js"],"names":["System","register","$__export","setters","execute","__moduleName","console","log","__filename","require","exports","cjs","env","jquery","myjquery","test","module","a","b","c","is","text"],"mappings":";AAAAA,OAAOC,QAAP,aAAgB,CAAC,aAAD,CAAhB,EAAiC,UAASC,SAAT,gBAAoB;AACnD,SAAO;AACLC,aAAS,CAAC,YAAW,CAAE,CAAd,CADJ;AAELC,aAAS,YAAW;AAClBF,gBAAU,MAAV,EAAkB,SAAlB;AACAA,gBAAU,IAAV,EAAgB,GAAhB;AACAA,gBAAU,MAAV,EAAkBG,YAAlB;AACD;AANI,GAAP;AAQD,CATD;;;;;;;ACAAC,UAAQC,GAAR,CAAYC,UAAZ;;AAEA,GAAC,UAASC,OAAT,EAAkB;AACjB,QAAI,OAAOA,OAAP,IAAkB,WAAtB,EACEC,QAAQC,GAAR,GAAc,IAAd;;AAEF,QAAI,KAAJ,EACEF,QAAQ,SAAS,YAAjB;AACH,GAND,EAMGA,UANH;;AAQAC,UAAQE,GAAR;;;;;MCRW,CAAC;;;;AAAD,OAAC,GAAG,CAAC;;;;;;;;;;QCFZC;AAAJ,QAAIA,SAAS,EAAb;0BAAIA;;;;;;;;;;;ACAJ;AACA;;AAEA,SAAKC,QAAL,CAAcC,IAAd,GAAqB,QAArB;;;;;;;;;;;;;;;ACHAC,SAAON,OAAP,GAAiB,mBAAjB;;ACAA,kCAAQ,aAAR,EAAuB,uBAAvB,EAAgD,6BAAhD;AAAA,UAAgF,UAASO,CAAT,EAAYC,CAAZ,EAAeC,CAAf,EAAkB;AAChG,WAAO,EAAEC,IAAI,KAAN,EAAaC,MAAMF,CAAnB,EAAP;AACD,GAFD,wBAAQ,aAAR,cAAuB,uBAAvB,cAAgD,6BAAhD;AAAA;;;;MCAI,OAEO;;;;AAFP,cAAQ,EAAR;;AACJ,YAAM,aAAN,GAAsB,YAAW;AAAC;AAAE,OAApC;AACW,kBAAY,MAAA,aAAA,CAAA,KAAA,EAAA,EAAK,WAAU,KAAf,EAAA,CAAZ;;;;;;;;;ACFXH,SAAON,OAAP,GAAiB,EAAC,QAAO,MAAR,EAAjB;;;;;eCOW,CAAC,EAED,CAAC;;;oBAPH,CAAC;;uBAGF,IAAI;;;AAED,OAAC,GAAG,IAAI;;;;AAER,OAAC,GAAG,CAAC;;;;AAChB,OAAC,CAAC","file":"output.js"} -------------------------------------------------------------------------------- /compilers/json.js: -------------------------------------------------------------------------------- 1 | function hasProperties(obj) { 2 | for (var p in obj) 3 | return true; 4 | return false; 5 | } 6 | 7 | exports.compile = function(load, opts, loader) { 8 | try { 9 | var json = JSON.parse(load.source) 10 | } 11 | catch(e) { 12 | throw new Error('Unable to parse JSON module ' + load.name + ' contents as JSON.'); 13 | } 14 | 15 | if (load.isPackageConfig) 16 | json = optimizePackageConfig(json); 17 | 18 | return Promise.resolve({ 19 | source: opts.systemGlobal + '.registerDynamic(' + (opts.anonymous ? '' : '"' + load.name + '", ') + '[], true, function() {\n' + 20 | ' return ' + JSON.stringify(json, null, 2).replace(/\n/g, '\n ') + ';\n' + 21 | '});\n' 22 | }); 23 | }; 24 | 25 | // because bundles are for the browser only 26 | // if this is a package config file json we are compiling 27 | // then we can optimize out the node-only configurations to make it smaller 28 | function optimizePackageConfig(json) { 29 | if (json.systemjs) 30 | json = json.systemjs; 31 | 32 | // remove non SystemJS package config properties 33 | var loaderConfigProperties = ['baseDir', 'defaultExtension', 'format', 'meta', 'map', 'main']; 34 | for (var p in json) 35 | if (loaderConfigProperties.indexOf(p) == -1) 36 | delete json[p]; 37 | 38 | if (json.map && !json.map['@env']) { 39 | Object.keys(json.map).forEach(function(target) { 40 | var mapped = json.map[target]; 41 | 42 | if (typeof mapped == 'string' && mapped.substr(0, 6) == '@node/') 43 | delete json.map[target]; 44 | 45 | if (typeof mapped == 'object') { 46 | Object.keys(mapped).forEach(function(condition) { 47 | if (condition == 'node') 48 | delete mapped[condition]; 49 | }); 50 | if (!hasProperties(mapped)) 51 | delete json.map[target]; 52 | } 53 | }); 54 | 55 | if (!hasProperties(json.map)) 56 | delete json.map; 57 | } 58 | 59 | return json; 60 | } 61 | -------------------------------------------------------------------------------- /test/fixtures/test-tree/babel.js: -------------------------------------------------------------------------------- 1 | import "./global.js"; 2 | import "./example.js"; 3 | 4 | // Expression bodies 5 | var odds = evens.map(v => v + 1); 6 | var nums = evens.map((v, i) => v + i); 7 | 8 | // Statement bodies 9 | nums.forEach(v => { 10 | if (v % 5 === 0) 11 | fives.push(v); 12 | }); 13 | 14 | // Lexical this 15 | var bob = { 16 | _name: "Bob", 17 | _friends: [], 18 | printFriends() { 19 | this._friends.forEach(f => 20 | console.log(this._name + " knows " + f)); 21 | } 22 | }; 23 | 24 | // list matching 25 | var [a, ,b] = [1,2,3]; 26 | a === 1; 27 | b === 3; 28 | 29 | // object matching 30 | var { op: a, lhs: { op: b }, rhs: c } 31 | = getASTNode() 32 | 33 | // object matching shorthand 34 | // binds `op`, `lhs` and `rhs` in scope 35 | var {op, lhs, rhs} = getASTNode() 36 | 37 | // Can be used in parameter position 38 | function g({name: x}) { 39 | console.log(x); 40 | } 41 | g({name: 5}) 42 | 43 | // Fail-soft destructuring 44 | var [a] = []; 45 | a === undefined; 46 | 47 | // Fail-soft destructuring with defaults 48 | var [a = 1] = []; 49 | a === 1; 50 | 51 | // Destructuring + defaults arguments 52 | function r({x, y, w = 10, h = 10}) { 53 | return x + y + w + h; 54 | } 55 | r({x:1, y:2}) === 23 56 | 57 | 58 | function f(x, y=12) { 59 | // y is 12 if not passed (or passed as undefined) 60 | return x + y; 61 | } 62 | f(3) == 15 63 | function f(x, ...y) { 64 | // y is an Array 65 | return x * y.length; 66 | } 67 | f(3, "hello", true) == 6 68 | function f(x, y, z) { 69 | return x + y + z; 70 | } 71 | // Pass each elem of array as argument 72 | f(...[1,2,3]) == 6 73 | 74 | 75 | function f() { 76 | { 77 | let x; 78 | { 79 | // okay, block scoped name 80 | const x = "sneaky"; 81 | } 82 | // okay, declared with `let` 83 | x = "bar"; 84 | } 85 | } 86 | 87 | 88 | function factorial(n, acc = 1) { 89 | if (n <= 1) return acc; 90 | return factorial(n - 1, n * acc); 91 | } 92 | 93 | // Stack overflow in most implementations today, 94 | // but safe on arbitrary inputs in ES2015 95 | export const tail = factorial(100000) -------------------------------------------------------------------------------- /test/fixtures/sourcemaps-expectations/traceur.tree.multi.json: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["test/fixtures/test-tree/third.js","test/fixtures/test-tree/cjs.js","test/fixtures/test-tree/second.js","test/fixtures/test-tree/jquery.js","test/fixtures/test-tree/global.js","test/fixtures/test-tree/text.txt","test/fixtures/test-tree/amd.js","test/fixtures/test-tree/unknown","test/fixtures/test-tree/file.json","test/fixtures/test-tree/first.js"],"names":["System","register","$__export","setters","execute","__moduleName","console","log","__filename","require","exports","cjs","env","jquery","myjquery","test","module","a","b","c","is","text"],"mappings":";AAAAA,OAAOC,QAAP,aAAgB,CAAC,aAAD,CAAhB,EAAiC,UAASC,SAAT,gBAAoB;AACnD,SAAO;AACLC,aAAS,CAAC,YAAW,CAAE,CAAd,CADJ;AAELC,aAAS,YAAW;AAClBF,gBAAU,MAAV,EAAkB,SAAlB;AACAA,gBAAU,IAAV,EAAgB,GAAhB;AACAA,gBAAU,MAAV,EAAkBG,YAAlB;AACD;AANI,GAAP;AAQD,CATD;;;;;;;ACAAC,UAAQC,GAAR,CAAYC,UAAZ;;AAEA,GAAC,UAASC,OAAT,EAAkB;AACjB,QAAI,OAAOA,OAAP,IAAkB,WAAtB,EACEC,QAAQC,GAAR,GAAc,IAAd;;AAEF,QAAI,KAAJ,EACEF,QAAQ,SAAS,YAAjB;AACH,GAND,EAMGA,UANH;;AAQAC,UAAQE,GAAR;;ACVA,KAAK,SAAS,AAAC,yCACc,UAAS,SAAQ;;;AAD9C,OAAO;AACD,UAAM,GADZ,SAAS,IAAG,CAAG,GAAC,CAAhB,UAAS,IAAG,CAAG,GAAC,CACe;AACzB,UAAM;QAAG,EAAA;AAFf,cAAQ,AAAC,QAAqC,CAAA;IAEf;EAC3B,CAAA;AAAM,CAAC,CAAC;AAAZ;;;;;QCHIC;AAAJ,QAAIA,SAAS,EAAb;0BAAIA;;;;;;;;;;;ACAJ;AACA;;AAEA,SAAKC,QAAL,CAAcC,IAAd,GAAqB,QAArB;;;;;;;;;;;;;;;ACHAC,SAAON,OAAP,GAAiB,mBAAjB;;ACAA,kCAAQ,aAAR,EAAuB,uBAAvB,EAAgD,6BAAhD;AAAA,UAAgF,UAASO,CAAT,EAAYC,CAAZ,EAAeC,CAAf,EAAkB;AAChG,WAAO,EAAEC,IAAI,KAAN,EAAaC,MAAMF,CAAnB,EAAP;AACD,GAFD,wBAAQ,aAAR,cAAuB,uBAAvB,cAAgD,6BAAhD;AAAA;;;;MCAI,OAEO;;;;AAFP,cAAQ,EAAR;;AACJ,YAAM,aAAN,GAAsB,YAAW;AAAC;AAAE,OAApC;AACW,kBAAY,MAAA,aAAA,CAAA,KAAA,EAAA,EAAK,WAAU,KAAf,EAAA,CAAZ;;;;;;;;;ACFXH,SAAON,OAAP,GAAiB,EAAC,QAAO,MAAR,EAAjB;;ACAA,KAAK,SAAS,AAAC,4GACc,UAAS,SAAQ;;;;;;AAD9C,OAAO;AACD,UAAM,GADZ,SAAS,IAAG,CAAG,GAAC,CAAhB,UAAS,IAAG,CAAG,GAAC,CAAhB,UAAS,IAAG,CAAG;AAAf,QAAoB,KAAG,EAAkB,CAAC;IAElC,CAFR,UAAS,IAAG,CAAG,GAAC,CAAhB,UAAS,IAAG,CAAG,GAAC,CAAhB,UAAS,IAAG,CAAG;AAAf,WAAoB,KAAG,KAAkB,CAAC;IAElC,CADuB;AACzB,UAAM;QAKG,KAAG;AAPlB,cAAQ,AAAC,QAAqC,CAAA;QAS/B,EAAA;AATf,cAAQ,AAAC,QAAqC,CAAA;AAU9C,MAAA,CAAC;IAR8B;EAC3B,CAAA;AAAM,CAAC,CAAC;AAOX","file":"output.js"} -------------------------------------------------------------------------------- /lib/get-deferred-imports.js: -------------------------------------------------------------------------------- 1 | var traceur = require('traceur'); 2 | 3 | var traceurGet = require('./utils').traceurGet; 4 | 5 | var ParseTreeTransformer = traceurGet('codegeneration/ParseTreeTransformer.js').ParseTreeTransformer; 6 | 7 | var getCanonicalName = require('./utils').getCanonicalName; 8 | 9 | var Promise = require('bluebird'); 10 | 11 | function DeferredImportsTransformer(map) { 12 | this.imports = []; 13 | return ParseTreeTransformer.apply(this, arguments); 14 | } 15 | 16 | DeferredImportsTransformer.prototype = Object.create(ParseTreeTransformer.prototype); 17 | 18 | DeferredImportsTransformer.prototype.transformCallExpression = function(tree) { 19 | tree = ParseTreeTransformer.prototype.transformCallExpression.call(this, tree); 20 | 21 | if (tree.operand.type == 'MEMBER_EXPRESSION' 22 | && tree.operand.memberName.value == 'import' 23 | && tree.operand.operand.type == 'IDENTIFIER_EXPRESSION' 24 | && tree.operand.operand.identifierToken.value == 'System' 25 | && tree.args.args.length == 1) { 26 | 27 | if (tree.args.args[0].type === 'LITERAL_EXPRESSION') 28 | this.imports.push(tree.args.args[0].literalToken.processedValue); 29 | } 30 | 31 | return tree; 32 | } 33 | 34 | module.exports = function(builder, trace) { 35 | var deferredImports = []; 36 | 37 | return Promise.all(Object.keys(trace).map(function(name) { 38 | var load = trace[name]; 39 | 40 | if (load.deferredImports) { 41 | deferredImports = deferredImports.concat(load.deferredImports); 42 | return; 43 | } 44 | 45 | var curDeferredImports = []; 46 | 47 | if (!load || load.conditional) 48 | return; 49 | 50 | var compiler = new traceur.Compiler({ script: load.metadata.format !== 'esm' }); 51 | 52 | var tree = compiler.parse(load.source, load.path); 53 | 54 | var transformer = new DeferredImportsTransformer(); 55 | 56 | tree = transformer.transformAny(tree); 57 | 58 | return Promise.all(transformer.imports.map(function(impt) { 59 | return builder.loader.normalize(impt) 60 | .then(function(moduleName) { 61 | var canonical = getCanonicalName(builder.loader, moduleName); 62 | 63 | curDeferredImports.push({ 64 | name: canonical, 65 | parent: name 66 | }); 67 | }); 68 | })) 69 | .then(function() { 70 | // store on the load record itself to allow caching 71 | load.deferredImports = curDeferredImports; 72 | deferredImports = deferredImports.concat(curDeferredImports); 73 | }); 74 | })) 75 | .then(function() { 76 | return deferredImports; 77 | }); 78 | }; -------------------------------------------------------------------------------- /templates/global-helpers.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | var loader = $__System; 3 | 4 | function readMemberExpression (p, value) { 5 | var pParts = p.split('.'); 6 | while (pParts.length) 7 | value = value[pParts.shift()]; 8 | return value; 9 | } 10 | 11 | function getGlobalValue (exports) { 12 | if (typeof exports == 'string') 13 | return readMemberExpression(exports, global); 14 | 15 | if (!(exports instanceof Array)) 16 | throw new Error('Global exports must be a string or array.'); 17 | 18 | var globalValue = {}; 19 | for (var i = 0; i < exports.length; i++) 20 | globalValue[exports[i].split('.').pop()] = readMemberExpression(exports[i], global); 21 | return globalValue; 22 | } 23 | 24 | // bare minimum ignores 25 | var ignoredGlobalProps = ['_g', 'sessionStorage', 'localStorage', 'clipboardData', 'frames', 'frameElement', 'external', 26 | 'mozAnimationStartTime', 'webkitStorageInfo', 'webkitIndexedDB', 'mozInnerScreenY', 'mozInnerScreenX']; 27 | 28 | var globalSnapshot; 29 | function globalIterator (globalName) { 30 | if (ignoredGlobalProps.indexOf(globalName) !== -1) 31 | return; 32 | try { 33 | var value = global[globalName]; 34 | } 35 | catch (e) { 36 | ignoredGlobalProps.push(globalName); 37 | } 38 | this(globalName, value); 39 | } 40 | 41 | loader.registry.set('@@global-helpers', loader.newModule({ 42 | prepareGlobal: function (moduleName, exports, globals) { 43 | // disable module detection 44 | var curDefine = global.define; 45 | global.define = undefined; 46 | 47 | // set globals 48 | var oldGlobals; 49 | if (globals) { 50 | oldGlobals = {}; 51 | for (var g in globals) { 52 | oldGlobals[g] = global[g]; 53 | global[g] = globals[g]; 54 | } 55 | } 56 | 57 | // store a complete copy of the global object in order to detect changes 58 | if (!exports) { 59 | globalSnapshot = {}; 60 | 61 | Object.keys(global).forEach(globalIterator, function (name, value) { 62 | globalSnapshot[name] = value; 63 | }); 64 | } 65 | 66 | // return function to retrieve global 67 | return function () { 68 | var globalValue = exports ? getGlobalValue(exports) : {}; 69 | 70 | var singleGlobal; 71 | var multipleExports = !!exports; 72 | 73 | if (!exports) 74 | Object.keys(global).forEach(globalIterator, function (name, value) { 75 | if (globalSnapshot[name] === value) 76 | return; 77 | if (value === undefined) 78 | return; 79 | 80 | if (!exports) { 81 | globalValue[name] = value; 82 | 83 | if (singleGlobal !== undefined) { 84 | if (!multipleExports && singleGlobal !== value) 85 | multipleExports = true; 86 | } 87 | else { 88 | singleGlobal = value; 89 | } 90 | } 91 | }); 92 | 93 | globalValue = multipleExports ? globalValue : singleGlobal; 94 | 95 | // revert globals 96 | if (oldGlobals) { 97 | for (var g in oldGlobals) 98 | global[g] = oldGlobals[g]; 99 | } 100 | global.define = curDefine; 101 | 102 | return globalValue; 103 | }; 104 | } 105 | })); 106 | })((typeof self !== 'undefined') ? self : (typeof global !== 'undefined') ? global : this) 107 | -------------------------------------------------------------------------------- /test/test-load-config.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | var fs = require('fs'); 3 | 4 | suite('Test builder.loadConfig', function() { 5 | 6 | test('builder.loadConfig works', function(done) { 7 | 8 | var configFile = 'test/output/builderConfig.js'; 9 | var builder = new Builder(); 10 | fs.writeFileSync(configFile, 'System.config({map: {m: "./m.js"}});'); 11 | builder.loadConfig(configFile).then(function() { 12 | 13 | assert.equal(builder.loader.map['m'], './m.js', 'loader map was loaded from config'); 14 | 15 | }).then(done, done); 16 | 17 | }); 18 | 19 | test('builder.loadConfig does not affect other builders', function(done) { 20 | 21 | var configFile1 = 'test/output/builder1Config.js'; 22 | var configFile2 = 'test/output/builder2Config.js'; 23 | fs.writeFileSync(configFile1, 'System.config({baseURL: "base1", map: {m1: "./m1.js"}, packages: {p1: {main: "index1.js"}}});'); 24 | fs.writeFileSync(configFile2, 'System.config({baseURL: "base2", map: {m2: "./m2.js"}, packages: {p2: {main: "index2.js"}}});'); 25 | 26 | var builder1 = new Builder(); 27 | var builder2 = new Builder(); 28 | 29 | var p1 = builder1.loadConfig(configFile1); 30 | var p2 = builder2.loadConfig(configFile2); 31 | 32 | 33 | Promise.all([p1, p2]).then(function() { 34 | 35 | assert.match(builder1.loader.baseURL, /base1\/$/, 'builder1 baseURL'); 36 | assert.match(builder2.loader.baseURL, /base2\/$/, 'builder2 baseURL'); 37 | 38 | assert.equal(builder1.loader.map['m1'], './m1.js', 'builder1.loader map was loaded from config'); 39 | assert.equal(builder1.loader.map['m2'], undefined, 'map for builder1.loader only'); 40 | assert.equal(builder2.loader.map['m2'], './m2.js', 'builder2.loader map was loaded from config'); 41 | assert.equal(builder2.loader.map['m1'], undefined, 'map for builder2.loader only'); 42 | 43 | builder1.loader.normalize('p1').then(function(p1) { 44 | assert.match(p1, /base1\/p1\/index1\.js$/, 'builder1 package p1'); 45 | 46 | assert.equal(builder1.loader.packages['p2'], undefined, 'builder1 package p2'); 47 | assert.equal(builder2.loader.packages['p1'], undefined, 'builder2 package p1'); 48 | builder2.loader.normalize('p2').then(function(p2) { 49 | assert.match(p2, /base2\/p2\/index2\.js$/, 'builder2 package p2'); 50 | }).then(done, done); 51 | }); 52 | }); 53 | }); 54 | 55 | test('builder.loadConfig does not affect global variables', function(done) { 56 | 57 | global._tmp_1 = '1'; 58 | 59 | var configFile = 'test/output/builderConfig.js'; 60 | var builder = new Builder(); 61 | fs.writeFileSync(configFile, '_tmp_1=2; _tmp_2=3; global._tmp_1=4;'); 62 | builder.loadConfig(configFile).then(function() { 63 | 64 | assert.equal(global._tmp_1, '1', 'previously defined global variable is not affected'); 65 | assert.equal(global._tmp_2, undefined, 'new global variables are not defined'); 66 | 67 | delete global._tmp_1; 68 | 69 | }).then(done, done); 70 | 71 | }); 72 | 73 | test('builder.loadConfig makes require available to config code', function(done) { 74 | var configFile = 'test/output/builderConfig.js'; 75 | var builder = new Builder(); 76 | fs.writeFileSync(configFile, 'var m = require("module"); System.config({baseURL:"base"});'); 77 | builder.loadConfig(configFile).then(function() { 78 | 79 | assert.match(builder.loader.baseURL, /base\/$/, 'builder baseURL set'); 80 | 81 | }).then(done, done); 82 | 83 | }); 84 | 85 | }); 86 | -------------------------------------------------------------------------------- /compilers/amd.js: -------------------------------------------------------------------------------- 1 | var vm = require('vm'); 2 | var compiler = require('./compiler'); 3 | var Promise = require('bluebird'); 4 | 5 | // override System instantiate to handle AMD dependencies 6 | exports.attach = function (loader) { 7 | var systemInstantiate = loader.instantiate; 8 | loader.instantiate = function (load) { 9 | var loader = this; 10 | 11 | return systemInstantiate.call(this, load).then(function (result) { 12 | if (load.metadata.format == 'amd') { 13 | 14 | if (!load.source) load.source = ' '; 15 | 16 | var output = compiler.compileAst(load, [require('babel-plugin-transform-amd-system-wrapper').default, { filterMode: true }]); 17 | load.metadata.ast = output.ast; 18 | load.metadata.anonNamed = output.metadata.anonNamed; 19 | 20 | var entry = loader.defined[load.name]; 21 | entry.deps = dedupe(output.metadata.amdDeps.concat(load.metadata.deps)); 22 | 23 | load.metadata.builderExecute = function (require, exports, module) { 24 | var removeDefine = loader.get('@@amd-helpers').createDefine(loader); 25 | 26 | // NB source maps, System overwriting skipped here 27 | vm.runInThisContext(load.source); 28 | 29 | removeDefine(loader); 30 | 31 | var lastModule = loader.get('@@amd-helpers').lastModule; 32 | 33 | if (!lastModule.anonDefine && !lastModule.isBundle) 34 | throw new TypeError('AMD module ' + load.name + ' did not define'); 35 | 36 | if (lastModule.anonDefine) 37 | return lastModule.anonDefine.execute.apply(this, arguments); 38 | 39 | lastModule.isBundle = false; 40 | lastModule.anonDefine = null; 41 | }; 42 | 43 | // first, normalize all dependencies 44 | var normalizePromises = []; 45 | for (var i = 0, l = entry.deps.length; i < l; i++) { 46 | normalizePromises.push(Promise.resolve(loader.normalize(entry.deps[i], load.name))); 47 | } 48 | 49 | return Promise.all(normalizePromises).then(function (normalizedDeps) { 50 | entry.normalizedDeps = normalizedDeps; 51 | entry.originalIndices = group(entry.deps); 52 | 53 | return { 54 | deps: entry.deps, 55 | execute: result.execute 56 | }; 57 | }); 58 | } 59 | 60 | return result; 61 | }); 62 | }; 63 | }; 64 | 65 | function dedupe(deps) { 66 | var newDeps = []; 67 | for (var i = 0, l = deps.length; i < l; i++) 68 | if (newDeps.indexOf(deps[i]) == -1) 69 | newDeps.push(deps[i]) 70 | return newDeps; 71 | } 72 | 73 | function group(deps) { 74 | var names = []; 75 | var indices = []; 76 | for (var i = 0, l = deps.length; i < l; i++) { 77 | var index = names.indexOf(deps[i]); 78 | if (index === -1) { 79 | names.push(deps[i]); 80 | indices.push([i]); 81 | } 82 | else { 83 | indices[index].push(i); 84 | } 85 | } 86 | return { names: names, indices: indices }; 87 | } 88 | 89 | // converts anonymous AMDs into named AMD for the module 90 | exports.compile = function (load, opts, loader) { 91 | opts.moduleId = !opts.anonymous && load.name; 92 | return compiler.compile(load, opts, [require('babel-plugin-transform-amd-system-wrapper').default, { 93 | anonNamed: load.metadata.anonNamed, 94 | map: function (dep) { 95 | return opts.normalize ? load.depMap[dep] : dep; 96 | }, 97 | systemGlobal: opts.systemGlobal, 98 | deps: load.deps, 99 | esModule: load.metadata.esModule 100 | }]); 101 | }; 102 | -------------------------------------------------------------------------------- /templates/sfx-core.min.js: -------------------------------------------------------------------------------- 1 | !function(e){function t(e){Object.defineProperty(this,e,{enumerable:!0,get:function(){return this[v][e]}})}function r(e){if("undefined"!=typeof System&&System.isModule?System.isModule(e):"[object Module]"===Object.prototype.toString.call(e))return e;var t={default:e,__useDefault:e};if(e&&e.__esModule)for(var r in e)Object.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return new o(t)}function o(e){Object.defineProperty(this,v,{value:e}),Object.keys(e).forEach(t,this)}function n(e){return"@node/"===e.substr(0,6)?c(e,r(m(e.substr(6))),{}):p[e]}function u(e){var t=n(e);if(!t)throw new Error('Module "'+e+'" expected, but not contained in build.');if(t.module)return t.module;var r=t.linkRecord;return i(t,r),a(t,r,[]),t.module}function i(e,t){if(!t.depLoads){t.declare&&d(e,t),t.depLoads=[];for(var r=0;r1)for(var a=1;a 1 && arguments[1] !== undefined ? arguments[1] : 12; 29 | 30 | // y is 12 if not passed (or passed as undefined) 31 | return x + y; 32 | } 33 | 34 | function f(x) { 35 | // y is an Array 36 | return x * (arguments.length <= 1 ? 0 : arguments.length - 1); 37 | } 38 | 39 | function f(x, y, z) { 40 | return x + y + z; 41 | } 42 | // Pass each elem of array as argument 43 | 44 | 45 | function f() { 46 | { 47 | var x = void 0; 48 | { 49 | // okay, block scoped name 50 | var _x2 = "sneaky"; 51 | } 52 | // okay, declared with `let` 53 | x = "bar"; 54 | } 55 | } 56 | 57 | function factorial(n) { 58 | var acc = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; 59 | 60 | if (n <= 1) return acc; 61 | return factorial(n - 1, n * acc); 62 | } 63 | 64 | // Stack overflow in most implementations today, 65 | // but safe on arbitrary inputs in ES2015 66 | return { 67 | setters: [function (_globalJs) {}, function (_exampleJs) {}], 68 | execute: function () { 69 | odds = evens.map(function (v) { 70 | return v + 1; 71 | }); 72 | nums = evens.map(function (v, i) { 73 | return v + i; 74 | }); 75 | 76 | 77 | // Statement bodies 78 | nums.forEach(function (v) { 79 | if (v % 5 === 0) fives.push(v); 80 | }); 81 | 82 | // Lexical this 83 | bob = { 84 | _name: "Bob", 85 | _friends: [], 86 | printFriends: function printFriends() { 87 | var _this = this; 88 | 89 | this._friends.forEach(function (f) { 90 | return console.log(_this._name + " knows " + f); 91 | }); 92 | } 93 | }; 94 | _ref = [1, 2, 3]; 95 | a = _ref[0]; 96 | b = _ref[2]; 97 | 98 | a === 1; 99 | b === 3; 100 | 101 | // object matching 102 | _getASTNode = getASTNode(); 103 | a = _getASTNode.op; 104 | b = _getASTNode.lhs.op; 105 | c = _getASTNode.rhs; 106 | _getASTNode2 = getASTNode(); 107 | op = _getASTNode2.op; 108 | lhs = _getASTNode2.lhs; 109 | rhs = _getASTNode2.rhs; 110 | g({ name: 5 }); 111 | 112 | // Fail-soft destructuring 113 | _ref3 = []; 114 | a = _ref3[0]; 115 | 116 | a === undefined; 117 | 118 | // Fail-soft destructuring with defaults 119 | _ref4 = []; 120 | _ref4$ = _ref4[0]; 121 | a = _ref4$ === undefined ? 1 : _ref4$; 122 | 123 | a === 1;r({ x: 1, y: 2 }) === 23;f(3) == 15;f(3, "hello", true) == 6;f.apply(undefined, [1, 2, 3]) == 6; 124 | _export("tail", tail = factorial(100000)); 125 | 126 | _export("tail", tail); 127 | } 128 | }; 129 | }); 130 | 131 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhYmVsLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBb0NBO0FBQ0EsV0FBUyxDQUFULFFBQXNCO0FBQUEsUUFBSixDQUFJLFNBQVYsSUFBVTs7QUFDcEIsWUFBUSxHQUFSLENBQVksQ0FBWjtBQUNEOzs7QUFXRDtBQUNBLFdBQVMsQ0FBVCxRQUFtQztBQUFBLFFBQXZCLENBQXVCLFNBQXZCLENBQXVCO0FBQUEsUUFBcEIsQ0FBb0IsU0FBcEIsQ0FBb0I7QUFBQSx3QkFBakIsQ0FBaUI7QUFBQSxRQUFqQixDQUFpQiwyQkFBYixFQUFhO0FBQUEsd0JBQVQsQ0FBUztBQUFBLFFBQVQsQ0FBUywyQkFBTCxFQUFLOztBQUNqQyxXQUFPLElBQUksQ0FBSixHQUFRLENBQVIsR0FBWSxDQUFuQjtBQUNEOzs7QUFJRCxXQUFTLENBQVQsQ0FBVyxDQUFYLEVBQW9CO0FBQUEsUUFBTixDQUFNLHVFQUFKLEVBQUk7O0FBQ2xCO0FBQ0EsV0FBTyxJQUFJLENBQVg7QUFDRDs7QUFFRCxXQUFTLENBQVQsQ0FBVyxDQUFYLEVBQW9CO0FBQ2xCO0FBQ0EsV0FBTyxzREFBUDtBQUNEOztBQUVELFdBQVMsQ0FBVCxDQUFXLENBQVgsRUFBYyxDQUFkLEVBQWlCLENBQWpCLEVBQW9CO0FBQ2xCLFdBQU8sSUFBSSxDQUFKLEdBQVEsQ0FBZjtBQUNEO0FBQ0Q7OztBQUlBLFdBQVMsQ0FBVCxHQUFhO0FBQ1g7QUFDRSxVQUFJLFVBQUo7QUFDQTtBQUNFO0FBQ0EsWUFBTSxNQUFJLFFBQVY7QUFDRDtBQUNEO0FBQ0EsVUFBSSxLQUFKO0FBQ0Q7QUFDRjs7QUFHRCxXQUFTLFNBQVQsQ0FBbUIsQ0FBbkIsRUFBK0I7QUFBQSxRQUFULEdBQVMsdUVBQUgsQ0FBRzs7QUFDN0IsUUFBSSxLQUFLLENBQVQsRUFBWSxPQUFPLEdBQVA7QUFDWixXQUFPLFVBQVUsSUFBSSxDQUFkLEVBQWlCLElBQUksR0FBckIsQ0FBUDtBQUNEOztBQUVEO0FBQ0E7Ozs7QUF6RkksVSxHQUFPLE1BQU0sR0FBTixDQUFVO0FBQUEsZUFBSyxJQUFJLENBQVQ7QUFBQSxPQUFWLEM7QUFDUCxVLEdBQU8sTUFBTSxHQUFOLENBQVUsVUFBQyxDQUFELEVBQUksQ0FBSjtBQUFBLGVBQVUsSUFBSSxDQUFkO0FBQUEsT0FBVixDOzs7QUFFWDtBQUNBLFdBQUssT0FBTCxDQUFhLGFBQUs7QUFDaEIsWUFBSSxJQUFJLENBQUosS0FBVSxDQUFkLEVBQ0UsTUFBTSxJQUFOLENBQVcsQ0FBWDtBQUNILE9BSEQ7O0FBS0E7QUFDSSxTLEdBQU07QUFDUixlQUFPLEtBREM7QUFFUixrQkFBVSxFQUZGO0FBR1Isb0JBSFEsMEJBR087QUFBQTs7QUFDYixlQUFLLFFBQUwsQ0FBYyxPQUFkLENBQXNCO0FBQUEsbUJBQ3BCLFFBQVEsR0FBUixDQUFZLE1BQUssS0FBTCxHQUFhLFNBQWIsR0FBeUIsQ0FBckMsQ0FEb0I7QUFBQSxXQUF0QjtBQUVEO0FBTk8sTzthQVVJLENBQUMsQ0FBRCxFQUFHLENBQUgsRUFBSyxDQUFMLEM7QUFBVCxPO0FBQUksTzs7QUFDVCxZQUFNLENBQU47QUFDQSxZQUFNLENBQU47O0FBRUE7b0JBRUksWTtBQURNLE8sZUFBSixFO0FBQWtCLE8sZUFBWCxHLENBQU8sRTtBQUFjLE8sZUFBTCxHO3FCQUtSLFk7QUFBaEIsUSxnQkFBQSxFO0FBQUksUyxnQkFBQSxHO0FBQUssUyxnQkFBQSxHO0FBTWQsUUFBRSxFQUFDLE1BQU0sQ0FBUCxFQUFGOztBQUVBO2NBQ1UsRTtBQUFMLE87O0FBQ0wsWUFBTSxTQUFOOztBQUVBO2NBQ2MsRTs7QUFBVCxPLDBCQUFJLEM7O0FBQ1QsWUFBTSxDQUFOLENBTUEsRUFBRSxFQUFDLEdBQUUsQ0FBSCxFQUFNLEdBQUUsQ0FBUixFQUFGLE1BQWtCLEVBQWxCLENBT0EsRUFBRSxDQUFGLEtBQVEsRUFBUixDQUtBLEVBQUUsQ0FBRixFQUFLLE9BQUwsRUFBYyxJQUFkLEtBQXVCLENBQXZCLENBS0EsbUJBQUssQ0FBQyxDQUFELEVBQUcsQ0FBSCxFQUFLLENBQUwsQ0FBTCxLQUFpQixDQUFqQjtzQkF1QmEsSSxHQUFPLFVBQVUsTUFBVixDIiwiZmlsZSI6InJlZ2lzdGVyLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFwiLi9nbG9iYWwuanNcIjtcbmltcG9ydCBcIi4vZXhhbXBsZS5qc1wiO1xuXG4vLyBFeHByZXNzaW9uIGJvZGllc1xudmFyIG9kZHMgPSBldmVucy5tYXAodiA9PiB2ICsgMSk7XG52YXIgbnVtcyA9IGV2ZW5zLm1hcCgodiwgaSkgPT4gdiArIGkpO1xuXG4vLyBTdGF0ZW1lbnQgYm9kaWVzXG5udW1zLmZvckVhY2godiA9PiB7XG4gIGlmICh2ICUgNSA9PT0gMClcbiAgICBmaXZlcy5wdXNoKHYpO1xufSk7XG5cbi8vIExleGljYWwgdGhpc1xudmFyIGJvYiA9IHtcbiAgX25hbWU6IFwiQm9iXCIsXG4gIF9mcmllbmRzOiBbXSxcbiAgcHJpbnRGcmllbmRzKCkge1xuICAgIHRoaXMuX2ZyaWVuZHMuZm9yRWFjaChmID0+XG4gICAgICBjb25zb2xlLmxvZyh0aGlzLl9uYW1lICsgXCIga25vd3MgXCIgKyBmKSk7XG4gIH1cbn07XG5cbi8vIGxpc3QgbWF0Y2hpbmdcbnZhciBbYSwgLGJdID0gWzEsMiwzXTtcbmEgPT09IDE7XG5iID09PSAzO1xuXG4vLyBvYmplY3QgbWF0Y2hpbmdcbnZhciB7IG9wOiBhLCBsaHM6IHsgb3A6IGIgfSwgcmhzOiBjIH1cbiAgPSBnZXRBU1ROb2RlKClcblxuLy8gb2JqZWN0IG1hdGNoaW5nIHNob3J0aGFuZFxuLy8gYmluZHMgYG9wYCwgYGxoc2AgYW5kIGByaHNgIGluIHNjb3BlXG52YXIge29wLCBsaHMsIHJoc30gPSBnZXRBU1ROb2RlKClcblxuLy8gQ2FuIGJlIHVzZWQgaW4gcGFyYW1ldGVyIHBvc2l0aW9uXG5mdW5jdGlvbiBnKHtuYW1lOiB4fSkge1xuICBjb25zb2xlLmxvZyh4KTtcbn1cbmcoe25hbWU6IDV9KVxuXG4vLyBGYWlsLXNvZnQgZGVzdHJ1Y3R1cmluZ1xudmFyIFthXSA9IFtdO1xuYSA9PT0gdW5kZWZpbmVkO1xuXG4vLyBGYWlsLXNvZnQgZGVzdHJ1Y3R1cmluZyB3aXRoIGRlZmF1bHRzXG52YXIgW2EgPSAxXSA9IFtdO1xuYSA9PT0gMTtcblxuLy8gRGVzdHJ1Y3R1cmluZyArIGRlZmF1bHRzIGFyZ3VtZW50c1xuZnVuY3Rpb24gcih7eCwgeSwgdyA9IDEwLCBoID0gMTB9KSB7XG4gIHJldHVybiB4ICsgeSArIHcgKyBoO1xufVxucih7eDoxLCB5OjJ9KSA9PT0gMjNcblxuXG5mdW5jdGlvbiBmKHgsIHk9MTIpIHtcbiAgLy8geSBpcyAxMiBpZiBub3QgcGFzc2VkIChvciBwYXNzZWQgYXMgdW5kZWZpbmVkKVxuICByZXR1cm4geCArIHk7XG59XG5mKDMpID09IDE1XG5mdW5jdGlvbiBmKHgsIC4uLnkpIHtcbiAgLy8geSBpcyBhbiBBcnJheVxuICByZXR1cm4geCAqIHkubGVuZ3RoO1xufVxuZigzLCBcImhlbGxvXCIsIHRydWUpID09IDZcbmZ1bmN0aW9uIGYoeCwgeSwgeikge1xuICByZXR1cm4geCArIHkgKyB6O1xufVxuLy8gUGFzcyBlYWNoIGVsZW0gb2YgYXJyYXkgYXMgYXJndW1lbnRcbmYoLi4uWzEsMiwzXSkgPT0gNlxuXG5cbmZ1bmN0aW9uIGYoKSB7XG4gIHtcbiAgICBsZXQgeDtcbiAgICB7XG4gICAgICAvLyBva2F5LCBibG9jayBzY29wZWQgbmFtZVxuICAgICAgY29uc3QgeCA9IFwic25lYWt5XCI7XG4gICAgfVxuICAgIC8vIG9rYXksIGRlY2xhcmVkIHdpdGggYGxldGBcbiAgICB4ID0gXCJiYXJcIjtcbiAgfVxufVxuXG5cbmZ1bmN0aW9uIGZhY3RvcmlhbChuLCBhY2MgPSAxKSB7XG4gIGlmIChuIDw9IDEpIHJldHVybiBhY2M7XG4gIHJldHVybiBmYWN0b3JpYWwobiAtIDEsIG4gKiBhY2MpO1xufVxuXG4vLyBTdGFjayBvdmVyZmxvdyBpbiBtb3N0IGltcGxlbWVudGF0aW9ucyB0b2RheSxcbi8vIGJ1dCBzYWZlIG9uIGFyYml0cmFyeSBpbnB1dHMgaW4gRVMyMDE1XG5leHBvcnQgY29uc3QgdGFpbCA9IGZhY3RvcmlhbCgxMDAwMDApIl19 -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | API Documentation 2 | === 3 | * [**Builder**](#builder) 4 | * [**Expression Strings**](#module-tree-expressions) 5 | 6 | Builder 7 | --- 8 | * [**new Builder()**](#new-builderconfig) 9 | * [**config()**](#builderconfigconfig-saveforreset-ignorebaseurl) 10 | * [**loadConfig()**](#builderloadconfigconfigfile-saveforreset-ignorebaseurl) 11 | * [**loadConfigSync()**](#builderloadconfigsyncconfigfilepath) 12 | * [**reset()**](#builderreset) 13 | * [**invalidate()**](#builderinvalidatemodulename) 14 | * [**bundle()**](#builderbundletree-outfile-options) 15 | * [**buildStatic()**](#builderbuildstatictree-outfile-options) 16 | * [**trace()**](#buildertraceexpression) 17 | * [**addTrees()**](#builderaddtreesfirsttree-secondtree) 18 | * [**subtractTrees()**](#buildersubtracttreesfirsttree-secondtree) 19 | * [**intersectTrees()**](#builderintersecttreesfirsttree-secondtree) 20 | 21 | ### new Builder(baseURL[, configFilePath]) 22 | `baseURL`: Sets the root for the loader 23 | `configFilePath`: A systemjs config file conforming to the [systemjs config api] (https://github.com/systemjs/systemjs/blob/master/docs/config-api.md) 24 | #### Example 25 | ```javascript 26 | new Builder('/scripts', 'config.js'); 27 | ``` 28 | 29 | ### builder.config(config) 30 | `config`: An object conforming to the [config api] (https://github.com/systemjs/systemjs/blob/master/docs/config-api.md) 31 | #### Example 32 | ```javascript 33 | builder.config({ 34 | map: { 35 | 'a': 'b.js' 36 | } 37 | }); 38 | ``` 39 | 40 | ### builder.loadConfig(configFilePath) 41 | `configFilePath`: A systemjs config file conforming to the [systemjs config api] (https://github.com/systemjs/systemjs/blob/master/docs/config-api.md) 42 | 43 | Returns a promise which resolves when config has been loaded 44 | #### Example 45 | ```javascript 46 | builder.loadConfig('config.js').then(() => { 47 | }); 48 | ``` 49 | ### builder.loadConfigSync(configFilePath) 50 | Synchronous version of `builder.loadConfig()` 51 | 52 | `configFilePath`: A systemjs config file conforming to the [systemjs config api] (https://github.com/systemjs/systemjs/blob/master/docs/config-api.md) 53 | #### Example 54 | ```javascript 55 | builder.loadConfigSync('config.js'); 56 | ``` 57 | 58 | ### builder.reset() 59 | Reset the builder config to its initial state and clear loader registry. This will remove any registered modules, but will maintain the builder's compiled module cache. For efficiency, use [builder.invalidate()](#builderinvalidatemodulename) to clear individual modules from the cache. 60 | #### Example 61 | ```javascript 62 | builder.reset(); 63 | ``` 64 | 65 | ### builder.invalidate(moduleName) 66 | Removes a module from the loader cache 67 | 68 | `moduleName`: The name of the module to invalidate, this can include wildcards 69 | 70 | Returns the list of invalidated modules 71 | #### Example 72 | ```javascript 73 | builder.invalidate("/jquery.js"); 74 | builder.invalidate("someLib/*"); 75 | ``` 76 | 77 | ### builder.bundle(tree[, outfile][, options]) 78 | ### builder.bundle(expression[, outfile][, options]) 79 | ### builder.bundle(moduleNames[, outfile][, options]) 80 | Concatenate all modules in the tree or module tree expression and optionally write them out to a file 81 | 82 | `tree`: A tree object as returned from builder.trace(), or one of the arithmetic functions 83 | `expression`: A [module tree expression](#module-tree-expressions) 84 | `moduleNames`: An array of exact, unnormalized module names \ 85 | **Important**: The module names in `expression` or `moduleNames` should only be `/` separated (On Windows, do NOT use `\` as your path separator for this argument). The module names are specified in URL space; in particular, they are not file-paths. \ 86 | `outfile`: The file to write out the bundle to 87 | `options`: Additional bundle options as outlined below 88 | 89 | Returns a promise which resolves with the bundle content 90 | #### Bundle options 91 | `minify`: Minify source in bundle output _(Default:true)_ 92 | `uglify`: Options to pass to uglifyjs 93 | `mangle`: Allow the minifier to shorten non-public variable names _(Default:false)_ 94 | `sourceMaps`: Generate source maps for minified code _(Default:false)_ 95 | `sourceMapContents`: Include original source in the generated sourceMaps, this will generate a self-contained sourceMap which will not require the browser to load the original source file during debugging _(Default:false)_ 96 | `lowResSourceMaps`: When true, use line-number level source maps, when false, use character level source maps _(Default:false)_ 97 | `globalName`: When building a self-executing bundle, assign the bundle output to a global variable _(Default:null)_ 98 | `globalDeps`: When building a self-executing bundle, indicates external dependendencies available in the global context _(Default:{})_ 99 | `fetch`: Override the fetch function to retrieve module source manually _(Default:undefined)_ 100 | `normalize`: Rewrite required module names to their normalized names _(Default:false)_ 101 | `anonymous`: Compile modules as anonymous modules _(Default:false)_ 102 | `systemGlobal`: The global used to register compiled modules with systemjs _(Default:'System')_ 103 | `format`: Module format to compile modules to _(Default:'umd')_ 104 | 105 | #### Example 106 | ```javascript 107 | builder.bundle('moduleA.js', { minify:true }).then((bundle) => { 108 | //bundle contains source to moduleA.js + dependencies 109 | }); 110 | ``` 111 | ### builder.buildStatic(tree[, outfile][, options]) 112 | ### builder.buildStatic(expression[, outfile][, options]) 113 | Similar to builder.bundle() but builds a self-executing bundle 114 | 115 | `tree`: A tree object as returned from builder.trace(), or one of the arithmetic functions 116 | `expression`: A [module tree expression](#module-tree-expressions) \ 117 | **Important**: The module names in `expression` or `moduleNames` should only be `/` separated (On Windows, do NOT use `\` as your path separator for this argument). The module names are specified in URL space; in particular, they are not file-paths. \ 118 | `outfile`: The file to write out the bundle to 119 | `options`: Additional bundle options as outlined in `builder.bundle()` 120 | 121 | Returns a promise which resolves with the bundle content 122 | #### Example 123 | ```javascript 124 | builder.buildStatic('moduleA.js').then((sfxBundle) => { 125 | //sfxBundle contains source to moduleA.js + dependencies + self-executing intialization code 126 | }); 127 | ``` 128 | 129 | ### builder.trace(expression) 130 | Creates the module tree object represented by `expression` 131 | 132 | `expression`: A [module tree expression](#module-tree-expressions) \ 133 | **Important**: The module names in `expression` should only be `/` separated (On Windows, do NOT use `\` as your path separator for this argument). The module names are specified in URL space; in particular, they are not file-paths. 134 | 135 | Returns a promise which resolves with the module tree 136 | 137 | #### Example 138 | ```javascript 139 | builder.trace('moduleA.js').then((moduleTree) => { 140 | }); 141 | ``` 142 | 143 | ### builder.addTrees(firstTree, secondTree) 144 | ```javascript 145 | let moduleTree = builder.addTrees('moduleA.js', 'moduleB.js') 146 | ``` 147 | 148 | ### builder.subtractTrees(firstTree, secondTree) 149 | ```javascript 150 | let moduleTree = builder.subtractTrees('moduleA.js', 'moduleB.js'); 151 | ``` 152 | 153 | ### builder.intersectTrees(firstTree, secondTree) 154 | ```javascript 155 | let moduleTree = builder.intersectTrees('moduleA.js', 'moduleB.js'); 156 | ``` 157 | 158 | ## Module Tree Expressions 159 | `builder.buildStatic`, `builder.bundle` and `builder.trace` accept module tree expressions 160 | 161 | ### Module Tree 162 | Represents moduleA and all of its dependencies 163 | ```javascript 164 | 'moduleA.js' 165 | ``` 166 | 167 | ### Single Module 168 | Represents moduleA only 169 | ```javascript 170 | '[moduleA.js]' 171 | ``` 172 | 173 | ### Addition 174 | Represents a tree that combines moduleA, moduleB and their dependencies 175 | ```javascript 176 | 'moduleA.js + moduleB.js' 177 | ``` 178 | 179 | ### Subtraction 180 | Represents a tree that includes moduleA and its dependencies, excluding moduleB and its dependencies 181 | ```javascript 182 | 'moduleA.js - moduleB.js' 183 | ``` 184 | 185 | ### Intersection 186 | Represents the dependencies shared between moduleA and moduleB 187 | ```javascript 188 | 'moduleA.js & moduleB.js' 189 | ``` 190 | 191 | ### Module Glob 192 | Represents the combination of all modules in dirA and their dependencies 193 | ```javascript 194 | 'dirA/*' 195 | ``` 196 | 197 | ### Parenthesis 198 | Use parenthesis to group module tree operations 199 | ```javascript 200 | '(moduleA.js & moduleB.js) + moduleC.js' 201 | ``` 202 | -------------------------------------------------------------------------------- /test/test-build.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | var inline = require('../lib/output').inlineSourceMap; 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var spawn = require('child_process').spawn; 6 | if (process.argv[2] == 'typescript') 7 | global.ts = require('typescript'); 8 | 9 | var minify = true; 10 | 11 | var err = function(e) { 12 | setTimeout(function() { 13 | throw e; 14 | }); 15 | }; 16 | 17 | var builder = new Builder('test/fixtures/test-tree', 'test/fixtures/test-tree.config.js'); 18 | 19 | function getCommandPath(command) { 20 | return path.resolve('./node_modules/.bin/' + command + (process.platform.match(/^win/) ? '.cmd' : '')); 21 | } 22 | 23 | function testPhantom(html) { 24 | return new Promise(function(resolve, reject) { 25 | spawn(getCommandPath('mocha-phantomjs'), ['-p', getCommandPath('phantomjs'), html], { stdio: 'inherit' }) 26 | .on('close', function(code) { 27 | if (code !== 0) 28 | reject(Error('Phantom test failed ' + html + ' failed.')); 29 | else 30 | resolve(); 31 | }); 32 | }); 33 | } 34 | 35 | function doTests(transpiler) { 36 | 37 | test('In-memory build', function() { 38 | builder.reset(); 39 | builder.config({ transpiler: transpiler }); 40 | return builder.bundle('first.js', { sourceMaps: true, minify: minify }) 41 | .then(function(output) { 42 | fs.writeFileSync('test/output/memory-test.js', inline(output.source, output.sourceMap)); 43 | }); 44 | }); 45 | 46 | test('Multi-format tree build', function() { 47 | builder.reset(); 48 | builder.config({ transpiler: transpiler }); 49 | 50 | return builder.bundle('first.js', 'test/output/tree-build.js', { sourceMaps: true, minify: minify, globalDefs: { DEBUG: false } }) 51 | .then(function(output) { 52 | assert(output.assetList[0].url); 53 | var treeFirst; 54 | Promise.all(['first.js', 'amd.js'].map(builder.trace.bind(builder))) 55 | .then(function(trees) { 56 | treeFirst = trees[0]; 57 | return builder.bundle(builder.subtractTrees(trees[0], trees[1]), 'test/output/excluded.js'); 58 | }) 59 | 60 | .then(function() { 61 | return builder.trace('global-inner.js').then(function(tree) { 62 | return builder.bundle(tree, 'test/output/global-inner.js'); 63 | }); 64 | }) 65 | 66 | .then(function() { 67 | return builder.trace('global-outer.js').then(function(tree) { 68 | return builder.bundle(tree, 'test/output/global-outer.js'); 69 | }); 70 | }) 71 | 72 | .then(function() { 73 | return builder.trace('amd-1.js').then(function(tree) { 74 | return builder.bundle(builder.subtractTrees(tree, treeFirst), 'test/output/amd-1.js'); 75 | }); 76 | }) 77 | 78 | .then(function() { 79 | return builder.trace('amd-2.js').then(function(tree) { 80 | return builder.bundle(builder.subtractTrees(tree, treeFirst), 'test/output/amd-2.js'); 81 | }); 82 | }) 83 | 84 | .then(function() { 85 | return builder.trace('amd-3.js').then(function(tree) { 86 | return builder.bundle(builder.subtractTrees(tree, treeFirst), 'test/output/amd-3.js'); 87 | }); 88 | }) 89 | 90 | .then(function() { 91 | return builder.trace('amd-4.js').then(function(tree) { 92 | return builder.getDeferredImports(tree) 93 | .then(function(deferredImports) { 94 | assert(deferredImports[0].name == 'x'); 95 | return builder.bundle(builder.subtractTrees(tree, treeFirst), 'test/output/amd-4.js'); 96 | }); 97 | }); 98 | }) 99 | 100 | .then(function() { 101 | return builder.trace('amd-5a.js').then(function(tree) { 102 | return builder.bundle(builder.subtractTrees(tree, treeFirst), 'test/output/amd-5a.js'); 103 | }); 104 | }) 105 | 106 | .then(function() { 107 | return builder.trace('amd-5b.js').then(function(tree) { 108 | return builder.bundle(builder.subtractTrees(tree, treeFirst), 'test/output/amd-5b.js'); 109 | }); 110 | }) 111 | 112 | 113 | .then(function() { 114 | return builder.trace('amd-6a.js').then(function(tree) { 115 | return builder.bundle(builder.subtractTrees(tree, treeFirst), 'test/output/amd-6a.js'); 116 | }); 117 | }) 118 | 119 | .then(function() { 120 | return builder.trace('amd-6b.js').then(function(tree) { 121 | return builder.bundle(builder.subtractTrees(tree, treeFirst), 'test/output/amd-6b.js'); 122 | }); 123 | }) 124 | 125 | .then(function() { 126 | return builder.trace('umd.js').then(function(tree) { 127 | return builder.bundle(builder.subtractTrees(tree, treeFirst), 'test/output/umd.js'); 128 | }); 129 | }) 130 | 131 | .then(function() { 132 | return builder.bundle('amd-7.js', 'test/output/amd-7.js'); 133 | }) 134 | 135 | .then(function() { 136 | return builder.bundle('amd-8.js', 'test/output/amd-8.js'); 137 | }) 138 | 139 | .then(function() { 140 | return builder.bundle('amd-9.js', 'test/output/amd-9.js'); 141 | }) 142 | 143 | .then(function() { 144 | return builder.bundle('amd-10.js', 'test/output/amd-10.js'); 145 | }) 146 | 147 | .then(function() { 148 | return builder.bundle('amd-11.js', 'test/output/amd-11.js'); 149 | }) 150 | 151 | .then(function() { 152 | return builder.bundle('amd-12.js', 'test/output/amd-12.js'); 153 | }) 154 | 155 | .then(function() { 156 | builder.loader.config({ paths: { 'output/*': './test/output/*' } }); 157 | return builder.bundle('cjs-globals.js - output/amd-8.js', 'test/output/cjs-globals.js'); 158 | }) 159 | 160 | .then(function() { 161 | return builder.bundle('cjs-resolve.js', 'test/output/cjs-resolve.js'); 162 | }) 163 | 164 | .then(function() { 165 | return builder.bundle('runtime.js', 'test/output/runtime.js'); 166 | }) 167 | }) 168 | .then(function () { 169 | return testPhantom('test/test-build.html'); 170 | }) 171 | ['catch'](function(err) { 172 | throw err; 173 | }); 174 | }); 175 | 176 | // traceur runtime function.bind fails in Phantom 177 | if (transpiler != 'traceur') 178 | test('SFX tree build', function() { 179 | builder.reset(); 180 | builder.config({ transpiler: transpiler }); 181 | builder.config({ 182 | map: { 183 | 'toamd1': 'amd-1.js' 184 | }, 185 | meta: { 186 | 'jquery-cdn': { 187 | build: false 188 | } 189 | } 190 | }); 191 | return builder.buildStatic('toamd1', 'test/output/sfx.js', { 192 | runtime: true, 193 | minify: minify, 194 | format: 'global', 195 | globalDefs: { DEBUG: false }, 196 | globalName: 'amd1', 197 | globalDeps: { 198 | 'jquery-cdn': '$' 199 | } 200 | }) 201 | .then(function() { 202 | return testPhantom('test/test-sfx.html'); 203 | }); 204 | }); 205 | } 206 | 207 | suite('Basics', function() { 208 | test('Circular map config', function() { 209 | builder.reset(); 210 | return builder.bundle('lodash'); 211 | }); 212 | }); 213 | 214 | suite('Test tree builds - Traceur', function() { 215 | 216 | doTests('traceur'); 217 | 218 | }); 219 | 220 | suite('Test tree builds - Babel', function() { 221 | 222 | doTests('babel'); 223 | 224 | }); 225 | 226 | suite('Test tree builds - TypeScript', function() { 227 | 228 | doTests('typescript'); 229 | 230 | }); 231 | 232 | suite('Bundle Format', function() { 233 | test('Test AMD format', function() { 234 | return Promise.resolve() 235 | .then(function() { 236 | return builder.buildStatic('sfx-format-01.js', 'test/output/sfx-amd-01.js', { format: 'amd' }); 237 | }) 238 | .then(function() { 239 | return builder.buildStatic('sfx-format-02.js', 'test/output/sfx-amd-02.js', { format: 'amd' }); 240 | }) 241 | .then(function() { 242 | return testPhantom('test/test-sfx-amd.html'); 243 | }); 244 | }); 245 | }); 246 | 247 | suite('Test ES6 minification', function() { 248 | test('ES6 syntax minify, unmangled', function() { 249 | builder.reset(); 250 | return builder.bundle('class-def.js', { minify: true , mangle: false }) 251 | .then(function(output) { 252 | assert.match(output.source, /class Foo{constructor\(\){/, 'source contains literal class'); 253 | }); 254 | }); 255 | 256 | test('ES6 syntax minify, mangled', function() { 257 | builder.reset(); 258 | return builder.bundle('class-def.js', { minify: true , mangle: true }) 259 | .then(function(output) { 260 | assert.match(output.source, /class \w{constructor\(\){/, 'source contains literal class, mangled'); 261 | }); 262 | }); 263 | }); 264 | -------------------------------------------------------------------------------- /test/test-build.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 285 | 291 | -------------------------------------------------------------------------------- /test/test-build-cache.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | var expect = require('chai').expect; 3 | var toFileURL = require('../lib/utils.js').toFileURL; 4 | var fs = require('fs'); 5 | 6 | suite('Test compiler cache', function() { 7 | var builder = new Builder('test/fixtures/test-cache-tree'); 8 | builder.config({ transpiler: 'babel' }); 9 | 10 | test('Use compile cache entry when available', function() { 11 | var loadName = 'simple.js'; 12 | var outputPath = 'test/output/cached.js'; 13 | var cacheObj; 14 | var tree; 15 | 16 | return builder.trace(loadName).then(function(_tree) { 17 | tree = _tree; 18 | return builder.bundle(tree); 19 | }) 20 | .then(function() { 21 | var cacheEntry = builder.getCache(); 22 | 23 | expect(cacheEntry).to.be.an('object'); 24 | 25 | cacheObj = cacheEntry.compile.loads['simple.js']; 26 | 27 | expect(cacheObj).to.be.an('object'); 28 | expect(cacheObj.hash).to.be.a('string'); 29 | expect(cacheObj.output).to.be.an('object'); 30 | 31 | // poison cache 32 | cacheObj.output.source = cacheObj.output.source.replace('hate', 'love'); 33 | 34 | return builder.bundle(tree); 35 | }) 36 | .then(function(output) { 37 | // verify buildTree use poisoned cache rather than recompiling 38 | var outputSource = output.source; 39 | expect(outputSource).not.to.contain('hate caches'); 40 | expect(outputSource).to.contain('love caches'); 41 | 42 | // invalidate poisoned cache entry and rebuild 43 | cacheObj.hash = 'out of date'; 44 | return builder.bundle(tree); 45 | }) 46 | .then(function(output) { 47 | // verify original source is used once more 48 | var outputSource = output.source; 49 | expect(outputSource).to.contain('hate caches'); 50 | expect(outputSource).not.to.contain('love caches'); 51 | }); 52 | }); 53 | 54 | test('Use trace cache when available', function() { 55 | // construct the load record for the cache 56 | var cacheObj = { 57 | trace: { 58 | 'simple.js': { 59 | name: 'simple.js', 60 | path: 'fixtures/test-cache-tree/simple.js', 61 | metadata: { 62 | deps: [], 63 | format: 'amd', 64 | isAnon: true 65 | }, 66 | deps: [], 67 | depMap: {}, 68 | source: 'define([], function(module) {\n console.log(\'fake cache\');\n});\n', 69 | originalSource: 'define([], function(module) {\n console.log(\'fake cache\');\n});\n' 70 | } 71 | } 72 | }; 73 | 74 | builder.reset(); 75 | builder.setCache(cacheObj); 76 | 77 | return builder.bundle('simple.js').then(function(output) { 78 | expect(output.source).to.contain('fake cache'); 79 | }); 80 | }); 81 | 82 | test('Cache invalidation', function() { 83 | var cacheObj = { 84 | trace: { 85 | 'simple.js': {}, 86 | 'another/path.js': {} 87 | } 88 | }; 89 | 90 | builder.reset(); 91 | builder.setCache(cacheObj); 92 | 93 | var invalidated = builder.invalidate('*'); 94 | assert.deepEqual(invalidated, [builder.loader.normalizeSync('simple.js'), builder.loader.normalizeSync('another/path.js')]); 95 | 96 | cacheObj = { 97 | trace: { 98 | 'simple.js': {}, 99 | 'new/path.js': {}, 100 | 'deep/wildcard/test.js': {} 101 | } 102 | }; 103 | 104 | builder.setCache(cacheObj); 105 | 106 | invalidated = builder.invalidate('new/path.js'); 107 | assert.deepEqual(invalidated, [builder.loader.normalizeSync('new/path.js')]); 108 | 109 | invalidated = builder.invalidate('deep/*.js'); 110 | assert.deepEqual(invalidated, [builder.loader.normalizeSync('deep/wildcard/test.js')]); 111 | }); 112 | 113 | test('builder.loader.fetch sets load.metadata.timestamp', function() { 114 | var source = 'export var p = 5;'; 115 | var builder = new Builder('test/output'); 116 | fs.writeFileSync('./test/output/timestamp-module.js', source); 117 | var address = builder.loader.normalizeSync('./test/output/timestamp-module.js'); 118 | var load = { name: address, address: address, metadata: {} }; 119 | return builder.loader.fetch(load) 120 | .then(function(text) { 121 | //console.log(JSON.stringify(load)); 122 | assert(text == source); // true 123 | assert(load.metadata.deps); // true 124 | assert(load.metadata.timestamp); // false 125 | }) 126 | }); 127 | 128 | test('Bundle example', function() { 129 | var builder = new Builder('test/output'); 130 | fs.writeFileSync('./test/output/dynamic-module.js', 'export var p = 5;'); 131 | 132 | return builder.bundle('dynamic-module.js') 133 | .then(function(output) { 134 | assert(output.source.match(/p = 5/)); 135 | 136 | fs.writeFileSync('./test/output/dynamic-module.js', 'export var p = 6;'); 137 | builder.invalidate('dynamic-module.js'); 138 | 139 | return builder.bundle('dynamic-module.js'); 140 | }) 141 | .then(function(output) { 142 | assert(output.source.match(/p = 6/)); 143 | }); 144 | }); 145 | 146 | test('Bundle example with imported file', function() { 147 | var builder = new Builder('test/output'); 148 | 149 | fs.writeFileSync('./test/output/dynamic-import.js', [ 150 | 'const d = 9;', 151 | 'export default d;' 152 | ].join('\n')); 153 | 154 | fs.writeFileSync('./test/output/dynamic-main.js', [ 155 | 'import d from "./dynamic-import.js";', 156 | 'console.log(d);' 157 | ].join('\n')); 158 | 159 | return builder.bundle('dynamic-main.js') 160 | .then(function(output) { 161 | assert(output.source.match(/d = 9/)); 162 | assert(output.source.match(/console/)); 163 | 164 | fs.writeFileSync('./test/output/dynamic-import.js', [ 165 | 'import "./dynamic-import2.js";', // Add another transitive dependency. 166 | 'const d = 7;', 167 | 'export default d;' 168 | ].join('\n')); 169 | builder.invalidate('dynamic-import.js'); 170 | 171 | fs.writeFileSync('./test/output/dynamic-import2.js', [ 172 | 'const u = "transitive";', 173 | 'export default u;' 174 | ].join('\n')); 175 | 176 | return builder.bundle('dynamic-main.js'); 177 | }) 178 | .then(function(output) { 179 | assert(output.source.match(/transitive/)); 180 | assert(output.source.match(/d = 7/)); 181 | assert(output.source.match(/console/)); 182 | 183 | // Remove the transitive dependency from the build. 184 | fs.writeFileSync('./test/output/dynamic-import.js', [ 185 | 'const d = 7;', 186 | 'export default d;' 187 | ].join('\n')); 188 | builder.invalidate('dynamic-import.js'); 189 | 190 | return builder.bundle('dynamic-main.js'); 191 | }) 192 | .then(function(output) { 193 | assert(!output.source.match(/transitive/)); 194 | }); 195 | }); 196 | 197 | test('Static build example statting check', function() { 198 | var builder = new Builder('test/output'); 199 | 200 | fs.writeFileSync('./test/output/static-main.js', "import { testThing } from './static-test-module.js'; testThing();"); 201 | fs.writeFileSync('./test/output/static-test-module.js', "export function testThing() { console.log('test'); }"); 202 | 203 | return builder.buildStatic('static-main.js') 204 | .then(function() { 205 | builder.invalidate('static-main.js'); 206 | // despite removing the file, it remains cached 207 | fs.unlinkSync('./test/output/static-test-module.js'); 208 | return builder.buildStatic('static-main.js'); 209 | }); 210 | }); 211 | 212 | test('Static build example dependency reload check', function() { 213 | var builder = new Builder('test/output'); 214 | 215 | fs.writeFileSync('./test/output/static-main.js', "import { testThing } from './static-test-module.js'; testThing();"); 216 | fs.writeFileSync('./test/output/static-test-module.js', "export function testThing() { console.log('test'); }"); 217 | 218 | return builder.buildStatic('static-main.js') 219 | .then(function() { 220 | fs.writeFileSync('./test/output/static-test-module.js', "export function testThing() { console.log('new test'); }") 221 | builder.invalidate('static-test-module.js'); 222 | return builder.buildStatic('static-main.js'); 223 | }); 224 | }); 225 | 226 | test('Static build, fetch override', function () { 227 | var builder = new Builder('test/fixtures/test-tree'); 228 | return builder.buildStatic('foo.js', { 229 | fetch: function (load, fetch) { 230 | if (load.name.indexOf('foo.js') !== -1) { 231 | return fs.readFileSync('test/fixtures/test-tree/cjs.js', 'utf8'); 232 | } else { 233 | return fetch(load); 234 | } 235 | } 236 | }); 237 | }); 238 | 239 | test('Static build, fetch override with callback', function () { 240 | var builder = new Builder('test/fixtures/test-tree'); 241 | return builder.buildStatic('cjs.js', { 242 | fetch: function (load, fetch) { 243 | return fetch(load); 244 | } 245 | }); 246 | }); 247 | 248 | test('Static string build', function () { 249 | var builder = new Builder('test/fixtures/test-tree'); 250 | return builder.bundle('foo.js', { 251 | fetch: function (load, fetch) { 252 | if (load.name.indexOf('foo.js') !== -1) { 253 | return fs.readFileSync('test/fixtures/test-tree/cjs.js', 'utf8'); 254 | } else { 255 | return fetch(load); 256 | } 257 | } 258 | }); 259 | }); 260 | }); 261 | -------------------------------------------------------------------------------- /test/sourcemaps.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var Builder = require('../index'); 3 | 4 | function atob(str) { 5 | return new Buffer(str, 'base64').toString('binary'); 6 | } 7 | 8 | var err = function(e) { 9 | setTimeout(function() { 10 | throw e; 11 | }); 12 | }; 13 | 14 | var buildOpts = { sourceMaps: true }; 15 | var configFile = './test/fixtures/test-tree.config.js'; 16 | 17 | var compareSourceMaps = function(filename, expectation, done, transpiler) { 18 | var instance = new Builder(); 19 | instance.loadConfigSync(configFile); 20 | buildOpts.config = buildOpts.config || {}; 21 | buildOpts.config.transpiler = transpiler || 'traceur'; 22 | instance.bundle(filename, null, buildOpts) 23 | .then(function(output) { 24 | assert.equal(output.sourceMap.toString(), expectation); 25 | }) 26 | .then(done) 27 | .catch(err); 28 | }; 29 | 30 | var readExpectation = function(filename) { 31 | return fs.readFileSync('test/fixtures/sourcemaps-expectations/' + filename).toString().replace(/\n$/, ''); 32 | }; 33 | 34 | function writeTestOutput() { 35 | var builder = new Builder(); 36 | return builder.loadConfig(configFile) 37 | .then(function() { 38 | builder.buildStatic('first.js', 'test/output/output.js', buildOpts); 39 | }); 40 | } 41 | 42 | function writeSourceMaps(moduleName, transpiler, sourceMapFile) { 43 | var instance = new Builder(); 44 | instance.loadConfigSync(configFile); 45 | buildOpts.config = buildOpts.config || {}; 46 | buildOpts.config.transpiler = transpiler || 'traceur'; 47 | instance.bundle(moduleName, null, buildOpts) 48 | .then(function(output) { 49 | fs.writeFile('test/fixtures/sourcemaps-expectations/' + sourceMapFile, output.sourceMap.toString()); 50 | }) 51 | .catch(err); 52 | } 53 | 54 | var toJSON = function(map) { 55 | return JSON.parse(map.toString()); 56 | }; 57 | 58 | var getSources = function(map) { 59 | return toJSON(map).sources; 60 | }; 61 | 62 | suite('Source Maps', function() { 63 | suiteSetup(writeTestOutput); 64 | 65 | test('can render inline', function(done) { 66 | var module = 'amd-2.js + cjs.js'; 67 | 68 | var instance = new Builder(); 69 | instance.loadConfigSync(configFile); 70 | instance.bundle(module, null, { sourceMaps: 'inline' }) 71 | .then(function(output) { 72 | assert.equal(undefined, output.sourceMap); 73 | var source = output.source; 74 | assert.equal(1, source.match(/sourceMappingURL=/g).length); 75 | var lines = output.source.split("\n"); 76 | var lastLine = lines[lines.length - 1]; 77 | var commentPrefix = /^\/\/# sourceMappingURL=data:application\/json;base64,/; 78 | assert(lastLine.match(commentPrefix)); 79 | var encoding = lastLine.replace(commentPrefix, ""); 80 | var decoded = JSON.parse(atob(encoding)); 81 | assert.deepEqual(decoded.sources, 82 | [ "test/fixtures/test-tree/amd-2.js", 83 | "test/fixtures/test-tree/cjs.js" ]); 84 | }) 85 | .then(done) 86 | .catch(err); 87 | }); 88 | 89 | test('can consume input source maps', function(done) { 90 | var module = 'register.js'; 91 | 92 | var instance = new Builder(); 93 | instance.loadConfigSync(configFile); 94 | instance.bundle(module, null, { sourceMaps: true }) 95 | .then(function(output) { 96 | var sources = getSources(output.sourceMap); 97 | assert.deepEqual(sources, 98 | [ "test/fixtures/test-tree/jquery.js", 99 | "test/fixtures/test-tree/global.js", 100 | "test/fixtures/test-tree/example", 101 | "test/fixtures/test-tree/babel.js" ]); 102 | }) 103 | .then(done) 104 | .catch(err); 105 | }); 106 | 107 | test('can be disabled for tracing', function(done) { 108 | var module = 'register.js'; 109 | var instance = new Builder(); 110 | 111 | // Load our test configuration. 112 | instance.loadConfigSync(configFile); 113 | 114 | instance 115 | .bundle(module, { sourceMaps: false }) 116 | .then(function(output) { 117 | assert.isUndefined(output.sourceMap); 118 | }) 119 | .then(done) 120 | .catch(err); 121 | }); 122 | 123 | suite('sources paths', function() { 124 | 125 | test('are relative to outFile', function(done) { 126 | var builder = new Builder(); 127 | builder.loadConfigSync(configFile); 128 | builder.buildStatic('first.js', 'test/output/output.js', buildOpts) 129 | .then(function(outputs) { 130 | var sources = getSources(outputs.sourceMap); 131 | assert.deepEqual(sources, 132 | [ '../fixtures/test-tree/third.js', 133 | '../fixtures/test-tree/cjs.js', 134 | '../fixtures/test-tree/second.js', 135 | '../fixtures/test-tree/jquery.js', 136 | '../fixtures/test-tree/global.js', 137 | '../fixtures/test-tree/text.txt', 138 | '../fixtures/test-tree/amd.js', 139 | '../fixtures/test-tree/unknown', // component.jsx 140 | '../fixtures/test-tree/file.json', 141 | '../fixtures/test-tree/first.js' ]); 142 | }) 143 | .then(done) 144 | .catch(err); 145 | }); 146 | 147 | test('are relative to baseURL, if no outFile', function(done) { 148 | var builder = new Builder(); 149 | builder.config({ baseURL: 'test/fixtures/test-tree' }); 150 | builder.loadConfigSync(configFile); 151 | var opts = { sourceMaps: true }; 152 | builder.buildStatic('first.js', null, opts) 153 | .then(function(outputs) { 154 | var sources = getSources(outputs.sourceMap); 155 | assert.deepEqual(sources, 156 | [ 'third.js', 157 | 'cjs.js', 158 | 'second.js', 159 | 'jquery.js', 160 | 'global.js', 161 | 'text.txt', 162 | 'amd.js', 163 | 'unknown', // component.jsx 164 | 'file.json', 165 | 'first.js' ]); 166 | }) 167 | .then(done) 168 | .catch(err); 169 | }); 170 | }); 171 | 172 | suite('Option: sourceMapContents', function() { 173 | test.skip('includes all file contents', function(done) { 174 | var builder = new Builder(); 175 | builder.loadConfigSync(configFile); 176 | var opts = { sourceMaps: true, sourceMapContents: true }; 177 | builder.buildStatic('first.js', null, opts) 178 | .then(function(outputs) { 179 | var map = toJSON(outputs.sourceMap); 180 | var sources = map.sources; 181 | var contents = map.sourcesContent; 182 | assert.equal(sources.length, contents.length); 183 | for (var i=0; i 0, 'empty sourcemap content'); 186 | assert.equal(content, fs.readFileSync(sources[i]).toString()); 187 | } 188 | }) 189 | .then(done) 190 | .catch(err); 191 | }); 192 | }); 193 | 194 | 195 | suite('Traceur', function() { 196 | var transpiler = 'traceur'; 197 | 198 | suite('without input source maps', function() { 199 | test('handles single compilation targets correctly', function(done) { 200 | var module = 'amd-2.js'; 201 | var source = 'traceur.tree.single.json'; 202 | if (process.env.UPDATE_EXPECTATIONS) 203 | writeSourceMaps(module, transpiler, source); 204 | var expected = readExpectation(source); 205 | compareSourceMaps(module, expected, done, transpiler); 206 | }); 207 | 208 | test('handles multiple compilation targets correctly', function(done) { 209 | var module = 'first.js'; 210 | var source = 'traceur.tree.multi.json'; 211 | if (process.env.UPDATE_EXPECTATIONS) 212 | writeSourceMaps(module, transpiler, source); 213 | var expected = readExpectation(source); 214 | compareSourceMaps(module, expected, done, transpiler); 215 | }); 216 | }); 217 | }); 218 | 219 | suite('babel', function() { 220 | var transpiler = 'babel'; 221 | 222 | suite('without input source maps', function() { 223 | test('handles single compilation targets correctly', function(done) { 224 | var module = 'amd-2.js'; 225 | var source = 'babel.tree.single.json'; 226 | if (process.env.UPDATE_EXPECTATIONS) 227 | writeSourceMaps(module, transpiler, source); 228 | var expected = readExpectation(source); 229 | compareSourceMaps(module, expected, done, transpiler); 230 | }); 231 | 232 | test('handles multiple compilation targets correctly', function(done) { 233 | var module = 'first.js'; 234 | var source = 'babel.tree.multi.json'; 235 | if (process.env.UPDATE_EXPECTATIONS) 236 | writeSourceMaps(module, transpiler, source); 237 | var expected = readExpectation(source); 238 | compareSourceMaps(module, expected, done, transpiler); 239 | }); 240 | }); 241 | }); 242 | 243 | test('can handle multiple transpiled files with same name', function(done) { 244 | var module = 'sameName/1/amd.js + sameName/2/amd.js'; 245 | 246 | var instance = new Builder(); 247 | instance.loadConfigSync(configFile); 248 | instance.config({ 249 | transpiler: 'plugin-babel', 250 | map: { 251 | 'plugin-babel': './node_modules/systemjs-plugin-babel/plugin-babel.js', 252 | 'systemjs-babel-build': './node_modules/systemjs-plugin-babel/systemjs-babel-node.js', 253 | }, 254 | packages: { 255 | sameName: { 256 | format: 'amd', 257 | }, 258 | }, 259 | }); 260 | instance.buildStatic(module, null, { sourceMaps: true, sourceMapContents: true }) 261 | .then(function() { 262 | done() 263 | }) 264 | .catch(err); 265 | }); 266 | }); 267 | -------------------------------------------------------------------------------- /compilers/esm.js: -------------------------------------------------------------------------------- 1 | var traceur = require('traceur'); 2 | var traceurGet = require('../lib/utils').traceurGet; 3 | 4 | var syntaxDynamicImport = require('babel-plugin-syntax-dynamic-import'); 5 | 6 | var ParseTreeTransformer = traceurGet('codegeneration/ParseTreeTransformer.js').ParseTreeTransformer; 7 | var ModuleSpecifier = traceurGet('syntax/trees/ParseTrees.js').ModuleSpecifier; 8 | var createStringLiteralToken = traceurGet('codegeneration/ParseTreeFactory.js').createStringLiteralToken; 9 | var InstantiateModuleTransformer = traceurGet('codegeneration/InstantiateModuleTransformer.js').InstantiateModuleTransformer; 10 | 11 | var extend = require('../lib/utils').extend; 12 | 13 | // patch pending https://github.com/google/traceur-compiler/pull/2053 14 | var createUseStrictDirective = traceurGet('codegeneration/ParseTreeFactory.js').createUseStrictDirective; 15 | InstantiateModuleTransformer.prototype.__proto__.moduleProlog = function() { 16 | return [createUseStrictDirective()]; 17 | }; 18 | 19 | 20 | var CollectingErrorReporter = traceurGet('util/CollectingErrorReporter.js').CollectingErrorReporter; 21 | var UniqueIdentifierGenerator = traceurGet('codegeneration/UniqueIdentifierGenerator.js').UniqueIdentifierGenerator; 22 | 23 | function TraceurImportNormalizeTransformer(map) { 24 | this.map = map; 25 | return ParseTreeTransformer.apply(this, arguments); 26 | } 27 | TraceurImportNormalizeTransformer.prototype = Object.create(ParseTreeTransformer.prototype); 28 | TraceurImportNormalizeTransformer.prototype.transformModuleSpecifier = function(tree) { 29 | var depName = this.map(tree.token.processedValue) || tree.token.processedValue; 30 | 31 | return new ModuleSpecifier(tree.location, createStringLiteralToken(depName)); 32 | }; 33 | 34 | exports.TraceurImportNormalizeTransformer = TraceurImportNormalizeTransformer; 35 | 36 | 37 | function remap(source, map, fileName) { 38 | var compiler = new traceur.Compiler(); 39 | 40 | var tree = compiler.parse(source, fileName); 41 | 42 | tree = new TraceurImportNormalizeTransformer(map).transformAny(tree); 43 | 44 | return Promise.resolve({ 45 | source: compiler.write(tree) 46 | }); 47 | } 48 | exports.remap = remap; 49 | 50 | // override System instantiate to handle esm tracing 51 | exports.attach = function(loader) { 52 | var systemInstantiate = loader.instantiate; 53 | loader.instantiate = function(load) { 54 | // skip plugin loader attachment || non es modules || es modules handled by internal transpilation layer 55 | if (!loader.builder || load.metadata.format != 'esm' || load.metadata.originalSource) 56 | return systemInstantiate.call(this, load); 57 | 58 | var depsList = load.metadata.deps.concat([]); 59 | 60 | var babel = require('babel-core'); 61 | var output = babel.transform(load.source, { 62 | babelrc: false, 63 | compact: false, 64 | filename: load.path, 65 | //sourceFileName: load.path, 66 | inputSourceMap: load.metadata.sourceMap, 67 | ast: true, 68 | plugins: [syntaxDynamicImport], 69 | resolveModuleSource: function(dep) { 70 | if (depsList.indexOf(dep) == -1) 71 | depsList.push(dep); 72 | return dep; 73 | } 74 | }); 75 | // turn back on comments (for some reason!) 76 | output.ast.comments.forEach(function(comment) { 77 | comment.ignore = false; 78 | }); 79 | load.metadata.ast = output.ast; 80 | 81 | return Promise.resolve({ 82 | deps: depsList, 83 | execute: null 84 | }); 85 | }; 86 | }; 87 | 88 | var versionCheck = true; 89 | 90 | exports.compile = function(load, opts, loader) { 91 | if (!load.metadata.originalSource || load.metadata.loader && load.metadata.format == 'esm') { 92 | var babel = require('babel-core'); 93 | 94 | var babelOptions = { 95 | babelrc: false, 96 | compact: false, 97 | plugins: [syntaxDynamicImport, [require('babel-plugin-transform-es2015-modules-systemjs'), { systemGlobal: opts.systemGlobal }]], 98 | filename: load.path, 99 | //sourceFileName: load.path, 100 | sourceMaps: !!opts.sourceMaps, 101 | inputSourceMap: load.metadata.sourceMap, 102 | moduleIds: !opts.anonymous, 103 | moduleId: !opts.anonymous && load.name, 104 | code: true, 105 | resolveModuleSource: function(dep) { 106 | if (opts.normalize) 107 | return load.depMap[dep]; 108 | else 109 | return dep; 110 | } 111 | }; 112 | 113 | var source = load.metadata.originalSource || load.source; 114 | 115 | var output; 116 | if (load.metadata.ast) 117 | output = babel.transformFromAst(load.metadata.ast, source, babelOptions); 118 | else 119 | output = babel.transform(source, babelOptions); 120 | 121 | // NB this can be removed with merging of () 122 | if (opts.systemGlobal != 'System') 123 | output.code = output.code.replace(/(\s|^)System\.register\(/, '$1' + opts.systemGlobal + '.register('); 124 | 125 | // for some reason Babel isn't respecting sourceFileName... 126 | if (output.map && !load.metadata.sourceMap) 127 | output.map.sources[0] = load.path; 128 | 129 | return Promise.resolve({ 130 | source: output.code, 131 | sourceMap: output.map 132 | }); 133 | } 134 | 135 | // ... legacy transpilation, to be deprecated with internal transpilation layer 136 | return Promise.resolve(global[loader.transpiler == 'typescript' ? 'ts' : loader.transpiler] || loader.pluginLoader.import(loader.transpiler)) 137 | .then(function(transpiler) { 138 | if (transpiler.__useDefault) 139 | transpiler = transpiler['default']; 140 | 141 | if (transpiler.Compiler) { 142 | var options = loader.traceurOptions || {}; 143 | options.modules = 'instantiate'; 144 | options.script = false; 145 | options.sourceRoot = true; 146 | options.moduleName = !opts.anonymous; 147 | 148 | if (opts.sourceMaps) 149 | options.sourceMaps = 'memory'; 150 | if (opts.lowResSourceMaps) 151 | options.lowResolutionSourceMap = true; 152 | 153 | if (load.metadata.sourceMap) 154 | options.inputSourceMap = load.metadata.sourceMap; 155 | 156 | var compiler = new transpiler.Compiler(options); 157 | 158 | var tree = compiler.parse(load.source, load.path); 159 | 160 | var transformer = new TraceurImportNormalizeTransformer(function(dep) { 161 | return opts.normalize ? load.depMap[dep] : dep; 162 | }); 163 | 164 | tree = transformer.transformAny(tree); 165 | 166 | tree = compiler.transform(tree, load.name); 167 | 168 | var outputSource = compiler.write(tree, load.path); 169 | 170 | if (outputSource.match(/\$traceurRuntime/)) 171 | load.metadata.usesTraceurRuntimeGlobal = true; 172 | 173 | return Promise.resolve({ 174 | source: outputSource, 175 | sourceMap: compiler.getSourceMap() 176 | }); 177 | } 178 | else if (transpiler.createLanguageService) { 179 | var options = loader.typescriptOptions || {}; 180 | if (options.target === undefined) 181 | options.target = transpiler.ScriptTarget.ES5; 182 | options.module = transpiler.ModuleKind.System; 183 | 184 | var transpileOptions = { 185 | compilerOptions: options, 186 | renamedDependencies: load.depMap, 187 | fileName: load.path, 188 | moduleName: !opts.anonymous && load.name 189 | }; 190 | 191 | var transpiled = transpiler.transpileModule(load.source, transpileOptions); 192 | 193 | return Promise.resolve({ 194 | source: transpiled.outputText, 195 | sourceMap: transpiled.sourceMapText 196 | }); 197 | } 198 | else { 199 | if (versionCheck) { 200 | var babelVersion = transpiler.version; 201 | if (babelVersion.split('.')[0] > 5) 202 | console.log('Warning - using Babel ' + babelVersion + '. This version of SystemJS builder is designed to run against Babel 5.'); 203 | versionCheck = false; 204 | } 205 | 206 | var options = extend({}, loader.babelOptions || {}); 207 | options.modules = 'system'; 208 | if (opts.sourceMaps) 209 | options.sourceMap = true; 210 | if (load.metadata.sourceMap) 211 | options.inputSourceMap = load.metadata.sourceMap; 212 | options.filename = load.path; 213 | options.filenameRelative = load.name; 214 | options.sourceFileName = load.path; 215 | options.keepModuleIdExtensions = true; 216 | options.code = true; 217 | options.ast = false; 218 | options.moduleIds = !opts.anonymous; 219 | options.externalHelpers = true; 220 | 221 | if (transpiler.version.match(/^4/)) 222 | options.returnUsedHelpers = true; 223 | else if (transpiler.version.match(/^5\.[01234]\./)) 224 | options.metadataUsedHelpers = true; 225 | 226 | if (opts.normalize) 227 | options.resolveModuleSource = function(dep) { 228 | return load.depMap[dep] || dep; 229 | }; 230 | 231 | var output = transpiler.transform(load.source, options); 232 | 233 | var usedHelpers = output.usedHelpers || output.metadata && output.metadata.usedHelpers; 234 | 235 | if ((!options.optional || options.optional.indexOf('runtime') == -1) && usedHelpers.length) 236 | load.metadata.usesBabelHelpersGlobal = true; 237 | 238 | // pending Babel v5, we need to manually map the helpers 239 | if (options.optional && options.optional.indexOf('runtime') != -1) 240 | load.deps.forEach(function(dep) { 241 | if (dep.match(/^babel-runtime/)) 242 | output.code = output.code.replace(dep, load.depMap[dep]); 243 | }); 244 | 245 | // clear options for reuse 246 | delete options.filenameRelative; 247 | delete options.sourceFileName; 248 | 249 | return Promise.resolve({ 250 | source: output.code, 251 | sourceMap: output.map 252 | }); 253 | } 254 | }) 255 | .then(function(output) { 256 | if (opts.systemGlobal != 'System') 257 | output.source = output.source.replace(/System\.register\(/, opts.systemGlobal + '.register('); 258 | return output; 259 | }); 260 | }; 261 | -------------------------------------------------------------------------------- /templates/sfx-core.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | var registry = {}; 3 | 4 | var BASE_OBJECT = typeof Symbol !== 'undefined' ? Symbol() : '@@baseObject'; 5 | 6 | function extendNamespace (key) { 7 | Object.defineProperty(this, key, { 8 | enumerable: true, 9 | get: function () { 10 | return this[BASE_OBJECT][key]; 11 | } 12 | }); 13 | } 14 | 15 | function createExternalModule (exports) { 16 | // a real ES module or SystemJS ES Module 17 | if (typeof System !== 'undefined' && System.isModule ? System.isModule(exports) : 18 | Object.prototype.toString.call(exports) === '[object Module]') { 19 | return exports; 20 | } 21 | 22 | var esModule = { default: exports, __useDefault: exports }; 23 | 24 | // CJS es module -> lift into namespace 25 | if (exports && exports.__esModule) { 26 | for (var p in exports) { 27 | if (Object.hasOwnProperty.call(exports, p)) 28 | esModule[p] = exports[p]; 29 | } 30 | } 31 | 32 | return new Module(esModule); 33 | } 34 | 35 | function Module (bindings) { 36 | Object.defineProperty(this, BASE_OBJECT, { 37 | value: bindings 38 | }); 39 | Object.keys(bindings).forEach(extendNamespace, this); 40 | } 41 | Module.prototype = Object.create(null); 42 | if (typeof Symbol !== 'undefined' && Symbol.toStringTag) 43 | Module.prototype[Symbol.toStringTag] = 'Module'; 44 | 45 | var nodeRequire = typeof System != 'undefined' && System._nodeRequire || typeof require != 'undefined' && typeof require.resolve != 'undefined' && typeof process != 'undefined' && process.platform && require; 46 | 47 | function getLoad (key) { 48 | if (key.substr(0, 6) === '@node/') 49 | return defineModule(key, createExternalModule(nodeRequire(key.substr(6))), {}); 50 | else 51 | return registry[key]; 52 | } 53 | 54 | function load (key) { 55 | var load = getLoad(key); 56 | 57 | if (!load) 58 | throw new Error('Module "' + key + '" expected, but not contained in build.'); 59 | 60 | if (load.module) 61 | return load.module; 62 | 63 | var link = load.linkRecord; 64 | 65 | instantiate(load, link); 66 | 67 | doEvaluate(load, link, []); 68 | 69 | return load.module; 70 | } 71 | 72 | function instantiate (load, link) { 73 | // circular stop condition 74 | if (link.depLoads) 75 | return; 76 | 77 | if (link.declare) 78 | registerDeclarative(load, link); 79 | 80 | link.depLoads = []; 81 | for (var i = 0; i < link.deps.length; i++) { 82 | var depLoad = getLoad(link.deps[i]); 83 | link.depLoads.push(depLoad); 84 | if (depLoad.linkRecord) 85 | instantiate(depLoad, depLoad.linkRecord); 86 | 87 | var setter = link.setters && link.setters[i]; 88 | if (setter) { 89 | setter(depLoad.module || depLoad.linkRecord.moduleObj); 90 | depLoad.importerSetters.push(setter); 91 | } 92 | } 93 | 94 | return load; 95 | } 96 | 97 | function registerDeclarative (load, link) { 98 | var moduleObj = link.moduleObj; 99 | var importerSetters = load.importerSetters; 100 | 101 | var locked = false; 102 | 103 | // closure especially not based on link to allow link record disposal 104 | var declared = link.declare.call(global, function (name, value) { 105 | // export setter propogation with locking to avoid cycles 106 | if (locked) 107 | return; 108 | 109 | if (typeof name == 'object') { 110 | for (var p in name) 111 | if (p !== '__useDefault') 112 | moduleObj[p] = name[p]; 113 | } 114 | else { 115 | moduleObj[name] = value; 116 | } 117 | 118 | locked = true; 119 | for (var i = 0; i < importerSetters.length; i++) 120 | importerSetters[i](moduleObj); 121 | locked = false; 122 | 123 | return value; 124 | }, { id: load.key }); 125 | 126 | if (typeof declared !== 'function') { 127 | link.setters = declared.setters; 128 | link.execute = declared.execute; 129 | } 130 | else { 131 | link.setters = []; 132 | link.execute = declared; 133 | } 134 | } 135 | 136 | function register (key, deps, declare) { 137 | return registry[key] = { 138 | key: key, 139 | module: undefined, 140 | importerSetters: [], 141 | linkRecord: { 142 | deps: deps, 143 | depLoads: undefined, 144 | declare: declare, 145 | setters: undefined, 146 | execute: undefined, 147 | moduleObj: {} 148 | } 149 | }; 150 | }; 151 | 152 | function registerDynamic (key, deps, executingRequire, execute) { 153 | var exports = {}; 154 | return registry[key] = { 155 | key: key, 156 | module: undefined, 157 | importerSetters: [], 158 | linkRecord: { 159 | deps: deps, 160 | depLoads: undefined, 161 | declare: undefined, 162 | execute: execute, 163 | executingRequire: executingRequire, 164 | moduleObj: { 165 | default: exports, 166 | __useDefault: exports 167 | }, 168 | setters: undefined 169 | } 170 | }; 171 | } 172 | 173 | function makeDynamicRequire (deps, depLoads, seen) { 174 | // we can only require from already-known dependencies 175 | return function (name) { 176 | for (var i = 0; i < deps.length; i++) 177 | if (deps[i] === name) { 178 | var depLoad = depLoads[i]; 179 | var depLink = depLoad.linkRecord; 180 | var module; 181 | if (depLink) { 182 | if (seen.indexOf(depLoad) === -1) 183 | module = doEvaluate(depLoad, depLink, seen); 184 | else 185 | module = depLink.moduleObj; 186 | } 187 | else { 188 | module = depLoad.module; 189 | } 190 | return '__useDefault' in module ? module.__useDefault : module; 191 | } 192 | }; 193 | } 194 | 195 | function doEvaluate (load, link, seen) { 196 | seen.push(load); 197 | 198 | if (load.module) 199 | return load.module; 200 | 201 | var err; 202 | 203 | // es modules evaluate dependencies first 204 | if (link.setters) { 205 | for (var i = 0; i < link.deps.length; i++) { 206 | var depLoad = link.depLoads[i]; 207 | var depLink = depLoad.linkRecord; 208 | 209 | if (depLink && seen.indexOf(depLoad) === -1) 210 | err = doEvaluate(depLoad, depLink, depLink.setters ? seen : []); 211 | } 212 | 213 | link.execute.call(nullContext); 214 | } 215 | else { 216 | var module = { id: load.key }; 217 | var moduleObj = link.moduleObj; 218 | Object.defineProperty(module, 'exports', { 219 | configurable: true, 220 | set: function (exports) { 221 | moduleObj.default = moduleObj.__useDefault = exports; 222 | }, 223 | get: function () { 224 | return moduleObj.__useDefault; 225 | } 226 | }); 227 | var require = makeDynamicRequire(link.deps, link.depLoads, seen); 228 | 229 | // evaluate deps first 230 | if (!link.executingRequire) 231 | for (var i = 0; i < link.deps.length; i++) 232 | require(link.deps[i]); 233 | 234 | var output = link.execute.call(global, require, moduleObj.__useDefault, module); 235 | if (output !== undefined) 236 | moduleObj.default = moduleObj.__useDefault = output; 237 | else if (module.exports !== moduleObj.__useDefault) 238 | moduleObj.default = moduleObj.__useDefault = module.exports; 239 | 240 | var moduleDefault = moduleObj.__useDefault; 241 | 242 | // __esModule flag extension support 243 | if (moduleDefault && moduleDefault.__esModule) 244 | for (var p in moduleDefault) 245 | if (Object.hasOwnProperty.call(moduleDefault, p)) 246 | moduleObj[p] = moduleDefault[p]; 247 | } 248 | 249 | var module = load.module = new Module(link.moduleObj); 250 | 251 | if (!link.setters) 252 | for (var i = 0; i < load.importerSetters.length; i++) 253 | load.importerSetters[i](module); 254 | 255 | return module; 256 | } 257 | 258 | // {} is the closest we can get to call(undefined) 259 | var nullContext = {}; 260 | if (Object.freeze) 261 | Object.freeze(nullContext); 262 | 263 | function defineModule (name, module) { 264 | return registry[name] = { 265 | key: name, 266 | module: module, 267 | importerSetters: [], 268 | linkRecord: undefined 269 | }; 270 | } 271 | 272 | return function (mains, depNames, exportDefault, declare) { 273 | return function (formatDetect) { 274 | formatDetect(function (deps) { 275 | var System = { 276 | _nodeRequire: nodeRequire, 277 | register: register, 278 | registerDynamic: registerDynamic, 279 | registry: { 280 | get: function (name) { 281 | return registry[name].module; 282 | }, 283 | set: defineModule 284 | }, 285 | newModule: function (module) { 286 | return new Module(module); 287 | } 288 | }; 289 | 290 | defineModule('@empty', new Module({})); 291 | 292 | // register external dependencies 293 | for (var i = 0; i < depNames.length; i++) 294 | defineModule(depNames[i], createExternalModule(arguments[i], {})); 295 | 296 | // register modules in this bundle 297 | declare(System); 298 | 299 | // load mains 300 | var firstLoad = load(mains[0]); 301 | if (mains.length > 1) 302 | for (var i = 1; i < mains.length; i++) 303 | load(mains[i]); 304 | 305 | if (exportDefault) 306 | return firstLoad.__useDefault; 307 | 308 | if (firstLoad instanceof Module) 309 | Object.defineProperty(firstLoad, '__esModule', { 310 | value: true 311 | }); 312 | 313 | return firstLoad; 314 | }); 315 | }; 316 | }; 317 | 318 | })((typeof self !== 'undefined') ? self : (typeof global !== 'undefined') ? global : this) 319 | /* (['mainModule'], ['external-dep'], false, function($__System) { 320 | System.register(...); 321 | }) 322 | (function(factory) { 323 | if (typeof define && define.amd) 324 | define(['external-dep'], factory); 325 | // etc UMD / module pattern 326 | })*/ 327 | -------------------------------------------------------------------------------- /test/arithmetic.js: -------------------------------------------------------------------------------- 1 | var Builder = require('../index'); 2 | var builder = new Builder(); 3 | 4 | builder.loadConfigSync('./test/fixtures/test-tree.config.js'); 5 | 6 | builder.config({ transpiler: 'babel' }); 7 | 8 | suite('Bundle Expressions', function() { 9 | test('Addition', function(done) { 10 | builder.trace('amd.js + amd-2.js') 11 | .then(function(tree) { 12 | assert.deepEqual(Object.keys(tree).sort(), 13 | ['amd-2.js', 'amd.js', 'global.js', 'jquery.js', 'some.js!plugin.js', 'text.txt!text-plugin.js']); 14 | }) 15 | .then(done, done); 16 | }); 17 | 18 | test('Single module subtraction', function(done) { 19 | builder.trace('amd.js + amd-2.js - [amd-1.js]') 20 | .then(function(tree) { 21 | assert.deepEqual(Object.keys(tree).sort(), 22 | ['amd-2.js', 'amd.js', 'global.js', 'jquery.js', 'some.js!plugin.js', 'text.txt!text-plugin.js']); 23 | }) 24 | .then(done, done); 25 | }); 26 | 27 | test('Commonality operator', function(done) { 28 | builder.trace('amd-5b.js & second.js') 29 | .then(function(tree) { 30 | assert.deepEqual(Object.keys(tree).sort(), ['cjs.js', 'second.js', 'third.js']); 31 | }) 32 | .then(done, done); 33 | }); 34 | 35 | test('Wildcard bundling', function(done) { 36 | builder.trace('*.js - [amd-*] - [sfx-format-*]') 37 | .then(function(tree) { 38 | assert.deepEqual(Object.keys(tree).sort(), [ 39 | 'Buffer.js', 'amd.js', 'babel', 'babel.js', 'cjs space.js', 'cjs-1.js', 'cjs-2.js', 'cjs-3.js', 'cjs-4.js', 'cjs-5.js', 'cjs-globals.js', 'cjs-in-12.js', 'cjs-in-13.js', 40 | 'cjs-resolve.js', 'cjs.js', 'class-def.js', 'component.jsx!jsx.js', 'example.js', 'file.json', 'first.js', 41 | 'global-inner.js', 'global-outer.js', 'global.js', 'jquery-cdn', 'jquery.js', 'json-plugin.js', 'jsx.js', 'plugin.js', 'register.js', 'runtime.js', 42 | 'second.js', 'some.js!plugin.js', 'text-plugin.js', 'text.txt!text-plugin.js', 'third.js', 'umd.js']); 43 | }) 44 | .then(done, done); 45 | }); 46 | 47 | test('Wildcard plugin', function(done) { 48 | builder.trace('*.jsx!jsx.js - [component.jsx!jsx.js]') 49 | .then(function(tree) { 50 | assert.deepEqual(Object.keys(tree).sort(), []); 51 | }) 52 | .then(done, done); 53 | }); 54 | 55 | test('cjs bundles added', function(done){ 56 | builder.trace('cjs-1.js + cjs-2.js + cjs-3.js') 57 | .then(function(tree) { 58 | assert.deepEqual(Object.keys(tree).sort(), [ 59 | 'cjs-1.js', 'cjs-2.js', 'cjs-3.js', 'cjs-in-12.js', 'cjs-in-13.js']); 60 | }) 61 | .then(done, done); 62 | }); 63 | 64 | test('cjs bundles added with parens and extra spaces', function(done){ 65 | builder.trace(' ( cjs-1.js & cjs-2.js ) + ( cjs-1.js & cjs-3.js)') 66 | .then(function(tree) { 67 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-12.js', 'cjs-in-13.js']); 68 | }) 69 | .then(done, done); 70 | }); 71 | 72 | test('cjs bundles added with single-value parameters', function(done){ 73 | builder.trace('(cjs-1.js) + (cjs-2.js) + (cjs-3.js)') 74 | .then(function(tree) { 75 | assert.deepEqual(Object.keys(tree).sort(), [ 76 | 'cjs-1.js', 'cjs-2.js', 'cjs-3.js', 'cjs-in-12.js', 'cjs-in-13.js']); 77 | }) 78 | .then(done, done); 79 | }); 80 | 81 | test('cjs bundles added with parens', function(done){ 82 | builder.trace('(cjs-1.js & cjs-2.js) + (cjs-1.js & cjs-3.js)') 83 | .then(function(tree) { 84 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-12.js', 'cjs-in-13.js']); 85 | }) 86 | .then(done, done); 87 | }); 88 | 89 | test('cjs bundles added with parens 2', function(done){ 90 | builder.trace('(cjs-1.js & cjs-2.js)') 91 | .then(function(tree) { 92 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-12.js']); 93 | }) 94 | .then(done, done); 95 | }); 96 | 97 | test('cjs bundles added with parens 3', function(done){ 98 | builder.trace('(cjs-1.js & cjs-2.js) + cjs-in-13.js - cjs-in-13.js') 99 | .then(function(tree) { 100 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-12.js']); 101 | }) 102 | .then(done, done); 103 | }); 104 | 105 | test('cjs bundles added with parens 4', function(done){ 106 | builder.trace('(cjs-1.js & cjs-2.js) + cjs-in-13.js - cjs-in-13.js') 107 | .then(function(tree) { 108 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-12.js']); 109 | }) 110 | .then(done, done); 111 | }); 112 | 113 | test('cjs bundles added with parens 5', function(done){ 114 | builder.trace('cjs-in-13.js + (cjs-1.js & cjs-2.js) - cjs-in-13.js') 115 | .then(function(tree) { 116 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-12.js']); 117 | }) 118 | .then(done, done); 119 | }); 120 | 121 | test('cjs bundles added with multiple parens', function(done){ 122 | builder.trace('cjs-in-13.js + (cjs-1.js & cjs-2.js) - (cjs-in-13.js)') 123 | .then(function(tree) { 124 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-12.js']); 125 | }) 126 | .then(done, done); 127 | }); 128 | 129 | test('cjs bundles added with multiple parens 2', function(done){ 130 | builder.trace('(cjs-1.js & cjs-2.js) + (cjs-1.js & cjs-3.js) - cjs-in-12.js') 131 | .then(function(tree) { 132 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-13.js']); 133 | }) 134 | .then(done, done); 135 | }); 136 | 137 | test('cjs bundles with nested parens', function(done){ 138 | builder.trace('(cjs-1.js + cjs-2.js - ([cjs-1.js] + [cjs-2.js])) - (cjs-in-12.js)') 139 | .then(function(tree) { 140 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-13.js']); 141 | }) 142 | .then(done, done); 143 | }); 144 | 145 | test('cjs bundles with nested parens 2', function(done){ 146 | builder.trace('(cjs-1.js + cjs-2.js - ([cjs-1.js] + [cjs-2.js])) - (cjs-in-12.js) + (cjs-4.js + cjs-5.js)') 147 | .then(function(tree) { 148 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-4.js', 'cjs-5.js', 'cjs-in-13.js']); 149 | }) 150 | .then(done, done); 151 | }); 152 | 153 | test('cjs bundles with nested parens 3', function(done){ 154 | builder.trace('((cjs-1.js + cjs-2.js - ([cjs-1.js] + [cjs-2.js])) - (cjs-in-12.js) + (cjs-4.js + cjs-5.js)) - ([cjs-4.js] + [cjs-5.js])') 155 | .then(function(tree) { 156 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-13.js']); 157 | }) 158 | .then(done, done); 159 | }); 160 | 161 | test('cjs bundles with nested parens 4', function(done){ 162 | builder.trace('((cjs-1.js + cjs-2.js - ([cjs-1.js] + [cjs-2.js] + ([cjs-4.js] + [cjs-5.js]))) - (cjs-4.js + cjs-5.js))') 163 | .then(function(tree) { 164 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-12.js', 'cjs-in-13.js']); 165 | }) 166 | .then(done, done); 167 | }); 168 | 169 | 170 | test('ibid with single module subtracted', function(done){ 171 | builder.trace('(cjs-1.js + cjs-2.js - ([cjs-1.js] + [cjs-2.js])) - ([cjs-in-12.js])') 172 | .then(function(tree) { 173 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-13.js']); 174 | }) 175 | .then(done, done); 176 | }); 177 | 178 | test('cjs bundles added with nested parens', function(done){ 179 | builder.trace('(cjs-1.js + cjs-2.js - (cjs-1.js & cjs-2.js))') 180 | .then(function(tree) { 181 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-1.js', 'cjs-2.js', 'cjs-in-13.js']); 182 | }) 183 | .then(done, done); 184 | }); 185 | 186 | test('cjs bundles with parens and single modules', function(done){ 187 | builder.trace('(cjs-1.js + cjs-2.js) - ([cjs-1.js] + [cjs-2.js])') 188 | .then(function(tree) { 189 | assert.deepEqual(Object.keys(tree).sort(), ['cjs-in-12.js', 'cjs-in-13.js']); 190 | }) 191 | .then(done, done); 192 | }); 193 | 194 | test('file with space', function(done){ 195 | builder.trace('cjs-1.js + cjs space.js') 196 | .then(function(tree) { 197 | assert.deepEqual(Object.keys(tree).sort(), [ 198 | 'cjs space.js', 'cjs-1.js', 'cjs-in-12.js', 'cjs-in-13.js']); 199 | }) 200 | .then(done, done); 201 | }); 202 | 203 | test('file with space 2', function(done){ 204 | builder.trace('cjs-1.js + cjs space.js ') 205 | .then(function(tree) { 206 | assert.deepEqual(Object.keys(tree).sort(), [ 207 | 'cjs space.js', 'cjs-1.js', 'cjs-in-12.js', 'cjs-in-13.js']); 208 | }) 209 | .then(done, done); 210 | }); 211 | 212 | test('file with space 3', function(done){ 213 | builder.trace('cjs-1.js + cjs space.js + cjs-2.js') 214 | .then(function(tree) { 215 | assert.deepEqual(Object.keys(tree).sort(), [ 216 | 'cjs space.js', 'cjs-1.js', 'cjs-2.js', 'cjs-in-12.js', 'cjs-in-13.js']); 217 | }) 218 | .then(done, done); 219 | }); 220 | 221 | test('file with space 4', function(done){ 222 | builder.trace('cjs-1.js + cjs space.js + cjs-2.js ') 223 | .then(function(tree) { 224 | assert.deepEqual(Object.keys(tree).sort(), [ 225 | 'cjs space.js', 'cjs-1.js', 'cjs-2.js', 'cjs-in-12.js', 'cjs-in-13.js']); 226 | }) 227 | .then(done, done); 228 | }); 229 | 230 | }); 231 | 232 | suite('Bundle Expression Validation', function() { 233 | test('missing identifier 1', function(){ 234 | return validateInvalidExpression('cjs-1.js + +'); 235 | }); 236 | 237 | test('missing identifier 2', function(){ 238 | return validateInvalidExpression('cjs-1.js + -'); 239 | }); 240 | 241 | test('missing identifier 3', function(){ 242 | return validateInvalidExpression('cjs-1.js + &'); 243 | }); 244 | 245 | test('unclosed parens 1', function(){ 246 | return validateInvalidExpression('(cjs-1.js + cjs-2.js'); 247 | }); 248 | 249 | test('unclosed parens 2', function(){ 250 | return validateInvalidExpression('(cjs-1.js + (cjs-2.js + cjs-3.js'); 251 | }); 252 | 253 | test('unclosed parens 3', function(){ 254 | return validateInvalidExpression('(cjs-1.js + (cjs-2.js + cjs-3.js)'); 255 | }); 256 | 257 | test('unclosed parens 4', function(){ 258 | return validateInvalidExpression('(cjs-2.js + cjs-3.js) + (cjs-1.js + (cjs-2.js + cjs-3.js)'); 259 | }); 260 | 261 | function validateInvalidExpression(expression){ 262 | return Promise 263 | .resolve() 264 | .then(function(){ return builder.trace(expression) }) 265 | .then( 266 | function(){ return Promise.reject('Invalid expression <' + expression + '> was parsed without error'); }, //it worked but shouldn't have 267 | function(err){ 268 | //uncomment this line to view the Syntax Errors' wordings in the test console 269 | //console.log(err); 270 | if (typeof err !== 'string' || !/^Syntax Error/i.test(err)){ 271 | return Promise.reject('Syntax error was expected, but not generated') 272 | } else { 273 | return Promise.resolve(1); 274 | } 275 | } 276 | ) 277 | } 278 | 279 | }); 280 | 281 | 282 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SystemJS Build Tool [![Build Status][travis-image]][travis-url] [![Support](https://supporterhq.com/api/b/33df4abbec4d39260f49015d2457eafe/SystemJS)](https://supporterhq.com/support/33df4abbec4d39260f49015d2457eafe/SystemJS) 2 | === 3 | 4 | **This project has been deprecated as of SystemJS 2.0. It will continue to support SystemJS 0.21 legacy builds though. Instead, [Rollup code splitting builds](https://rollupjs.org/guide/en#experimental-code-splitting) are encouraged.** 5 | 6 | _[SystemJS Builder 0.16 release notes](https://github.com/systemjs/builder/releases/tag/0.16.0)_ 7 | 8 | _Note for SystemJS 0.19 support use SystemJS Builder 0.15_ 9 | 10 | Provides a single-file build for SystemJS of mixed-dependency module trees. 11 | 12 | Builds ES6 into ES5, CommonJS, AMD and globals into a single file in a way that supports the CSP SystemJS loader 13 | as well as circular references. 14 | 15 | Example 16 | --- 17 | 18 | app.js 19 | ```javascript 20 | import $ from "./jquery.js"; 21 | export var hello = 'es6'; 22 | ``` 23 | 24 | jquery.js 25 | ```javascript 26 | define(function() { 27 | return 'this is jquery'; 28 | }); 29 | ``` 30 | 31 | Will build the module `app` into a bundle containing both `app` and `jquery` defined through `System.register` calls. 32 | 33 | Circular references and bindings in ES6, CommonJS and AMD all behave exactly as they should, including maintaining execution order. 34 | 35 | Documentation 36 | --- 37 | [API Reference](docs/api.md) 38 | 39 | Usage 40 | --- 41 | 42 | ### Install 43 | 44 | ```javascript 45 | npm install systemjs-builder 46 | ``` 47 | 48 | ### Basic Use 49 | 50 | Ensure that the transpiler is installed separately (`npm install babel-core` here). 51 | 52 | ```javascript 53 | var path = require("path"); 54 | var Builder = require('systemjs-builder'); 55 | 56 | // optional constructor options 57 | // sets the baseURL and loads the configuration file 58 | var builder = new Builder('path/to/baseURL', 'path/to/system/config-file.js'); 59 | 60 | builder 61 | .bundle('local/module.js', 'outfile.js') 62 | .then(function() { 63 | console.log('Build complete'); 64 | }) 65 | .catch(function(err) { 66 | console.log('Build error'); 67 | console.log(err); 68 | }); 69 | ``` 70 | 71 | ### Setting Configuration 72 | 73 | Configuration can be injected via `builder.config`: 74 | 75 | ```javascript 76 | builder.config({ 77 | map: { 78 | 'a': 'b.js' 79 | } 80 | }); 81 | builder.build('a'); 82 | ``` 83 | 84 | To load custom configuration files use `builder.loadConfig`: 85 | 86 | ```javascript 87 | // `builder.loadConfig` will load config from a file containing `System.config({...})` 88 | builder.loadConfig('./cfg.js') 89 | .then(function() { 90 | // ready to build 91 | }); 92 | ``` 93 | 94 | Multiple config calls can be run, which will combine into the loader configuration. 95 | 96 | #### Resetting Configuration 97 | 98 | To reset the loader state and configuration use `builder.reset()`. 99 | 100 | When config was passed into the `new Builder(baseURL, configFile)` constructor, the config will be reset to this exact `configFile` state. 101 | 102 | ### Self-Executing (SFX) Bundles 103 | 104 | To make a bundle that is independent of the SystemJS loader entirely, we can make SFX bundles: 105 | 106 | ```javascript 107 | builder.buildStatic('myModule.js', 'outfile.js', options); 108 | ``` 109 | 110 | This bundle file can then be included with a ` 138 | 139 | ``` 140 | 141 | Note that another way of excluding `react` would be with `externals`. 142 | 143 | ```javascript 144 | builder.buildStatic('src/NavBar.js', 'dist/NavBarStaticBuild.js', { 145 | externals: ['react'], 146 | globalName: 'NavBar', 147 | globalDeps: { 148 | 'react': 'React' 149 | } 150 | }); 151 | ``` 152 | 153 | This would also exclude react but, if react defined any dependencies which NavBar *also* defined, those dependencies would be *included* in the build. 154 | 155 | Of course the above explanations involving `globalDeps` and `globalName` only apply to when your end user loads the static file from a script tag. Since the output is (by default, see below) UMD, a 156 | script loader like SystemJS or requireJS would process it as configured, or via AMD respectively. 157 | 158 | By default, the Traceur or Babel runtime are automatically included in the SFX bundle if needed. To exclude the Babel or Traceur runtime set the `runtime` build option to false: 159 | 160 | ```javascript 161 | builder.buildStatic('myModule.js', 'outfile.js', { runtime: false }); 162 | ``` 163 | 164 | #### SFX Format 165 | 166 | SFX bundles can also be output as a custom module format - `amd`, `cjs` or `es6` for consumption in different environments. 167 | 168 | This is handled via the `format` (previously `sfxFormat`) option: 169 | 170 | ```javascript 171 | builder.buildStatic('myModule.js', 'outfile.js', { format: 'cjs' }); 172 | ``` 173 | 174 | The first module used as input (`myModule.js` here) will then have its exports output as the CommonJS exports of the whole SFX bundle itself 175 | when run in a CommonJS environment. 176 | 177 | #### Adapter Modules 178 | 179 | To have globals like `jQuery` not included, and included in a separate script tag, set up an adapter module something like: 180 | 181 | jquery.js 182 | ```javascript 183 | module.exports = window.jQuery; 184 | ``` 185 | 186 | ### Minification & Source Maps 187 | 188 | As well as an `options.config` parameter, it is also possible to specify minification and source maps options: 189 | 190 | ```javascript 191 | builder.bundle('myModule.js', 'outfile.js', { minify: true, sourceMaps: true, config: cfg }); 192 | ``` 193 | 194 | Compile time with source maps can also be improved with the `lowResSourceMaps` option, where the mapping granularity is per-line instead of per-character: 195 | 196 | ```javascript 197 | builder.bundle('myModule.js', 'outfile.js', { sourceMaps: true, lowResSourceMaps: true }); 198 | ``` 199 | 200 | #### Minification Options 201 | 202 | * `mangle`, defaults to true. 203 | * `globalDefs`, object allowing for global definition assignments for dead code removal. 204 | 205 | ```javascript 206 | builder.bundle('myModule.js', 'outfile.js', { minify: true, mangle: false, globalDefs: { DEBUG: false } }); 207 | ``` 208 | 209 | #### SourceMap Options 210 | 211 | * `sourceMaps`, Either boolean value (enable/disable) or string value `'inline'` which will inline the SourceMap data as Base64 data URI right in the generated output file (never use in production). *(Default is `false`)* 212 | * `sourceMapContents`, Boolean value that determines if original sources shall be directly included in the SourceMap. Using inline source contents generates truely self contained SourceMaps which will not need to load the external original source files during debugging. *(Default is `false`; when using `sourceMaps='inline'` it defaults `true`)* 213 | 214 | 215 | ### In-Memory Builds 216 | 217 | Leave out the `outFile` option to run an in-memory build: 218 | 219 | ```javascript 220 | builder.bundle('myModule.js', { minify: true }).then(function(output) { 221 | output.source; // generated bundle source 222 | output.sourceMap; // generated bundle source map 223 | output.modules; // array of module names defined in the bundle 224 | }); 225 | ``` 226 | 227 | The `output` object above is provided for all builds, including when `outFile` is set. 228 | 229 | `output.modules` can be used to directly populate SystemJS bundles configuration. 230 | 231 | ### Ignore Resources 232 | 233 | If loading resources that shouldn't even be traced as part of the build (say an external import), these 234 | can be configured with: 235 | 236 | ```javascript 237 | builder.config({ 238 | meta: { 239 | 'resource/to/ignore.js': { 240 | build: false 241 | } 242 | } 243 | }); 244 | ``` 245 | 246 | ### Overriding Fetch 247 | 248 | The framework fetch function can be overridden in order to provide the source for a file manually. This is useful if you want to pre-process the source of a file before using the builder. 249 | 250 | ```javascript 251 | var mySource = 'import * from foo; var foo = "bar";'; // get source as a string 252 | builder.bundle('foo.js', { 253 | fetch: function (load, fetch) { 254 | if (load.name.indexOf('foo.js') !== -1) { 255 | return mySource; 256 | } else { 257 | // fall back to the normal fetch method 258 | return fetch(load); 259 | } 260 | } 261 | }); 262 | ``` 263 | 264 | The `load` variable describes the file that is trying to be loaded. This is called once for every file that is trying to be fetched, including dependencies. 265 | 266 | The `fetch` function should return a string. 267 | 268 | ### Bundle Arithmetic 269 | 270 | Both `builder.build` and `builder.buildStatic` support bundle arithmetic expressions. This allows for the easy construction of custom bundles. 271 | 272 | There is also a `builder.trace` for building direct trace tree objects, which can be directly passed into `builder.bundle` or `builder.buildStatic`. 273 | 274 | #### Example - Arithmetic Expressions 275 | 276 | In this example we build all our application code in `app/` excluding the tree `app/corelibs`: 277 | 278 | ```javascript 279 | var Builder = require('systemjs-builder'); 280 | 281 | var builder = new Builder({ 282 | baseURL: '...', 283 | map: { 284 | } // etc. config 285 | }); 286 | 287 | builder.bundle('app/* - app/corelibs.js', 'output-file.js', { minify: true, sourceMaps: true }); 288 | ``` 289 | 290 | #### Example - Common Bundles 291 | 292 | To build the dependencies in common between two modules, use the `&` operator: 293 | 294 | ```javascript 295 | builder.bundle('app/page1.js & app/page2.js', 'common.js'); 296 | ``` 297 | 298 | We can then exclude this common bundle in future builds: 299 | 300 | ```javascript 301 | builder.bundle('app/componentA.js - common.js', { minify: true, sourceMaps: true }); 302 | ``` 303 | 304 | #### Example - Third-Party Dependency Bundles 305 | 306 | Build a bundle of all dependencies of the `app/` package excluding anything from `app/` itself. 307 | 308 | For this we can use the `[module]` syntax which represents a single module instead of all its dependencies as well: 309 | 310 | ```javascript 311 | builder.bundle('app/**/* - [app/**/*]', 'dependencies.js', { minify: true, sourceMaps: true }); 312 | ``` 313 | 314 | The above means _take the tree of app and all its dependencies, and subtract just the modules in app_, thus leaving us with just the tree of dependencies of the app package. 315 | 316 | #### Example - Multiple Common Bundles 317 | 318 | Parentheses are supported, so the following would bundle everything in common with `page1` and `page2`, and also everything in common between `page3` and `page4`: 319 | 320 | ```javascript 321 | builder.bundle('(app/page1.js & app/page2.js) + (app/page3.js & app/page4.js)', 'common.js'); 322 | ``` 323 | 324 | #### Example - Direct Trace API 325 | 326 | Instead of using the arithmetic syntax, we can construct the trace ourselves. 327 | 328 | In this example we build `app/first` and `app/second` into two separate bundles, while creating a separate shared bundle: 329 | 330 | ```javascript 331 | var Builder = require('systemjs-builder'); 332 | 333 | var builder = new Builder({ 334 | // ... 335 | }); 336 | 337 | Promise.all([builder.trace('app/first.js'), builder.trace('app/second.js')]) 338 | .then(function(trees) { 339 | var commonTree = builder.intersectTrees(trees[0], trees[1]); 340 | return Promise.all([ 341 | builder.bundle(commonTree, 'shared-bundle.js'), 342 | builder.bundle(builder.subtractTrees(trees[0], commonTree), 'first-bundle.js'), 343 | builder.bundle(builder.subtractTrees(trees[1], commonTree), 'second-bundle.js') 344 | ]); 345 | }); 346 | ``` 347 | 348 | License 349 | --- 350 | 351 | MIT 352 | 353 | [travis-url]: https://travis-ci.org/systemjs/builder 354 | [travis-image]: https://travis-ci.org/systemjs/builder.svg?branch=master 355 | -------------------------------------------------------------------------------- /lib/rollup.js: -------------------------------------------------------------------------------- 1 | var rollup = require('rollup'); 2 | var traverseTree = require('./arithmetic').traverseTree; 3 | var getConditionModule = require('./trace').getConditionModule; 4 | var extend = require('./utils').extend; 5 | var getAlias = require('./utils').getAlias; 6 | var pluginBundleHook = require('./compile').pluginBundleHook; 7 | 8 | exports.rollupTree = function(loader, tree, entryPoints, traceOpts, compileOpts, outputOpts) { 9 | /* 10 | * 1. Determine the tree entry points and optimization points 11 | * 12 | * eg for the tree: 13 | * 14 | * A -> B -> C 15 | * D -> C 16 | * 17 | * A and D are the entry points. 18 | * Optimization points are ES module entry points to be optimized 19 | * 20 | */ 21 | 22 | entryPoints = entryPoints.concat([]); 23 | 24 | var optimizationPoints = []; 25 | 26 | var entryMap = {}; 27 | 28 | function isESM(moduleName) { 29 | return tree[moduleName] && tree[moduleName].metadata && tree[moduleName].metadata.format == 'esm' && !tree[moduleName].metadata.originalSource && !tree[moduleName].source.match(/\s+import\s*\(/); 30 | } 31 | 32 | // for each module in the tree, we traverse the whole tree 33 | // we then relate each module in the tree to the first traced entry point 34 | Object.keys(tree).forEach(function(entryPoint) { 35 | traverseTree(tree, entryPoint, function(depName, parentName) { 36 | // esm from a non-esm parent means this is an optimization entry point from the linking alogorithm perspective 37 | if (parentName && isESM(depName) && !isESM(parentName) && optimizationPoints.indexOf(depName) == -1) 38 | optimizationPoints.push(depName); 39 | 40 | // if we have a entryMap for the given module, then stop 41 | if (entryMap[depName]) 42 | return false; 43 | 44 | if (parentName) 45 | entryMap[depName] = entryPoint; 46 | }, traceOpts); 47 | }); 48 | 49 | // the entry points are then the modules not represented in entryMap 50 | Object.keys(tree).forEach(function(entryPoint) { 51 | if (!entryMap[entryPoint] && tree[entryPoint] && entryPoints.indexOf(entryPoint) == -1) 52 | entryPoints.push(entryPoint); 53 | }); 54 | 55 | // if all the entry points are ES modules, 56 | // then we can create a single dummy entry point 57 | // that represents the tree 58 | var esmEntryPoints = 0; 59 | entryPoints.forEach(function(entryPoint) { 60 | if (tree[entryPoint].metadata && tree[entryPoint].metadata.format == 'esm') 61 | esmEntryPoints ++; 62 | }); 63 | 64 | if (esmEntryPoints > 1 && esmEntryPoints == entryPoints.length) { 65 | var dummySource = 'export * from "' + entryPoints[0] + '";\n'; 66 | var dummyDepMap = {}; 67 | 68 | entryPoints.forEach(function(entryPoint) { 69 | dummyDepMap[entryPoint] = entryPoint; 70 | 71 | dummySource += 'import "' + entryPoint + '";'; 72 | }); 73 | 74 | tree['@dummy-entry-point'] = { 75 | name: '@dummy-entry-point', 76 | path: null, 77 | metadata: { format: 'esm' }, 78 | deps: entryPoints, 79 | depMap: dummyDepMap, 80 | source: dummySource 81 | }; 82 | entryPoints = ['@dummy-entry-point']; 83 | } 84 | 85 | // optimization points are then es module entry points 86 | entryPoints.forEach(function(entryPoint) { 87 | if (isESM(entryPoint) && optimizationPoints.indexOf(entryPoint) == -1) 88 | optimizationPoints.push(entryPoint); 89 | }); 90 | 91 | /* 92 | * 2. Determine unoptimizable modules, splitting them out into their own optimization points 93 | * 94 | * eg for the tree: 95 | * A -> B -> C -> D 96 | * E -> C -> D 97 | * 98 | * A, E are top-level entry points detected by the previous step 99 | * (and hence optimization points if they are es modules) 100 | * C is not optimizable because it has two unique parent entry points 101 | * (which is what this step looks for) 102 | * So C becomes its own optimization point 103 | * Leading to D inlining into C and B inlining into A 104 | * 105 | */ 106 | 107 | // for each module in the tree, we track its parent optimization point 108 | // as soon as a module has two parent entry points, it is not optimizable 109 | // and we set it to undefined here. It then becomes its own optimizationPoint. 110 | var optimizationParentMap = {}; 111 | 112 | // build up the parent entry point map as above 113 | // we use for over forEach because this list will grow as we go 114 | for (var i = 0; i < optimizationPoints.length; i++) { 115 | var entryPoint = optimizationPoints[i]; 116 | traverseTree(tree, entryPoint, function(depName, parentName) { 117 | 118 | // we only traverse ES module tree subgraphs 119 | if (!isESM(depName)) 120 | return false; 121 | 122 | if (depName == entryPoint) 123 | return; 124 | 125 | // dont traverse through other entry points 126 | if (optimizationPoints.indexOf(depName) != -1) 127 | return false; 128 | 129 | if (!optimizationParentMap[depName]) { 130 | optimizationParentMap[depName] = entryPoint; 131 | return; 132 | } 133 | 134 | // module in two separate entry point graphs -> it becomes its own optimization entry point 135 | if (optimizationParentMap[depName] != entryPoint) { 136 | optimizationParentMap[depName] = undefined; 137 | 138 | // this new optimization point will then be traversed in turn as part of this loop later 139 | optimizationPoints.push(depName); 140 | } 141 | }, traceOpts); 142 | } 143 | 144 | /* 145 | * 3. Given complete optimization points, populate subgraph externals 146 | * 147 | * eg for the graph 148 | * A -> B -> C 149 | * 150 | * Where A is the optimization point, and C is not ESM, another optimization point, 151 | * or not contained in our build tree, then we mark 'C' as an external. 152 | * 153 | * That is, optimizationGraphExternals[A] = [C] 154 | * 155 | * This externals input is used in the Rollup API. 156 | * This way we just optimize B into A, retaining an explicit dependency on C. 157 | */ 158 | 159 | var inlinedModules = []; 160 | var optimizationGraphExternals = {}; 161 | 162 | optimizationPoints.forEach(function(entryPoint) { 163 | // the subgraph object is the list of modules in the subgraph 164 | // and the list of modules that are "external" boundaries of the subgraph 165 | var externals = []; 166 | 167 | // this traversal is a bit odd, since we need to traverse the full 168 | // dependency graph to detect externals, not just the direct build graph 169 | traverseTree(tree, entryPoint, function(depName, parentName) { 170 | if (!isESM(depName) || (depName != entryPoint && optimizationPoints.indexOf(depName) != -1)) 171 | return false; 172 | 173 | var depLoad = tree[depName]; 174 | depLoad.deps && depLoad.deps.forEach(function(depName) { 175 | depName = depLoad.depMap[depName]; 176 | if (depName == entryPoint) 177 | return; 178 | 179 | // anything not ESM, not in the tree, or an optimization point, is external 180 | if (!isESM(depName) || optimizationPoints.indexOf(depName) != -1) { 181 | if (externals.indexOf(depName) == -1) 182 | externals.push(depName); 183 | } 184 | else { 185 | if (inlinedModules.indexOf(depName) == -1) 186 | inlinedModules.push(depName); 187 | } 188 | }, traceOpts); 189 | }); 190 | 191 | optimizationGraphExternals[entryPoint] = externals; 192 | }); 193 | 194 | // finally we rollup each optimization graph 195 | var rolledUpTree = {}; 196 | Object.keys(tree).forEach(function(moduleName) { 197 | if (inlinedModules.indexOf(moduleName) == -1) 198 | rolledUpTree[moduleName] = tree[moduleName]; 199 | }); 200 | 201 | // compute the inlineMap 202 | var inlineMap = {}; 203 | inlinedModules.forEach(function(moduleName) { 204 | var optimizationParent = optimizationParentMap[moduleName]; 205 | (inlineMap[optimizationParent] = inlineMap[optimizationParent] || []).push(moduleName); 206 | }); 207 | 208 | // if every module in the tree is rolled-up, then we can do a full tree rollup 209 | var fullTreeRollup = entryPoints.length == 1 && optimizationPoints.length == 1 && Object.keys(optimizationGraphExternals).length == 1; 210 | 211 | return Promise.all(Object.keys(optimizationGraphExternals).map(function(entryPoint) { 212 | var externals = optimizationGraphExternals[entryPoint]; 213 | var loadList = []; 214 | var entryPointLoad; 215 | 216 | // if all externals are outside the tree then this really is a full tree rollup 217 | // also @node/x requires mean we do need sfx core (pending a better output of these) 218 | if (fullTreeRollup) 219 | externals.forEach(function(external) { 220 | if (external.substr(0, 5) == '@node' || tree[external]) 221 | fullTreeRollup = false; 222 | }); 223 | 224 | var aliasedExternals = externals.map(function(external) { 225 | var alias = getAlias(loader, external) || externals; 226 | if (alias.indexOf('#:') != -1) 227 | alias = alias.replace('#:', '/'); 228 | return alias; 229 | }); 230 | 231 | return rollup.rollup({ 232 | entry: entryPoint, 233 | external: aliasedExternals, 234 | acorn: { 235 | allowReserved: true, 236 | ecmaVersion: 8 237 | }, 238 | plugins: [{ 239 | resolveId: function(id, importer, options) { 240 | var resolved = importer ? tree[importer].depMap[id] : id; 241 | var externalIndex = externals.indexOf(resolved); 242 | if (externalIndex != -1) 243 | return aliasedExternals[externalIndex]; 244 | return resolved; 245 | }, 246 | load: function(id, options) { 247 | if (loadList.indexOf(tree[id]) == -1) 248 | loadList.push(tree[id]); 249 | loadList.push(tree[id]); 250 | return { 251 | code: tree[id].metadata.originalSource || tree[id].source, 252 | map: tree[id].metadata.sourceMap 253 | }; 254 | } 255 | }], 256 | onwarn: function(message) {} 257 | }) 258 | .then(function(bundle) { 259 | entryPointLoad = tree[entryPoint]; 260 | 261 | var defaultExport = compileOpts.defaultExport; 262 | if (entryPointLoad.metadata.format == 'register') 263 | throw new Error('Assertion failed: internal format should be "system" not "register".'); 264 | 265 | if (entryPointLoad.metadata.format != 'esm' && entryPointLoad.metadata.format != 'system') 266 | defaultExport = true; 267 | 268 | var generateOptions = { 269 | format: 'es', 270 | sourceMap: !!compileOpts.sourceMaps, 271 | exports: defaultExport ? 'default' : 'named', 272 | dest: 'output.js' // workaround for rollup/rollup#1015 273 | }; 274 | 275 | // for a full tree rollup, we pass all the output options into rollup itself 276 | if (fullTreeRollup) { 277 | generateOptions.format = compileOpts.format; 278 | if (generateOptions.format == 'global') 279 | generateOptions.format = 'iife'; 280 | if (generateOptions.format == 'esm') 281 | generateOptions.format = 'es'; 282 | 283 | if ((generateOptions.format == 'iife' || generateOptions.format == 'umd') && 284 | !compileOpts.globalName) 285 | throw new Error('The globalName option must be set for full-tree rollup global and UMD builds.'); 286 | 287 | if (compileOpts.globalName) 288 | generateOptions.name = compileOpts.globalName; 289 | 290 | if (compileOpts.globalDeps) 291 | generateOptions.globals = compileOpts.globalDeps; 292 | } 293 | 294 | return bundle.generate(generateOptions); 295 | }) 296 | .then(function(output) { 297 | 298 | // convert sources list into paths 299 | if (output.map) { 300 | output.map.sources = output.map.sources.map(function(name) { 301 | name = loader.getCanonicalName(loader.decanonicalize(name)); 302 | return tree[name] && tree[name].path || loader.decanonicalize(name); 303 | }); 304 | } 305 | 306 | if (fullTreeRollup) 307 | return { 308 | source: output.code, 309 | sourceMap: output.map 310 | }; 311 | 312 | // replace the entry point module itself with the inlined subgraph module 313 | var curInlined = inlineMap[entryPoint] || []; 314 | 315 | // the process of running rollup will itself normalize all dependencies 316 | // the depMap then just becomes the identity map for non-externals 317 | var inlinedDepMap = {}; 318 | aliasedExternals.forEach(function(dep, index) { 319 | inlinedDepMap[dep] = externals[index]; 320 | }); 321 | 322 | rolledUpTree[entryPoint] = extend(extend({}, entryPointLoad), { 323 | deps: aliasedExternals, 324 | depMap: inlinedDepMap, 325 | metadata: extend(extend({}, entryPointLoad.metadata), { 326 | originalSource: undefined, 327 | sourceMap: output.map 328 | }), 329 | source: output.code, 330 | compactedLoads: loadList 331 | }); 332 | }); 333 | })) 334 | .then(function(outputs) { 335 | if (fullTreeRollup) { 336 | // for the full tree rollup case, we need to run the plugin bundle hook as we skip compile entirely 337 | return pluginBundleHook(loader, Object.keys(tree).map(function(name) { 338 | return tree[name]; 339 | }).filter(function(load) { 340 | return load; 341 | }), compileOpts, outputOpts) 342 | .then(function(pluginResult) { 343 | return { 344 | outputs: outputs.concat(pluginResult.outputs), 345 | assetList: pluginResult.assetList 346 | }; 347 | }); 348 | } 349 | 350 | return { 351 | tree: rolledUpTree, 352 | inlineMap: inlineMap 353 | }; 354 | }); 355 | }; 356 | -------------------------------------------------------------------------------- /lib/arithmetic.js: -------------------------------------------------------------------------------- 1 | var asp = require('bluebird').promisify; 2 | var Promise = require('bluebird'); 3 | var glob = require('glob'); 4 | var path = require('path'); 5 | var url = require('url'); 6 | 7 | var getLoadDependencies = require('./trace').getLoadDependencies; 8 | 9 | var fromFileURL = require('./utils').fromFileURL; 10 | var toFileURL = require('./utils').toFileURL; 11 | 12 | var verifyTree = require('./utils').verifyTree; 13 | 14 | function parseExpression(expressionString) { 15 | expressionString = ' + ' + expressionString; 16 | 17 | var index = 0; 18 | var operations = []; 19 | var operatorRegex = /[\+\-\&]/; 20 | var errorMessagesFromIndex = 3; 21 | 22 | function getNextIdentifier() { 23 | eatWhitespace(); 24 | var firstChar = expressionString.charAt(index); 25 | 26 | if (operatorRegex.test(firstChar)){ 27 | throw 'Syntax Error: Identifier or sub expression expected after <' + expressionString.slice(errorMessagesFromIndex).substr(0, index - errorMessagesFromIndex) + '> but found <' + firstChar + '> instead'; 28 | } 29 | 30 | if (firstChar === '(') { 31 | var closingParenIndex = index, 32 | numOpenBeforeSelf = 0; 33 | 34 | while (++closingParenIndex < expressionString.length){ 35 | if (expressionString.charAt(closingParenIndex) === '('){ 36 | numOpenBeforeSelf++; 37 | } else if (expressionString.charAt(closingParenIndex) === ')') { 38 | if (numOpenBeforeSelf){ 39 | numOpenBeforeSelf--; 40 | } else { 41 | break; 42 | } 43 | } 44 | } 45 | if (expressionString.charAt(closingParenIndex) !== ')'){ 46 | throw 'Syntax Error: Expression <' + expressionString.substr(index) + '> is never terminated. Did you forget to add a closing ")"?'; 47 | } 48 | 49 | var wholeExpression = expressionString.substring(index + 1, closingParenIndex); 50 | index = closingParenIndex + 1; 51 | return { bulkOperation: wholeExpression }; 52 | } 53 | 54 | var result = ""; 55 | //scan the identifier 56 | for (; index < expressionString.length; index++) { 57 | var currentChar = expressionString.charAt(index); 58 | //can have spaces in file names - so we need whitespace, operator, whitespace. 59 | if (/^\s+[\+\-\&]\s+/.test(expressionString.substr(index))) { 60 | return result; 61 | } else { 62 | result += currentChar; 63 | } 64 | } 65 | return result.replace(/\s+$/, ''); //it appears as though trailing whitespace is trimmed downstream, but I'm snipping here to be safe 66 | } 67 | 68 | function getNextOperator() { 69 | eatWhitespace(); 70 | if (index === expressionString.length) return null; 71 | 72 | var candidateResult = expressionString.charAt(index++); //all operators are single characters at the moment 73 | 74 | if (!operatorRegex.test(candidateResult)){ 75 | throw 'Syntax Error: An operator was expected after <' + expressionString.slice(errorMessagesFromIndex).substr(0, index - 1 - errorMessagesFromIndex) + '> but found <' + expressionString.substring(index - 1) + '> instead'; 76 | } 77 | 78 | return candidateResult; 79 | } 80 | 81 | function eatWhitespace() { 82 | //wind past whitespace 83 | for (; index < expressionString.length; index++) { 84 | if (/\S/.test(expressionString.charAt(index))) { 85 | break; 86 | } 87 | } 88 | } 89 | 90 | var operator; 91 | while (index < expressionString.length && (operator = getNextOperator())) { 92 | var moduleNameOrSubExpression = getNextIdentifier(); 93 | 94 | if (typeof moduleNameOrSubExpression === 'object'){ 95 | operations.push({ 96 | operator: operator, 97 | bulkOperation: moduleNameOrSubExpression.bulkOperation 98 | }); 99 | } else { 100 | // detect [moduleName] syntax for individual modules not trees 101 | var singleModule = moduleNameOrSubExpression.substr(0, 1) == '[' && moduleNameOrSubExpression.substr(moduleNameOrSubExpression.length - 1, 1) == ']'; 102 | if (singleModule) { 103 | moduleNameOrSubExpression = moduleNameOrSubExpression.substr(1, moduleNameOrSubExpression.length - 2); 104 | } 105 | 106 | var canonicalized = moduleNameOrSubExpression.substr(0, 1) == '`' && moduleNameOrSubExpression.substr(moduleNameOrSubExpression.length - 1, 1) == '`'; 107 | if (canonicalized) { 108 | moduleNameOrSubExpression = moduleNameOrSubExpression.substr(1, moduleNameOrSubExpression.length - 2); 109 | } 110 | 111 | operations.push({ 112 | operator: operator, 113 | moduleName: moduleNameOrSubExpression, 114 | singleModule: singleModule, 115 | canonicalized: canonicalized 116 | }); 117 | } 118 | } 119 | 120 | return operations; 121 | } 122 | 123 | function getTreeOperation(symbol) { 124 | if (symbol == '+') 125 | return addTrees; 126 | else if (symbol == '-') 127 | return subtractTrees; 128 | else if (symbol == '&') 129 | return intersectTrees; 130 | else 131 | throw new TypeError('Unknown operator ' + symbol); 132 | } 133 | 134 | function getTreeModuleOperation(builder, symbol, traceOpts) { 135 | if (symbol == '+') 136 | return function(tree, canonical) { 137 | var addedTree = {}; 138 | for (var p in tree) 139 | addedTree[p] = tree[p]; 140 | 141 | return builder.tracer.getLoadRecord(canonical, traceOpts).then(function(load) { 142 | addedTree[canonical] = load; 143 | return addedTree; 144 | }); 145 | }; 146 | else if (symbol == '-') 147 | return function(tree, canonical) { 148 | var subtractedTree = {}; 149 | for (var p in tree) { 150 | if (p != canonical) 151 | subtractedTree[p] = tree[p]; 152 | } 153 | return subtractedTree; 154 | }; 155 | else if (symbol == '&') 156 | throw new TypeError('Single modules cannot be intersected.'); 157 | else 158 | throw new TypeError('Unknown operator ' + symbol); 159 | } 160 | 161 | function expandGlobAndCanonicalize(builder, operation) { 162 | var loader = builder.loader; 163 | 164 | // no glob -> just canonicalize 165 | if (operation.moduleName.indexOf('*') == -1) { 166 | if (operation.canonicalized) 167 | return [operation]; 168 | 169 | return loader.normalize(operation.moduleName) 170 | .then(function(normalized) { 171 | operation.moduleName = builder.getCanonicalName(normalized); 172 | return [operation]; 173 | }); 174 | } 175 | 176 | // globbing 177 | var metadata = {}; 178 | var globSuffix = operation.moduleName[operation.moduleName.length - 1] == '*'; 179 | var pluginSyntax = operation.moduleName.indexOf('!') != -1; 180 | 181 | return Promise.resolve() 182 | .then(function() { 183 | if (operation.canonicalized) { 184 | return loader.decanonicalize(operation.moduleName); 185 | } 186 | else { 187 | // normalizeSync avoids package config loading which we don't want for wildcards 188 | return loader.normalizeSync(operation.moduleName); 189 | } 190 | }) 191 | .then(function(normalized) { 192 | // remove ALL extension adding when globbing 193 | if (globSuffix && !operation.canonicalized) { 194 | var extIndex = normalized.lastIndexOf('.'); 195 | if (extIndex != -1 && normalized[extIndex - 1] == '*') 196 | normalized = normalized.substr(0, extIndex); 197 | } 198 | 199 | return loader.locate({ name: normalized, metadata: metadata }); 200 | }) 201 | .then(function(address) { 202 | // now we have a file path to glob -> glob the pattern 203 | return asp(glob)(fromFileURL(address), { 204 | nobrace: true, 205 | noext: true, 206 | nodir: true 207 | }); 208 | }) 209 | .then(function(addresses) { 210 | return (metadata.loader && pluginSyntax ? loader.normalize(metadata.loader) : Promise.resolve()) 211 | .then(function(loaderSyntaxName) { 212 | return addresses.map(function(file) { 213 | return { 214 | operator: operation.operator, 215 | moduleName: builder.getCanonicalName(toFileURL(file) + (loaderSyntaxName ? '!' + loader.getCanonicalName(loaderSyntaxName) : '')), 216 | singleModule: operation.singleModule 217 | }; 218 | }); 219 | }); 220 | }); 221 | } 222 | 223 | exports.traceExpression = function(builder, expression, traceOpts) { 224 | if (!expression) 225 | throw new Error('A module expression must be provided to trace.'); 226 | 227 | if (expression instanceof Array) { 228 | var tree = {}; 229 | return Promise.all(expression.map(function(moduleName) { 230 | return builder.loader.normalize(moduleName) 231 | .then(function(normalized) { 232 | var canonical = builder.getCanonicalName(normalized); 233 | return builder.tracer.getLoadRecord(canonical, traceOpts) 234 | .then(function(load) { 235 | tree[canonical] = load; 236 | }); 237 | }); 238 | })) 239 | .then(function() { 240 | return tree; 241 | }); 242 | } 243 | 244 | return Promise 245 | .resolve(expandAndCanonicalizeExpression(builder, expression)) 246 | .then(function processExpandedOperations(expandedOperations) { 247 | // chain the operations, applying them with the trace of the next module 248 | return expandedOperations.reduce(function(p, op) { 249 | return p.then(function(curTree) { 250 | // tree . module 251 | if (op.singleModule) 252 | return getTreeModuleOperation(builder, op.operator, traceOpts)(curTree, op.moduleName); 253 | 254 | if (op.operationsTree){ 255 | return processExpandedOperations(op.operationsTree).then(function(expandedTree){ 256 | return getTreeOperation(op.operator)(curTree, expandedTree); 257 | }); 258 | } 259 | // tree . tree 260 | return builder.tracer.traceCanonical(op.moduleName, traceOpts) 261 | .then(function(nextTrace) { 262 | return getTreeOperation(op.operator)(curTree, nextTrace.tree); 263 | }); 264 | }); 265 | }, Promise.resolve({})); 266 | }); 267 | }; 268 | 269 | function expandAndCanonicalizeExpression(builder, expression) { 270 | var operations = parseExpression(expression); 271 | var expandPromise = Promise.resolve(1); 272 | var expandedOperations = []; 273 | 274 | operations.forEach(function(operation){ 275 | if (operation.bulkOperation) { 276 | var expandedTreePromise = expandAndCanonicalizeExpression(builder, operation.bulkOperation); 277 | expandPromise = expandPromise.then(function() { 278 | return Promise.resolve(expandedTreePromise) 279 | .then(function(expressionsOperations){ 280 | expandedOperations = expandedOperations.concat({ operator: operation.operator, operationsTree: expressionsOperations }); 281 | }); 282 | }); 283 | } else { 284 | expandPromise = expandPromise.then(function() { 285 | return Promise.resolve(expandGlobAndCanonicalize(builder, operation)) 286 | .then(function (expanded) { 287 | expandedOperations = expandedOperations.concat(expanded); 288 | }); 289 | }) 290 | } 291 | }); 292 | 293 | return Promise.resolve(expandPromise).then(function(){ return expandedOperations; }); 294 | } 295 | 296 | // returns a new tree containing tree1 n tree2 297 | exports.intersectTrees = intersectTrees; 298 | function intersectTrees(tree1, tree2) { 299 | verifyTree(tree1); 300 | verifyTree(tree2); 301 | 302 | var name; 303 | var intersectTree = {}; 304 | 305 | var tree1Names = []; 306 | for (name in tree1) 307 | tree1Names.push(name); 308 | 309 | for (name in tree2) { 310 | if (tree1Names.indexOf(name) == -1) 311 | continue; 312 | // intersect deps layer (load: false) and actual bundle includes separately 313 | if (tree1[name] === false && tree2[name] === false) 314 | continue; 315 | 316 | intersectTree[name] = tree1[name] || tree2[name]; 317 | } 318 | 319 | return intersectTree; 320 | } 321 | 322 | // returns a new tree containing tree1 + tree2 323 | exports.addTrees = addTrees; 324 | function addTrees(tree1, tree2) { 325 | verifyTree(tree1); 326 | verifyTree(tree2); 327 | 328 | var name; 329 | var unionTree = {}; 330 | 331 | for (name in tree2) 332 | unionTree[name] = tree2[name]; 333 | 334 | for (name in tree1) 335 | if (!(name in unionTree)) 336 | unionTree[name] = tree1[name]; 337 | 338 | return unionTree; 339 | } 340 | 341 | // returns a new tree containing tree1 - tree2 342 | exports.subtractTrees = subtractTrees; 343 | function subtractTrees(tree1, tree2) { 344 | verifyTree(tree1); 345 | verifyTree(tree2); 346 | 347 | var name; 348 | var subtractTree = {}; 349 | 350 | for (name in tree1) 351 | subtractTree[name] = tree1[name]; 352 | 353 | for (name in tree2) { 354 | if (tree2[name] !== false) 355 | delete subtractTree[name]; 356 | } 357 | 358 | return subtractTree; 359 | } 360 | 361 | // pre-order tree traversal with a visitor and stop condition 362 | exports.traverseTree = traverseTree; 363 | function traverseTree(tree, moduleName, visitor, traceOpts, reversePost, parent, seen) { 364 | if (!seen) { 365 | // NB traceOpts.conditions should be strictly canonicalized on the first run 366 | traceOpts = traceOpts || {}; 367 | verifyTree(tree); 368 | } 369 | 370 | seen = seen || []; 371 | seen.push(moduleName); 372 | parent = parent || null; 373 | 374 | var curNode = tree[moduleName]; 375 | 376 | if (curNode && visitor(moduleName, parent) !== false) { 377 | var deps = getLoadDependencies(curNode, traceOpts, traceOpts.conditions, traceOpts.inlineConditions); 378 | if (reversePost) 379 | deps = deps.reverse(); 380 | deps.forEach(function(dep) { 381 | if (seen.indexOf(dep) == -1) 382 | traverseTree(tree, dep, visitor, traceOpts, reversePost, moduleName, seen); 383 | }); 384 | } 385 | } --------------------------------------------------------------------------------