Skip to content

Commit d8cbaa0

Browse files
committed
fix #2139: dangling else minify label edge case
1 parent 2244c09 commit d8cbaa0

File tree

3 files changed

+33
-0
lines changed

3 files changed

+33
-0
lines changed

CHANGELOG.md

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

33
## Unreleased
44

5+
* Fix a minification bug with a double-nested `if` inside a label followed by `else` ([#2139](https://github.com/evanw/esbuild/issues/2139))
6+
7+
This fixes a minification bug that affects the edge case where `if` is followed by `else` and the `if` contains a label that contains a nested `if`. Normally esbuild's AST printer automatically wraps the body of a single-statement `if` in braces to avoid the "dangling else" `if`/`else` ambiguity common to C-like languages (where the `else` accidentally becomes associated with the inner `if` instead of the outer `if`). However, I was missing automatic wrapping of label statements, which did not have test coverage because they are a rarely-used feature. This release fixes the bug:
8+
9+
```js
10+
// Original code
11+
if (a)
12+
b: {
13+
if (c) break b
14+
}
15+
else if (d)
16+
e()
17+
18+
// Old output (with --minify)
19+
if(a)e:if(c)break e;else d&&e();
20+
21+
// New output (with --minify)
22+
if(a){e:if(c)break e}else d&&e();
23+
```
24+
525
* Fix edge case regarding `baseUrl` and `paths` in `tsconfig.json` ([#2119](https://github.com/evanw/esbuild/issues/2119))
626

727
In `tsconfig.json`, TypeScript forbids non-relative values inside `paths` if `baseUrl` is not present, and esbuild does too. However, TypeScript checked this after the entire `tsconfig.json` hierarchy was parsed while esbuild incorrectly checked this immediately when parsing the file containing the `paths` map. This caused incorrect warnings to be generated for `tsconfig.json` files that specify a `baseUrl` value and that inherit a `paths` value from an `extends` clause. Now esbuild will only check for non-relative `paths` values after the entire hierarchy has been parsed to avoid generating incorrect warnings.

internal/js_parser/js_parser_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3192,6 +3192,16 @@ func TestMangleIf(t *testing.T) {
31923192
expectPrintedMangle(t, "(x ? 1 : y) || foo();", "x || y || foo();\n")
31933193
}
31943194

3195+
func TestMangleWrapToAvoidAmbiguousElse(t *testing.T) {
3196+
expectPrintedMangle(t, "if (a) { if (b) return c } else return d", "if (a) {\n if (b)\n return c;\n} else\n return d;\n")
3197+
expectPrintedMangle(t, "if (a) while (1) { if (b) return c } else return d", "if (a) {\n for (; ; )\n if (b)\n return c;\n} else\n return d;\n")
3198+
expectPrintedMangle(t, "if (a) for (;;) { if (b) return c } else return d", "if (a) {\n for (; ; )\n if (b)\n return c;\n} else\n return d;\n")
3199+
expectPrintedMangle(t, "if (a) for (x in y) { if (b) return c } else return d", "if (a) {\n for (x in y)\n if (b)\n return c;\n} else\n return d;\n")
3200+
expectPrintedMangle(t, "if (a) for (x of y) { if (b) return c } else return d", "if (a) {\n for (x of y)\n if (b)\n return c;\n} else\n return d;\n")
3201+
expectPrintedMangle(t, "if (a) with (x) { if (b) return c } else return d", "if (a) {\n with (x)\n if (b)\n return c;\n} else\n return d;\n")
3202+
expectPrintedMangle(t, "if (a) x: { if (b) return c } else return d", "if (a) {\n x:\n if (b)\n return c;\n} else\n return d;\n")
3203+
}
3204+
31953205
func TestMangleOptionalChain(t *testing.T) {
31963206
expectPrintedMangle(t, "let a; return a != null ? a.b : undefined", "let a;\nreturn a?.b;\n")
31973207
expectPrintedMangle(t, "let a; return a != null ? a[b] : undefined", "let a;\nreturn a?.[b];\n")

internal/js_printer/js_printer.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2901,6 +2901,9 @@ func wrapToAvoidAmbiguousElse(s js_ast.S) bool {
29012901
case *js_ast.SWith:
29022902
s = current.Body.Data
29032903

2904+
case *js_ast.SLabel:
2905+
s = current.Stmt.Data
2906+
29042907
default:
29052908
return false
29062909
}

0 commit comments

Comments
 (0)