From 90b0b47a47868018699428b16711ec12d85709c6 Mon Sep 17 00:00:00 2001
From: Guy Bedford
Date: Mon, 26 Aug 2019 17:51:16 -0400
Subject: [PATCH 01/13] fall back to eval for application/javascript on module
types
---
package.json | 4 +-
src/features/script-load.js | 35 ++++++++----
test/browser/core.js | 9 ++-
test/fixtures/browser/importmap.json | 1 +
test/fixtures/css-modules/a.css | 5 +-
test/fixtures/css-modules/javascript.css | 5 ++
test/server.js | 72 ++++++++++++++++++++++++
test/test.html | 8 ++-
8 files changed, 122 insertions(+), 17 deletions(-)
create mode 100644 test/fixtures/css-modules/javascript.css
create mode 100644 test/server.js
diff --git a/package.json b/package.json
index 53613ba09..54df430e3 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"construct-style-sheets-polyfill": "^2.1.0",
"esm": "^3.2.25",
"mocha": "^5.2.0",
+ "opn": "^6.0.0",
"rollup": "^0.64.1",
"rollup-plugin-replace": "^2.0.0",
"terser": "^3.8.1",
@@ -33,7 +34,8 @@
"footprint": "npm run footprint:systemjs && npm run footprint:sjs",
"footprint:systemjs": "terser dist/system.js -c passes=2 -m | gzip -9f | wc -c",
"footprint:sjs": "terser dist/s.js -c passes=2 -m | gzip -9f | wc -c",
- "test": "mocha -b -r esm",
+ "test": "mocha -b -r esm && npm run test-browser",
+ "test-browser": "node test/server.js",
"prepublish": "npm run build"
}
}
diff --git a/src/features/script-load.js b/src/features/script-load.js
index 3ffb5f410..bb0dea1f6 100644
--- a/src/features/script-load.js
+++ b/src/features/script-load.js
@@ -12,11 +12,11 @@ systemJSPrototype.register = function (deps, declare) {
systemJSPrototype.instantiate = function (url, firstParentUrl) {
const loader = this;
if (url.slice(-5) === '.json') {
- return loadDynamicModule(url, function (_export, source) {
+ return loadDynamicModule(function (_export, source) {
_export('default', JSON.parse(source));
});
} else if (url.slice(-4) === '.css') {
- return loadDynamicModule(url, function (_export, source) {
+ return loadDynamicModule(function (_export, source) {
// Relies on a Constructable Stylesheet polyfill
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync(source);
@@ -41,7 +41,7 @@ systemJSPrototype.instantiate = function (url, firstParentUrl) {
script.crossOrigin = 'anonymous';
script.addEventListener('error', function () {
window.removeEventListener('error', windowErrorListener);
- reject(Error('Error loading ' + url + (firstParentUrl ? ' from ' + firstParentUrl : '')));
+ reject(loadError(''));
});
script.addEventListener('load', function () {
window.removeEventListener('error', windowErrorListener);
@@ -59,14 +59,25 @@ systemJSPrototype.instantiate = function (url, firstParentUrl) {
document.head.appendChild(script);
});
}
+ 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)};
+ }];
+ });
+ });
+ }
};
-function loadDynamicModule (url, createExec) {
- return fetch(url).then(function (resp) {
- return resp.text();
- }).then(function (source) {
- return [[], function (_export) {
- return {execute: createExec(_export, source)};
- }];
- });
-}
diff --git a/test/browser/core.js b/test/browser/core.js
index 8534354f4..49f7de523 100644
--- a/test/browser/core.js
+++ b/test/browser/core.js
@@ -160,9 +160,16 @@ suite('SystemJS Standard Tests', function() {
});
test('should load a css module', async function () {
- const m = await System.import('./css-modules/a.css')
+ const m = await System.import('fixturesbase/css-modules/a.css');
assert.ok(m);
assert.ok(m.default instanceof CSSStyleSheet);
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, m.default];
+ });
+
+ test('should support application/javascript css module override', async function () {
+ const m = await System.import('fixturesbase/css-modules/javascript.css');
+ assert.ok(!m);
+ assert.ok(m.css, 'module');
});
test('should throw when trying to load an HTML module', function () {
diff --git a/test/fixtures/browser/importmap.json b/test/fixtures/browser/importmap.json
index ae9ce9296..6fe9b4736 100644
--- a/test/fixtures/browser/importmap.json
+++ b/test/fixtures/browser/importmap.json
@@ -1,6 +1,7 @@
{
"imports": {
"fixtures/": "./",
+ "fixturesbase/": "../",
"a": "/b",
"a/": "./a/",
"f": "a:",
diff --git a/test/fixtures/css-modules/a.css b/test/fixtures/css-modules/a.css
index 2b3c82b3b..f8b3ad928 100644
--- a/test/fixtures/css-modules/a.css
+++ b/test/fixtures/css-modules/a.css
@@ -1,3 +1,6 @@
.hello {
background-color: peru;
-}
\ No newline at end of file
+}
+body {
+ background-color: lightblue;
+}
diff --git a/test/fixtures/css-modules/javascript.css b/test/fixtures/css-modules/javascript.css
new file mode 100644
index 000000000..ca30fb166
--- /dev/null
+++ b/test/fixtures/css-modules/javascript.css
@@ -0,0 +1,5 @@
+System.register([], function (_export) {
+ return { execute: function () {
+ _export('css', 'module');
+ }};
+});
diff --git a/test/server.js b/test/server.js
new file mode 100644
index 000000000..1ee5e935e
--- /dev/null
+++ b/test/server.js
@@ -0,0 +1,72 @@
+const http = require('http');
+const fs = require('fs');
+const path = require('path');
+const { once } = require('events');
+const { pathToFileURL, fileURLToPath } = require('url');
+const opn = require('opn');
+
+const port = 8080;
+
+const systemJSURL = pathToFileURL(path.resolve(__dirname, '..') + '/');
+
+const mimes = {
+ '.html': 'text/html',
+ '.css': 'text/css',
+ '.js': 'application/javascript',
+ '.mjs': 'application/javascript',
+ '.json': 'application/json',
+ '.wasm': 'application/wasm'
+};
+
+http.createServer(async function (req, res) {
+ if (req.url === '/done') {
+ console.log('Tests completed successfully.');
+ process.exit();
+ return;
+ }
+ else if (req.url === '/error') {
+ console.log('\033[31mTest failures found.\033[0m');
+ }
+
+ const url = new URL(req.url[0] === '/' ? req.url.slice(1) : req.url, systemJSURL);
+ const filePath = fileURLToPath(url);
+
+ // redirect to test/test.html file by default
+ if (url.href === systemJSURL.href) {
+ res.writeHead(301, {
+ 'location': '/test/test.html'
+ });
+ res.end();
+ return;
+ }
+
+ const fileStream = fs.createReadStream(filePath);
+ try {
+ await once(fileStream, 'readable');
+ }
+ catch (e) {
+ if (e.code === 'EISDIR' || e.code === 'ENOENT') {
+ res.writeHead(404, {
+ 'content-type': 'text/html'
+ });
+ res.end(`File not found.`);
+ }
+ return;
+ }
+
+ let mime;
+ if (filePath.endsWith('javascript.css'))
+ mime = 'application/javascript';
+ else
+ mime = mimes[path.extname(filePath)] || 'text/plain';
+
+ res.writeHead(200, {
+ 'content-type': mime
+ });
+ fileStream.pipe(res);
+ await once(fileStream, 'end');
+ res.end();
+}).listen(port);
+
+console.log(`Test server listening on http://localhost:${port}\n`);
+opn(`http://localhost:${port}/test/test.html`);
diff --git a/test/test.html b/test/test.html
index 8722d2ec0..8d93aa86f 100644
--- a/test/test.html
+++ b/test/test.html
@@ -8,7 +8,7 @@
if (typeof fetch === 'undefined')
document.write('
-
+
```
-_Note that this polyfill does not currently work in IE11._
\ No newline at end of file
+_Note that this polyfill does not currently work in IE11._
+
+## Web Assembly Modules
+
+[Web Assembly Modules](https://github.com/WebAssembly/esm-integration/tree/master/proposals/esm-integration) support importing Web Assembly with Web Assembly in turn supporting other modules.
+
+### Example
+
+```html
+
+
+```
+
+wasm-dependency.js
+```js
+// function called from Wasm
+export function exampleImport (num) {
+ return num * num;
+}
+```
+
+where `wasm-module.wasm` is generated from:
+
+**wasm-module.wat**
+```wat
+(module
+ (func $exampleImport (import "example" "exampleImport") (param i32) (result i32))
+ (func $exampleExport (export "exampleExport") (param $value i32) (result i32)
+ get_local $value
+ call $exampleImport
+ )
+)
+```