Skip to content

Commit

Permalink
create dedicated module-types extra
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford committed Aug 27, 2019
1 parent 2a0d7e9 commit 4ce3e2b
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 130 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ The following [pluggable extras](dist/extras) are provided which can be dropped

* [AMD loading](dist/extras/amd.js) support (through `Window.define` which is created).
* [Global loading](dist/extras/global.js) support for loading global scripts and detecting the defined global as the default export. Useful for loading common library scripts from CDN like `System.import('//unpkg.com/lodash')`. _(Already included in the system.js loader build)_.
* [Module Types](dist/extras/module-types.js) `.css`, `.wasm`, `.json` [module type](docs/module-types.md) loading support in line with the existing modules specifications.
* [Named exports](dist/extras/named-exports.js) convenience extension support for global and AMD module formats (`import { x } from './global.js'` instead of `import G from './global.js'; G.x`)
* [Named register](dist/extras/named-register.js) supports `System.register('name', ...)` named bundles which can then be imported as `System.import('name')` (as well as AMD named define support)
* [Transform loader](dist/extras/transform.js) support, using fetch and eval, supporting a hookable `loader.transform`
Expand Down
6 changes: 3 additions & 3 deletions docs/module-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ SystemJS supports loading modules that are in the following formats:
| Module Format | s.js | system.js | File Extension |
| ------------- | ---- | --------- | -------------- |
| [System.register](/docs/system-register.md) | :heavy_check_mark: | :heavy_check_mark: | * |
| [JSON Modules](https://github.com/whatwg/html/pull/4407) | :heavy_check_mark: | :heavy_check_mark: | *.json |
| [CSS Modules](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/css-modules-v1-explainer.md) | :heavy_check_mark: | :heavy_check_mark: | *.css |
| [Web Assembly](https://github.com/WebAssembly/esm-integration/tree/master/proposals/esm-integration) | :x: | :heavy_check_mark: | *.wasm |
| [JSON Modules](https://github.com/whatwg/html/pull/4407) | [Module Types extra](/README.md#extras) | :heavy_check_mark: | *.json |
| [CSS Modules](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/css-modules-v1-explainer.md) | [Module Types extra](/README.md#extras) | :heavy_check_mark: | *.css |
| [Web Assembly](https://github.com/WebAssembly/esm-integration/tree/master/proposals/esm-integration) | [Module Types extra](/README.md#extras) | :heavy_check_mark: | *.wasm |
| Global variable | [global extra](/README.md#extras) | :heavy_check_mark: | * |
| [AMD](https://github.com/amdjs/amdjs-api/wiki/AMD) | [AMD extra](/README.md#extras) | [AMD extra](/README.md#extras) | * |
| [UMD](https://github.com/umdjs/umd) | [AMD extra](/README.md#extras) | [AMD extra](/README.md#extras) | * |
Expand Down
91 changes: 91 additions & 0 deletions src/extras/module-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Loads JSON, CSS, Wasm module types based on file extensions
* Supports application/javascript falling back to JS eval
*/
import { systemJSPrototype } from '../system-core';
const instantiate = systemJSPrototype.instantiate;
systemJSPrototype.instantiate = function (url, parent) {
const loader = this;
const ext = url.slice(url.lastIndexOf('.'));
switch (ext) {
case '.css':
return loadDynamicModule(function (_export, source) {
// Relies on a Constructable Stylesheet polyfill
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync(source);
_export('default', stylesheet);
});
case '.html':
return getSourceRes().then(function (res) {
return maybeJSFallback(res) || loadError("'.html' modules not implemented");
});
case '.json':
return loadDynamicModule(function (_export, source) {
_export('default', JSON.parse(source));
});
case '.wasm':
return getSourceRes().then(function (res) {
return maybeJSFallback(res) ||
(WebAssembly.compileStreaming ? WebAssembly.compileStreaming(res) : res.arrayBuffer().then(WebAssembly.compile));
})
.then(function (module) {
const deps = [];
const setters = [];
const importObj = {};

// we can only set imports if supported (eg early Safari doesnt support)
if (WebAssembly.Module.imports)
WebAssembly.Module.imports(module).forEach(function (impt) {
const key = impt.module;
setters.push(function (m) {
importObj[key] = m;
});
if (deps.indexOf(key) === -1)
deps.push(key);
});

return [deps, function (_export) {
return {
setters: setters,
execute: function () {
return WebAssembly.instantiate(module, importObj)
.then(function (instance) {
_export(instance.exports);
});
}
};
}];
});
}
return instantiate.apply(this, arguments);

function getSourceRes () {
return fetch(url).then(function (res) {
if (!res.ok)
loadError(res.status + ' ' + res.statusText);
return res;
});
}
function maybeJSFallback (res) {
const contentType = res.headers.get('content-type');
// if the resource is sent as application/javascript, support eval-based execution
if (contentType && contentType.match(/^application\/javascript(;|$)/)) {
return res.text().then(function (source) {
(0, eval)(source);
return loader.getRegister();
});
}
}
function loadDynamicModule (createExec) {
return getSourceRes().then(function (res) {
return maybeJSFallback(res) || res.text().then(function (source) {
return [[], function (_export) {
return { execute: createExec(_export, source) };
}];
});
});
}
function loadError (msg) {
throw Error(msg + ', loading ' + url + (parent ? ' from ' + parent : ''));
}
};
91 changes: 28 additions & 63 deletions src/features/script-load.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,73 +11,38 @@ systemJSPrototype.register = function (deps, declare) {

systemJSPrototype.instantiate = function (url, firstParentUrl) {
const loader = this;
if (url.slice(-5) === '.json') {
return loadDynamicModule(function (_export, source) {
_export('default', JSON.parse(source));
});
} else if (url.slice(-4) === '.css') {
return loadDynamicModule(function (_export, source) {
// Relies on a Constructable Stylesheet polyfill
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync(source);
_export('default', stylesheet);
});
} else if (url.slice(-5) === '.html') {
return Promise.reject(Error("Error loading " + url + ". '.html' modules not implemented."));
} else {
return new Promise(function (resolve, reject) {
let err;
return new Promise(function (resolve, reject) {
let err;

function windowErrorListener(evt) {
if (evt.filename === url)
err = evt.error;
}
function windowErrorListener(evt) {
if (evt.filename === url)
err = evt.error;
}

window.addEventListener('error', windowErrorListener);
window.addEventListener('error', windowErrorListener);

const script = document.createElement('script');
script.charset = 'utf-8';
script.async = true;
script.crossOrigin = 'anonymous';
script.addEventListener('error', function () {
window.removeEventListener('error', windowErrorListener);
reject(loadError(''));
});
script.addEventListener('load', function () {
window.removeEventListener('error', windowErrorListener);
document.head.removeChild(script);
// Note that if an error occurs that isn't caught by this if statement,
// that getRegister will return null and a "did not instantiate" error will be thrown.
if (err) {
reject(err);
}
else {
resolve(loader.getRegister());
}
});
script.src = url;
document.head.appendChild(script);
const script = document.createElement('script');
script.charset = 'utf-8';
script.async = true;
script.crossOrigin = 'anonymous';
script.addEventListener('error', function () {
window.removeEventListener('error', windowErrorListener);
reject(Error('Error loading ' + url + (firstParentUrl ? ' from ' + firstParentUrl : '')));
});
}
function loadError (msg) {
return Error('Error loading ' + url + (firstParentUrl ? ' from ' + firstParentUrl : '') + msg);
}
function loadDynamicModule (createExec) {
return fetch(url).then(function (res) {
if (!res.ok)
throw loadError(', ' + res.statusText);
return res.text().then(function (source) {
const contentType = res.headers.get('content-type');
// if the resource is sent as application/javascript, support eval-based execution
if (contentType && contentType.match(/^application\/javascript(;|$)/)) {
(0, eval)(source);
return loader.getRegister();
}
return [[], function (_export) {
return {execute: createExec(_export, source)};
}];
});
script.addEventListener('load', function () {
window.removeEventListener('error', windowErrorListener);
document.head.removeChild(script);
// Note that if an error occurs that isn't caught by this if statement,
// that getRegister will return null and a "did not instantiate" error will be thrown.
if (err) {
reject(err);
}
else {
resolve(loader.getRegister());
}
});
}
script.src = url;
document.head.appendChild(script);
});
};

52 changes: 0 additions & 52 deletions src/features/wasm-load.js

This file was deleted.

3 changes: 2 additions & 1 deletion src/features/worker-load.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ if (hasSelf && typeof importScripts === 'function')
}
resolve(loader.getRegister());
});
};
};

2 changes: 1 addition & 1 deletion src/system.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './features/script-load.js';
import './features/worker-load.js';
import './extras/global.js';
import './features/wasm-load.js';
import './extras/module-types.js';
import './features/import-map.js';
import './features/registry.js';
16 changes: 6 additions & 10 deletions test/browser/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ suite('SystemJS Standard Tests', function() {
return System.import('fixtures/error-loader2.js').then(function () {
assert.fail('Should fail');
}, function (e) {
console.log(e.message);
assert.ok(e);
console.log(e);
assert.ok(e.message.indexOf('Error loading ') === 0);
assert.ok(e.message.indexOf('non-existent') !== -1);
assert.ok(e.message.indexOf('error-loader2.js') !== -1);
Expand Down Expand Up @@ -173,14 +173,10 @@ suite('SystemJS Standard Tests', function() {
});

test('should throw when trying to load an HTML module', function () {
return System.import('/a.html')
.then(
function () {
throw Error("Loading html modules isn't implemented, but attempting to do so didn't throw an Error");
},
function (err) {
assert.ok(err.message.indexOf("'.html' modules not implemented.") !== -1);
}
);
return System.import('/test/test.html').then(function () {
throw Error("Loading html modules isn't implemented, but attempting to do so didn't throw an Error");
}, function (err) {
assert.ok(err.message.indexOf("'.html' modules not implemented") !== -1);
});
});
});

0 comments on commit 4ce3e2b

Please sign in to comment.