Skip to content

Commit

Permalink
fix #1407: add "undefined" literals in "--define:"
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Dec 29, 2021
1 parent e321373 commit 54cfb1e
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 5 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,21 @@

## Unreleased

* Treat `--define:foo=undefined` as an undefined literal instead of an identifier ([#1407](https://github.com/evanw/esbuild/issues/1407))

References to the global variable `undefined` are automatically replaced with the literal value for undefined, which appears as `void 0` when printed. This allows for additional optimizations such as collapsing `undefined ?? bar` into just `bar`. However, this substitution was not done for values specified via `--define:`. As a result, esbuild could potentially miss out on certain optimizations in these cases. With this release, it's now possible to use `--define:` to substitute something with an undefined literal:

```js
// Original code
let win = typeof window !== 'undefined' ? window : {}

// Old output (with --define:window=undefined --minify)
let win=typeof undefined!="undefined"?undefined:{};

// New output (with --define:window=undefined --minify)
let win={};
```

* Add the `--drop:debugger` flag ([#1809](https://github.com/evanw/esbuild/issues/1809))

Passing this flag causes all [`debugger;` statements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger) to be removed from the output. This is similar to the `drop_debugger: true` flag available in the popular UglifyJS and Terser JavaScript minifiers.
Expand Down
25 changes: 20 additions & 5 deletions pkg/api/api_impl.go
Expand Up @@ -471,11 +471,26 @@ func validateDefines(
// Allow substituting for an identifier
if js_lexer.IsIdentifier(value) {
if _, ok := js_lexer.Keywords[value]; !ok {
name := value // The closure must close over a variable inside the loop
rawDefines[key] = config.DefineData{
DefineFunc: func(args config.DefineArgs) js_ast.E {
return &js_ast.EIdentifier{Ref: args.FindSymbol(args.Loc, name)}
},
switch value {
case "undefined":
rawDefines[key] = config.DefineData{
DefineFunc: func(config.DefineArgs) js_ast.E { return js_ast.EUndefinedShared },
}
case "NaN":
rawDefines[key] = config.DefineData{
DefineFunc: func(config.DefineArgs) js_ast.E { return &js_ast.ENumber{Value: math.NaN()} },
}
case "Infinity":
rawDefines[key] = config.DefineData{
DefineFunc: func(config.DefineArgs) js_ast.E { return &js_ast.ENumber{Value: math.Inf(1)} },
}
default:
name := value // The closure must close over a variable inside the loop
rawDefines[key] = config.DefineData{
DefineFunc: func(args config.DefineArgs) js_ast.E {
return &js_ast.EIdentifier{Ref: args.FindSymbol(args.Loc, name)}
},
}
}
continue
}
Expand Down
6 changes: 6 additions & 0 deletions scripts/js-api-tests.js
Expand Up @@ -3727,6 +3727,12 @@ let transformTests = {
assert.strictEqual(code, `console.log("production");\n`)
},

async defineBuiltInConstants({ esbuild }) {
const define = { a: 'NaN', b: 'Infinity', c: 'undefined', d: 'something' }
const { code } = await esbuild.transform(`console.log([typeof a, typeof b, typeof c, typeof d])`, { define })
assert.strictEqual(code, `console.log(["number", "number", "undefined", typeof something]);\n`)
},

async defineArray({ esbuild }) {
const define = { 'process.env.NODE_ENV': '[1,2,3]', 'something.else': '[2,3,4]' }
const { code } = await esbuild.transform(`console.log(process.env.NODE_ENV)`, { define })
Expand Down

0 comments on commit 54cfb1e

Please sign in to comment.