From 978a0809bbf3137a8326927942c555066490b02d Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Fri, 28 Aug 2026 10:39:12 +0200 Subject: [PATCH 1/4] Turbopack: unify `member` and `in` handling (#97985) Followup to #95310 Purely a refactoring. These functions were 99% identical --- .../src/references/mod.rs | 137 ++++++++---------- 1 file changed, 64 insertions(+), 73 deletions(-) diff --git a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs index 0819ea2fe366..702e8d726d88 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs @@ -1270,8 +1270,16 @@ async fn analyze_ecmascript_module_internal( .link_value(take(&mut *prop), ImportAttributes::empty_ref()) .await?; - handle_member(&ast_path, obj, prop, span, &analysis_state, &mut analysis) - .await?; + handle_membership( + &ast_path, + obj, + prop, + span, + &analysis_state, + &mut analysis, + MembershipType::Member, + ) + .await?; } Effect::DestructuredMember { mut obj, @@ -1319,7 +1327,16 @@ async fn analyze_ecmascript_module_internal( .link_value(take(&mut *left), ImportAttributes::empty_ref()) .await?; - handle_in(&ast_path, right, left, &analysis_state, &mut analysis, span).await?; + handle_membership( + &ast_path, + right, + left, + span, + &analysis_state, + &mut analysis, + MembershipType::In, + ) + .await?; } Effect::ImportedBinding { esm_reference_index, @@ -3406,13 +3423,18 @@ fn extract_hot_dep_strings(arg: &JsValue<'_>) -> Option> { None } -async fn handle_member<'a>( +enum MembershipType { + Member, + In, +} +async fn handle_membership<'a>( ast_path: &[AstParentKind], link_obj: impl Future>> + Send + Sync, prop: JsValue<'a>, span: Span, state: &AnalysisState<'a>, analysis: &mut AnalyzeEcmascriptModuleResultBuilder, + ty: MembershipType, ) -> Result<()> { if let Some(prop) = prop.as_str() { let has_member = state.free_var_references_members.contains_key(prop).await?; @@ -3423,83 +3445,52 @@ async fn handle_member<'a>( if has_member && let Some((mut name, false)) = obj_name.clone() { name.0.push(DefinableNameSegmentRef::Name(prop)); - if let Some(value) = state - .compile_time_info_ref - .free_var_references - .get(&name) - .await? - { - // Inline env var - handle_free_var_reference(ast_path, &value, span, state, analysis).await?; - return Ok(()); + match ty { + MembershipType::Member => { + if let Some(value) = state + .compile_time_info_ref + .free_var_references + .get(&name) + .await? + { + // Inline env var + handle_free_var_reference(ast_path, &value, span, state, analysis).await?; + return Ok(()); + } + } + MembershipType::In => { + if state + .compile_time_info_ref + .free_var_references + .get(&name) + .await? + .is_some() + { + analysis.add_code_gen(ConstantValueCodeGen::new( + CompileTimeDefineValue::Bool(true), + ast_path.to_vec().into(), + )); + return Ok(()); + } + } } } if is_prop_cache && let JsValue::WellKnownFunction(WellKnownFunctionKind::Require) = &obj { - analysis.add_code_gen(CjsRequireCacheAccess::new(ast_path.to_vec().into())); - return Ok(()); - } - - if let Some((name, false)) = &obj_name - && matches!( - name.0.as_slice(), - [ - DefinableNameSegmentRef::Name("process"), - DefinableNameSegmentRef::Name("env") - ] - ) - { - // non-inlined env var - analysis.add_runtime_env_var_reference(RcStr::from(prop)); - return Ok(()); - } - } - - Ok(()) -} - -async fn handle_in<'a>( - ast_path: &[AstParentKind], - link_right: impl Future>> + Send + Sync, - left: JsValue<'a>, - state: &AnalysisState<'a>, - analysis: &mut AnalyzeEcmascriptModuleResultBuilder, - _span: Span, -) -> Result<()> { - if let Some(left) = left.as_str() { - let has_member = state.free_var_references_members.contains_key(left).await?; - let is_left_cache = left == "cache"; - - let right = link_right.await?; - let right_name = right.get_definable_name(Some(&state.var_graph)); - - if has_member && let Some((mut name, false)) = right_name.clone() { - name.0.push(DefinableNameSegmentRef::Name(left)); - if state - .compile_time_info_ref - .free_var_references - .get(&name) - .await? - .is_some() - { - analysis.add_code_gen(ConstantValueCodeGen::new( + analysis.add_code_gen::(match ty { + MembershipType::Member => { + CjsRequireCacheAccess::new(ast_path.to_vec().into()).into() + } + MembershipType::In => ConstantValueCodeGen::new( CompileTimeDefineValue::Bool(true), ast_path.to_vec().into(), - )); - return Ok(()); - } - } - - if is_left_cache && let JsValue::WellKnownFunction(WellKnownFunctionKind::Require) = &right - { - analysis.add_code_gen(ConstantValueCodeGen::new( - CompileTimeDefineValue::Bool(true), - ast_path.to_vec().into(), - )); + ) + .into(), + }); return Ok(()); } - if let Some((name, false)) = &right_name + if let Some((name, false)) = &obj_name && matches!( name.0.as_slice(), [ @@ -3509,7 +3500,7 @@ async fn handle_in<'a>( ) { // non-inlined env var - analysis.add_runtime_env_var_reference(RcStr::from(left)); + analysis.add_runtime_env_var_reference(RcStr::from(prop)); return Ok(()); } } From 9a7061053efed6dba303fa5ad4c88b1b672e492f Mon Sep 17 00:00:00 2001 From: Joseph Date: Fri, 28 Aug 2026 11:08:57 +0200 Subject: [PATCH 2/4] docs(examples): document env var handling in the Docker examples (#97968) Improve with-docker example env var usage documentation. Closes: https://github.com/vercel/next.js/issues/97959 --- .../with-docker-export-output/.dockerignore | 6 +- examples/with-docker-export-output/README.md | 34 +++++++++++ examples/with-docker/.dockerignore | 7 ++- examples/with-docker/README.md | 61 +++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) diff --git a/examples/with-docker-export-output/.dockerignore b/examples/with-docker-export-output/.dockerignore index 5565861bf350..96c48026cf5c 100644 --- a/examples/with-docker-export-output/.dockerignore +++ b/examples/with-docker-export-output/.dockerignore @@ -52,7 +52,11 @@ test-results/ *~ *.log -# Environment variables (only commit template files) +# Environment variables - keep credentials out of the build context. +# A static export has no run-time server, so anything left in the context is +# baked into the files served to every visitor. `.env.production` is +# deliberately not listed: use it for non-secret build-time config only. See +# the "Environment Variables" section of README.md. .env .env*.local .env.development diff --git a/examples/with-docker-export-output/README.md b/examples/with-docker-export-output/README.md index dfc77b1bcbe8..ef394869b69c 100644 --- a/examples/with-docker-export-output/README.md +++ b/examples/with-docker-export-output/README.md @@ -182,6 +182,40 @@ Both Dockerfiles support multiple package managers: The Dockerfiles automatically detect which lockfile is present and use the appropriate package manager. +## Environment Variables + +The [`.dockerignore`](./.dockerignore) in this example **excludes `.env`**, so a local development file — which usually holds real credentials — is never copied into the build context or the final image. + +With `output: "export"` there is **no server at run time**: Nginx (or `serve`) only hands out the prebuilt files in `out/`. `docker run -e …` therefore has no effect on the application. Values used by the exported application must be present while `next build` runs. Any value inlined or rendered into `out/` is served to every visitor. + +### Non-secret configuration: use `.env.production` + +`.env.production` is intentionally **not** ignored, so `next build` picks it up: + +```bash +# .env.production +NEXT_PUBLIC_SITE_URL=https://example.com +``` + +### Per-environment values: use a build argument + +To build the same source for several environments, pass the value into the build: + +```dockerfile +ARG NEXT_PUBLIC_SITE_URL +ENV NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL +# ... before the build step +``` + +```bash +docker build \ + --build-arg NEXT_PUBLIC_SITE_URL=https://example.com \ + -t nextjs-export-nginx . +``` + +> [!IMPORTANT] +> A static export cannot keep a secret. `NEXT_PUBLIC_*` values are inlined verbatim into the exported JavaScript, and values without the prefix are read while pages are prerendered, so anything derived from them can end up in the exported HTML. Keep API keys and other credentials out of the build entirely and call them from a separate backend. + ## Deployment This example can be deployed to any container-based platform: diff --git a/examples/with-docker/.dockerignore b/examples/with-docker/.dockerignore index a8f13a8c413b..cdac25c3c0f3 100644 --- a/examples/with-docker/.dockerignore +++ b/examples/with-docker/.dockerignore @@ -49,7 +49,12 @@ playwright.config.* *~ *.log -# Environment variables (only commit template files) +# Environment variables - keep credentials out of the build context. +# Anything left in the context is loaded by `next build` and copied into +# `.next/standalone`, so it ships inside the runner image. `.env.production` is +# deliberately not listed: use it for non-secret build-time config, and pass +# real secrets at run time with `docker run -e`. See the "Environment Variables" +# section of README.md. .env .env*.local .env.development diff --git a/examples/with-docker/README.md b/examples/with-docker/README.md index 7db8a64ccaf6..396f5d71a4f5 100644 --- a/examples/with-docker/README.md +++ b/examples/with-docker/README.md @@ -156,6 +156,67 @@ To switch to Alpine, simply change the `NODE_VERSION` ARG in the Dockerfile to ` > [!IMPORTANT] > **Node.js Version Maintenance**: This Dockerfile uses Node.js 24.13.0-slim, which was the latest LTS version at the time of writing. To ensure security and stay up-to-date, regularly check and update the `NODE_VERSION` ARG in the Dockerfile to the latest Node.js LTS version. Check the latest version at [Nodejs official website](https://nodejs.org/) and browse available Node.js images on [Docker Hub](https://hub.docker.com/_/node). +## Environment Variables + +The [`.dockerignore`](./.dockerignore) in this example **excludes `.env`**, so a local development file — which usually holds real credentials — is never copied into the build context or the final image. + +The consequence is worth knowing up front: a value you rely on from `.env` is `undefined` inside the container, even though the same code works with `next build && next start` locally. Use one of the following instead. + +### Secrets and server-only values: pass them at run time + +Values read on the server at request time (Route Handlers, dynamically rendered Server Components, Server Actions) are read from the environment when the request happens, so they need nothing at build time: + +```bash +docker run -p 3000:3000 -e MY_SECRET=value nextjs-standalone-image +``` + +or in [`compose.yml`](./compose.yml): + +```yaml +services: + nextjs-standalone: + environment: + MY_SECRET: value + # or, to read a file that is not committed: + # env_file: + # - .env.production.local +``` + +Prefer this wherever it works: it keeps one image promotable across environments instead of baking values into a per-environment build. + +### Non-secret build-time configuration: use `.env.production` + +`.env.production` is intentionally **not** ignored, so it is available to `next build` and loaded by the server at run time. Use it only for values that are safe to publish: + +```bash +# .env.production +NEXT_PUBLIC_SITE_URL=https://example.com +``` + +### Public values needed in the client bundle: use a build argument + +`NEXT_PUBLIC_*` values referenced from Client Components are inlined into the JavaScript sent to the browser, so they have to be present while `next build` runs. Add them to the builder stage: + +```dockerfile +ARG NEXT_PUBLIC_SITE_URL +ENV NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL +# ... before the build step +``` + +```bash +docker build \ + --build-arg NEXT_PUBLIC_SITE_URL=https://example.com \ + -t nextjs-standalone-image . +``` + +> [!IMPORTANT] +> Never pass secrets as build arguments. They are recoverable from the image history, and `NEXT_PUBLIC_*` values are sent to the browser by definition. + +> [!IMPORTANT] +> Any env file that **is** present in the build context is also copied into `.next/standalone` by `output: "standalone"`, and this Dockerfile copies that directory wholesale into the runner stage. The file therefore ships inside the image and is readable by anyone who can pull it. Keep credentials out of committed env files and pass them at run time. + +To build a separate image per environment instead, see [`with-docker-multi-env`](../with-docker-multi-env). + ## Deployment This example can be deployed to any container-based platform: From 027205875b6c8ad1befb73b874c7d42b06ac8470 Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Fri, 28 Aug 2026 11:09:32 +0200 Subject: [PATCH 3/4] [ci] Run flake detection and new deploy tests when merged and on backport branches (#97991) The previous detection mechanism special-cased `canary` which lead to large diffs when backport branches ran CI. We stop special casing `canary` reducing complexity and giving us another flake-detecting attempt when the change is merged. That way we can safely enable flake detection on backport branches without having to test large diffs when PRs are merged targetting backport branches. New backport branches will no longer have flake detecton and new deploy test runs disabled. --- .github/workflows/build_and_deploy.yml | 6 ++++- .github/workflows/build_and_test.yml | 10 ------- .github/workflows/build_reusable.yml | 3 +++ scripts/create-release-branch.js | 6 ----- scripts/get-changed-tests.mjs | 10 ++----- scripts/git-info.mjs | 37 +++++++++++++------------- scripts/run-for-change.mjs | 4 +-- 7 files changed, 31 insertions(+), 45 deletions(-) diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index df6ad3fb49d5..6897fd7970c1 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -54,7 +54,11 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - fetch-depth: 1 + # Changing the checked out ref has wide implications: + # Be aware that all git-diff operations in the repo rely on the + # default behavior of GitHub actions (ref on push, merge-commit on PR). + # 2 last commits so that we can create a diff with HEAD~1 + fetch-depth: 2 persist-credentials: false - run: echo "${{ github.event.after }}" - name: Setup node diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 713f5dd0a9fb..2b15ed43c144 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -747,9 +747,7 @@ jobs: test-new-tests-dev: name: Test new and changed tests for flakes (dev) needs: ['optimize-ci', 'changes', 'build-native', 'build-next'] - # test-new-tests-if if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }} - # test-new-tests-end-if strategy: fail-fast: false @@ -776,9 +774,7 @@ jobs: test-new-tests-start: name: Test new and changed tests for flakes (prod) needs: ['optimize-ci', 'changes', 'build-native', 'build-next'] - # test-new-tests-if if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }} - # test-new-tests-end-if strategy: fail-fast: false @@ -814,9 +810,7 @@ jobs: needs: ['optimize-ci', 'changes'] # `docs-only` and `is-release` mirror the cases where `build-and-deploy` # resolves its deploy target to `skipped` and never publishes a tarball. - # test-new-tests-if if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' && needs.changes.outputs.is-release == 'false' }} - # test-new-tests-end-if runs-on: ubuntu-latest # Outer bound only. The script gives up first, so a tarball that never # arrives is reported by it rather than by the runner killing the job. @@ -850,9 +844,7 @@ jobs: test-new-tests-deploy: name: Test new and changed tests when deployed needs: ['optimize-ci', 'changes', 'wait-for-preview-tarball'] - # test-new-tests-if if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }} - # test-new-tests-end-if strategy: fail-fast: false @@ -883,9 +875,7 @@ jobs: test-new-tests-deploy-cache-components: name: Test new and changed tests when deployed (cache components) needs: ['optimize-ci', 'changes', 'wait-for-preview-tarball'] - # test-new-tests-if if: ${{ needs.optimize-ci.outputs.skip == 'false' && needs.changes.outputs.docs-only == 'false' }} - # test-new-tests-end-if strategy: fail-fast: false diff --git a/.github/workflows/build_reusable.yml b/.github/workflows/build_reusable.yml index 24f5336aab47..0d30cdd8c897 100644 --- a/.github/workflows/build_reusable.yml +++ b/.github/workflows/build_reusable.yml @@ -181,6 +181,9 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: + # Changing the checked out ref has wide implications: + # Be aware that all git-diff operations in the repo rely on the + # default behavior of GitHub actions (ref on push, merge-commit on PR). fetch-depth: 25 persist-credentials: false diff --git a/scripts/create-release-branch.js b/scripts/create-release-branch.js index 87418af2cdcc..490e0a2e7874 100644 --- a/scripts/create-release-branch.js +++ b/scripts/create-release-branch.js @@ -117,12 +117,6 @@ async function main() { .replace(`['canary']`, `['${branchName}']`) .replace(/[\s]{1,}('test-new-tests-.+',)/g, '') - buildAndTest = buildAndTest.replace( - /(^[ \t]*)# test-new-tests-if\n(^[ \t]*)if:.*\n(^[ \t]*)# test-new-tests-end-if/gm, - (_, indent1, indent2, indent3) => - `${indent1}# test-new-tests-if\n${indent2}if: false\n${indent3}# test-new-tests-end-if` - ) - await fs.promises.writeFile(buildAndTestPath, buildAndTest) const commitMessage = 'setup release branch' diff --git a/scripts/get-changed-tests.mjs b/scripts/get-changed-tests.mjs index 8ef7cd7d82a3..148be258e23c 100644 --- a/scripts/get-changed-tests.mjs +++ b/scripts/get-changed-tests.mjs @@ -110,7 +110,7 @@ export function getDeployManifestChangedTests( } /** - * Detects changed tests files by comparing the current branch with `origin/canary` + * Detects changed (see {@link getDiffRevision}) tests files. * Returns tests separated by test mode (dev/prod), as well as the corresponding commit hash * that the current branch is pointing to */ @@ -118,12 +118,7 @@ export default async function getChangedTests() { /** @type import('execa').Options */ const EXECA_OPTS = { shell: true } - const { branchName, remoteUrl, commitSha, isCanary } = await getGitInfo() - - if (isCanary) { - console.log(`Skipping flake detection for canary`) - return { devTests: [], prodTests: [], deployTests: [], commitSha } - } + const { branchName, remoteUrl, commitSha } = await getGitInfo() const diffRevision = await getDiffRevision() @@ -138,7 +133,6 @@ export default async function getChangedTests() { { branchName, remoteUrl, - isCanary, commitSha, }, `\ngit diff:\n${changesResult.stderr}\n${changesResult.stdout}` diff --git a/scripts/git-info.mjs b/scripts/git-info.mjs index f9f49ff810fd..d2cfa5ae8f21 100644 --- a/scripts/git-info.mjs +++ b/scripts/git-info.mjs @@ -7,7 +7,7 @@ const exec = promisify(execOrig) /** * Gets git repository information from the environment - * @returns {Promise<{branchName: string, remoteUrl: string, commitSha: string, isCanary: boolean}>} + * @returns {Promise<{branchName: string, remoteUrl: string, commitSha: string}>} */ export async function getGitInfo() { let eventData = {} @@ -34,10 +34,7 @@ export async function getGitInfo() { process.env.GITHUB_SHA || (await exec('git rev-parse HEAD')).stdout.trim() - const isCanary = - branchName === 'canary' && remoteUrl.includes('vercel/next.js') - - return { branchName, remoteUrl, commitSha, isCanary } + return { branchName, remoteUrl, commitSha } } /** @@ -45,19 +42,23 @@ export async function getGitInfo() { * @returns {Promise} The git revision to diff against */ export async function getDiffRevision() { - if ( - process.env.GITHUB_ACTIONS === 'true' && - process.env.GITHUB_EVENT_NAME === 'pull_request' - ) { - // GH Actions for `pull_request` run on the merge commit so HEAD~1: - // 1. includes all changes in the PR - // e.g. in - // A-B-C-main - F - // \ / - // D-E-branch - // GH actions for `branch` runs on F, so a diff for HEAD~1 includes the diff of D and E combined - // 2. Includes all changes of the commit for pushes - return 'HEAD~1' + if (process.env.GITHUB_ACTIONS === 'true') { + const eventName = process.env.GITHUB_EVENT_NAME + switch (eventName) { + // GH Actions for `pull_request` run on the merge commit by default so HEAD~1: + // 1. includes all changes in the PR + // e.g. in + // A-B-C-main - F + // \ / + // D-E-branch + // GH actions for `branch` runs on F, so a diff for HEAD~1 includes the diff of D and E combined + // 2. Includes all changes of the commit for pushes (assuming the push event is from a squash merge) + case 'pull_request': + case 'push': + return 'HEAD~1' + default: + throw new Error(`Unsupported GITHUB_EVENT_NAME: ${eventName}`) + } } else { try { await exec('git remote set-branches --add origin canary') diff --git a/scripts/run-for-change.mjs b/scripts/run-for-change.mjs index 8bbf8ae3ef0b..5f58b84b093d 100644 --- a/scripts/run-for-change.mjs +++ b/scripts/run-for-change.mjs @@ -72,7 +72,7 @@ const CHANGE_ITEM_GROUPS = { } async function main() { - const { branchName, remoteUrl, isCanary } = await getGitInfo() + const { branchName, remoteUrl } = await getGitInfo() const diffRevision = await getDiffRevision() const changesResult = await exec( @@ -82,7 +82,7 @@ async function main() { return { stdout: '' } }) - console.error({ branchName, remoteUrl, isCanary, changesResult }) + console.error({ branchName, remoteUrl, changesResult }) const changedFilesOutput = changesResult.stdout const typeIndex = process.argv.indexOf('--type') From f6c48e199f5e2f5eb5f9fd1c5cc95e9ef98afda1 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:20:02 +0200 Subject: [PATCH 4/4] Turbopack: allow `get_definable_name` to return a list (#97984) Followup to #95310 Now we can correctly track env vars in cases like these were a `JsValue::Alternative` is involved ```js // An alternative (process|unknown).env let p2 if (foo) { p2 = foo } else { p2 = process } console.log(p2.env.FOO8) ``` The inlining logic behaves the same as before: - If `get_definable_name` returns exactly one result, we can inline. - Otherwise, we can't inline (previously this case returned `None`) A bit of trivia why we need that boolean in the return type in the first place (because conceptually it should be possible to just model it via `JsValue::Alternative` anyway: > but why is that a problem? if the value is reassigned, then there should just an alternative, then get_definable_name returns multiple values and it should still not be inlining > > Because module is a `FreeVar`, not a normal `Variable`. > > The assignment is stored separately in `VarGraph`: > > ``` > free_var_ids["module"] -> id > values[id] -> assigned function > ``` > > But later reads still evaluate to: > > `JsValue::FreeVar("module")` > > The linker expands JsValue::Variable, but not JsValue::FreeVar. Therefore there is no JsValue::Alternatives at this call site. > > Instead, `get_definable_name()` detects the graph entry and returns: > > `[Some((["module", TypeOf], true))]` > > That boolean is how reassignment is represented for free variables. Cardinality remains one, so ignoring the boolean at references/mod.rs:4007 incorrectly permits inlining. > > Actual `Alternatives` primarily represent multiple values assigned to tracked variables. For free globals, the original ambient value plus reassignment is represented by `FreeVar` plus `potentially_reassigned`. No perf impact: ``` commit cfc7da3049f91669a1b61a0f7a5edef503cddc53 (HEAD -> canary, origin/canary, origin/HEAD) 393.17s user 31.34s system 722% cpu 58.775 total 391.91s user 35.37s system 746% cpu 57.256 total 389.48s user 30.40s system 770% cpu 54.513 total commit 38f2708659be31538c951b12ba806ef53710f074 (HEAD -> mischnic/get-definable-name-list) 392.86s user 34.00s system 745% cpu 57.221 total 388.10s user 32.72s system 766% cpu 54.912 total 391.84s user 37.82s system 717% cpu 59.865 total ``` --- .../src/analyzer/jsvalue/mod.rs | 123 ++++++++------ .../src/references/cross_module_constants.rs | 4 +- .../src/references/mod.rs | 157 ++++++++++-------- .../env-vars.codegen.snapshot | 4 +- .../env-vars.snapshot | 4 +- .../env-vars.tracing.snapshot | 4 +- .../dynamic-fn-default-rest-spread/input.js | 1 - .../env-vars.codegen.snapshot | 4 +- .../dynamic-fn-default/env-vars.snapshot | 4 +- .../env-vars.tracing.snapshot | 4 +- .../env-var-info/dynamic-fn-default/input.js | 1 - .../env-vars.codegen.snapshot | 2 + .../static-assign-obj/env-vars.snapshot | 2 + .../env-vars.tracing.snapshot | 2 + .../env-var-info/static-assign-obj/input.js | 2 - .../static/env-vars.codegen.snapshot | 2 + .../env-var-info/static/env-vars.snapshot | 2 + .../static/env-vars.tracing.snapshot | 2 + .../references/env-var-info/static/input.js | 9 - .../input/exports-reassign.js | 4 +- .../input/module-reassign.js | 4 +- 21 files changed, 195 insertions(+), 146 deletions(-) diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/jsvalue/mod.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/jsvalue/mod.rs index 5fb9f85795c1..47078f8db413 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/jsvalue/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/jsvalue/mod.rs @@ -8,7 +8,7 @@ use std::{ use anyhow::{Context, Result, bail}; use bumpalo::boxed::Box as BumpBox; use num_bigint::BigInt; -use smallvec::SmallVec; +use smallvec::{SmallVec, smallvec}; use swc_core::ecma::{ast::Id, atoms::Atom}; use turbo_rcstr::{RcStr, rcstr}; use turbopack_core::compile_time_info::{ @@ -1257,6 +1257,8 @@ impl<'a> JsValue<'a> { // Definable name management impl JsValue<'_> { + // Clippy is wrong. It's not the same lifetime + #[allow(mismatched_lifetime_syntaxes)] /// When the value has a user-definable name, return it in segments. Otherwise /// returns None. /// It also returns a boolean whether the variable was potentially reassigned. @@ -1265,65 +1267,76 @@ impl JsValue<'_> { /// - some well-known objects/functions have a user-definable names: ["import"] /// - member calls without arguments also have a user-definable name: ["foo", Call("func")] /// - typeof expressions add `typeof` after the argument's segments: ["foo", "typeof"] - pub fn get_definable_name( - &self, + pub fn get_definable_name<'v>( + &'v self, var_graph: Option<&VarGraph<'_>>, - ) -> Option<(DefinableNameSegmentRefs<'_>, bool)> { - let mut current = self; - let mut segments = SmallVec::new(); - let mut potentially_reassigned = false; - loop { - match current { - JsValue::FreeVar(name) => { - if var_graph.is_some_and(|var_graph| { - var_graph - .free_var_ids - .get(name) - .is_some_and(|id| var_graph.values.contains_key(id)) - }) { - // `foo` was potentially reassigned - potentially_reassigned = true; + ) -> SmallVec<[Option<(DefinableNameSegmentRefs<'_>, bool)>; 1]> { + let inner = |value: &'v JsValue| { + let mut current = value; + let mut segments = SmallVec::new(); + let mut potentially_reassigned = false; + loop { + match current { + JsValue::FreeVar(name) => { + if var_graph.is_some_and(|var_graph| { + var_graph + .free_var_ids + .get(name) + .is_some_and(|id| var_graph.values.contains_key(id)) + }) { + // `foo` was potentially reassigned + potentially_reassigned = true; + } + segments.push(DefinableNameSegmentRef::Name(name)); + break; } - segments.push(DefinableNameSegmentRef::Name(name)); - break; - } - JsValue::Member(_, obj, prop) => { - segments.push(DefinableNameSegmentRef::Name(prop.as_str()?)); - current = obj; - } - JsValue::WellKnownObject(obj) => { - segments.extend( - obj.as_define_name()? - .iter() - .rev() - .copied() - .map(DefinableNameSegmentRef::Name), - ); - break; - } - JsValue::WellKnownFunction(func) => { - segments.extend( - func.as_define_name()? - .iter() - .rev() - .copied() - .map(DefinableNameSegmentRef::Name), - ); - break; - } - JsValue::MemberCall(_, call) if call.args().is_empty() => { - segments.push(DefinableNameSegmentRef::Call(call.prop().as_str()?)); - current = call.obj(); - } - JsValue::TypeOf(_, arg) => { - segments.push(DefinableNameSegmentRef::TypeOf); - current = arg; + JsValue::Member(_, obj, prop) => { + segments.push(DefinableNameSegmentRef::Name(prop.as_str()?)); + current = obj; + } + JsValue::WellKnownObject(obj) => { + segments.extend( + obj.as_define_name()? + .iter() + .rev() + .copied() + .map(DefinableNameSegmentRef::Name), + ); + break; + } + JsValue::WellKnownFunction(func) => { + segments.extend( + func.as_define_name()? + .iter() + .rev() + .copied() + .map(DefinableNameSegmentRef::Name), + ); + break; + } + JsValue::MemberCall(_, call) if call.args().is_empty() => { + let Some(call_prop) = call.prop().as_str() else { + return Default::default(); + }; + segments.push(DefinableNameSegmentRef::Call(call_prop)); + current = call.obj(); + } + JsValue::TypeOf(_, arg) => { + segments.push(DefinableNameSegmentRef::TypeOf); + current = arg; + } + _ => return None, } - _ => return None, } + segments.reverse(); + Some((DefinableNameSegmentRefs(segments), potentially_reassigned)) + }; + + if let JsValue::Alternatives { values, .. } = self { + values.iter().map(inner).collect() + } else { + smallvec![inner(self)] } - segments.reverse(); - Some((DefinableNameSegmentRefs(segments), potentially_reassigned)) } } diff --git a/turbopack/crates/turbopack-ecmascript/src/references/cross_module_constants.rs b/turbopack/crates/turbopack-ecmascript/src/references/cross_module_constants.rs index 1246b3c09157..2b7832ed9386 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/cross_module_constants.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/cross_module_constants.rs @@ -260,8 +260,8 @@ pub async fn get_constants( value.clone_in(arena.get_or_default()), &|value| early_value_visitor(&arena, value), &async |v| { - if let Some((name, _)) = v.get_definable_name(Some(&var_graph)) - && let Some(value) = compile_time_info_ref.defines.get(&name).await? + if let [Some((name, _))] = &*v.get_definable_name(Some(&var_graph)) + && let Some(value) = compile_time_info_ref.defines.get(name).await? { return Ok(( JsValue::from_compile_time_define_value_in( diff --git a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs index 702e8d726d88..666f6da659d0 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs @@ -1299,15 +1299,20 @@ async fn analyze_ecmascript_module_internal( .link_value(take(&mut *obj), ImportAttributes::empty_ref()) .await?; - if let Some((name, false)) = - obj.get_definable_name(Some(&analysis_state.var_graph)) - && matches!( - name.0.as_slice(), - [ - DefinableNameSegmentRef::Name("process"), - DefinableNameSegmentRef::Name("env") - ] - ) + if obj + .get_definable_name(Some(&analysis_state.var_graph)) + .iter() + .flatten() + .any(|(name, reassigned)| { + !reassigned + && matches!( + name.0.as_slice(), + [ + DefinableNameSegmentRef::Name("process"), + DefinableNameSegmentRef::Name("env") + ] + ) + }) { analysis.add_runtime_env_var_reference(RcStr::from(prop)); } @@ -3443,68 +3448,73 @@ async fn handle_membership<'a>( let obj = link_obj.await?; let obj_name = obj.get_definable_name(Some(&state.var_graph)); - if has_member && let Some((mut name, false)) = obj_name.clone() { - name.0.push(DefinableNameSegmentRef::Name(prop)); - match ty { - MembershipType::Member => { - if let Some(value) = state - .compile_time_info_ref - .free_var_references - .get(&name) - .await? - { - // Inline env var - handle_free_var_reference(ast_path, &value, span, state, analysis).await?; - return Ok(()); + if let [obj_name] = &*obj_name { + // Exactly one name. We can potentially inline + if has_member && let Some((mut name, false)) = obj_name.clone() { + name.0.push(DefinableNameSegmentRef::Name(prop)); + match ty { + MembershipType::Member => { + if let Some(value) = state + .compile_time_info_ref + .free_var_references + .get(&name) + .await? + { + // Inline env var + handle_free_var_reference(ast_path, &value, span, state, analysis) + .await?; + return Ok(()); + } } - } - MembershipType::In => { - if state - .compile_time_info_ref - .free_var_references - .get(&name) - .await? - .is_some() - { - analysis.add_code_gen(ConstantValueCodeGen::new( - CompileTimeDefineValue::Bool(true), - ast_path.to_vec().into(), - )); - return Ok(()); + MembershipType::In => { + if state + .compile_time_info_ref + .free_var_references + .get(&name) + .await? + .is_some() + { + analysis.add_code_gen(ConstantValueCodeGen::new( + CompileTimeDefineValue::Bool(true), + ast_path.to_vec().into(), + )); + return Ok(()); + } } } } + if is_prop_cache + && let JsValue::WellKnownFunction(WellKnownFunctionKind::Require) = &obj + { + analysis.add_code_gen::(match ty { + MembershipType::Member => { + CjsRequireCacheAccess::new(ast_path.to_vec().into()).into() + } + MembershipType::In => ConstantValueCodeGen::new( + CompileTimeDefineValue::Bool(true), + ast_path.to_vec().into(), + ) + .into(), + }); + return Ok(()); + } } - if is_prop_cache && let JsValue::WellKnownFunction(WellKnownFunctionKind::Require) = &obj { - analysis.add_code_gen::(match ty { - MembershipType::Member => { - CjsRequireCacheAccess::new(ast_path.to_vec().into()).into() - } - MembershipType::In => ConstantValueCodeGen::new( - CompileTimeDefineValue::Bool(true), - ast_path.to_vec().into(), + // Not inlined, potentially register as runtime env var. + if obj_name.iter().flatten().any(|(name, reassigned)| { + !reassigned + && matches!( + name.0.as_slice(), + [ + DefinableNameSegmentRef::Name("process"), + DefinableNameSegmentRef::Name("env") + ] ) - .into(), - }); - return Ok(()); - } - - if let Some((name, false)) = &obj_name - && matches!( - name.0.as_slice(), - [ - DefinableNameSegmentRef::Name("process"), - DefinableNameSegmentRef::Name("env") - ] - ) - { - // non-inlined env var + }) { analysis.add_runtime_env_var_reference(RcStr::from(prop)); return Ok(()); } } - Ok(()) } @@ -3515,7 +3525,11 @@ async fn handle_typeof<'a>( state: &AnalysisState<'a>, analysis: &mut AnalyzeEcmascriptModuleResultBuilder, ) -> Result<()> { - if let Some((mut name, false)) = arg.get_definable_name(Some(&state.var_graph)) { + let arg_name = arg.get_definable_name(Some(&state.var_graph)); + if arg_name.len() == 1 + && let Some((mut name, false)) = arg_name.into_iter().next().unwrap() + { + // Exactly one name. We can potentially inline name.0.push(DefinableNameSegmentRef::TypeOf); if let Some(value) = state .compile_time_info_ref @@ -3538,11 +3552,12 @@ async fn handle_free_var<'a>( state: &AnalysisState<'a>, analysis: &mut AnalyzeEcmascriptModuleResultBuilder, ) -> Result<()> { - if let Some((name, _)) = var.get_definable_name(None) + // Exactly one name. We can potentially inline + if let [Some((name, _))] = &*var.get_definable_name(None) && let Some(value) = state .compile_time_info_ref .free_var_references - .get(&name) + .get(name) .await? { handle_free_var_reference(ast_path, &value, span, state, analysis).await?; @@ -3960,16 +3975,22 @@ async fn value_visitor_inner<'a>( ) -> Result<(JsValue<'a>, Modified)> { if let JsValue::In(_, left, right) = &v && let Some(left) = left.as_str() - && let Some((mut name, _)) = right.get_definable_name(Some(var_graph)) + && let right_name = right.get_definable_name(Some(var_graph)) + && right_name.len() == 1 + && let Some((mut right_name, false)) = right_name.into_iter().next().unwrap() { - name.0.push(DefinableNameSegmentRef::Name(left)); - if compile_time_info_ref.defines.contains_key(&name).await? { + right_name.0.push(DefinableNameSegmentRef::Name(left)); + if compile_time_info_ref + .defines + .contains_key(&right_name) + .await? + { return Ok((JsValue::Constant(JsConstantValue::True), Modified::Yes)); } } - if let Some((name, _)) = v.get_definable_name(Some(var_graph)) - && let Some(value) = compile_time_info_ref.defines.get(&name).await? + if let [Some((name, false))] = &*v.get_definable_name(Some(var_graph)) + && let Some(value) = compile_time_info_ref.defines.get(name).await? { return Ok(( JsValue::from_compile_time_define_value_in(arena.get_or_default(), &value)?, diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.codegen.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.codegen.snapshot index e08877920854..b3405f2970f0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.codegen.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.codegen.snapshot @@ -1 +1,3 @@ -runtime: [] +runtime: [ + "FOO1", +] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.snapshot index e08877920854..b3405f2970f0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.snapshot @@ -1 +1,3 @@ -runtime: [] +runtime: [ + "FOO1", +] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.tracing.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.tracing.snapshot index e08877920854..b3405f2970f0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.tracing.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/env-vars.tracing.snapshot @@ -1 +1,3 @@ -runtime: [] +runtime: [ + "FOO1", +] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/input.js b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/input.js index e9acc37d0f9f..a9d2335bffd3 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/input.js +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default-rest-spread/input.js @@ -1,4 +1,3 @@ -// TODO properly handle Pat::Assign function readEnv({ FOO1, ...rest } = process.env) { return rest } diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.codegen.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.codegen.snapshot index e08877920854..b3405f2970f0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.codegen.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.codegen.snapshot @@ -1 +1,3 @@ -runtime: [] +runtime: [ + "FOO1", +] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.snapshot index e08877920854..b3405f2970f0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.snapshot @@ -1 +1,3 @@ -runtime: [] +runtime: [ + "FOO1", +] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.tracing.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.tracing.snapshot index e08877920854..b3405f2970f0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.tracing.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/env-vars.tracing.snapshot @@ -1 +1,3 @@ -runtime: [] +runtime: [ + "FOO1", +] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/input.js b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/input.js index 55a9975c065d..866de1d7caff 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/input.js +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/dynamic-fn-default/input.js @@ -1,4 +1,3 @@ -// TODO properly handle Pat::Assign function readEnv(env = process.env) { return env.FOO1 } diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.codegen.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.codegen.snapshot index 943f898b486c..a1bdca1e96f0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.codegen.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.codegen.snapshot @@ -5,4 +5,6 @@ runtime: [ "FOO4", "FOO5", "FOO6", + "FOO7", + "FOO8", ] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.snapshot index 943f898b486c..a1bdca1e96f0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.snapshot @@ -5,4 +5,6 @@ runtime: [ "FOO4", "FOO5", "FOO6", + "FOO7", + "FOO8", ] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.tracing.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.tracing.snapshot index 943f898b486c..a1bdca1e96f0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.tracing.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/env-vars.tracing.snapshot @@ -5,4 +5,6 @@ runtime: [ "FOO4", "FOO5", "FOO6", + "FOO7", + "FOO8", ] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/input.js b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/input.js index 76766a357d82..761be8ebace7 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/input.js +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static-assign-obj/input.js @@ -38,7 +38,6 @@ if (foo) { } else { e = process.env } -// TODO currently not tracked console.log(e.FOO7) // --- @@ -50,5 +49,4 @@ if (foo) { } else { p2 = process } -// TODO currently not tracked console.log(p2.env.FOO8) diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.codegen.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.codegen.snapshot index 2f7e0db383f7..b82349f92dc8 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.codegen.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.codegen.snapshot @@ -5,4 +5,6 @@ runtime: [ "FOO4", "FOO5", "INLINED3", + "FOO6", + "INLINED4", ] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.snapshot index 2f7e0db383f7..b82349f92dc8 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.snapshot @@ -5,4 +5,6 @@ runtime: [ "FOO4", "FOO5", "INLINED3", + "FOO6", + "INLINED4", ] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.tracing.snapshot b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.tracing.snapshot index 2f7e0db383f7..b82349f92dc8 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.tracing.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/env-vars.tracing.snapshot @@ -5,4 +5,6 @@ runtime: [ "FOO4", "FOO5", "INLINED3", + "FOO6", + "INLINED4", ] diff --git a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/input.js b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/input.js index 849d68f02033..284ef26b96a2 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/input.js +++ b/turbopack/crates/turbopack-ecmascript/tests/references/env-var-info/static/input.js @@ -20,15 +20,6 @@ console.log(FOO4, renamed) // TODO this is actually not inlined yet console.log(INLINED3) -// TODO not tracked yet -// The re-entered object pattern now receives: -// Alternatives [ -// Argument(...), -// process.env -// ] -// add_object_pat_effects() therefore creates DestructuredMember with that Alternatives object. -// Later, Effect::DestructuredMember calls get_definable_name(). That function does not handle JsValue::Alternatives, so it returns None instead of recognizing the process.env alternative. -// Thus FOO6 and INLINED4 remain untracked in `function readEnv({ FOO6, INLINED4 } = process.env)`. function readEnv({ FOO6, INLINED4 } = process.env) { // TODO this is actually not inlined yet return FOO6 + INLINED4 diff --git a/turbopack/crates/turbopack-tests/tests/execution/turbopack/code-gen/typeof-exports-module/input/exports-reassign.js b/turbopack/crates/turbopack-tests/tests/execution/turbopack/code-gen/typeof-exports-module/input/exports-reassign.js index 264cbf88107d..a7accc311163 100644 --- a/turbopack/crates/turbopack-tests/tests/execution/turbopack/code-gen/typeof-exports-module/input/exports-reassign.js +++ b/turbopack/crates/turbopack-tests/tests/execution/turbopack/code-gen/typeof-exports-module/input/exports-reassign.js @@ -1,3 +1,5 @@ exports = () => 'hello' -if (typeof exports === 'object') throw 'oh no' +if (typeof exports === 'object') { + throw new Error("exports-reassign: it's an object, so incorrectly inlined") +} module.exports = 1234 diff --git a/turbopack/crates/turbopack-tests/tests/execution/turbopack/code-gen/typeof-exports-module/input/module-reassign.js b/turbopack/crates/turbopack-tests/tests/execution/turbopack/code-gen/typeof-exports-module/input/module-reassign.js index 3e0101d2af31..512b4bb9f716 100644 --- a/turbopack/crates/turbopack-tests/tests/execution/turbopack/code-gen/typeof-exports-module/input/module-reassign.js +++ b/turbopack/crates/turbopack-tests/tests/execution/turbopack/code-gen/typeof-exports-module/input/module-reassign.js @@ -1,3 +1,5 @@ module = () => 'hello' -if (typeof module === 'object') throw 'oh no' +if (typeof module === 'object') { + throw new Error("module-reassign: it's an object, so incorrectly inlined") +} exports.foo = 1234