From aad881805ee7ef9da2fce07db4c1ab8ced706335 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Wed, 12 Jul 2023 19:25:10 -0400 Subject: [PATCH] fix #3205: panic with `const enum` inside parens --- CHANGELOG.md | 11 +++++++++++ internal/js_parser/ts_parser.go | 5 ++++- internal/js_parser/ts_parser_test.go | 10 ++++++++++ scripts/end-to-end-tests.js | 12 ++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 894275b6ff0..97b66617cfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## Unreleased +* Fix a panic with `const enum` inside parentheses ([#3205](https://github.com/evanw/esbuild/issues/3205)) + + This release fixes an edge case where esbuild could potentially panic if a TypeScript `const enum` statement was used inside of a parenthesized expression and was followed by certain other scope-related statements. Here's a minimal example that triggers this edge case: + + ```ts + (() => { + const enum E { a }; + () => E.a + }) + ``` + * Allow a newline in the middle of TypeScript `export type` statement ([#3225](https://github.com/evanw/esbuild/issues/3225)) Previously esbuild incorrectly rejected the following valid TypeScript code: diff --git a/internal/js_parser/ts_parser.go b/internal/js_parser/ts_parser.go index f076dd6db8b..84d9175be11 100644 --- a/internal/js_parser/ts_parser.go +++ b/internal/js_parser/ts_parser.go @@ -1427,7 +1427,10 @@ func (p *parser) parseTypeScriptEnumStmt(loc logger.Loc, opts parseStmtOpts) js_ if p.scopesInOrderForEnum == nil { p.scopesInOrderForEnum = make(map[logger.Loc][]scopeOrder) } - p.scopesInOrderForEnum[loc] = p.scopesInOrder[scopeIndex:] + + // Make a copy of "scopesInOrder" instead of a slice since the original + // array may be flattened in the future by "popAndFlattenScope" + p.scopesInOrderForEnum[loc] = append([]scopeOrder{}, p.scopesInOrder[scopeIndex:]...) return js_ast.Stmt{Loc: loc, Data: &js_ast.SEnum{ Name: name, diff --git a/internal/js_parser/ts_parser_test.go b/internal/js_parser/ts_parser_test.go index d0b0984d10c..22596ebf121 100644 --- a/internal/js_parser/ts_parser_test.go +++ b/internal/js_parser/ts_parser_test.go @@ -1600,6 +1600,16 @@ var Foo = /* @__PURE__ */ ((Foo) => { return Foo; })(Foo || {}); bar = 0 /* FOO */; +`) + + // https://github.com/evanw/esbuild/issues/3205 + expectPrintedTS(t, "(() => { const enum Foo { A } () => Foo.A })", `() => { + let Foo; + ((Foo) => { + Foo[Foo["A"] = 0] = "A"; + })(Foo || (Foo = {})); + () => 0 /* A */; +}; `) } diff --git a/scripts/end-to-end-tests.js b/scripts/end-to-end-tests.js index 089ab24e047..af35dcca730 100644 --- a/scripts/end-to-end-tests.js +++ b/scripts/end-to-end-tests.js @@ -144,6 +144,18 @@ tests.push( if (foo !== 1 || bar !== 3) throw 'fail' `, }), + + // https://github.com/evanw/esbuild/issues/3205 + test(['entry.ts', '--outfile=node.js'], { + 'entry.ts': ` + // Note: The parentheses are important here + let x = (() => { + const enum E { a = 123 } + return () => E.a + }) + if (x()() !== 123) throw 'fail' + `, + }), ) // Check "tsconfig.json" behavior