Skip to content

Commit

Permalink
fix #2697: jsx + spread + computed property crash
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Nov 23, 2022
1 parent ef348a3 commit 89e4520
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

* Fix crash when pretty-printing minified JSX with object spread of object literal with computed property ([#2697](https://github.com/evanw/esbuild/issues/2697))

JSX elements are translated to JavaScript function calls and JSX element attributes are translated to properties on a JavaScript object literal. These properties are always either strings (e.g. in `<x y />`, `y` is a string) or an object spread (e.g. in `<x {...y} />`, `y` is an object spread) because JSX doesn't provide syntax for directly passing a computed property as a JSX attribute. However, esbuild's minifier has a rule that tries to inline object spread with an inline object literal in JavaScript. For example, `x = { ...{ y } }` is minified to `x={y}` when minification is enabled. This means that there is a way to generate a non-string non-spread JSX attribute in esbuild's internal representation. One example is with `<x {...{ [y]: z }} />`. When minification is enabled, esbuild's internal representation of this is something like `<x [y]={z} />` due to object spread inlining, which is not valid JSX syntax. If this internal representation is then pretty-printed as JSX using `--minify --jsx=preserve`, esbuild previously crashed when trying to print this invalid syntax. With this release, esbuild will now print `<x {...{[y]:z}}/>` in this scenario instead of crashing.

## 0.15.15

* Remove duplicate CSS rules across files ([#2688](https://github.com/evanw/esbuild/issues/2688))
Expand Down
39 changes: 39 additions & 0 deletions internal/bundler/bundler_default_test.go
Expand Up @@ -6777,3 +6777,42 @@ func TestNonDeterminismIssue2537(t *testing.T) {
},
})
}

// See: https://github.com/evanw/esbuild/issues/2697
func TestMinifiedJSXPreserveWithObjectSpread(t *testing.T) {
loader_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.jsx": `
const obj = {
before,
...{ [key]: value },
...{ key: value },
after,
};
<Foo
before
{...{ [key]: value }}
{...{ key: value }}
after
/>;
<Bar
{...{
a,
[b]: c,
...d,
e,
}}
/>;
`,
},
entryPaths: []string{"/entry.jsx"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputFile: "/out.js",
MinifySyntax: true,
JSX: config.JSXOptions{
Preserve: true,
},
},
})
}
23 changes: 23 additions & 0 deletions internal/bundler/snapshots/snapshots_loader.txt
Expand Up @@ -922,6 +922,29 @@ x.a, x?.a, x[y ? "a" : z], x?.[y ? "a" : z], x[y ? z : "a"], x?.[y ? z : "a"], x
var { a: x } = y, { ["a"]: x } = y, { [(z, "a")]: x } = y;
"a" in x, (y ? "a" : z) in x, (y ? z : "a") in x, y, "a" in x;

================================================================================
TestMinifiedJSXPreserveWithObjectSpread
---------- /out.js ----------
// entry.jsx
var obj = {
before,
[key]: value,
key: value,
after
};
<Foo
before
{...{ [key]: value }}
key={value}
after
/>;
<Bar
a={a}
{...{ [b]: c }}
{...d}
e={e}
/>;

================================================================================
TestMinifyIdentifiersImportPathFrequencyAnalysis
---------- /out/import.js ----------
Expand Down
15 changes: 13 additions & 2 deletions internal/js_printer/js_printer.go
Expand Up @@ -1619,9 +1619,20 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla
name := p.mangledPropName(mangled.Ref)
p.addSourceMappingForName(property.Key.Loc, name, mangled.Ref)
p.printIdentifier(name)
} else {
} else if str, ok := property.Key.Data.(*js_ast.EString); ok {
p.addSourceMapping(property.Key.Loc)
p.print(helpers.UTF16ToString(property.Key.Data.(*js_ast.EString).Value))
p.print(helpers.UTF16ToString(str.Value))
} else {
p.print("{...{")
p.printSpace()
p.print("[")
p.printExpr(property.Key, js_ast.LComma, 0)
p.print("]:")
p.printSpace()
p.printExpr(property.ValueOrNil, js_ast.LComma, 0)
p.printSpace()
p.print("}}")
continue
}

// Special-case string values
Expand Down

0 comments on commit 89e4520

Please sign in to comment.