Skip to main content

skif_js_syntax/
config.rs

1//! Static evaluation of the value a tool configuration file exports. Nothing is executed.
2
3use skif_core::{HashMap, HashSet};
4use std::cell::{Cell, RefCell};
5use std::path::{Component, Path, PathBuf};
6use std::rc::Rc;
7use std::sync::LazyLock;
8
9use oxc_allocator::Allocator;
10use oxc_ast::ast::{
11    Argument, ArrayExpressionElement, ArrowFunctionExpression, AssignmentOperator, AssignmentTarget,
12    AssignmentTargetPropertyIdentifier, BinaryExpression, BinaryOperator, BindingIdentifier, BindingPattern,
13    BlockStatement, CallExpression, CatchClause, ChainElement, ChainExpression, ComputedMemberExpression,
14    ConditionalExpression, Declaration, ExportDefaultDeclarationKind, Expression, ExpressionStatement, ForInStatement,
15    ForOfStatement, ForStatement, Function, IdentifierReference, IfStatement, ImportDeclarationSpecifier,
16    ImportOrExportKind, LogicalExpression, LogicalOperator, MemberExpression, NewExpression, ObjectProperty,
17    ObjectPropertyKind, Program, ReturnStatement, SimpleAssignmentTarget, Statement, StaticMemberExpression,
18    TaggedTemplateExpression, TemplateLiteral, UnaryExpression, UnaryOperator, VariableDeclaration,
19    VariableDeclarationKind, VariableDeclarator,
20};
21use oxc_ast_visit::{Visit, walk};
22use oxc_parser::{ParseOptions as OxcParseOptions, Parser};
23use oxc_span::{SourceType, Span};
24use serde::{Deserialize, Serialize};
25
26use crate::finder::static_string;
27use crate::sharing::{
28    Change, Imports, Unstable, Write, alias_names, alias_source, changed_by_call, given, given_parts, property_defined,
29    reach, statement_call,
30};
31
32const MAX_DEPTH: u32 = 32;
33
34/// How many constants may be evaluated inside one another; a longer chain is unknown.
35const MAX_CONST_CHAIN: usize = 64;
36
37/// The largest value a constant may evaluate to. Constants that reference each other twice at every level
38/// (`const b = [a, a]; const c = [b, b]; …`) double in size per level; a value past this is unknown.
39const MAX_CONST_NODES: usize = 10_000;
40
41/// How many function calls one evaluation may make; a function that calls itself twice at every level is unknown
42/// past this.
43const MAX_CALLS: usize = 256;
44
45/// How many modules, the config file included, may be evaluated inside one another; an import past the eighth module
46/// of a chain is unknown.
47const MAX_IMPORT_DEPTH: usize = 8;
48
49/// How many distinct modules one config may load, so a config that reaches many modules stays cheap.
50const MAX_MODULE_LOADS: usize = 64;
51
52/// How many module analyses one config may run in all ([`Modules::facts`]). What an analysis finds under a pending
53/// module is not kept, so a cycle of imports is analysed again at every entry; past this, a module reads as unknown.
54const MAX_MODULE_ANALYSES: usize = 4 * MAX_MODULE_LOADS;
55
56/// How many imported exports one config may evaluate in all, calls with arguments included, so imported functions
57/// that call one another many times stay cheap.
58const MAX_MODULE_EVALUATIONS: usize = 1024;
59
60/// Packages whose default export or named exports list files by glob.
61const GLOB_PACKAGES: &[&str] = &["glob", "fast-glob", "globby", "tinyglobby"];
62
63/// Packages whose `merge` and `mergeWith` merge configuration objects. Any other module's `merge` (an `RxJS` stream
64/// combinator) is not a config combinator.
65const MERGE_PACKAGES: &[&str] = &[
66    "lodash",
67    "lodash-es",
68    "lodash/merge",
69    "lodash/mergeWith",
70    "lodash.merge",
71    "lodash.mergewith",
72    "deepmerge",
73    "deepmerge-ts",
74    "ts-deepmerge",
75    "merge",
76    "merge-deep",
77    "webpack-merge",
78    "defu",
79];
80
81/// Names a combinator call may use that other libraries use for unrelated functions.
82const AMBIGUOUS_MERGES: &[&str] = &["merge", "mergeWith"];
83
84/// Members of a glob function that list files too (`fg.sync`, `glob.globSync`).
85const GLOB_MEMBERS: &[&str] = &["sync", "async", "glob", "globSync", "globbySync", "stream"];
86
87/// The members every object inherits from `Object.prototype`.
88const OBJECT_PROTOTYPE_MEMBERS: &[&str] = &[
89    "__defineGetter__",
90    "__defineSetter__",
91    "__lookupGetter__",
92    "__lookupSetter__",
93    "__proto__",
94    "constructor",
95    "hasOwnProperty",
96    "isPrototypeOf",
97    "propertyIsEnumerable",
98    "toLocaleString",
99    "toString",
100    "valueOf",
101];
102
103/// A statically known value from a config file.
104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105#[serde(rename_all = "kebab-case")]
106pub enum ConfigValue {
107    /// `null`.
108    Null,
109    /// `true` or `false`.
110    Bool(bool),
111    /// A numeric literal.
112    Number(f64),
113    /// A string literal, or a string computed from known values (a template literal, a concatenation, a path).
114    String(String),
115    /// An array literal; spreads of static arrays are flattened, anything else becomes [`ConfigValue::Unknown`].
116    Array(Vec<ConfigValue>),
117    /// An object literal in source order. A spread of an unknown value is recorded as the key [`UNKNOWN_SPREAD`], or
118    /// [`OWN_SPREAD`] when the module computes the value itself.
119    Object(Vec<(String, ConfigValue)>),
120    /// A regular expression literal.
121    Regex {
122        /// The pattern, without the slashes.
123        pattern: String,
124        /// The flags after the closing slash, in alphabetical order (`gi`).
125        flags: String,
126    },
127    /// Anything that cannot be known without running the code.
128    Unknown,
129    /// One of these values, without knowing which: the branches of a condition that cannot be decided statically
130    /// (`process.env.CI ? 'github' : 'list'`). There are at least two alternatives, all different, and none is
131    /// [`ConfigValue::Unknown`] or another `OneOf`, though an array or object alternative may hold unknown parts.
132    /// [`ConfigValue::is_unknown`] and [`ConfigValue::has_unknown`] count it as unknown, so code that does not ask
133    /// for its [`ConfigValue::alternatives`] treats it as an unknown value.
134    OneOf(Vec<ConfigValue>),
135}
136
137/// The key under which an object records an unknown spread, or an unknown argument of a merge, of a value the module
138/// takes from elsewhere: an imported base config, a merge of imported ones, a global. It may add or replace any key.
139pub const UNKNOWN_SPREAD: &str = "...";
140
141/// Like [`UNKNOWN_SPREAD`], for an unknown value the module computes itself: a spread or merge of a call of, or a
142/// reference to, a function or constant it defines, or a binding of a function being evaluated
143/// (`build: { ...makeLib() }`). A value imported from another module records these as [`UNKNOWN_SPREAD`] too.
144pub const OWN_SPREAD: &str = "...own";
145
146impl ConfigValue {
147    /// Whether `key` is a key an object records an unknown spread under, [`UNKNOWN_SPREAD`] or [`OWN_SPREAD`].
148    #[must_use]
149    pub fn is_spread_key(key: &str) -> bool {
150        key == UNKNOWN_SPREAD || key == OWN_SPREAD
151    }
152
153    /// The value of `key` when this is an object.
154    #[must_use]
155    pub fn get(&self, key: &str) -> Option<&ConfigValue> {
156        match self {
157            Self::Object(entries) => entries.iter().rev().find(|(k, _)| k == key).map(|(_, v)| v),
158            _ => None,
159        }
160    }
161
162    /// The string when this is a string.
163    #[must_use]
164    pub fn as_str(&self) -> Option<&str> {
165        match self {
166            Self::String(s) => Some(s),
167            _ => None,
168        }
169    }
170
171    /// The elements when this is an array.
172    #[must_use]
173    pub fn as_array(&self) -> Option<&[ConfigValue]> {
174        match self {
175            Self::Array(items) => Some(items),
176            _ => None,
177        }
178    }
179
180    /// The entries when this is an object.
181    #[must_use]
182    pub fn as_object(&self) -> Option<&[(String, ConfigValue)]> {
183        match self {
184            Self::Object(entries) => Some(entries),
185            _ => None,
186        }
187    }
188
189    /// The pattern when this is a regular expression literal.
190    #[must_use]
191    pub fn as_regex(&self) -> Option<&str> {
192        match self {
193            Self::Regex { pattern, .. } => Some(pattern),
194            _ => None,
195        }
196    }
197
198    /// The boolean when this is a boolean.
199    #[must_use]
200    pub fn as_bool(&self) -> Option<bool> {
201        match self {
202            Self::Bool(b) => Some(*b),
203            _ => None,
204        }
205    }
206
207    /// Whether the value is not known as one value: [`ConfigValue::Unknown`], or [`ConfigValue::OneOf`] of several.
208    #[must_use]
209    pub fn is_unknown(&self) -> bool {
210        matches!(self, Self::Unknown | Self::OneOf(_))
211    }
212
213    /// The values this may be: the alternatives of a [`ConfigValue::OneOf`], or the value itself.
214    #[must_use]
215    pub fn alternatives(&self) -> &[ConfigValue] {
216        match self {
217            Self::OneOf(alternatives) => alternatives,
218            other => std::slice::from_ref(other),
219        }
220    }
221
222    /// Whether the value, or anything inside it, is [`ConfigValue::Unknown`] or [`ConfigValue::OneOf`].
223    #[must_use]
224    pub fn has_unknown(&self) -> bool {
225        match self {
226            Self::Unknown | Self::OneOf(_) => true,
227            Self::Array(items) => items.iter().any(Self::has_unknown),
228            Self::Object(entries) => entries.iter().any(|(_, v)| v.has_unknown()),
229            _ => false,
230        }
231    }
232}
233
234/// A value as the evaluator holds it: a [`ConfigValue`] that may also record that a value is not definite. Only
235/// the evaluator sees it; what it hands out is converted to a [`ConfigValue`], which has no such record.
236#[derive(Debug, Clone, PartialEq)]
237enum Value {
238    Null,
239    /// `undefined`: the value of a key an object does not have. A default applies to it, unlike to `null`; it is
240    /// handed out as [`ConfigValue::Null`].
241    Undefined,
242    Bool(bool),
243    Number(f64),
244    String(String),
245    Array(Vec<Value>),
246    Object(Vec<(String, Value)>),
247    Regex {
248        pattern: String,
249        flags: String,
250    },
251    Unknown,
252    /// One of several values, not knowing which; see [`one_of`]. Handed out as [`ConfigValue::OneOf`].
253    OneOf(Vec<Value>),
254    /// A value that is not definite: a key merged from the branches of a condition the evaluator cannot decide
255    /// ([`any_entries`]), which the object may lack or hold any one of the values its union lists in, and every value
256    /// that carries a read of one on: a key of an object or a binding holding it, an argument, a value a function
257    /// returns, a fallback over it (`x.setupFiles ?? []`) and an array spreading it. Only the values the evaluator
258    /// hands out drop it.
259    Maybe(Box<Value>),
260}
261
262impl Value {
263    /// The value of `key` when this is an object, as [`ConfigValue::get`] finds it.
264    fn get(&self, key: &str) -> Option<&Value> {
265        match self {
266            Self::Object(entries) => entries.iter().rev().find(|(k, _)| k == key).map(|(_, v)| v),
267            _ => None,
268        }
269    }
270
271    /// The string when this is a string.
272    fn as_str(&self) -> Option<&str> {
273        match self {
274            Self::String(s) => Some(s),
275            _ => None,
276        }
277    }
278
279    /// `value`, or a [`Value::Maybe`] of it when it is not `definite`. An unknown value is unknown either way.
280    fn doubtful(value: Self, definite: bool) -> Self {
281        if definite || matches!(value, Self::Unknown | Self::Maybe(_)) {
282            value
283        } else {
284            Self::Maybe(Box::new(value))
285        }
286    }
287
288    /// The value without its [`Value::Maybe`], and whether it is definite.
289    fn split(self) -> (Self, bool) {
290        match self {
291            Self::Maybe(value) => (*value, false),
292            value => (value, true),
293        }
294    }
295
296    /// Whether the value is definite, not a [`Value::Maybe`].
297    fn is_definite(&self) -> bool {
298        !matches!(self, Self::Maybe(_))
299    }
300
301    /// The value inside a [`Value::Maybe`], or the value itself.
302    fn definite(&self) -> &Self {
303        match self {
304            Self::Maybe(value) => value,
305            value => value,
306        }
307    }
308
309    /// The value inside a [`Value::Maybe`], or the value itself, to change in place.
310    fn definite_mut(&mut self) -> &mut Self {
311        match self {
312            Self::Maybe(value) => value,
313            value => value,
314        }
315    }
316}
317
318impl From<ConfigValue> for Value {
319    fn from(value: ConfigValue) -> Self {
320        match value {
321            ConfigValue::Null => Self::Null,
322            ConfigValue::Bool(b) => Self::Bool(b),
323            ConfigValue::Number(n) => Self::Number(n),
324            ConfigValue::String(s) => Self::String(s),
325            ConfigValue::Array(items) => Self::Array(items.into_iter().map(Self::from).collect()),
326            ConfigValue::Object(entries) => Self::Object(
327                entries
328                    .into_iter()
329                    .map(|(key, value)| (key, Self::from(value)))
330                    .collect(),
331            ),
332            ConfigValue::Regex { pattern, flags } => Self::Regex { pattern, flags },
333            ConfigValue::Unknown => Self::Unknown,
334            ConfigValue::OneOf(alternatives) => Self::OneOf(alternatives.into_iter().map(Self::from).collect()),
335        }
336    }
337}
338
339impl From<Value> for ConfigValue {
340    fn from(value: Value) -> Self {
341        match value {
342            Value::Null | Value::Undefined => Self::Null,
343            Value::Bool(b) => Self::Bool(b),
344            Value::Number(n) => Self::Number(n),
345            Value::String(s) => Self::String(s),
346            Value::Array(items) => Self::Array(items.into_iter().map(Self::from).collect()),
347            Value::Object(entries) => Self::Object(
348                entries
349                    .into_iter()
350                    .map(|(key, value)| (key, Self::from(value)))
351                    .collect(),
352            ),
353            Value::Regex { pattern, flags } => Self::Regex { pattern, flags },
354            Value::Unknown => Self::Unknown,
355            Value::OneOf(alternatives) => {
356                // `null` and `undefined` are both handed out as `null`.
357                let mut out: Vec<Self> = Vec::new();
358                for alternative in alternatives.into_iter().map(Self::from) {
359                    if !out.contains(&alternative) {
360                        out.push(alternative);
361                    }
362                }
363                match out.len() {
364                    1 => out.pop().unwrap_or(Self::Unknown),
365                    _ => Self::OneOf(out),
366                }
367            }
368            Value::Maybe(value) => Self::from(*value),
369        }
370    }
371}
372
373/// Finds the modules a config file imports, so their values can be evaluated statically too.
374///
375/// The evaluator asks for every module a config imports a value from (`import base from './base'`,
376/// `import { vitestConfig } from '@scope/vitest-config/frontend'`, `require('./base')`). An implementation returns the
377/// source of the modules worth following, typically files of the project itself and of its workspace packages, and
378/// `None` for anything else (an installed package, a builtin), whose values then stay [`ConfigValue::Unknown`].
379pub trait ModuleLoader {
380    /// The path and source of the module `specifier` names when imported from the file `from`.
381    fn load(&self, from: &Path, specifier: &str) -> Option<(PathBuf, String)>;
382
383    /// Whether `specifier`, a bare specifier imported from the file `from` that [`Self::load`] does not load, names an
384    /// installed package rather than code of the project: not a workspace package the loader cannot map to a source
385    /// file, nor an alias of a project directory (a `tsconfig` path such as `@app/tools`). Only for an installed
386    /// package does the evaluator take a `define…` helper (`defineConfig`) to be the value it is given, a copying merge
387    /// (`mergeConfig`) the merge of what it is given, and the methods of a binding the config only calls methods on to
388    /// keep nothing they are given; code of the project may do anything. `false` unless the loader says otherwise, so
389    /// that nothing it does not vouch for is trusted. Without a loader, every bare specifier counts as one.
390    fn is_installed(&self, _from: &Path, _specifier: &str) -> bool {
391        false
392    }
393
394    /// Whether `specifier`, a relative import from the file `from` that [`Self::load`] does not load, names a file of
395    /// the project that holds no code: a declaration file (`./types` beside `types.d.ts`), JSON, a stylesheet or an
396    /// asset. A re-export of one changes nothing; one of any other module the loader does not load may run code of the
397    /// project. `false` unless the loader says otherwise.
398    fn holds_no_code(&self, _from: &Path, _specifier: &str) -> bool {
399        false
400    }
401}
402
403/// Statically evaluates what `source` exports: `export default …`, `module.exports = …` or `export = …`.
404///
405/// Understood: object, array, string, number, boolean and `null` literals; template literals whose substitutions are
406/// known strings, numbers or booleans, and `+` of such values with a string (or of two numbers);
407/// `as`/`satisfies`/`!`/parenthesised wrappers; calls named `define*` (such as `defineConfig`) of a global or of a
408/// package that is not followed, which evaluate to their first argument; arrow and function expressions that return a
409/// single expression; spreads of static values; and identifiers that name a top-level `const` initialised with something static; and paths computed from the config
410/// file's own location (`__dirname`, `import.meta.dirname`, `new URL('./x', import.meta.url)`, `path.resolve`,
411/// `path.join`, `path.dirname` and `fileURLToPath`, however `path` was imported), which evaluate to strings relative to
412/// the config file's directory (`"."`, `"./src"`, `"../shared"`).
413///
414/// `a ? b : c` takes the branch a known test selects; with an unknown test it is what both branches share (for two
415/// objects, every key either has, one of their values where they differ and unknown where only one has it), and for
416/// other values that differ [`ConfigValue::OneOf`] the two (`process.env.CI ? 'github' : 'list'`), unknown when a
417/// branch is unknown or there would be more than eight alternatives. `a && b`, `a || b`, `a ?? b` and `!a`
418/// are followed when `a` is known, and `.length` of a known string or array is its length. A member `a.b` of a known
419/// object is the value of its key `b`, unknown when an unknown spread may set it or the object may inherit it (a member
420/// of `Object.prototype`, an object with a `__proto__`), and `undefined` (handed out as `null`) when the object does
421/// not have it, so `[...base.plugins, 'x']` and `options.setupFiles ?? []` are known. An optional member `a?.b` reads
422/// the same way and is `undefined` when `a` is `null` or `undefined`, the rest of its chain unread, and so does a
423/// computed member whose key evaluates to a string (`a['b']`, `a[KEY]`). A destructuring reads a key the same way,
424/// and a parameter's or a destructuring's default applies to `undefined`, not to a literal `null`. A key merged from a
425/// spread whose test is unknown (below) may be absent or hold any of the values merged for it, so a read of it is not
426/// definite, and neither is a value that carries the read on: a key of an object or a binding holding
427/// it, the argument it is passed as, the value a function returns it as, a fallback over it and an array spreading it,
428/// in the module that reads it or one importing it. Over a value that is not definite, `a ?? c` and `a || c` credit
429/// both sides, a default is credited next to it, and a test on it and its `.length` are unknown.
430/// An `if (test) return a;` before a function's last `return` is followed the same way: a known test takes its branch,
431/// and an unknown one makes the value what both returns share. A spread of a conditional with an unknown test
432/// (`...(tmp && { cacheDir: tmp })`, `...(ci ? { a } : {})`) adds every key a branch sets: a list gets the elements of
433/// every branch (a string counting as a list of one), a nested object merges the same way, any other value every
434/// branch sets is one of them and one only some branches set is unknown; only a branch that is not an object (or
435/// `null`, a boolean, a number or an empty string) leaves the spread unknown. A spread of a value that is one of
436/// several adds the keys of each the same way. A path function, a template literal or a `+` over a value that is one
437/// of several is one of the results of each combination (`path.join(ci ? 'a' : 'b', 'setup.ts')` is one of two
438/// paths), unknown when a result is unknown or there would be more than eight. Spread into an array, such a condition
439/// or value adds the elements of every branch (`[...(ci ? ['a'] : [])]`), a branch of `null`, `undefined` or a
440/// boolean none. `.filter(Boolean)` of a known array drops the falsy elements and keeps one that may be falsy as the
441/// values it has when it is not (`[ci ? 'github' : null, ci && 'blob'].filter(Boolean)` keeps `'github'` and
442/// `'blob'`).
443/// `.map`, `.flatMap` and `.filter` of a known array call an inline callback, or one the module declares, with each
444/// element and its index (`` ['light', 'dark'].map((theme) => ({ name: `browser-${theme}` })) ``); an unknown element,
445/// which may stand for a spread of any length, stays unknown, and a predicate whose result is not known keeps the
446/// element and leaves the length unknown. `Object.fromEntries` of a known list of `[key, value]` pairs is their object,
447/// an element that is not such a pair adding an unknown spread. `a === b` and `a !== b` of known primitives are known,
448/// and a known array's element at a constant index (`LIST[0]`) is its value, `undefined` past its end and unknown at
449/// or after an unknown element.
450///
451/// Functions are followed: a returned config function may declare local `const`/`let` bindings and local functions,
452/// assign members of a local object, and call top-level or local functions with known arguments, a parameter not passed
453/// taking its default and an object pattern parameter (`{ pinCjs = [] } = {}`) its properties or their defaults. A
454/// function the tool calls (the exported config function, the callback given to `defineConfig`) is passed what the
455/// config does not say, so each of its parameters is unknown, a default included: `(env = {}) => env.analyze ? a : b`
456/// keeps both branches. `await x` and
457/// `Promise.resolve(x)` evaluate to `x`; `require.resolve('pkg')` and `import.meta.resolve('pkg')` evaluate to
458/// `'pkg'`, and the storybook idioms `dirname(require.resolve(join('pkg', 'package.json')))` and
459/// `` dirname(fileURLToPath(import.meta.resolve(`${pkg}/package.json`))) `` to `'pkg'`. A call of a glob function
460/// (`glob`, `fast-glob`, `globby`, `tinyglobby`, however imported) evaluates to the array of its literal patterns, so a
461/// dynamic `entry` becomes entry globs. `process.cwd()` and a first `path.resolve` argument that is not absolute are
462/// taken to be the config file's directory, which is right when the tool runs from the package that holds the
463/// config and a heuristic otherwise. The directory itself is `"."`, so `dirname(__dirname)` is `".."`. A URL with a
464/// scheme (`new URL(x, 'https://…')`, `fileURLToPath('file:///…')`) and `.href` are [`ConfigValue::Unknown`], as is
465/// everything else. Returns `None` when the file does not parse or exports nothing recognisable.
466///
467/// Imports are not followed; [`extract_config`] with a loader follows them.
468#[must_use]
469pub fn extract_config_object(source: &str, path: &Path) -> Option<ConfigValue> {
470    evaluate_config(source, path, None).map(|config| config.value)
471}
472
473/// What a config file exports, with the arguments of the calls it is made of that cannot be followed.
474#[derive(Debug, Clone, PartialEq)]
475#[non_exhaustive]
476pub struct ExtractedConfig {
477    /// The exported value, as [`extract_config_object`] evaluates it.
478    pub value: ConfigValue,
479    /// The evaluated arguments of a call the evaluator cannot follow (`createConfig({ setupFiles: ['./setup.ts'] })`
480    /// of an installed package) whose result is the exported value, which is then [`ConfigValue::Unknown`], or is an
481    /// argument of the merge the file exports (`mergeConfig(createBase({ … }), { … })`), which then has an unknown
482    /// spread. A `define*` call (`defineConfig(createBase(options))`) is looked through to the expression it wraps, a
483    /// top-level constant to its initialiser, and a config function the tool calls whose body is one expression or a
484    /// single `return` (`export default () => createConfig({ … })`) to what it returns, its parameters unknown.
485    /// Such a call nested in an object or array literal of that value keeps its arguments too, each in an object under
486    /// the keys it is found at: `{ test: createTest({ setupFiles }) }` keeps `{ test: { setupFiles } }`, so a file
487    /// named under a key that excludes files stays under it. The values are those the evaluation computed. Empty
488    /// otherwise; an unfollowed call anywhere else, such as one a function body computes in a local first, keeps no
489    /// arguments.
490    pub call_arguments: Vec<ConfigValue>,
491}
492
493/// Statically evaluates what `source` exports, as [`extract_config_object`] does, keeping the arguments of the calls
494/// it is made of that cannot be followed ([`ExtractedConfig::call_arguments`]).
495///
496/// With a `loader`, imports are followed: a value imported from another module (a default or named export, a
497/// namespace member, a `require`d `module.exports`) is evaluated statically in that module, and a call of an imported
498/// function with the arguments given. A chain of imports reaches at most eight modules, the config file included, an
499/// export whose value depends on itself through imports is [`ConfigValue::Unknown`] there, and one config loads at
500/// most 64 distinct modules and evaluates at most 1024 imported exports and calls. Paths an imported module computes
501/// from its own location (`__dirname`, `import.meta.url`) come back relative to the directory of the config file at
502/// `path`. Without one, imports are unknown.
503#[must_use]
504pub fn extract_config(source: &str, path: &Path, loader: Option<&dyn ModuleLoader>) -> Option<ExtractedConfig> {
505    let Some(loader) = loader else {
506        return evaluate_config(source, path, None);
507    };
508    let modules = Modules {
509        loader,
510        root: path.parent().map_or_default(Path::to_path_buf),
511        active: RefCell::new(vec![(path.to_path_buf(), Imported::Default)]),
512        loads: RefCell::new(HashSet::default()),
513        evaluations: Cell::new(0),
514        cuts: Cell::new(0),
515        values: RefCell::new(HashMap::default()),
516        loaded: RefCell::new(HashMap::default()),
517        changing: RefCell::new(HashMap::default()),
518        pending: RefCell::new(Vec::new()),
519        shallowest_cut: Cell::new(usize::MAX),
520        analyses: Cell::new(0),
521        installed: RefCell::new(HashMap::default()),
522    };
523    evaluate_config(source, path, Some(&modules))
524}
525
526/// The exported value [`extract_config`] finds with `loader`, following the imports it finds.
527#[must_use]
528pub fn extract_config_object_with(source: &str, path: &Path, loader: &dyn ModuleLoader) -> Option<ConfigValue> {
529    extract_config(source, path, Some(loader)).map(|config| config.value)
530}
531
532fn evaluate_config(source: &str, path: &Path, modules: Option<&Modules<'_>>) -> Option<ExtractedConfig> {
533    crate::with_arena(|allocator| {
534        let program = parse_program(allocator, source, path)?;
535        let followed = modules.map(|modules| FollowedImports { modules, from: path });
536        let module_imports: &dyn Imports = match &followed {
537            Some(followed) => followed,
538            None => &NoImports,
539        };
540        let top = top_level(&program, module_imports);
541        let bindings = module_bindings(&program);
542        let imports = import_bindings(&program);
543        let file = path.file_name().and_then(|name| name.to_str()).unwrap_or_default();
544        let eval = Evaluator::new(&top, &bindings, &imports, file, ".", path, modules);
545        let exported = exported(&program)?;
546        let value = eval.exported_value(exported, exports_changed(&program));
547        let call_arguments = eval.exported_call_arguments(exported, &value);
548        Some(ExtractedConfig {
549            value: ConfigValue::from(value),
550            call_arguments: call_arguments.into_iter().map(ConfigValue::from).collect(),
551        })
552    })
553}
554
555/// What an import binds: a module's default export, one named export, or the module namespace.
556#[derive(Debug, Clone, PartialEq, Eq, Hash)]
557enum Imported {
558    Default,
559    Named(String),
560    Namespace,
561}
562
563impl Imported {
564    fn named(name: String) -> Self {
565        if name == "default" {
566            Self::Default
567        } else {
568            Self::Named(name)
569        }
570    }
571}
572
573/// A local name bound by an `import` declaration.
574struct ImportBinding {
575    specifier: String,
576    imported: Imported,
577}
578
579/// The value imports of a module by local name; type-only imports bind no value.
580fn import_bindings(program: &Program<'_>) -> HashMap<String, ImportBinding> {
581    let mut imports = HashMap::default();
582    for stmt in &program.body {
583        let Statement::ImportDeclaration(d) = stmt else {
584            continue;
585        };
586        if d.import_kind.is_type() {
587            continue;
588        }
589        let specifier = crate::js_string(d.source.value);
590        for item in d.specifiers.iter().flatten() {
591            let (local, imported) = match item {
592                ImportDeclarationSpecifier::ImportSpecifier(s) if s.import_kind == ImportOrExportKind::Value => (
593                    s.local.name.as_str(),
594                    Imported::named(crate::collect::module_export_name(&s.imported)),
595                ),
596                ImportDeclarationSpecifier::ImportSpecifier(_) => continue,
597                ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => (s.local.name.as_str(), Imported::Default),
598                ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => (s.local.name.as_str(), Imported::Namespace),
599            };
600            imports.insert(
601                local.to_owned(),
602                ImportBinding {
603                    specifier: specifier.clone(),
604                    imported,
605                },
606            );
607        }
608    }
609    imports
610}
611
612/// Where the value of an export comes from.
613enum ExportSource {
614    /// A binding of the module itself, by local name.
615    Local(String),
616    /// An export of another module (`export { a as b } from './x'`).
617    From(String, Imported),
618}
619
620/// The named exports of a module, and the modules it re-exports everything of (`export * from './x'`).
621fn export_table(program: &Program<'_>) -> (HashMap<String, ExportSource>, Vec<String>) {
622    let mut table = HashMap::default();
623    let mut stars = Vec::new();
624    for stmt in &program.body {
625        match stmt {
626            Statement::ExportDeclaration(e) => match &e.declaration {
627                Declaration::VariableDeclaration(v) => {
628                    for d in &v.declarations {
629                        if let BindingPattern::BindingIdentifier(id) = &d.id {
630                            let name = id.name.as_str().to_owned();
631                            table.insert(name.clone(), ExportSource::Local(name));
632                        }
633                    }
634                }
635                Declaration::FunctionDeclaration(f) => {
636                    if let Some(id) = &f.id {
637                        let name = id.name.as_str().to_owned();
638                        table.insert(name.clone(), ExportSource::Local(name));
639                    }
640                }
641                _ => {}
642            },
643            Statement::ExportNamedDeclaration(e) if !e.export_kind.is_type() => {
644                for s in e.specifiers.iter().filter(|s| !s.export_kind.is_type()) {
645                    table.insert(
646                        crate::collect::module_export_name(&s.exported),
647                        ExportSource::Local(crate::collect::module_export_name(&s.local)),
648                    );
649                }
650            }
651            Statement::ExportFromDeclaration(e) if !e.export_kind.is_type() => {
652                let from = crate::js_string(e.source.value);
653                for s in e.specifiers.iter().filter(|s| !s.export_kind.is_type()) {
654                    table.insert(
655                        crate::collect::module_export_name(&s.exported),
656                        ExportSource::From(
657                            from.clone(),
658                            Imported::named(crate::collect::module_export_name(&s.local)),
659                        ),
660                    );
661                }
662            }
663            Statement::ExportAllDeclaration(e) if !e.export_kind.is_type() => {
664                let from = crate::js_string(e.source.value);
665                match &e.exported {
666                    Some(name) => {
667                        table.insert(
668                            crate::collect::module_export_name(name),
669                            ExportSource::From(from, Imported::Namespace),
670                        );
671                    }
672                    None => stars.push(from),
673                }
674            }
675            _ => {}
676        }
677    }
678    (table, stars)
679}
680
681/// The path and source of a module the loader found.
682type LoadedModule = (PathBuf, Rc<str>);
683
684/// Follows the imports of a config and the modules it reaches.
685struct Modules<'l> {
686    loader: &'l dyn ModuleLoader,
687    /// The directory of the config file, which paths computed in imported modules are relative to.
688    root: PathBuf,
689    /// The exports being evaluated, outermost (the config file's default export) first. One that depends on itself
690    /// is unknown; other exports of the same module are not, as ES modules may import each other.
691    active: RefCell<Vec<(PathBuf, Imported)>>,
692    /// The modules evaluated so far.
693    loads: RefCell<HashSet<PathBuf>>,
694    /// How many exports have been evaluated so far, calls with arguments included.
695    evaluations: Cell<usize>,
696    /// How many times an evaluation was cut short (a cycle, the depth limit, a budget) and gave
697    /// [`ConfigValue::Unknown`]. A value computed while this grew depends on what else was being evaluated at the
698    /// time, so it is not cached.
699    cuts: Cell<usize>,
700    /// The value of every export evaluated without arguments so far, by module and export, computed without a cut.
701    values: RefCell<HashMap<(PathBuf, Imported), Option<Value>>>,
702    /// What the loader returned for each importing directory and specifier.
703    loaded: RefCell<HashMap<(PathBuf, String), Option<LoadedModule>>>,
704    /// What the analysis of each module found ([`Modules::facts`]), by path: `None` while that is being found.
705    changing: RefCell<HashMap<PathBuf, Option<Rc<ModuleFacts>>>>,
706    /// The modules being analysed, outermost first.
707    pending: RefCell<Vec<PathBuf>>,
708    /// The index in [`Self::pending`] of the outermost module an analysis under way found pending, or past a limit
709    /// (`0`): what it finds then depends on that module, so it is not kept for a module inside it.
710    shallowest_cut: Cell<usize>,
711    /// How many module analyses have run, kept or not ([`MAX_MODULE_ANALYSES`]).
712    analyses: Cell<usize>,
713    /// What the loader said of each importing directory and specifier ([`ModuleLoader::is_installed`]).
714    installed: RefCell<HashMap<(PathBuf, String), bool>>,
715}
716
717impl Modules<'_> {
718    /// The module `specifier` names from `from`, asking the loader once per directory and specifier.
719    fn load(&self, from: &Path, specifier: &str) -> Option<LoadedModule> {
720        let key = (from.parent().unwrap_or(from).to_path_buf(), specifier.to_owned());
721        if let Some(known) = self.loaded.borrow().get(&key) {
722            return known.clone();
723        }
724        let module = self
725            .loader
726            .load(from, specifier)
727            .map(|(path, source)| (path, Rc::from(source)));
728        self.loaded.borrow_mut().insert(key, module.clone());
729        module
730    }
731
732    /// Whether the module `specifier` names from `from` is followed.
733    fn follows(&self, from: &Path, specifier: &str) -> bool {
734        self.load(from, specifier).is_some()
735    }
736
737    /// Whether `specifier` names an installed package from `from` ([`ModuleLoader::is_installed`]), asking the loader
738    /// once per directory and specifier.
739    fn is_installed(&self, from: &Path, specifier: &str) -> bool {
740        let key = (from.parent().unwrap_or(from).to_path_buf(), specifier.to_owned());
741        if let Some(&known) = self.installed.borrow().get(&key) {
742            return known;
743        }
744        let installed = self.loader.is_installed(from, specifier);
745        self.installed.borrow_mut().insert(key, installed);
746        installed
747    }
748
749    /// Whether a function of the followed module `specifier` names from `from` may change a value of a module when it
750    /// is called ([`Unstable::changes_modules`]), one it re-exports or hands out included
751    /// ([`Self::reexports_change_modules`], [`Unstable::exposed_modules`]): so it may when the module does not parse,
752    /// when finding out would reach past [`MAX_IMPORT_DEPTH`] or [`MAX_MODULE_LOADS`] modules, and in a cycle of modules
753    /// whose functions call each other's. Not one of a module that is not followed, whose functions the config's
754    /// analysis does not reach.
755    fn changes_modules(&self, from: &Path, specifier: &str) -> bool {
756        self.facts(from, specifier).is_some_and(|(_, facts)| facts.changes)
757    }
758
759    /// The places in what a call of the export `export` of the followed module `specifier` names from `from` returns
760    /// that may hold an object of a value of a module ([`Unstable::result_places`]), the export followed through
761    /// re-exports; `None` when that is not known, as for an export that is not a function the module declares.
762    fn result_places(&self, from: &Path, specifier: &str, export: &str) -> Option<Vec<Write>> {
763        match self.export_result_places(from, specifier, export, 0) {
764            ExportResult::Places(places) => Some(places),
765            ExportResult::Missing | ExportResult::Unknown => None,
766        }
767    }
768
769    /// [`Self::result_places`], `depth` re-exports deep.
770    fn export_result_places(&self, from: &Path, specifier: &str, export: &str, depth: usize) -> ExportResult {
771        if depth >= MAX_IMPORT_DEPTH {
772            return ExportResult::Unknown;
773        }
774        let Some((path, facts)) = self.facts(from, specifier) else {
775            return ExportResult::Missing;
776        };
777        let local = match facts.exports.get(export) {
778            Some(ExportSource::Local(local)) => Some(local.as_str()),
779            Some(ExportSource::From(next, imported)) => {
780                let name = match imported {
781                    Imported::Default => "default",
782                    Imported::Named(name) => name.as_str(),
783                    Imported::Namespace => return ExportResult::Unknown,
784                };
785                return match self.export_result_places(&path, next, name, depth + 1) {
786                    ExportResult::Missing => ExportResult::Unknown,
787                    found => found,
788                };
789            }
790            None if export == "default" => facts.default.as_deref(),
791            None => {
792                return facts
793                    .stars
794                    .iter()
795                    .map(|next| self.export_result_places(&path, next, export, depth + 1))
796                    .find(|found| !matches!(found, ExportResult::Missing))
797                    .unwrap_or(ExportResult::Missing);
798            }
799        };
800        local
801            .and_then(|local| facts.results.get(local).cloned())
802            .map_or(ExportResult::Unknown, ExportResult::Places)
803    }
804
805    /// What the analysis of the followed module `specifier` names from `from` finds, with its path; what may hold for
806    /// any module ([`ModuleFacts::unknown`]) while the module is being analysed, past [`MAX_IMPORT_DEPTH`] modules being
807    /// analysed, [`MAX_MODULE_LOADS`] analysed or [`MAX_MODULE_ANALYSES`] analyses run, and when the module does not parse. What one finds while a module that
808    /// encloses it is pending is not kept, as it depends on that module.
809    fn facts(&self, from: &Path, specifier: &str) -> Option<(PathBuf, Rc<ModuleFacts>)> {
810        let (path, source) = self.load(from, specifier)?;
811        if let Some(known) = self.changing.borrow().get(&path) {
812            let facts = known.clone().unwrap_or_else(|| {
813                let at = self.pending.borrow().iter().position(|pending| *pending == path);
814                self.shallowest_cut.set(self.shallowest_cut.get().min(at.unwrap_or(0)));
815                Rc::new(ModuleFacts::unknown())
816            });
817            return Some((path, facts));
818        }
819        let depth = self.pending.borrow().len();
820        if depth >= MAX_IMPORT_DEPTH
821            || self.changing.borrow().len() >= MAX_MODULE_LOADS
822            || self.analyses.get() >= MAX_MODULE_ANALYSES
823        {
824            self.shallowest_cut.set(0);
825            return Some((path, Rc::new(ModuleFacts::unknown())));
826        }
827        self.analyses.set(self.analyses.get() + 1);
828        self.changing.borrow_mut().insert(path.clone(), None);
829        self.pending.borrow_mut().push(path.clone());
830        let outer = self.shallowest_cut.replace(usize::MAX);
831        let facts = Rc::new(self.module_facts(&path, &source));
832        self.pending.borrow_mut().pop();
833        let cut = self.shallowest_cut.get();
834        if cut < depth {
835            self.changing.borrow_mut().remove(&path);
836            self.shallowest_cut.set(outer.min(cut));
837        } else {
838            self.changing.borrow_mut().insert(path.clone(), Some(Rc::clone(&facts)));
839            self.shallowest_cut.set(outer);
840        }
841        Some((path, facts))
842    }
843
844    /// [`Self::facts`] of the module at `path` with the text `source`.
845    fn module_facts(&self, path: &Path, source: &str) -> ModuleFacts {
846        let Ok(source_type) = SourceType::from_path(path) else {
847            return ModuleFacts::unknown();
848        };
849        let allocator = Allocator::default();
850        let ret = Parser::new(&allocator, source, source_type).parse();
851        if ret.diagnostics.errors().next().is_some() {
852            return ModuleFacts::unknown();
853        }
854        let unstable = Unstable::of(
855            &ret.program,
856            &FollowedImports {
857                modules: self,
858                from: path,
859            },
860        );
861        let changes = unstable.changes_modules()
862            || unstable
863                .exposed_modules()
864                .iter()
865                .any(|specifier| self.changes_modules(path, specifier))
866            || self.reexports_change_modules(path, &ret.program);
867        let (exports, stars) = export_table(&ret.program);
868        let default = exported(&ret.program).and_then(|exported| match exported {
869            Exported::Function(function) => function.id.as_ref().map(|id| id.name.as_str().to_owned()),
870            Exported::Expression(expr) => match expr.get_inner_expression() {
871                Expression::Identifier(id) => Some(id.name.as_str().to_owned()),
872                _ => None,
873            },
874        });
875        ModuleFacts {
876            changes,
877            results: unstable.result_places().clone(),
878            exports,
879            stars,
880            default,
881        }
882    }
883
884    /// Whether a function the module at `path` re-exports (`export { tweak } from './tweak'`, `export * from './tools'`)
885    /// may change a value of a module when it is called: one of a followed module that may ([`Self::changes_modules`]),
886    /// or of a module that is neither followed, nor an installed package ([`ModuleLoader::is_installed`]), nor a file
887    /// that holds no code ([`ModuleLoader::holds_no_code`]), whose code is not known.
888    fn reexports_change_modules(&self, path: &Path, program: &Program<'_>) -> bool {
889        reexported(program).into_iter().any(|specifier| {
890            if self.follows(path, &specifier) {
891                self.changes_modules(path, &specifier)
892            } else {
893                !self.is_installed(path, &specifier) && !self.loader.holds_no_code(path, &specifier)
894            }
895        })
896    }
897
898    /// The value `imported` names in the module `specifier` resolves to from `from`, called with `args` when given.
899    /// `None` when the module is not followed or does not have the export.
900    fn evaluate(&self, from: &Path, specifier: &str, imported: &Imported, args: Option<&[Value]>) -> Option<Value> {
901        let (path, source) = self.load(from, specifier)?;
902        let key = (path.clone(), imported.clone());
903        if args.is_none()
904            && let Some(known) = self.values.borrow().get(&key)
905        {
906            return known.clone();
907        }
908        if self.cut(&key) {
909            self.cuts.set(self.cuts.get() + 1);
910            return Some(Value::Unknown);
911        }
912        self.loads.borrow_mut().insert(path.clone());
913        self.evaluations.set(self.evaluations.get() + 1);
914        let cuts = self.cuts.get();
915        self.active.borrow_mut().push(key.clone());
916        let value = self.evaluate_module(&path, &source, imported, args).map(as_imported);
917        self.active.borrow_mut().pop();
918        if args.is_none() && self.cuts.get() == cuts {
919            self.values.borrow_mut().insert(key, value.clone());
920        }
921        value
922    }
923
924    /// Whether evaluating the export `key` would depend on itself, nest past [`MAX_IMPORT_DEPTH`] modules or exceed a
925    /// budget.
926    fn cut(&self, key: &(PathBuf, Imported)) -> bool {
927        let active = self.active.borrow();
928        let loads = self.loads.borrow();
929        active.contains(key)
930            || active.len() >= MAX_IMPORT_DEPTH
931            || (!loads.contains(&key.0) && loads.len() >= MAX_MODULE_LOADS)
932            || self.evaluations.get() >= MAX_MODULE_EVALUATIONS
933    }
934
935    fn evaluate_module(&self, path: &Path, source: &str, imported: &Imported, args: Option<&[Value]>) -> Option<Value> {
936        let source_type = SourceType::from_path(path).ok()?;
937        let allocator = Allocator::default();
938        let ret = Parser::new(&allocator, source, source_type)
939            .with_options(OxcParseOptions {
940                preserve_parens: false,
941                ..OxcParseOptions::default()
942            })
943            .parse();
944        if ret.diagnostics.errors().next().is_some() {
945            return Some(Value::Unknown);
946        }
947        let program = &ret.program;
948        let top = top_level(
949            program,
950            &FollowedImports {
951                modules: self,
952                from: path,
953            },
954        );
955        let bindings = module_bindings(program);
956        let imports = import_bindings(program);
957        let dir = relative_dir(&self.root, path.parent().unwrap_or(path));
958        let file = path.file_name().and_then(|name| name.to_str()).unwrap_or_default();
959        let eval = Evaluator::new(&top, &bindings, &imports, file, &dir, path, Some(self));
960        match imported {
961            Imported::Namespace => Some(Value::Unknown),
962            Imported::Default => Some(match exported(program)? {
963                Exported::Function(function) => eval.invoke(Callable::Function(function), args, true, 0),
964                Exported::Expression(expr) => {
965                    match (args, Callable::of_expression(expr), expr.get_inner_expression()) {
966                        (None, _, _) => eval.expr(expr, 0),
967                        (Some(args), Some(callable), _) => eval.invoke(callable, Some(args), true, 0),
968                        (Some(args), None, Expression::Identifier(id)) => eval.call_named(id.name.as_str(), args),
969                        (Some(_), None, _) => Value::Unknown,
970                    }
971                }
972            })
973            .map(|value| {
974                if exports_changed(program) {
975                    changed(value)
976                } else {
977                    value
978                }
979            }),
980            Imported::Named(name) => {
981                let (table, stars) = export_table(program);
982                match table.get(name) {
983                    Some(ExportSource::Local(local)) => Some(match args {
984                        None => eval.identifier(local, 0),
985                        Some(args) => eval.call_named(local, args),
986                    }),
987                    Some(ExportSource::From(from, imported)) => self.evaluate(path, from, imported, args),
988                    None => stars.iter().find_map(|from| self.evaluate(path, from, imported, args)),
989                }
990            }
991        }
992    }
993}
994
995/// The modules `program` re-exports a value of (`export { tweak } from './tweak'`, `export * from './tools'`), by
996/// specifier.
997fn reexported(program: &Program<'_>) -> Vec<String> {
998    program
999        .body
1000        .iter()
1001        .filter_map(|stmt| match stmt {
1002            Statement::ExportFromDeclaration(e) if !e.export_kind.is_type() => Some(&e.source),
1003            Statement::ExportAllDeclaration(e) if !e.export_kind.is_type() => Some(&e.source),
1004            _ => None,
1005        })
1006        .map(|source| crate::js_string(source.value))
1007        .collect()
1008}
1009
1010/// What a followed module's export returns, as far as [`Modules::export_result_places`] finds it.
1011enum ExportResult {
1012    /// The module is not followed or has no such export.
1013    Missing,
1014    /// The module has the export, and what it returns is not known.
1015    Unknown,
1016    /// The places of the module's values the export may return.
1017    Places(Vec<Write>),
1018}
1019
1020/// What the analysis of a followed module finds ([`Modules::facts`]).
1021struct ModuleFacts {
1022    /// Whether a function of the module may change a value of a module when it is called ([`Modules::changes_modules`]).
1023    changes: bool,
1024    /// The places in what a call of each top-level function of the module returns that may hold an object of a value
1025    /// of a module, by name ([`Unstable::result_places`]).
1026    results: HashMap<String, Vec<Write>>,
1027    /// The named exports of the module ([`export_table`]).
1028    exports: HashMap<String, ExportSource>,
1029    /// The modules it re-exports everything of.
1030    stars: Vec<String>,
1031    /// The name of the binding it exports as its default, when it exports one.
1032    default: Option<String>,
1033}
1034
1035impl ModuleFacts {
1036    /// What may hold for any module: its functions may change a value of a module, and nothing is known of what they
1037    /// return.
1038    fn unknown() -> Self {
1039        Self {
1040            changes: true,
1041            results: HashMap::default(),
1042            exports: HashMap::default(),
1043            stars: Vec::new(),
1044            default: None,
1045        }
1046    }
1047}
1048
1049/// A value of another module as the module importing it sees it: what that module computes itself is a value taken
1050/// from elsewhere, so each [`OWN_SPREAD`] becomes an [`UNKNOWN_SPREAD`].
1051fn as_imported(value: Value) -> Value {
1052    match value {
1053        Value::Object(entries) => Value::Object(
1054            entries
1055                .into_iter()
1056                .map(|(key, value)| {
1057                    let key = if key == OWN_SPREAD {
1058                        UNKNOWN_SPREAD.to_owned()
1059                    } else {
1060                        key
1061                    };
1062                    (key, as_imported(value))
1063                })
1064                .collect(),
1065        ),
1066        Value::Array(items) => Value::Array(items.into_iter().map(as_imported).collect()),
1067        Value::OneOf(alternatives) => Value::OneOf(alternatives.into_iter().map(as_imported).collect()),
1068        Value::Maybe(value) => Value::Maybe(Box::new(as_imported(*value))),
1069        other => other,
1070    }
1071}
1072
1073/// The directory `to` relative to the directory `from`, lexically: `"."`, `"./a"` or `"../b"`.
1074fn relative_dir(from: &Path, to: &Path) -> String {
1075    let from: Vec<Component<'_>> = from.components().collect();
1076    let to: Vec<Component<'_>> = to.components().collect();
1077    let common = from.iter().zip(&to).take_while(|(a, b)| a == b).count();
1078    let mut parts: Vec<String> = vec!["..".to_owned(); from.len() - common];
1079    parts.extend(
1080        to[common..]
1081            .iter()
1082            .map(|part| part.as_os_str().to_string_lossy().into_owned()),
1083    );
1084    let parts: Vec<&str> = parts.iter().map(String::as_str).collect();
1085    normalized(&parts)
1086}
1087
1088/// Name of the first parameter of `callable`, when it is a plain identifier.
1089fn first_parameter(callable: Callable<'_, '_>) -> Option<String> {
1090    let params = match callable {
1091        Callable::Function(f) => &f.params,
1092        Callable::Arrow(f) => &f.params,
1093    };
1094    match &params.items.first()?.pattern {
1095        BindingPattern::BindingIdentifier(id) => Some(id.name.as_str().to_owned()),
1096        _ => None,
1097    }
1098}
1099
1100/// The top-level `const` initialisers and function declarations of a module.
1101struct TopLevel<'p, 'a> {
1102    consts: HashMap<String, &'p Expression<'a>>,
1103    functions: HashMap<String, Callable<'p, 'a>>,
1104    /// The names top-level statements other than function declarations assign or change (`config.plugins.push(x)`,
1105    /// `if (env) config.a = 1`, `const n = list.push(x)`), with how: a constant among them is not only its initialiser.
1106    changed: HashMap<String, TopChange>,
1107    /// The bindings code the evaluator does not follow may change.
1108    unstable: Unstable,
1109}
1110
1111/// A function the evaluator can call.
1112#[derive(Clone, Copy)]
1113enum Callable<'p, 'a> {
1114    Function(&'p Function<'a>),
1115    Arrow(&'p ArrowFunctionExpression<'a>),
1116}
1117
1118impl<'p, 'a> Callable<'p, 'a> {
1119    /// Whether both are the same function.
1120    fn same(self, other: Self) -> bool {
1121        match (self, other) {
1122            (Self::Function(a), Self::Function(b)) => std::ptr::eq(a, b),
1123            (Self::Arrow(a), Self::Arrow(b)) => std::ptr::eq(a, b),
1124            _ => false,
1125        }
1126    }
1127
1128    fn of_expression(expr: &'p Expression<'a>) -> Option<Self> {
1129        match expr.get_inner_expression() {
1130            Expression::ArrowFunctionExpression(f) => Some(Self::Arrow(f)),
1131            Expression::FunctionExpression(f) => Some(Self::Function(f)),
1132            _ => None,
1133        }
1134    }
1135}
1136
1137/// What a name is bound to in the functions being evaluated.
1138#[derive(Clone, Copy)]
1139enum LocalBinding<'p, 'a> {
1140    Function(Callable<'p, 'a>),
1141    Other,
1142}
1143
1144/// What a name is bound to inside a function being evaluated.
1145#[derive(Clone)]
1146enum Binding<'p, 'a> {
1147    Value(Value),
1148    Callable(Callable<'p, 'a>),
1149    /// A `const` that takes a part of a local of its function (`const { coverage } = config.test`), whose changes the
1150    /// evaluator applies to that local too ([`Unstable::follows`]).
1151    Alias(Alias),
1152}
1153
1154/// The value of an alias binding, and the part of a local it takes: the local `root`, bound in the frame at index
1155/// `frame`, at `keys` below it.
1156#[derive(Clone)]
1157struct Alias {
1158    value: Value,
1159    root: String,
1160    frame: usize,
1161    keys: Vec<String>,
1162}
1163
1164impl Alias {
1165    /// Whether `other` takes the same part of the same local.
1166    fn same_part(&self, other: &Self) -> bool {
1167        self.root == other.root && self.frame == other.frame && self.keys == other.keys
1168    }
1169}
1170
1171/// A change the evaluator applies to a local object.
1172enum Edit {
1173    /// `name.key = value`.
1174    Set(String, Value),
1175    /// `name.push(…)`, or with `front` `name.unshift(…)`: the list then holds `items` too.
1176    Insert { front: bool, items: Vec<Value> },
1177    /// A change it does not follow, which leaves the object partly unknown.
1178    Taint,
1179}
1180
1181/// How top-level statements the evaluator does not run change a top-level binding.
1182enum TopChange {
1183    /// They assign it: it may hold any value.
1184    Assigned,
1185    /// They change the parts of the objects it holds that these say.
1186    Written(Vec<Write>),
1187}
1188
1189type Frame<'p, 'a> = HashMap<String, Binding<'p, 'a>>;
1190
1191/// The imports of a module whose imports the evaluator does not follow.
1192struct NoImports;
1193
1194impl Imports for NoImports {
1195    fn follows(&self, _: &str) -> bool {
1196        false
1197    }
1198
1199    fn changes_modules(&self, _: &str) -> bool {
1200        false
1201    }
1202
1203    /// Every package is taken to be installed when no loader can tell.
1204    fn is_installed(&self, _: &str) -> bool {
1205        true
1206    }
1207}
1208
1209/// The imports of the module at `from`, as `modules` follows them.
1210struct FollowedImports<'m, 'l> {
1211    modules: &'m Modules<'l>,
1212    from: &'m Path,
1213}
1214
1215impl Imports for FollowedImports<'_, '_> {
1216    fn follows(&self, specifier: &str) -> bool {
1217        self.modules.follows(self.from, specifier)
1218    }
1219
1220    fn changes_modules(&self, specifier: &str) -> bool {
1221        self.modules.changes_modules(self.from, specifier)
1222    }
1223
1224    fn result_places(&self, specifier: &str, export: &str) -> Option<Vec<Write>> {
1225        self.modules.result_places(self.from, specifier, export)
1226    }
1227
1228    fn is_installed(&self, specifier: &str) -> bool {
1229        self.modules.is_installed(self.from, specifier)
1230    }
1231}
1232
1233/// The top-level bindings of `program`, where `imports` says what the evaluator knows of the modules it imports.
1234fn top_level<'p, 'a>(program: &'p Program<'a>, imports: &dyn Imports) -> TopLevel<'p, 'a> {
1235    let unstable = Unstable::of(program, imports);
1236    let mut top = TopLevel {
1237        consts: HashMap::default(),
1238        functions: HashMap::default(),
1239        changed: HashMap::default(),
1240        unstable: Unstable::default(),
1241    };
1242    for stmt in &program.body {
1243        let decl = match stmt {
1244            Statement::ExportDeclaration(e) => Some(&e.declaration),
1245            other => other.as_declaration(),
1246        };
1247        let writes = match stmt {
1248            // Only what the statement itself does, as for a statement of a function: an exported value may read
1249            // members of a constant (`base.plugins.map(f)`) without changing it.
1250            Statement::ExpressionStatement(s) => Some(Writes::of_effect(&s.expression, &unstable)),
1251            Statement::IfStatement(_)
1252            | Statement::BlockStatement(_)
1253            | Statement::ForStatement(_)
1254            | Statement::ForInStatement(_)
1255            | Statement::ForOfStatement(_)
1256            | Statement::WhileStatement(_)
1257            | Statement::DoWhileStatement(_)
1258            | Statement::SwitchStatement(_)
1259            | Statement::TryStatement(_)
1260            | Statement::LabeledStatement(_)
1261            | Statement::WithStatement(_)
1262            | Statement::VariableDeclaration(_)
1263            | Statement::ExportNamedDeclaration(_)
1264            | Statement::ExportDefaultDeclaration(_) => Some(Writes::of_statement(stmt, &unstable)),
1265            _ => None,
1266        };
1267        if let Some(writes) = writes {
1268            top.record(writes);
1269        }
1270        match decl {
1271            Some(Declaration::VariableDeclaration(v)) if v.kind == VariableDeclarationKind::Const => {
1272                for d in &v.declarations {
1273                    if let (BindingPattern::BindingIdentifier(id), Some(init)) = (&d.id, &d.init) {
1274                        top.consts.insert(id.name.as_str().to_string(), init);
1275                        if let Some(callable) = Callable::of_expression(init) {
1276                            top.functions.insert(id.name.as_str().to_string(), callable);
1277                        }
1278                    }
1279                }
1280            }
1281            Some(Declaration::FunctionDeclaration(f)) => {
1282                if let Some(id) = &f.id {
1283                    top.functions
1284                        .insert(id.name.as_str().to_string(), Callable::Function(f));
1285                }
1286            }
1287            _ => {}
1288        }
1289    }
1290    top.unstable = unstable;
1291    top
1292}
1293
1294/// Array methods [`Evaluator::array_callback_call`] runs over a known array.
1295const ARRAY_CALLBACK_METHODS: &[&str] = &["map", "flatMap", "filter"];
1296
1297impl TopLevel<'_, '_> {
1298    /// Records what a top-level statement writes ([`Writes`]).
1299    fn record(&mut self, writes: Writes<'_>) {
1300        for name in writes.assigned {
1301            self.changed.insert(name, TopChange::Assigned);
1302        }
1303        for (name, write) in writes.changed {
1304            if let TopChange::Written(list) = self
1305                .changed
1306                .entry(name)
1307                .or_insert_with(|| TopChange::Written(Vec::new()))
1308            {
1309                list.push(write);
1310            }
1311        }
1312    }
1313}
1314
1315/// Functions of `path`, whose results are computed lexically.
1316const PATH_FUNCTIONS: &[&str] = &["resolve", "join", "dirname", "normalize"];
1317
1318/// The local names the config file gives to the `path` and `url` modules and their functions.
1319#[derive(Default)]
1320struct ModuleBindings {
1321    path_modules: HashSet<String>,
1322    path_functions: HashMap<String, String>,
1323    url_modules: HashSet<String>,
1324    file_url_to_path: HashSet<String>,
1325    /// Local names of glob functions and glob modules.
1326    glob_names: HashSet<String>,
1327    /// Local names bound to a package that merges configuration objects.
1328    merge_names: HashSet<String>,
1329    /// Local `merge` and `mergeWith` imported from a package that does something else with them.
1330    foreign_merges: HashSet<String>,
1331    /// Local names of the functions that combine `ESLint` flat configs into one list (`defineConfig` of
1332    /// `eslint/config`).
1333    flat_config_functions: HashSet<String>,
1334    /// Local names of `typescript-eslint`, whose `config` combines flat configs.
1335    flat_config_modules: HashSet<String>,
1336}
1337
1338impl ModuleBindings {
1339    /// Records `local` as the module itself (`member` is `None`) or as its export `member`.
1340    fn bind(&mut self, module: &str, member: Option<&str>, local: &str) {
1341        let local = local.to_owned();
1342        if MERGE_PACKAGES.contains(&module) {
1343            self.merge_names.insert(local.clone());
1344        } else if AMBIGUOUS_MERGES.contains(&local.as_str()) {
1345            self.foreign_merges.insert(local.clone());
1346        }
1347        match (module.strip_prefix("node:").unwrap_or(module), member) {
1348            ("path" | "path/posix", None | Some("default")) => {
1349                self.path_modules.insert(local);
1350            }
1351            ("path" | "path/posix", Some(name)) if PATH_FUNCTIONS.contains(&name) => {
1352                self.path_functions.insert(local, name.to_owned());
1353            }
1354            ("url", None | Some("default")) => {
1355                self.url_modules.insert(local);
1356            }
1357            ("url", Some("fileURLToPath")) => {
1358                self.file_url_to_path.insert(local);
1359            }
1360            (package, _) if GLOB_PACKAGES.contains(&package) => {
1361                self.glob_names.insert(local);
1362            }
1363            ("fs", Some("globSync")) | ("fs/promises", Some("glob")) => {
1364                self.glob_names.insert(local);
1365            }
1366            ("eslint/config", Some("defineConfig")) | ("typescript-eslint", Some("config")) => {
1367                self.flat_config_functions.insert(local);
1368            }
1369            ("typescript-eslint", None | Some("default")) => {
1370                self.flat_config_modules.insert(local);
1371            }
1372            _ => {}
1373        }
1374    }
1375}
1376
1377fn module_bindings(program: &Program<'_>) -> ModuleBindings {
1378    let mut bindings = ModuleBindings::default();
1379    for stmt in &program.body {
1380        if let Statement::ImportDeclaration(d) = stmt {
1381            let module = d.source.value.as_str().unwrap_or_default();
1382            for specifier in d.specifiers.iter().flatten() {
1383                match specifier {
1384                    ImportDeclarationSpecifier::ImportSpecifier(s) => {
1385                        let imported = crate::collect::module_export_name(&s.imported);
1386                        bindings.bind(module, Some(&imported), s.local.name.as_str());
1387                    }
1388                    ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => {
1389                        bindings.bind(module, None, s.local.name.as_str());
1390                    }
1391                    ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => {
1392                        bindings.bind(module, None, s.local.name.as_str());
1393                    }
1394                }
1395            }
1396        } else if let Some(Declaration::VariableDeclaration(v)) = stmt.as_declaration() {
1397            for declarator in &v.declarations {
1398                if let Some(module) = declarator.init.as_ref().and_then(required_module) {
1399                    bind_required(&mut bindings, &module, &declarator.id);
1400                } else if let Some(Expression::Identifier(source)) =
1401                    declarator.init.as_ref().map(Expression::get_inner_expression)
1402                {
1403                    // `const { resolve } = path;` destructures a module binding made earlier.
1404                    let module = if bindings.path_modules.contains(source.name.as_str()) {
1405                        Some("path")
1406                    } else if bindings.url_modules.contains(source.name.as_str()) {
1407                        Some("url")
1408                    } else {
1409                        None
1410                    };
1411                    if let Some(module) = module {
1412                        bind_required(&mut bindings, module, &declarator.id);
1413                    }
1414                }
1415            }
1416        }
1417    }
1418    bindings
1419}
1420
1421/// The module a `require("module")` call loads.
1422fn required_module(init: &Expression<'_>) -> Option<String> {
1423    let Expression::CallExpression(call) = init.get_inner_expression() else {
1424        return None;
1425    };
1426    match &call.callee {
1427        Expression::Identifier(id) if id.name.as_str() == "require" => call
1428            .arguments
1429            .first()
1430            .and_then(Argument::as_expression)
1431            .and_then(static_string),
1432        _ => None,
1433    }
1434}
1435
1436fn bind_required(bindings: &mut ModuleBindings, module: &str, id: &BindingPattern<'_>) {
1437    match id {
1438        BindingPattern::BindingIdentifier(local) => bindings.bind(module, None, local.name.as_str()),
1439        BindingPattern::ObjectPattern(pattern) => {
1440            for property in &pattern.properties {
1441                if let (Some(key), BindingPattern::BindingIdentifier(local)) =
1442                    (property.key.static_name(), &property.value)
1443                {
1444                    bindings.bind(module, Some(&crate::js_string(key.as_js_str())), local.name.as_str());
1445                }
1446            }
1447        }
1448        _ => {}
1449    }
1450}
1451
1452/// Joins path segments lexically into a path relative to the config directory: `"."`, `"./a/b"` or `"../a"`.
1453fn normalized(parts: &[&str]) -> String {
1454    let mut segments: Vec<&str> = Vec::new();
1455    for part in parts.iter().flat_map(|part| part.split(['/', '\\'])) {
1456        match part {
1457            "" | "." => {}
1458            ".." if segments.last().is_some_and(|last| *last != "..") => {
1459                segments.pop();
1460            }
1461            other => segments.push(other),
1462        }
1463    }
1464    match segments.first() {
1465        None => ".".to_owned(),
1466        Some(&"..") => segments.join("/"),
1467        Some(_) => format!("./{}", segments.join("/")),
1468    }
1469}
1470
1471/// The result of a `path` or `url` function, `Unknown` when an argument is not a known relative path.
1472fn apply_path_function(function: &str, args: &[Value]) -> Value {
1473    let Some(parts) = args.iter().map(Value::as_str).collect::<Option<Vec<&str>>>() else {
1474        return Value::Unknown;
1475    };
1476    let absolute = |part: &&str| part.starts_with(['/', '\\']);
1477    let path = match (function, parts.as_slice()) {
1478        // Only a path derived from `import.meta.url` is known; a URL with a scheme names something else.
1479        ("fileURLToPath", [file]) if !file.contains(':') => (*file).to_owned(),
1480        ("dirname", [path]) => normalized(&[*path, ".."]),
1481        ("normalize", [path]) => normalized(&[*path]),
1482        ("resolve", parts) if !parts.iter().any(absolute) => normalized(parts),
1483        ("join", parts) if !parts.first().is_some_and(absolute) => normalized(parts),
1484        _ => return Value::Unknown,
1485    };
1486    Value::String(path)
1487}
1488
1489/// Statically evaluates the option `key` of every `new constructor({ … })` in `source`, however the constructor was
1490/// imported (`new NodejsFunction(…)`, `new lambda_nodejs.NodejsFunction(…)`). Path computations are relative to the
1491/// directory of `path`. Returns nothing when the file does not parse.
1492#[must_use]
1493pub fn extract_constructor_options(source: &str, path: &Path, constructor: &str, key: &str) -> Vec<ConfigValue> {
1494    let Ok(source_type) = SourceType::from_path(path) else {
1495        return Vec::new();
1496    };
1497    crate::with_arena(|allocator| {
1498        let ret = Parser::new(allocator, source, source_type)
1499            .with_options(OxcParseOptions {
1500                preserve_parens: false,
1501                ..OxcParseOptions::default()
1502            })
1503            .parse();
1504        if ret.diagnostics.errors().next().is_some() {
1505            return Vec::new();
1506        }
1507        let program = &ret.program;
1508        let top = top_level(program, &NoImports);
1509        let bindings = module_bindings(program);
1510        let mut finder = OptionFinder {
1511            top: &top,
1512            bindings: &bindings,
1513            file: path.file_name().and_then(|name| name.to_str()).unwrap_or_default(),
1514            constructor,
1515            key,
1516            found: Vec::new(),
1517        };
1518        finder.visit_program(program);
1519        finder.found.into_iter().map(ConfigValue::from).collect()
1520    })
1521}
1522
1523/// Statically evaluates the value of every property named `key` of every object literal in `source`, however deeply
1524/// nested and wherever it appears (`handler: "src/api.handler"` inside a function body), in source order. Values are
1525/// evaluated as in [`extract_config_object`], against the top-level bindings, with paths relative to the directory of
1526/// `path`; a name a function, block, loop or `catch` around the property declares is unknown, whatever top-level
1527/// binding it shadows. Methods are left out. Returns nothing when the file does not parse.
1528#[must_use]
1529pub fn extract_property_values(source: &str, path: &Path, key: &str) -> Vec<ConfigValue> {
1530    find_properties(source, path, key, None)
1531}
1532
1533/// Statically calls every function that is the property `key` of an object literal in `source`, a method
1534/// (`configureWebpack(config) { … }`) or a function or arrow value, with `argument` as its first parameter, in source
1535/// order. Each result is the value the function returns or, when its body finishes without a `return`, what it leaves
1536/// its first parameter as: a function that changes the object it is given (`config.entry = { … }`) yields the changed
1537/// object. Calls are evaluated as in [`extract_config_object`]; a property that is not a function is left out.
1538/// Returns nothing when the file does not parse.
1539#[must_use]
1540pub fn extract_property_calls(source: &str, path: &Path, key: &str, argument: &ConfigValue) -> Vec<ConfigValue> {
1541    find_properties(source, path, key, Some(argument))
1542}
1543
1544/// The values of the properties `key` in `source`, or the results of calling them with `call_with`.
1545fn find_properties(source: &str, path: &Path, key: &str, call_with: Option<&ConfigValue>) -> Vec<ConfigValue> {
1546    crate::with_arena(|allocator| {
1547        let Some(program) = parse_program(allocator, source, path) else {
1548            return Vec::new();
1549        };
1550        let top = top_level(&program, &NoImports);
1551        let bindings = module_bindings(&program);
1552        let mut finder = PropertyFinder {
1553            top: &top,
1554            bindings: &bindings,
1555            file: path.file_name().and_then(|name| name.to_str()).unwrap_or_default(),
1556            key,
1557            call_with,
1558            evaluated: HashMap::default(),
1559            shadowed: Vec::new(),
1560            found: Vec::new(),
1561        };
1562        finder.visit_program(&program);
1563        finder.found.into_iter().map(ConfigValue::from).collect()
1564    })
1565}
1566
1567/// Statically evaluates the arguments of every call of a method named `method` in `source`, whatever object it is
1568/// called on (`eleventyConfig.addPassthroughCopy("img")`), in source order: one list of arguments per call. Arguments
1569/// are evaluated as in [`extract_config_object`], so a top-level constant is known and a local of the enclosing
1570/// function is not. Returns nothing when the file does not parse.
1571#[must_use]
1572pub fn extract_method_calls(source: &str, path: &Path, method: &str) -> Vec<Vec<ConfigValue>> {
1573    crate::with_arena(|allocator| {
1574        let Some(program) = parse_program(allocator, source, path) else {
1575            return Vec::new();
1576        };
1577        let top = top_level(&program, &NoImports);
1578        let bindings = module_bindings(&program);
1579        let mut finder = MethodCallFinder {
1580            top: &top,
1581            bindings: &bindings,
1582            file: path.file_name().and_then(|name| name.to_str()).unwrap_or_default(),
1583            method,
1584            found: Vec::new(),
1585        };
1586        finder.visit_program(&program);
1587        finder
1588            .found
1589            .into_iter()
1590            .map(|arguments| arguments.into_iter().map(ConfigValue::from).collect())
1591            .collect()
1592    })
1593}
1594
1595/// Parses `source` as the JavaScript or TypeScript file `path` names, without keeping parentheses; `None` for another
1596/// kind of file or a file that does not parse.
1597fn parse_program<'a>(allocator: &'a Allocator, source: &'a str, path: &Path) -> Option<Program<'a>> {
1598    let source_type = SourceType::from_path(path).ok()?;
1599    let ret = Parser::new(allocator, source, source_type)
1600        .with_options(OxcParseOptions {
1601            preserve_parens: false,
1602            ..OxcParseOptions::default()
1603        })
1604        .parse();
1605    if ret.diagnostics.errors().next().is_some() {
1606        return None;
1607    }
1608    Some(ret.program)
1609}
1610
1611/// A call, or a tagged template, of a function a module exports; see [`extract_imported_calls`].
1612#[derive(Debug, Clone, PartialEq)]
1613pub struct ImportedCall {
1614    /// The module the function comes from.
1615    pub module: String,
1616    /// The export called: `"default"` for the default export or the module object itself (`pino(…)`), the export's
1617    /// name for a named import (`execa(…)`), or the member called on the module object (`pino.transport(…)`).
1618    pub export: String,
1619    /// The statically evaluated arguments, in order. For a call that runs a command ([`extract_command_calls`]), the
1620    /// `cwd` option of an options object is left out when it names the directory the call runs in anyway
1621    /// (`process.cwd()`, `'.'`, `undefined`), and starts with [`FILE_DIR`] when the file's own location computes it.
1622    /// A tagged template has none, except that one run in another directory by a chained `.cwd(dir)` (Bun Shell's
1623    /// `` $`…`.cwd(dir) ``) has an options object with that `cwd`, marked the same way. Such a call also marks each
1624    /// string of the `entrypoints` array of an options object (`Bun.build`) that the file's own location computes
1625    /// (`path.join(import.meta.dir, 'src/x.ts')`): it starts with [`FILE_DIR`] and `/`, followed by the path relative
1626    /// to the file's directory.
1627    pub arguments: Vec<ConfigValue>,
1628    /// For a tagged template (`` $`cmd ${x}` ``), its raw text with every substitution replaced by
1629    /// [`TEMPLATE_SUBSTITUTION`]; `None` for a call.
1630    pub template: Option<String>,
1631}
1632
1633/// What a substitution of a tagged template reads as in [`ImportedCall::template`].
1634pub const TEMPLATE_SUBSTITUTION: &str = "$__skif__";
1635
1636/// What the directory of the calling file reads as in the `cwd` option of an [`ImportedCall`] that the file's own
1637/// location computes (`__dirname`, `import.meta.dirname`, `path.join(__dirname, 'fixtures')`), followed by `/` and the
1638/// path relative to the file's directory (`$__skif_file_dir__/./fixtures`). [`command_in_call_dir`] changes to it, and
1639/// [`place_file_dir`] puts the file's directory in its place.
1640pub const FILE_DIR: &str = "$__skif_file_dir__";
1641
1642/// The directory the arguments of a call are evaluated from a second time to tell a path the file's own location
1643/// computes, which changes with it, from one that does not (`process.cwd()`, a literal).
1644const PROBE_DIR: &str = "__skif__/probe";
1645
1646/// Every call and tagged template in `source` whose function is imported from one of `modules` (`import`, `import * as`,
1647/// or a top-level `require`, plain or destructured), with its arguments statically evaluated as in
1648/// [`extract_config_object`]. `globals` names functions available without an import, as `(name, module)` pairs (the
1649/// `$` of a zx script); a name written `Bun.*` stands for the module object itself, so `` Bun.$`…` `` and
1650/// `Bun.build(…)` call the exports `$` and `build` of the module. Shadowed names are not tracked. Returns nothing when
1651/// the file does not parse.
1652#[must_use]
1653pub fn extract_imported_calls(
1654    source: &str,
1655    path: &Path,
1656    modules: &[&str],
1657    globals: &[(&str, &str)],
1658) -> Vec<ImportedCall> {
1659    parse_imported_calls(source, path, modules, globals, false)
1660}
1661
1662/// The calls of [`extract_imported_calls`] for functions that run a command (`execa`, `nano-spawn`), whose `cwd`
1663/// option is read as the directory the command runs in (see [`ImportedCall::arguments`]).
1664#[must_use]
1665pub fn extract_command_calls(
1666    source: &str,
1667    path: &Path,
1668    modules: &[&str],
1669    globals: &[(&str, &str)],
1670) -> Vec<ImportedCall> {
1671    parse_imported_calls(source, path, modules, globals, true)
1672}
1673
1674fn parse_imported_calls(
1675    source: &str,
1676    path: &Path,
1677    modules: &[&str],
1678    globals: &[(&str, &str)],
1679    commands: bool,
1680) -> Vec<ImportedCall> {
1681    let Ok(source_type) = SourceType::from_path(path) else {
1682        return Vec::new();
1683    };
1684    crate::with_arena(|allocator| {
1685        let ret = Parser::new(allocator, source, source_type)
1686            .with_options(OxcParseOptions {
1687                preserve_parens: false,
1688                ..OxcParseOptions::default()
1689            })
1690            .parse();
1691        if ret.diagnostics.errors().next().is_some() {
1692            return Vec::new();
1693        }
1694        let file = path.file_name().and_then(|name| name.to_str()).unwrap_or_default();
1695        imported_calls_in(&ret.program, file, modules, globals, commands)
1696    })
1697}
1698
1699/// The calls and tagged templates in a parsed `program` whose function is imported from one of `modules`, as
1700/// [`extract_imported_calls`] finds them, or with `commands` as [`extract_command_calls`] does; `file` is the file name
1701/// `__filename` evaluates to.
1702pub(crate) fn imported_calls_in(
1703    program: &Program<'_>,
1704    file: &str,
1705    modules: &[&str],
1706    globals: &[(&str, &str)],
1707    commands: bool,
1708) -> Vec<ImportedCall> {
1709    let imported = imported_functions(program, modules, globals);
1710    if imported.is_empty() {
1711        return Vec::new();
1712    }
1713    let top = top_level(program, &NoImports);
1714    let bindings = module_bindings(program);
1715    let mut finder = CallFinder {
1716        top: &top,
1717        bindings: &bindings,
1718        file,
1719        imported: &imported,
1720        commands,
1721        chained_cwd: HashMap::default(),
1722        found: Vec::new(),
1723    };
1724    finder.visit_program(program);
1725    finder.found
1726}
1727
1728/// The command line of a `(file, [arguments])` call such as `spawn('eslint', ['.'])`: the file and its literal
1729/// arguments, a computed argument left out. `None` when the file or a literal argument is not a plain word, because
1730/// dropping it would change what the command means.
1731#[must_use]
1732pub fn literal_command(arguments: &[ConfigValue]) -> Option<String> {
1733    let is_safe = |word: &str| {
1734        !word.is_empty()
1735            && word
1736                .chars()
1737                .all(|c| c.is_alphanumeric() || matches!(c, '_' | '@' | '.' | ',' | ':' | '/' | '=' | '+' | '~' | '-'))
1738    };
1739    let file = arguments.first()?.as_str().filter(|file| is_safe(file))?;
1740    let mut words = vec![file];
1741    if let Some(ConfigValue::Array(items)) = arguments.get(1) {
1742        for word in items.iter().filter_map(ConfigValue::as_str) {
1743            if !is_safe(word) {
1744                return None;
1745            }
1746            words.push(word);
1747        }
1748    }
1749    Some(words.join(" "))
1750}
1751
1752/// The command of a `(command line, options)` call such as `execSync('tsc -b', { cwd })`, in the directory its `cwd`
1753/// option names (see [`command_in_call_dir`]); `None` unless the command line is a string.
1754#[must_use]
1755pub fn command_line_call(arguments: &[ConfigValue]) -> Option<String> {
1756    let command = arguments.first().and_then(ConfigValue::as_str)?;
1757    Some(command_in_call_dir(command.to_owned(), arguments.get(1)))
1758}
1759
1760/// The options object of a `(file, [arguments], options)` call such as `spawn('eslint', ['.'], { cwd })`: the argument
1761/// after the arguments array, or the second argument when that is no array.
1762#[must_use]
1763pub fn file_call_options(arguments: &[ConfigValue]) -> Option<&ConfigValue> {
1764    match arguments.get(1) {
1765        Some(ConfigValue::Array(_)) => arguments.get(2),
1766        other => other,
1767    }
1768}
1769
1770/// `command` as a call whose options object is `options` runs it. A `cwd` option the file's own location computes
1771/// ([`FILE_DIR`]) runs it in that directory, so the command line first changes to it (`cd $__skif_file_dir__/. && …`).
1772/// Any other `cwd` runs it in a directory the script parser cannot place (a literal path is relative to the process,
1773/// which the parser does not know), so the command line first changes to a directory a substitution names
1774/// (`cd $__skif__ && …`): a package manager's script run there is another package's, as with `bun --cwd`.
1775#[must_use]
1776pub fn command_in_call_dir(command: String, options: Option<&ConfigValue>) -> String {
1777    match options.and_then(|options| options.get("cwd")) {
1778        None => command,
1779        Some(ConfigValue::String(dir)) if dir.starts_with(FILE_DIR) => format!("cd {dir} && {command}"),
1780        Some(_) => format!("cd {TEMPLATE_SUBSTITUTION} && {command}"),
1781    }
1782}
1783
1784/// `command` with the directory of `file`, the file that runs it as a `/`-separated path relative to the directory
1785/// where the command starts, in place of [`FILE_DIR`], single-quoted so that a shell reads it as part of one word
1786/// whatever it holds (`cd 'my scripts'/../sub`).
1787#[must_use]
1788pub fn place_file_dir(command: &str, file: &str) -> String {
1789    let dir = file.rsplit_once('/').map_or("", |(dir, _)| dir);
1790    let quoted = if dir.is_empty() {
1791        ".".to_owned()
1792    } else {
1793        format!("'{}'", dir.replace('\'', r"'\''"))
1794    };
1795    command.replace(FILE_DIR, &quoted)
1796}
1797
1798/// Marks the `cwd` option of `options`, a call argument evaluated from the file's directory, by what it is relative
1799/// to: left out when it names the directory the call runs in anyway (`process.cwd()`, `'.'`, `undefined`), and
1800/// [`FILE_DIR`] joined with the path when `probe`, an evaluator from [`PROBE_DIR`], finds that the file's own location
1801/// computes it. A path a command line cannot hold as one plain word is left as it is.
1802fn mark_cwd<'p, 'a>(options: &mut ConfigValue, expression: &'p Expression<'a>, probe: &Evaluator<'_, 'p, 'a>) {
1803    let ConfigValue::Object(entries) = options else {
1804        return;
1805    };
1806    let Some(index) = entries.iter().rposition(|(key, _)| key == "cwd") else {
1807        return;
1808    };
1809    let probed = ConfigValue::from(probe.expr(expression, 0));
1810    let cwd = std::mem::replace(&mut entries[index].1, ConfigValue::Unknown);
1811    match marked_cwd(cwd, probed.get("cwd"), sets_undefined(expression, "cwd")) {
1812        Some(cwd) => entries[index].1 = cwd,
1813        None => entries.retain(|(key, _)| key != "cwd"),
1814    }
1815}
1816
1817/// The option of a command call's options object that lists files to bundle (`Bun.build`), relative to the working
1818/// directory unless the file's own location computes them.
1819const ENTRYPOINTS: &str = "entrypoints";
1820
1821/// Marks each string of the `entrypoints` array of `options`, a call argument evaluated from the file's directory, that
1822/// `probe`, an evaluator from [`PROBE_DIR`], evaluates differently, so that the file's own location computes it: it
1823/// becomes [`FILE_DIR`] joined with the path. Any other entry is left as it is.
1824fn mark_entrypoints<'p, 'a>(options: &mut ConfigValue, expression: &'p Expression<'a>, probe: &Evaluator<'_, 'p, 'a>) {
1825    let ConfigValue::Object(entries) = options else {
1826        return;
1827    };
1828    let Some(index) = entries.iter().rposition(|(key, _)| key == ENTRYPOINTS) else {
1829        return;
1830    };
1831    let ConfigValue::Array(items) = &mut entries[index].1 else {
1832        return;
1833    };
1834    let probed = ConfigValue::from(probe.expr(expression, 0));
1835    let Some(ConfigValue::Array(probed)) = probed.get(ENTRYPOINTS) else {
1836        return;
1837    };
1838    for (item, probed) in items.iter_mut().zip(probed) {
1839        let file_relative = probed != &*item;
1840        if file_relative && let ConfigValue::String(path) = item {
1841            *path = format!("{FILE_DIR}/{path}");
1842        }
1843    }
1844}
1845
1846/// A `cwd` evaluated from the file's directory, marked as [`mark_cwd`] does: `None` when it names the directory the
1847/// call runs in anyway (`'.'`, or `undefined` set explicitly), [`FILE_DIR`] joined with the path when `probed`, the
1848/// same `cwd` evaluated from [`PROBE_DIR`], differs from it, so that the file's own location computes it.
1849fn marked_cwd(cwd: ConfigValue, probed: Option<&ConfigValue>, undefined: bool) -> Option<ConfigValue> {
1850    let file_relative = matches!(cwd, ConfigValue::String(_)) && probed != Some(&cwd);
1851    let unchanged = !file_relative && matches!(cwd.as_str(), Some("." | "./"));
1852    if unchanged || undefined {
1853        return None;
1854    }
1855    match cwd {
1856        ConfigValue::String(path)
1857            if file_relative
1858                && path
1859                    .chars()
1860                    .all(|c| c.is_alphanumeric() || matches!(c, '_' | '@' | '.' | '/' | '+' | '-')) =>
1861        {
1862            Some(ConfigValue::String(format!("{FILE_DIR}/{path}")))
1863        }
1864        other => Some(other),
1865    }
1866}
1867
1868/// Whether `expression` is `undefined` (or `void 0`).
1869fn is_undefined(expression: &Expression<'_>) -> bool {
1870    match expression.get_inner_expression() {
1871        Expression::Identifier(id) => id.name.as_str() == "undefined",
1872        Expression::UnaryExpression(u) => u.operator == UnaryOperator::Void,
1873        _ => false,
1874    }
1875}
1876
1877/// Whether `expression` is an object literal whose last property `key` is `undefined` (or `void 0`).
1878fn sets_undefined(expression: &Expression<'_>, key: &str) -> bool {
1879    let Expression::ObjectExpression(object) = expression.get_inner_expression() else {
1880        return false;
1881    };
1882    let last = object.properties.iter().rev().find_map(|property| match property {
1883        ObjectPropertyKind::ObjectProperty(p)
1884            if p.key
1885                .static_name()
1886                .is_some_and(|name| crate::js_string(name.as_js_str()) == key) =>
1887        {
1888            Some(&p.value)
1889        }
1890        _ => None,
1891    });
1892    last.is_some_and(is_undefined)
1893}
1894
1895/// Local name to module and named export (`None` for the default export or the module object) of everything a
1896/// program imports from `modules`, plus `globals`.
1897type ImportedFunctions = HashMap<String, (String, Option<String>)>;
1898
1899/// The entries of one object literal, as a spread branch lists them.
1900type Entries<'v> = &'v [(String, Value)];
1901
1902fn imported_functions(program: &Program<'_>, modules: &[&str], globals: &[(&str, &str)]) -> ImportedFunctions {
1903    let mut imported: ImportedFunctions = globals
1904        .iter()
1905        .map(|(name, module)| match name.strip_suffix(".*") {
1906            Some(object) => (object.to_owned(), ((*module).to_owned(), None)),
1907            None => ((*name).to_owned(), ((*module).to_owned(), Some((*name).to_owned()))),
1908        })
1909        .collect();
1910    let named = |export: String| Some(export).filter(|e| e != "default");
1911    for stmt in &program.body {
1912        if let Statement::ImportDeclaration(d) = stmt {
1913            let module = d.source.value.as_str().unwrap_or_default();
1914            if !modules.contains(&module) {
1915                continue;
1916            }
1917            for specifier in d.specifiers.iter().flatten() {
1918                let (local, export) = match specifier {
1919                    ImportDeclarationSpecifier::ImportSpecifier(s) => (
1920                        s.local.name.as_str(),
1921                        named(crate::collect::module_export_name(&s.imported)),
1922                    ),
1923                    ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => (s.local.name.as_str(), None),
1924                    ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => (s.local.name.as_str(), None),
1925                };
1926                imported.insert(local.to_owned(), (module.to_owned(), export));
1927            }
1928        } else if let Some(Declaration::VariableDeclaration(v)) = stmt.as_declaration() {
1929            for declarator in &v.declarations {
1930                if let Some(module) = declarator.init.as_ref().and_then(required_module)
1931                    && modules.contains(&module.as_str())
1932                {
1933                    for (local, export) in required_names(&declarator.id) {
1934                        imported.insert(local, (module.clone(), export.and_then(named)));
1935                    }
1936                }
1937            }
1938        }
1939    }
1940    imported
1941}
1942
1943/// The local names a `require` result is bound to, with the export each destructured one takes.
1944fn required_names(id: &BindingPattern<'_>) -> Vec<(String, Option<String>)> {
1945    match id {
1946        BindingPattern::BindingIdentifier(local) => vec![(local.name.as_str().to_owned(), None)],
1947        BindingPattern::ObjectPattern(pattern) => pattern
1948            .properties
1949            .iter()
1950            .filter_map(|property| match (property.key.static_name(), &property.value) {
1951                (Some(key), BindingPattern::BindingIdentifier(local)) => {
1952                    Some((local.name.as_str().to_owned(), Some(crate::js_string(key.as_js_str()))))
1953                }
1954                _ => None,
1955            })
1956            .collect(),
1957        _ => Vec::new(),
1958    }
1959}
1960
1961struct CallFinder<'t, 'p, 'a> {
1962    top: &'t TopLevel<'p, 'a>,
1963    bindings: &'t ModuleBindings,
1964    file: &'t str,
1965    /// Local name to module and named export (`None` for the default export or the module object).
1966    imported: &'t ImportedFunctions,
1967    /// The calls run commands, so their `cwd` option is marked (see [`mark_cwd`]).
1968    commands: bool,
1969    /// The `cwd` a chained `.cwd(dir)` sets for the tagged template at a span, marked (see [`marked_cwd`]); `None`
1970    /// when it names the directory the command runs in anyway.
1971    chained_cwd: HashMap<Span, Option<ConfigValue>>,
1972    found: Vec<ImportedCall>,
1973}
1974
1975/// The function a tagged template calls: its tag, or the callee of a tag that is itself a call (`` $(opts)`…` ``).
1976fn template_tag<'e, 'a>(template: &'e TaggedTemplateExpression<'a>) -> &'e Expression<'a> {
1977    match template.tag.get_inner_expression() {
1978        Expression::CallExpression(call) => &call.callee,
1979        other => other,
1980    }
1981}
1982
1983impl<'a> CallFinder<'_, '_, 'a> {
1984    /// The module and export `callee` calls, when it is an imported function or a member of an imported module.
1985    fn target(&self, callee: &Expression<'a>) -> Option<(String, String)> {
1986        match callee.get_inner_expression() {
1987            Expression::Identifier(id) => self
1988                .imported
1989                .get(id.name.as_str())
1990                .map(|(module, export)| (module.clone(), export.clone().unwrap_or_else(|| "default".to_owned()))),
1991            Expression::StaticMemberExpression(member) => match member.object.get_inner_expression() {
1992                Expression::Identifier(id) => self
1993                    .imported
1994                    .get(id.name.as_str())
1995                    .filter(|(_, export)| export.is_none())
1996                    .map(|(module, _)| (module.clone(), member.property.name.as_str().to_owned())),
1997                _ => None,
1998            },
1999            _ => None,
2000        }
2001    }
2002
2003    /// The tagged template of an imported function that a chain of method calls starts from: `object` in
2004    /// `` object.cwd(dir) `` for `` $`…`.cwd(dir) `` or `` $`…`.quiet().cwd(dir) ``.
2005    fn chained_template<'e>(&self, mut object: &'e Expression<'a>) -> Option<&'e TaggedTemplateExpression<'a>> {
2006        loop {
2007            match object.get_inner_expression() {
2008                Expression::TaggedTemplateExpression(template) => {
2009                    return self.target(template_tag(template)).is_some().then_some(&**template);
2010                }
2011                Expression::CallExpression(call) => match call.callee.get_inner_expression() {
2012                    Expression::StaticMemberExpression(member) => object = &member.object,
2013                    _ => return None,
2014                },
2015                _ => return None,
2016            }
2017        }
2018    }
2019
2020    /// Records the directory `` $`…`.cwd(dir) `` (Bun Shell) runs a command template in, for the template to carry
2021    /// as its `cwd` option. The outermost `.cwd` of a chain is the one that applies, and it is visited first.
2022    fn record_chained_cwd(&mut self, it: &CallExpression<'a>) {
2023        let Expression::StaticMemberExpression(member) = it.callee.get_inner_expression() else {
2024            return;
2025        };
2026        if member.property.name.as_str() != "cwd" {
2027            return;
2028        }
2029        let Some(template) = self.chained_template(&member.object) else {
2030            return;
2031        };
2032        let Some(directory) = it.arguments.first().and_then(Argument::as_expression) else {
2033            return;
2034        };
2035        let eval = Evaluator::in_file(self.top, self.bindings, self.file, HashMap::default());
2036        let probe = Evaluator {
2037            dir: PROBE_DIR,
2038            ..Evaluator::in_file(self.top, self.bindings, self.file, HashMap::default())
2039        };
2040        let probed = ConfigValue::from(probe.expr(directory, 0));
2041        let cwd = marked_cwd(
2042            ConfigValue::from(eval.expr(directory, 0)),
2043            Some(&probed),
2044            is_undefined(directory),
2045        );
2046        self.chained_cwd.entry(template.span).or_insert(cwd);
2047    }
2048}
2049
2050impl<'a> Visit<'a> for CallFinder<'_, '_, 'a> {
2051    fn visit_call_expression(&mut self, it: &CallExpression<'a>) {
2052        if self.commands {
2053            self.record_chained_cwd(it);
2054        }
2055        if let Some((module, export)) = self.target(&it.callee) {
2056            let eval = Evaluator::in_file(self.top, self.bindings, self.file, HashMap::default());
2057            let mut arguments: Vec<ConfigValue> = it
2058                .arguments
2059                .iter()
2060                .map(|argument| {
2061                    argument
2062                        .as_expression()
2063                        .map_or(ConfigValue::Unknown, |e| ConfigValue::from(eval.expr(e, 0)))
2064                })
2065                .collect();
2066            if self.commands
2067                && arguments
2068                    .iter()
2069                    .any(|argument| argument.get("cwd").is_some() || argument.get(ENTRYPOINTS).is_some())
2070            {
2071                let probe = Evaluator {
2072                    dir: PROBE_DIR,
2073                    ..Evaluator::in_file(self.top, self.bindings, self.file, HashMap::default())
2074                };
2075                for (argument, expression) in arguments.iter_mut().zip(&it.arguments) {
2076                    if let Some(expression) = expression.as_expression() {
2077                        mark_entrypoints(argument, expression, &probe);
2078                        mark_cwd(argument, expression, &probe);
2079                    }
2080                }
2081            }
2082            self.found.push(ImportedCall {
2083                module,
2084                export,
2085                arguments,
2086                template: None,
2087            });
2088        }
2089        walk::walk_call_expression(self, it);
2090    }
2091
2092    fn visit_tagged_template_expression(&mut self, it: &TaggedTemplateExpression<'a>) {
2093        if let Some((module, export)) = self.target(template_tag(it)) {
2094            let quasis: Vec<&str> = it.quasi.quasis.iter().map(|q| q.value.raw.as_str()).collect();
2095            let arguments = self
2096                .chained_cwd
2097                .remove(&it.span)
2098                .flatten()
2099                .map_or_default(|cwd| vec![ConfigValue::Object(vec![("cwd".to_owned(), cwd)])]);
2100            self.found.push(ImportedCall {
2101                module,
2102                export,
2103                arguments,
2104                template: Some(quasis.join(TEMPLATE_SUBSTITUTION)),
2105            });
2106        }
2107        walk::walk_tagged_template_expression(self, it);
2108    }
2109}
2110
2111struct OptionFinder<'t, 'p, 'a> {
2112    top: &'t TopLevel<'p, 'a>,
2113    bindings: &'t ModuleBindings,
2114    file: &'t str,
2115    constructor: &'t str,
2116    key: &'t str,
2117    found: Vec<Value>,
2118}
2119
2120impl<'a> Visit<'a> for OptionFinder<'_, '_, 'a> {
2121    fn visit_new_expression(&mut self, it: &NewExpression<'a>) {
2122        let name = match it.callee.get_inner_expression() {
2123            Expression::Identifier(id) => Some(id.name.as_str()),
2124            Expression::StaticMemberExpression(member) => Some(member.property.name.as_str()),
2125            _ => None,
2126        };
2127        if name == Some(self.constructor) {
2128            let eval = Evaluator::in_file(self.top, self.bindings, self.file, HashMap::default());
2129            for argument in &it.arguments {
2130                let Some(Expression::ObjectExpression(options)) =
2131                    argument.as_expression().map(Expression::get_inner_expression)
2132                else {
2133                    continue;
2134                };
2135                for property in &options.properties {
2136                    if let ObjectPropertyKind::ObjectProperty(p) = property
2137                        && !p.method
2138                        && p.key
2139                            .static_name()
2140                            .is_some_and(|name| crate::js_string(name.as_js_str()) == self.key)
2141                    {
2142                        self.found.push(eval.expr(&p.value, 0));
2143                    }
2144                }
2145            }
2146        }
2147        walk::walk_new_expression(self, it);
2148    }
2149}
2150
2151struct MethodCallFinder<'t, 'p, 'a> {
2152    top: &'t TopLevel<'p, 'a>,
2153    bindings: &'t ModuleBindings,
2154    file: &'t str,
2155    method: &'t str,
2156    found: Vec<Vec<Value>>,
2157}
2158
2159impl<'a> Visit<'a> for MethodCallFinder<'_, '_, 'a> {
2160    fn visit_call_expression(&mut self, it: &CallExpression<'a>) {
2161        if let Expression::StaticMemberExpression(member) = it.callee.get_inner_expression()
2162            && member.property.name.as_str() == self.method
2163        {
2164            let eval = Evaluator::in_file(self.top, self.bindings, self.file, HashMap::default());
2165            let arguments = it
2166                .arguments
2167                .iter()
2168                .map(|argument| argument.as_expression().map_or(Value::Unknown, |e| eval.expr(e, 0)))
2169                .collect();
2170            self.found.push(arguments);
2171        }
2172        walk::walk_call_expression(self, it);
2173    }
2174}
2175
2176struct PropertyFinder<'t, 'p, 'a> {
2177    top: &'t TopLevel<'p, 'a>,
2178    bindings: &'t ModuleBindings,
2179    file: &'t str,
2180    key: &'t str,
2181    /// When set, the property is a function called with this argument (see [`extract_property_calls`]) rather than a
2182    /// value.
2183    call_with: Option<&'t ConfigValue>,
2184    /// The top-level constants evaluated so far, carried from one property to the next so each is evaluated once.
2185    evaluated: HashMap<String, Value>,
2186    /// The names the scopes around the current node declare, innermost last: they shadow top-level bindings.
2187    shadowed: Vec<Vec<String>>,
2188    found: Vec<Value>,
2189}
2190
2191impl PropertyFinder<'_, '_, '_> {
2192    /// Visits a scope with `walk`, the names `declared` holds shadowing top-level bindings inside it.
2193    fn in_scope(&mut self, declared: DeclaredNames, walk: impl FnOnce(&mut Self)) {
2194        self.shadowed.push(declared.0);
2195        walk(self);
2196        self.shadowed.pop();
2197    }
2198}
2199
2200/// The names every binding of a syntax subtree declares.
2201#[derive(Default)]
2202struct DeclaredNames(Vec<String>);
2203
2204impl<'a> Visit<'a> for DeclaredNames {
2205    fn visit_binding_identifier(&mut self, it: &BindingIdentifier<'a>) {
2206        self.0.push(it.name.as_str().to_owned());
2207    }
2208}
2209
2210/// What `visit` finds a [`DeclaredNames`] visitor.
2211fn declared(visit: impl FnOnce(&mut DeclaredNames)) -> DeclaredNames {
2212    let mut names = DeclaredNames::default();
2213    visit(&mut names);
2214    names
2215}
2216
2217impl<'a> Visit<'a> for PropertyFinder<'_, '_, 'a> {
2218    fn visit_function(&mut self, it: &Function<'a>, flags: oxc_semantic::ScopeFlags) {
2219        let names = declared(|names| names.visit_function(it, flags));
2220        self.in_scope(names, |finder| walk::walk_function(finder, it, flags));
2221    }
2222
2223    fn visit_arrow_function_expression(&mut self, it: &ArrowFunctionExpression<'a>) {
2224        let names = declared(|names| names.visit_arrow_function_expression(it));
2225        self.in_scope(names, |finder| walk::walk_arrow_function_expression(finder, it));
2226    }
2227
2228    fn visit_block_statement(&mut self, it: &BlockStatement<'a>) {
2229        let names = declared(|names| names.visit_block_statement(it));
2230        self.in_scope(names, |finder| walk::walk_block_statement(finder, it));
2231    }
2232
2233    fn visit_for_statement(&mut self, it: &ForStatement<'a>) {
2234        let names = declared(|names| names.visit_for_statement(it));
2235        self.in_scope(names, |finder| walk::walk_for_statement(finder, it));
2236    }
2237
2238    fn visit_for_in_statement(&mut self, it: &ForInStatement<'a>) {
2239        let names = declared(|names| names.visit_for_in_statement(it));
2240        self.in_scope(names, |finder| walk::walk_for_in_statement(finder, it));
2241    }
2242
2243    fn visit_for_of_statement(&mut self, it: &ForOfStatement<'a>) {
2244        let names = declared(|names| names.visit_for_of_statement(it));
2245        self.in_scope(names, |finder| walk::walk_for_of_statement(finder, it));
2246    }
2247
2248    fn visit_catch_clause(&mut self, it: &CatchClause<'a>) {
2249        let names = declared(|names| names.visit_catch_clause(it));
2250        self.in_scope(names, |finder| walk::walk_catch_clause(finder, it));
2251    }
2252
2253    fn visit_object_property(&mut self, it: &ObjectProperty<'a>) {
2254        let callable = self.call_with.and_then(|_| Callable::of_expression(&it.value));
2255        let wanted = match self.call_with {
2256            None => !it.method,
2257            Some(_) => callable.is_some() && it.kind == oxc_ast::ast::PropertyKind::Init,
2258        };
2259        if wanted
2260            && it
2261                .key
2262                .static_name()
2263                .is_some_and(|name| crate::js_string(name.as_js_str()) == self.key)
2264        {
2265            let eval = Evaluator::in_file(self.top, self.bindings, self.file, std::mem::take(&mut self.evaluated));
2266            let shadowed: Frame<'_, '_> = self
2267                .shadowed
2268                .iter()
2269                .flatten()
2270                .map(|name| (name.clone(), Binding::Value(Value::Unknown)))
2271                .collect();
2272            if !shadowed.is_empty() {
2273                eval.frames.borrow_mut().push(shadowed);
2274            }
2275            let value = match (self.call_with, callable) {
2276                (Some(argument), Some(callable)) => eval.call_with(callable, Value::from(argument.clone()), 0),
2277                _ => eval.expr(&it.value, 0),
2278            };
2279            self.found.push(value);
2280            self.evaluated = eval.evaluated.into_inner();
2281        }
2282        walk::walk_object_property(self, it);
2283    }
2284}
2285
2286/// What a module exports.
2287#[derive(Clone, Copy)]
2288enum Exported<'p, 'a> {
2289    Expression(&'p Expression<'a>),
2290    Function(&'p Function<'a>),
2291}
2292
2293fn exported<'p, 'a>(program: &'p Program<'a>) -> Option<Exported<'p, 'a>> {
2294    for stmt in &program.body {
2295        match stmt {
2296            Statement::ExportDefaultDeclaration(e) => {
2297                return match &e.declaration {
2298                    ExportDefaultDeclarationKind::FunctionDeclaration(f) => Some(Exported::Function(f)),
2299                    ExportDefaultDeclarationKind::ClassDeclaration(_)
2300                    | ExportDefaultDeclarationKind::TSInterfaceDeclaration(_) => None,
2301                    other => other.as_expression().map(Exported::Expression),
2302                };
2303            }
2304            Statement::TSExportAssignment(e) => return Some(Exported::Expression(&e.expression)),
2305            Statement::ExpressionStatement(s) if is_module_exports_assignment(stmt) => {
2306                if let Expression::AssignmentExpression(a) = &s.expression {
2307                    return Some(Exported::Expression(&a.right));
2308                }
2309            }
2310            _ => {}
2311        }
2312    }
2313    None
2314}
2315
2316/// Whether a statement after the `module.exports = …` that [`exported`] reads changes or replaces what it exports
2317/// (`module.exports.plugins.push(x)`, `exports.a = 1`).
2318fn exports_changed(program: &Program<'_>) -> bool {
2319    let mut statements = program
2320        .body
2321        .iter()
2322        .skip_while(|stmt| !is_module_exports_assignment(stmt));
2323    // With no [`Unstable`] of the module, no call counts as one that changes nothing it is given.
2324    let unstable = Unstable::default();
2325    statements.next().is_some()
2326        && statements.any(|stmt| {
2327            let writes = Writes::of_statement(stmt, &unstable);
2328            writes
2329                .assigned
2330                .iter()
2331                .chain(writes.changed.iter().map(|(name, _)| name))
2332                .any(|name| name == "module" || name == "exports")
2333        })
2334}
2335
2336fn is_module_exports_assignment(stmt: &Statement<'_>) -> bool {
2337    if let Statement::ExpressionStatement(s) = stmt
2338        && let Expression::AssignmentExpression(a) = &s.expression
2339        && let AssignmentTarget::StaticMemberExpression(m) = &a.left
2340    {
2341        matches!(&m.object, Expression::Identifier(id) if id.name.as_str() == "module")
2342            && m.property.name.as_str() == "exports"
2343    } else {
2344        false
2345    }
2346}
2347
2348/// The number of values `value` consists of, itself included.
2349fn node_count(value: &Value) -> usize {
2350    1 + match value {
2351        Value::Array(items) | Value::OneOf(items) => items.iter().map(node_count).sum(),
2352        Value::Object(entries) => entries.iter().map(|(_, item)| node_count(item)).sum(),
2353        Value::Maybe(value) => node_count(value),
2354        _ => 0,
2355    }
2356}
2357
2358struct Evaluator<'c, 'p, 'a> {
2359    consts: &'c HashMap<String, &'p Expression<'a>>,
2360    /// Top-level names changed by statements after their declaration; see [`TopLevel::changed`].
2361    changed: &'c HashMap<String, TopChange>,
2362    /// The bindings code the evaluator does not follow may change; see [`Unstable`].
2363    unstable: &'c Unstable,
2364    functions: &'c HashMap<String, Callable<'p, 'a>>,
2365    bindings: &'c ModuleBindings,
2366    /// File name of the module, which `__filename` and `import.meta.url` name.
2367    file: &'c str,
2368    /// The module's directory relative to the config file's directory: `"."` in the config file itself.
2369    dir: &'c str,
2370    /// Path of the module, which its imports are resolved from.
2371    path: &'c Path,
2372    /// The module's value imports by local name.
2373    imports: &'c HashMap<String, ImportBinding>,
2374    /// Follows imports; `None` when imports are not followed.
2375    modules: Option<&'c Modules<'c>>,
2376    /// The value of every top-level constant evaluated so far.
2377    evaluated: RefCell<HashMap<String, Value>>,
2378    /// The constants being evaluated, outermost first; a constant that refers back to one of them is unknown.
2379    chain: RefCell<Vec<String>>,
2380    /// The bindings of the functions being evaluated, outermost first.
2381    frames: RefCell<Vec<Frame<'p, 'a>>>,
2382    /// How many functions have been called so far.
2383    calls: Cell<usize>,
2384    /// The first parameter of an exported config function (`config` in `module.exports = function (config) { … }`):
2385    /// the object the function hands the real config to with `config.set({ … })`.
2386    config_setter: RefCell<Option<String>>,
2387    /// The argument of the last `<config_setter>.set(…)` call evaluated.
2388    captured: RefCell<Option<Value>>,
2389    /// The argument values of every call whose arguments were evaluated, by the call's span, as first evaluated.
2390    arguments_of: RefCell<HashMap<Span, Vec<Value>>>,
2391}
2392
2393/// The imports of an evaluator that follows none.
2394static NO_IMPORTS: LazyLock<HashMap<String, ImportBinding>> = LazyLock::new(HashMap::default);
2395
2396impl<'c, 'p, 'a> Evaluator<'c, 'p, 'a> {
2397    /// An evaluator of the module at `path`, named `file` and in the directory `dir` relative to the config file's,
2398    /// with its top-level bindings, module bindings and imports, following imports with `modules` when given.
2399    fn new(
2400        top: &'c TopLevel<'p, 'a>,
2401        bindings: &'c ModuleBindings,
2402        imports: &'c HashMap<String, ImportBinding>,
2403        file: &'c str,
2404        dir: &'c str,
2405        path: &'c Path,
2406        modules: Option<&'c Modules<'c>>,
2407    ) -> Self {
2408        Self {
2409            consts: &top.consts,
2410            changed: &top.changed,
2411            unstable: &top.unstable,
2412            functions: &top.functions,
2413            bindings,
2414            file,
2415            dir,
2416            path,
2417            imports,
2418            modules,
2419            evaluated: RefCell::new(HashMap::default()),
2420            chain: RefCell::new(Vec::new()),
2421            frames: RefCell::new(Vec::new()),
2422            calls: Cell::new(0),
2423            config_setter: RefCell::new(None),
2424            captured: RefCell::new(None),
2425            arguments_of: RefCell::new(HashMap::default()),
2426        }
2427    }
2428
2429    /// An evaluator of the config file `file` alone, which follows no import; `evaluated` holds the top-level
2430    /// constants evaluated so far.
2431    fn in_file(
2432        top: &'c TopLevel<'p, 'a>,
2433        bindings: &'c ModuleBindings,
2434        file: &'c str,
2435        evaluated: HashMap<String, Value>,
2436    ) -> Self {
2437        let eval = Self::new(top, bindings, &NO_IMPORTS, file, ".", Path::new(file), None);
2438        *eval.evaluated.borrow_mut() = evaluated;
2439        eval
2440    }
2441}
2442
2443impl<'p, 'a> Evaluator<'_, 'p, 'a> {
2444    /// The value of what a module exports, `exported`: a config function is the one the tool calls, the config it
2445    /// hands to `config.set(…)` when it does, and with `changed_later` the value is one later statements change.
2446    fn exported_value(&self, exported: Exported<'p, 'a>, changed_later: bool) -> Value {
2447        let callable = match exported {
2448            Exported::Expression(expr) => Callable::of_expression(expr),
2449            Exported::Function(function) => Some(Callable::Function(function)),
2450        };
2451        *self.config_setter.borrow_mut() = callable.and_then(first_parameter);
2452        let value = match exported {
2453            Exported::Expression(expr) => self.expr(expr, 0),
2454            Exported::Function(function) => self.invoke(Callable::Function(function), None, false, 0),
2455        };
2456        let value = if changed_later { changed(value) } else { value };
2457        self.captured.borrow_mut().take().unwrap_or(value)
2458    }
2459
2460    /// The arguments of the calls the evaluator cannot follow that make up what a module exports, `exported`, which
2461    /// evaluated to `value`; see [`ExtractedConfig::call_arguments`].
2462    fn exported_call_arguments(&self, exported: Exported<'p, 'a>, value: &Value) -> Vec<Value> {
2463        match exported {
2464            Exported::Expression(expr) => self.unknown_call_arguments(expr, value, 0),
2465            Exported::Function(function) => self.returned_call_arguments(Callable::Function(function), value, 0),
2466        }
2467    }
2468
2469    fn expr(&self, expr: &'p Expression<'a>, depth: u32) -> Value {
2470        if depth > MAX_DEPTH {
2471            return Value::Unknown;
2472        }
2473        let depth = depth + 1;
2474        let inner = expr.get_inner_expression();
2475        match inner {
2476            Expression::NullLiteral(_) => Value::Null,
2477            Expression::BooleanLiteral(b) => Value::Bool(b.value),
2478            Expression::NumericLiteral(n) => Value::Number(n.value),
2479            Expression::StringLiteral(_) => static_string(inner).map_or(Value::Unknown, Value::String),
2480            Expression::RegExpLiteral(r) => Value::Regex {
2481                pattern: r.regex.pattern.text.as_str().to_owned(),
2482                flags: r.regex.flags.to_string(),
2483            },
2484            Expression::TemplateLiteral(template) => self.template(template, depth),
2485            Expression::BinaryExpression(b) => self.binary(b, depth),
2486            Expression::StaticMemberExpression(member) => self.member(member, depth),
2487            Expression::ComputedMemberExpression(member) => self.computed_member(member, depth),
2488            Expression::ChainExpression(chain) => self.chain(chain, depth),
2489            Expression::NewExpression(new) => self.url(new, depth),
2490            Expression::UnaryExpression(u) if u.operator == UnaryOperator::UnaryNegation => {
2491                match self.expr(&u.argument, depth) {
2492                    Value::Number(n) => Value::Number(-n),
2493                    _ => Value::Unknown,
2494                }
2495            }
2496            Expression::UnaryExpression(u) if u.operator == UnaryOperator::LogicalNot => self
2497                .truthy(&u.argument, depth)
2498                .map_or(Value::Unknown, |t| Value::Bool(!t)),
2499            Expression::LogicalExpression(l) => self.logical(l, depth),
2500            Expression::ConditionalExpression(c) => self.conditional(c, depth),
2501            Expression::AwaitExpression(a) => self.expr(&a.argument, depth),
2502            Expression::ArrayExpression(a) => self.array(&a.elements, depth),
2503            Expression::ObjectExpression(o) => self.object(&o.properties, depth),
2504            Expression::Identifier(id) => self.reference(id, depth),
2505            Expression::CallExpression(c) => self.call(c, depth),
2506            Expression::ArrowFunctionExpression(f) => self.invoke(Callable::Arrow(f), None, false, depth),
2507            Expression::FunctionExpression(f) => self.invoke(Callable::Function(f), None, false, depth),
2508            _ => Value::Unknown,
2509        }
2510    }
2511
2512    /// The value the name `id` refers to ([`Self::identifier`]), as code the evaluator does not follow may leave it,
2513    /// as definite as the value bound to it.
2514    fn reference(&self, id: &IdentifierReference<'a>, depth: u32) -> Value {
2515        let value = self.identifier(id.name.as_str(), depth);
2516        let Some(change) = self.unstable.of_reference(id) else {
2517            return value;
2518        };
2519        let (value, definite) = value.split();
2520        Value::doubtful(unstable(value, change), definite)
2521    }
2522
2523    /// The value of a name: a binding of a function being evaluated, a top-level constant (evaluated once however
2524    /// often it is referenced), a top-level function called with arguments that cannot be known (as the tool calls a
2525    /// config function the config exports by name), or a path global. A name bound to a read that is not definite
2526    /// ([`Self::read`]) is a [`Value::Maybe`].
2527    fn identifier(&self, name: &str, depth: u32) -> Value {
2528        if let Some(binding) = self.lookup(name) {
2529            return match binding {
2530                Binding::Value(value) | Binding::Alias(Alias { value, .. }) => value,
2531                Binding::Callable(_) => Value::Unknown,
2532            };
2533        }
2534        let Some(&init) = self.consts.get(name) else {
2535            return match self.functions.get(name) {
2536                Some(callable) => self.invoke(*callable, None, true, depth),
2537                None => match self.import_value(name, None) {
2538                    Some(value) => {
2539                        let (value, definite) = value.split();
2540                        Value::doubtful(self.changed_top(name, value), definite)
2541                    }
2542                    None => self.global(name),
2543                },
2544            };
2545        };
2546        if let Some(known) = self.evaluated.borrow().get(name) {
2547            return known.clone();
2548        }
2549        {
2550            let chain = self.chain.borrow();
2551            if chain.len() >= MAX_CONST_CHAIN || chain.iter().any(|active| active == name) {
2552                return Value::Unknown;
2553            }
2554        }
2555        self.chain.borrow_mut().push(name.to_owned());
2556        // The value does not depend on where it is referenced from: evaluate it outside any function's bindings and
2557        // from a fresh depth budget.
2558        let saved = std::mem::take(&mut *self.frames.borrow_mut());
2559        let cuts = self.modules.map(|modules| modules.cuts.get());
2560        let (value, definite) = self.expr(init, 0).split();
2561        *self.frames.borrow_mut() = saved;
2562        self.chain.borrow_mut().pop();
2563        let value = if node_count(&value) > MAX_CONST_NODES {
2564            Value::Unknown
2565        } else {
2566            self.changed_top(name, value)
2567        };
2568        let value = Value::doubtful(value, definite);
2569        // A value an import cycle or budget cut short depends on the order of evaluation; it is computed again.
2570        if self.modules.map(|modules| modules.cuts.get()) == cuts {
2571            self.evaluated.borrow_mut().insert(name.to_owned(), value.clone());
2572        }
2573        value
2574    }
2575
2576    /// `value` of the top-level binding `name`, a constant or an import, as the top-level statements the evaluator does
2577    /// not run ([`TopLevel::changed`]) and code it does not follow ([`Unstable::of_top`]) may leave it.
2578    fn changed_top(&self, name: &str, value: Value) -> Value {
2579        match self.changed.get(name) {
2580            Some(TopChange::Assigned) => changed(value),
2581            written => {
2582                let mut value = value;
2583                if let Some(TopChange::Written(writes)) = written {
2584                    for write in writes {
2585                        edit_at(&mut value, &write.keys, &write.edit());
2586                    }
2587                }
2588                match self.unstable.of_top(name) {
2589                    Some(change) => unstable(value, change),
2590                    None => value,
2591                }
2592            }
2593        }
2594    }
2595
2596    /// The innermost binding of `name` in the functions being evaluated.
2597    fn lookup(&self, name: &str) -> Option<Binding<'p, 'a>> {
2598        self.frames
2599            .borrow()
2600            .iter()
2601            .rev()
2602            .find_map(|frame| frame.get(name).cloned())
2603    }
2604
2605    /// Whether a function being evaluated binds `name`.
2606    fn is_local(&self, name: &str) -> bool {
2607        self.frames.borrow().iter().any(|frame| frame.contains_key(name))
2608    }
2609
2610    /// Binds `name` in the innermost frame, or replaces the binding where it is already made.
2611    fn assign(&self, name: &str, binding: Binding<'p, 'a>) {
2612        let mut frames = self.frames.borrow_mut();
2613        if let Some(held) = frames.iter_mut().rev().find_map(|frame| frame.get_mut(name)) {
2614            *held = binding;
2615        } else if let Some(frame) = frames.last_mut() {
2616            frame.insert(name.to_owned(), binding);
2617        }
2618    }
2619
2620    /// The path of the module's directory (`__dirname`) or of the module itself (`__filename`).
2621    fn global(&self, name: &str) -> Value {
2622        match name {
2623            "__dirname" => Value::String(normalized(&[self.dir])),
2624            "__filename" => Value::String(self.file_path()),
2625            "undefined" => Value::Undefined,
2626            _ => Value::Unknown,
2627        }
2628    }
2629
2630    /// The module's path relative to the config file's directory.
2631    fn file_path(&self) -> String {
2632        normalized(&[self.dir, self.file])
2633    }
2634
2635    /// The value an imported name stands for, called with `args` when given. `None` when the name is not imported or
2636    /// its module is not followed.
2637    fn import_value(&self, name: &str, args: Option<&[Value]>) -> Option<Value> {
2638        let binding = self.imports.get(name)?;
2639        self.modules?
2640            .evaluate(self.path, &binding.specifier, &binding.imported, args)
2641    }
2642
2643    /// The key an object records the unknown value `expr` spreads or merges under: [`OWN_SPREAD`] when the module
2644    /// computes it itself ([`Self::computes_itself`]), [`UNKNOWN_SPREAD`] otherwise.
2645    fn spread_key(&self, expr: &Expression<'_>) -> &'static str {
2646        if self.computes_itself(expr, 0) {
2647            OWN_SPREAD
2648        } else {
2649            UNKNOWN_SPREAD
2650        }
2651    }
2652
2653    /// Whether `expr` calls or refers to a binding the module defines: a binding of a function being evaluated, a
2654    /// top-level function, or a top-level constant that does not only hold an imported value
2655    /// ([`Self::takes_imported`]). Members, calls, `await` and the branches of a conditional or logical expression are
2656    /// looked through; an imported binding, a global and any other expression are not the module's own.
2657    fn computes_itself(&self, expr: &Expression<'_>, depth: usize) -> bool {
2658        if depth > MAX_CONST_CHAIN {
2659            return false;
2660        }
2661        let depth = depth + 1;
2662        match expr.get_inner_expression() {
2663            Expression::AwaitExpression(e) => self.computes_itself(&e.argument, depth),
2664            Expression::StaticMemberExpression(member) => self.computes_itself(&member.object, depth),
2665            Expression::ComputedMemberExpression(member) => self.computes_itself(&member.object, depth),
2666            Expression::ChainExpression(chain) => {
2667                chain_object(chain).is_some_and(|object| self.computes_itself(object, depth))
2668            }
2669            Expression::CallExpression(call) => self.computes_itself(&call.callee, depth),
2670            Expression::ConditionalExpression(conditional) => {
2671                self.computes_itself(&conditional.consequent, depth)
2672                    || self.computes_itself(&conditional.alternate, depth)
2673            }
2674            Expression::LogicalExpression(logical) => {
2675                self.computes_itself(&logical.left, depth) || self.computes_itself(&logical.right, depth)
2676            }
2677            Expression::Identifier(id) => {
2678                let name = id.name.as_str();
2679                self.is_local(name)
2680                    || self.functions.contains_key(name)
2681                    || self
2682                        .consts
2683                        .get(name)
2684                        .is_some_and(|init| !self.takes_imported(init, depth))
2685            }
2686            _ => false,
2687        }
2688    }
2689
2690    /// Whether `expr` only takes a value from another module: an imported binding, a `require(…)`, a member or a call
2691    /// of one, or a top-level constant holding one (`const base = require('./base')`).
2692    fn takes_imported(&self, expr: &Expression<'_>, depth: usize) -> bool {
2693        if depth > MAX_CONST_CHAIN {
2694            return false;
2695        }
2696        let depth = depth + 1;
2697        match expr.get_inner_expression() {
2698            Expression::AwaitExpression(e) => self.takes_imported(&e.argument, depth),
2699            Expression::StaticMemberExpression(member) => self.takes_imported(&member.object, depth),
2700            Expression::ComputedMemberExpression(member) => self.takes_imported(&member.object, depth),
2701            Expression::ChainExpression(chain) => {
2702                chain_object(chain).is_some_and(|object| self.takes_imported(object, depth))
2703            }
2704            Expression::CallExpression(call) => {
2705                let required = matches!(
2706                    call.callee.get_inner_expression(),
2707                    Expression::Identifier(id) if id.name.as_str() == "require"
2708                );
2709                required || self.takes_imported(&call.callee, depth)
2710            }
2711            Expression::Identifier(id) => {
2712                let name = id.name.as_str();
2713                self.imports.contains_key(name)
2714                    || (!self.functions.contains_key(name)
2715                        && self
2716                            .consts
2717                            .get(name)
2718                            .is_some_and(|init| self.takes_imported(init, depth)))
2719            }
2720            _ => false,
2721        }
2722    }
2723
2724    /// A call of an imported function whose module is followed; `None` otherwise, before evaluating any argument.
2725    fn imported_call(&self, name: &str, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
2726        let binding = self.imports.get(name)?;
2727        let modules = self.modules?;
2728        if !modules.follows(self.path, &binding.specifier) {
2729            return None;
2730        }
2731        let args = self.arguments(call, depth);
2732        modules.evaluate(self.path, &binding.specifier, &binding.imported, Some(&args))
2733    }
2734
2735    /// `ns.name` of a namespace import `ns`: the export `name` of its module.
2736    fn namespace_member(&self, namespace: &str, name: &str) -> Value {
2737        let (Some(binding), Some(modules)) = (self.imports.get(namespace), self.modules) else {
2738            return Value::Unknown;
2739        };
2740        modules
2741            .evaluate(self.path, &binding.specifier, &Imported::named(name.to_owned()), None)
2742            .unwrap_or(Value::Unknown)
2743    }
2744
2745    /// Calls the function `name` of this module, declared or imported, with `args`.
2746    fn call_named(&self, name: &str, args: &[Value]) -> Value {
2747        match self.functions.get(name) {
2748            Some(callable) => self.invoke(*callable, Some(args), true, 0),
2749            None => self.import_value(name, Some(args)).unwrap_or(Value::Unknown),
2750        }
2751    }
2752
2753    /// `module.exports` of the module a `require('./x')` call loads. A package is followed only where its value is the
2754    /// config (`module.exports = require('@scope/config')`); nested in a config a required package is a name.
2755    fn required_value(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
2756        let modules = self.modules?;
2757        if !matches!(call.callee.get_inner_expression(), Expression::Identifier(id) if id.name.as_str() == "require") {
2758            return None;
2759        }
2760        let specifier = single_argument(call).and_then(static_string)?;
2761        if depth >= 2 && !specifier.starts_with('.') {
2762            return None;
2763        }
2764        modules.evaluate(self.path, &specifier, &Imported::Default, None)
2765    }
2766
2767    /// `a + b` of known values ([`add`]), and `a === b` or `a !== b` of two known primitives ([`strictly_equal`]);
2768    /// any other operator is unknown.
2769    fn binary(&self, binary: &'p BinaryExpression<'a>, depth: u32) -> Value {
2770        let equal = match binary.operator {
2771            BinaryOperator::Addition => None,
2772            BinaryOperator::StrictEquality => Some(true),
2773            BinaryOperator::StrictInequality => Some(false),
2774            _ => return Value::Unknown,
2775        };
2776        let (left, right) = (self.expr(&binary.left, depth), self.expr(&binary.right, depth));
2777        match equal {
2778            None => each_combination(&[left, right], |pair| match pair {
2779                [left, right] => add(left, right),
2780                _ => Value::Unknown,
2781            }),
2782            Some(equal) => strictly_equal(&left, &right).map_or(Value::Unknown, |same| Value::Bool(same == equal)),
2783        }
2784    }
2785
2786    /// A template literal whose substitutions are known strings, numbers or booleans ([`primitive_text`]), or one of
2787    /// several of them ([`each_combination`]).
2788    fn template(&self, template: &'p TemplateLiteral<'a>, depth: u32) -> Value {
2789        let mut substitutions = Vec::with_capacity(template.expressions.len());
2790        for expression in &template.expressions {
2791            let value = self.expr(expression, depth);
2792            if !matches!(value, Value::OneOf(_)) && primitive_text(&value).is_none() {
2793                return Value::Unknown;
2794            }
2795            substitutions.push(value);
2796        }
2797        each_combination(&substitutions, |texts| {
2798            let mut out = String::new();
2799            for (index, quasi) in template.quasis.iter().enumerate() {
2800                out.push_str(
2801                    &quasi
2802                        .value
2803                        .cooked
2804                        .map_or_else(|| quasi.value.raw.as_str().to_owned(), crate::js_string),
2805                );
2806                if let Some(value) = texts.get(index) {
2807                    let Some(text) = primitive_text(value) else {
2808                        return Value::Unknown;
2809                    };
2810                    out.push_str(&text);
2811                }
2812            }
2813            Value::String(out)
2814        })
2815    }
2816
2817    /// The value of `expr` without its [`Value::Maybe`], and whether it is definite.
2818    fn read(&self, expr: &'p Expression<'a>, depth: u32) -> (Value, bool) {
2819        self.expr(expr, depth).split()
2820    }
2821
2822    /// Whether `expr` is truthy; `None` when that is not known, as for a value that is not definite ([`Self::read`]).
2823    fn truthy(&self, expr: &'p Expression<'a>, depth: u32) -> Option<bool> {
2824        match self.read(expr, depth) {
2825            (value, true) => truthiness(&value),
2826            (_, false) => None,
2827        }
2828    }
2829
2830    /// `import.meta.dirname`, `import.meta.filename`, `import.meta.url`, Bun's `import.meta.dir`, `import.meta.path`
2831    /// and `import.meta.file` (the file name), the `pathname` of a `new URL(…)`, an export of a namespace import, the
2832    /// `length` of a known string or array, and a key of a known object ([`property_of`]). The `length` of a value that
2833    /// is not definite is unknown.
2834    fn member(&self, member: &'p StaticMemberExpression<'a>, depth: u32) -> Value {
2835        self.member_of(&member.object, member.property.name.as_str(), depth)
2836    }
2837
2838    /// `object[key]` with a key known as a string or a number (`base['setupFiles']`, `base[KEY]` of a constant,
2839    /// `LIST[0]`), read as `object.key` is; unknown for any other key.
2840    fn computed_member(&self, member: &'p ComputedMemberExpression<'a>, depth: u32) -> Value {
2841        self.member_key(&member.expression, depth)
2842            .map_or(Value::Unknown, |key| self.member_of(&member.object, &key, depth))
2843    }
2844
2845    /// The member `property` of `object`, as [`Self::member`] reads it.
2846    fn member_of(&self, object: &'p Expression<'a>, property: &str, depth: u32) -> Value {
2847        self.special_member(object, property, depth)
2848            .unwrap_or_else(|| property_of(&self.expr(object, depth), property))
2849    }
2850
2851    /// A member whose value is not a key of the object's value: a path `import.meta` names, the `pathname` of a
2852    /// `new URL(…)` and an export of a namespace import. `None` for any other member.
2853    fn special_member(&self, object: &'p Expression<'a>, property: &str, depth: u32) -> Option<Value> {
2854        Some(match (object.get_inner_expression(), property) {
2855            (Expression::ImportMeta(_), "dirname" | "dir") => Value::String(normalized(&[self.dir])),
2856            (Expression::ImportMeta(_), "filename" | "url" | "path") => Value::String(self.file_path()),
2857            (Expression::ImportMeta(_), "file") => Value::String(self.file.to_owned()),
2858            (url @ Expression::NewExpression(_), "pathname") => self.expr(url, depth),
2859            (Expression::Identifier(namespace), _) if self.is_namespace(namespace.name.as_str()) => {
2860                // What the module changes below the namespace, at the export read, applies to it, as does what code
2861                // the evaluator does not follow may change where it is read.
2862                let name = namespace.name.as_str();
2863                let exports = Value::Object(vec![(property.to_owned(), self.namespace_member(name, property))]);
2864                let exports = self.changed_top(name, exports);
2865                let exports = match self.unstable.of_reference(namespace) {
2866                    Some(change) => unstable(exports, change),
2867                    None => exports,
2868                };
2869                property_of(&exports, property)
2870            }
2871            _ => return None,
2872        })
2873    }
2874
2875    /// The key of a computed member when it evaluates to a string, or to a number, as JavaScript writes it
2876    /// ([`number_text`]): `list[0]` reads the key `"0"`, which [`property_of`] finds in an array.
2877    fn member_key(&self, key: &'p Expression<'a>, depth: u32) -> Option<String> {
2878        match self.expr(key, depth) {
2879            Value::String(key) => Some(key),
2880            Value::Number(number) => number_text(number),
2881            _ => None,
2882        }
2883    }
2884
2885    /// An optional chain (`base?.setupFiles`, `base?.['setupFiles']`, `base?.test.include`): its members read as
2886    /// [`Self::member`] and [`Self::computed_member`] read them, and the chain is `undefined` when the object of an
2887    /// optional member is `null` or `undefined`, the rest of it unread. A call in the chain is evaluated as any call.
2888    fn chain(&self, chain: &'p ChainExpression<'a>, depth: u32) -> Value {
2889        let link = match &chain.expression {
2890            ChainElement::StaticMemberExpression(m) => {
2891                self.chain_member(&m.object, Some(m.property.name.as_str().to_owned()), m.optional, depth)
2892            }
2893            ChainElement::ComputedMemberExpression(m) => {
2894                let key = self.member_key(&m.expression, depth);
2895                self.chain_member(&m.object, key, m.optional, depth)
2896            }
2897            ChainElement::CallExpression(call) => Some(self.call(call, depth)),
2898            ChainElement::TSNonNullExpression(e) => self.chain_link(&e.expression, depth),
2899            ChainElement::PrivateFieldExpression(_) => Some(Value::Unknown),
2900        };
2901        link.unwrap_or(Value::Undefined)
2902    }
2903
2904    /// The value of `expr` inside an optional chain; `None` when an optional member in it short-circuits.
2905    fn chain_link(&self, expr: &'p Expression<'a>, depth: u32) -> Option<Value> {
2906        match expr.get_inner_expression() {
2907            Expression::StaticMemberExpression(m) => {
2908                self.chain_member(&m.object, Some(m.property.name.as_str().to_owned()), m.optional, depth)
2909            }
2910            Expression::ComputedMemberExpression(m) => {
2911                let key = self.member_key(&m.expression, depth);
2912                self.chain_member(&m.object, key, m.optional, depth)
2913            }
2914            _ => Some(self.expr(expr, depth)),
2915        }
2916    }
2917
2918    /// The member `key` of `object` inside an optional chain, unknown when the key is not known: `None` when the
2919    /// member is `optional` and the object is `null` or `undefined`.
2920    fn chain_member(
2921        &self,
2922        object: &'p Expression<'a>,
2923        key: Option<String>,
2924        optional: bool,
2925        depth: u32,
2926    ) -> Option<Value> {
2927        if let Some(special) = key.as_deref().and_then(|key| self.special_member(object, key, depth)) {
2928            return Some(special);
2929        }
2930        let base = self.chain_link(object, depth)?;
2931        if optional && matches!(base, Value::Null | Value::Undefined) {
2932            return None;
2933        }
2934        Some(key.map_or(Value::Unknown, |key| property_of(&base, &key)))
2935    }
2936
2937    /// Whether `name` is a namespace import that no binding of a function being evaluated shadows.
2938    fn is_namespace(&self, name: &str) -> bool {
2939        self.imports
2940            .get(name)
2941            .is_some_and(|binding| binding.imported == Imported::Namespace)
2942            && !self.is_local(name)
2943    }
2944
2945    /// `a && b`, `a || b` and `a ?? b`, when `a` decides which side is the value: over an `a` that is not definite,
2946    /// `a ?? b` and `a || b` are the union of both sides ([`any_of`]), not definite either, and `a && b` is unknown;
2947    /// the side a definite `a` selects is as definite as it is.
2948    fn logical(&self, logical: &'p LogicalExpression<'a>, depth: u32) -> Value {
2949        let (left, definite) = self.read(&logical.left, depth);
2950        if !definite {
2951            return match logical.operator {
2952                LogicalOperator::And => Value::Unknown,
2953                LogicalOperator::Or | LogicalOperator::Coalesce => {
2954                    let right = self.read(&logical.right, depth).0;
2955                    Value::doubtful(any_of(&[Some(left), Some(right)]), false)
2956                }
2957            };
2958        }
2959        match left_is_value(logical.operator, &left) {
2960            Some(true) => left,
2961            Some(false) => self.expr(&logical.right, depth),
2962            None => Value::Unknown,
2963        }
2964    }
2965
2966    /// The values a spread argument may have: one for an expression whose value is known or not decided by a
2967    /// condition, two for one that is not definite (`undefined` and the value, which it may lack), one per branch
2968    /// for `test ? a : b`, `a && b`, `a || b` and `a ?? b` whose test is unknown (parenthesised or not), and one per
2969    /// alternative of a value that is one of several. A left side
2970    /// that adds nothing when spread is [`ConfigValue::Null`]: the falsy `a` of `a && b`, and a comparison, negation or
2971    /// `void` on the left of `||` or `??`.
2972    fn possibilities(&self, expr: &'p Expression<'a>, depth: u32) -> Vec<Value> {
2973        if depth > MAX_DEPTH {
2974            return vec![Value::Unknown];
2975        }
2976        match expr.get_inner_expression() {
2977            Expression::LogicalExpression(logical) => {
2978                let (left, definite) = self.read(&logical.left, depth);
2979                let primitive = is_primitive(&logical.left);
2980                let decided = match logical.operator {
2981                    LogicalOperator::Coalesce if primitive && !is_void(&logical.left) => Some(true),
2982                    _ if !definite => None,
2983                    operator => left_is_value(operator, &left),
2984                };
2985                // The left side as the value: a falsy `a` of `a && b`, a comparison, a negation and `void` add
2986                // nothing.
2987                let left = if primitive || logical.operator == LogicalOperator::And {
2988                    Value::Null
2989                } else {
2990                    left
2991                };
2992                match decided {
2993                    Some(true) => alternatives(left),
2994                    Some(false) => self.possibilities(&logical.right, depth + 1),
2995                    None => {
2996                        let mut out = alternatives(left);
2997                        out.extend(self.possibilities(&logical.right, depth + 1));
2998                        out
2999                    }
3000                }
3001            }
3002            Expression::ConditionalExpression(conditional) => match self.truthy(&conditional.test, depth) {
3003                Some(true) => self.possibilities(&conditional.consequent, depth + 1),
3004                Some(false) => self.possibilities(&conditional.alternate, depth + 1),
3005                None => {
3006                    let mut out = self.possibilities(&conditional.consequent, depth + 1);
3007                    out.extend(self.possibilities(&conditional.alternate, depth + 1));
3008                    out
3009                }
3010            },
3011            _ => match self.expr(expr, depth) {
3012                Value::Maybe(value) => {
3013                    let mut out = vec![Value::Undefined];
3014                    out.extend(alternatives(*value));
3015                    out
3016                }
3017                value => alternatives(value),
3018            },
3019        }
3020    }
3021
3022    /// Adds to `out` what spreading `argument` adds. A conditional spread whose branches are all known adds every key
3023    /// a branch sets, with what the branches have in common (see [`any_of`]); a branch that is not known adds the
3024    /// marker key `...`.
3025    fn spread(&self, argument: &'p Expression<'a>, out: &mut Vec<(String, Value)>, depth: u32) {
3026        let branches: Option<Vec<Vec<(String, Value)>>> = self
3027            .possibilities(argument, depth)
3028            .into_iter()
3029            .map(spread_entries)
3030            .collect();
3031        match branches {
3032            None => out.push((self.spread_key(argument).to_owned(), Value::Unknown)),
3033            Some(mut branches) if branches.len() == 1 => out.extend(branches.pop().unwrap_or_default()),
3034            Some(branches) => {
3035                let added = spread_any(out, &branches);
3036                out.extend(added);
3037            }
3038        }
3039    }
3040
3041    /// `test ? a : b`: the branch a known test takes, or when the test is unknown what both branches have in common or
3042    /// one of them ([`either`]).
3043    fn conditional(&self, conditional: &'p ConditionalExpression<'a>, depth: u32) -> Value {
3044        match self.truthy(&conditional.test, depth) {
3045            Some(true) => self.expr(&conditional.consequent, depth),
3046            Some(false) => self.expr(&conditional.alternate, depth),
3047            None => either(
3048                &self.expr(&conditional.consequent, depth),
3049                &self.expr(&conditional.alternate, depth),
3050            ),
3051        }
3052    }
3053
3054    /// `new URL('./x', import.meta.url)` as the path it names.
3055    fn url(&self, new: &'p NewExpression<'a>, depth: u32) -> Value {
3056        let is_url =
3057            matches!(new.callee.get_inner_expression(), Expression::Identifier(id) if id.name.as_str() == "URL");
3058        let mut args = new
3059            .arguments
3060            .iter()
3061            .map(|arg| arg.as_expression().map_or(Value::Unknown, |e| self.expr(e, depth)));
3062        match (is_url, args.next(), args.next(), args.next()) {
3063            (true, Some(Value::String(relative)), Some(Value::String(base)), None)
3064                if !relative.starts_with('/') && !relative.contains(':') && !base.contains(':') =>
3065            {
3066                Value::String(normalized(&[base.as_str(), "..", relative.as_str()]))
3067            }
3068            _ => Value::Unknown,
3069        }
3070    }
3071
3072    /// Whether `callee` is the function `wanted` of the `path` module, however it was imported.
3073    fn is_path_function(&self, callee: &Expression<'_>, wanted: &str) -> bool {
3074        match callee.get_inner_expression() {
3075            Expression::Identifier(id) => self
3076                .bindings
3077                .path_functions
3078                .get(id.name.as_str())
3079                .is_some_and(|function| function == wanted),
3080            Expression::StaticMemberExpression(member) => {
3081                member.property.name.as_str() == wanted
3082                    && matches!(
3083                        member.object.get_inner_expression(),
3084                        Expression::Identifier(object) if self.bindings.path_modules.contains(object.name.as_str())
3085                    )
3086            }
3087            _ => false,
3088        }
3089    }
3090
3091    /// A call of a `path` or `url` function, or of `process.cwd()`; `None` for any other call.
3092    fn path_call(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3093        let function = match call.callee.get_inner_expression() {
3094            Expression::Identifier(id) => {
3095                let name = id.name.as_str();
3096                if self.bindings.file_url_to_path.contains(name) {
3097                    "fileURLToPath"
3098                } else {
3099                    self.bindings.path_functions.get(name)?.as_str()
3100                }
3101            }
3102            Expression::StaticMemberExpression(member) => {
3103                // `path.posix.join` computes like `path.join` for the relative paths a config holds.
3104                let object = match member.object.get_inner_expression() {
3105                    Expression::StaticMemberExpression(inner) if inner.property.name.as_str() == "posix" => {
3106                        inner.object.get_inner_expression()
3107                    }
3108                    other => other,
3109                };
3110                let Expression::Identifier(object) = object else {
3111                    return None;
3112                };
3113                let (object, property) = (object.name.as_str(), member.property.name.as_str());
3114                let path_function = self.bindings.path_modules.contains(object) && PATH_FUNCTIONS.contains(&property);
3115                let url_function = self.bindings.url_modules.contains(object) && property == "fileURLToPath";
3116                if path_function || url_function {
3117                    property
3118                } else if object == "process" && property == "cwd" {
3119                    return Some(Value::String(".".to_owned()));
3120                } else {
3121                    return None;
3122                }
3123            }
3124            _ => return None,
3125        };
3126        Some(each_combination(&self.arguments(call, depth), |args| {
3127            apply_path_function(function, args)
3128        }))
3129    }
3130
3131    /// The values of a call's arguments, a spread argument unknown, recorded for [`Self::unknown_call_arguments`].
3132    fn arguments(&self, call: &'p CallExpression<'a>, depth: u32) -> Vec<Value> {
3133        let values: Vec<Value> = call
3134            .arguments
3135            .iter()
3136            .map(|arg| arg.as_expression().map_or(Value::Unknown, |e| self.expr(e, depth)))
3137            .collect();
3138        self.arguments_of
3139            .borrow_mut()
3140            .entry(call.span)
3141            .or_insert_with(|| values.clone());
3142        values
3143    }
3144
3145    /// The directory of a package's manifest, which is the package itself: `dirname(require.resolve(join(pkg,
3146    /// 'package.json')))`, the idiom the Storybook CLI writes, also with `import.meta.resolve` for `require.resolve`,
3147    /// its URL turned into a path with `fileURLToPath`, the package written in several `join` arguments
3148    /// (`'@scope', 'name'`), and the manifest written as a string (`` `${pkg}/package.json` ``,
3149    /// `'pkg/package.json'`) instead of the `join`. The directory is classified by [`manifest_directory`]: `None`
3150    /// leaves a relative path to the path functions.
3151    fn package_directory(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3152        if !self.is_path_function(&call.callee, "dirname") {
3153            return None;
3154        }
3155        let mut resolved = single_argument(call)?.get_inner_expression();
3156        if let Expression::CallExpression(to_path) = resolved
3157            && self.is_file_url_to_path(&to_path.callee)
3158        {
3159            resolved = single_argument(to_path)?.get_inner_expression();
3160        }
3161        let Expression::CallExpression(resolve) = resolved else {
3162            return None;
3163        };
3164        if !is_module_resolve(resolve) {
3165            return None;
3166        }
3167        let manifest = single_argument(resolve)?.get_inner_expression();
3168        if let Expression::CallExpression(join) = manifest
3169            && self.is_path_function(&join.callee, "join")
3170        {
3171            let arguments: Vec<&Expression<'a>> = join
3172                .arguments
3173                .iter()
3174                .map(Argument::as_expression)
3175                .collect::<Option<_>>()?;
3176            let (file, segments) = arguments.split_last()?;
3177            if segments.is_empty() || static_string(file).as_deref() != Some("package.json") {
3178                return None;
3179            }
3180            let segments: Option<Vec<String>> = segments
3181                .iter()
3182                .copied()
3183                .map(|segment| match self.expr(segment, depth) {
3184                    Value::String(text) => Some(text),
3185                    _ => None,
3186                })
3187                .collect();
3188            return match segments {
3189                Some(segments) => manifest_directory(&segments.join("/")),
3190                None => Some(Value::Unknown),
3191            };
3192        }
3193        let Value::String(manifest) = self.expr(manifest, depth) else {
3194            return None;
3195        };
3196        manifest_directory(manifest.strip_suffix("/package.json")?)
3197    }
3198
3199    /// Whether `callee` is `fileURLToPath` of the `url` module, however it was imported.
3200    fn is_file_url_to_path(&self, callee: &Expression<'_>) -> bool {
3201        match callee.get_inner_expression() {
3202            Expression::Identifier(id) => self.bindings.file_url_to_path.contains(id.name.as_str()),
3203            Expression::StaticMemberExpression(member) => {
3204                member.property.name.as_str() == "fileURLToPath"
3205                    && matches!(
3206                        member.object.get_inner_expression(),
3207                        Expression::Identifier(object) if self.bindings.url_modules.contains(object.name.as_str())
3208                    )
3209            }
3210            _ => false,
3211        }
3212    }
3213
3214    /// `require.resolve(x)`, `import.meta.resolve(x)` and `Promise.resolve(x)`, which evaluate to `x`.
3215    fn resolve_call(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3216        let known = is_module_resolve(call) || is_resolve_of(call, "Promise");
3217        known.then(|| single_argument(call).map_or(Value::Unknown, |arg| self.expr(arg, depth)))
3218    }
3219
3220    /// A call of a glob function as the array of its literal patterns.
3221    fn glob_call(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3222        let is_glob = match call.callee.get_inner_expression() {
3223            Expression::Identifier(id) => self.bindings.glob_names.contains(id.name.as_str()),
3224            Expression::StaticMemberExpression(member) => {
3225                GLOB_MEMBERS.contains(&member.property.name.as_str())
3226                    && matches!(
3227                        member.object.get_inner_expression(),
3228                        Expression::Identifier(object) if self.bindings.glob_names.contains(object.name.as_str())
3229                    )
3230            }
3231            _ => false,
3232        };
3233        if !is_glob {
3234            return None;
3235        }
3236        Some(match call.arguments.first().and_then(Argument::as_expression) {
3237            Some(patterns) => match self.read(patterns, depth).0 {
3238                Value::String(pattern) => Value::Array(vec![Value::String(pattern)]),
3239                array @ Value::Array(_) => array,
3240                _ => Value::Unknown,
3241            },
3242            None => Value::Unknown,
3243        })
3244    }
3245
3246    /// `defineConfig(a, [b, c], ...d)` of `eslint/config` and `tseslint.config(…)`: the flat config list of all their
3247    /// arguments, nested lists flattened.
3248    fn flat_config_call(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3249        let combines = match call.callee.get_inner_expression() {
3250            Expression::Identifier(id) => self.bindings.flat_config_functions.contains(id.name.as_str()),
3251            Expression::StaticMemberExpression(member) => {
3252                member.property.name.as_str() == "config"
3253                    && matches!(
3254                        member.object.get_inner_expression(),
3255                        Expression::Identifier(object) if self.bindings.flat_config_modules.contains(object.name.as_str())
3256                    )
3257            }
3258            _ => false,
3259        };
3260        if !combines {
3261            return None;
3262        }
3263        let mut list = Vec::new();
3264        for argument in &call.arguments {
3265            let value = match argument {
3266                Argument::SpreadElement(spread) => self.expr(&spread.argument, depth),
3267                other => other.as_expression().map_or(Value::Unknown, |e| self.expr(e, depth)),
3268            };
3269            flatten_into(value, &mut list);
3270        }
3271        Some(Value::Array(list))
3272    }
3273
3274    /// A call of a top-level function or of a function declared inside the function being evaluated.
3275    fn local_call(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3276        let Expression::Identifier(id) = call.callee.get_inner_expression() else {
3277            return None;
3278        };
3279        let name = id.name.as_str();
3280        let (callable, isolate) = match self.local_function(name) {
3281            Some(LocalBinding::Function(callable)) => (callable, false),
3282            Some(LocalBinding::Other) => return None,
3283            None => match self.functions.get(name) {
3284                Some(callable) => (*callable, true),
3285                None => return self.imported_call(name, call, depth),
3286            },
3287        };
3288        Some(self.invoke(callable, Some(self.arguments(call, depth).as_slice()), isolate, depth))
3289    }
3290
3291    /// The innermost binding of `name` in the functions being evaluated; `None` when no function being evaluated binds
3292    /// it. The binding's kind decides, so a value bound to the name is not cloned to find out.
3293    fn local_function(&self, name: &str) -> Option<LocalBinding<'p, 'a>> {
3294        self.frames.borrow().iter().rev().find_map(|frame| {
3295            frame.get(name).map(|binding| match binding {
3296                Binding::Callable(callable) => LocalBinding::Function(*callable),
3297                Binding::Value(_) | Binding::Alias(_) => LocalBinding::Other,
3298            })
3299        })
3300    }
3301
3302    /// The function a callback argument `expr` is, and whether a call of it is isolated from the bindings of the
3303    /// functions being evaluated: an inline function, which sees them, a function one of them declares, or a top-level
3304    /// function. `None` for anything else, such as an imported function.
3305    fn callback(&self, expr: &'p Expression<'a>) -> Option<(Callable<'p, 'a>, bool)> {
3306        if let Some(callable) = Callable::of_expression(expr) {
3307            return Some((callable, false));
3308        }
3309        let Expression::Identifier(id) = expr.get_inner_expression() else {
3310            return None;
3311        };
3312        let name = id.name.as_str();
3313        match self.local_function(name) {
3314            Some(LocalBinding::Function(callable)) => Some((callable, false)),
3315            Some(LocalBinding::Other) => None,
3316            None => self.functions.get(name).map(|callable| (*callable, true)),
3317        }
3318    }
3319
3320    /// `list.map(f)`, `list.flatMap(f)` and `list.filter(f)` of a known array with a callback the evaluator can call
3321    /// ([`Self::callback`]): `f` is called with each element and its index (the array itself is not known to it),
3322    /// within the call budget. An unknown element, which may stand for a spread of any length, is not passed to `f` and
3323    /// stays unknown, and the indexes after it are unknown. `flatMap` adds the elements of an array `f` returns, or the
3324    /// value itself; `filter` keeps the elements `f` is truthy for, and one it may be truthy for, which leaves the result
3325    /// not definite. A result is not definite when the array is not. `None` for any other call, and when the array or
3326    /// the callback is not known.
3327    fn array_callback_call(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3328        let Expression::StaticMemberExpression(member) = call.callee.get_inner_expression() else {
3329            return None;
3330        };
3331        let method = member.property.name.as_str();
3332        if !ARRAY_CALLBACK_METHODS.contains(&method) {
3333            return None;
3334        }
3335        let (callable, isolate) = self.callback(call.arguments.first()?.as_expression()?)?;
3336        let (Value::Array(items), mut definite) = self.read(&member.object, depth) else {
3337            return None;
3338        };
3339        let mut out = Vec::new();
3340        let mut indexed = true;
3341        for (index, item) in items.into_iter().enumerate() {
3342            if item == Value::Unknown {
3343                indexed = false;
3344                out.push(Value::Unknown);
3345                continue;
3346            }
3347            let index = u32::try_from(index)
3348                .ok()
3349                .filter(|_| indexed)
3350                .map_or(Value::Unknown, |index| Value::Number(f64::from(index)));
3351            let args = [item.clone(), index, Value::Unknown];
3352            let (result, result_definite) = self.invoke(callable, Some(args.as_slice()), isolate, depth).split();
3353            match method {
3354                "map" => out.push(Value::doubtful(result, result_definite)),
3355                "flatMap" => match result {
3356                    Value::Array(listed) => {
3357                        out.extend(listed);
3358                        definite &= result_definite;
3359                    }
3360                    Value::OneOf(_) => out.push(Value::Unknown),
3361                    other => out.push(Value::doubtful(other, result_definite)),
3362                },
3363                _ => match truthiness(&result).filter(|_| result_definite) {
3364                    Some(false) => {}
3365                    Some(true) => out.push(item),
3366                    None => {
3367                        definite = false;
3368                        out.push(item);
3369                    }
3370                },
3371            }
3372        }
3373        Some(Value::doubtful(Value::Array(out), definite))
3374    }
3375
3376    /// `Object.fromEntries(list)` of a known list of `[key, value]` pairs: the object they make, a later pair for a key
3377    /// replacing the value of an earlier one, and a key that is a number or a boolean written as JavaScript writes it
3378    /// ([`primitive_text`]). An element that is not such a pair with a known key may add any key, recorded under the
3379    /// marker key `list` spreads under ([`Self::spread_key`]), before the pairs after it. The keys are not definite
3380    /// when the list is not. Unknown for any other argument; `None` for any other call, and for an `Object` the module
3381    /// binds.
3382    fn object_from_entries(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3383        let Expression::StaticMemberExpression(member) = call.callee.get_inner_expression() else {
3384            return None;
3385        };
3386        let on_object = matches!(
3387            member.object.get_inner_expression(),
3388            Expression::Identifier(object) if object.name.as_str() == "Object"
3389        );
3390        if !on_object || member.property.name.as_str() != "fromEntries" || self.binds("Object") {
3391            return None;
3392        }
3393        let Some(argument) = single_argument(call) else {
3394            return Some(Value::Unknown);
3395        };
3396        let (Value::Array(pairs), definite) = self.read(argument, depth) else {
3397            return Some(Value::Unknown);
3398        };
3399        let mut entries: Vec<(String, Value)> = Vec::new();
3400        for pair in pairs {
3401            let Some((key, value)) = entry_pair(pair) else {
3402                let marker = self.spread_key(argument);
3403                if entries.last().is_none_or(|(last, _)| last != marker) {
3404                    entries.push((marker.to_owned(), Value::Unknown));
3405                }
3406                continue;
3407            };
3408            let value = Value::doubtful(value, definite);
3409            let found = entries
3410                .iter()
3411                .rposition(|(name, _)| *name == key || ConfigValue::is_spread_key(name))
3412                .filter(|&at| entries[at].0 == key);
3413            match found {
3414                Some(at) => entries[at].1 = value,
3415                None => entries.push((key, value)),
3416            }
3417        }
3418        Some(Value::Object(entries))
3419    }
3420
3421    /// A call of a config combinator (`mergeConfig`, `defu`, `Object.assign`, `deepmerge`, `merge`): the merge of the
3422    /// object arguments. An argument that cannot be known contributes a `...` entry, so the known part still applies.
3423    /// A merge given options that may say how it merges (`deepmerge`'s third argument, `mergeWith`'s customizer) is
3424    /// unknown.
3425    fn merge_call(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3426        let style = self.merge_style(call)?;
3427        if takes_options(&call.callee, call.arguments.len()) {
3428            // The arguments are still recorded, for the files they name ([`Self::unknown_call_arguments`]).
3429            self.arguments(call, depth);
3430            return Some(Value::Unknown);
3431        }
3432        let mut merged: Option<Vec<(String, Value)>> = None;
3433        let expressions = call.arguments.iter().map(Argument::as_expression);
3434        let mut values: Vec<_> = self.arguments(call, depth).into_iter().zip(expressions).collect();
3435        if style == MergeStyle::FirstWins {
3436            values.reverse();
3437        }
3438        for (value, expression) in values {
3439            let (value, definite) = value.split();
3440            match value {
3441                Value::Null | Value::Undefined | Value::Bool(_) => {}
3442                Value::Object(entries) => {
3443                    // An argument that may be absent may set each of its keys or not.
3444                    let entries = entries
3445                        .into_iter()
3446                        .map(|(key, value)| (key, Value::doubtful(value, definite)))
3447                        .collect();
3448                    let base = merged.get_or_insert_with(Vec::new);
3449                    merge_objects(base, entries, style != MergeStyle::Shallow);
3450                }
3451                _ => {
3452                    let key = expression.map_or(UNKNOWN_SPREAD, |expression| self.spread_key(expression));
3453                    merge_objects(
3454                        merged.get_or_insert_with(Vec::new),
3455                        vec![(key.to_owned(), Value::Unknown)],
3456                        false,
3457                    );
3458                }
3459            }
3460        }
3461        Some(merged.map_or(Value::Unknown, Value::Object))
3462    }
3463
3464    /// `list.filter(Boolean)` of a known array: its elements that may be truthy. An element that may be falsy is kept
3465    /// as the values it has when it is not (`ci ? 'github' : null` as `'github'`, and in an array literal `ci &&
3466    /// 'github'` as `'github'`), and the array is then not definite, as its length is not.
3467    fn filter_call(&self, call: &'p CallExpression<'a>, depth: u32) -> Option<Value> {
3468        let Expression::StaticMemberExpression(member) = call.callee.get_inner_expression() else {
3469            return None;
3470        };
3471        let by_boolean = matches!(
3472            single_argument(call).map(Expression::get_inner_expression),
3473            Some(Expression::Identifier(id))
3474                if id.name.as_str() == "Boolean" && !self.binds("Boolean")
3475        );
3476        if member.property.name.as_str() != "filter" || !by_boolean {
3477            return None;
3478        }
3479        let (items, mut definite) = match member.object.get_inner_expression() {
3480            Expression::ArrayExpression(array) => self.filtered_elements(&array.elements, depth),
3481            object => match self.read(object, depth) {
3482                (Value::Array(items), definite) => (items, definite),
3483                _ => return None,
3484            },
3485        };
3486        let mut kept = Vec::new();
3487        for item in items {
3488            if let Some((value, may_drop)) = truthy_part(item) {
3489                definite &= !may_drop;
3490                kept.push(value);
3491            }
3492        }
3493        Some(Value::doubtful(Value::Array(kept), definite))
3494    }
3495
3496    /// Whether the module or a function being evaluated binds `name` itself, shadowing the global.
3497    fn binds(&self, name: &str) -> bool {
3498        self.is_local(name)
3499            || self.consts.contains_key(name)
3500            || self.functions.contains_key(name)
3501            || self.imports.contains_key(name)
3502    }
3503
3504    /// The elements of an array literal that `.filter(Boolean)` filters, and whether they are definite: as
3505    /// [`Self::array`] has them, except that `test && x` with an unknown test, which is dropped when it is falsy, is
3506    /// `x` and leaves them not definite.
3507    fn filtered_elements(&self, elements: &'p [ArrayExpressionElement<'a>], depth: u32) -> (Vec<Value>, bool) {
3508        let mut items = Vec::new();
3509        let mut definite = true;
3510        for element in elements {
3511            if let Some(Expression::LogicalExpression(and)) =
3512                element.as_expression().map(Expression::get_inner_expression)
3513                && and.operator == LogicalOperator::And
3514                && self.truthy(&and.left, depth).is_none()
3515            {
3516                items.push(self.expr(&and.right, depth));
3517                definite = false;
3518                continue;
3519            }
3520            let (part, part_definite) = self.array(std::slice::from_ref(element), depth).split();
3521            if let Value::Array(part) = part {
3522                items.extend(part);
3523            }
3524            definite &= part_definite;
3525        }
3526        (items, definite)
3527    }
3528
3529    /// Records the argument of `<config_setter>.set(…)` as the config; returns whether `call` is such a call.
3530    fn capture_set(&self, call: &'p CallExpression<'a>, depth: u32) -> bool {
3531        let Some(setter) = self.config_setter.borrow().clone() else {
3532            return false;
3533        };
3534        let Expression::StaticMemberExpression(member) = call.callee.get_inner_expression() else {
3535            return false;
3536        };
3537        let on_setter =
3538            matches!(member.object.get_inner_expression(), Expression::Identifier(id) if id.name.as_str() == setter);
3539        let Some(argument) = single_argument(call).filter(|_| on_setter && member.property.name.as_str() == "set")
3540        else {
3541            return false;
3542        };
3543        let value = self.expr(argument, depth);
3544        *self.captured.borrow_mut() = Some(value);
3545        true
3546    }
3547
3548    fn call(&self, call: &'p CallExpression<'a>, depth: u32) -> Value {
3549        if self.capture_set(call, depth) {
3550            return Value::Unknown;
3551        }
3552        let special = self
3553            .required_value(call, depth)
3554            .or_else(|| required_package(call, depth))
3555            .or_else(|| self.package_directory(call, depth))
3556            .or_else(|| self.path_call(call, depth))
3557            .or_else(|| self.resolve_call(call, depth))
3558            .or_else(|| self.glob_call(call, depth))
3559            .or_else(|| self.flat_config_call(call, depth))
3560            .or_else(|| self.local_call(call, depth))
3561            .or_else(|| self.merge_call(call, depth))
3562            .or_else(|| self.filter_call(call, depth))
3563            .or_else(|| self.array_callback_call(call, depth))
3564            .or_else(|| self.object_from_entries(call, depth));
3565        if let Some(value) = special {
3566            return value;
3567        }
3568        self.defined(call)
3569            .map_or(Value::Unknown, |first| self.expr(first, depth))
3570    }
3571
3572    /// What a `define…` helper is given ([`defined`]) when no code of the project is behind it
3573    /// ([`Unstable::calls_foreign`]): a helper of a module of the project or of a workspace package, or a method of
3574    /// an object the module declares (`const { defineProperty } = Object`), may return anything.
3575    fn defined(&self, call: &'p CallExpression<'a>) -> Option<&'p Expression<'a>> {
3576        defined(call).filter(|_| self.unstable.calls_foreign(call))
3577    }
3578
3579    /// How `call` merges its arguments ([`merge_style`]) when no code of the project is behind it
3580    /// ([`Unstable::calls_foreign`]): a `merge` of a module of the project may merge any way.
3581    fn merge_style(&self, call: &CallExpression<'_>) -> Option<MergeStyle> {
3582        merge_style(&call.callee, self.bindings).filter(|_| self.unstable.calls_foreign(call))
3583    }
3584
3585    /// The arguments of the calls the evaluator cannot follow whose results `expr`, which evaluated to `value`, is or
3586    /// merges: a call that evaluated to unknown, looking through `await`, a `define*` call to the expression it wraps,
3587    /// a top-level constant to its initialiser, a function the tool calls to the expression it returns
3588    /// ([`Self::returned_call_arguments`]), a merge (`mergeConfig`, `Object.assign`) to each of its arguments, and an
3589    /// object or array literal to what it holds ([`Self::nested_call_arguments`]). The values are those the evaluation
3590    /// computed; arguments it did not evaluate are evaluated now.
3591    fn unknown_call_arguments(&self, expr: &'p Expression<'a>, value: &Value, depth: u32) -> Vec<Value> {
3592        if depth > MAX_DEPTH {
3593            return Vec::new();
3594        }
3595        let depth = depth + 1;
3596        match expr.get_inner_expression() {
3597            Expression::AwaitExpression(a) => self.unknown_call_arguments(&a.argument, value, depth),
3598            Expression::CallExpression(call) => self.unknown_arguments_of_call(call, value, depth),
3599            Expression::Identifier(id) => self.constant_call_arguments(id.name.as_str(), value, depth),
3600            Expression::ArrowFunctionExpression(f) => self.returned_call_arguments(Callable::Arrow(f), value, depth),
3601            Expression::FunctionExpression(f) => self.returned_call_arguments(Callable::Function(f), value, depth),
3602            Expression::ObjectExpression(object) => self.object_call_arguments(&object.properties, value, depth),
3603            Expression::ArrayExpression(array) => self.array_call_arguments(&array.elements, value, depth),
3604            _ => Vec::new(),
3605        }
3606    }
3607
3608    /// [`Self::unknown_call_arguments`] of `expr`, which evaluated to `value`, inside an object or array literal: of
3609    /// a call, `await` looked through, and of the object and array literals it nests. A name is not looked up there,
3610    /// so constants that refer to each other are not walked once per reference.
3611    fn nested_call_arguments(&self, expr: &'p Expression<'a>, value: &Value, depth: u32) -> Vec<Value> {
3612        if depth > MAX_DEPTH {
3613            return Vec::new();
3614        }
3615        let depth = depth + 1;
3616        match expr.get_inner_expression() {
3617            Expression::AwaitExpression(a) => self.nested_call_arguments(&a.argument, value, depth),
3618            Expression::CallExpression(call) => self.unknown_arguments_of_call(call, value, depth),
3619            Expression::ObjectExpression(object) => self.object_call_arguments(&object.properties, value, depth),
3620            Expression::ArrayExpression(array) => self.array_call_arguments(&array.elements, value, depth),
3621            _ => Vec::new(),
3622        }
3623    }
3624
3625    /// The arguments of the calls the evaluator cannot follow in the keys of an object literal that evaluated to
3626    /// `value`, each put in an object under the key it is found at (`{ test: <argument> }`), so a file named under an
3627    /// exclusion key (`coverage: { exclude: makeList(['./a.ts']) }`) is still found under it. A spread counts as
3628    /// unknown only when the object has an unknown spread.
3629    fn object_call_arguments(&self, properties: &'p [ObjectPropertyKind<'a>], value: &Value, depth: u32) -> Vec<Value> {
3630        let object = value.definite();
3631        let spread = if object.get(UNKNOWN_SPREAD).is_some() || object.get(OWN_SPREAD).is_some() {
3632            Value::Unknown
3633        } else {
3634            Value::Null
3635        };
3636        let mut out = Vec::new();
3637        for property in properties {
3638            match property {
3639                ObjectPropertyKind::ObjectProperty(p) => {
3640                    let Some(key) = p.key.static_name() else {
3641                        continue;
3642                    };
3643                    let key = crate::js_string(key.as_js_str());
3644                    let field = object.get(&key).cloned().unwrap_or(Value::Unknown);
3645                    out.extend(
3646                        self.nested_call_arguments(&p.value, &field, depth)
3647                            .into_iter()
3648                            .map(|argument| Value::Object(vec![(key.clone(), argument)])),
3649                    );
3650                }
3651                ObjectPropertyKind::SpreadProperty(s) => {
3652                    out.extend(self.nested_call_arguments(&s.argument, &spread, depth));
3653                }
3654            }
3655        }
3656        out
3657    }
3658
3659    /// The arguments of the calls the evaluator cannot follow in the elements of an array literal that evaluated to
3660    /// `value`, up to its first spread, after which an element's value is not known by its position.
3661    fn array_call_arguments(
3662        &self,
3663        elements: &'p [ArrayExpressionElement<'a>],
3664        value: &Value,
3665        depth: u32,
3666    ) -> Vec<Value> {
3667        let unknown = Value::Unknown;
3668        let items: &[Value] = match value.definite() {
3669            Value::Array(items) => items,
3670            _ => &[],
3671        };
3672        elements
3673            .iter()
3674            .take_while(|element| !matches!(element, ArrayExpressionElement::SpreadElement(_)))
3675            .enumerate()
3676            .filter_map(|(index, element)| Some((element.as_expression()?, items.get(index).unwrap_or(&unknown))))
3677            .flat_map(|(element, item)| self.nested_call_arguments(element, item, depth))
3678            .collect()
3679    }
3680
3681    /// [`Self::unknown_call_arguments`] of `call`.
3682    fn unknown_arguments_of_call(&self, call: &'p CallExpression<'a>, value: &Value, depth: u32) -> Vec<Value> {
3683        if let Some(first) = self.defined(call) {
3684            return self.unknown_call_arguments(first, value, depth);
3685        }
3686        let recorded = self.arguments_of.borrow().get(&call.span).cloned();
3687        if self.merge_style(call).is_some() {
3688            let values = recorded.unwrap_or_default();
3689            return call
3690                .arguments
3691                .iter()
3692                .zip(&values)
3693                .filter_map(|(argument, value)| Some((argument.as_expression()?, value)))
3694                .flat_map(|(argument, value)| self.unknown_call_arguments(argument, value, depth))
3695                .collect();
3696        }
3697        if !matches!(value.definite(), Value::Unknown | Value::OneOf(_)) {
3698            return Vec::new();
3699        }
3700        recorded.unwrap_or_else(|| self.arguments(call, depth))
3701    }
3702
3703    /// [`Self::unknown_call_arguments`] of the top-level constant `name`, outside any function's bindings as it is
3704    /// evaluated; none for any other name.
3705    fn constant_call_arguments(&self, name: &str, value: &Value, depth: u32) -> Vec<Value> {
3706        if self.is_local(name) || self.functions.contains_key(name) {
3707            return Vec::new();
3708        }
3709        let Some(&init) = self.consts.get(name) else {
3710            return Vec::new();
3711        };
3712        let saved = std::mem::take(&mut *self.frames.borrow_mut());
3713        let arguments = self.unknown_call_arguments(init, value, depth);
3714        *self.frames.borrow_mut() = saved;
3715        arguments
3716    }
3717
3718    /// [`Self::unknown_call_arguments`] of what a function the tool calls returns, `value`, when its body is one
3719    /// expression or a single `return`: its parameters are unknown there, as when the evaluation called it. Any other
3720    /// body has none.
3721    fn returned_call_arguments(&self, callable: Callable<'p, 'a>, value: &Value, depth: u32) -> Vec<Value> {
3722        let (params, body, statements) = match callable {
3723            Callable::Arrow(f) => (&f.params, f.get_expression(), f.get_function_body()),
3724            Callable::Function(f) => (&f.params, None, f.body.as_deref()),
3725        };
3726        let returned = body.or_else(|| match statements?.statements.as_slice() {
3727            [only] => single_return(only)?.argument.as_ref(),
3728            _ => None,
3729        });
3730        let Some(returned) = returned else {
3731            return Vec::new();
3732        };
3733        self.frames.borrow_mut().push(Frame::default());
3734        for param in &params.items {
3735            self.bind_pattern(&param.pattern, Value::Unknown, depth);
3736        }
3737        if let Some(param) = &params.rest {
3738            self.bind_pattern(&param.rest.argument, Value::Unknown, depth);
3739        }
3740        let arguments = self.unknown_call_arguments(returned, value, depth);
3741        self.frames.borrow_mut().pop();
3742        arguments
3743    }
3744
3745    /// Calls `callable` with `args`, or with arguments that cannot be known when it is `None`: a function the tool calls
3746    /// (an exported config function, the callback of `defineConfig`) is given what the config does not say, so each of
3747    /// its parameters is unknown, a default included. With `isolate`, the function cannot see the bindings of the
3748    /// function evaluating it.
3749    fn invoke(&self, callable: Callable<'p, 'a>, args: Option<&[Value]>, isolate: bool, depth: u32) -> Value {
3750        if self.calls.get() >= MAX_CALLS {
3751            return Value::Unknown;
3752        }
3753        self.calls.set(self.calls.get() + 1);
3754        let params = match callable {
3755            Callable::Function(f) => &f.params,
3756            Callable::Arrow(f) => &f.params,
3757        };
3758        let saved = if isolate {
3759            std::mem::take(&mut *self.frames.borrow_mut())
3760        } else {
3761            Vec::new()
3762        };
3763        // Parameters are bound in order inside the function, so a default sees the parameters before it.
3764        self.frames.borrow_mut().push(Frame::default());
3765        for (index, param) in params.items.iter().enumerate() {
3766            let value = match (args.map(|args| args.get(index)), &param.initializer) {
3767                (Some(Some(value)), Some(default)) => self.defaulted(value.clone(), default, depth),
3768                (Some(Some(value)), None) => value.clone(),
3769                (Some(None), Some(default)) => self.expr(default, depth),
3770                (None | Some(None), _) => Value::Unknown,
3771            };
3772            self.bind_pattern(&param.pattern, value, depth);
3773        }
3774        if let Some(param) = &params.rest {
3775            self.bind_pattern(&param.rest.argument, Value::Unknown, depth);
3776        }
3777        let value = match callable {
3778            Callable::Arrow(f) => match f.get_expression() {
3779                Some(body) => self.expr(body, depth),
3780                None => f
3781                    .get_function_body()
3782                    .map_or(Value::Unknown, |b| self.returned(&b.statements, depth)),
3783            },
3784            Callable::Function(f) => f
3785                .body
3786                .as_ref()
3787                .map_or(Value::Unknown, |b| self.returned(&b.statements, depth)),
3788        };
3789        self.frames.borrow_mut().pop();
3790        if isolate {
3791            *self.frames.borrow_mut() = saved;
3792        }
3793        value
3794    }
3795
3796    /// Calls `callable` with `argument` as its first parameter: the value it returns or, when a block body finishes
3797    /// without a `return`, the value it leaves that parameter with.
3798    fn call_with(&self, callable: Callable<'p, 'a>, argument: Value, depth: u32) -> Value {
3799        let statements = match callable {
3800            Callable::Arrow(f) => f.get_function_body().map(|body| &body.statements),
3801            Callable::Function(f) => f.body.as_ref().map(|body| &body.statements),
3802        };
3803        let (Some(name), Some(statements)) = (first_parameter(callable), statements) else {
3804            return self.invoke(callable, Some(std::slice::from_ref(&argument)), false, depth);
3805        };
3806        let params = match callable {
3807            Callable::Function(f) => &f.params,
3808            Callable::Arrow(f) => &f.params,
3809        };
3810        self.frames.borrow_mut().push(Frame::default());
3811        self.bind(&name, argument);
3812        for param in params.items.iter().skip(1) {
3813            self.bind_pattern(&param.pattern, Value::Unknown, depth);
3814        }
3815        if let Some(param) = &params.rest {
3816            self.bind_pattern(&param.rest.argument, Value::Unknown, depth);
3817        }
3818        let returned = self.run(statements, depth);
3819        let frame = self.frames.borrow_mut().pop();
3820        returned.unwrap_or_else(|| match frame.and_then(|mut frame| frame.remove(&name)) {
3821            Some(Binding::Value(value)) => value,
3822            _ => Value::Unknown,
3823        })
3824    }
3825
3826    /// Binds the names `pattern` declares to the parts of `value` they take, in the innermost frame. A property of an
3827    /// object pattern takes its key's value as a member read does ([`property_of`]), with its default applied
3828    /// ([`Self::defaulted`]); a name that cannot be known (an array pattern, a rest element, a key an unknown spread
3829    /// may provide) is bound to [`ConfigValue::Unknown`], so it does not resolve to a binding of an outer scope.
3830    fn bind_pattern(&self, pattern: &'p BindingPattern<'a>, value: Value, depth: u32) {
3831        match pattern {
3832            BindingPattern::BindingIdentifier(id) => self.bind(id.name.as_str(), value),
3833            BindingPattern::AssignmentPattern(p) => {
3834                let value = self.defaulted(value, &p.right, depth);
3835                self.bind_pattern(&p.left, value, depth);
3836            }
3837            BindingPattern::ObjectPattern(object) => {
3838                for property in &object.properties {
3839                    let field = property.key.static_name().map_or(Value::Unknown, |key| {
3840                        property_of(&value, &crate::js_string(key.as_js_str()))
3841                    });
3842                    self.bind_pattern(&property.value, field, depth);
3843                }
3844                if let Some(rest) = &object.rest {
3845                    self.bind_pattern(&rest.argument, Value::Unknown, depth);
3846                }
3847            }
3848            BindingPattern::ArrayPattern(_) => {
3849                for id in pattern.get_binding_identifiers() {
3850                    self.bind(id.name.as_str(), Value::Unknown);
3851                }
3852            }
3853        }
3854    }
3855
3856    /// `value` with the `default` of a parameter or a destructuring applied: the default for `undefined`, the union of
3857    /// the value and the default, not definite, for a value that is not definite and may be absent, and the value
3858    /// itself otherwise, `null` included.
3859    fn defaulted(&self, value: Value, default: &'p Expression<'a>, depth: u32) -> Value {
3860        match value {
3861            Value::Undefined => self.expr(default, depth),
3862            Value::Maybe(value) => {
3863                let default = self.read(default, depth).0;
3864                Value::doubtful(any_of(&[Some(*value), Some(default)]), false)
3865            }
3866            value => value,
3867        }
3868    }
3869
3870    /// Binds `name` to `value` in the innermost frame.
3871    fn bind(&self, name: &str, value: Value) {
3872        self.bind_to(name, Binding::Value(value));
3873    }
3874
3875    /// Binds `name` in the innermost frame, shadowing any binding of it in an outer one.
3876    fn bind_to(&self, name: &str, binding: Binding<'p, 'a>) {
3877        if let Some(frame) = self.frames.borrow_mut().last_mut() {
3878            frame.insert(name.to_owned(), binding);
3879        }
3880    }
3881
3882    /// Value returned by a function body: its single expression or the value of the `return` it reaches.
3883    /// Declarations before it are bound in the innermost frame.
3884    fn returned(&self, statements: &'p [Statement<'a>], depth: u32) -> Value {
3885        if let [Statement::ExpressionStatement(s)] = statements {
3886            return self.expr(&s.expression, depth);
3887        }
3888        self.run(statements, depth).unwrap_or(Value::Unknown)
3889    }
3890
3891    /// Runs `statements` in the innermost frame: the value of the `return` they reach, or `None` when they finish
3892    /// without one. An `if` with a known test runs the branch it takes; with an unknown test, a branch that is a single
3893    /// `return x` makes the value what `x` and the other path's value have in common, and any other `return` that may
3894    /// or may not be reached (in a loop, a `switch`, a `try`, a larger branch) makes the value unknown, never only the
3895    /// last `return`. A statement that may or may not run (a branch of an unknown test, a loop) leaves every local it
3896    /// assigns or changes unknown, so the value after it is never the one from before it.
3897    fn run(&self, statements: &'p [Statement<'a>], depth: u32) -> Option<Value> {
3898        for (index, statement) in statements.iter().enumerate() {
3899            match statement {
3900                Statement::ReturnStatement(r) => {
3901                    // What the evaluator reads expressions for, it does not run for what they change.
3902                    if let Some(argument) = &r.argument {
3903                        self.havoc(Writes::of_expression(argument, self.unstable));
3904                    }
3905                    return Some(r.argument.as_ref().map_or(Value::Unknown, |e| self.expr(e, depth)));
3906                }
3907                Statement::ExpressionStatement(s) => self.mutation(&s.expression, depth),
3908                Statement::BlockStatement(b) => {
3909                    if let Some(value) = self.block(&b.body, depth) {
3910                        return Some(value);
3911                    }
3912                }
3913                Statement::IfStatement(i) => {
3914                    self.havoc(Writes::of_expression(&i.test, self.unstable));
3915                    let taken = match self.truthy(&i.test, depth) {
3916                        Some(true) => Some(&i.consequent),
3917                        Some(false) => i.alternate.as_ref(),
3918                        None => {
3919                            if !returns(statement) {
3920                                self.either_branch(i, depth);
3921                                continue;
3922                            }
3923                            let rest = &statements[index + 1..];
3924                            return Some(self.either_return(i, rest, depth));
3925                        }
3926                    };
3927                    if let Some(value) = taken.and_then(|branch| self.block(std::slice::from_ref(branch), depth)) {
3928                        return Some(value);
3929                    }
3930                }
3931                other if returns(other) => return Some(Value::Unknown),
3932                other => match other.as_declaration() {
3933                    Some(Declaration::VariableDeclaration(v)) => {
3934                        self.havoc(Writes::of_statement(other, self.unstable));
3935                        self.declare(v, depth);
3936                    }
3937                    Some(Declaration::FunctionDeclaration(f)) => {
3938                        if let Some(id) = &f.id {
3939                            self.bind_to(id.name.as_str(), Binding::Callable(Callable::Function(f)));
3940                        }
3941                    }
3942                    Some(_) => {}
3943                    None => self.havoc(Writes::of_statement(other, self.unstable)),
3944                },
3945            }
3946        }
3947        None
3948    }
3949
3950    /// Runs the statements of a block in a frame of their own, so a `const` or `let` declared in it does not replace a
3951    /// binding outside it. A `var` it declares is function-scoped, and is left unknown outside the block.
3952    fn block(&self, statements: &'p [Statement<'a>], depth: u32) -> Option<Value> {
3953        self.frames.borrow_mut().push(Frame::default());
3954        let value = self.run(statements, depth);
3955        self.frames.borrow_mut().pop();
3956        let mut vars = Vars::default();
3957        for statement in statements {
3958            vars.visit_statement(statement);
3959        }
3960        for name in vars.0 {
3961            self.assign(&name, Binding::Value(Value::Unknown));
3962        }
3963        value
3964    }
3965
3966    /// Runs an `if` with an unknown test whose branches do not return: each branch from the same bindings, after which
3967    /// a binding is what both branches leave it with in common.
3968    fn either_branch(&self, statement: &'p IfStatement<'a>, depth: u32) {
3969        let before = self.frames.borrow().clone();
3970        let captured = self.captured.borrow().clone();
3971        self.block(std::slice::from_ref(&statement.consequent), depth);
3972        let consequent = std::mem::replace(&mut *self.frames.borrow_mut(), before);
3973        let consequent_captured = std::mem::replace(&mut *self.captured.borrow_mut(), captured);
3974        if let Some(alternate) = &statement.alternate {
3975            self.block(std::slice::from_ref(alternate), depth);
3976        }
3977        let mut frames = self.frames.borrow_mut();
3978        for (frame, other) in frames.iter_mut().zip(consequent) {
3979            // Each name once: joining a binding with a branch again is not the same as joining it once.
3980            let mut names: Vec<String> = frame.keys().chain(other.keys()).cloned().collect();
3981            names.sort_unstable();
3982            names.dedup();
3983            for name in names {
3984                let joined = match (frame.get(&name), other.get(&name)) {
3985                    (Some(Binding::Value(a)), Some(Binding::Value(b))) => Binding::Value(join(a, b)),
3986                    (Some(Binding::Alias(a)), Some(Binding::Alias(b))) if a.same_part(b) => Binding::Alias(Alias {
3987                        value: join(&a.value, &b.value),
3988                        ..a.clone()
3989                    }),
3990                    (Some(Binding::Callable(a)), Some(Binding::Callable(b))) if a.same(*b) => Binding::Callable(*a),
3991                    _ => Binding::Value(Value::Unknown),
3992                };
3993                frame.insert(name, joined);
3994            }
3995        }
3996        let mut captured = self.captured.borrow_mut();
3997        if *captured != consequent_captured {
3998            *captured = Some(match (&*captured, &consequent_captured) {
3999                (Some(a), Some(b)) => either(a, b),
4000                _ => Value::Unknown,
4001            });
4002        }
4003    }
4004
4005    /// The value of an `if` with an unknown test followed by `rest`, where a branch returns: what a branch's single
4006    /// `return x` and the other path's value have in common, or unknown when a branch does more. A branch that goes on
4007    /// to `rest` leaves what it assigns unknown there.
4008    fn either_return(&self, statement: &'p IfStatement<'a>, rest: &'p [Statement<'a>], depth: u32) -> Value {
4009        let path = |branch: Option<&'p Statement<'a>>| -> Option<Value> {
4010            match branch.map(single_return) {
4011                // A branch that returns its value.
4012                Some(Some(r)) => Some(r.argument.as_ref().map_or(Value::Unknown, |e| self.expr(e, depth))),
4013                // A branch that does more than return, and may return.
4014                Some(None) if branch.is_some_and(returns) => Some(Value::Unknown),
4015                // No branch, or one that does not return: control goes on to the statements after the `if`.
4016                _ => None,
4017            }
4018        };
4019        // Both returns are evaluated before `rest` runs, which may change the locals they read.
4020        let consequent = path(Some(&statement.consequent));
4021        let alternate = path(statement.alternate.as_ref());
4022        let after = || {
4023            for branch in [Some(&statement.consequent), statement.alternate.as_ref()]
4024                .into_iter()
4025                .flatten()
4026            {
4027                if !returns(branch) {
4028                    self.havoc(Writes::of_statement(branch, self.unstable));
4029                }
4030            }
4031            self.run(rest, depth).unwrap_or(Value::Unknown)
4032        };
4033        let (a, b) = match (consequent, alternate) {
4034            (Some(a), Some(b)) => (a, b),
4035            (Some(a), None) => (a, after()),
4036            (None, Some(b)) => (after(), b),
4037            (None, None) => return Value::Unknown,
4038        };
4039        either(&a, &b)
4040    }
4041
4042    /// Binds the names a `const`/`let`/`var` declaration declares in the innermost frame: a name to the value, as
4043    /// definite as it is, and a destructured name to the part of the value it takes, or to [`ConfigValue::Unknown`].
4044    /// A name that is an alias the evaluator follows is bound as one ([`Binding::Alias`]).
4045    fn declare(&self, declaration: &'p VariableDeclaration<'a>, depth: u32) {
4046        for declarator in &declaration.declarations {
4047            if let BindingPattern::BindingIdentifier(id) = &declarator.id
4048                && let Some(callable) = declarator.init.as_ref().and_then(Callable::of_expression)
4049            {
4050                self.bind_to(id.name.as_str(), Binding::Callable(callable));
4051                continue;
4052            }
4053            let value = declarator
4054                .init
4055                .as_ref()
4056                .map_or(Value::Unknown, |init| self.expr(init, depth));
4057            let value = if node_count(&value) > MAX_CONST_NODES {
4058                Value::Unknown
4059            } else {
4060                value
4061            };
4062            let aliases = self.aliases(declaration.kind, declarator);
4063            self.bind_pattern(&declarator.id, value, depth);
4064            for (name, alias) in aliases {
4065                self.bind_alias(&name, alias);
4066            }
4067        }
4068    }
4069
4070    /// The aliases a declaration makes that the evaluator follows ([`Unstable::follows`]), by name, before they are
4071    /// bound: each takes the part of the local the declaration reads ([`alias_source`]) at the key it destructures.
4072    fn aliases(&self, kind: VariableDeclarationKind, declarator: &'p VariableDeclarator<'a>) -> Vec<(String, Alias)> {
4073        if kind != VariableDeclarationKind::Const {
4074            return Vec::new();
4075        }
4076        let Some((root, keys)) = declarator.init.as_ref().and_then(|init| alias_source(init)) else {
4077            return Vec::new();
4078        };
4079        let root = root.name.as_str();
4080        let Some(frame) = self.frames.borrow().iter().rposition(|frame| frame.contains_key(root)) else {
4081            return Vec::new();
4082        };
4083        alias_names(&declarator.id)
4084            .unwrap_or_default()
4085            .into_iter()
4086            .filter(|(id, _)| self.unstable.follows(id))
4087            .map(|(id, key)| {
4088                let mut keys = keys.clone();
4089                keys.extend(key);
4090                let alias = Alias {
4091                    value: Value::Unknown,
4092                    root: root.to_owned(),
4093                    frame,
4094                    keys,
4095                };
4096                (id.name.as_str().to_owned(), alias)
4097            })
4098            .collect()
4099    }
4100
4101    /// Makes the binding `name` of the innermost frame `alias`, holding the value it is bound to.
4102    fn bind_alias(&self, name: &str, mut alias: Alias) {
4103        let mut frames = self.frames.borrow_mut();
4104        if let Some(binding) = frames.last_mut().and_then(|frame| frame.get_mut(name))
4105            && let Binding::Value(value) = &mut *binding
4106        {
4107            alias.value = std::mem::replace(value, Value::Unknown);
4108            *binding = Binding::Alias(alias);
4109        }
4110    }
4111
4112    /// Leaves every local `writes` assigns unknown, and every part of a local object or array it changes partly
4113    /// unknown.
4114    fn havoc(&self, writes: Writes<'_>) {
4115        for name in writes.assigned {
4116            self.assign(&name, Binding::Value(Value::Unknown));
4117        }
4118        let mut changed = writes.changed;
4119        changed.sort_unstable();
4120        changed.dedup();
4121        for (name, write) in changed {
4122            self.edit(&name, &write.keys, &write.edit());
4123        }
4124    }
4125
4126    /// Applies what an expression statement does to a local object: `name = x`, `name.a.key = x`, `name.list.push(x)`
4127    /// and `name.list.unshift(x)`; anything else that reaches into the object (`name.list.splice(0, 1)`,
4128    /// `name.list[i] = x`) leaves the part it reaches partly unknown. Any other expression leaves what it may write
4129    /// unknown.
4130    fn mutation(&self, expr: &'p Expression<'a>, depth: u32) {
4131        match expr.get_inner_expression() {
4132            Expression::AwaitExpression(a) => self.mutation(&a.argument, depth),
4133            Expression::AssignmentExpression(a) => {
4134                self.havoc(Writes::of_expression(&a.right, self.unstable));
4135                let assign = a.operator == AssignmentOperator::Assign;
4136                match &a.left {
4137                    AssignmentTarget::AssignmentTargetIdentifier(id) => {
4138                        let value = if assign {
4139                            self.expr(&a.right, depth)
4140                        } else {
4141                            Value::Unknown
4142                        };
4143                        self.assign(id.name.as_str(), Binding::Value(value));
4144                    }
4145                    AssignmentTarget::StaticMemberExpression(m) => match reach(&m.object) {
4146                        Some((root, keys, true)) => {
4147                            let value = if assign {
4148                                self.expr(&a.right, depth)
4149                            } else {
4150                                Value::Unknown
4151                            };
4152                            let edit = Edit::Set(m.property.name.as_str().to_owned(), value);
4153                            self.edit(root.name.as_str(), &keys, &edit);
4154                        }
4155                        Some((root, keys, false)) => self.edit(root.name.as_str(), &keys, &Edit::Taint),
4156                        // An object that is not a name or a member chain (`list.find(…).loader = …`) may be any part
4157                        // of a local it may hold.
4158                        None => {
4159                            let mut writes = Writes::new(self.unstable);
4160                            writes.visit_assignment_target(&a.left);
4161                            self.havoc(writes.through_aliases());
4162                        }
4163                    },
4164                    _ => self.havoc(Writes::of_expression(expr, self.unstable)),
4165                }
4166            }
4167            Expression::CallExpression(c) => {
4168                if !self.capture_set(c, depth) && !self.insert(c, depth) {
4169                    self.havoc(Writes::of_effect(expr, self.unstable));
4170                }
4171            }
4172            // `cond && (x = 1)`, `x++`, `a = b = 1`: what it writes may or may not be written.
4173            _ => self.havoc(Writes::of_expression(expr, self.unstable)),
4174        }
4175    }
4176
4177    /// Applies `name.list.push(…)` or `name.list.unshift(…)` of a local list, given no spread, to the list; returns
4178    /// whether `call` is such a call. What is known to be a list only adds what it is given; anything else may have a
4179    /// method of that name that changes it.
4180    fn insert(&self, call: &'p CallExpression<'a>, depth: u32) -> bool {
4181        let Expression::StaticMemberExpression(member) = call.callee.get_inner_expression() else {
4182            return false;
4183        };
4184        let front = match member.property.name.as_str() {
4185            "push" => false,
4186            "unshift" => true,
4187            _ => return false,
4188        };
4189        let Some((root, keys, true)) = reach(&member.object) else {
4190            return false;
4191        };
4192        let Some(arguments) = call
4193            .arguments
4194            .iter()
4195            .map(Argument::as_expression)
4196            .collect::<Option<Vec<_>>>()
4197        else {
4198            return false;
4199        };
4200        if !self.is_list(root.name.as_str(), &keys) {
4201            return false;
4202        }
4203        for argument in &arguments {
4204            self.havoc(Writes::of_expression(argument, self.unstable));
4205        }
4206        let items = arguments
4207            .into_iter()
4208            .map(|argument| self.expr(argument, depth))
4209            .collect();
4210        self.edit(root.name.as_str(), &keys, &Edit::Insert { front, items });
4211        true
4212    }
4213
4214    /// Whether the innermost local `name` holds a list at `keys`, in every value it may be.
4215    fn is_list(&self, name: &str, keys: &[String]) -> bool {
4216        let frames = self.frames.borrow();
4217        let Some(binding) = frames.iter().rev().find_map(|frame| frame.get(name)) else {
4218            return false;
4219        };
4220        match binding {
4221            Binding::Value(value) | Binding::Alias(Alias { value, .. }) => {
4222                value_at(value.definite(), keys).is_some_and(holds_list)
4223            }
4224            Binding::Callable(_) => false,
4225        }
4226    }
4227
4228    /// Applies `edit` at `keys` below the innermost local `name`, and through it when it is an alias
4229    /// ([`edit_binding`]).
4230    fn edit(&self, name: &str, keys: &[String], edit: &Edit) {
4231        let mut frames = self.frames.borrow_mut();
4232        if let Some(index) = frames.iter().rposition(|frame| frame.contains_key(name)) {
4233            edit_binding(&mut frames, index, name, keys, edit);
4234        }
4235    }
4236
4237    /// An array literal: a spread adds the elements [`Self::spread_elements`] finds. A spread of an array that is not
4238    /// definite leaves the array not definite, as its length and elements are not.
4239    fn array(&self, elements: &'p [ArrayExpressionElement<'a>], depth: u32) -> Value {
4240        let mut out = Vec::new();
4241        let mut definite = true;
4242        for element in elements {
4243            match element {
4244                ArrayExpressionElement::SpreadElement(s) => {
4245                    let (items, spread_definite) = self.spread_elements(&s.argument, depth);
4246                    out.extend(items);
4247                    definite &= spread_definite;
4248                }
4249                other => out.push(other.as_expression().map_or(Value::Unknown, |e| self.expr(e, depth))),
4250            }
4251        }
4252        Value::doubtful(Value::Array(out), definite)
4253    }
4254
4255    /// The elements spreading `argument` into an array adds, and whether they are definite: those of a known array;
4256    /// for a value with several possibilities ([`Self::possibilities`]: `...(ci ? [a] : [])`, `...(ci && [a])`, a
4257    /// value that is one of several arrays or not definite), the elements of each, a possibility of `null`,
4258    /// `undefined` or a boolean adding none, which are then not definite; one unknown element for anything else.
4259    fn spread_elements(&self, argument: &'p Expression<'a>, depth: u32) -> (Vec<Value>, bool) {
4260        let unknown = || (vec![Value::Unknown], true);
4261        let mut branches = self.possibilities(argument, depth);
4262        if branches.len() == 1 {
4263            return match branches.pop() {
4264                Some(Value::Array(items)) => (items, true),
4265                _ => unknown(),
4266            };
4267        }
4268        let mut items: Vec<Value> = Vec::new();
4269        for branch in branches {
4270            match branch {
4271                Value::Array(listed) => {
4272                    for item in listed {
4273                        if !items.contains(&item) {
4274                            items.push(item);
4275                        }
4276                    }
4277                }
4278                Value::Null | Value::Undefined | Value::Bool(_) => {}
4279                _ => return unknown(),
4280            }
4281        }
4282        (items, false)
4283    }
4284
4285    fn object(&self, properties: &'p [ObjectPropertyKind<'a>], depth: u32) -> Value {
4286        let mut out: Vec<(String, Value)> = Vec::new();
4287        for property in properties {
4288            match property {
4289                ObjectPropertyKind::SpreadProperty(s) => self.spread(&s.argument, &mut out, depth),
4290                ObjectPropertyKind::ObjectProperty(p) => {
4291                    let Some(key) = p.key.static_name() else {
4292                        out.push((UNKNOWN_SPREAD.to_owned(), Value::Unknown));
4293                        continue;
4294                    };
4295                    let value = if p.method || p.kind != oxc_ast::ast::PropertyKind::Init {
4296                        Value::Unknown
4297                    } else {
4298                        self.expr(&p.value, depth)
4299                    };
4300                    out.push((crate::js_string(key.as_js_str()), value));
4301                }
4302            }
4303        }
4304        Value::Object(out)
4305    }
4306}
4307
4308/// Applies `edit` at `keys` below the binding `name` of the frame at `index`. Through an alias it applies to the part
4309/// of the local the alias takes as well, when that part still holds what the alias held, and leaves the part partly
4310/// unknown when it does not (the local was given another object there).
4311fn edit_binding(frames: &mut [Frame<'_, '_>], index: usize, name: &str, keys: &[String], edit: &Edit) {
4312    let Some(binding) = frames.get_mut(index).and_then(|frame| frame.get_mut(name)) else {
4313        return;
4314    };
4315    let (root, frame, part, before) = match binding {
4316        Binding::Value(value) => return edit_at(value, keys, edit),
4317        Binding::Callable(_) => return,
4318        Binding::Alias(alias) => {
4319            let before = alias.value.clone();
4320            edit_at(&mut alias.value, keys, edit);
4321            (alias.root.clone(), alias.frame, alias.keys.clone(), before)
4322        }
4323    };
4324    let held = frames
4325        .get(frame)
4326        .and_then(|bindings| bindings.get(&root))
4327        .and_then(|binding| match binding {
4328            Binding::Value(value) | Binding::Alias(Alias { value, .. }) => value_at(value, &part),
4329            Binding::Callable(_) => None,
4330        });
4331    if held == Some(&before) {
4332        let mut path = part;
4333        path.extend_from_slice(keys);
4334        edit_binding(frames, frame, &root, &path, edit);
4335    } else {
4336        edit_binding(frames, frame, &root, &part, &Edit::Taint);
4337    }
4338}
4339
4340/// Applies `edit` to the object at `keys` below `value`, in each value it may be ([`Value::OneOf`]). A key on the way
4341/// that an unknown spread may set, or that the object may inherit ([`inherits`], such as `__proto__` and
4342/// `constructor`), leaves the object above it partly unknown instead. One the object neither has nor inherits leaves
4343/// it as it is: the key is `undefined`, so reaching below it throws and changes nothing.
4344fn edit_at(value: &mut Value, keys: &[String], edit: &Edit) {
4345    let target = value.definite_mut();
4346    if let Value::OneOf(alternatives) = &mut *target {
4347        for alternative in alternatives {
4348            edit_at(alternative, keys, edit);
4349        }
4350        return;
4351    }
4352    let Some((key, rest)) = keys.split_first() else {
4353        return match edit {
4354            Edit::Set(key, field) => set_entry(target, key, field.clone()),
4355            Edit::Insert { front, items } => insert_items(target, *front, items),
4356            Edit::Taint => taint_value(target),
4357        };
4358    };
4359    match target {
4360        Value::Object(entries) => {
4361            let found = entries
4362                .iter()
4363                .rposition(|(name, _)| name == key || ConfigValue::is_spread_key(name));
4364            match found {
4365                Some(at) if entries[at].0 == *key => edit_at(&mut entries[at].1, rest, edit),
4366                // An unknown spread may have set the key.
4367                Some(_) => open_entries(entries),
4368                None if inherits(entries, key) => open_entries(entries),
4369                None => {}
4370            }
4371        }
4372        other => taint_value(other),
4373    }
4374}
4375
4376/// Sets `key` of an object to `field`; on an array (`list.length = 0`) the elements are no longer known.
4377fn set_entry(value: &mut Value, key: &str, field: Value) {
4378    match value.definite_mut() {
4379        Value::Object(entries) => match entries.iter_mut().find(|(existing, _)| existing == key) {
4380            Some((_, slot)) => *slot = field,
4381            None => entries.push((key.to_owned(), field)),
4382        },
4383        Value::Array(items) => items.push(Value::Unknown),
4384        _ => {}
4385    }
4386}
4387
4388/// Whether a value is a list, in every value it may be.
4389fn holds_list(value: &Value) -> bool {
4390    match value.definite() {
4391        Value::Array(_) => true,
4392        Value::OneOf(alternatives) => alternatives.iter().all(holds_list),
4393        _ => false,
4394    }
4395}
4396
4397/// Adds `items` to a list, at its start with `front`; anything else is left partly unknown.
4398fn insert_items(value: &mut Value, front: bool, items: &[Value]) {
4399    match value {
4400        Value::Array(list) if front => {
4401            list.splice(0..0, items.iter().cloned());
4402        }
4403        Value::Array(list) => list.extend(items.iter().cloned()),
4404        other => taint_value(other),
4405    }
4406}
4407
4408/// Leaves an object or array partly unknown: an object may have any other key, and an array any other element and
4409/// any change below each of its elements, as a change of what it holds (`rules[0].loader = …`) does not say which
4410/// element it reaches.
4411fn taint_value(value: &mut Value) {
4412    match value.definite_mut() {
4413        Value::Object(entries) => open_entries(entries),
4414        Value::Array(items) => {
4415            items.iter_mut().for_each(open_below);
4416            if !matches!(items.last(), Some(Value::Unknown)) {
4417                items.push(Value::Unknown);
4418            }
4419        }
4420        _ => {}
4421    }
4422}
4423
4424/// Leaves every object and array in a value open, at every depth, in each value it may be.
4425fn open_below(value: &mut Value) {
4426    match value {
4427        Value::Object(entries) => {
4428            for (_, field) in entries.iter_mut() {
4429                open_below(field);
4430            }
4431            open_entries(entries);
4432        }
4433        Value::Array(items) => {
4434            items.iter_mut().for_each(open_below);
4435            if !matches!(items.last(), Some(Value::Unknown)) {
4436                items.push(Value::Unknown);
4437            }
4438        }
4439        Value::OneOf(alternatives) => alternatives.iter_mut().for_each(open_below),
4440        Value::Maybe(inner) => open_below(inner),
4441        _ => {}
4442    }
4443}
4444
4445/// Leaves the entries of an object open to any other key, once: a marker that already ends them says as much.
4446fn open_entries(entries: &mut Vec<(String, Value)>) {
4447    if entries.last().is_none_or(|(key, _)| key != UNKNOWN_SPREAD) {
4448        entries.push((UNKNOWN_SPREAD.to_owned(), Value::Unknown));
4449    }
4450}
4451
4452/// The value at `keys` below `value`, as member reads find it; `None` where a key is missing or an unknown spread may
4453/// set it.
4454fn value_at<'v>(value: &'v Value, keys: &[String]) -> Option<&'v Value> {
4455    let Some((key, rest)) = keys.split_first() else {
4456        return Some(value);
4457    };
4458    let Value::Object(entries) = value else {
4459        return None;
4460    };
4461    entries
4462        .iter()
4463        .rev()
4464        .find(|(name, _)| name == key || ConfigValue::is_spread_key(name))
4465        .filter(|(name, _)| name == key)
4466        .and_then(|(_, child)| value_at(child, rest))
4467}
4468
4469/// The first argument of a call of a function named `define*` (`defineConfig`, `vitest.defineProject`), which
4470/// evaluates to that argument; `None` for any other call, among them a method of `Object` or `Reflect`
4471/// (`Object.defineProperty`), which changes that argument.
4472pub(crate) fn defined<'p, 'a>(call: &'p CallExpression<'a>) -> Option<&'p Expression<'a>> {
4473    let callee = match call.callee.get_inner_expression() {
4474        Expression::Identifier(id) => id.name.as_str(),
4475        Expression::StaticMemberExpression(m) if !on_reflection(&m.object) => m.property.name.as_str(),
4476        _ => return None,
4477    };
4478    let is_define = callee.starts_with("define") && callee.len() > "define".len();
4479    call.arguments
4480        .first()
4481        .and_then(Argument::as_expression)
4482        .filter(|_| is_define)
4483}
4484
4485/// Whether a method is called on `Object` or `Reflect`, whose `define…` methods change what they are given.
4486fn on_reflection(object: &Expression<'_>) -> bool {
4487    matches!(
4488        object.get_inner_expression(),
4489        Expression::Identifier(id) if matches!(id.name.as_str(), "Object" | "Reflect")
4490    )
4491}
4492
4493/// How a config combinator merges its arguments.
4494#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4495enum MergeStyle {
4496    /// Nested objects merge and arrays concatenate; later arguments win (`mergeConfig`, `deepmerge`, `merge`).
4497    Deep,
4498    /// Top-level keys of later arguments replace earlier ones (`Object.assign`).
4499    Shallow,
4500    /// Like [`MergeStyle::Deep`], but earlier arguments win (`defu`).
4501    FirstWins,
4502}
4503
4504/// The merge style of a call to a well-known config combinator, by callee. `merge` and `mergeWith` count only when
4505/// the file does not import them from another package, and as members only of `lodash` or a merge package.
4506fn merge_style(callee: &Expression<'_>, bindings: &ModuleBindings) -> Option<MergeStyle> {
4507    match callee.get_inner_expression() {
4508        Expression::Identifier(id) => match id.name.as_str() {
4509            name if bindings.foreign_merges.contains(name) => None,
4510            "mergeConfig" | "merge" | "deepmerge" | "deepMerge" | "mergeDeep" | "mergeWith" => Some(MergeStyle::Deep),
4511            "defu" => Some(MergeStyle::FirstWins),
4512            _ => None,
4513        },
4514        Expression::StaticMemberExpression(member) => {
4515            let object_is = |name: &str| matches!(member.object.get_inner_expression(), Expression::Identifier(o) if o.name.as_str() == name);
4516            let merge_object = matches!(
4517                member.object.get_inner_expression(),
4518                Expression::Identifier(o)
4519                    if matches!(o.name.as_str(), "_" | "lodash") || bindings.merge_names.contains(o.name.as_str())
4520            );
4521            match member.property.name.as_str() {
4522                "assign" if object_is("Object") => Some(MergeStyle::Shallow),
4523                "merge" | "mergeWith" if merge_object => Some(MergeStyle::Deep),
4524                "mergeConfig" | "deepmerge" | "mergeDeep" => Some(MergeStyle::Deep),
4525                _ => None,
4526            }
4527        }
4528        _ => None,
4529    }
4530}
4531
4532/// Whether a merge called with `count` arguments is given options that may say how it merges: a third argument of
4533/// `deepmerge`, and any argument of `mergeWith`, whose last one may be a function that merges each key.
4534fn takes_options(callee: &Expression<'_>, count: usize) -> bool {
4535    let name = match callee.get_inner_expression() {
4536        Expression::Identifier(id) => id.name.as_str(),
4537        Expression::StaticMemberExpression(member) => member.property.name.as_str(),
4538        _ => return false,
4539    };
4540    match name {
4541        "deepmerge" | "deepMerge" => count > 2,
4542        "mergeWith" => true,
4543        _ => false,
4544    }
4545}
4546
4547/// Merges `incoming` into `base`; with `deep`, an object meeting an object merges and an array meeting an array
4548/// concatenates, otherwise the incoming value replaces the earlier one in place ([`merged_value`]).
4549fn merge_objects(base: &mut Vec<(String, Value)>, incoming: Vec<(String, Value)>, deep: bool) {
4550    for (key, value) in incoming {
4551        let Some(index) = base.iter().position(|(k, _)| *k == key) else {
4552            base.push((key, value));
4553            continue;
4554        };
4555        if ConfigValue::is_spread_key(&key) {
4556            continue;
4557        }
4558        let slot = &mut base[index].1;
4559        *slot = merged_value(std::mem::replace(slot, Value::Unknown), value, deep);
4560    }
4561}
4562
4563/// What a key holds after a merge ([`merge_objects`]) meets its `current` value with an `incoming` one: with `deep`,
4564/// the merge of two objects or two lists, or of one and an unknown value, definite only when both sides are;
4565/// otherwise the incoming value, or when it is not definite and so may be absent, the union of both ([`any_of`]),
4566/// not definite either.
4567fn merged_value(current: Value, incoming: Value, deep: bool) -> Value {
4568    let (current, current_definite) = current.split();
4569    let (incoming, incoming_definite) = incoming.split();
4570    let merged = match (deep, current, incoming) {
4571        (true, Value::Object(mut left), Value::Object(right)) => {
4572            merge_objects(&mut left, right, true);
4573            Value::Object(left)
4574        }
4575        (true, Value::Array(mut left), Value::Array(right)) => {
4576            left.extend(right);
4577            Value::Array(left)
4578        }
4579        // A deep merge keeps what the known side has and whatever the unknown side may add to it.
4580        (true, Value::Object(mut known), Value::Unknown | Value::OneOf(_))
4581        | (true, Value::Unknown | Value::OneOf(_), Value::Object(mut known)) => {
4582            if !known.iter().any(|(k, _)| ConfigValue::is_spread_key(k)) {
4583                known.push((UNKNOWN_SPREAD.to_owned(), Value::Unknown));
4584            }
4585            Value::Object(known)
4586        }
4587        (true, Value::Array(mut known), Value::Unknown | Value::OneOf(_))
4588        | (true, Value::Unknown | Value::OneOf(_), Value::Array(mut known)) => {
4589            known.push(Value::Unknown);
4590            Value::Array(known)
4591        }
4592        (_, current, incoming) if !incoming_definite => {
4593            return Value::doubtful(any_of(&[Some(current), Some(incoming)]), false);
4594        }
4595        (_, _, incoming) => return incoming,
4596    };
4597    Value::doubtful(merged, current_definite && incoming_definite)
4598}
4599
4600/// A statement that is only a `return` (or a block holding just one).
4601fn single_return<'p, 'a>(statement: &'p Statement<'a>) -> Option<&'p ReturnStatement<'a>> {
4602    match statement {
4603        Statement::ReturnStatement(r) => Some(r),
4604        Statement::BlockStatement(b) => match b.body.as_slice() {
4605            [only] => single_return(only),
4606            _ => None,
4607        },
4608        _ => None,
4609    }
4610}
4611
4612/// Whether a statement may `return` from the function it is in (a `return` inside a nested function does not count).
4613fn returns(statement: &Statement<'_>) -> bool {
4614    #[derive(Default)]
4615    struct Finder(bool);
4616    impl<'a> Visit<'a> for Finder {
4617        fn visit_return_statement(&mut self, _: &ReturnStatement<'a>) {
4618            self.0 = true;
4619        }
4620        fn visit_function(&mut self, _: &Function<'a>, _: oxc_semantic::ScopeFlags) {}
4621        fn visit_arrow_function_expression(&mut self, _: &ArrowFunctionExpression<'a>) {}
4622        fn visit_class(&mut self, _: &oxc_ast::ast::Class<'a>) {}
4623    }
4624    let mut finder = Finder::default();
4625    finder.visit_statement(statement);
4626    finder.0
4627}
4628
4629/// The locals a statement or expression may write, outside the functions it declares: the names it assigns
4630/// (`x = …`, `x++`, `[x] = …`) and the parts of objects it may change (`x.a = …`, `x.list.push(…)`,
4631/// `Object.assign(x.part, …)`), among them the whole local an alias declared there takes a part of when the alias is
4632/// changed.
4633struct Writes<'u> {
4634    /// The bindings code the evaluator does not follow may change, which also say what a call is ([`Unstable`]).
4635    unstable: &'u Unstable,
4636    assigned: Vec<String>,
4637    changed: Vec<(String, Write)>,
4638    /// The aliases declared ([`alias_source`]), each with the local it takes a part of.
4639    aliases: Vec<(String, String)>,
4640}
4641
4642impl Write {
4643    /// The edit that leaves what the change may have changed unknown.
4644    fn edit(&self) -> Edit {
4645        self.key
4646            .clone()
4647            .map_or(Edit::Taint, |key| Edit::Set(key, Value::Unknown))
4648    }
4649}
4650
4651impl<'u> Writes<'u> {
4652    fn new(unstable: &'u Unstable) -> Self {
4653        Self {
4654            unstable,
4655            assigned: Vec::new(),
4656            changed: Vec::new(),
4657            aliases: Vec::new(),
4658        }
4659    }
4660
4661    fn of_statement(statement: &Statement<'_>, unstable: &'u Unstable) -> Self {
4662        let mut writes = Self::new(unstable);
4663        writes.visit_statement(statement);
4664        writes.through_aliases()
4665    }
4666
4667    fn of_expression(expression: &Expression<'_>, unstable: &'u Unstable) -> Self {
4668        let mut writes = Self::new(unstable);
4669        writes.visit_expression(expression);
4670        writes.through_aliases()
4671    }
4672
4673    /// The writes with the local each changed alias takes a part of changed too, in full, as the evaluator, which
4674    /// does not run the code, cannot apply them to it.
4675    fn through_aliases(mut self) -> Self {
4676        let mut index = 0;
4677        while let Some((name, _)) = self.changed.get(index).cloned() {
4678            for (alias, root) in &self.aliases {
4679                let whole = |(changed, write): &(String, Write)| changed == root && *write == Write::default();
4680                if *alias == name && !self.changed.iter().any(whole) {
4681                    self.changed.push((root.clone(), Write::default()));
4682                }
4683            }
4684            index += 1;
4685        }
4686        self
4687    }
4688
4689    /// What an expression statement writes itself, as [`Evaluator::mutation`] reads it: the target of an assignment
4690    /// and what its value may change, and what a call made as a statement may change ([`Self::arguments`]); anything
4691    /// else, in full.
4692    fn of_effect(expression: &Expression<'_>, unstable: &'u Unstable) -> Self {
4693        let mut writes = Self::new(unstable);
4694        match expression.get_inner_expression() {
4695            Expression::AssignmentExpression(a) => {
4696                match a.left.as_simple_assignment_target() {
4697                    Some(target) => writes.visit_simple_assignment_target(target),
4698                    None => writes.visit_assignment_target(&a.left),
4699                }
4700                writes.visit_expression(&a.right);
4701            }
4702            Expression::CallExpression(c) => {
4703                writes.visit_call_expression(c);
4704                writes.arguments(c.span, &c.arguments, true);
4705            }
4706            Expression::AwaitExpression(a) => return Self::of_effect(&a.argument, unstable),
4707            other => writes.visit_expression(other),
4708        }
4709        writes
4710    }
4711
4712    /// Records a change of what the object `expr` holds: the part of a local a name or a member chain is, or each part
4713    /// any other object may be ([`Unstable::parts_of`]).
4714    fn object(&mut self, expr: &Expression<'_>) {
4715        if let Some((root, keys, _)) = reach(expr) {
4716            return self.change(root.name.as_str(), Write { keys, key: None });
4717        }
4718        let unstable = self.unstable;
4719        for (name, keys, _) in unstable.parts_of(expr) {
4720            self.change(
4721                name,
4722                Write {
4723                    keys: keys.clone(),
4724                    key: None,
4725                },
4726            );
4727        }
4728    }
4729
4730    /// Records the change `write` of the local `name`, once.
4731    fn change(&mut self, name: &str, write: Write) {
4732        let change = (name.to_owned(), write);
4733        if !self.changed.contains(&change) {
4734            self.changed.push(change);
4735        }
4736    }
4737
4738    /// The objects the call at `span`, made as a statement or not (`statement`), is given, which it may change
4739    /// ([`Unstable::changes_arguments`]): each part of a local an argument gives it, a literal's included
4740    /// ([`given_parts`]), where the change may be read after the call ([`Unstable::seen_after`]).
4741    fn arguments(&mut self, span: Span, arguments: &[Argument<'_>], statement: bool) {
4742        if !self.unstable.changes_arguments(span, statement) {
4743            return;
4744        }
4745        for argument in arguments.iter().filter_map(given) {
4746            for (root, keys) in given_parts(argument) {
4747                if self.unstable.seen_after(root) {
4748                    self.change(root.name.as_str(), Write { keys, key: None });
4749                }
4750            }
4751        }
4752    }
4753}
4754
4755impl<'a> Visit<'a> for Writes<'_> {
4756    fn visit_variable_declarator(&mut self, it: &VariableDeclarator<'a>) {
4757        if let Some((root, _)) = it.init.as_ref().and_then(|init| alias_source(init)) {
4758            for (id, _) in alias_names(&it.id).unwrap_or_default() {
4759                self.aliases
4760                    .push((id.name.as_str().to_owned(), root.name.as_str().to_owned()));
4761            }
4762        }
4763        walk::walk_variable_declarator(self, it);
4764    }
4765
4766    fn visit_simple_assignment_target(&mut self, it: &SimpleAssignmentTarget<'a>) {
4767        match it {
4768            SimpleAssignmentTarget::AssignmentTargetIdentifier(id) => self.assigned.push(id.name.as_str().to_owned()),
4769            SimpleAssignmentTarget::StaticMemberExpression(m) => match reach(&m.object) {
4770                // `x.a.key = …` sets one key of the object at `x.a`.
4771                Some((root, keys, true)) => {
4772                    let key = Some(m.property.name.as_str().to_owned());
4773                    self.change(root.name.as_str(), Write { keys, key });
4774                }
4775                Some(_) => self.object(&m.object),
4776                // `(a ? x.a : x.b).key = …` sets the key of each part of a local the object is.
4777                None => {
4778                    let unstable = self.unstable;
4779                    for (name, keys, exact) in unstable.parts_of(&m.object) {
4780                        let key = exact.then(|| m.property.name.as_str().to_owned());
4781                        self.change(
4782                            name,
4783                            Write {
4784                                keys: keys.clone(),
4785                                key,
4786                            },
4787                        );
4788                    }
4789                }
4790            },
4791            other => {
4792                if let Some(member) = other.as_member_expression() {
4793                    self.object(member.object());
4794                }
4795            }
4796        }
4797        walk::walk_simple_assignment_target(self, it);
4798    }
4799
4800    fn visit_assignment_target_property_identifier(&mut self, it: &AssignmentTargetPropertyIdentifier<'a>) {
4801        self.assigned.push(it.binding.name.as_str().to_owned());
4802        walk::walk_assignment_target_property_identifier(self, it);
4803    }
4804
4805    fn visit_unary_expression(&mut self, it: &UnaryExpression<'a>) {
4806        if it.operator == UnaryOperator::Delete
4807            && let Some(member) = it.argument.get_inner_expression().as_member_expression()
4808        {
4809            self.object(member.object());
4810        }
4811        walk::walk_unary_expression(self, it);
4812    }
4813
4814    fn visit_call_expression(&mut self, it: &CallExpression<'a>) {
4815        if let Expression::Identifier(callee) = it.callee.get_inner_expression()
4816            && self.unstable.calls_value(it)
4817        {
4818            self.change(callee.name.as_str(), Write::default());
4819        }
4820        if let Some(target) = changed_by_call(it, self.unstable.is_pure(it), self.unstable.calls_back(it))
4821            && !self.unstable.is_namespace(target)
4822        {
4823            match (property_defined(it), reach(target)) {
4824                (Some(key), Some((root, keys, true))) => {
4825                    self.change(root.name.as_str(), Write { keys, key: Some(key) });
4826                }
4827                _ => self.object(target),
4828            }
4829        }
4830        self.arguments(it.span, &it.arguments, false);
4831        walk::walk_call_expression(self, it);
4832    }
4833
4834    fn visit_new_expression(&mut self, it: &NewExpression<'a>) {
4835        self.arguments(it.span, &it.arguments, false);
4836        walk::walk_new_expression(self, it);
4837    }
4838
4839    fn visit_expression_statement(&mut self, it: &ExpressionStatement<'a>) {
4840        if let Some(call) = statement_call(&it.expression) {
4841            self.arguments(call.span, &call.arguments, true);
4842        }
4843        walk::walk_expression_statement(self, it);
4844    }
4845
4846    fn visit_with_statement(&mut self, it: &oxc_ast::ast::WithStatement<'a>) {
4847        // A name the body writes may be a key of the object.
4848        self.object(&it.object);
4849        walk::walk_with_statement(self, it);
4850    }
4851
4852    fn visit_function(&mut self, _: &Function<'a>, _: oxc_semantic::ScopeFlags) {}
4853    fn visit_arrow_function_expression(&mut self, _: &ArrowFunctionExpression<'a>) {}
4854    fn visit_class(&mut self, _: &oxc_ast::ast::Class<'a>) {}
4855}
4856
4857/// The names `var` declarations bind in a statement, outside the functions it declares.
4858#[derive(Default)]
4859struct Vars(Vec<String>);
4860
4861impl<'a> Visit<'a> for Vars {
4862    fn visit_variable_declaration(&mut self, it: &VariableDeclaration<'a>) {
4863        if it.kind == VariableDeclarationKind::Var {
4864            for declarator in &it.declarations {
4865                self.0.extend(
4866                    declarator
4867                        .id
4868                        .get_binding_identifiers()
4869                        .iter()
4870                        .map(|id| id.name.as_str().to_owned()),
4871                );
4872            }
4873        }
4874        walk::walk_variable_declaration(self, it);
4875    }
4876
4877    fn visit_function(&mut self, _: &Function<'a>, _: oxc_semantic::ScopeFlags) {}
4878    fn visit_arrow_function_expression(&mut self, _: &ArrowFunctionExpression<'a>) {}
4879    fn visit_class(&mut self, _: &oxc_ast::ast::Class<'a>) {}
4880}
4881
4882/// A value that code the evaluator does not follow may have changed: an object may have any other key and an array
4883/// any other element, as [`Evaluator::edit`] leaves them partly unknown; anything else is unknown.
4884fn changed(value: Value) -> Value {
4885    match value {
4886        Value::Object(mut entries) => {
4887            open_entries(&mut entries);
4888            Value::Object(entries)
4889        }
4890        Value::Array(mut items) => {
4891            items.push(Value::Unknown);
4892            Value::Array(items)
4893        }
4894        Value::Maybe(value) => Value::Maybe(Box::new(changed(*value))),
4895        _ => Value::Unknown,
4896    }
4897}
4898
4899/// A value code the evaluator does not follow may have changed as `change` says. A change of contents leaves only the
4900/// parts it reaches partly unknown, as [`Evaluator::edit`] does: an object or array there may hold more, a key given a
4901/// new value is unknown, and a value that holds no object or array is as it is, in each alternative of a
4902/// [`Value::OneOf`] on its own, so a binding that only may share an object with a changed one keeps the values it may
4903/// be.
4904fn unstable(value: Value, change: &Change) -> Value {
4905    let Change::Contents(writes) = change else {
4906        return changed(value);
4907    };
4908    let mut value = value;
4909    for write in writes {
4910        edit_at(&mut value, &write.keys, &write.edit());
4911    }
4912    value
4913}
4914
4915/// Whether the left side of `left <operator> right` is the value of the expression; `None` when that is not known.
4916fn left_is_value(operator: LogicalOperator, left: &Value) -> Option<bool> {
4917    match operator {
4918        LogicalOperator::And => truthiness(left).map(|truthy| !truthy),
4919        LogicalOperator::Or => truthiness(left),
4920        LogicalOperator::Coalesce => match left {
4921            Value::Unknown | Value::OneOf(_) | Value::Maybe(_) => None,
4922            Value::Null | Value::Undefined => Some(false),
4923            _ => Some(true),
4924        },
4925    }
4926}
4927
4928/// Whether `expr` is a comparison, a negation or a `void`, whose value is `true`, `false` or `undefined` and so adds
4929/// nothing when spread.
4930fn is_primitive(expr: &Expression<'_>) -> bool {
4931    match expr.get_inner_expression() {
4932        Expression::BinaryExpression(binary) => {
4933            binary.operator.is_equality() || binary.operator.is_compare() || binary.operator.is_relational()
4934        }
4935        Expression::UnaryExpression(unary) => matches!(unary.operator, UnaryOperator::LogicalNot | UnaryOperator::Void),
4936        _ => false,
4937    }
4938}
4939
4940/// Whether `expr` is a `void`, whose value is `undefined`.
4941fn is_void(expr: &Expression<'_>) -> bool {
4942    matches!(expr.get_inner_expression(), Expression::UnaryExpression(unary) if unary.operator == UnaryOperator::Void)
4943}
4944
4945/// The entries spreading `value` adds to an object: those of an object, none for `null`, `undefined`, a boolean, a
4946/// number or an empty string, and `None` for anything else, whose entries are not known.
4947fn spread_entries(value: Value) -> Option<Vec<(String, Value)>> {
4948    match value {
4949        Value::Object(entries) => Some(entries),
4950        Value::Null | Value::Undefined | Value::Bool(_) | Value::Number(_) => Some(Vec::new()),
4951        Value::String(s) => s.is_empty().then(Vec::new),
4952        Value::Array(_) | Value::Regex { .. } | Value::Unknown | Value::OneOf(_) | Value::Maybe(_) => None,
4953    }
4954}
4955
4956/// The value of `key` in the entries of an object: the last entry for it, a [`Value::Maybe`] when that is not
4957/// definite, and unknown when an unknown spread follows it; `None` when the object has neither.
4958fn entry_value(entries: &[(String, Value)], key: &str) -> Option<Value> {
4959    entries
4960        .iter()
4961        .rev()
4962        .find(|(name, _)| name == key || ConfigValue::is_spread_key(name))
4963        .map(|(name, value)| if name == key { value.clone() } else { Value::Unknown })
4964}
4965
4966/// The `length` of a string or an array; unknown for an array with an unknown element, which may stand for a spread of
4967/// any length.
4968fn length_of(value: &Value) -> Value {
4969    let length = match value {
4970        Value::String(text) => Some(text.encode_utf16().count()),
4971        Value::Array(items) if !items.contains(&Value::Unknown) => Some(items.len()),
4972        _ => None,
4973    };
4974    length
4975        .and_then(|length| u32::try_from(length).ok())
4976        .map_or(Value::Unknown, |length| Value::Number(f64::from(length)))
4977}
4978
4979/// `value.key`, as a member read or a destructuring takes it: the `length` of a string or an array; the value of `key`
4980/// in a known object ([`entry_value`]), not definite when the key or the object is not; unknown for the `length` of a
4981/// value that is not definite, a key the object may inherit ([`inherits`]) and a member of anything else; and
4982/// [`Value::Undefined`] when the object has neither the key nor an unknown spread.
4983fn property_of(value: &Value, key: &str) -> Value {
4984    let definite = value.is_definite();
4985    match (value.definite(), key) {
4986        (_, "length") if !definite => Value::Unknown,
4987        (known @ (Value::String(_) | Value::Array(_)), "length") => length_of(known),
4988        (Value::Object(entries), _) => match entry_value(entries, key) {
4989            Some(field) => Value::doubtful(field, definite),
4990            None if inherits(entries, key) => Value::Unknown,
4991            None => Value::Undefined,
4992        },
4993        (Value::Array(items), _) if definite => {
4994            array_index(key).map_or(Value::Unknown, |index| element_at(items, index))
4995        }
4996        _ => Value::Unknown,
4997    }
4998}
4999
5000/// The array index a property key names: a canonical non-negative integer (`"0"`, `"12"`, not `"01"` or `"+1"`).
5001fn array_index(key: &str) -> Option<usize> {
5002    let index: u32 = key.parse().ok()?;
5003    (index.to_string() == key)
5004        .then(|| usize::try_from(index).ok())
5005        .flatten()
5006}
5007
5008/// The element at `index` of a known array: unknown at or after an unknown element, which may stand for a spread of any
5009/// length, and `undefined` past the end.
5010fn element_at(items: &[Value], index: usize) -> Value {
5011    if items
5012        .iter()
5013        .take(index.saturating_add(1))
5014        .any(|item| *item == Value::Unknown)
5015    {
5016        return Value::Unknown;
5017    }
5018    items.get(index).cloned().unwrap_or(Value::Undefined)
5019}
5020
5021/// The key and value of a `[key, value]` pair `Object.fromEntries` takes: a known array whose first element is a known
5022/// string, number or boolean ([`primitive_text`]), the value `undefined` when the pair has no second element. `None`
5023/// for anything else.
5024fn entry_pair(pair: Value) -> Option<(String, Value)> {
5025    let Value::Array(parts) = pair else {
5026        return None;
5027    };
5028    let mut parts = parts.into_iter();
5029    let key = primitive_text(&parts.next()?)?;
5030    Some((key, parts.next().unwrap_or(Value::Undefined)))
5031}
5032
5033/// The text JavaScript makes of a known string, number ([`number_text`]) or boolean in a template literal, a `+` with a
5034/// string or a property key; `None` for anything else, `null` and `undefined` included, which a path or name written
5035/// from them is unlikely to mean.
5036fn primitive_text(value: &Value) -> Option<String> {
5037    match value {
5038        Value::String(text) => Some(text.clone()),
5039        Value::Number(number) => number_text(*number),
5040        Value::Bool(b) => Some(b.to_string()),
5041        _ => None,
5042    }
5043}
5044
5045/// How JavaScript writes the number `number`, when it writes it without an exponent (zero, or a magnitude from
5046/// `1e-6` up to `1e21`), where Rust writes the same shortest digits; `None` for any other number, `NaN` and the
5047/// infinities included.
5048fn number_text(number: f64) -> Option<String> {
5049    if number == 0.0 {
5050        // `-0` is written `0` too.
5051        return Some("0".to_owned());
5052    }
5053    (1e-6..1e21).contains(&number.abs()).then(|| format!("{number}"))
5054}
5055
5056/// `left + right` of known values: the sum of two numbers, or the text of both ([`primitive_text`]) when one is a
5057/// string; unknown otherwise.
5058fn add(left: &Value, right: &Value) -> Value {
5059    match (left, right) {
5060        (Value::Number(a), Value::Number(b)) => Value::Number(a + b),
5061        (Value::String(_), _) | (_, Value::String(_)) => match (primitive_text(left), primitive_text(right)) {
5062            (Some(a), Some(b)) => Value::String(a + &b),
5063            _ => Value::Unknown,
5064        },
5065        _ => Value::Unknown,
5066    }
5067}
5068
5069/// Whether `left === right` for two known primitives; `None` when either is anything else, such as an object or an
5070/// array, which compare by identity, or a value that is not known or not definite.
5071fn strictly_equal(left: &Value, right: &Value) -> Option<bool> {
5072    let primitive = |value: &Value| {
5073        matches!(
5074            value,
5075            Value::Null | Value::Undefined | Value::Bool(_) | Value::Number(_) | Value::String(_)
5076        )
5077    };
5078    (primitive(left) && primitive(right)).then(|| left == right)
5079}
5080
5081/// Whether an object with `entries` may inherit `key` it does not have itself: a member of `Object.prototype`, or any
5082/// key when the object sets its prototype (`{ __proto__: base }`), which the evaluator does not follow.
5083fn inherits(entries: &[(String, Value)], key: &str) -> bool {
5084    OBJECT_PROTOTYPE_MEMBERS.contains(&key) || entries.iter().any(|(name, _)| name == "__proto__")
5085}
5086
5087/// The entries a spread of one of `branches` adds after `before`; see [`any_entries`].
5088fn spread_any(before: &[(String, Value)], branches: &[Vec<(String, Value)>]) -> Vec<(String, Value)> {
5089    let objects: Vec<Option<&[(String, Value)]>> = branches.iter().map(|branch| Some(branch.as_slice())).collect();
5090    any_entries(&objects, before)
5091}
5092
5093/// The entries of an object that is one of `objects`, `None` standing for a branch without the object: each key any
5094/// of them has, with [`any_of`] what each leaves it (its own value, or the one in `before` when it does not have the
5095/// key), a [`Value::Maybe`] unless every branch leaves it the same definite value, and the marker key `...` last when
5096/// one of them has an unknown spread.
5097fn any_entries(objects: &[Option<&[(String, Value)]>], before: &[(String, Value)]) -> Vec<(String, Value)> {
5098    let entries = || objects.iter().flatten().copied().flatten();
5099    let mut keys: Vec<&str> = Vec::new();
5100    for (key, _) in entries() {
5101        if !ConfigValue::is_spread_key(key) && !keys.contains(&key.as_str()) {
5102            keys.push(key);
5103        }
5104    }
5105    let mut out: Vec<(String, Value)> = Vec::new();
5106    for key in keys {
5107        let fields: Vec<Option<(Value, bool)>> = objects
5108            .iter()
5109            .map(|object| {
5110                object
5111                    .and_then(|object| entry_value(object, key))
5112                    .or_else(|| entry_value(before, key))
5113                    .map(Value::split)
5114            })
5115            .collect();
5116        let each_definite = fields
5117            .iter()
5118            .all(|field| field.as_ref().is_some_and(|(_, definite)| *definite));
5119        let values: Vec<Option<Value>> = fields.into_iter().map(|field| field.map(|(value, _)| value)).collect();
5120        let definite = each_definite && agree(&values);
5121        out.push((key.to_owned(), Value::doubtful(any_of(&values), definite)));
5122    }
5123    for marker in [UNKNOWN_SPREAD, OWN_SPREAD] {
5124        if entries().any(|(key, _)| key == marker) {
5125            out.push((marker.to_owned(), Value::Unknown));
5126        }
5127    }
5128    out
5129}
5130
5131/// The value of a key that holds one of `values`, `None` standing for a branch that leaves it out: the value itself
5132/// when every branch sets it alike; for lists, the elements of all of them, a string counting as a list of one, so
5133/// that every file or package a branch names is credited; for objects, each key any of them has, merged the same way;
5134/// for other values every branch sets, such as two different strings, one of them ([`one_of`]); anything else is
5135/// unknown, such as a string only some branches set.
5136fn any_of(values: &[Option<Value>]) -> Value {
5137    let present: Vec<&Value> = values.iter().flatten().collect();
5138    let Some(&first) = present.first() else {
5139        return Value::Unknown;
5140    };
5141    if agree(values) {
5142        return first.clone();
5143    }
5144    let lists: Option<Vec<&[Value]>> = present
5145        .iter()
5146        .map(|&value| match value {
5147            Value::Array(list) => Some(list.as_slice()),
5148            Value::String(_) => Some(std::slice::from_ref(value)),
5149            _ => None,
5150        })
5151        .collect();
5152    if let Some(lists) = lists
5153        && present.iter().any(|value| matches!(value, Value::Array(_)))
5154    {
5155        let mut items: Vec<Value> = Vec::new();
5156        for item in lists.into_iter().flatten() {
5157            if !items.contains(item) {
5158                items.push(item.clone());
5159            }
5160        }
5161        return Value::Array(items);
5162    }
5163    let objects: Option<Vec<Option<Entries<'_>>>> = values
5164        .iter()
5165        .map(|value| match value {
5166            None => Some(None),
5167            Some(Value::Object(entries)) => Some(Some(entries.as_slice())),
5168            Some(_) => None,
5169        })
5170        .collect();
5171    if let Some(objects) = objects {
5172        return Value::Object(any_entries(&objects, &[]));
5173    }
5174    if present.len() < values.len() {
5175        return Value::Unknown;
5176    }
5177    one_of(present.into_iter().cloned())
5178}
5179
5180/// The most values a [`Value::OneOf`] holds; a value that may be one of more is unknown.
5181const MAX_ALTERNATIVES: usize = 8;
5182
5183/// A value that is one of `values`, not knowing which: the value itself when they are all the same, otherwise a
5184/// [`Value::OneOf`] of the different ones in the order first seen, a `OneOf` among them flattened into its
5185/// alternatives. Unknown when one of them is unknown or not definite, or when there are more than
5186/// [`MAX_ALTERNATIVES`]; a caller that knows the value is not definite marks that itself.
5187fn one_of(values: impl IntoIterator<Item = Value>) -> Value {
5188    let mut out: Vec<Value> = Vec::new();
5189    for value in values {
5190        let nested = match value {
5191            Value::Unknown | Value::Maybe(_) => return Value::Unknown,
5192            Value::OneOf(nested) => nested,
5193            other => vec![other],
5194        };
5195        for alternative in nested {
5196            if !out.contains(&alternative) {
5197                out.push(alternative);
5198            }
5199        }
5200        if out.len() > MAX_ALTERNATIVES {
5201            return Value::Unknown;
5202        }
5203    }
5204    match out.len() {
5205        0 => Value::Unknown,
5206        1 => out.pop().unwrap_or(Value::Unknown),
5207        _ => Value::OneOf(out),
5208    }
5209}
5210
5211/// `apply` of every combination of the values `args` may be ([`alternatives`]), as [`one_of`] the results: a file
5212/// joined to a directory that is one of two is one of two paths. Unknown when a result is, or when there are more
5213/// combinations than [`MAX_ALTERNATIVES`].
5214fn each_combination(args: &[Value], apply: impl Fn(&[Value]) -> Value) -> Value {
5215    if !args.iter().any(|arg| matches!(arg, Value::OneOf(_))) {
5216        return apply(args);
5217    }
5218    let mut combinations: Vec<Vec<Value>> = vec![Vec::new()];
5219    for arg in args {
5220        let choices = alternatives(arg.clone());
5221        if combinations.len().saturating_mul(choices.len()) > MAX_ALTERNATIVES {
5222            return Value::Unknown;
5223        }
5224        let mut next = Vec::with_capacity(combinations.len() * choices.len());
5225        for combination in &combinations {
5226            for choice in &choices {
5227                let mut extended = combination.clone();
5228                extended.push(choice.clone());
5229                next.push(extended);
5230            }
5231        }
5232        combinations = next;
5233    }
5234    one_of(combinations.iter().map(|combination| apply(combination.as_slice())))
5235}
5236
5237/// The values `value` may be: the alternatives of a [`Value::OneOf`], or the value itself.
5238fn alternatives(value: Value) -> Vec<Value> {
5239    match value {
5240        Value::OneOf(alternatives) => alternatives,
5241        other => vec![other],
5242    }
5243}
5244
5245/// What `.filter(Boolean)` keeps of the element `value`: `None` when it is falsy; otherwise the values it may have that
5246/// are not falsy, and whether it may be dropped.
5247fn truthy_part(value: Value) -> Option<(Value, bool)> {
5248    match truthiness(&value) {
5249        Some(false) => None,
5250        Some(true) => Some((value, false)),
5251        None => match value {
5252            Value::OneOf(alternatives) => {
5253                let count = alternatives.len();
5254                let kept: Vec<Value> = alternatives
5255                    .into_iter()
5256                    .filter(|alternative| truthiness(alternative) != Some(false))
5257                    .collect();
5258                let may_drop = kept.len() < count || kept.iter().any(|alternative| truthiness(alternative).is_none());
5259                (!kept.is_empty()).then(|| (one_of(kept), may_drop))
5260            }
5261            other => Some((other, true)),
5262        },
5263    }
5264}
5265
5266/// Whether every one of `values` is present and they are all the same.
5267fn agree(values: &[Option<Value>]) -> bool {
5268    values
5269        .first()
5270        .is_some_and(|first| first.is_some() && values.iter().all(|value| value == first))
5271}
5272
5273/// Whether a value is truthy in JavaScript; `None` when it is unknown.
5274fn truthiness(value: &Value) -> Option<bool> {
5275    match value {
5276        Value::Null | Value::Undefined => Some(false),
5277        Value::Bool(b) => Some(*b),
5278        Value::Number(n) => Some(*n != 0.0 && !n.is_nan()),
5279        Value::String(s) => Some(!s.is_empty()),
5280        Value::Array(_) | Value::Object(_) | Value::Regex { .. } => Some(true),
5281        Value::Unknown | Value::OneOf(_) | Value::Maybe(_) => None,
5282    }
5283}
5284
5285/// The value of an expression that is `a` or `b`, not knowing which: the value itself when both are equal; for two
5286/// objects, an object with every key either has, whose value is what both have in common when both have the key and
5287/// [`ConfigValue::Unknown`] when only one does (it may be present or not); anything else is one of the two
5288/// ([`one_of`]), unknown when either is. No key appears that neither branch has, so other keys stay known, and a key
5289/// that may differ is one of its values, or unknown, wherever it is read. It is not definite ([`Value::Maybe`]) when
5290/// `a` or `b` is not.
5291fn either(a: &Value, b: &Value) -> Value {
5292    if a == b {
5293        return a.clone();
5294    }
5295    if !a.is_definite() || !b.is_definite() {
5296        return Value::doubtful(either(a.definite(), b.definite()), false);
5297    }
5298    let (Value::Object(left), Value::Object(right)) = (a, b) else {
5299        return one_of([a.clone(), b.clone()]);
5300    };
5301    let mut keys: Vec<&str> = Vec::new();
5302    for (key, _) in left.iter().chain(right) {
5303        if !ConfigValue::is_spread_key(key) && !keys.contains(&key.as_str()) {
5304            keys.push(key);
5305        }
5306    }
5307    let mut out: Vec<(String, Value)> = keys
5308        .into_iter()
5309        .map(|key| {
5310            let value = match (a.get(key), b.get(key)) {
5311                (Some(x), Some(y)) => either(x, y),
5312                (Some(only), None) | (None, Some(only)) => Value::doubtful(Value::Unknown, only.is_definite()),
5313                (None, None) => Value::Unknown,
5314            };
5315            (key.to_owned(), value)
5316        })
5317        .collect();
5318    for marker in [UNKNOWN_SPREAD, OWN_SPREAD] {
5319        if a.get(marker).is_some() || b.get(marker).is_some() {
5320            out.push((marker.to_owned(), Value::Unknown));
5321        }
5322    }
5323    Value::Object(out)
5324}
5325
5326/// What a binding holds after one of two branches left it `a` and the other `b`: as [`either`], except that a list
5327/// one branch appended to (`plugins.push(x)`) keeps the elements both have.
5328fn join(a: &Value, b: &Value) -> Value {
5329    if !a.is_definite() || !b.is_definite() {
5330        return Value::doubtful(join(a.definite(), b.definite()), false);
5331    }
5332    match (a, b) {
5333        (Value::Array(x), Value::Array(y)) if x != y && (x.starts_with(y) || y.starts_with(x)) => {
5334            let mut common = if x.len() < y.len() { x.clone() } else { y.clone() };
5335            common.push(Value::Unknown);
5336            Value::Array(common)
5337        }
5338        _ => either(a, b),
5339    }
5340}
5341
5342/// Appends a flat config, or the configs of a (nested) list of them, to `list`.
5343fn flatten_into(value: Value, list: &mut Vec<Value>) {
5344    match value {
5345        Value::Array(items) => {
5346            for item in items {
5347                flatten_into(item, list);
5348            }
5349        }
5350        other => list.push(other),
5351    }
5352}
5353
5354/// The only argument of a call, when it is a plain expression.
5355fn single_argument<'p, 'a>(call: &'p CallExpression<'a>) -> Option<&'p Expression<'a>> {
5356    let mut arguments = call.arguments.iter();
5357    match (arguments.next(), arguments.next()) {
5358        (Some(only), None) => only.as_expression(),
5359        _ => None,
5360    }
5361}
5362
5363/// `require('pkg')` and the factory call `require('pkg')(options)` nested in a config value (a `PostCSS` plugin list),
5364/// as the package name. A top-level `require` stays unknown: it is a whole config, not a name. Relative paths stay
5365/// unknown too, as they load a module rather than name a package.
5366fn required_package(call: &CallExpression<'_>, depth: u32) -> Option<Value> {
5367    if depth < 2 {
5368        return None;
5369    }
5370    let call = match call.callee.get_inner_expression() {
5371        Expression::CallExpression(factory) => &**factory,
5372        _ => call,
5373    };
5374    let Expression::Identifier(callee) = call.callee.get_inner_expression() else {
5375        return None;
5376    };
5377    if callee.name.as_str() != "require" {
5378        return None;
5379    }
5380    let module = single_argument(call).and_then(static_string)?;
5381    (!module.starts_with(['.', '/'])).then_some(Value::String(module))
5382}
5383
5384/// What the directory `dir` of a resolved `package.json` evaluates to: the package for a bare specifier (`pkg`,
5385/// `@scope/pkg`, a subpath of one), `None` for a relative path, which the path functions compute, and unknown for
5386/// anything else, which names no package: an absolute path, a URL, a subpath import (`#x`), or a path with an empty,
5387/// `.` or `..` segment.
5388fn manifest_directory(dir: &str) -> Option<Value> {
5389    if dir.starts_with('.') {
5390        return None;
5391    }
5392    let package = !dir.starts_with(['/', '#'])
5393        && !dir.contains([':', '\\'])
5394        && dir.split('/').all(|segment| !matches!(segment, "" | "." | ".."));
5395    Some(if package {
5396        Value::String(dir.to_owned())
5397    } else {
5398        Value::Unknown
5399    })
5400}
5401
5402/// Whether `call` resolves a module specifier: `require.resolve(…)` or `import.meta.resolve(…)`.
5403fn is_module_resolve(call: &CallExpression<'_>) -> bool {
5404    is_resolve_of(call, "require")
5405        || matches!(
5406            call.callee.get_inner_expression(),
5407            Expression::StaticMemberExpression(m)
5408                if m.property.name.as_str() == "resolve"
5409                    && matches!(m.object.get_inner_expression(), Expression::ImportMeta(_))
5410        )
5411}
5412
5413/// Whether `call` is `<object>.resolve(…)` of the global `object`.
5414fn is_resolve_of(call: &CallExpression<'_>, object: &str) -> bool {
5415    matches!(
5416        call.callee.get_inner_expression(),
5417        Expression::StaticMemberExpression(m)
5418            if m.property.name.as_str() == "resolve"
5419                && matches!(m.object.get_inner_expression(), Expression::Identifier(o) if o.name.as_str() == object)
5420    )
5421}
5422
5423/// What an optional chain reads from: the object of its outermost member, or the callee of its call.
5424fn chain_object<'b, 'a>(chain: &'b ChainExpression<'a>) -> Option<&'b Expression<'a>> {
5425    match &chain.expression {
5426        ChainElement::CallExpression(call) => Some(&call.callee),
5427        ChainElement::TSNonNullExpression(e) => Some(&e.expression),
5428        element => element.as_member_expression().map(MemberExpression::object),
5429    }
5430}