From e54fe5995b60ddea18796bbb11adf7bd7af6d8bb Mon Sep 17 00:00:00 2001 From: dave caruso Date: Thu, 25 Jul 2024 17:29:20 -0700 Subject: [PATCH] fix(bundler): dont tree-shake imported enum if inlined and used (#12826) --- src/bundler/bundle_v2.zig | 6 +- src/js_parser.zig | 10 ++- test/bundler/bundler_edgecase.test.ts | 102 ++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 3 deletions(-) diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index e8534cee92..7ec5274433 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -5797,10 +5797,12 @@ pub const LinkerContext = struct { } if (!found_non_inlined_enum) { - _ = part.symbol_uses.swapRemove(ref); + if (use.count_estimate == 0) { + _ = part.symbol_uses.swapRemove(ref); + } + continue; } } - continue; } } } diff --git a/src/js_parser.zig b/src/js_parser.zig index 30e164ef98..01a73111b5 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -18653,7 +18653,15 @@ fn NewParser_( name_loc, E.Identifier{ .ref = ref }, name, - identifier_opts, + .{ + .assign_target = identifier_opts.assign_target, + .is_call_target = identifier_opts.is_call_target, + .is_delete_target = identifier_opts.is_delete_target, + + // If this expression is used as the target of a call expression, make + // sure the value of "this" is preserved. + .was_originally_identifier = false, + }, ); } } diff --git a/test/bundler/bundler_edgecase.test.ts b/test/bundler/bundler_edgecase.test.ts index 49b992fba1..6bc239e3cd 100644 --- a/test/bundler/bundler_edgecase.test.ts +++ b/test/bundler/bundler_edgecase.test.ts @@ -1665,6 +1665,108 @@ describe("bundler", () => { stdout: "123", }, }); + itBundled("edgecase/TsEnumTreeShakingUseAndInlineClass", { + files: { + "/entry.ts": ` + import { TestEnum } from './enum'; + + class TestClass { + constructor() { + console.log(JSON.stringify(TestEnum)); + } + + testMethod(name: TestEnum) { + return name === TestEnum.A; + } + } + + // This must use wrapper class + console.log(new TestClass()); + // This must inline + console.log(TestClass.prototype.testMethod.toString().includes('TestEnum')); + `, + "/enum.ts": ` + export enum TestEnum { + A, + B, + } + `, + }, + dce: true, + run: { + stdout: ` + {"0":"A","1":"B","A":0,"B":1} + TestClass { + testMethod: [Function: testMethod], + } + false + `, + }, + }); + // this test checks that visit order doesnt matter (inline then use, above is use then inline) + itBundled("edgecase/TsEnumTreeShakingUseAndInlineClass2", { + files: { + "/entry.ts": ` + import { TestEnum } from './enum'; + + class TestClass { + testMethod(name: TestEnum) { + return name === TestEnum.A; + } + + constructor() { + console.log(JSON.stringify(TestEnum)); + } + } + + // This must use wrapper class + console.log(new TestClass()); + // This must inline + console.log(TestClass.prototype.testMethod.toString().includes('TestEnum')); + `, + "/enum.ts": ` + export enum TestEnum { + A, + B, + } + `, + }, + dce: true, + run: { + stdout: ` + {"0":"A","1":"B","A":0,"B":1} + TestClass { + testMethod: [Function: testMethod], + } + false + `, + }, + }); + itBundled("edgecase/TsEnumTreeShakingUseAndInlineNamespace", { + files: { + "/entry.ts": ` + import { TestEnum } from './enum'; + + namespace TestClass { + console.log(JSON.stringify(TestEnum)); + console.log((() => TestEnum.A).toString().includes('TestEnum')); + } + `, + "/enum.ts": ` + export enum TestEnum { + A, + B, + } + `, + }, + dce: true, + run: { + stdout: ` + {"0":"A","1":"B","A":0,"B":1} + false + `, + }, + }); // TODO(@paperdave): test every case of this. I had already tested it manually, but it may break later const requireTranspilationListESM = [