diff --git a/CHANGELOG.md b/CHANGELOG.md index d1148fd41ea..680b6b62684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 `` 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. diff --git a/pkg/api/api_impl.go b/pkg/api/api_impl.go index 7868f917e01..519951bc692 100644 --- a/pkg/api/api_impl.go +++ b/pkg/api/api_impl.go @@ -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) { @@ -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 { @@ -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, @@ -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, diff --git a/scripts/js-api-tests.js b/scripts/js-api-tests.js index e56bf33476d..3225c414259 100644 --- a/scripts/js-api-tests.js +++ b/scripts/js-api-tests.js @@ -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 }) {