Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for import maps in Worker; resolves #9 #17

Merged
merged 4 commits into from
Jun 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions dist/es-module-shims.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
baseUrl = baseUrl.slice(0, lastSepIndex + 1);
}

let esModuleShimsSrc;
if (typeof document !== 'undefined') {
esModuleShimsSrc = document.currentScript && document.currentScript.src;
}

const backslashRegEx = /\\/g;
function resolveIfNotPlainOrUrl (relUrl, parentUrl) {
if (relUrl.indexOf('\\') !== -1)
Expand Down Expand Up @@ -178,6 +183,10 @@
throw new Error('Unable to resolve bare specifier "' + id + (parentUrl ? '" from ' + parentUrl : '"'));
}

function createBlob (source) {
return URL.createObjectURL(new Blob([source], { type: 'application/javascript' }));
}

function analyzeModuleSyntax (_str) {
str = _str;
let err = null;
Expand Down Expand Up @@ -689,6 +698,25 @@
throw new Error();
}

class WorkerShim {
constructor(aURL, options = {type: 'classic'}) {
if (options.type !== 'module') {
return new Worker(aURL, options);
}

if (!esModuleShimsSrc)
throw new Error('es-module-shims.js must be loaded with a script tag for WorkerShim support.');

const workerScriptUrl = createBlob(
`importScripts('${esModuleShimsSrc}'); self.importMap = ${JSON.stringify(options.importMap || {})}; importShim('${new URL(aURL, baseUrl).href}')`);

const workerOptions = {...options};
workerOptions.type = 'classic';

return new Worker(workerScriptUrl, workerOptions);
}
}

let id = 0;
const registry = {};

Expand Down Expand Up @@ -826,7 +854,6 @@
load.b = createBlob(resolvedSource + '\n//# sourceURL=' + load.r);
load.S = undefined;
}
const createBlob = source => URL.createObjectURL(new Blob([source], { type: 'application/javascript' }));

function getOrCreateLoad (url, source) {
let load = registry[url];
Expand Down Expand Up @@ -913,19 +940,19 @@
return load;
}

let importMap, importMapPromise;
let importMapPromise;
if (typeof document !== 'undefined') {
const scripts = document.getElementsByTagName('script');
for (let i = 0; i < scripts.length; i++) {
const script = scripts[i];
if (script.type === 'importmap-shim' && !importMapPromise) {
if (script.src) {
importMapPromise = (async function () {
importMap = parseImportMap(await (await fetch(script.src)).json(), script.src.slice(0, script.src.lastIndexOf('/') + 1));
self.importMap = parseImportMap(await (await fetch(script.src)).json(), script.src.slice(0, script.src.lastIndexOf('/') + 1));
})();
}
else {
importMap = parseImportMap(JSON.parse(script.innerHTML), baseUrl);
self.importMap = parseImportMap(JSON.parse(script.innerHTML), baseUrl);
}
}
// this works here because there is a .then before resolve
Expand All @@ -938,18 +965,20 @@
}
}

importMap = importMap || { imports: {}, scopes: {} };
self.importMap = self.importMap || { imports: {}, scopes: {} };

async function resolve (id, parentUrl) {
parentUrl = parentUrl || baseUrl;

if (importMapPromise)
return importMapPromise
.then(function () {
return resolveImportMap(id, parentUrl, importMap);
return resolveImportMap(id, parentUrl, self.importMap);
});

return resolveImportMap(id, parentUrl, importMap);
return resolveImportMap(id, parentUrl, self.importMap);
}

self.WorkerShim = WorkerShim;

}());
11 changes: 10 additions & 1 deletion src/common.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ if (typeof location !== 'undefined') {
baseUrl = baseUrl.slice(0, lastSepIndex + 1);
}

export let esModuleShimsSrc;
if (typeof document !== 'undefined') {
esModuleShimsSrc = document.currentScript && document.currentScript.src;
}

const backslashRegEx = /\\/g;
export function resolveIfNotPlainOrUrl (relUrl, parentUrl) {
if (relUrl.indexOf('\\') !== -1)
Expand Down Expand Up @@ -177,4 +182,8 @@ export function resolveImportMap (id, parentUrl, importMap) {

export function throwBare (id, parentUrl) {
throw new Error('Unable to resolve bare specifier "' + id + (parentUrl ? '" from ' + parentUrl : '"'));
}
}

export function createBlob (source) {
return URL.createObjectURL(new Blob([source], { type: 'application/javascript' }));
}
18 changes: 10 additions & 8 deletions src/es-module-shims.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { resolveIfNotPlainOrUrl, baseUrl as pageBaseUrl, parseImportMap, resolveImportMap } from './common.js';
import { resolveIfNotPlainOrUrl, baseUrl as pageBaseUrl, parseImportMap, resolveImportMap, createBlob } from './common.js';
import { analyzeModuleSyntax } from './lexer.js';
import { WorkerShim } from './worker-shims.js';

let id = 0;
const registry = {};
Expand Down Expand Up @@ -139,7 +140,6 @@ async function resolveDeps (load, seen) {
load.b = createBlob(resolvedSource + '\n//# sourceURL=' + load.r);
load.S = undefined;
}
const createBlob = source => URL.createObjectURL(new Blob([source], { type: 'application/javascript' }));

function getOrCreateLoad (url, source) {
let load = registry[url];
Expand Down Expand Up @@ -226,19 +226,19 @@ function getOrCreateLoad (url, source) {
return load;
}

let importMap, importMapPromise;
let importMapPromise;
if (typeof document !== 'undefined') {
const scripts = document.getElementsByTagName('script');
for (let i = 0; i < scripts.length; i++) {
const script = scripts[i];
if (script.type === 'importmap-shim' && !importMapPromise) {
if (script.src) {
importMapPromise = (async function () {
importMap = parseImportMap(await (await fetch(script.src)).json(), script.src.slice(0, script.src.lastIndexOf('/') + 1));
self.importMap = parseImportMap(await (await fetch(script.src)).json(), script.src.slice(0, script.src.lastIndexOf('/') + 1));
})();
}
else {
importMap = parseImportMap(JSON.parse(script.innerHTML), pageBaseUrl);
self.importMap = parseImportMap(JSON.parse(script.innerHTML), pageBaseUrl);
}
}
// this works here because there is a .then before resolve
Expand All @@ -251,16 +251,18 @@ if (typeof document !== 'undefined') {
}
}

importMap = importMap || { imports: {}, scopes: {} };
self.importMap = self.importMap || { imports: {}, scopes: {} };

async function resolve (id, parentUrl) {
parentUrl = parentUrl || pageBaseUrl;

if (importMapPromise)
return importMapPromise
.then(function () {
return resolveImportMap(id, parentUrl, importMap);
return resolveImportMap(id, parentUrl, self.importMap);
});

return resolveImportMap(id, parentUrl, importMap);
return resolveImportMap(id, parentUrl, self.importMap);
}

self.WorkerShim = WorkerShim;
19 changes: 19 additions & 0 deletions src/worker-shims.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { baseUrl as pageBaseUrl, esModuleShimsSrc, createBlob } from './common.js';

export class WorkerShim {
constructor(aURL, options = {type: 'classic'}) {
if (options.type !== 'module')
return new Worker(aURL, options);

if (!esModuleShimsSrc)
throw new Error('es-module-shims.js must be loaded with a script tag for WorkerShim support.');

const workerScriptUrl = createBlob(
`importScripts('${esModuleShimsSrc}'); self.importMap = ${JSON.stringify(options.importMap || {})}; importShim('${new URL(aURL, pageBaseUrl).href}')`);

const workerOptions = {...options};
workerOptions.type = 'classic';

return new Worker(workerScriptUrl, workerOptions);
}
}
2 changes: 1 addition & 1 deletion test/browser-modules.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,4 @@ suite('wasm', () => {
const m = await importShim('/test/fixtures/wasm/example.wasm');
assert.equal(m.exampleExport(1), 2);
});
});
});
1 change: 1 addition & 0 deletions test/fixtures/worker/clasic-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
self.postMessage('classic');
3 changes: 3 additions & 0 deletions test/fixtures/worker/module-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import test from "test";

self.postMessage(test);
52 changes: 52 additions & 0 deletions test/test-worker.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!doctype html>
<link rel="stylesheet" type="text/css" href="../node_modules/mocha/mocha.css"/>
<script src="../node_modules/mocha/mocha.js"></script>
<script type="importmap-shim">
{
"imports": {
"test": "/test/fixtures/es-modules/es6-file.js",
"test/": "/test/fixtures/",
"example": "/test/fixtures/wasm/example.js"
},
"scopes": {
"/": {
"test-dep": "/test/fixtures/test-dep.js"
}
}
}
</script>
<script type="text/javascript" src="../dist/es-module-shims.js"></script>
<script type="module">
mocha.setup('tdd');
mocha.allowUncaught();

self.baseURL = location.href.substr(0, location.href.lastIndexOf('/') + 1);
self.rootURL = location.href.substr(0, location.href.length - 'test/test-worker.html'.length);
self.assert = function (val) {
equal(!!val, true);
};
assert.equal = equal;
assert.ok = assert;
function equal (a, b) {
if (a !== b)
throw new Error('Expected "' + a + '" to be "' + b + '"');
}
self.fail = function (msg) {
throw new Error(msg);
};

const suites = ['worker'];
function runNextSuite () {
mocha.suite.suites.shift();
const suite = suites.shift();
if (suite)
importShim('./' + suite + '.js')
.then(function () {
mocha.run(runNextSuite);
});
}

runNextSuite();
</script>

<div id="mocha"></div>
Empty file modified test/test.html
100644 → 100755
Empty file.
54 changes: 54 additions & 0 deletions test/worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
suite('Worker', () => {
test(`should create a worker type=classic and then receive a message containing the string 'classic'`, async () => {
const worker = new WorkerShim("./fixtures/worker/clasic-worker.js", {
name: 'classic-worker'
});

const result = await new Promise((resolve, reject) => {
// set a timeout to resolve the promise if the worker doesn't 'respond'
const timeoutId = setTimeout(() => {
resolve(null);
}, 2000);

worker.onmessage = (e) => {
clearTimeout(timeoutId);

resolve(e.data);
};

worker.onerror = (e) => {
clearTimeout(timeoutId);
resolve(null);
}
});

assert.equal(result, 'classic');
});

test('should create worker type=module and then receive a message containing the result of a bare import', async () => {
const worker = new WorkerShim("./fixtures/worker/module-worker.js", {
type: 'module',
name: 'test_import_map',
importMap: self.importMap
});

const result = await new Promise((resolve, reject) => {
// set a timeout to resolve the promise if the worker doesn't 'respond'
const timeoutId = setTimeout(() => {
resolve(null);
}, 2000);

worker.onmessage = (e) => {
clearTimeout(timeoutId);
resolve(e.data);
};

worker.onerror = (e) => {
clearTimeout(timeoutId);
resolve(null);
}
});

assert.equal(result, 4);
});
});