@surma/rollup-plugin-off-main-thread
Advanced tools
Comparing version 1.4.2 to 2.0.0
149
index.js
@@ -18,2 +18,3 @@ /** | ||
const MagicString = require("magic-string"); | ||
const tippex = require("tippex"); | ||
@@ -28,5 +29,2 @@ const defaultOpts = { | ||
useEval: false, | ||
// A RegExp to find `new Workers()` calls. The second capture group _must_ | ||
// capture the provided file name without the quotes. | ||
workerRegexp: /new Worker\((["'])(.+?)\1(,[^)]+)?\)/g, | ||
// Function name to use instead of AMD’s `define`. | ||
@@ -41,3 +39,3 @@ amdFunctionName: "define", | ||
// Silence the warning about ESM being badly supported in workers. | ||
silenceESMWorkerWarning: false, | ||
silenceESMWorkerWarning: false | ||
}; | ||
@@ -62,6 +60,8 @@ | ||
outputOptions({ format }) { | ||
if ((format === "esm" || format === "es") && !opts.silenceESMWorkerWarning) { | ||
this.warn( | ||
'Very few browsers support ES modules in Workers. If you want to your code to run in all browsers, set `output.format = "amd";`' | ||
); | ||
if (format === "esm" || format === "es") { | ||
if (!opts.silenceESMWorkerWarning) { | ||
this.warn( | ||
'Very few browsers support ES modules in Workers. If you want to your code to run in all browsers, set `output.format = "amd";`' | ||
); | ||
} | ||
// In ESM, we never prepend a loader. | ||
@@ -81,3 +81,4 @@ isEsmOutput = true; | ||
const resolved = await this.resolve(path, importer); | ||
if (!resolved) throw Error(`Cannot find module '${path}' from '${importer}'`); | ||
if (!resolved) | ||
throw Error(`Cannot find module '${path}' from '${importer}'`); | ||
const newId = resolved.id; | ||
@@ -97,58 +98,94 @@ | ||
async transform(code, id) { | ||
// Copy the regexp as they are stateful and this hook is async. | ||
const workerRegexp = new RegExp( | ||
opts.workerRegexp.source, | ||
opts.workerRegexp.flags | ||
); | ||
if (!workerRegexp.test(code)) { | ||
return; | ||
} | ||
// A regexp to find static `new Worker` invocations. | ||
// File part matches one of: | ||
// - '...' | ||
// - "..." | ||
// - `import.meta.url` | ||
// - `new URL('...', import.meta.url) | ||
// - `new URL("...", import.meta.url) | ||
// Also matches optional options param. | ||
const workerRegexp = /new\s+Worker\(\s*('.*?'|".*?"|import\.meta\.url|new\s+URL\(('.*?'|".*?"),\s*import\.meta\.url\))\s*(?:,(.+?))?\)/gs; | ||
const ms = new MagicString(code); | ||
// Reset the regexp | ||
workerRegexp.lastIndex = 0; | ||
while (true) { | ||
const match = workerRegexp.exec(code); | ||
if (!match) { | ||
break; | ||
} | ||
const workerFile = match[2]; | ||
let optionsObject = {}; | ||
// Parse the optional options object | ||
if (match[3] && match[3].length > 0) { | ||
// FIXME: ooooof! | ||
optionsObject = new Function(`return ${match[3].slice(1)};`)(); | ||
} | ||
if (!isEsmOutput) { | ||
delete optionsObject.type; | ||
} | ||
const replacementPromises = []; | ||
if (!new RegExp("^.*/").test(workerFile)) { | ||
this.warn( | ||
`Paths passed to the Worker constructor must be relative or absolute, i.e. start with /, ./ or ../ (just like dynamic import!). Ignoring "${workerFile}".` | ||
// Tippex is performing regex matching under the hood, but automatically ignores comments | ||
// and string contents so it's more reliable on JS syntax. | ||
tippex.match( | ||
code, | ||
workerRegexp, | ||
( | ||
fullMatch, | ||
directWorkerFile, | ||
workerFile = directWorkerFile, | ||
optionsStr = "" | ||
) => { | ||
let workerIdPromise; | ||
if (directWorkerFile === "import.meta.url") { | ||
// Turn the current file into a chunk | ||
workerIdPromise = Promise.resolve(id); | ||
} else { | ||
// Otherwise it's a string literal either directly or in the URL | ||
// constructor, which we treat as the same thing. | ||
// Cut off surrounding quotes. | ||
workerFile = workerFile.slice(1, -1); | ||
if (!/^\.{0,2}\//.test(workerFile)) { | ||
this.warn( | ||
`Paths passed to the Worker constructor must be relative or absolute, i.e. start with /, ./ or ../ (just like dynamic import!). Ignoring "${workerFile}".` | ||
); | ||
return; | ||
} | ||
workerIdPromise = this.resolve(workerFile, id).then(res => res.id); | ||
} | ||
// We need to get this before the `await`, otherwise `lastIndex` | ||
// will be already overridden. | ||
const matchIndex = workerRegexp.lastIndex - fullMatch.length; | ||
const workerParametersStartIndex = matchIndex + "new Worker(".length; | ||
const workerParametersEndIndex = | ||
matchIndex + fullMatch.length - ")".length; | ||
// Parse the optional options object if provided. | ||
optionsStr = optionsStr.trim(); | ||
if (optionsStr) { | ||
let optionsObject = new Function(`return ${optionsStr};`)(); | ||
if (!isEsmOutput) { | ||
delete optionsObject.type; | ||
} | ||
optionsStr = JSON.stringify(optionsObject); | ||
optionsStr = optionsStr === "{}" ? "" : `, ${optionsStr}`; | ||
} | ||
// tippex.match accepts only sync callback, but we want to perform & | ||
// wait for async job here, so we track those promises separately. | ||
replacementPromises.push( | ||
(async () => { | ||
const resolvedWorkerFile = await workerIdPromise; | ||
workerFiles.push(resolvedWorkerFile); | ||
const chunkRefId = this.emitFile({ | ||
id: resolvedWorkerFile, | ||
type: "chunk" | ||
}); | ||
ms.overwrite( | ||
workerParametersStartIndex, | ||
workerParametersEndIndex, | ||
`import.meta.ROLLUP_FILE_URL_${chunkRefId}${optionsStr}` | ||
); | ||
})() | ||
); | ||
continue; | ||
} | ||
); | ||
const resolvedWorkerFile = (await this.resolve(workerFile, id)).id; | ||
workerFiles.push(resolvedWorkerFile); | ||
const chunkRefId = this.emitFile({ | ||
id: resolvedWorkerFile, | ||
type: "chunk" | ||
}); | ||
// No matches found. | ||
if (!replacementPromises.length) { | ||
return; | ||
} | ||
const workerParametersStartIndex = match.index + "new Worker(".length; | ||
const workerParametersEndIndex = | ||
match.index + match[0].length - ")".length; | ||
// Wait for all the scheduled replacements to finish. | ||
await Promise.all(replacementPromises); | ||
ms.overwrite( | ||
workerParametersStartIndex, | ||
workerParametersEndIndex, | ||
`import.meta.ROLLUP_FILE_URL_${chunkRefId}, ${JSON.stringify( | ||
optionsObject | ||
)}` | ||
); | ||
} | ||
return { | ||
@@ -155,0 +192,0 @@ code: ms.toString(), |
{ | ||
"name": "@surma/rollup-plugin-off-main-thread", | ||
"version": "1.4.2", | ||
"version": "2.0.0", | ||
"description": "Use Rollup with workers and ES6 modules today.", | ||
@@ -31,4 +31,5 @@ "main": "index.js", | ||
"ejs": "^2.6.1", | ||
"magic-string": "^0.25.0" | ||
"magic-string": "^0.25.0", | ||
"tippex": "^3.0.0" | ||
} | ||
} |
@@ -48,2 +48,10 @@ # rollup-plugin-off-main-thread | ||
If required, the plugin also supports explicitly module-relative paths: | ||
```js | ||
const worker = new Worker(new URL("./worker.js", import.meta.url), { | ||
type: "module" | ||
}); | ||
``` | ||
### Importing workers as URLs | ||
@@ -50,0 +58,0 @@ |
@@ -44,4 +44,6 @@ /** | ||
"./tests/fixtures/import-worker-url/entry.js", | ||
"./tests/fixtures/import-meta-worker/entry.js", | ||
"./tests/fixtures/import-worker-url-custom-scheme/entry.js", | ||
"./tests/fixtures/assets-in-worker/entry.js" | ||
"./tests/fixtures/assets-in-worker/entry.js", | ||
"./tests/fixtures/url-import-meta-worker/entry.js" | ||
].map(async input => { | ||
@@ -48,0 +50,0 @@ const pathName = path.dirname(input); |
@@ -14,2 +14,8 @@ /** | ||
/* | ||
* This is commented out and should be ignored by the plugin. | ||
* If it doesn't, Rollup and test will fail. | ||
* new Worker("non-existing-worker", { type: "module" }); | ||
*/ | ||
// The type module should get removed for AMD format! | ||
@@ -16,0 +22,0 @@ const w = new Worker("./worker.js", { type: "module" }); |
@@ -26,3 +26,3 @@ /** | ||
window.addEventListener("message", function l(ev) { | ||
if (/^https?:\/\/[^/]+\/a-[0-9a-f]+\.js$/.test(ev.data)) { | ||
if (/^https?:\/\/.+\.js$/.test(ev.data)) { | ||
window.removeEventListener("message", l); | ||
@@ -29,0 +29,0 @@ done(); |
Sorry, the diff of this file is not supported yet
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
87338
89
1466
92
3
+ Addedtippex@^3.0.0
+ Addedtippex@3.0.0(transitive)