Skip to content

Commit

Permalink
fix #2718: transform keeps process.env.NODE_ENV
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Dec 8, 2022
1 parent a4d7d97 commit 7b45a3b
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,12 @@

## Unreleased

* Fix `process.env.NODE_ENV` substitution when transforming ([#2718](https://github.com/evanw/esbuild/issues/2718))

Version 0.16.0 introduced an unintentional regression that caused `process.env.NODE_ENV` to be automatically substituted with either `"development"` or `"production"` when using esbuild's `transform` API. This substitution is a necessary feature of esbuild's `build` API because the React framework crashes when you bundle it without doing this. But the `transform` API is typically used as part of a larger build pipeline so the benefit of esbuild doing this automatically is not as clear, and esbuild previously didn't do this.

However, version 0.16.0 switched the default value of the `platform` setting for the `transform` API from `neutral` to `browser`, both to align it with esbuild's documentation (which says `browser` is the default value) and because escaping the `</script>` character sequence is now tied to the `browser` platform (see the release notes for version 0.16.0 for details). That accidentally enabled automatic substitution of `process.env.NODE_ENV` because esbuild always did that for code meant for the browser. To fix this regression, esbuild will now only automatically substitute `process.env.NODE_ENV` when using the `build` API.

* Prevent `define` from substituting constants into assignment position ([#2719](https://github.com/evanw/esbuild/issues/2719))

The `define` feature lets you replace certain expressions with constants. For example, you could use it to replace references to the global property reference `window.DEBUG` with `false` at compile time, which can then potentially help esbuild remove unused code from your bundle. It's similar to [DefinePlugin](https://webpack.js.org/plugins/define-plugin/) in Webpack.
Expand Down
7 changes: 4 additions & 3 deletions pkg/api/api_impl.go
Expand Up @@ -554,6 +554,7 @@ func validateDefines(
defines map[string]string,
pureFns []string,
platform config.Platform,
isBuildAPI bool,
minify bool,
drop Drop,
) (*config.ProcessedDefines, []config.InjectedDefine) {
Expand Down Expand Up @@ -619,7 +620,7 @@ func validateDefines(
// that must be handled to avoid all React code crashing instantly. This
// is only done if it's not already defined so that you can override it if
// necessary.
if platform == config.PlatformBrowser {
if isBuildAPI && platform == config.PlatformBrowser {
if _, process := rawDefines["process"]; !process {
if _, processEnv := rawDefines["process.env"]; !processEnv {
if _, processEnvNodeEnv := rawDefines["process.env.NODE_ENV"]; !processEnvNodeEnv {
Expand Down Expand Up @@ -949,7 +950,7 @@ func rebuildImpl(
footerJS, footerCSS := validateBannerOrFooter(log, "footer", buildOpts.Footer)
minify := buildOpts.MinifyWhitespace && buildOpts.MinifyIdentifiers && buildOpts.MinifySyntax
platform := validatePlatform(buildOpts.Platform)
defines, injectedDefines := validateDefines(log, buildOpts.Define, buildOpts.Pure, platform, minify, buildOpts.Drop)
defines, injectedDefines := validateDefines(log, buildOpts.Define, buildOpts.Pure, platform, true /* isBuildAPI */, minify, buildOpts.Drop)
mangleCache := cloneMangleCache(log, buildOpts.MangleCache)
options := config.Options{
TargetFromAPI: targetFromAPI,
Expand Down Expand Up @@ -1491,7 +1492,7 @@ func transformImpl(input string, transformOpts TransformOptions) TransformResult
targetFromAPI, jsFeatures, cssFeatures, targetEnv := validateFeatures(log, transformOpts.Target, transformOpts.Engines)
jsOverrides, jsMask, cssOverrides, cssMask := validateSupported(log, transformOpts.Supported)
platform := validatePlatform(transformOpts.Platform)
defines, injectedDefines := validateDefines(log, transformOpts.Define, transformOpts.Pure, platform, false /* minify */, transformOpts.Drop)
defines, injectedDefines := validateDefines(log, transformOpts.Define, transformOpts.Pure, platform, false /* isBuildAPI */, false /* minify */, transformOpts.Drop)
mangleCache := cloneMangleCache(log, transformOpts.MangleCache)
options := config.Options{
TargetFromAPI: targetFromAPI,
Expand Down
16 changes: 11 additions & 5 deletions scripts/js-api-tests.js
Expand Up @@ -4689,19 +4689,25 @@ let transformTests = {
},

async define({ esbuild }) {
const define = { 'process.env.NODE_ENV': '"production"' }
const define = { 'process.env.NODE_ENV': '"something"' }

const { code: code1 } = await esbuild.transform(`console.log(process.env.NODE_ENV)`, { define })
assert.strictEqual(code1, `console.log("production");\n`)
assert.strictEqual(code1, `console.log("something");\n`)

const { code: code2 } = await esbuild.transform(`console.log(process.env['NODE_ENV'])`, { define })
assert.strictEqual(code2, `console.log("production");\n`)
assert.strictEqual(code2, `console.log("something");\n`)

const { code: code3 } = await esbuild.transform(`console.log(process['env'].NODE_ENV)`, { define })
assert.strictEqual(code3, `console.log("production");\n`)
assert.strictEqual(code3, `console.log("something");\n`)

const { code: code4 } = await esbuild.transform(`console.log(process['env']['NODE_ENV'])`, { define })
assert.strictEqual(code4, `console.log("production");\n`)
assert.strictEqual(code4, `console.log("something");\n`)

const { code: code5 } = await esbuild.transform(`console.log(process.env.NODE_ENV)`, {})
assert.strictEqual(code5, `console.log(process.env.NODE_ENV);\n`)

const { code: code6 } = await esbuild.transform(`console.log(process.env.NODE_ENV)`, { platform: 'browser' })
assert.strictEqual(code6, `console.log(process.env.NODE_ENV);\n`)
},

async defineBuiltInConstants({ esbuild }) {
Expand Down

0 comments on commit 7b45a3b

Please sign in to comment.