Skip to content

Commit 83a2cbf

Browse files
committed
fix #4482: don't inline using declarations
1 parent 308ad74 commit 83a2cbf

3 files changed

Lines changed: 21 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
22

33
## Unreleased
44

5+
* Avoid inlining `using` and `await using` declarations ([#4482](https://github.com/evanw/esbuild/issues/4482))
6+
7+
Previously esbuild's minifier sometimes incorrectly inlined `using` and `await using` declarations into subsequent uses of that declaration, which then fails to dispose of the resource correctly. This bug happened because inlining was done for `let` and `const` declarations by avoiding doing it for `var` declarations, which no longer worked when more declaration types were added. Here's an example:
8+
9+
```js
10+
// Original code
11+
{
12+
using x = new Resource()
13+
x.activate()
14+
}
15+
16+
// Old output (with --minify)
17+
new Resource().activate();
18+
19+
// New output (with --minify)
20+
{using e=new Resource;e.activate()}
21+
```
22+
523
* Fix module evaluation when an error is thrown ([#4461](https://github.com/evanw/esbuild/issues/4461), [#4467](https://github.com/evanw/esbuild/pull/4467))
624

725
If an error is thrown during module evaluation, esbuild previously didn't preserve the state of the module for subsequent module references. This was observable if `import()` or `require()` is used to import a module multiple times. The thrown error is supposed to be thrown by every call to `import()` or `require()`, not just the first. With this release, esbuild will now throw the same error every time you call `import()` or `require()` on a module that throws during its evaluation.

internal/js_parser/js_parser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9190,7 +9190,7 @@ func (p *parser) mangleStmts(stmts []js_ast.Stmt, kind stmtsKind) []js_ast.Stmt
91909190
// should have visited all the uses of "let" and "const" declarations
91919191
// by now since they are scoped to this block which we just finished
91929192
// visiting.
9193-
if prevS, ok := result[len(result)-1].Data.(*js_ast.SLocal); ok && prevS.Kind != js_ast.LocalVar {
9193+
if prevS, ok := result[len(result)-1].Data.(*js_ast.SLocal); ok && (prevS.Kind == js_ast.LocalLet || prevS.Kind == js_ast.LocalConst) {
91949194
last := prevS.Decls[len(prevS.Decls)-1]
91959195

91969196
// The binding must be an identifier that is only used once.

internal/js_parser/js_parser_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5265,6 +5265,8 @@ func TestMangleInlineLocals(t *testing.T) {
52655265
check("var x = 1; return x", "var x = 1;\nreturn x;")
52665266
check("let x = 1; return x", "return 1;")
52675267
check("const x = 1; return x", "return 1;")
5268+
check("using x = 1; return x", "using x = 1;\nreturn x;")
5269+
check("return async () => { await using x = 1; return x }", "return async () => {\n await using x = 1;\n return x;\n};")
52685270

52695271
check("let x = 1; if (false) x++; return x", "return 1;")
52705272
check("let x = 1; if (true) x++; return x", "let x = 1;\nreturn x++, x;")

0 commit comments

Comments
 (0)