Skip to content

Commit

Permalink
fix #2331: use ||= to make global names shorter
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jun 19, 2022
1 parent de70834 commit f808dab
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 4 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## Unreleased

* Make global names more compact when `||=` is available ([#2331](https://github.com/evanw/esbuild/issues/2331))

With this release, the code esbuild generates for the `--global-name=` setting is now slightly shorter when you don't configure esbuild such that the `||=` operator is unsupported (e.g. with `--target=chrome80` or `--supported:logical-assignment=false`):

```js
// Original code
exports.foo = 123

// Old output (with --format=iife --global-name=foo.bar.baz --minify)
var foo=foo||{};foo.bar=foo.bar||{};foo.bar.baz=(()=>{var b=(a,o)=>()=>(o||a((o={exports:{}}).exports,o),o.exports);var c=b(f=>{f.foo=123});return c();})();

// New output (with --format=iife --global-name=foo.bar.baz --minify)
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();})();
```

## 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
30 changes: 28 additions & 2 deletions internal/bundler/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -5131,7 +5131,8 @@ func (c *linkerContext) generateChunkJS(chunks []chunkInfo, chunkIndex int, chun

func (c *linkerContext) generateGlobalNamePrefix() string {
var text string
prefix := c.options.GlobalName[0]
globalName := c.options.GlobalName
prefix := globalName[0]
space := " "
join := ";\n"

Expand All @@ -5140,6 +5141,31 @@ func (c *linkerContext) generateGlobalNamePrefix() string {
join = ";"
}

// Use "||=" to make the code more compact when it's supported
if len(globalName) > 1 && !c.options.UnsupportedJSFeatures.Has(compat.LogicalAssignment) {
if js_printer.CanEscapeIdentifier(prefix, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
if c.options.ASCIIOnly {
prefix = string(js_printer.QuoteIdentifier(nil, prefix, c.options.UnsupportedJSFeatures))
}
text = fmt.Sprintf("var %s%s", prefix, join)
} else {
prefix = fmt.Sprintf("this[%s]", js_printer.QuoteForJSON(prefix, c.options.ASCIIOnly))
}
for _, name := range globalName[1:] {
var dotOrIndex string
if js_printer.CanEscapeIdentifier(name, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
if c.options.ASCIIOnly {
name = string(js_printer.QuoteIdentifier(nil, name, c.options.UnsupportedJSFeatures))
}
dotOrIndex = fmt.Sprintf(".%s", name)
} else {
dotOrIndex = fmt.Sprintf("[%s]", js_printer.QuoteForJSON(name, c.options.ASCIIOnly))
}
prefix = fmt.Sprintf("(%s%s||=%s{})%s", prefix, space, space, dotOrIndex)
}
return fmt.Sprintf("%s%s%s=%s", text, prefix, space, space)
}

if js_printer.CanEscapeIdentifier(prefix, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
if c.options.ASCIIOnly {
prefix = string(js_printer.QuoteIdentifier(nil, prefix, c.options.UnsupportedJSFeatures))
Expand All @@ -5150,7 +5176,7 @@ func (c *linkerContext) generateGlobalNamePrefix() string {
text = fmt.Sprintf("%s%s=%s", prefix, space, space)
}

for _, name := range c.options.GlobalName[1:] {
for _, name := range globalName[1:] {
oldPrefix := prefix
if js_printer.CanEscapeIdentifier(name, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
if c.options.ASCIIOnly {
Expand Down
24 changes: 22 additions & 2 deletions scripts/js-api-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3792,14 +3792,34 @@ let transformTests = {
vm.createContext(globals)
vm.runInContext(code, globals)
assert.strictEqual(globals.π["π 𐀀"].𐀀["𐀀 π"].default, 123)
assert.strictEqual(code.slice(0, code.indexOf('(() => {\n')), `var \\u03C0;
(((\\u03C0 ||= {})["\\u03C0 \\uD800\\uDC00"] ||= {})["\\uD800\\uDC00"] ||= {})["\\uD800\\uDC00 \\u03C0"] = `)
},

async iifeGlobalNameUnicodeNoEscape({ esbuild }) {
const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'π["π 𐀀"].𐀀["𐀀 π"]', charset: 'utf8' })
const globals = {}
vm.createContext(globals)
vm.runInContext(code, globals)
assert.strictEqual(globals.π["π 𐀀"].𐀀["𐀀 π"].default, 123)
assert.strictEqual(code.slice(0, code.indexOf('(() => {\n')), `var π;
(((π ||= {})["π 𐀀"] ||= {})["𐀀"] ||= {})["𐀀 π"] = `)
},

async iifeGlobalNameUnicodeEscapeNoLogicalAssignment({ esbuild }) {
const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'π["π 𐀀"].𐀀["𐀀 π"]', supported: { 'logical-assignment': false } })
const globals = {}
vm.createContext(globals)
vm.runInContext(code, globals)
assert.strictEqual(globals.π["π 𐀀"].𐀀["𐀀 π"].default, 123)
assert.strictEqual(code.slice(0, code.indexOf('(() => {\n')), `var \\u03C0 = \\u03C0 || {};
\\u03C0["\\u03C0 \\uD800\\uDC00"] = \\u03C0["\\u03C0 \\uD800\\uDC00"] || {};
\\u03C0["\\u03C0 \\uD800\\uDC00"]["\\uD800\\uDC00"] = \\u03C0["\\u03C0 \\uD800\\uDC00"]["\\uD800\\uDC00"] || {};
\\u03C0["\\u03C0 \\uD800\\uDC00"]["\\uD800\\uDC00"]["\\uD800\\uDC00 \\u03C0"] = `)
},

async iifeGlobalNameUnicodeNoEscape({ esbuild }) {
const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'π["π 𐀀"].𐀀["𐀀 π"]', charset: 'utf8' })
async iifeGlobalNameUnicodeNoEscapeNoLogicalAssignment({ esbuild }) {
const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'π["π 𐀀"].𐀀["𐀀 π"]', supported: { 'logical-assignment': false }, charset: 'utf8' })
const globals = {}
vm.createContext(globals)
vm.runInContext(code, globals)
Expand Down

0 comments on commit f808dab

Please sign in to comment.