Conversation
if(false) {
function test() {}
}
test(); |
|
Hmm. You're right I totally forgot hoisting. Then I guess we cannot entirely skip node traversal in |
|
Not sure. You can walk each statement in unreachable branches and remove by statement type. Remove statements, keep and parse Declarations, enter and remove control statements (if, while, do). While on this you can fix this bug: if(false) {
do { while(false) { for(;;) {
function test() {
require("./file"); // <- not processed by webpack
}
}}} while(false);
}
test(); |
|
What about this: if (false) {
function f() {
return import("./x")
}
}
f()Here is the problem, if |
|
|
Start walking FunctionDeclarations again. It's actually a bug that each are not walked. The code is available on runtime. |
|
@sokra It's even more trickier than I initially though: (function() {
console.log("f", f) // "g" undefined
console.log("g", g) // TypeError
if (false) {
function f() {}
}
})()
(function() {
"use strict"
console.log("f", f) // TypeError
if (false) {
function f() {}
}
})()
(function() {
"use strict";
(function() {
console.log("f", f) // TypeError
if (false) {
function f() {}
}
})()
})()Since the result depends on strict mode, it is really hard to handle. Moreover, strict seems to propagate. |
|
If webpack automatically adds |
Only for ESM, other module types stay in it's original mode. |
|
Oh I tried this and it's actually more simple than expected. Functions doesn't hoist out of the block scope. Only the variable declaration hoists. (function() { function g() {} return g; }()) => [Function]
(function() { if(true) {function g() {}} return g; }()) => [Function]
(function() { if(false) {function g() {}} return g; }()) => undefined
(function() { if(false) {} return g; }()) => g is not definedSo the solution is pretty simple. You don't need to parse any code instead of the dead scope. You can remove all code, but insert if(false) {
while (true) {
function foo() {}
}
}
var x = foo;=> var foo;
var x = foo; |
|
@sokra I'm working on it using strict mode propagation. It's pretty simple. I'll update the PR in few minutes |
|
@sokra It will be a little longer. Here is the algorithm I'm implementing:
Here below is a rough implementation: |
(function() { "use strict"; if(false) { var foo; } return foo; }()) => undefinedSo you need to walk the block in strict mode too to keep VarDeclarations... Only FunctionDeclarations change their behavior in strict mode... (JS is weird) |
6b9d34d to
48a1d44
Compare
|
@sokra Finally I'm done with this. I hope. |
lib/UseStrictPlugin.js
Outdated
| parserInstance.state.module.buildInfo.strict = true; | ||
| parser.state.current.addDependency(dep); | ||
| parser.state.module.buildInfo.strict = true; | ||
| parser.scope.isStrict = true; |
There was a problem hiding this comment.
This seem to be a bit missplaced here. All logic for detecting parser.scope.isStrict should be in Parser. Better remove this and add a call to this.detectStrictMode(statement.body.body); after if(this.hooks.program.call(ast, comments) === undefined) { in Parser.parse.
| isStrict: false, | ||
| definitions: new StackedSetMap(), | ||
| renames: new StackedSetMap() | ||
| }; |
There was a problem hiding this comment.
In HarmonyDetectionsPlugin add a parser.scope.isStrict = true, because all ESM modules are in strict mode automatically.
Webpack does not transpile unreachable branches leaving `import` expressions
in the code. This PR modifies `ConstPlugin` to remove the unreachable branch.
Before:
```js
if (true) { 1 } else { import("a") }
if (false) { import("a") } else { 1 }
true ? 1 : import("a");
false ? import("a") : 1;
```
After:
```js
if (true) { 1 } else {}
if (false) {} else { 1 }
true ? 1 : null;
false ? null : 1;
```
48a1d44 to
c9ff97f
Compare
|
@ooflorent Thanks for your update. I labeled the Pull Request so reviewers will review it again. @sokra Please review the new changes. |
Codecov Report
@@ Coverage Diff @@
## next #6273 +/- ##
==========================================
- Coverage 92.75% 92.74% -0.01%
==========================================
Files 303 303
Lines 14400 14498 +98
Branches 3095 3126 +31
==========================================
+ Hits 13356 13446 +90
- Misses 1044 1052 +8
Continue to review full report at Codecov.
|
sokra
left a comment
There was a problem hiding this comment.
Look good 👍
If you want to go for 100% test coverage, there are 3 items left.
| if(element) stack.push(element); | ||
| break; | ||
| case "AssignmentPattern": | ||
| stack.push(node.left); |
| stack.push(node.body); | ||
| break; | ||
| case "SwitchStatement": | ||
| for(const cs of node.cases) |
| stack.push(node.left); | ||
| stack.push(node.body); | ||
| break; | ||
| case "DoWhileStatement": |
|
@sokra I've added more tests. Now everything should be covered. |
👍 |
|
Thank you for your pull request! The most important CI builds succeeded, we’ll review the pull request soon. |
|
Thanks |

What kind of change does this PR introduce?
bugfix
Did you add tests for your changes?
yes
If relevant, link to documentation update:
n/a
Summary
Webpack does not transpile unreachable branches leaving
importexpressionsin the code. This PR modifies
ConstPluginto remove the unreachable branch.Before:
After:
Does this PR introduce a breaking change?
no
Other information
Fixes #4857.
Fixes #5663.