compile: do not inline NODE_ENV into runtime-transpiled files when .env autoload is off - #38672
compile: do not inline NODE_ENV into runtime-transpiled files when .env autoload is off#38672robobun wants to merge 1 commit into
Conversation
An executable built with --no-compile-autoload-dotenv (compile.autoloadDotenv: false) switched the runtime transpiler's env behavior to `disable`. Every behavior other than LoadAllWithoutInlining makes defines_from_transform_options add process.env.NODE_ENV, process.env.BUN_ENV and process.browser to the define table, so any file the executable transpiled at runtime had the values from the launch environment (or "development") baked in, unlike `bun run` and unlike the same executable built with autoload on. Keep LoadAllWithoutInlining and express the opt-out through disable_default_env_files, which is how `bun --no-env-file` does it. This applies to the main thread and to workers, which share apply_standalone_runtime_flags.
|
Warning Review limit reached
Next review available in: 7 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
Comment |
|
This PR may be a duplicate of:
🤖 Generated with Claude Code |
|
Status: reproduced on bun 1.4.0 with |
|
Looked at both; neither covers this one.
|
Problem
bun build --compileexecutable built with--no-compile-autoload-dotenv(compile.autoloadDotenv: false) inlinesprocess.env.NODE_ENV,process.env.BUN_ENVandprocess.browserinto every file it transpiles at runtime (anything imported from disk rather than embedded in the binary). The values are the ones in the environment at launch, or"development"when unset, andprocess.browserbecomes the literalfalse.bun runand the same executable built with autoload on (the default) leave these as live reads, so in the autoload-off executable a laterprocess.env.NODE_ENV = ...is invisible to disk-loaded files, and the"development"default gets baked in whenNODE_ENVis unset. The same happens on the main thread and in workers.apply_standalone_runtime_flags(src/bun.js.rs:21) switchesoptions.env.behaviortoDotEnvBehavior::disablewhen the executable carries theDISABLE_DEFAULT_ENV_FILESflag.defines_from_transform_options(src/bundler/options.rs:887) adds theNODE_ENV/BUN_ENV/process.browserdefines for every behavior exceptLoadAllWithoutInlining;disableonly skips the copy of the other env vars.disableis the bundler's--env=disablesetting; whether the bundler should keep inliningNODE_ENVunder it is a separate open question (Bun.build unexpectedly inlines NODE_ENV whenenv: "disable"flag is set #20183, Bun is not honoringenvoption during build but only forNODE_ENV#22820, Bun.build: honorenv: "disable"for process.env.NODE_ENV #35952, with fix(bundler): honor env: disable for process.env.NODE_ENV #35954 and bun build --compile: read NODE_ENV at runtime, set argv[0] to the executable path #32851 proposing changes to the shared function) that this PR does not touch: the runtime inside an executable should not be running with a bundler setting in the first place, and no other runtime path does.entry.tssetsprocess.env.NODE_ENV = "changed"and dynamically imports a file from the cwd that printsprocess.env.NODE_ENV;bun runand the default executable printchanged, the--no-compile-autoload-dotenvexecutable printsdevelopment.Fix
apply_standalone_runtime_flagsalways keepsLoadAllWithoutInliningand only setsdisable_default_env_filesfrom the flag.run_env_loaderpasses that flag toLoader::load, which then skips the default.envfiles; process env vars are still loaded, andNODE_ENV=productionstill switches production mode, exactly as before.bun --no-env-filealready runs with (src/runtime/cli/Arguments.rs:986 sets the same field,bootin run_command.rs sets the same behavior), so an autoload-off executable now behaves likebun --no-env-file <entry>, and identically to an autoload-on executable apart from the.envfiles. For executables built with autoload on (flag clear) the function produces the same values as before, so they are unaffected.--env-file=...at runtime (viaBUN_OPTIONSor--compile-exec-argv) now loads that file, asbun --no-env-file --env-file=...and autoload-on executables do; previously thedisablebranch ignored it. The autoloaded.env*files stay skipped.apply_standalone_runtime_flags, so both are fixed by the one change.test/bundler/bundler_compile_autoload.test.tsgetscompile/AutoloadDotenvDisabledDoesNotInlineNodeEnvCLI, which builds one autoload-off executable whose main thread and worker each assign the three values and then import a package from the cwd'snode_modulesthat reads them back. It fails on bun 1.4.0 (prints["from-launch-env","from-launch-env",false]twice) and passes with this change; the same program with autoload on already passes on 1.4.0, which is the parity the test pins.bundler_compile_autoload.test.ts(24 tests, including the autoload-off.envand worker cases that guard the opt-out itself) passes on the debug build.test/regression/issue/27431.test.tscovers the autoload-off worker configuration on Windows and runs in CI.Background
node_modulesnext to the cwd, dynamic imports of absolute paths) go through the runtime transpiler like they would underbun run.DotEnvBehavioris the transpiler's env setting, shared betweenbun buildand the runtime.LoadAll/prefixinline env vars into the code (bundler--env=inline,--env=PREFIX_*),disableinlines nothing except theNODE_ENV/BUN_ENV/process.browserdefaults (bundler--env=disable, also the struct default), andLoadAllWithoutInliningis the runtime setting: load env files intoprocess.env, inline nothing. The runtime (bun run,bun test, workers, executables) always uses the last one.disable_default_env_filesis a separate option that only controls whetherLoader::loadreads the automatic.env,.env.local,.env.{development,production,test}files. It is what--no-env-fileand bunfigenv = falseset; the executable'sDISABLE_DEFAULT_ENV_FILESflag is the compiled-in form of it.apply_standalone_runtime_flagsis called once per VM in an executable, for the main thread (RunCommand::boot_standalone) and for each worker (WebWorker::start_vm), right beforeconfigure_definesbuilds the define table from these options.bun buildinlines; even with either of them, an autoload-off executable would still run with the bundler setting (still inliningprocess.browserwith fix(bundler): honor env: disable for process.env.NODE_ENV #35954, still ignoring--env-file), so this change is needed independently of them.