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 {string} request request
|
||||||
* @param {[number, number]} range range
|
* @param {[number, number]} range range
|
||||||
* @param {Record<string, any> | undefined} options options
|
|
||||||
*/
|
*/
|
||||||
constructor(request, range, options) {
|
constructor(request, range) {
|
||||||
super(request);
|
super(request);
|
||||||
this.range = range;
|
this.range = range;
|
||||||
this.options = options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,9 +79,7 @@ WorkerDependency.Template = class WorkerDependencyTemplate extends (
|
||||||
dep.range[1] - 1,
|
dep.range[1] - 1,
|
||||||
`new URL(/* worker import */ ${RuntimeGlobals.publicPath} + ${
|
`new URL(/* worker import */ ${RuntimeGlobals.publicPath} + ${
|
||||||
RuntimeGlobals.getChunkScriptFilename
|
RuntimeGlobals.getChunkScriptFilename
|
||||||
}(${JSON.stringify(chunk.id)}), ${RuntimeGlobals.baseURI})${
|
}(${JSON.stringify(chunk.id)}), ${RuntimeGlobals.baseURI})`
|
||||||
dep.options ? `, ${JSON.stringify(dep.options)}` : ""
|
|
||||||
}`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,12 +14,17 @@ const EnableChunkLoadingPlugin = require("../javascript/EnableChunkLoadingPlugin
|
||||||
const { equals } = require("../util/ArrayHelpers");
|
const { equals } = require("../util/ArrayHelpers");
|
||||||
const { contextify } = require("../util/identifier");
|
const { contextify } = require("../util/identifier");
|
||||||
const EnableWasmLoadingPlugin = require("../wasm/EnableWasmLoadingPlugin");
|
const EnableWasmLoadingPlugin = require("../wasm/EnableWasmLoadingPlugin");
|
||||||
|
const ConstDependency = require("./ConstDependency");
|
||||||
const {
|
const {
|
||||||
harmonySpecifierTag
|
harmonySpecifierTag
|
||||||
} = require("./HarmonyImportDependencyParserPlugin");
|
} = require("./HarmonyImportDependencyParserPlugin");
|
||||||
const WorkerDependency = require("./WorkerDependency");
|
const WorkerDependency = require("./WorkerDependency");
|
||||||
|
|
||||||
/** @typedef {import("estree").Expression} Expression */
|
/** @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("../Compiler")} Compiler */
|
||||||
/** @typedef {import("../Entrypoint").EntryOptions} EntryOptions */
|
/** @typedef {import("../Entrypoint").EntryOptions} EntryOptions */
|
||||||
/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
|
/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
|
||||||
|
@ -73,7 +78,7 @@ class WorkerPlugin {
|
||||||
/**
|
/**
|
||||||
* @param {JavascriptParser} parser the parser
|
* @param {JavascriptParser} parser the parser
|
||||||
* @param {Expression} expr expression
|
* @param {Expression} expr expression
|
||||||
* @returns {BasicEvaluatedExpression} parsed
|
* @returns {[BasicEvaluatedExpression, [number, number]]} parsed
|
||||||
*/
|
*/
|
||||||
const parseModuleUrl = (parser, expr) => {
|
const parseModuleUrl = (parser, expr) => {
|
||||||
if (
|
if (
|
||||||
|
@ -95,35 +100,55 @@ class WorkerPlugin {
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return parser.evaluateExpression(arg1);
|
const arg1Value = parser.evaluateExpression(arg1);
|
||||||
|
return [arg1Value, [arg1.range[0], arg2.range[1]]];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {JavascriptParser} parser the parser
|
* @param {JavascriptParser} parser the parser
|
||||||
* @param {Expression} expr expression
|
* @param {ObjectExpression} expr expression
|
||||||
* @returns {object | undefined} parsed object
|
* @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) => {
|
const parseObjectExpression = (parser, expr) => {
|
||||||
if (expr.type !== "ObjectExpression") return;
|
/** @type {Record<string, any>} */
|
||||||
const obj = {};
|
const values = {};
|
||||||
|
/** @type {Record<string, Expression | Pattern>} */
|
||||||
|
const expressions = {};
|
||||||
|
/** @type {(Property | SpreadElement)[]} */
|
||||||
|
const otherElements = [];
|
||||||
|
let spread = false;
|
||||||
for (const prop of expr.properties) {
|
for (const prop of expr.properties) {
|
||||||
if (prop.type === "Property") {
|
if (prop.type === "SpreadElement") {
|
||||||
if (
|
spread = true;
|
||||||
!prop.method &&
|
} else if (
|
||||||
!prop.computed &&
|
prop.type === "Property" &&
|
||||||
!prop.shorthand &&
|
!prop.method &&
|
||||||
prop.key.type === "Identifier" &&
|
!prop.computed &&
|
||||||
!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(
|
const value = parser.evaluateExpression(
|
||||||
/** @type {Expression} */ (prop.value)
|
/** @type {Expression} */ (prop.value)
|
||||||
);
|
);
|
||||||
if (value.isCompileTimeValue())
|
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)
|
if (expr.arguments.length === 0 || expr.arguments.length > 2)
|
||||||
return;
|
return;
|
||||||
const [arg1, arg2] = expr.arguments;
|
const [arg1, arg2] = expr.arguments;
|
||||||
/** @type {[number, number]} */
|
if (arg1.type === "SpreadElement") return;
|
||||||
const range = [arg1.range[0], (arg2 || arg1).range[1]];
|
if (arg2 && arg2.type === "SpreadElement") return;
|
||||||
const url = parseModuleUrl(parser, arg1);
|
const parsedUrl = parseModuleUrl(parser, arg1);
|
||||||
if (!url || !url.isString()) return;
|
if (!parsedUrl) return;
|
||||||
let options;
|
const [url, range] = parsedUrl;
|
||||||
if (arg2) {
|
if (!url.isString()) return;
|
||||||
options = parseObjectLiteral(parser, arg2);
|
const {
|
||||||
if (!options) return;
|
expressions,
|
||||||
// Remove `type: "module"` if present.
|
otherElements,
|
||||||
delete options.type;
|
values: options,
|
||||||
// If the `options` is now an empty object,
|
spread: hasSpreadInOptions,
|
||||||
// remove it from the final bundle.
|
insertType,
|
||||||
if (Object.keys(options).length === 0) {
|
insertLocation
|
||||||
options = undefined;
|
} =
|
||||||
}
|
arg2 && arg2.type === "ObjectExpression"
|
||||||
}
|
? parseObjectExpression(parser, arg2)
|
||||||
|
: {
|
||||||
|
expressions: {},
|
||||||
|
otherElements: [],
|
||||||
|
values: {},
|
||||||
|
spread: false,
|
||||||
|
insertType: arg2 ? "spread" : "argument",
|
||||||
|
insertLocation: arg2 ? arg2.range : arg1.range[1]
|
||||||
|
};
|
||||||
const {
|
const {
|
||||||
options: importOptions,
|
options: importOptions,
|
||||||
errors: commentErrors
|
errors: commentErrors
|
||||||
|
@ -225,7 +258,7 @@ class WorkerPlugin {
|
||||||
if (
|
if (
|
||||||
!Object.prototype.hasOwnProperty.call(entryOptions, "name") &&
|
!Object.prototype.hasOwnProperty.call(entryOptions, "name") &&
|
||||||
options &&
|
options &&
|
||||||
options.name
|
typeof options.name === "string"
|
||||||
) {
|
) {
|
||||||
entryOptions.name = options.name;
|
entryOptions.name = options.name;
|
||||||
}
|
}
|
||||||
|
@ -245,12 +278,52 @@ class WorkerPlugin {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
block.loc = expr.loc;
|
block.loc = expr.loc;
|
||||||
const dep = new WorkerDependency(url.string, range, options);
|
const dep = new WorkerDependency(url.string, range);
|
||||||
dep.loc = expr.loc;
|
dep.loc = expr.loc;
|
||||||
block.addDependency(dep);
|
block.addDependency(dep);
|
||||||
parser.state.module.addBlock(block);
|
parser.state.module.addBlock(block);
|
||||||
parser.walkExpression(expr.callee);
|
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;
|
return true;
|
||||||
};
|
};
|
||||||
const processItem = item => {
|
const processItem = item => {
|
||||||
|
|
|
@ -2286,20 +2286,24 @@ class JavascriptParser extends Parser {
|
||||||
propIndex++
|
propIndex++
|
||||||
) {
|
) {
|
||||||
const prop = expression.properties[propIndex];
|
const prop = expression.properties[propIndex];
|
||||||
if (prop.type === "SpreadElement") {
|
this.walkProperty(prop);
|
||||||
this.walkExpression(prop.argument);
|
}
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
if (prop.computed) {
|
walkProperty(prop) {
|
||||||
this.walkExpression(prop.key);
|
if (prop.type === "SpreadElement") {
|
||||||
}
|
this.walkExpression(prop.argument);
|
||||||
if (prop.shorthand && prop.value && prop.value.type === "Identifier") {
|
return;
|
||||||
this.scope.inShorthand = prop.value.name;
|
}
|
||||||
this.walkIdentifier(prop.value);
|
if (prop.computed) {
|
||||||
this.scope.inShorthand = false;
|
this.walkExpression(prop.key);
|
||||||
} else {
|
}
|
||||||
this.walkExpression(prop.value);
|
if (prop.shorthand && prop.value && prop.value.type === "Identifier") {
|
||||||
}
|
this.scope.inShorthand = prop.value.name;
|
||||||
|
this.walkIdentifier(prop.value);
|
||||||
|
this.scope.inShorthand = false;
|
||||||
|
} else {
|
||||||
|
this.walkExpression(prop.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,48 @@ it("should allow to create a WebWorker", async () => {
|
||||||
await worker.terminate();
|
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 () => {
|
it("should allow to share chunks", async () => {
|
||||||
const promise = import("./module");
|
const promise = import("./module");
|
||||||
const script = document.head._children[0];
|
const script = document.head._children[0];
|
||||||
|
|
|
@ -4626,6 +4626,7 @@ declare class JavascriptParser extends Parser {
|
||||||
walkArrayExpression(expression?: any): void;
|
walkArrayExpression(expression?: any): void;
|
||||||
walkSpreadElement(expression?: any): void;
|
walkSpreadElement(expression?: any): void;
|
||||||
walkObjectExpression(expression?: any): void;
|
walkObjectExpression(expression?: any): void;
|
||||||
|
walkProperty(prop?: any): void;
|
||||||
walkFunctionExpression(expression?: any): void;
|
walkFunctionExpression(expression?: any): void;
|
||||||
walkArrowFunctionExpression(expression?: any): void;
|
walkArrowFunctionExpression(expression?: any): void;
|
||||||
walkSequenceExpression(expression: SequenceExpression): void;
|
walkSequenceExpression(expression: SequenceExpression): void;
|
||||||
|
|
Loading…
Reference in New Issue