Skip to content

Commit

Permalink
Add support for import maps in Worker; resolves #9 (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
costingeana authored and guybedford committed Jun 24, 2019
1 parent a53d46e commit 998d8dd
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 17 deletions.
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);
});
});

0 comments on commit 998d8dd

Please sign in to comment.