refactor(linter): resolve globals from env on build#20100
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Merging this PR will not alter performance
Comparing Footnotes
|
3449eab to
b1b3ac0
Compare
There was a problem hiding this comment.
Pull request overview
Refactors linter configuration handling so that environment presets (env) are resolved into concrete globals during config build time, removing runtime env lookups in linting/context code.
Changes:
- Remove
envfromLintConfig/ContextHostand stop checking enabled envs at lint-time (globals are expected to be pre-resolved). - Resolve
envpresets intoOxlintGlobalswhen buildingLintConfigand when resolving overrides. - Update/add unit tests to assert env-to-globals resolution and override behavior.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/oxc_linter/src/external_linter.rs | Adjusts the serialized config payload sent to JS plugins to no longer include envs (now globals-only). |
| crates/oxc_linter/src/context/mod.rs | Removes env-based global resolution from LintContext global queries. |
| crates/oxc_linter/src/context/host.rs | Removes env() accessor from ContextHost. |
| crates/oxc_linter/src/config/mod.rs | Drops env from LintConfig and resolves env → globals during From<Oxlintrc>. |
| crates/oxc_linter/src/config/globals.rs | Adds internal insertion helper to support env → globals expansion. |
| crates/oxc_linter/src/config/env.rs | Implements env → globals expansion logic (override_globals). |
| crates/oxc_linter/src/config/config_store.rs | Removes env override merging; overrides now apply via pre-resolved globals. |
| crates/oxc_linter/src/config/config_builder.rs | Resolves base + override env/global settings into a single globals map; adds tests for override behavior. |
b1b3ac0 to
fe8dce4
Compare
67983f3 to
ea41224
Compare
e233211 to
78e61c4
Compare
| let Some(globals_entries) = GLOBALS.get(name) else { | ||
| continue; | ||
| }; | ||
| for (env, supported) in globals_entries.entries() { | ||
| if *active { | ||
| globals_to_override.insert(env.to_string(), GlobalValue::from(*supported)); | ||
| } else { | ||
| globals_to_override.insert(env.to_string(), GlobalValue::Off); | ||
| } |
There was a problem hiding this comment.
OxlintEnv::override_globals currently inserts GlobalValue::Off for every global in an env when that env is set to false. This changes semantics vs the previous lookup-based approach: if a global is provided by multiple envs (e.g. setTimeout exists in both node and browser), disabling one env can incorrectly force that global to off even though another enabled env still provides it. Consider computing resolved globals as the union of enabled envs (ignore disabled envs), then apply explicit globals overrides last; for overrides that disable envs, this likely requires recomputing the resolved globals for that override rather than representing env: false as globals: off entries.
| let Some(globals_entries) = GLOBALS.get(name) else { | |
| continue; | |
| }; | |
| for (env, supported) in globals_entries.entries() { | |
| if *active { | |
| globals_to_override.insert(env.to_string(), GlobalValue::from(*supported)); | |
| } else { | |
| globals_to_override.insert(env.to_string(), GlobalValue::Off); | |
| } | |
| // Only enabled environments contribute globals; disabled envs are ignored here. | |
| if !*active { | |
| continue; | |
| } | |
| let Some(globals_entries) = GLOBALS.get(name) else { | |
| continue; | |
| }; | |
| for (env, supported) in globals_entries.entries() { | |
| globals_to_override.insert(env.to_string(), GlobalValue::from(*supported)); |
| let mut globals = OxlintGlobals::default(); | ||
| config.env.override_globals(&mut globals); | ||
| config.globals.override_globals(&mut globals); |
There was a problem hiding this comment.
LintConfig::from expands env presets into OxlintGlobals, which can make config.globals extremely large (builtins + enabled envs). This map is cloned in Config::apply_overrides and serialized per-file for JS plugins (see crates/oxc_linter/src/config/config_store.rs and crates/oxc_linter/src/lib.rs), so this change likely introduces a significant per-file CPU/memory regression. Consider caching a pre-serialized globals JSON per resolved config, using shared/immutable maps (e.g. Arc-backed), or keeping a more compact representation for builtins/env presets when communicating with JS.
There was a problem hiding this comment.
@overlookmotel I am afraid this is a real problem for JS side? The snapshots-diff looks big for "node": true.
But in my opinion there should be no concept of "env" for the lint context. This is for me only a helper section to avoid defining all globals per line. With ESLint flat config and our js config, most users are using the globals npm package with destructruring like {globals: {...npm-globals['vue'], ...npm-globals['node']}.
So maybe this is at the end already a problem and did get only shown by the refactoring?
78e61c4 to
c2d4fb9
Compare
c2d4fb9 to
ccf8d18
Compare
ea41224 to
7269cc0
Compare
ccf8d18 to
f75f83c
Compare
f75f83c to
58a1a9c
Compare
7269cc0 to
a9acb2b
Compare
58a1a9c to
7f99530
Compare

Refactored how
envandglobalsworked together. Before we checked each individual entry if it is enabled/disabled, but they are working "hand in hand". This was a problem for overriding global with env. An example:This config would tell the lint rule that
windowis still off, but in real we re-enabled it with theenvsetting.Separated to logic to resolve the globals at the beginning and avoiding to have a "OxlintEnv" instance in the resolved config. We could probably rename
OxlintEnvtoOxlintrcEnvnow.