Skip to content

Commit ea3b3d3

Browse files
committed
fix #2080 fix #2085 fix #2098 fix #2099: var bug
1 parent 9fe4a61 commit ea3b3d3

File tree

4 files changed

+203
-2
lines changed

4 files changed

+203
-2
lines changed

CHANGELOG.md

+39
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,45 @@
22

33
## Unreleased
44

5+
* Fix a tree shaking regression regarding `var` declarations ([#2080](https://github.com/evanw/esbuild/issues/2080), [#2085](https://github.com/evanw/esbuild/pull/2085), [#2098](https://github.com/evanw/esbuild/issues/2098), [#2099](https://github.com/evanw/esbuild/issues/2099))
6+
7+
Version 0.14.8 of esbuild enabled removal of duplicate function declarations when minification is enabled (see [#610](https://github.com/evanw/esbuild/issues/610)):
8+
9+
```js
10+
// Original code
11+
function x() { return 1 }
12+
console.log(x())
13+
function x() { return 2 }
14+
15+
// Output (with --minify-syntax)
16+
console.log(x());
17+
function x() {
18+
return 2;
19+
}
20+
```
21+
22+
This transformation is safe because function declarations are "hoisted" in JavaScript, which means they are all done first before any other code is evaluted. This means the last function declaration will overwrite all previous function declarations with the same name.
23+
24+
However, this introduced an unintentional regression for `var` declarations in which all but the last declaration was dropped if tree-shaking was enabled. This only happens for top-level `var` declarations that re-declare the same variable multiple times. This regression has now been fixed:
25+
26+
```js
27+
// Original code
28+
var x = 1
29+
console.log(x)
30+
var x = 2
31+
32+
// Old output (with --tree-shaking=true)
33+
console.log(x);
34+
var x = 2;
35+
36+
// New output (with --tree-shaking=true)
37+
var x = 1;
38+
console.log(x);
39+
var x = 2;
40+
```
41+
42+
This case now has test coverage.
43+
544
* Add support for parsing "instantiation expressions" from TypeScript 4.7 ([#2038](https://github.com/evanw/esbuild/pull/2038))
645

746
The upcoming version of TypeScript now lets you specify `<...>` type parameters on a JavaScript identifier without using a call expression:

internal/bundler/bundler_dce_test.go

+84
Original file line numberDiff line numberDiff line change
@@ -3016,3 +3016,87 @@ func TestCrossModuleConstantFolding(t *testing.T) {
30163016
},
30173017
})
30183018
}
3019+
3020+
func TestMultipleDeclarationTreeShaking(t *testing.T) {
3021+
dce_suite.expectBundled(t, bundled{
3022+
files: map[string]string{
3023+
"/var2.js": `
3024+
var x = 1
3025+
console.log(x)
3026+
var x = 2
3027+
`,
3028+
"/var3.js": `
3029+
var x = 1
3030+
console.log(x)
3031+
var x = 2
3032+
console.log(x)
3033+
var x = 3
3034+
`,
3035+
"/function2.js": `
3036+
function x() { return 1 }
3037+
console.log(x())
3038+
function x() { return 2 }
3039+
`,
3040+
"/function3.js": `
3041+
function x() { return 1 }
3042+
console.log(x())
3043+
function x() { return 2 }
3044+
console.log(x())
3045+
function x() { return 3 }
3046+
`,
3047+
},
3048+
entryPaths: []string{
3049+
"/var2.js",
3050+
"/var3.js",
3051+
"/function2.js",
3052+
"/function3.js",
3053+
},
3054+
options: config.Options{
3055+
Mode: config.ModeBundle,
3056+
AbsOutputDir: "/out",
3057+
MinifySyntax: false,
3058+
},
3059+
})
3060+
}
3061+
3062+
func TestMultipleDeclarationTreeShakingMinifySyntax(t *testing.T) {
3063+
dce_suite.expectBundled(t, bundled{
3064+
files: map[string]string{
3065+
"/var2.js": `
3066+
var x = 1
3067+
console.log(x)
3068+
var x = 2
3069+
`,
3070+
"/var3.js": `
3071+
var x = 1
3072+
console.log(x)
3073+
var x = 2
3074+
console.log(x)
3075+
var x = 3
3076+
`,
3077+
"/function2.js": `
3078+
function x() { return 1 }
3079+
console.log(x())
3080+
function x() { return 2 }
3081+
`,
3082+
"/function3.js": `
3083+
function x() { return 1 }
3084+
console.log(x())
3085+
function x() { return 2 }
3086+
console.log(x())
3087+
function x() { return 3 }
3088+
`,
3089+
},
3090+
entryPaths: []string{
3091+
"/var2.js",
3092+
"/var3.js",
3093+
"/function2.js",
3094+
"/function3.js",
3095+
},
3096+
options: config.Options{
3097+
Mode: config.ModeBundle,
3098+
AbsOutputDir: "/out",
3099+
MinifySyntax: true,
3100+
},
3101+
})
3102+
}

internal/bundler/snapshots/snapshots_dce.txt

+71
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,77 @@ TestJSONLoaderRemoveUnused
894894
// entry.js
895895
console.log("unused import");
896896

897+
================================================================================
898+
TestMultipleDeclarationTreeShaking
899+
---------- /out/var2.js ----------
900+
// var2.js
901+
var x = 1;
902+
console.log(x);
903+
var x = 2;
904+
905+
---------- /out/var3.js ----------
906+
// var3.js
907+
var x = 1;
908+
console.log(x);
909+
var x = 2;
910+
console.log(x);
911+
var x = 3;
912+
913+
---------- /out/function2.js ----------
914+
// function2.js
915+
function x() {
916+
return 1;
917+
}
918+
console.log(x());
919+
function x() {
920+
return 2;
921+
}
922+
923+
---------- /out/function3.js ----------
924+
// function3.js
925+
function x() {
926+
return 1;
927+
}
928+
console.log(x());
929+
function x() {
930+
return 2;
931+
}
932+
console.log(x());
933+
function x() {
934+
return 3;
935+
}
936+
937+
================================================================================
938+
TestMultipleDeclarationTreeShakingMinifySyntax
939+
---------- /out/var2.js ----------
940+
// var2.js
941+
var x = 1;
942+
console.log(x);
943+
var x = 2;
944+
945+
---------- /out/var3.js ----------
946+
// var3.js
947+
var x = 1;
948+
console.log(x);
949+
var x = 2;
950+
console.log(x);
951+
var x = 3;
952+
953+
---------- /out/function2.js ----------
954+
// function2.js
955+
console.log(x());
956+
function x() {
957+
return 2;
958+
}
959+
960+
---------- /out/function3.js ----------
961+
// function3.js
962+
console.log(x());
963+
console.log(x());
964+
function x() {
965+
return 3;
966+
}
967+
897968
================================================================================
898969
TestPackageJsonSideEffectsArrayGlob
899970
---------- /out.js ----------

internal/js_parser/js_parser.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -15303,8 +15303,15 @@ func (p *parser) toAST(parts []js_ast.Part, hashbang string, directive string) j
1530315303
for partIndex, part := range parts {
1530415304
for _, declared := range part.DeclaredSymbols {
1530515305
if declared.IsTopLevel {
15306-
p.topLevelSymbolToParts[declared.Ref] = append(
15307-
p.topLevelSymbolToParts[declared.Ref], uint32(partIndex))
15306+
// If this symbol was merged, use the symbol at the end of the
15307+
// linked list in the map. This is the case for multiple "var"
15308+
// declarations with the same name, for example.
15309+
ref := declared.Ref
15310+
for p.symbols[ref.InnerIndex].Link != js_ast.InvalidRef {
15311+
ref = p.symbols[ref.InnerIndex].Link
15312+
}
15313+
p.topLevelSymbolToParts[ref] = append(
15314+
p.topLevelSymbolToParts[ref], uint32(partIndex))
1530815315
}
1530915316
}
1531015317
}

0 commit comments

Comments
 (0)