mirror of https://github.com/webpack/webpack.git
improve parsing to handle non-literal options
This commit is contained in:
parent
e839494ff0
commit
61cc65c58e
|
@ -25,12 +25,10 @@ class WorkerDependency extends ModuleDependency {
|
|||
/**
|
||||
* @param {string} request request
|
||||
* @param {[number, number]} range range
|
||||
* @param {Record<string, any> | undefined} options options
|
||||
*/
|
||||
constructor(request, range, options) {
|
||||
constructor(request, range) {
|
||||
super(request);
|
||||
this.range = range;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,9 +79,7 @@ WorkerDependency.Template = class WorkerDependencyTemplate extends (
|
|||
dep.range[1] - 1,
|
||||
`new URL(/* worker import */ ${RuntimeGlobals.publicPath} + ${
|
||||
RuntimeGlobals.getChunkScriptFilename
|
||||
}(${JSON.stringify(chunk.id)}), ${RuntimeGlobals.baseURI})${
|
||||
dep.options ? `, ${JSON.stringify(dep.options)}` : ""
|
||||
}`
|
||||
}(${JSON.stringify(chunk.id)}), ${RuntimeGlobals.baseURI})`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,12 +14,17 @@ const EnableChunkLoadingPlugin = require("../javascript/EnableChunkLoadingPlugin
|
|||
const { equals } = require("../util/ArrayHelpers");
|
||||
const { contextify } = require("../util/identifier");
|
||||
const EnableWasmLoadingPlugin = require("../wasm/EnableWasmLoadingPlugin");
|
||||
const ConstDependency = require("./ConstDependency");
|
||||
const {
|
||||
harmonySpecifierTag
|
||||
} = require("./HarmonyImportDependencyParserPlugin");
|
||||
const WorkerDependency = require("./WorkerDependency");
|
||||
|
||||
/** @typedef {import("estree").Expression} Expression */
|
||||
/** @typedef {import("estree").ObjectExpression} ObjectExpression */
|
||||
/** @typedef {import("estree").Pattern} Pattern */
|
||||
/** @typedef {import("estree").Property} Property */
|
||||
/** @typedef {import("estree").SpreadElement} SpreadElement */
|
||||
/** @typedef {import("../Compiler")} Compiler */
|
||||
/** @typedef {import("../Entrypoint").EntryOptions} EntryOptions */
|
||||
/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
|
||||
|
@ -73,7 +78,7 @@ class WorkerPlugin {
|
|||
/**
|
||||
* @param {JavascriptParser} parser the parser
|
||||
* @param {Expression} expr expression
|
||||
* @returns {BasicEvaluatedExpression} parsed
|
||||
* @returns {[BasicEvaluatedExpression, [number, number]]} parsed
|
||||
*/
|
||||
const parseModuleUrl = (parser, expr) => {
|
||||
if (
|
||||
|
@ -95,35 +100,55 @@ class WorkerPlugin {
|
|||
) {
|
||||
return;
|
||||
}
|
||||
return parser.evaluateExpression(arg1);
|
||||
const arg1Value = parser.evaluateExpression(arg1);
|
||||
return [arg1Value, [arg1.range[0], arg2.range[1]]];
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {JavascriptParser} parser the parser
|
||||
* @param {Expression} expr expression
|
||||
* @returns {object | undefined} parsed object
|
||||
* @param {ObjectExpression} expr expression
|
||||
* @returns {{ expressions: Record<string, Expression | Pattern>, otherElements: (Property | SpreadElement)[], values: Record<string, any>, spread: boolean, insertType: "comma" | "single", insertLocation: number }} parsed object
|
||||
*/
|
||||
const parseObjectLiteral = (parser, expr) => {
|
||||
if (expr.type !== "ObjectExpression") return;
|
||||
const obj = {};
|
||||
const parseObjectExpression = (parser, expr) => {
|
||||
/** @type {Record<string, any>} */
|
||||
const values = {};
|
||||
/** @type {Record<string, Expression | Pattern>} */
|
||||
const expressions = {};
|
||||
/** @type {(Property | SpreadElement)[]} */
|
||||
const otherElements = [];
|
||||
let spread = false;
|
||||
for (const prop of expr.properties) {
|
||||
if (prop.type === "Property") {
|
||||
if (
|
||||
if (prop.type === "SpreadElement") {
|
||||
spread = true;
|
||||
} else if (
|
||||
prop.type === "Property" &&
|
||||
!prop.method &&
|
||||
!prop.computed &&
|
||||
!prop.shorthand &&
|
||||
prop.key.type === "Identifier" &&
|
||||
!prop.value.type.endsWith("Pattern")
|
||||
prop.key.type === "Identifier"
|
||||
) {
|
||||
expressions[prop.key.name] = prop.value;
|
||||
if (!prop.shorthand && !prop.value.type.endsWith("Pattern")) {
|
||||
const value = parser.evaluateExpression(
|
||||
/** @type {Expression} */ (prop.value)
|
||||
);
|
||||
if (value.isCompileTimeValue())
|
||||
obj[prop.key.name] = value.asCompileTimeValue();
|
||||
values[prop.key.name] = value.asCompileTimeValue();
|
||||
}
|
||||
} else {
|
||||
otherElements.push(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
const insertType = expr.properties.length > 0 ? "comma" : "single";
|
||||
const insertLocation =
|
||||
expr.properties[expr.properties.length - 1].range[1];
|
||||
return {
|
||||
expressions,
|
||||
otherElements,
|
||||
values,
|
||||
spread,
|
||||
insertType,
|
||||
insertLocation
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -139,22 +164,30 @@ class WorkerPlugin {
|
|||
if (expr.arguments.length === 0 || expr.arguments.length > 2)
|
||||
return;
|
||||
const [arg1, arg2] = expr.arguments;
|
||||
/** @type {[number, number]} */
|
||||
const range = [arg1.range[0], (arg2 || arg1).range[1]];
|
||||
const url = parseModuleUrl(parser, arg1);
|
||||
if (!url || !url.isString()) return;
|
||||
let options;
|
||||
if (arg2) {
|
||||
options = parseObjectLiteral(parser, arg2);
|
||||
if (!options) return;
|
||||
// Remove `type: "module"` if present.
|
||||
delete options.type;
|
||||
// If the `options` is now an empty object,
|
||||
// remove it from the final bundle.
|
||||
if (Object.keys(options).length === 0) {
|
||||
options = undefined;
|
||||
}
|
||||
}
|
||||
if (arg1.type === "SpreadElement") return;
|
||||
if (arg2 && arg2.type === "SpreadElement") return;
|
||||
const parsedUrl = parseModuleUrl(parser, arg1);
|
||||
if (!parsedUrl) return;
|
||||
const [url, range] = parsedUrl;
|
||||
if (!url.isString()) return;
|
||||
const {
|
||||
expressions,
|
||||
otherElements,
|
||||
values: options,
|
||||
spread: hasSpreadInOptions,
|
||||
insertType,
|
||||
insertLocation
|
||||
} =
|
||||
arg2 && arg2.type === "ObjectExpression"
|
||||
? parseObjectExpression(parser, arg2)
|
||||
: {
|
||||
expressions: {},
|
||||
otherElements: [],
|
||||
values: {},
|
||||
spread: false,
|
||||
insertType: arg2 ? "spread" : "argument",
|
||||
insertLocation: arg2 ? arg2.range : arg1.range[1]
|
||||
};
|
||||
const {
|
||||
options: importOptions,
|
||||
errors: commentErrors
|
||||
|
@ -225,7 +258,7 @@ class WorkerPlugin {
|
|||
if (
|
||||
!Object.prototype.hasOwnProperty.call(entryOptions, "name") &&
|
||||
options &&
|
||||
options.name
|
||||
typeof options.name === "string"
|
||||
) {
|
||||
entryOptions.name = options.name;
|
||||
}
|
||||
|
@ -245,12 +278,52 @@ class WorkerPlugin {
|
|||
}
|
||||
});
|
||||
block.loc = expr.loc;
|
||||
const dep = new WorkerDependency(url.string, range, options);
|
||||
const dep = new WorkerDependency(url.string, range);
|
||||
dep.loc = expr.loc;
|
||||
block.addDependency(dep);
|
||||
parser.state.module.addBlock(block);
|
||||
parser.walkExpression(expr.callee);
|
||||
if (arg2) parser.walkExpression(arg2);
|
||||
|
||||
if (expressions.type) {
|
||||
const expr = expressions.type;
|
||||
if (options.type !== false) {
|
||||
const dep = new ConstDependency("undefined", expr.range);
|
||||
dep.loc = expr.loc;
|
||||
parser.state.module.addPresentationalDependency(dep);
|
||||
expressions.type = undefined;
|
||||
}
|
||||
} else if (hasSpreadInOptions && insertType === "comma") {
|
||||
const dep = new ConstDependency(
|
||||
",type: undefined",
|
||||
insertLocation
|
||||
);
|
||||
dep.loc = expr.loc;
|
||||
parser.state.module.addPresentationalDependency(dep);
|
||||
} else if (insertType === "spread") {
|
||||
const dep1 = new ConstDependency(
|
||||
"Object.assign({}, ",
|
||||
insertLocation[0]
|
||||
);
|
||||
const dep2 = new ConstDependency(
|
||||
", { type: undefined })",
|
||||
insertLocation[1]
|
||||
);
|
||||
dep1.loc = expr.loc;
|
||||
dep2.loc = expr.loc;
|
||||
parser.state.module.addPresentationalDependency(dep1);
|
||||
parser.state.module.addPresentationalDependency(dep2);
|
||||
}
|
||||
|
||||
for (const key of Object.keys(expressions)) {
|
||||
if (expressions[key]) parser.walkExpression(expressions[key]);
|
||||
}
|
||||
for (const prop of otherElements) {
|
||||
parser.walkProperty(prop);
|
||||
}
|
||||
if (insertType === "spread") {
|
||||
parser.walkExpression(arg2);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
const processItem = item => {
|
||||
|
|
|
@ -2286,9 +2286,14 @@ class JavascriptParser extends Parser {
|
|||
propIndex++
|
||||
) {
|
||||
const prop = expression.properties[propIndex];
|
||||
this.walkProperty(prop);
|
||||
}
|
||||
}
|
||||
|
||||
walkProperty(prop) {
|
||||
if (prop.type === "SpreadElement") {
|
||||
this.walkExpression(prop.argument);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
if (prop.computed) {
|
||||
this.walkExpression(prop.key);
|
||||
|
@ -2301,7 +2306,6 @@ class JavascriptParser extends Parser {
|
|||
this.walkExpression(prop.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walkFunctionExpression(expression) {
|
||||
const wasTopLevel = this.scope.topLevelScope;
|
||||
|
|
|
@ -12,6 +12,48 @@ it("should allow to create a WebWorker", async () => {
|
|||
await worker.terminate();
|
||||
});
|
||||
|
||||
it("should allow to create a WebWorker (multiple options)", async () => {
|
||||
const worker = new Worker(new URL("./worker.js", import.meta.url), {
|
||||
type: "module",
|
||||
name: "worker1"
|
||||
});
|
||||
worker.postMessage("ok");
|
||||
const result = await new Promise(resolve => {
|
||||
worker.onmessage = event => {
|
||||
resolve(event.data);
|
||||
};
|
||||
});
|
||||
expect(result).toBe("data: OK, thanks");
|
||||
await worker.terminate();
|
||||
});
|
||||
|
||||
it("should allow to create a WebWorker (spread type)", 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");
|
||||
await worker.terminate();
|
||||
});
|
||||
|
||||
it("should allow to create a WebWorker (expression)", async () => {
|
||||
const options = { type: "module" };
|
||||
const worker = new Worker(new URL("./worker.js", import.meta.url), options);
|
||||
worker.postMessage("ok");
|
||||
const result = await new Promise(resolve => {
|
||||
worker.onmessage = event => {
|
||||
resolve(event.data);
|
||||
};
|
||||
});
|
||||
expect(result).toBe("data: OK, thanks");
|
||||
await worker.terminate();
|
||||
});
|
||||
|
||||
it("should allow to share chunks", async () => {
|
||||
const promise = import("./module");
|
||||
const script = document.head._children[0];
|
||||
|
|
|
@ -4626,6 +4626,7 @@ declare class JavascriptParser extends Parser {
|
|||
walkArrayExpression(expression?: any): void;
|
||||
walkSpreadElement(expression?: any): void;
|
||||
walkObjectExpression(expression?: any): void;
|
||||
walkProperty(prop?: any): void;
|
||||
walkFunctionExpression(expression?: any): void;
|
||||
walkArrowFunctionExpression(expression?: any): void;
|
||||
walkSequenceExpression(expression: SequenceExpression): void;
|
||||
|
|
Loading…
Reference in New Issue