allow to pass custom worker syntax

This commit is contained in:
Tobias Koppers 2020-09-25 11:11:09 +02:00
parent 8295202bc8
commit c6a7db6537
8 changed files with 103 additions and 20 deletions

View File

@ -11,6 +11,7 @@ const CommentCompilationWarning = require("../CommentCompilationWarning");
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
const formatLocation = require("../formatLocation");
const EnableChunkLoadingPlugin = require("../javascript/EnableChunkLoadingPlugin");
const { equals } = require("../util/ArrayHelpers");
const {
harmonySpecifierTag
} = require("./HarmonyImportDependencyParserPlugin");
@ -27,6 +28,13 @@ const getUrl = module => {
return pathToFileURL(module.resource).toString();
};
const DEFAULT_SYNTAX = [
"Worker",
"SharedWorker",
"navigator.serviceWorker.register()",
"Worker from worker_threads"
];
class WorkerPlugin {
constructor(chunkLoading) {
this._chunkLoading = chunkLoading;
@ -115,6 +123,9 @@ class WorkerPlugin {
*/
const parserPlugin = (parser, parserOptions) => {
if (parserOptions.worker === false) return;
const options = !Array.isArray(parserOptions.worker)
? ["..."]
: parserOptions.worker;
const handleNewWorker = expr => {
if (expr.arguments.length === 0 || expr.arguments.length > 2)
return;
@ -224,27 +235,40 @@ class WorkerPlugin {
return true;
}
};
parser.hooks.new.for("Worker").tap("WorkerPlugin", handleNewWorker);
parser.hooks.new
.for("SharedWorker")
.tap("WorkerPlugin", handleNewWorker);
parser.hooks.call
.for("navigator.serviceWorker.register")
.tap("WorkerPlugin", handleNewWorker);
parser.hooks.new
.for(harmonySpecifierTag)
.tap("WorkerPlugin", expr => {
const settings = /** @type {HarmonySettings} */ (parser.currentTagData);
if (
!settings ||
settings.source !== "worker_threads" ||
settings.ids.length !== 1 ||
settings.ids[0] !== "Worker"
) {
return;
const processItem = item => {
if (item.endsWith("()")) {
parser.hooks.call
.for(item.slice(0, -2))
.tap("WorkerPlugin", handleNewWorker);
} else {
const match = /^(.+?)(\(\))?\s+from\s+(.+)$/.exec(item);
if (match) {
const ids = match[1].split(".");
const call = match[2];
const source = match[3];
(call ? parser.hooks.call : parser.hooks.new)
.for(harmonySpecifierTag)
.tap("WorkerPlugin", expr => {
const settings = /** @type {HarmonySettings} */ (parser.currentTagData);
if (
!settings ||
settings.source !== source ||
!equals(settings.ids, ids)
) {
return;
}
return handleNewWorker(expr);
});
} else {
parser.hooks.new.for(item).tap("WorkerPlugin", handleNewWorker);
}
return handleNewWorker(expr);
});
}
};
for (const item of options) {
if (item === "...") {
DEFAULT_SYNTAX.forEach(processItem);
} else processItem(item);
}
};
normalModuleFactory.hooks.parser
.for("javascript/auto")

View File

@ -0,0 +1,25 @@
import Worker from "web-worker";
it("should allow to create a WebWorker", async () => {
const worker = new Worker(new URL("./worker.js", import.meta.url), {
type: "module"
});
worker.postMessage("ok");
const result = await new Promise(resolve => {
worker.onmessage = event => {
resolve(event.data);
};
});
expect(result).toBe("data: OK, thanks");
});
it("should allow to share chunks", async () => {
const promise = import("./module");
const script = document.head._children[0];
const src = script.src;
const file = src.slice(src.lastIndexOf("/"));
__non_webpack_require__(`./${file}`);
script.onload();
const { upper } = await promise;
expect(upper("ok")).toBe("OK");
});

View File

@ -0,0 +1,3 @@
export function upper(str) {
return str.toUpperCase();
}

View File

@ -0,0 +1 @@
export default Worker;

View File

@ -0,0 +1,5 @@
module.exports = {
findBundle: function(i, options) {
return ["main.js"];
}
};

View File

@ -0,0 +1,5 @@
var supportsWorker = require("../../../helpers/supportsWorker");
module.exports = function (config) {
return supportsWorker();
};

View File

@ -0,0 +1,16 @@
module.exports = {
output: {
filename: "[name].js"
},
target: "web",
module: {
rules: [
{
test: /\.[cm]?js$/,
parser: {
worker: ["default from web-worker", "..."]
}
}
]
}
};

View File

@ -0,0 +1,4 @@
onmessage = async event => {
const { upper } = await import("./module");
postMessage(`data: ${upper(event.data)}, thanks`);
};