Skip to content

Commit

Permalink
fix --mangle-quoted=false with --minify-syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jun 20, 2022
1 parent f808dab commit 1dd274d
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 6 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@
var foo;((foo||={}).bar||={}).baz=(()=>{var b=(a,o)=>()=>(o||a((o={exports:{}}).exports,o),o.exports);var c=b(f=>{f.foo=123});return c();})();
```

* Fix `--mangle-quoted=false` with `--minify-syntax=true`

If property mangling is active and `--mangle-quoted` is disabled, quoted properties are supposed to be preserved. However, there was a case when this didn't happen if `--minify-syntax` was enabled, since that internally transforms `x['y']` into `x.y` to reduce code size. This issue has been fixed:

```js
// Original code
x.foo = x['bar'] = { foo: y, 'bar': z }

// Old output (with --mangle-props=. --mangle-quoted=false --minify-syntax=true)
x.a = x.b = { a: y, bar: z };

// New output (with --mangle-props=. --mangle-quoted=false --minify-syntax=true)
x.a = x.bar = { a: y, bar: z };
```

Notice how the property `foo` is always used unquoted but the property `bar` is always used quoted, so `foo` should be consistently mangled while `bar` should be consistently not mangled.

## 0.14.46

* Add the ability to override support for individual syntax features ([#2060](https://github.com/evanw/esbuild/issues/2060), [#2290](https://github.com/evanw/esbuild/issues/2290), [#2308](https://github.com/evanw/esbuild/issues/2308))
Expand Down
29 changes: 29 additions & 0 deletions internal/bundler/bundler_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6342,6 +6342,35 @@ func TestMangleNoQuotedProps(t *testing.T) {
})
}

func TestMangleNoQuotedPropsMinifySyntax(t *testing.T) {
loader_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
x['_doNotMangleThis'];
x?.['_doNotMangleThis'];
x[y ? '_doNotMangleThis' : z];
x?.[y ? '_doNotMangleThis' : z];
x[y ? z : '_doNotMangleThis'];
x?.[y ? z : '_doNotMangleThis'];
({ '_doNotMangleThis': x });
(class { '_doNotMangleThis' = x });
var { '_doNotMangleThis': x } = y;
'_doNotMangleThis' in x;
(y ? '_doNotMangleThis' : z) in x;
(y ? z : '_doNotMangleThis') in x;
`,
},
entryPaths: []string{"/entry.js"},
options: config.Options{
Mode: config.ModePassThrough,
AbsOutputDir: "/out",
MangleProps: regexp.MustCompile("_"),
MangleQuoted: false,
MinifySyntax: true,
},
})
}

func TestMangleQuotedProps(t *testing.T) {
loader_suite.expectBundled(t, bundled{
files: map[string]string{
Expand Down
9 changes: 9 additions & 0 deletions internal/bundler/snapshots/snapshots_loader.txt
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,15 @@ var { "_doNotMangleThis": x } = y;
(y ? "_doNotMangleThis" : z) in x;
(y ? z : "_doNotMangleThis") in x;

================================================================================
TestMangleNoQuotedPropsMinifySyntax
---------- /out/entry.js ----------
x._doNotMangleThis, x?._doNotMangleThis, x[y ? "_doNotMangleThis" : z], x?.[y ? "_doNotMangleThis" : z], x[y ? z : "_doNotMangleThis"], x?.[y ? z : "_doNotMangleThis"], class {
_doNotMangleThis = x;
};
var { _doNotMangleThis: x } = y;
"_doNotMangleThis" in x, (y ? "_doNotMangleThis" : z) in x, (y ? z : "_doNotMangleThis") in x;

================================================================================
TestMangleProps
---------- /out/entry1.js ----------
Expand Down
25 changes: 19 additions & 6 deletions internal/js_parser/js_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2321,8 +2321,21 @@ func (p *parser) symbolForMangledProp(name string) js_ast.Ref {
return ref
}

func (p *parser) dotOrMangledPropParse(target js_ast.Expr, name js_lexer.MaybeSubstring, nameLoc logger.Loc, optionalChain js_ast.OptionalChain) js_ast.E {
if p.isMangledProp(name.String) {
type wasOriginallyDotOrIndex uint8

const (
wasOriginallyDot wasOriginallyDotOrIndex = iota
wasOriginallyIndex
)

func (p *parser) dotOrMangledPropParse(
target js_ast.Expr,
name js_lexer.MaybeSubstring,
nameLoc logger.Loc,
optionalChain js_ast.OptionalChain,
original wasOriginallyDotOrIndex,
) js_ast.E {
if (original != wasOriginallyIndex || p.options.mangleQuoted) && p.isMangledProp(name.String) {
return &js_ast.EIndex{
Target: target,
Index: js_ast.Expr{Loc: nameLoc, Data: &js_ast.EMangledProp{Ref: p.storeNameInRef(name)}},
Expand Down Expand Up @@ -3645,7 +3658,7 @@ func (p *parser) parseSuffix(left js_ast.Expr, level js_ast.L, errors *deferredE
name := p.lexer.Identifier
nameLoc := p.lexer.Loc()
p.lexer.Next()
left = js_ast.Expr{Loc: left.Loc, Data: p.dotOrMangledPropParse(left, name, nameLoc, oldOptionalChain)}
left = js_ast.Expr{Loc: left.Loc, Data: p.dotOrMangledPropParse(left, name, nameLoc, oldOptionalChain, wasOriginallyDot)}
}

optionalChain = oldOptionalChain
Expand Down Expand Up @@ -3735,7 +3748,7 @@ func (p *parser) parseSuffix(left js_ast.Expr, level js_ast.L, errors *deferredE
name := p.lexer.Identifier
nameLoc := p.lexer.Loc()
p.lexer.Next()
left = js_ast.Expr{Loc: left.Loc, Data: p.dotOrMangledPropParse(left, name, nameLoc, optionalStart)}
left = js_ast.Expr{Loc: left.Loc, Data: p.dotOrMangledPropParse(left, name, nameLoc, optionalStart, wasOriginallyDot)}
}
}

Expand Down Expand Up @@ -4416,7 +4429,7 @@ func (p *parser) parseJSXTag() (logger.Range, string, js_ast.Expr) {
}

chain += "." + member.String
tag = js_ast.Expr{Loc: loc, Data: p.dotOrMangledPropParse(tag, member, memberRange.Loc, js_ast.OptionalChainNone)}
tag = js_ast.Expr{Loc: loc, Data: p.dotOrMangledPropParse(tag, member, memberRange.Loc, js_ast.OptionalChainNone, wasOriginallyDot)}
tagRange.Len = memberRange.Loc.Start + memberRange.Len - tagRange.Loc.Start
}

Expand Down Expand Up @@ -12748,7 +12761,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
// "a['b']" => "a.b"
if p.options.minifySyntax {
if str, ok := e.Index.Data.(*js_ast.EString); ok && js_lexer.IsIdentifierUTF16(str.Value) {
dot := p.dotOrMangledPropParse(e.Target, js_lexer.MaybeSubstring{String: helpers.UTF16ToString(str.Value)}, e.Index.Loc, e.OptionalChain)
dot := p.dotOrMangledPropParse(e.Target, js_lexer.MaybeSubstring{String: helpers.UTF16ToString(str.Value)}, e.Index.Loc, e.OptionalChain, wasOriginallyIndex)
if isCallTarget {
p.callTarget = dot
}
Expand Down

0 comments on commit 1dd274d

Please sign in to comment.