diff --git a/docs/production-build.md b/docs/production-build.md index f41854c02..34d28a454 100644 --- a/docs/production-build.md +++ b/docs/production-build.md @@ -2,7 +2,7 @@ The SystemJS production build supports loading System, AMD and global modules. -The configuration options of `baseURL`, `paths`, `map` and `depCache` are supported identically to the +The configuration options of `baseURL`, `paths`, `map`, `depCache` and `wasm` are supported identically to the full development build, and as documented at the [configuration API page](config-api.md). For contextual map configuration, the production build permits objects in the map configuration which act just diff --git a/node-production.js b/node-production.js index 6b442f51e..90366e87f 100755 --- a/node-production.js +++ b/node-production.js @@ -78,7 +78,7 @@ SystemJSProductionNodeLoader.prototype[SystemJSProductionLoader.instantiate] = f })); } - throw new TypeError('SystemJS production does not support loading ES modules. For ES module support see the node-es-module-loader project.'); + throw new TypeError('SystemJS production does not support loading ES or WASM modules in Node.'); }; function tryNodeLoad (path) { diff --git a/package.json b/package.json index 1f9d084bb..ac1c68daa 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "es-module-loader": "^2.2.8", "mocha": "^3.1.2", "rimraf": "^2.6.1", - "rollup": "^0.41.1", + "rollup": "~0.57.1", "rollup-plugin-node-resolve": "^2.0.0", "rollup-plugin-replace": "^1.1.1", "systemjs-plugin-babel": "0.0.17", diff --git a/rollup.config.js b/rollup.config.js index 52e822bc6..166c4607e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -12,16 +12,17 @@ else var name = `system${process.env.production ? '-production' : ''}`; export default { - entry: `src/${name}.js`, - format: 'iife', - dest: `dist/${name}.src.js`, - - sourceMap: true, - sourceMapFile: `dist/${name}.js.map`, - - banner: `/* - * SystemJS v${version} - */`, + input: `src/${name}.js`, + + output: { + format: 'iife', + file: `dist/${name}.src.js`, + sourceMap: true, + sourceMapFile: `dist/${name}.js.map`, + banner: `/* + * SystemJS v${version} + */` + }, plugins: [ nodeResolve({ diff --git a/src/systemjs-production-loader.js b/src/systemjs-production-loader.js index f90df9ac3..45acfaa60 100644 --- a/src/systemjs-production-loader.js +++ b/src/systemjs-production-loader.js @@ -115,6 +115,8 @@ systemJSPrototype.config = function (cfg) { } } + config.wasm = cfg.wasm === true; + for (var p in cfg) { if (!Object.hasOwnProperty.call(cfg, p)) continue; @@ -125,6 +127,7 @@ systemJSPrototype.config = function (cfg) { case 'baseURL': case 'paths': case 'map': + case 'wasm': break; case 'depCache': @@ -165,7 +168,8 @@ systemJSPrototype.getConfig = function (name) { baseURL: config.baseURL, paths: extend({}, config.paths), depCache: depCache, - map: map + map: map, + wasm: config.wasm === true }; }; @@ -207,8 +211,36 @@ function plainResolve (key, parentKey) { } } -function doScriptLoad (loader, url, processAnonRegister) { +function instantiateWasm (loader, response, processAnonRegister) { + return WebAssembly.compileStreaming(response).then(function (module) { + var deps = []; + var setters = []; + var importObj = {}; + + // we can only set imports if supported (eg early Safari doesnt support) + if (WebAssembly.Module.imports) + WebAssembly.Module.imports(module).forEach(function (i) { + var key = i.module; + setters.push(function (m) { + importObj[key] = m; + }); + if (deps.indexOf(key) === -1) + deps.push(key); + }); + + loader.register(deps, function (_export) { + return { + setters: setters, + execute: function () { + _export(new WebAssembly.Instance(module, importObj).exports); + } + }; + }); + processAnonRegister(); + }); +} +function doScriptLoad (loader, url, processAnonRegister) { // store a global snapshot in case it turns out to be global globalSnapshot = {}; Object.keys(global).forEach(globalIterator, function (name, value) { @@ -227,18 +259,47 @@ function doScriptLoad (loader, url, processAnonRegister) { // still no registration -> attempt a global detection if (!registered) { - loader.registerDynamic([], false, function () { - return retrieveGlobal(); + loader.register([], function () { + return { + execute: retrieveGlobal + }; }); processAnonRegister(); } } - resolve(); }, reject); }); } +function doEvalLoad (loader, url, source, processAnonRegister) { + // store a global snapshot in case it turns out to be global + globalSnapshot = {}; + Object.keys(global).forEach(globalIterator, function (name, value) { + globalSnapshot[name] = value; + }); + + (0, eval)(source + '\n//# sourceURL=' + url); + + // check for System.register call + var registered = processAnonRegister(); + if (!registered) { + // no System.register -> support named AMD as anonymous + registerLastDefine(loader); + registered = processAnonRegister(); + + // still no registration -> attempt a global detection + if (!registered) { + loader.register([], function () { + return { + execute: retrieveGlobal + }; + }); + processAnonRegister(); + } + } +} + var globalSnapshot; function retrieveGlobal () { var globalValue = { __esModule: true }; @@ -277,5 +338,21 @@ function coreInstantiate (key, processAnonRegister) { this.resolve(depCache[i], key).then(preloadScript); } + if (config.wasm) { + var loader = this; + return fetch(key) + .then(function (res) { + if (!res.ok) + throw new Error('Fetch error: ' + res.status + ' ' + res.statusText); + if (res.headers.get('content-type').indexOf('application/wasm') === -1) { + return res.text() + .then(function (source) { + doEvalLoad(loader, key, source, processAnonRegister); + }); + } + return instantiateWasm(loader, res, processAnonRegister); + }); + } + return doScriptLoad(this, key, processAnonRegister); } diff --git a/test/test-production.js b/test/test-production.js index be6d62f71..09f7c77d1 100644 --- a/test/test-production.js +++ b/test/test-production.js @@ -10,7 +10,7 @@ if (typeof document !== 'undefined') { base = document.baseURI.substr(0, document.baseURI.lastIndexOf('/') + 1); } else { - var cwd = process.cwd(); + var cwd = process.cwd().replace(/\\/g, '/'); base = 'file://' + (cwd[0] !== '/' ? '/' : '') + cwd + '/test/'; } @@ -249,6 +249,20 @@ suite('SystemJS Standard Tests', function() { }); }); } + + if (typeof WebAssembly !== 'undefined' && typeof process === 'undefined') + test('Loading WASM', function () { + System.config({ + wasm: true, + map: { + 'example': 'tests/wasm/example.js' + } + }); + return System.import('tests/wasm/example.wasm') + .then(function (m) { + ok(m.exampleExport(1) === 2); + }); + }); }); System.register([], function () { return function () {} });