Skip to content

fix(lint): detect imported process env usage#10470

Merged
ematipico merged 7 commits into
biomejs:mainfrom
ShaharAviram1:shahar/fix-no-process-env-imports
Jun 10, 2026
Merged

fix(lint): detect imported process env usage#10470
ematipico merged 7 commits into
biomejs:mainfrom
ShaharAviram1:shahar/fix-no-process-env-imports

Conversation

@ShaharAviram1

Copy link
Copy Markdown
Contributor

Summary

Fixes #10447.

noProcessEnv already reports direct process.env usage, but it was missing cases where env is imported from process or node:process.

This updates the rule to also catch cases like:

  • import { env } from "process"
  • import { env as processEnv } from "node:process"

while still ignoring unrelated local imports with the same name.

I used AI tools while investigating the issue and tracing the relevant code paths, but reviewed and cleaned up the final implementation and tests manually before submitting.

Test Plan

Added fixtures/snapshots covering:

  • direct env imports from process
  • aliased env imports
  • valid local env imports

Ran the relevant noProcessEnv analyzer tests and regenerated snapshots with the normal test workflow.

Docs

No documentation changes needed.

@changeset-bot

changeset-bot Bot commented May 26, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7efde0d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

✅ Organic activity

No automation signals detected in the analyzed events.

View full analysis →

This is an automated analysis by AgentScan

@github-actions github-actions Bot added A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels May 26, 2026
@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR extends the noProcessEnv lint to detect environment reads when env is imported as a named export from "process" or "node:process". The rule resolves static member expressions, checks object === process and property === env, and uses is_env_from_process (ancestor-based) to confirm the binding originates from a named env import or an import source of process/node:process. Tests cover direct and aliased imports plus a non-matching local import; a Changeset documents the patch.

Suggested reviewers

  • dyc3
  • ematipico
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(lint): detect imported process env usage' clearly and accurately summarises the main change—extending noProcessEnv to catch env imported from process/node:process.
Description check ✅ Passed The description is well-related to the changeset, detailing what was fixed (#10447), examples of caught cases, test coverage, and acknowledging AI assistance in investigation.
Linked Issues check ✅ Passed The PR fully addresses #10447's core requirements: detecting env imported from 'process'/'node:process' while preserving existing behaviour and ignoring unrelated local imports.
Out of Scope Changes check ✅ Passed All changes are scoped to the noProcessEnv rule refactoring and related test fixtures; no unrelated modifications were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/style/no_process_env.rs (1)

103-114: ⚡ Quick win

Avoid allocating and rescanning ancestors in is_env_from_process.

This path runs per candidate member access; collecting ancestors into a Vec adds avoidable overhead.

♻️ Proposed refactor
 fn is_env_from_process(binding: &biome_js_semantic::Binding) -> bool {
     const PROCESS_MODULE_NAMES: [&str; 2] = ["process", "node:process"];
-    let ancestors: Vec<_> = binding.syntax().ancestors().collect();
-    ancestors
-        .iter()
-        .find_map(|n| AnyJsNamedImportSpecifier::cast(n.clone())?.imported_name())
-        .is_some_and(|name| name.text_trimmed() == "env")
-        && ancestors
-            .iter()
-            .find_map(|n| JsImport::cast(n.clone())?.source_text().ok())
-            .is_some_and(|src| PROCESS_MODULE_NAMES.contains(&src.text()))
+    let imports_env = binding
+        .syntax()
+        .ancestors()
+        .find_map(|n| AnyJsNamedImportSpecifier::cast(n)?.imported_name())
+        .is_some_and(|name| name.text_trimmed() == "env");
+
+    let from_process_module = binding
+        .syntax()
+        .ancestors()
+        .find_map(|n| JsImport::cast(n)?.source_text().ok())
+        .is_some_and(|src| PROCESS_MODULE_NAMES.contains(&src.text()));
+
+    imports_env && from_process_module
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_js_analyze/src/lint/style/no_process_env.rs` around lines 103 -
114, The function is_env_from_process currently collects
binding.syntax().ancestors() into a Vec and rescans it twice; change it to a
single non-allocating pass over binding.syntax().ancestors() (iterate the
iterator directly) and track two booleans (found_env and found_process_src) as
you inspect each ancestor: if AnyJsNamedImportSpecifier::cast yields an
imported_name equal to "env" set found_env, and if JsImport::cast yields a
source_text matching "process" or "node:process" set found_process_src; return
found_env && found_process_src as the result. This removes the Vec allocation
and dual scans while keeping the logic in is_env_from_process.
crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnv.js (1)

1-3: ⚡ Quick win

Add a direct node:process named-import invalid fixture as well.

Nice start. You currently cover direct import for process and aliased import for node:process; adding a non-aliased import { env } from 'node:process' fixture would lock the full behaviour matrix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnv.js`
around lines 1 - 3, Add a parallel invalid fixture that uses a named import from
the node:process specifier; specifically create/update a test that does "import
{ env } from 'node:process'" and then references env.NODE_ENV and env.HOME so
the runner covers the non-aliased node:process named-import case (symbols: env
and the import statement "import { env } from 'node:process'"). Ensure this new
fixture mirrors the existing invalidImportedEnv.js usage pattern so the full
behaviour matrix is tested.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/no-process-env-named-import.md:
- Line 5: Update the changeset .changeset/no-process-env-named-import.md to
include the issue reference and rule link: prepend the summary with "Fixed
[`#10447`](https://github.com/biomejs/biome/issues/10447):" and include a link to
the rule using the exact symbol
[noProcessEnv](https://biomejs.dev/linter/rules/no-process-env/); also make the
description explicit about what previously wasn't detected (uses of env imported
as a named export or aliased import, e.g. import { env } from 'node:process' and
import { env as processEnv } from 'process') so it clearly states the gap that
was fixed.

---

Nitpick comments:
In `@crates/biome_js_analyze/src/lint/style/no_process_env.rs`:
- Around line 103-114: The function is_env_from_process currently collects
binding.syntax().ancestors() into a Vec and rescans it twice; change it to a
single non-allocating pass over binding.syntax().ancestors() (iterate the
iterator directly) and track two booleans (found_env and found_process_src) as
you inspect each ancestor: if AnyJsNamedImportSpecifier::cast yields an
imported_name equal to "env" set found_env, and if JsImport::cast yields a
source_text matching "process" or "node:process" set found_process_src; return
found_env && found_process_src as the result. This removes the Vec allocation
and dual scans while keeping the logic in is_env_from_process.

In
`@crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnv.js`:
- Around line 1-3: Add a parallel invalid fixture that uses a named import from
the node:process specifier; specifically create/update a test that does "import
{ env } from 'node:process'" and then references env.NODE_ENV and env.HOME so
the runner covers the non-aliased node:process named-import case (symbols: env
and the import statement "import { env } from 'node:process'"). Ensure this new
fixture mirrors the existing invalidImportedEnv.js usage pattern so the full
behaviour matrix is tested.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 24bce479-a2aa-4b01-bc20-ee5e50db4e17

📥 Commits

Reviewing files that changed from the base of the PR and between 19c7141 and 446729e.

⛔ Files ignored due to path filters (3)
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnv.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnvAlias.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/validImportedEnv.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (5)
  • .changeset/no-process-env-named-import.md
  • crates/biome_js_analyze/src/lint/style/no_process_env.rs
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnv.js
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnvAlias.js
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/validImportedEnv.js

Comment thread .changeset/no-process-env-named-import.md Outdated
@ShaharAviram1 ShaharAviram1 force-pushed the shahar/fix-no-process-env-imports branch from 446729e to 93dc7f9 Compare May 27, 2026 14:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/biome_js_analyze/src/lint/style/no_process_env.rs (1)

103-114: 💤 Low value

Optional: avoid intermediate Vec allocation.

The ancestors can be iterated twice directly rather than collecting into a Vec first. Minor optimisation, feel free to defer.

♻️ Suggested change
 fn is_env_from_process(binding: &biome_js_semantic::Binding) -> bool {
     const PROCESS_MODULE_NAMES: [&str; 2] = ["process", "node:process"];
-    let ancestors: Vec<_> = binding.syntax().ancestors().collect();
-    ancestors
+    binding
+        .syntax()
+        .ancestors()
-        .iter()
-        .find_map(|n| AnyJsNamedImportSpecifier::cast(n.clone())?.imported_name())
+        .find_map(|n| AnyJsNamedImportSpecifier::cast(n)?.imported_name())
         .is_some_and(|name| name.text_trimmed() == "env")
-        && ancestors
+        && binding
+            .syntax()
+            .ancestors()
-            .iter()
-            .find_map(|n| JsImport::cast(n.clone())?.source_text().ok())
+            .find_map(|n| JsImport::cast(n)?.source_text().ok())
             .is_some_and(|src| PROCESS_MODULE_NAMES.contains(&src.text()))
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_js_analyze/src/lint/style/no_process_env.rs` around lines 103 -
114, The code in is_env_from_process allocates a Vec by collecting
binding.syntax().ancestors() but only needs to iterate ancestors twice; remove
the intermediate Vec and call binding.syntax().ancestors() directly for each
check instead (i.e. replace ancestors.iter().find_map(...) and the second
ancestors.iter().find_map(...) with two separate calls to
binding.syntax().ancestors().find_map(...)). Keep the same casts to
AnyJsNamedImportSpecifier and JsImport and the same checks (imported_name() ==
"env" and source_text() in PROCESS_MODULE_NAMES) but invoke
binding.syntax().ancestors() twice to avoid the temporary allocation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/biome_js_analyze/src/lint/style/no_process_env.rs`:
- Around line 103-114: The code in is_env_from_process allocates a Vec by
collecting binding.syntax().ancestors() but only needs to iterate ancestors
twice; remove the intermediate Vec and call binding.syntax().ancestors()
directly for each check instead (i.e. replace ancestors.iter().find_map(...) and
the second ancestors.iter().find_map(...) with two separate calls to
binding.syntax().ancestors().find_map(...)). Keep the same casts to
AnyJsNamedImportSpecifier and JsImport and the same checks (imported_name() ==
"env" and source_text() in PROCESS_MODULE_NAMES) but invoke
binding.syntax().ancestors() twice to avoid the temporary allocation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f4a78188-1b3f-44ad-895f-1052fd62a183

📥 Commits

Reviewing files that changed from the base of the PR and between 446729e and 93dc7f9.

⛔ Files ignored due to path filters (3)
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnv.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnvAlias.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/validImportedEnv.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (5)
  • .changeset/no-process-env-named-import.md
  • crates/biome_js_analyze/src/lint/style/no_process_env.rs
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnv.js
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnvAlias.js
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/validImportedEnv.js
✅ Files skipped from review due to trivial changes (3)
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/validImportedEnv.js
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnv.js
  • .changeset/no-process-env-named-import.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_js_analyze/tests/specs/style/noProcessEnv/invalidImportedEnvAlias.js

}

fn is_env_from_process(binding: &biome_js_semantic::Binding) -> bool {
const PROCESS_MODULE_NAMES: [&str; 2] = ["process", "node:process"];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a const, you can move it outside of the function

Comment thread crates/biome_js_analyze/src/lint/style/no_process_env.rs Outdated
Comment thread .changeset/no-process-env-named-import.md Outdated
Comment thread crates/biome_js_analyze/src/lint/style/no_process_env.rs Outdated
Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
Comment thread crates/biome_js_analyze/src/lint/style/no_process_env.rs Outdated
@ematipico ematipico self-assigned this Jun 5, 2026
@ematipico

Copy link
Copy Markdown
Member

Still need to add some cases because the new changes don't cover require and import()

@codspeed-hq

codspeed-hq Bot commented Jun 5, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 11.16%

⚡ 6 improved benchmarks
✅ 52 untouched benchmarks
⏩ 196 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
js_analyzer[typescript_3735799142832611563.ts] 200.7 ms 177.1 ms +13.29%
js_analyzer[statement_263793315104667298.ts] 128 ms 114.3 ms +11.98%
js_analyzer[parser_13571644119461115204.ts] 139.2 ms 124.7 ms +11.64%
js_analyzer[index_3894593175024091846.js] 77.1 ms 70.1 ms +10.11%
js_analyzer[router_17129688031671448157.ts] 39.1 ms 35.6 ms +10.05%
js_analyzer[css_16118272471217147034.js] 36.2 ms 32.9 ms +9.94%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing ShaharAviram1:shahar/fix-no-process-env-imports (b1d7f81) with main (3634a3f)2

Open in CodSpeed

Footnotes

  1. 196 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (19c7141) during the generation of this report, so 3634a3f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@ematipico ematipico left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest commit wasn't needed. The change was way simpler. Use AnyJsImportLike instead of JsImport. The comment was more for me, I didn't know were still maintaining the PR

@ematipico ematipico closed this Jun 9, 2026
@ematipico ematipico reopened this Jun 9, 2026
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

✅ Organic activity

No automation signals detected in the analyzed events.

View full analysis →

This is an automated analysis by AgentScan

@ematipico

Copy link
Copy Markdown
Member

Leave it to me. I'll fix it.

@ematipico ematipico closed this Jun 9, 2026
@ematipico ematipico reopened this Jun 9, 2026
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

✅ Organic activity

No automation signals detected in the analyzed events.

View full analysis →

This is an automated analysis by AgentScan

@ematipico ematipico merged commit 84b43c5 into biomejs:main Jun 10, 2026
21 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💅 noProcessEnv should report env imported from node:process

2 participants