Skip to content

Commit

Permalink
fix #1976: parse "super()" with "--mangle-props=."
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Feb 1, 2022
1 parent 4cc9406 commit 3dd6c57
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 1 deletion.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## Unreleased

* Avoid a syntax error due to `--mangle-props=.` and `super()` ([#1976](https://github.com/evanw/esbuild/issues/1976))

This release fixes an issue where passing `--mangle-props=.` (i.e. telling esbuild to mangle every single property) caused a syntax error with code like this:

```js
class Foo {}
class Bar extends Foo {
constructor() {
super();
}
}
```

The problem was that `constructor` was being renamed to another method, which then made it no longer a constructor, which meant that `super()` was now a syntax error. I have added a workaround that avoids renaming any property named `constructor` so that esbuild doesn't generate a syntax error here.

Despite this fix, I highly recommend not using `--mangle-props=.` because your code will almost certainly be broken. You will have to manually add every single property that you don't want mangled to `--reserve-props=` which is an excessive maintenance burden (e.g. reserve `parse` to use `JSON.parse`). Instead I recommend using a common pattern for all properties you intend to be mangled that is unlikely to appear in the APIs you use such as "ends in an underscore." This is an opt-in approach instead of an opt-out approach. It also makes it obvious when reading the code which properties will be mangled and which ones won't be.

## 0.14.16

* Support property name mangling with some TypeScript syntax features
Expand Down
25 changes: 25 additions & 0 deletions internal/bundler/bundler_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5826,3 +5826,28 @@ func TestManglePropsLoweredClassFields(t *testing.T) {
},
})
}

// This tests for a case where "constructor" was being mangled, which made the
// method become a non-constructor, and then "super()" caused a parse error.
// The fix was to prevent the property "constructor" from being mangled.
// See: https://github.com/evanw/esbuild/issues/1976
func TestManglePropsSuperCall(t *testing.T) {
loader_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
class Foo {}
class Bar extends Foo {
constructor() {
super();
}
}
`,
},
entryPaths: []string{"/entry.js"},
options: config.Options{
Mode: config.ModePassThrough,
AbsOutputFile: "/out.js",
MangleProps: regexp.MustCompile("."),
},
})
}
11 changes: 11 additions & 0 deletions internal/bundler/snapshots/snapshots_loader.txt
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,17 @@ TestManglePropsShorthand
---------- /out.js ----------
export let yyyyy = ({ y }) => ({ y });

================================================================================
TestManglePropsSuperCall
---------- /out.js ----------
class Foo {
}
class Bar extends Foo {
constructor() {
super();
}
}

================================================================================
TestManglePropsTypeScriptFeatures
---------- /out/parameter-properties.js ----------
Expand Down
16 changes: 15 additions & 1 deletion internal/js_parser/js_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2480,11 +2480,25 @@ func (p *parser) parsePropertyBinding() js_ast.PropertyBinding {
}
}

// These properties have special semantics in JavaScript. They must not be
// mangled or we could potentially fail to parse valid JavaScript syntax or
// generate invalid JavaScript syntax as output.
//
// This list is only intended to contain properties specific to the JavaScript
// language itself to avoid syntax errors in the generated output. It's not
// intended to contain properties for JavaScript APIs. Those must be provided
// by the user.
var permanentReservedProps = map[string]bool{
"__proto__": true,
"constructor": true,
"prototype": true,
}

func (p *parser) isMangledProp(name string) bool {
if p.options.mangleProps == nil {
return false
}
if p.options.mangleProps.MatchString(name) && name != "__proto__" && (p.options.reserveProps == nil || !p.options.reserveProps.MatchString(name)) {
if p.options.mangleProps.MatchString(name) && !permanentReservedProps[name] && (p.options.reserveProps == nil || !p.options.reserveProps.MatchString(name)) {
return true
}
reservedProps := p.reservedProps
Expand Down

0 comments on commit 3dd6c57

Please sign in to comment.