Visitar URL original
fix(parser): SyntaxError for `new import.defer/source(...)` and SequenceExpression specifiers; clean up test262 known-bugs list by alexander-akait · Pull Request #20917 · webpack/webpack · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dynamic-import-sequence-expression.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"webpack": patch
---

Resolve the static specifier of a dynamic `import()` whose argument is a side-effect-free `SequenceExpression`, e.g. `import((1, 0, "./mod.js"))` is now treated the same as `import("./mod.js")` instead of being rejected as unresolvable.
Comment thread
alexander-akait marked this conversation as resolved.
Resolved
5 changes: 5 additions & 0 deletions .changeset/new-import-call-syntax-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"webpack": patch
---

Reject `new import.defer(...)` and `new import.source(...)` as a parse-time `SyntaxError`, matching the spec — `ImportCall` is a `CallExpression` and is not a valid operand of `new`. Parenthesized forms (`new (import.defer(...))`) remain valid and continue to throw `TypeError` at runtime as before.
49 changes: 49 additions & 0 deletions lib/javascript/JavascriptParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1980,6 +1980,21 @@ class JavascriptParser extends Parser {
}
return this.evaluateExpression(expr.expression);
});
this.hooks.evaluate.for("SequenceExpression").tap(CLASS_NAME, (_expr) => {
const expr = /** @type {SequenceExpression} */ (_expr);
if (!expr.range) return;
let commentsStartPos = /** @type {Range} */ (expr.range)[0];
Comment thread
alexander-akait marked this conversation as resolved.
Resolved
for (let i = 0; i < expr.expressions.length - 1; i++) {
const item = expr.expressions[i];
if (!item.range) return;
if (!this.isPure(item, commentsStartPos)) return;
commentsStartPos = /** @type {Range} */ (item.range)[1];
}
const last = expr.expressions[expr.expressions.length - 1];
const evaluated = this.evaluateExpression(last);
if (!evaluated.isCompileTimeValue()) return;
return evaluated.setRange(/** @type {Range} */ (expr.range));
});
}

/**
Expand Down Expand Up @@ -3849,6 +3864,37 @@ class JavascriptParser extends Parser {
* @param {NewExpression} expression new expression
*/
walkNewExpression(expression) {
// TODO: not a webpack bug — `acorn-import-phases` accepts
// `new import.defer(...)` / `new import.source(...)` even though
// `ImportCall` is a `CallExpression` per spec and is therefore not a
// valid `new` operand. Acorn rejects bare `new import(...)` correctly.
// Drop this block once the upstream plugin (or acorn itself) reports
// the SyntaxError. Parenthesized forms (`new (import.defer(...))`)
// produce the same AST shape, so we look at the source between `new`
// and the callee (with comments stripped) to keep them valid.
if (
expression.callee.type === "ImportExpression" &&
typeof this.state.source === "string"
) {
const newStart = /** @type {Range} */ (expression.range)[0];
const calleeStart = /** @type {Range} */ (expression.callee.range)[0];
const between = this.state.source
.slice(newStart, calleeStart)
.replace(/\/\*[\s\S]*?\*\//g, "")
.replace(/\/\/[^\n]*/g, "");
if (!between.includes("(")) {
const err =
/** @type {SyntaxError & { loc?: { line: number, column: number } }} */
(new SyntaxError("import call cannot be the target of `new`"));
if (expression.loc) {
err.loc = {
line: expression.loc.start.line,
column: expression.loc.start.column
};
}
throw err;
}
}
const result = this.callHooksForExpression(
this.hooks.new,
expression.callee,
Expand Down Expand Up @@ -4850,6 +4896,9 @@ class JavascriptParser extends Parser {

if (Buffer.isBuffer(source)) {
source = source.toString("utf8");
// Keep `state.source` as a string so downstream walkers can read
// the original text without re-decoding the Buffer on every use.
state.source = source;
}

let ast;
Expand Down
Loading
Loading