diff --git a/dist/es-module-shims.js b/dist/es-module-shims.js index 4bb34666..d8633a6f 100644 --- a/dist/es-module-shims.js +++ b/dist/es-module-shims.js @@ -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) @@ -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; @@ -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 = {}; @@ -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]; @@ -913,7 +940,7 @@ return load; } - let importMap, importMapPromise; + let importMapPromise; if (typeof document !== 'undefined') { const scripts = document.getElementsByTagName('script'); for (let i = 0; i < scripts.length; i++) { @@ -921,11 +948,11 @@ 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 @@ -938,7 +965,7 @@ } } - importMap = importMap || { imports: {}, scopes: {} }; + self.importMap = self.importMap || { imports: {}, scopes: {} }; async function resolve (id, parentUrl) { parentUrl = parentUrl || baseUrl; @@ -946,10 +973,12 @@ 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; + }()); diff --git a/src/common.js b/src/common.js old mode 100644 new mode 100755 index e3a0edcb..fd71f4a8 --- a/src/common.js +++ b/src/common.js @@ -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) @@ -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 : '"')); -} \ No newline at end of file +} + +export function createBlob (source) { + return URL.createObjectURL(new Blob([source], { type: 'application/javascript' })); +} diff --git a/src/es-module-shims.js b/src/es-module-shims.js old mode 100644 new mode 100755 index fc30a64c..9c41dcbc --- a/src/es-module-shims.js +++ b/src/es-module-shims.js @@ -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 = {}; @@ -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]; @@ -226,7 +226,7 @@ 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++) { @@ -234,11 +234,11 @@ if (typeof document !== 'undefined') { 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 @@ -251,7 +251,7 @@ if (typeof document !== 'undefined') { } } -importMap = importMap || { imports: {}, scopes: {} }; +self.importMap = self.importMap || { imports: {}, scopes: {} }; async function resolve (id, parentUrl) { parentUrl = parentUrl || pageBaseUrl; @@ -259,8 +259,10 @@ async function resolve (id, parentUrl) { 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; diff --git a/src/worker-shims.js b/src/worker-shims.js new file mode 100755 index 00000000..2dcb3ee9 --- /dev/null +++ b/src/worker-shims.js @@ -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); + } +} diff --git a/test/browser-modules.js b/test/browser-modules.js old mode 100644 new mode 100755 index d40161e1..99df29c9 --- a/test/browser-modules.js +++ b/test/browser-modules.js @@ -219,4 +219,4 @@ suite('wasm', () => { const m = await importShim('/test/fixtures/wasm/example.wasm'); assert.equal(m.exampleExport(1), 2); }); -}); \ No newline at end of file +}); diff --git a/test/fixtures/worker/clasic-worker.js b/test/fixtures/worker/clasic-worker.js new file mode 100755 index 00000000..8c537313 --- /dev/null +++ b/test/fixtures/worker/clasic-worker.js @@ -0,0 +1 @@ +self.postMessage('classic'); diff --git a/test/fixtures/worker/module-worker.js b/test/fixtures/worker/module-worker.js new file mode 100755 index 00000000..bae9331c --- /dev/null +++ b/test/fixtures/worker/module-worker.js @@ -0,0 +1,3 @@ +import test from "test"; + +self.postMessage(test); diff --git a/test/test-worker.html b/test/test-worker.html new file mode 100755 index 00000000..57fbb5a1 --- /dev/null +++ b/test/test-worker.html @@ -0,0 +1,52 @@ + + + + + + + +
diff --git a/test/test.html b/test/test.html old mode 100644 new mode 100755 diff --git a/test/worker.js b/test/worker.js new file mode 100755 index 00000000..8ac2d2c6 --- /dev/null +++ b/test/worker.js @@ -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); + }); +});