Skip to content

Commit

Permalink
module-types use assert type to detect module type
Browse files Browse the repository at this point in the history
  • Loading branch information
wenerme committed Oct 5, 2022
1 parent 1b03996 commit 3eda65d
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 46 deletions.
12 changes: 11 additions & 1 deletion docs/module-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,22 @@ SystemJS supports loading modules that are in the following formats:
| [AMD](https://github.com/amdjs/amdjs-api/wiki/AMD) | [AMD extra](/dist/extras/amd.js) | [AMD extra](/dist/extras/amd.js) | * |
| [UMD](https://github.com/umdjs/umd) | [AMD extra](/dist/extras/amd.js) | [AMD extra](/dist/extras/amd.js) | * |

### File Extension Limitations
### Module type detection

When loading JSON modules, CSS modules and Web Assembly modules, the browser specifications require interpreting these modules based on checking their MIME type. Since SystemJS has to choose upfront whether to append a script element (for JS modules) or make a fetch request (for a JSON/CSS/Wasm module), it needs to know the module type upfront at resolution time.

Instead of reading the MIME type, the file extension is thus used specifically for the JSON, CSS and Web Assembly module cases.

If module type is specified by import, the file extension & mime is ignored.

e.g.

```js
System.import('https://example.com/my-module.txt', {assert: {type: 'css'}});
```

This will force resolve the module as a CSS module, even if the file extension is `.txt` and the MIME type is `text/plain`.

## JSON Modules

[JSON modules](https://github.com/whatwg/html/pull/4407) support importing a JSON file as the default export.
Expand Down
4 changes: 2 additions & 2 deletions src/extras/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@
}

var impt = systemJSPrototype.import;
systemJSPrototype.import = function (id, parentUrl) {
systemJSPrototype.import = function (id, parentUrl, meta) {
noteGlobalProps();
return impt.call(this, id, parentUrl);
return impt.call(this, id, parentUrl, meta);
};

var emptyInstantiation = [[], function () { return {} }];
Expand Down
74 changes: 42 additions & 32 deletions src/extras/module-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,29 @@ import { resolveUrl } from '../common.js';

var moduleTypesRegEx = /^[^#?]+\.(css|html|json|wasm)([?#].*)?$/;
var _shouldFetch = systemJSPrototype.shouldFetch.bind(systemJSPrototype)
systemJSPrototype.shouldFetch = function (url) {
return _shouldFetch(url) || moduleTypesRegEx.test(url);
systemJSPrototype.shouldFetch = function (url, parent, meta) {
var assertType = ((meta || {}).assert || {}).type;
return _shouldFetch(url, parent, meta) || moduleTypesRegEx.test(url) || !!loaders[assertType];
};

var jsonContentType = /^application\/json(;|$)/;
var cssContentType = /^text\/css(;|$)/;
var wasmContentType = /^application\/wasm(;|$)/;

var fetch = systemJSPrototype.fetch;
systemJSPrototype.fetch = function (url, options) {
return fetch(url, options)
.then(function (res) {
if (options.passThrough)
return res;

if (!res.ok)
return res;
var contentType = res.headers.get('content-type');
if (jsonContentType.test(contentType))
return res.json()
var contentTypes = [
[/^application\/json(;|$)/, 'json'],
[/^text\/css(;|$)/, 'css'],
[/^application\/wasm(;|$)/, 'webassembly'],
]
var loaders = {
'json': function (res) {
return res.json()
.then(function (json) {
return new Response(new Blob([
'System.register([],function(e){return{execute:function(){e("default",' + JSON.stringify(json) + ')}}})'
], {
type: 'application/javascript'
}));
});
if (cssContentType.test(contentType))
return res.text()
})
},
'css': function (res, url) {
return res.text()
.then(function (source) {
source = source.replace(/url\(\s*(?:(["'])((?:\\.|[^\n\\"'])+)\1|((?:\\.|[^\s,"'()\\])+))\s*\)/g, function (match, quotes, relUrl1, relUrl2) {
return 'url(' + quotes + resolveUrl(relUrl1 || relUrl2, url) + quotes + ')';
Expand All @@ -48,11 +42,11 @@ import { resolveUrl } from '../common.js';
type: 'application/javascript'
}));
});
if (wasmContentType.test(contentType))
return (WebAssembly.compileStreaming ? WebAssembly.compileStreaming(res) : res.arrayBuffer().then(WebAssembly.compile))
},
'webassembly': function (res, url) {
return (WebAssembly.compileStreaming ? WebAssembly.compileStreaming(res) : res.arrayBuffer().then(WebAssembly.compile))
.then(function (module) {
if (!global.System.wasmModules)
global.System.wasmModules = Object.create(null);
if (!global.System.wasmModules) global.System.wasmModules = Object.create(null);
global.System.wasmModules[url] = module;
// we can only set imports if supported (eg early Safari doesnt support)
var deps = [];
Expand All @@ -65,15 +59,31 @@ import { resolveUrl } from '../common.js';
setterSources.push('function(m){i[' + key + ']=m}');
}
});
return new Response(new Blob([
'System.register([' + deps.join(',') + '],function(e){var i={};return{setters:[' + setterSources.join(',') +
'],execute:function(){return WebAssembly.instantiate(System.wasmModules[' + JSON.stringify(url) +
'],i).then(function(m){e(m.exports)})}}})'
], {
return new Response(new Blob(['System.register([' + deps.join(',') + '],function(e){var i={};return{setters:[' + setterSources.join(',') + '],execute:function(){return WebAssembly.instantiate(System.wasmModules[' + JSON.stringify(url) + '],i).then(function(m){e(m.exports)})}}})'], {
type: 'application/javascript'
}));
});
return res;
});
}
}

var fetch = systemJSPrototype.fetch;
systemJSPrototype.fetch = function (url, options) {
return fetch(url, options)
.then(function (res) {
if (options.passThrough) return res;

if (!res.ok) return res;

var assertType = ((options.meta || {}).assert || {}).type;
if (!assertType) {
var contentType = res.headers.get('content-type');
for (var i = 0; i < contentTypes.length; i++) {
if (contentTypes[i][0].test(contentType)) assertType = contentTypes[i][1]
}
}
var loader = loaders[assertType];
if (loader) return loader(res, url, options);
return res;
});
};
})(typeof self !== 'undefined' ? self : global);
6 changes: 3 additions & 3 deletions src/extras/named-register.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
firstNamedDefine = null;
firstName = null;
});
return register.apply(this, [deps, declare]);
return register.apply(this, [deps, declare, metas]);
};

var resolve = systemJSPrototype.resolve;
Expand All @@ -55,13 +55,13 @@
};

var instantiate = systemJSPrototype.instantiate;
systemJSPrototype.instantiate = function (url, firstParentUrl) {
systemJSPrototype.instantiate = function (url, firstParentUrl, meta) {
var result = this.registerRegistry[url];
if (result) {
this.registerRegistry[url] = null;
return result;
} else {
return instantiate.call(this, url, firstParentUrl);
return instantiate.call(this, url, firstParentUrl, meta);
}
};

Expand Down
4 changes: 2 additions & 2 deletions src/extras/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { errMsg } from '../err-msg.js';
var systemJSPrototype = global.System.constructor.prototype;

var instantiate = systemJSPrototype.instantiate;
systemJSPrototype.instantiate = function (url, parent) {
systemJSPrototype.instantiate = function (url, parent, meta) {
if (url.slice(-5) === '.wasm')
return instantiate.call(this, url, parent);
return instantiate.call(this, url, parent, meta);

var loader = this;
return fetch(url, { credentials: 'same-origin' })
Expand Down
6 changes: 3 additions & 3 deletions src/features/depcache.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { systemJSPrototype, getOrCreateLoad } from '../system-core.js';
import { importMap } from './import-maps.js';

var systemInstantiate = systemJSPrototype.instantiate;
systemJSPrototype.instantiate = function (url, firstParentUrl) {
systemJSPrototype.instantiate = function (url, firstParentUrl, meta) {
var preloads = (!process.env.SYSTEM_BROWSER && this[IMPORT_MAP] || importMap).depcache[url];
if (preloads) {
for (var i = 0; i < preloads.length; i++)
getOrCreateLoad(this, this.resolve(preloads[i], url), url);
}
return systemInstantiate.call(this, url, firstParentUrl);
};
return systemInstantiate.call(this, url, firstParentUrl, meta);
};
7 changes: 4 additions & 3 deletions src/features/fetch-load.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ if (typeof fetch !== 'undefined')

var instantiate = systemJSPrototype.instantiate;
var jsContentTypeRegEx = /^(text|application)\/(x-)?javascript(;|$)/;
systemJSPrototype.instantiate = function (url, parent) {
systemJSPrototype.instantiate = function (url, parent, meta) {
var loader = this;
if (!this.shouldFetch(url))
if (!this.shouldFetch(url, parent, meta))
return instantiate.apply(this, arguments);
return this.fetch(url, {
credentials: 'same-origin',
integrity: importMap.integrity[url]
integrity: importMap.integrity[url],
meta
})
.then(function (res) {
if (!res.ok)
Expand Down
16 changes: 16 additions & 0 deletions test/browser/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,22 @@ suite('SystemJS Standard Tests', function() {
});
});

test('should load a.txt as css module by assert', function () {
return System.import('fixturesbase/css-modules/a.txt', {assert: {type: 'css'}}).then(function (m) {
assert.ok(m);
assert.ok(isCSSStyleSheet(m.default));
document.adoptedStyleSheets = document.adoptedStyleSheets.concat(m.default);
});
});

test('should load a as css module by assert', function () {
return System.import('fixturesbase/css-modules/a', {assert: {type: 'css'}}).then(function (m) {
assert.ok(m);
assert.ok(isCSSStyleSheet(m.default));
document.adoptedStyleSheets = document.adoptedStyleSheets.concat(m.default);
});
});

test('should support application/javascript css module override', function () {
return System.import('fixturesbase/css-modules/javascript.css').then(function (m) {
assert.ok(m);
Expand Down
6 changes: 6 additions & 0 deletions test/fixtures/css-modules/a
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.hello {
background-color: peru;
}
body {
background-color: lightblue;
}
6 changes: 6 additions & 0 deletions test/fixtures/css-modules/a.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.hello {
background-color: peru;
}
body {
background-color: lightblue;
}

0 comments on commit 3eda65d

Please sign in to comment.