From 54cfb1eac850d7c9e58ae4bc66453143cc71c0e9 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Wed, 29 Dec 2021 03:53:19 +0000 Subject: [PATCH] fix #1407: add "undefined" literals in "--define:" --- CHANGELOG.md | 15 +++++++++++++++ pkg/api/api_impl.go | 25 ++++++++++++++++++++----- scripts/js-api-tests.js | 6 ++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68a3a4fbca4..f418ae4c31e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/pkg/api/api_impl.go b/pkg/api/api_impl.go index afd4c407bcf..53827c9ccca 100644 --- a/pkg/api/api_impl.go +++ b/pkg/api/api_impl.go @@ -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 } diff --git a/scripts/js-api-tests.js b/scripts/js-api-tests.js index 38bf359c320..16dadd72302 100644 --- a/scripts/js-api-tests.js +++ b/scripts/js-api-tests.js @@ -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 })