turbopack_ecmascript/references/
mod.rs

1pub mod amd;
2pub mod async_module;
3pub mod cjs;
4pub mod constant_condition;
5pub mod constant_value;
6pub mod dynamic_expression;
7pub mod esm;
8pub mod exports_info;
9pub mod external_module;
10pub mod ident;
11pub mod member;
12pub mod node;
13pub mod pattern_mapping;
14pub mod raw;
15pub mod require_context;
16pub mod type_issue;
17pub mod typescript;
18pub mod unreachable;
19pub mod util;
20pub mod worker;
21
22use std::{
23    borrow::Cow,
24    collections::BTreeMap,
25    future::Future,
26    mem::take,
27    ops::Deref,
28    sync::{Arc, LazyLock},
29};
30
31use anyhow::{Result, bail};
32use bincode::{Decode, Encode};
33use constant_condition::{ConstantConditionCodeGen, ConstantConditionValue};
34use constant_value::ConstantValueCodeGen;
35use either::Either;
36use indexmap::map::Entry;
37use num_traits::Zero;
38use once_cell::sync::Lazy;
39use parking_lot::Mutex;
40use regex::Regex;
41use rustc_hash::{FxHashMap, FxHashSet};
42use swc_core::{
43    atoms::{Atom, Wtf8Atom, atom},
44    common::{
45        GLOBALS, Globals, Span, Spanned, SyntaxContext,
46        comments::{CommentKind, Comments},
47        errors::{DiagnosticId, HANDLER, Handler},
48        pass::AstNodePath,
49        source_map::SmallPos,
50    },
51    ecma::{
52        ast::*,
53        utils::IsDirective,
54        visit::{
55            AstParentKind, AstParentNodeRef, VisitAstPath, VisitWithAstPath,
56            fields::{
57                AssignExprField, AssignTargetField, BindingIdentField, SimpleAssignTargetField,
58            },
59        },
60    },
61};
62use tokio::sync::OnceCell;
63use tracing::Instrument;
64use turbo_rcstr::{RcStr, rcstr};
65use turbo_tasks::{
66    FxIndexMap, FxIndexSet, NonLocalValue, ReadRef, ResolvedVc, TaskInput, TryJoinIterExt, Upcast,
67    ValueToString, Vc, trace::TraceRawVcs,
68};
69use turbo_tasks_fs::FileSystemPath;
70use turbopack_core::{
71    compile_time_info::{
72        CompileTimeDefineValue, CompileTimeDefines, CompileTimeInfo, DefinableNameSegment,
73        FreeVarReference, FreeVarReferenceVcs, FreeVarReferences, FreeVarReferencesIndividual,
74        InputRelativeConstant,
75    },
76    environment::Rendering,
77    error::PrettyPrintError,
78    issue::{IssueExt, IssueSeverity, IssueSource, StyledString, analyze::AnalyzeIssue},
79    module::{Module, ModuleSideEffects},
80    reference::{ModuleReference, ModuleReferences},
81    reference_type::{CommonJsReferenceSubType, ReferenceType},
82    resolve::{
83        FindContextFileResult, ImportUsage, ModulePart, find_context_file,
84        origin::{PlainResolveOrigin, ResolveOrigin},
85        parse::Request,
86        pattern::Pattern,
87        resolve,
88    },
89    source::Source,
90    source_map::GenerateSourceMap,
91};
92use turbopack_resolve::{
93    ecmascript::{apply_cjs_specific_options, cjs_resolve_source},
94    typescript::tsconfig,
95};
96use turbopack_swc_utils::emitter::IssueEmitter;
97use unreachable::Unreachable;
98use worker::WorkerAssetReference;
99
100pub use crate::references::esm::export::{FollowExportsResult, follow_reexports};
101use crate::{
102    AnalyzeMode, EcmascriptInputTransforms, EcmascriptModuleAsset, EcmascriptModuleAssetType,
103    EcmascriptParsable, ModuleTypeResult, SpecifiedModuleType, TreeShakingMode, TypeofWindow,
104    analyzer::{
105        ConstantNumber, ConstantString, ConstantValue as JsConstantValue, JsValue, JsValueUrlKind,
106        ObjectPart, RequireContextValue, WellKnownFunctionKind, WellKnownObjectKind,
107        builtin::{early_replace_builtin, replace_builtin},
108        graph::{
109            ConditionalKind, DeclUsage, Effect, EffectArg, EvalContext, VarGraph, create_graph,
110        },
111        imports::{ImportAnnotations, ImportAttributes, ImportedSymbol, Reexport},
112        linker::link,
113        parse_require_context, side_effects,
114        top_level_await::has_top_level_await,
115        well_known::replace_well_known,
116    },
117    chunk::EcmascriptExports,
118    code_gen::{CodeGen, CodeGens, IntoCodeGenReference},
119    errors,
120    export::Liveness,
121    magic_identifier,
122    parse::ParseResult,
123    references::{
124        amd::{
125            AmdDefineAssetReference, AmdDefineDependencyElement, AmdDefineFactoryType,
126            AmdDefineWithDependenciesCodeGen,
127        },
128        async_module::{AsyncModule, OptionAsyncModule},
129        cjs::{
130            CjsAssetReference, CjsRequireAssetReference, CjsRequireCacheAccess,
131            CjsRequireResolveAssetReference,
132        },
133        dynamic_expression::DynamicExpression,
134        esm::{
135            EsmAssetReference, EsmAsyncAssetReference, EsmBinding, EsmExports, EsmModuleItem,
136            ImportMetaBinding, ImportMetaRef, UrlAssetReference, UrlRewriteBehavior,
137            base::EsmAssetReferences, export::EsmExport, module_id::EsmModuleIdAssetReference,
138        },
139        exports_info::{ExportsInfoBinding, ExportsInfoRef},
140        ident::IdentReplacement,
141        member::MemberReplacement,
142        node::{FilePathModuleReference, PackageJsonReference},
143        raw::{DirAssetReference, FileSourceReference},
144        require_context::{RequireContextAssetReference, RequireContextMap},
145        type_issue::SpecifiedModuleTypeIssue,
146        typescript::{
147            TsConfigReference, TsReferencePathAssetReference, TsReferenceTypeAssetReference,
148        },
149    },
150    runtime_functions::{
151        TURBOPACK_EXPORT_NAMESPACE, TURBOPACK_EXPORT_VALUE, TURBOPACK_EXPORTS, TURBOPACK_GLOBAL,
152        TURBOPACK_REQUIRE_REAL, TURBOPACK_REQUIRE_STUB, TURBOPACK_RUNTIME_FUNCTION_SHORTCUTS,
153    },
154    source_map::parse_source_map_comment,
155    tree_shake::{find_turbopack_part_id_in_asserts, part_of_module, split_module},
156    utils::{AstPathRange, js_value_to_pattern, module_value_to_well_known_object},
157    webpack::{
158        WebpackChunkAssetReference, WebpackEntryAssetReference, WebpackRuntimeAssetReference,
159        parse::{WebpackRuntime, webpack_runtime},
160    },
161};
162
163#[turbo_tasks::value(shared)]
164pub struct AnalyzeEcmascriptModuleResult {
165    references: Vec<ResolvedVc<Box<dyn ModuleReference>>>,
166
167    pub esm_references: ResolvedVc<EsmAssetReferences>,
168    pub esm_local_references: ResolvedVc<EsmAssetReferences>,
169    pub esm_reexport_references: ResolvedVc<EsmAssetReferences>,
170
171    pub code_generation: ResolvedVc<CodeGens>,
172    pub exports: ResolvedVc<EcmascriptExports>,
173    pub async_module: ResolvedVc<OptionAsyncModule>,
174    pub side_effects: ModuleSideEffects,
175    /// `true` when the analysis was successful.
176    pub successful: bool,
177    pub source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
178}
179
180#[turbo_tasks::value_impl]
181impl AnalyzeEcmascriptModuleResult {
182    #[turbo_tasks::function]
183    pub async fn references(&self) -> Result<Vc<ModuleReferences>> {
184        Ok(Vc::cell(
185            self.esm_references
186                .await?
187                .iter()
188                .map(|r| ResolvedVc::upcast(*r))
189                .chain(self.references.iter().copied())
190                .collect(),
191        ))
192    }
193
194    #[turbo_tasks::function]
195    pub async fn local_references(&self) -> Result<Vc<ModuleReferences>> {
196        Ok(Vc::cell(
197            self.esm_local_references
198                .await?
199                .iter()
200                .map(|r| ResolvedVc::upcast(*r))
201                .chain(self.references.iter().copied())
202                .collect(),
203        ))
204    }
205}
206
207/// In debug builds, use FxIndexSet to catch duplicate code gens
208/// In release builds, use Vec for better performance
209#[cfg(debug_assertions)]
210type CodeGenCollection = FxIndexSet<CodeGen>;
211#[cfg(not(debug_assertions))]
212type CodeGenCollection = Vec<CodeGen>;
213
214/// A temporary analysis result builder to pass around, to be turned into an
215/// `Vc<AnalyzeEcmascriptModuleResult>` eventually.
216struct AnalyzeEcmascriptModuleResultBuilder {
217    analyze_mode: AnalyzeMode,
218
219    references: FxIndexSet<ResolvedVc<Box<dyn ModuleReference>>>,
220
221    esm_references: FxHashSet<usize>,
222    esm_local_references: FxHashSet<usize>,
223    esm_reexport_references: FxHashSet<usize>,
224
225    esm_references_free_var: FxIndexMap<RcStr, ResolvedVc<EsmAssetReference>>,
226    // Ad-hoc created import references that are resolved `import * as x from ...; x.foo` accesses
227    // This caches repeated access because EsmAssetReference::new is not a turbo task function.
228    esm_references_rewritten: FxHashMap<usize, FxIndexMap<RcStr, ResolvedVc<EsmAssetReference>>>,
229
230    code_gens: CodeGenCollection,
231    exports: EcmascriptExports,
232    async_module: ResolvedVc<OptionAsyncModule>,
233    successful: bool,
234    source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
235    side_effects: ModuleSideEffects,
236    #[cfg(debug_assertions)]
237    ident: RcStr,
238}
239
240impl AnalyzeEcmascriptModuleResultBuilder {
241    fn new(analyze_mode: AnalyzeMode) -> Self {
242        Self {
243            analyze_mode,
244            references: Default::default(),
245            esm_references: Default::default(),
246            esm_local_references: Default::default(),
247            esm_reexport_references: Default::default(),
248            esm_references_rewritten: Default::default(),
249            esm_references_free_var: Default::default(),
250            code_gens: Default::default(),
251            exports: EcmascriptExports::Unknown,
252            async_module: ResolvedVc::cell(None),
253            successful: false,
254            source_map: None,
255            side_effects: ModuleSideEffects::SideEffectful,
256            #[cfg(debug_assertions)]
257            ident: Default::default(),
258        }
259    }
260
261    /// Adds an asset reference to the analysis result.
262    pub fn add_reference(&mut self, reference: ResolvedVc<impl Upcast<Box<dyn ModuleReference>>>) {
263        let r = ResolvedVc::upcast_non_strict(reference);
264        self.references.insert(r);
265    }
266
267    /// Adds an asset reference with codegen to the analysis result.
268    pub fn add_reference_code_gen<R: IntoCodeGenReference>(&mut self, reference: R, path: AstPath) {
269        let (reference, code_gen) = reference.into_code_gen_reference(path);
270        self.references.insert(reference);
271        self.add_code_gen(code_gen);
272    }
273
274    /// Adds an ESM asset reference to the analysis result.
275    pub fn add_esm_reference(&mut self, idx: usize) {
276        self.esm_references.insert(idx);
277        self.esm_local_references.insert(idx);
278    }
279
280    /// Adds an reexport ESM reference to the analysis result.
281    /// If you're unsure about which function to use, use `add_reference()`
282    pub fn add_esm_reexport_reference(&mut self, idx: usize) {
283        self.esm_references.insert(idx);
284        self.esm_reexport_references.insert(idx);
285    }
286
287    /// Adds an evaluation ESM reference to the analysis result.
288    /// If you're unsure about which function to use, use `add_reference()`
289    pub fn add_esm_evaluation_reference(&mut self, idx: usize) {
290        self.esm_references.insert(idx);
291        self.esm_local_references.insert(idx);
292    }
293
294    /// Adds a codegen to the analysis result.
295    pub fn add_code_gen<C>(&mut self, code_gen: C)
296    where
297        C: Into<CodeGen>,
298    {
299        if self.analyze_mode.is_code_gen() {
300            #[cfg(debug_assertions)]
301            {
302                let (index, added) = self.code_gens.insert_full(code_gen.into());
303                debug_assert!(
304                    added,
305                    "Duplicate code gen added: {:?} in {}",
306                    self.code_gens.get_index(index).unwrap(),
307                    self.ident
308                );
309            }
310            #[cfg(not(debug_assertions))]
311            {
312                self.code_gens.push(code_gen.into());
313            }
314        }
315    }
316
317    /// Sets the analysis result ES export.
318    pub fn set_source_map(&mut self, source_map: ResolvedVc<Box<dyn GenerateSourceMap>>) {
319        self.source_map = Some(source_map);
320    }
321
322    /// Sets the analysis result ES export.
323    pub fn set_exports(&mut self, exports: EcmascriptExports) {
324        self.exports = exports;
325    }
326
327    /// Sets the analysis result ES export.
328    pub fn set_async_module(&mut self, async_module: ResolvedVc<AsyncModule>) {
329        self.async_module = ResolvedVc::cell(Some(async_module));
330    }
331
332    /// Set whether this module is side-effect free according to a user-provided directive.
333    pub fn set_side_effects_mode(&mut self, value: ModuleSideEffects) {
334        self.side_effects = value;
335    }
336
337    /// Sets whether the analysis was successful.
338    pub fn set_successful(&mut self, successful: bool) {
339        self.successful = successful;
340    }
341
342    pub fn add_esm_reference_namespace_resolved(
343        &mut self,
344        esm_reference_idx: usize,
345        export: RcStr,
346        on_insert: impl FnOnce() -> ResolvedVc<EsmAssetReference>,
347    ) -> ResolvedVc<EsmAssetReference> {
348        *self
349            .esm_references_rewritten
350            .entry(esm_reference_idx)
351            .or_default()
352            .entry(export)
353            .or_insert_with(on_insert)
354    }
355
356    pub async fn add_esm_reference_free_var(
357        &mut self,
358        request: RcStr,
359        on_insert: impl AsyncFnOnce() -> Result<ResolvedVc<EsmAssetReference>>,
360    ) -> Result<ResolvedVc<EsmAssetReference>> {
361        Ok(match self.esm_references_free_var.entry(request) {
362            Entry::Occupied(e) => *e.get(),
363            Entry::Vacant(e) => *e.insert(on_insert().await?),
364        })
365    }
366
367    /// Builds the final analysis result. Resolves internal Vcs.
368    pub async fn build(
369        mut self,
370        import_references: Vec<ResolvedVc<EsmAssetReference>>,
371        track_reexport_references: bool,
372    ) -> Result<Vc<AnalyzeEcmascriptModuleResult>> {
373        // esm_references_rewritten (and esm_references_free_var) needs to be spliced in at the
374        // correct index into esm_references and esm_local_references
375        let mut esm_references = Vec::with_capacity(
376            self.esm_references.len()
377                + self.esm_references_free_var.len()
378                + self.esm_references_rewritten.len(),
379        );
380        esm_references.extend(self.esm_references_free_var.values());
381
382        let mut esm_local_references = track_reexport_references.then(|| {
383            let mut esm_local_references = Vec::with_capacity(
384                self.esm_local_references.len()
385                    + self.esm_references_free_var.len()
386                    + self.esm_references_rewritten.len(),
387            );
388            esm_local_references.extend(self.esm_references_free_var.values());
389            esm_local_references
390        });
391        let mut esm_reexport_references = track_reexport_references
392            .then(|| Vec::with_capacity(self.esm_reexport_references.len()));
393        for (i, reference) in import_references.iter().enumerate() {
394            if self.esm_references.contains(&i) {
395                esm_references.push(*reference);
396            }
397            esm_references.extend(
398                self.esm_references_rewritten
399                    .get(&i)
400                    .iter()
401                    .flat_map(|m| m.values().copied()),
402            );
403            if let Some(esm_local_references) = &mut esm_local_references {
404                if self.esm_local_references.contains(&i) {
405                    esm_local_references.push(*reference);
406                }
407                esm_local_references.extend(
408                    self.esm_references_rewritten
409                        .get(&i)
410                        .iter()
411                        .flat_map(|m| m.values().copied()),
412                );
413            }
414            if let Some(esm_reexport_references) = &mut esm_reexport_references
415                && self.esm_reexport_references.contains(&i)
416            {
417                esm_reexport_references.push(*reference);
418            }
419        }
420
421        let references: Vec<_> = self.references.into_iter().collect();
422
423        if !self.analyze_mode.is_code_gen() {
424            debug_assert!(self.code_gens.is_empty());
425        }
426
427        self.code_gens.shrink_to_fit();
428
429        #[cfg(debug_assertions)]
430        let code_generation = self.code_gens.into_iter().collect::<Vec<_>>();
431        #[cfg(not(debug_assertions))]
432        let code_generation = self.code_gens;
433
434        Ok(AnalyzeEcmascriptModuleResult::cell(
435            AnalyzeEcmascriptModuleResult {
436                references,
437                esm_references: ResolvedVc::cell(esm_references),
438                esm_local_references: ResolvedVc::cell(esm_local_references.unwrap_or_default()),
439                esm_reexport_references: ResolvedVc::cell(
440                    esm_reexport_references.unwrap_or_default(),
441                ),
442                code_generation: ResolvedVc::cell(code_generation),
443                exports: self.exports.resolved_cell(),
444                async_module: self.async_module,
445                side_effects: self.side_effects,
446                successful: self.successful,
447                source_map: self.source_map,
448            },
449        ))
450    }
451}
452
453struct AnalysisState<'a> {
454    handler: &'a Handler,
455    module: ResolvedVc<EcmascriptModuleAsset>,
456    source: ResolvedVc<Box<dyn Source>>,
457    origin: ResolvedVc<Box<dyn ResolveOrigin>>,
458    compile_time_info: ResolvedVc<CompileTimeInfo>,
459    var_graph: &'a VarGraph,
460    /// Whether to allow tracing to reference files from the project root. This is used to prevent
461    /// random node_modules packages from tracing the entire project due to some dynamic
462    /// `path.join(foo, bar)` call.
463    allow_project_root_tracing: bool,
464    /// This is the current state of known values of function
465    /// arguments.
466    fun_args_values: Mutex<FxHashMap<u32, Vec<JsValue>>>,
467    var_cache: Mutex<FxHashMap<Id, JsValue>>,
468    // There can be many references to import.meta, but only the first should hoist
469    // the object allocation.
470    first_import_meta: bool,
471    // There can be many references to __webpack_exports_info__, but only the first should hoist
472    // the object allocation.
473    first_webpack_exports_info: bool,
474    tree_shaking_mode: Option<TreeShakingMode>,
475    import_externals: bool,
476    ignore_dynamic_requests: bool,
477    url_rewrite_behavior: Option<UrlRewriteBehavior>,
478    free_var_references: ReadRef<FreeVarReferencesIndividual>,
479    // Whether we should collect affecting sources from referenced files. Only usedful when
480    // tracing.
481    collect_affecting_sources: bool,
482}
483
484impl AnalysisState<'_> {
485    /// Links a value to the graph, returning the linked value.
486    async fn link_value(&self, value: JsValue, attributes: &ImportAttributes) -> Result<JsValue> {
487        Ok(link(
488            self.var_graph,
489            value,
490            &early_value_visitor,
491            &|value| {
492                value_visitor(
493                    *self.origin,
494                    value,
495                    *self.compile_time_info,
496                    &self.free_var_references,
497                    self.var_graph,
498                    attributes,
499                    self.allow_project_root_tracing,
500                )
501            },
502            &self.fun_args_values,
503            &self.var_cache,
504        )
505        .await?
506        .0)
507    }
508}
509
510fn set_handler_and_globals<F, R>(handler: &Handler, globals: &Arc<Globals>, f: F) -> R
511where
512    F: FnOnce() -> R,
513{
514    HANDLER.set(handler, || GLOBALS.set(globals, f))
515}
516
517/// Analyse a provided [EcmascriptModuleAsset] and return a [AnalyzeEcmascriptModuleResult].
518#[turbo_tasks::function]
519pub async fn analyze_ecmascript_module(
520    module: ResolvedVc<EcmascriptModuleAsset>,
521    part: Option<ModulePart>,
522) -> Result<Vc<AnalyzeEcmascriptModuleResult>> {
523    let span = tracing::info_span!(
524        "analyze ecmascript module",
525        name = display(module.ident().to_string().await?)
526    );
527    let result = analyze_ecmascript_module_internal(module, part)
528        .instrument(span)
529        .await;
530
531    match result {
532        Ok(result) => Ok(result),
533        Err(err) => Err(err.context(format!(
534            "failed to analyze ecmascript module '{}'",
535            module.ident().to_string().await?
536        ))),
537    }
538}
539
540async fn analyze_ecmascript_module_internal(
541    module: ResolvedVc<EcmascriptModuleAsset>,
542    part: Option<ModulePart>,
543) -> Result<Vc<AnalyzeEcmascriptModuleResult>> {
544    let raw_module = module.await?;
545
546    let source = raw_module.source;
547    let ty = raw_module.ty;
548    let transforms = raw_module.transforms;
549    let options = raw_module.options;
550    let options = options.await?;
551    let import_externals = options.import_externals;
552    let analyze_mode = options.analyze_mode;
553
554    let origin = ResolvedVc::upcast::<Box<dyn ResolveOrigin>>(module);
555    let path = &*origin.origin_path().await?;
556    let mut analysis = AnalyzeEcmascriptModuleResultBuilder::new(analyze_mode);
557    #[cfg(debug_assertions)]
558    {
559        analysis.ident = source.ident().to_string().owned().await?;
560    }
561
562    // Is this a typescript file that requires analyzing type references?
563    let analyze_types = match &ty {
564        EcmascriptModuleAssetType::Typescript { analyze_types, .. } => *analyze_types,
565        EcmascriptModuleAssetType::TypescriptDeclaration => true,
566        EcmascriptModuleAssetType::Ecmascript
567        | EcmascriptModuleAssetType::EcmascriptExtensionless => false,
568    };
569
570    // Split out our module part if we have one.
571    let parsed = if let Some(part) = part {
572        let split_data = split_module(*module);
573        part_of_module(split_data, part)
574    } else {
575        module.failsafe_parse()
576    };
577
578    let ModuleTypeResult {
579        module_type: specified_type,
580        ref referenced_package_json,
581    } = *module.determine_module_type().await?;
582
583    if let Some(package_json) = referenced_package_json {
584        let span = tracing::trace_span!("package.json reference");
585        async {
586            analysis.add_reference(
587                PackageJsonReference::new(package_json.clone())
588                    .to_resolved()
589                    .await?,
590            );
591            anyhow::Ok(())
592        }
593        .instrument(span)
594        .await?;
595    }
596
597    if analyze_types {
598        let span = tracing::trace_span!("tsconfig reference");
599        async {
600            match &*find_context_file(path.parent(), tsconfig(), false).await? {
601                FindContextFileResult::Found(tsconfig, _) => {
602                    analysis.add_reference(
603                        TsConfigReference::new(*origin, tsconfig.clone())
604                            .to_resolved()
605                            .await?,
606                    );
607                }
608                FindContextFileResult::NotFound(_) => {}
609            };
610            anyhow::Ok(())
611        }
612        .instrument(span)
613        .await?;
614    }
615
616    let parsed = if !analyze_mode.is_code_gen() {
617        // We are never code-gening the module, so we can drop the AST after the analysis.
618        parsed.final_read_hint().await?
619    } else {
620        parsed.await?
621    };
622
623    let ParseResult::Ok {
624        program,
625        globals,
626        eval_context,
627        comments,
628        source_map,
629        ..
630    } = &*parsed
631    else {
632        return analysis.build(Default::default(), false).await;
633    };
634
635    let has_side_effect_free_directive = match program {
636        Program::Module(module) => Either::Left(
637            module
638                .body
639                .iter()
640                .take_while(|i| match i {
641                    ModuleItem::Stmt(stmt) => stmt.directive_continue(),
642                    ModuleItem::ModuleDecl(_) => false,
643                })
644                .filter_map(|i| i.as_stmt()),
645        ),
646        Program::Script(script) => Either::Right(
647            script
648                .body
649                .iter()
650                .take_while(|stmt| stmt.directive_continue()),
651        ),
652    }
653    .any(|f| match f {
654        Stmt::Expr(ExprStmt { expr, .. }) => match &**expr {
655            Expr::Lit(Lit::Str(Str { value, .. })) => value == "use turbopack no side effects",
656            _ => false,
657        },
658        _ => false,
659    });
660    analysis.set_side_effects_mode(if has_side_effect_free_directive {
661        ModuleSideEffects::SideEffectFree
662    } else if options.infer_module_side_effects {
663        // Analyze the AST to infer side effects
664        GLOBALS.set(globals, || {
665            side_effects::compute_module_evaluation_side_effects(
666                program,
667                comments,
668                eval_context.unresolved_mark,
669            )
670        })
671    } else {
672        // If inference is disabled, assume side effects
673        ModuleSideEffects::SideEffectful
674    });
675
676    let is_esm = eval_context.is_esm(specified_type);
677    let compile_time_info = compile_time_info_for_module_options(
678        *raw_module.compile_time_info,
679        is_esm,
680        options.enable_typeof_window_inlining,
681    )
682    .to_resolved()
683    .await?;
684
685    let pos = program.span().lo;
686    if analyze_types {
687        let span = tracing::trace_span!("type references");
688        async {
689            if let Some(comments) = comments.get_leading(pos) {
690                for comment in comments.iter() {
691                    if let CommentKind::Line = comment.kind {
692                        static REFERENCE_PATH: LazyLock<Regex> = LazyLock::new(|| {
693                            Regex::new(r#"^/\s*<reference\s*path\s*=\s*["'](.+)["']\s*/>\s*$"#)
694                                .unwrap()
695                        });
696                        static REFERENCE_TYPES: LazyLock<Regex> = LazyLock::new(|| {
697                            Regex::new(r#"^/\s*<reference\s*types\s*=\s*["'](.+)["']\s*/>\s*$"#)
698                                .unwrap()
699                        });
700                        let text = &comment.text;
701                        if let Some(m) = REFERENCE_PATH.captures(text) {
702                            let path = &m[1];
703                            analysis.add_reference(
704                                TsReferencePathAssetReference::new(*origin, path.into())
705                                    .to_resolved()
706                                    .await?,
707                            );
708                        } else if let Some(m) = REFERENCE_TYPES.captures(text) {
709                            let types = &m[1];
710                            analysis.add_reference(
711                                TsReferenceTypeAssetReference::new(*origin, types.into())
712                                    .to_resolved()
713                                    .await?,
714                            );
715                        }
716                    }
717                }
718            }
719            anyhow::Ok(())
720        }
721        .instrument(span)
722        .await?;
723    }
724
725    if options.extract_source_map {
726        let span = tracing::trace_span!("source map reference");
727        async {
728            if let Some((source_map, reference)) = parse_source_map_comment(
729                source,
730                Either::Left(comments),
731                &*origin.origin_path().await?,
732            )
733            .await?
734            {
735                analysis.set_source_map(source_map);
736                if let Some(reference) = reference {
737                    analysis.add_reference(reference);
738                }
739            }
740            anyhow::Ok(())
741        }
742        .instrument(span)
743        .await?;
744    }
745
746    let (emitter, collector) = IssueEmitter::new(source, source_map.clone(), None);
747    let handler = Handler::with_emitter(true, false, Box::new(emitter));
748
749    let mut var_graph = {
750        let _span = tracing::trace_span!("analyze variable values").entered();
751        set_handler_and_globals(&handler, globals, || {
752            create_graph(program, eval_context, analyze_mode)
753        })
754    };
755
756    let mut import_usage =
757        FxHashMap::with_capacity_and_hasher(var_graph.import_usages.len(), Default::default());
758    for (reference, usage) in &var_graph.import_usages {
759        // TODO make this more efficient, i.e. cache the result?
760        if let DeclUsage::Bindings(ids) = usage {
761            // compute transitive closure of `ids` over `top_level_mappings`
762            let mut visited = ids.clone();
763            let mut stack = ids.iter().collect::<Vec<_>>();
764            let mut has_global_usage = false;
765            while let Some(id) = stack.pop() {
766                match var_graph.decl_usages.get(id) {
767                    Some(DeclUsage::SideEffects) => {
768                        has_global_usage = true;
769                        break;
770                    }
771                    Some(DeclUsage::Bindings(callers)) => {
772                        for caller in callers {
773                            if visited.insert(caller.clone()) {
774                                stack.push(caller);
775                            }
776                        }
777                    }
778                    _ => {}
779                }
780            }
781
782            // Collect all `visited` declarations which are exported
783            import_usage.insert(
784                *reference,
785                if has_global_usage {
786                    ImportUsage::SideEffects
787                } else {
788                    ImportUsage::Exports(
789                        var_graph
790                            .exports
791                            .iter()
792                            .filter(|(_, id)| visited.contains(*id))
793                            .map(|(exported, _)| exported.as_str().into())
794                            .collect(),
795                    )
796                },
797            );
798        }
799    }
800
801    let span = tracing::trace_span!("esm import references");
802    let import_references = async {
803        let mut import_references = Vec::with_capacity(eval_context.imports.references().len());
804        for (i, r) in eval_context.imports.references().enumerate() {
805            let mut should_add_evaluation = false;
806            let reference = EsmAssetReference::new(
807                module,
808                ResolvedVc::upcast(module),
809                RcStr::from(&*r.module_path.to_string_lossy()),
810                r.issue_source
811                    .unwrap_or_else(|| IssueSource::from_source_only(source)),
812                r.annotations.clone(),
813                match &r.imported_symbol {
814                    ImportedSymbol::ModuleEvaluation => {
815                        should_add_evaluation = true;
816                        Some(ModulePart::evaluation())
817                    }
818                    ImportedSymbol::Symbol(name) => Some(ModulePart::export((&**name).into())),
819                    ImportedSymbol::PartEvaluation(part_id) | ImportedSymbol::Part(part_id) => {
820                        if !matches!(
821                            options.tree_shaking_mode,
822                            Some(TreeShakingMode::ModuleFragments)
823                        ) {
824                            bail!(
825                                "Internal imports only exist in reexports only mode when \
826                                 importing {:?} from {}",
827                                r.imported_symbol,
828                                r.module_path.to_string_lossy()
829                            );
830                        }
831                        if matches!(&r.imported_symbol, ImportedSymbol::PartEvaluation(_)) {
832                            should_add_evaluation = true;
833                        }
834                        Some(ModulePart::internal(*part_id))
835                    }
836                    ImportedSymbol::Exports => matches!(
837                        options.tree_shaking_mode,
838                        Some(TreeShakingMode::ModuleFragments)
839                    )
840                    .then(ModulePart::exports),
841                },
842                import_usage.get(&i).cloned().unwrap_or_default(),
843                import_externals,
844                options.tree_shaking_mode,
845            )
846            .resolved_cell();
847
848            import_references.push(reference);
849            if should_add_evaluation {
850                analysis.add_esm_evaluation_reference(i);
851            }
852        }
853        anyhow::Ok(import_references)
854    }
855    .instrument(span)
856    .await?;
857
858    let span = tracing::trace_span!("exports");
859    let (webpack_runtime, webpack_entry, webpack_chunks) = async {
860        let (webpack_runtime, webpack_entry, webpack_chunks, mut esm_exports) =
861            set_handler_and_globals(&handler, globals, || {
862                // TODO migrate to effects
863                let mut visitor = ModuleReferencesVisitor::new(
864                    eval_context,
865                    &import_references,
866                    &mut analysis,
867                    analyze_mode,
868                );
869                // ModuleReferencesVisitor has already called analysis.add_esm_reexport_reference
870                // for any references in esm_exports
871                program.visit_with_ast_path(&mut visitor, &mut Default::default());
872                (
873                    visitor.webpack_runtime,
874                    visitor.webpack_entry,
875                    visitor.webpack_chunks,
876                    visitor.esm_exports,
877                )
878            });
879
880        let mut esm_star_exports: Vec<ResolvedVc<Box<dyn ModuleReference>>> = vec![];
881        for (i, reexport) in eval_context.imports.reexports() {
882            let reference = import_references[i];
883            match reexport {
884                Reexport::Star => {
885                    esm_star_exports.push(ResolvedVc::upcast(reference));
886                    analysis.add_esm_reexport_reference(i);
887                }
888                Reexport::Namespace { exported: n } => {
889                    esm_exports.insert(
890                        n.as_str().into(),
891                        EsmExport::ImportedNamespace(ResolvedVc::upcast(reference)),
892                    );
893                    analysis.add_esm_reexport_reference(i);
894                }
895                Reexport::Named { imported, exported } => {
896                    esm_exports.insert(
897                        exported.as_str().into(),
898                        EsmExport::ImportedBinding(
899                            ResolvedVc::upcast(reference),
900                            imported.to_string().into(),
901                            false,
902                        ),
903                    );
904                    analysis.add_esm_reexport_reference(i);
905                }
906            }
907        }
908
909        let exports = if !esm_exports.is_empty() || !esm_star_exports.is_empty() {
910            if specified_type == SpecifiedModuleType::CommonJs {
911                SpecifiedModuleTypeIssue {
912                    // TODO(PACK-4879): this should point at one of the exports
913                    source: IssueSource::from_source_only(source),
914                    specified_type,
915                }
916                .resolved_cell()
917                .emit();
918            }
919
920            let esm_exports = EsmExports {
921                exports: esm_exports,
922                star_exports: esm_star_exports,
923            }
924            .cell();
925
926            EcmascriptExports::EsmExports(esm_exports.to_resolved().await?)
927        } else if specified_type == SpecifiedModuleType::EcmaScript {
928            match detect_dynamic_export(program) {
929                DetectedDynamicExportType::CommonJs => {
930                    SpecifiedModuleTypeIssue {
931                        // TODO(PACK-4879): this should point at the source location of the commonjs
932                        // export
933                        source: IssueSource::from_source_only(source),
934                        specified_type,
935                    }
936                    .resolved_cell()
937                    .emit();
938
939                    EcmascriptExports::EsmExports(
940                        EsmExports {
941                            exports: Default::default(),
942                            star_exports: Default::default(),
943                        }
944                        .resolved_cell(),
945                    )
946                }
947                DetectedDynamicExportType::Namespace => EcmascriptExports::DynamicNamespace,
948                DetectedDynamicExportType::Value => EcmascriptExports::Value,
949                DetectedDynamicExportType::UsingModuleDeclarations
950                | DetectedDynamicExportType::None => EcmascriptExports::EsmExports(
951                    EsmExports {
952                        exports: Default::default(),
953                        star_exports: Default::default(),
954                    }
955                    .resolved_cell(),
956                ),
957            }
958        } else {
959            match detect_dynamic_export(program) {
960                DetectedDynamicExportType::CommonJs => EcmascriptExports::CommonJs,
961                DetectedDynamicExportType::Namespace => EcmascriptExports::DynamicNamespace,
962                DetectedDynamicExportType::Value => EcmascriptExports::Value,
963                DetectedDynamicExportType::UsingModuleDeclarations => {
964                    EcmascriptExports::EsmExports(
965                        EsmExports {
966                            exports: Default::default(),
967                            star_exports: Default::default(),
968                        }
969                        .resolved_cell(),
970                    )
971                }
972                DetectedDynamicExportType::None => EcmascriptExports::EmptyCommonJs,
973            }
974        };
975        analysis.set_exports(exports);
976        anyhow::Ok((webpack_runtime, webpack_entry, webpack_chunks))
977    }
978    .instrument(span)
979    .await?;
980
981    let mut ignore_effect_span = None;
982    // Check if it was a webpack entry
983    if let Some((request, webpack_runtime_span)) = webpack_runtime {
984        let span = tracing::trace_span!("webpack runtime reference");
985        async {
986            let request = Request::parse(request.into()).to_resolved().await?;
987            let runtime = resolve_as_webpack_runtime(*origin, *request, *transforms)
988                .to_resolved()
989                .await?;
990
991            if let WebpackRuntime::Webpack5 { .. } = &*runtime.await? {
992                ignore_effect_span = Some(webpack_runtime_span);
993                analysis.add_reference(
994                    WebpackRuntimeAssetReference {
995                        origin,
996                        request,
997                        runtime,
998                        transforms,
999                    }
1000                    .resolved_cell(),
1001                );
1002
1003                if webpack_entry {
1004                    analysis.add_reference(
1005                        WebpackEntryAssetReference {
1006                            source,
1007                            runtime,
1008                            transforms,
1009                        }
1010                        .resolved_cell(),
1011                    );
1012                }
1013
1014                for chunk in webpack_chunks {
1015                    analysis.add_reference(
1016                        WebpackChunkAssetReference {
1017                            chunk_id: chunk,
1018                            runtime,
1019                            transforms,
1020                        }
1021                        .resolved_cell(),
1022                    );
1023                }
1024            }
1025            anyhow::Ok(())
1026        }
1027        .instrument(span)
1028        .await?;
1029    }
1030    // TODO: we can do this when constructing the var graph
1031    let span = tracing::trace_span!("async module handling");
1032    async {
1033        let top_level_await_span =
1034            set_handler_and_globals(&handler, globals, || has_top_level_await(program));
1035        let has_top_level_await = top_level_await_span.is_some();
1036
1037        if eval_context.is_esm(specified_type) {
1038            let async_module = AsyncModule {
1039                has_top_level_await,
1040                import_externals,
1041            }
1042            .resolved_cell();
1043            analysis.set_async_module(async_module);
1044        } else if let Some(span) = top_level_await_span {
1045            AnalyzeIssue::new(
1046                IssueSeverity::Error,
1047                source.ident(),
1048                Vc::cell(rcstr!("unexpected top level await")),
1049                StyledString::Text(rcstr!("top level await is only supported in ESM modules."))
1050                    .cell(),
1051                None,
1052                Some(issue_source(source, span)),
1053            )
1054            .to_resolved()
1055            .await?
1056            .emit();
1057        }
1058        anyhow::Ok(())
1059    }
1060    .instrument(span)
1061    .await?;
1062
1063    let span = tracing::trace_span!("effects processing");
1064    async {
1065        let effects = take(&mut var_graph.effects);
1066
1067        let mut analysis_state = AnalysisState {
1068            handler: &handler,
1069            module,
1070            source,
1071            origin,
1072            compile_time_info,
1073            var_graph: &var_graph,
1074            allow_project_root_tracing: !source.ident().path().await?.is_in_node_modules(),
1075            fun_args_values: Default::default(),
1076            var_cache: Default::default(),
1077            first_import_meta: true,
1078            first_webpack_exports_info: true,
1079            tree_shaking_mode: options.tree_shaking_mode,
1080            import_externals: options.import_externals,
1081            ignore_dynamic_requests: options.ignore_dynamic_requests,
1082            url_rewrite_behavior: options.url_rewrite_behavior,
1083            free_var_references: compile_time_info
1084                .await?
1085                .free_var_references
1086                .individual()
1087                .await?,
1088            collect_affecting_sources: options.analyze_mode.is_tracing(),
1089        };
1090
1091        enum Action {
1092            Effect(Effect),
1093            LeaveScope(u32),
1094        }
1095
1096        // This is a stack of effects to process. We use a stack since during processing
1097        // of an effect we might want to add more effects into the middle of the
1098        // processing. Using a stack where effects are appended in reverse
1099        // order allows us to do that. It's recursion implemented as Stack.
1100        let mut queue_stack = Mutex::new(Vec::new());
1101        queue_stack
1102            .get_mut()
1103            .extend(effects.into_iter().map(Action::Effect).rev());
1104
1105        while let Some(action) = queue_stack.get_mut().pop() {
1106            let effect = match action {
1107                Action::LeaveScope(func_ident) => {
1108                    analysis_state.fun_args_values.get_mut().remove(&func_ident);
1109                    continue;
1110                }
1111                Action::Effect(effect) => effect,
1112            };
1113
1114            let add_effects = |effects: Vec<Effect>| {
1115                queue_stack
1116                    .lock()
1117                    .extend(effects.into_iter().map(Action::Effect).rev())
1118            };
1119
1120            match effect {
1121                Effect::Unreachable { start_ast_path } => {
1122                    debug_assert!(
1123                        analyze_mode.is_code_gen(),
1124                        "unexpected Effect::Unreachable in tracing mode"
1125                    );
1126
1127                    analysis
1128                        .add_code_gen(Unreachable::new(AstPathRange::StartAfter(start_ast_path)));
1129                }
1130                Effect::Conditional {
1131                    condition,
1132                    kind,
1133                    ast_path: condition_ast_path,
1134                    span: _,
1135                } => {
1136                    // Don't replace condition with it's truth-y value, if it has side effects
1137                    // (e.g. function calls)
1138                    let condition_has_side_effects = condition.has_side_effects();
1139
1140                    let condition = analysis_state
1141                        .link_value(*condition, ImportAttributes::empty_ref())
1142                        .await?;
1143
1144                    macro_rules! inactive {
1145                        ($block:ident) => {
1146                            if analyze_mode.is_code_gen() {
1147                                analysis.add_code_gen(Unreachable::new($block.range.clone()));
1148                            }
1149                        };
1150                    }
1151                    macro_rules! condition {
1152                        ($expr:expr) => {
1153                            if analyze_mode.is_code_gen() && !condition_has_side_effects {
1154                                analysis.add_code_gen(ConstantConditionCodeGen::new(
1155                                    $expr,
1156                                    condition_ast_path.to_vec().into(),
1157                                ));
1158                            }
1159                        };
1160                    }
1161                    macro_rules! active {
1162                        ($block:ident) => {
1163                            queue_stack
1164                                .get_mut()
1165                                .extend($block.effects.into_iter().map(Action::Effect).rev())
1166                        };
1167                    }
1168                    match *kind {
1169                        ConditionalKind::If { then } => match condition.is_truthy() {
1170                            Some(true) => {
1171                                condition!(ConstantConditionValue::Truthy);
1172                                active!(then);
1173                            }
1174                            Some(false) => {
1175                                condition!(ConstantConditionValue::Falsy);
1176                                inactive!(then);
1177                            }
1178                            None => {
1179                                active!(then);
1180                            }
1181                        },
1182                        ConditionalKind::Else { r#else } => match condition.is_truthy() {
1183                            Some(true) => {
1184                                condition!(ConstantConditionValue::Truthy);
1185                                inactive!(r#else);
1186                            }
1187                            Some(false) => {
1188                                condition!(ConstantConditionValue::Falsy);
1189                                active!(r#else);
1190                            }
1191                            None => {
1192                                active!(r#else);
1193                            }
1194                        },
1195                        ConditionalKind::IfElse { then, r#else }
1196                        | ConditionalKind::Ternary { then, r#else } => {
1197                            match condition.is_truthy() {
1198                                Some(true) => {
1199                                    condition!(ConstantConditionValue::Truthy);
1200                                    active!(then);
1201                                    inactive!(r#else);
1202                                }
1203                                Some(false) => {
1204                                    condition!(ConstantConditionValue::Falsy);
1205                                    active!(r#else);
1206                                    inactive!(then);
1207                                }
1208                                None => {
1209                                    active!(then);
1210                                    active!(r#else);
1211                                }
1212                            }
1213                        }
1214                        ConditionalKind::IfElseMultiple { then, r#else } => {
1215                            match condition.is_truthy() {
1216                                Some(true) => {
1217                                    condition!(ConstantConditionValue::Truthy);
1218                                    for then in then {
1219                                        active!(then);
1220                                    }
1221                                    for r#else in r#else {
1222                                        inactive!(r#else);
1223                                    }
1224                                }
1225                                Some(false) => {
1226                                    condition!(ConstantConditionValue::Falsy);
1227                                    for then in then {
1228                                        inactive!(then);
1229                                    }
1230                                    for r#else in r#else {
1231                                        active!(r#else);
1232                                    }
1233                                }
1234                                None => {
1235                                    for then in then {
1236                                        active!(then);
1237                                    }
1238                                    for r#else in r#else {
1239                                        active!(r#else);
1240                                    }
1241                                }
1242                            }
1243                        }
1244                        ConditionalKind::And { expr } => match condition.is_truthy() {
1245                            Some(true) => {
1246                                condition!(ConstantConditionValue::Truthy);
1247                                active!(expr);
1248                            }
1249                            Some(false) => {
1250                                // The condition value needs to stay since it's used
1251                                inactive!(expr);
1252                            }
1253                            None => {
1254                                active!(expr);
1255                            }
1256                        },
1257                        ConditionalKind::Or { expr } => match condition.is_truthy() {
1258                            Some(true) => {
1259                                // The condition value needs to stay since it's used
1260                                inactive!(expr);
1261                            }
1262                            Some(false) => {
1263                                condition!(ConstantConditionValue::Falsy);
1264                                active!(expr);
1265                            }
1266                            None => {
1267                                active!(expr);
1268                            }
1269                        },
1270                        ConditionalKind::NullishCoalescing { expr } => {
1271                            match condition.is_nullish() {
1272                                Some(true) => {
1273                                    condition!(ConstantConditionValue::Nullish);
1274                                    active!(expr);
1275                                }
1276                                Some(false) => {
1277                                    inactive!(expr);
1278                                }
1279                                None => {
1280                                    active!(expr);
1281                                }
1282                            }
1283                        }
1284                        ConditionalKind::Labeled { body } => {
1285                            active!(body);
1286                        }
1287                    }
1288                }
1289                Effect::Call {
1290                    func,
1291                    args,
1292                    ast_path,
1293                    span,
1294                    in_try,
1295                    new,
1296                } => {
1297                    if let Some(ignored) = &ignore_effect_span
1298                        && *ignored == span
1299                    {
1300                        continue;
1301                    }
1302
1303                    let func = analysis_state
1304                        .link_value(*func, eval_context.imports.get_attributes(span))
1305                        .await?;
1306
1307                    handle_call(
1308                        &ast_path,
1309                        span,
1310                        func,
1311                        args,
1312                        &analysis_state,
1313                        &add_effects,
1314                        &mut analysis,
1315                        in_try,
1316                        new,
1317                    )
1318                    .await?;
1319                }
1320                Effect::MemberCall {
1321                    obj,
1322                    prop,
1323                    mut args,
1324                    ast_path,
1325                    span,
1326                    in_try,
1327                    new,
1328                } => {
1329                    if let Some(ignored) = &ignore_effect_span
1330                        && *ignored == span
1331                    {
1332                        continue;
1333                    }
1334
1335                    let func = analysis_state
1336                        .link_value(
1337                            JsValue::member(obj.clone(), prop),
1338                            eval_context.imports.get_attributes(span),
1339                        )
1340                        .await?;
1341
1342                    if !new
1343                        && matches!(
1344                            func,
1345                            JsValue::WellKnownFunction(
1346                                WellKnownFunctionKind::ArrayFilter
1347                                    | WellKnownFunctionKind::ArrayForEach
1348                                    | WellKnownFunctionKind::ArrayMap
1349                            )
1350                        )
1351                        && let [EffectArg::Closure(value, block)] = &mut args[..]
1352                        && let JsValue::Array {
1353                            items: ref mut values,
1354                            mutable,
1355                            ..
1356                        } = analysis_state
1357                            .link_value(*obj, eval_context.imports.get_attributes(span))
1358                            .await?
1359                    {
1360                        *value = analysis_state
1361                            .link_value(take(value), ImportAttributes::empty_ref())
1362                            .await?;
1363                        if let JsValue::Function(_, func_ident, _) = value {
1364                            let mut closure_arg = JsValue::alternatives(take(values));
1365                            if mutable {
1366                                closure_arg.add_unknown_mutations(true);
1367                            }
1368                            analysis_state
1369                                .fun_args_values
1370                                .get_mut()
1371                                .insert(*func_ident, vec![closure_arg]);
1372                            queue_stack.get_mut().push(Action::LeaveScope(*func_ident));
1373                            queue_stack.get_mut().extend(
1374                                take(&mut block.effects)
1375                                    .into_iter()
1376                                    .map(Action::Effect)
1377                                    .rev(),
1378                            );
1379                            continue;
1380                        }
1381                    }
1382
1383                    handle_call(
1384                        &ast_path,
1385                        span,
1386                        func,
1387                        args,
1388                        &analysis_state,
1389                        &add_effects,
1390                        &mut analysis,
1391                        in_try,
1392                        new,
1393                    )
1394                    .await?;
1395                }
1396                Effect::FreeVar {
1397                    var,
1398                    ast_path,
1399                    span,
1400                } => {
1401                    debug_assert!(
1402                        analyze_mode.is_code_gen(),
1403                        "unexpected Effect::FreeVar in tracing mode"
1404                    );
1405
1406                    if options.enable_exports_info_inlining && var == "__webpack_exports_info__" {
1407                        if analysis_state.first_webpack_exports_info {
1408                            analysis_state.first_webpack_exports_info = false;
1409                            analysis.add_code_gen(ExportsInfoBinding::new());
1410                        }
1411                        analysis.add_code_gen(ExportsInfoRef::new(ast_path.into()));
1412                        continue;
1413                    }
1414
1415                    // FreeVar("require") might be turbopackIgnore-d
1416                    if !analysis_state
1417                        .link_value(
1418                            JsValue::FreeVar(var.clone()),
1419                            eval_context.imports.get_attributes(span),
1420                        )
1421                        .await?
1422                        .is_unknown()
1423                    {
1424                        // Call handle free var
1425                        handle_free_var(
1426                            &ast_path,
1427                            JsValue::FreeVar(var),
1428                            span,
1429                            &analysis_state,
1430                            &mut analysis,
1431                        )
1432                        .await?;
1433                    }
1434                }
1435                Effect::Member {
1436                    obj,
1437                    prop,
1438                    ast_path,
1439                    span,
1440                } => {
1441                    debug_assert!(
1442                        analyze_mode.is_code_gen(),
1443                        "unexpected Effect::Member in tracing mode"
1444                    );
1445
1446                    // Intentionally not awaited because `handle_member` reads this only when needed
1447                    let obj = analysis_state.link_value(*obj, ImportAttributes::empty_ref());
1448
1449                    let prop = analysis_state
1450                        .link_value(*prop, ImportAttributes::empty_ref())
1451                        .await?;
1452
1453                    handle_member(&ast_path, obj, prop, span, &analysis_state, &mut analysis)
1454                        .await?;
1455                }
1456                Effect::ImportedBinding {
1457                    esm_reference_index,
1458                    export,
1459                    ast_path,
1460                    span: _,
1461                } => {
1462                    let Some(r) = import_references.get(esm_reference_index) else {
1463                        continue;
1464                    };
1465
1466                    if let Some("__turbopack_module_id__") = export.as_deref() {
1467                        analysis.add_reference_code_gen(
1468                            EsmModuleIdAssetReference::new(*r),
1469                            ast_path.into(),
1470                        )
1471                    } else {
1472                        if matches!(
1473                            options.tree_shaking_mode,
1474                            Some(TreeShakingMode::ReexportsOnly)
1475                        ) {
1476                            // TODO move this logic into Effect creation itself and don't create new
1477                            // references after the fact here.
1478                            let original_reference = r.await?;
1479                            if original_reference.export_name.is_none()
1480                                && export.is_some()
1481                                && let Some(export) = export
1482                            {
1483                                // Rewrite `import * as ns from 'foo'; foo.bar()` to behave like
1484                                // `import {bar} from 'foo'; bar()` for tree shaking purposes.
1485                                let named_reference = analysis
1486                                    .add_esm_reference_namespace_resolved(
1487                                        esm_reference_index,
1488                                        export.clone(),
1489                                        || {
1490                                            EsmAssetReference::new(
1491                                                original_reference.module,
1492                                                original_reference.origin,
1493                                                original_reference.request.clone(),
1494                                                original_reference.issue_source,
1495                                                original_reference.annotations.clone(),
1496                                                Some(ModulePart::export(export.clone())),
1497                                                // TODO this is correct, but an overapproximation.
1498                                                // We should have individual import_usage data for
1499                                                // each export. This would be fixed by moving this
1500                                                // logic earlier (see TODO above)
1501                                                original_reference.import_usage.clone(),
1502                                                original_reference.import_externals,
1503                                                original_reference.tree_shaking_mode,
1504                                            )
1505                                            .resolved_cell()
1506                                        },
1507                                    );
1508                                analysis.add_code_gen(EsmBinding::new_keep_this(
1509                                    named_reference,
1510                                    Some(export),
1511                                    ast_path.into(),
1512                                ));
1513                                continue;
1514                            }
1515                        }
1516
1517                        analysis.add_esm_reference(esm_reference_index);
1518                        analysis.add_code_gen(EsmBinding::new(*r, export, ast_path.into()));
1519                    }
1520                }
1521                Effect::TypeOf {
1522                    arg,
1523                    ast_path,
1524                    span,
1525                } => {
1526                    debug_assert!(
1527                        analyze_mode.is_code_gen(),
1528                        "unexpected Effect::TypeOf in tracing mode"
1529                    );
1530                    let arg = analysis_state
1531                        .link_value(*arg, ImportAttributes::empty_ref())
1532                        .await?;
1533                    handle_typeof(&ast_path, arg, span, &analysis_state, &mut analysis).await?;
1534                }
1535                Effect::ImportMeta { ast_path, span: _ } => {
1536                    debug_assert!(
1537                        analyze_mode.is_code_gen(),
1538                        "unexpected Effect::ImportMeta in tracing mode"
1539                    );
1540                    if analysis_state.first_import_meta {
1541                        analysis_state.first_import_meta = false;
1542                        analysis.add_code_gen(ImportMetaBinding::new(
1543                            source.ident().path().owned().await?,
1544                        ));
1545                    }
1546
1547                    analysis.add_code_gen(ImportMetaRef::new(ast_path.into()));
1548                }
1549            }
1550        }
1551        anyhow::Ok(())
1552    }
1553    .instrument(span)
1554    .await?;
1555
1556    analysis.set_successful(true);
1557
1558    collector.emit(false).await?;
1559
1560    analysis
1561        .build(
1562            import_references,
1563            matches!(
1564                options.tree_shaking_mode,
1565                Some(TreeShakingMode::ReexportsOnly)
1566            ),
1567        )
1568        .await
1569}
1570
1571#[turbo_tasks::function]
1572async fn compile_time_info_for_module_options(
1573    compile_time_info: Vc<CompileTimeInfo>,
1574    is_esm: bool,
1575    enable_typeof_window_inlining: Option<TypeofWindow>,
1576) -> Result<Vc<CompileTimeInfo>> {
1577    let compile_time_info = compile_time_info.await?;
1578    let free_var_references = compile_time_info.free_var_references;
1579
1580    let mut free_var_references = free_var_references.owned().await?;
1581    let (typeof_exports, typeof_module, require) = if is_esm {
1582        ("undefined", "undefined", TURBOPACK_REQUIRE_STUB)
1583    } else {
1584        ("object", "object", TURBOPACK_REQUIRE_REAL)
1585    };
1586    free_var_references
1587        .entry(vec![
1588            DefinableNameSegment::Name(rcstr!("import")),
1589            DefinableNameSegment::Name(rcstr!("meta")),
1590            DefinableNameSegment::TypeOf,
1591        ])
1592        .or_insert(rcstr!("object").into());
1593    free_var_references
1594        .entry(vec![
1595            DefinableNameSegment::Name(rcstr!("exports")),
1596            DefinableNameSegment::TypeOf,
1597        ])
1598        .or_insert(typeof_exports.into());
1599    free_var_references
1600        .entry(vec![
1601            DefinableNameSegment::Name(rcstr!("module")),
1602            DefinableNameSegment::TypeOf,
1603        ])
1604        .or_insert(typeof_module.into());
1605    free_var_references
1606        .entry(vec![
1607            DefinableNameSegment::Name(rcstr!("require")),
1608            DefinableNameSegment::TypeOf,
1609        ])
1610        .or_insert(rcstr!("function").into());
1611    free_var_references
1612        .entry(vec![DefinableNameSegment::Name(rcstr!("require"))])
1613        .or_insert(require.into());
1614
1615    let dir_name = rcstr!("__dirname");
1616    free_var_references
1617        .entry(vec![
1618            DefinableNameSegment::Name(dir_name.clone()),
1619            DefinableNameSegment::TypeOf,
1620        ])
1621        .or_insert(rcstr!("string").into());
1622    free_var_references
1623        .entry(vec![DefinableNameSegment::Name(dir_name)])
1624        .or_insert(FreeVarReference::InputRelative(
1625            InputRelativeConstant::DirName,
1626        ));
1627    let file_name = rcstr!("__filename");
1628
1629    free_var_references
1630        .entry(vec![
1631            DefinableNameSegment::Name(file_name.clone()),
1632            DefinableNameSegment::TypeOf,
1633        ])
1634        .or_insert(rcstr!("string").into());
1635    free_var_references
1636        .entry(vec![DefinableNameSegment::Name(file_name)])
1637        .or_insert(FreeVarReference::InputRelative(
1638            InputRelativeConstant::FileName,
1639        ));
1640
1641    // Compiletime rewrite the nodejs `global` to `__turbopack_context_.g` which is a shortcut for
1642    // `globalThis` that cannot be shadowed by a local variable.
1643    let global = rcstr!("global");
1644    free_var_references
1645        .entry(vec![
1646            DefinableNameSegment::Name(global.clone()),
1647            DefinableNameSegment::TypeOf,
1648        ])
1649        .or_insert(rcstr!("object").into());
1650    free_var_references
1651        .entry(vec![DefinableNameSegment::Name(global)])
1652        .or_insert(TURBOPACK_GLOBAL.into());
1653
1654    free_var_references.extend(TURBOPACK_RUNTIME_FUNCTION_SHORTCUTS.into_iter().map(
1655        |(name, shortcut)| {
1656            (
1657                vec![DefinableNameSegment::Name(name.into())],
1658                shortcut.into(),
1659            )
1660        },
1661    ));
1662    // A 'free' reference to `this` in an ESM module is meant to be `undefined`
1663    // Compile time replace it so we can represent module-factories as arrow functions without
1664    // needing to be defensive about rebinding this. Do the same for CJS modules while we are
1665    // here.
1666    let this = rcstr!("this");
1667    free_var_references
1668        .entry(vec![DefinableNameSegment::Name(this.clone())])
1669        .or_insert(if is_esm {
1670            FreeVarReference::Value(CompileTimeDefineValue::Undefined)
1671        } else {
1672            // Insert shortcut which is equivalent to `module.exports` but should
1673            // not be shadowed by user symbols.
1674            TURBOPACK_EXPORTS.into()
1675        });
1676    free_var_references
1677        .entry(vec![
1678            DefinableNameSegment::Name(this),
1679            DefinableNameSegment::TypeOf,
1680        ])
1681        .or_insert(if is_esm {
1682            rcstr!("undefined").into()
1683        } else {
1684            rcstr!("object").into()
1685        });
1686
1687    let mut defines = compile_time_info.defines;
1688    if let Some(enable_typeof_window_inlining) = enable_typeof_window_inlining {
1689        let value = match enable_typeof_window_inlining {
1690            TypeofWindow::Object => rcstr!("object"),
1691            TypeofWindow::Undefined => rcstr!("undefined"),
1692        };
1693        let window = rcstr!("window");
1694        let mut defines_value = defines.owned().await?;
1695        defines_value
1696            .entry(vec![
1697                DefinableNameSegment::Name(window.clone()),
1698                DefinableNameSegment::TypeOf,
1699            ])
1700            .or_insert(value.clone().into());
1701        free_var_references
1702            .entry(vec![
1703                DefinableNameSegment::Name(window),
1704                DefinableNameSegment::TypeOf,
1705            ])
1706            .or_insert(value.into());
1707        defines = CompileTimeDefines(defines_value).resolved_cell()
1708    }
1709
1710    Ok(CompileTimeInfo {
1711        environment: compile_time_info.environment,
1712        defines,
1713        free_var_references: FreeVarReferences(free_var_references).resolved_cell(),
1714    }
1715    .cell())
1716}
1717
1718async fn handle_call<G: Fn(Vec<Effect>) + Send + Sync>(
1719    ast_path: &[AstParentKind],
1720    span: Span,
1721    func: JsValue,
1722    args: Vec<EffectArg>,
1723    state: &AnalysisState<'_>,
1724    add_effects: &G,
1725    analysis: &mut AnalyzeEcmascriptModuleResultBuilder,
1726    in_try: bool,
1727    new: bool,
1728) -> Result<()> {
1729    let &AnalysisState {
1730        handler,
1731        origin,
1732        source,
1733        compile_time_info,
1734        ignore_dynamic_requests,
1735        url_rewrite_behavior,
1736        collect_affecting_sources,
1737        allow_project_root_tracing: _,
1738        ..
1739    } = state;
1740
1741    // Process all effects first so they happen exactly once.
1742    // If we end up modeling the behavior of the closures passed to any of these functions then we
1743    // will need to inline this into the appropriate spot just like Array.prototype.map support.
1744    let unlinked_args = args
1745        .into_iter()
1746        .map(|effect_arg| match effect_arg {
1747            EffectArg::Value(value) => value,
1748            EffectArg::Closure(value, block) => {
1749                add_effects(block.effects);
1750                value
1751            }
1752            EffectArg::Spread => JsValue::unknown_empty(true, "spread is not supported yet"),
1753        })
1754        .collect::<Vec<_>>();
1755
1756    // Create a OnceCell to cache linked args across multiple calls
1757    let linked_args_cache = OnceCell::new();
1758
1759    // Create the lazy linking closure that will be passed to handle_well_known_function_call
1760    let linked_args = || async {
1761        linked_args_cache
1762            .get_or_try_init(|| async {
1763                unlinked_args
1764                    .iter()
1765                    .cloned()
1766                    .map(|arg| state.link_value(arg, ImportAttributes::empty_ref()))
1767                    .try_join()
1768                    .await
1769            })
1770            .await
1771    };
1772
1773    match func {
1774        JsValue::Alternatives {
1775            total_nodes: _,
1776            values,
1777            logical_property: _,
1778        } => {
1779            for alt in values {
1780                if let JsValue::WellKnownFunction(wkf) = alt {
1781                    handle_well_known_function_call(
1782                        wkf,
1783                        new,
1784                        &linked_args,
1785                        handler,
1786                        span,
1787                        ignore_dynamic_requests,
1788                        analysis,
1789                        origin,
1790                        compile_time_info,
1791                        url_rewrite_behavior,
1792                        source,
1793                        ast_path,
1794                        in_try,
1795                        state,
1796                        collect_affecting_sources,
1797                    )
1798                    .await?;
1799                }
1800            }
1801        }
1802        JsValue::WellKnownFunction(wkf) => {
1803            handle_well_known_function_call(
1804                wkf,
1805                new,
1806                &linked_args,
1807                handler,
1808                span,
1809                ignore_dynamic_requests,
1810                analysis,
1811                origin,
1812                compile_time_info,
1813                url_rewrite_behavior,
1814                source,
1815                ast_path,
1816                in_try,
1817                state,
1818                collect_affecting_sources,
1819            )
1820            .await?;
1821        }
1822        _ => {}
1823    }
1824
1825    Ok(())
1826}
1827
1828async fn handle_well_known_function_call<'a, F, Fut>(
1829    func: WellKnownFunctionKind,
1830    new: bool,
1831    linked_args: &F,
1832    handler: &Handler,
1833    span: Span,
1834    ignore_dynamic_requests: bool,
1835    analysis: &mut AnalyzeEcmascriptModuleResultBuilder,
1836    origin: ResolvedVc<Box<dyn ResolveOrigin>>,
1837    compile_time_info: ResolvedVc<CompileTimeInfo>,
1838    url_rewrite_behavior: Option<UrlRewriteBehavior>,
1839    source: ResolvedVc<Box<dyn Source>>,
1840    ast_path: &[AstParentKind],
1841    in_try: bool,
1842    state: &'a AnalysisState<'a>,
1843    collect_affecting_sources: bool,
1844) -> Result<()>
1845where
1846    F: Fn() -> Fut,
1847    Fut: Future<Output = Result<&'a Vec<JsValue>>>,
1848{
1849    fn explain_args(args: &[JsValue]) -> (String, String) {
1850        JsValue::explain_args(args, 10, 2)
1851    }
1852
1853    let get_traced_project_dir = async || -> Result<FileSystemPath> {
1854        // readFileSync("./foo") should always be relative to the project root, but this is
1855        // dangerous inside of node_modules as it can cause a lot of false positives in the
1856        // tracing, if some package does `path.join(dynamic)`, it would include
1857        // everything from the project root as well.
1858        //
1859        // Also, when there's no cwd set (i.e. in a tracing-specific module context, as we
1860        // shouldn't assume a `process.cwd()` for all of node_modules), fallback to
1861        // the source file directory. This still allows relative file accesses, just
1862        // not from the project root.
1863        if state.allow_project_root_tracing
1864            && let Some(cwd) = compile_time_info.environment().cwd().owned().await?
1865        {
1866            Ok(cwd)
1867        } else {
1868            Ok(source.ident().path().await?.parent())
1869        }
1870    };
1871
1872    let get_issue_source =
1873        || IssueSource::from_swc_offsets(source, span.lo.to_u32(), span.hi.to_u32());
1874    if new {
1875        match func {
1876            WellKnownFunctionKind::URLConstructor => {
1877                let args = linked_args().await?;
1878                if let [
1879                    url,
1880                    JsValue::Member(
1881                        _,
1882                        box JsValue::WellKnownObject(WellKnownObjectKind::ImportMeta),
1883                        box JsValue::Constant(super::analyzer::ConstantValue::Str(meta_prop)),
1884                    ),
1885                ] = &args[..]
1886                    && meta_prop.as_str() == "url"
1887                {
1888                    let pat = js_value_to_pattern(url);
1889                    if !pat.has_constant_parts() {
1890                        let (args, hints) = explain_args(args);
1891                        handler.span_warn_with_code(
1892                            span,
1893                            &format!("new URL({args}) is very dynamic{hints}",),
1894                            DiagnosticId::Lint(
1895                                errors::failed_to_analyze::ecmascript::NEW_URL_IMPORT_META
1896                                    .to_string(),
1897                            ),
1898                        );
1899                        if ignore_dynamic_requests {
1900                            return Ok(());
1901                        }
1902                    }
1903                    analysis.add_reference_code_gen(
1904                        UrlAssetReference::new(
1905                            origin,
1906                            Request::parse(pat).to_resolved().await?,
1907                            *compile_time_info.environment().rendering().await?,
1908                            issue_source(source, span),
1909                            in_try,
1910                            url_rewrite_behavior.unwrap_or(UrlRewriteBehavior::Relative),
1911                        ),
1912                        ast_path.to_vec().into(),
1913                    );
1914                }
1915                return Ok(());
1916            }
1917            WellKnownFunctionKind::WorkerConstructor => {
1918                let args = linked_args().await?;
1919                if let Some(url @ JsValue::Url(_, JsValueUrlKind::Relative)) = args.first() {
1920                    let pat = js_value_to_pattern(url);
1921                    if !pat.has_constant_parts() {
1922                        let (args, hints) = explain_args(args);
1923                        handler.span_warn_with_code(
1924                            span,
1925                            &format!("new Worker({args}) is very dynamic{hints}",),
1926                            DiagnosticId::Lint(
1927                                errors::failed_to_analyze::ecmascript::NEW_WORKER.to_string(),
1928                            ),
1929                        );
1930                        if ignore_dynamic_requests {
1931                            return Ok(());
1932                        }
1933                    }
1934
1935                    if *compile_time_info.environment().rendering().await? == Rendering::Client {
1936                        analysis.add_reference_code_gen(
1937                            WorkerAssetReference::new(
1938                                origin,
1939                                Request::parse(pat).to_resolved().await?,
1940                                issue_source(source, span),
1941                                in_try,
1942                            ),
1943                            ast_path.to_vec().into(),
1944                        );
1945                    }
1946
1947                    return Ok(());
1948                }
1949                // Ignore (e.g. dynamic parameter or string literal), just as Webpack does
1950                return Ok(());
1951            }
1952            WellKnownFunctionKind::NodeWorkerConstructor if analysis.analyze_mode.is_tracing() => {
1953                // Only for tracing, not for bundling (yet?)
1954                let args = linked_args().await?;
1955                if !args.is_empty() {
1956                    let pat = js_value_to_pattern(&args[0]);
1957                    if !pat.has_constant_parts() {
1958                        let (args, hints) = explain_args(args);
1959                        handler.span_warn_with_code(
1960                            span,
1961                            &format!("new Worker({args}) is very dynamic{hints}",),
1962                            DiagnosticId::Lint(
1963                                errors::failed_to_analyze::ecmascript::NEW_WORKER.to_string(),
1964                            ),
1965                        );
1966                        if ignore_dynamic_requests {
1967                            return Ok(());
1968                        }
1969                    }
1970                    analysis.add_reference(
1971                        FilePathModuleReference::new(
1972                            origin.asset_context(),
1973                            get_traced_project_dir().await?,
1974                            Pattern::new(pat),
1975                            collect_affecting_sources,
1976                            get_issue_source(),
1977                        )
1978                        .to_resolved()
1979                        .await?,
1980                    );
1981                    return Ok(());
1982                }
1983                let (args, hints) = explain_args(args);
1984                handler.span_warn_with_code(
1985                    span,
1986                    &format!("new Worker({args}) is not statically analyze-able{hints}",),
1987                    DiagnosticId::Error(
1988                        errors::failed_to_analyze::ecmascript::FS_METHOD.to_string(),
1989                    ),
1990                );
1991                // Ignore (e.g. dynamic parameter or string literal)
1992                return Ok(());
1993            }
1994            _ => {}
1995        }
1996
1997        return Ok(());
1998    }
1999
2000    match func {
2001        WellKnownFunctionKind::Import => {
2002            let args = linked_args().await?;
2003            if args.len() == 1 || args.len() == 2 {
2004                let pat = js_value_to_pattern(&args[0]);
2005                let options = args.get(1);
2006                let import_annotations = options
2007                    .and_then(|options| {
2008                        if let JsValue::Object { parts, .. } = options {
2009                            parts.iter().find_map(|part| {
2010                                if let ObjectPart::KeyValue(
2011                                    JsValue::Constant(super::analyzer::ConstantValue::Str(key)),
2012                                    value,
2013                                ) = part
2014                                    && key.as_str() == "with"
2015                                {
2016                                    return Some(value);
2017                                }
2018                                None
2019                            })
2020                        } else {
2021                            None
2022                        }
2023                    })
2024                    .and_then(ImportAnnotations::parse_dynamic)
2025                    .unwrap_or_default();
2026                if !pat.has_constant_parts() {
2027                    let (args, hints) = explain_args(args);
2028                    handler.span_warn_with_code(
2029                        span,
2030                        &format!("import({args}) is very dynamic{hints}",),
2031                        DiagnosticId::Lint(
2032                            errors::failed_to_analyze::ecmascript::DYNAMIC_IMPORT.to_string(),
2033                        ),
2034                    );
2035                    if ignore_dynamic_requests {
2036                        analysis
2037                            .add_code_gen(DynamicExpression::new_promise(ast_path.to_vec().into()));
2038                        return Ok(());
2039                    }
2040                }
2041                analysis.add_reference_code_gen(
2042                    EsmAsyncAssetReference::new(
2043                        origin,
2044                        Request::parse(pat).to_resolved().await?,
2045                        issue_source(source, span),
2046                        import_annotations,
2047                        in_try,
2048                        state.import_externals,
2049                    ),
2050                    ast_path.to_vec().into(),
2051                );
2052                return Ok(());
2053            }
2054            let (args, hints) = explain_args(args);
2055            handler.span_warn_with_code(
2056                span,
2057                &format!("import({args}) is not statically analyze-able{hints}",),
2058                DiagnosticId::Error(
2059                    errors::failed_to_analyze::ecmascript::DYNAMIC_IMPORT.to_string(),
2060                ),
2061            )
2062        }
2063        WellKnownFunctionKind::Require => {
2064            let args = linked_args().await?;
2065            if args.len() == 1 {
2066                let pat = js_value_to_pattern(&args[0]);
2067                if !pat.has_constant_parts() {
2068                    let (args, hints) = explain_args(args);
2069                    handler.span_warn_with_code(
2070                        span,
2071                        &format!("require({args}) is very dynamic{hints}",),
2072                        DiagnosticId::Lint(
2073                            errors::failed_to_analyze::ecmascript::REQUIRE.to_string(),
2074                        ),
2075                    );
2076                    if ignore_dynamic_requests {
2077                        analysis.add_code_gen(DynamicExpression::new(ast_path.to_vec().into()));
2078                        return Ok(());
2079                    }
2080                }
2081                analysis.add_reference_code_gen(
2082                    CjsRequireAssetReference::new(
2083                        origin,
2084                        Request::parse(pat).to_resolved().await?,
2085                        issue_source(source, span),
2086                        in_try,
2087                    ),
2088                    ast_path.to_vec().into(),
2089                );
2090                return Ok(());
2091            }
2092            let (args, hints) = explain_args(args);
2093            handler.span_warn_with_code(
2094                span,
2095                &format!("require({args}) is not statically analyze-able{hints}",),
2096                DiagnosticId::Error(errors::failed_to_analyze::ecmascript::REQUIRE.to_string()),
2097            )
2098        }
2099        WellKnownFunctionKind::Define => {
2100            analyze_amd_define(
2101                source,
2102                analysis,
2103                origin,
2104                handler,
2105                span,
2106                ast_path,
2107                linked_args().await?,
2108                in_try,
2109            )
2110            .await?;
2111        }
2112
2113        WellKnownFunctionKind::RequireResolve => {
2114            let args = linked_args().await?;
2115            if args.len() == 1 || args.len() == 2 {
2116                // TODO error TP1003 require.resolve(???*0*, {"paths": [???*1*]}) is not
2117                // statically analyze-able with ignore_dynamic_requests =
2118                // true
2119                let pat = js_value_to_pattern(&args[0]);
2120                if !pat.has_constant_parts() {
2121                    let (args, hints) = explain_args(args);
2122                    handler.span_warn_with_code(
2123                        span,
2124                        &format!("require.resolve({args}) is very dynamic{hints}",),
2125                        DiagnosticId::Lint(
2126                            errors::failed_to_analyze::ecmascript::REQUIRE_RESOLVE.to_string(),
2127                        ),
2128                    );
2129                    if ignore_dynamic_requests {
2130                        analysis.add_code_gen(DynamicExpression::new(ast_path.to_vec().into()));
2131                        return Ok(());
2132                    }
2133                }
2134                analysis.add_reference_code_gen(
2135                    CjsRequireResolveAssetReference::new(
2136                        origin,
2137                        Request::parse(pat).to_resolved().await?,
2138                        issue_source(source, span),
2139                        in_try,
2140                    ),
2141                    ast_path.to_vec().into(),
2142                );
2143                return Ok(());
2144            }
2145            let (args, hints) = explain_args(args);
2146            handler.span_warn_with_code(
2147                span,
2148                &format!("require.resolve({args}) is not statically analyze-able{hints}",),
2149                DiagnosticId::Error(
2150                    errors::failed_to_analyze::ecmascript::REQUIRE_RESOLVE.to_string(),
2151                ),
2152            )
2153        }
2154
2155        WellKnownFunctionKind::RequireContext => {
2156            let args = linked_args().await?;
2157            let options = match parse_require_context(args) {
2158                Ok(options) => options,
2159                Err(err) => {
2160                    let (args, hints) = explain_args(args);
2161                    handler.span_err_with_code(
2162                        span,
2163                        &format!(
2164                            "require.context({args}) is not statically analyze-able: {}{hints}",
2165                            PrettyPrintError(&err)
2166                        ),
2167                        DiagnosticId::Error(
2168                            errors::failed_to_analyze::ecmascript::REQUIRE_CONTEXT.to_string(),
2169                        ),
2170                    );
2171                    return Ok(());
2172                }
2173            };
2174
2175            analysis.add_reference_code_gen(
2176                RequireContextAssetReference::new(
2177                    source,
2178                    origin,
2179                    options.dir,
2180                    options.include_subdirs,
2181                    options.filter.cell(),
2182                    Some(issue_source(source, span)),
2183                    in_try,
2184                )
2185                .await?,
2186                ast_path.to_vec().into(),
2187            );
2188        }
2189
2190        WellKnownFunctionKind::FsReadMethod(name) if analysis.analyze_mode.is_tracing() => {
2191            let args = linked_args().await?;
2192            if !args.is_empty() {
2193                let pat = js_value_to_pattern(&args[0]);
2194                if !pat.has_constant_parts() {
2195                    let (args, hints) = explain_args(args);
2196                    handler.span_warn_with_code(
2197                        span,
2198                        &format!("fs.{name}({args}) is very dynamic{hints}",),
2199                        DiagnosticId::Lint(
2200                            errors::failed_to_analyze::ecmascript::FS_METHOD.to_string(),
2201                        ),
2202                    );
2203                    if ignore_dynamic_requests {
2204                        return Ok(());
2205                    }
2206                }
2207                analysis.add_reference(
2208                    FileSourceReference::new(
2209                        get_traced_project_dir().await?,
2210                        Pattern::new(pat),
2211                        collect_affecting_sources,
2212                        get_issue_source(),
2213                    )
2214                    .to_resolved()
2215                    .await?,
2216                );
2217                return Ok(());
2218            }
2219            let (args, hints) = explain_args(args);
2220            handler.span_warn_with_code(
2221                span,
2222                &format!("fs.{name}({args}) is not statically analyze-able{hints}",),
2223                DiagnosticId::Error(errors::failed_to_analyze::ecmascript::FS_METHOD.to_string()),
2224            )
2225        }
2226
2227        WellKnownFunctionKind::PathResolve(..) if analysis.analyze_mode.is_tracing() => {
2228            let parent_path = origin.origin_path().owned().await?.parent();
2229            let args = linked_args().await?;
2230
2231            let linked_func_call = state
2232                .link_value(
2233                    JsValue::call(
2234                        Box::new(JsValue::WellKnownFunction(
2235                            WellKnownFunctionKind::PathResolve(Box::new(
2236                                parent_path.path.as_str().into(),
2237                            )),
2238                        )),
2239                        args.clone(),
2240                    ),
2241                    ImportAttributes::empty_ref(),
2242                )
2243                .await?;
2244
2245            let pat = js_value_to_pattern(&linked_func_call);
2246            if !pat.has_constant_parts() {
2247                let (args, hints) = explain_args(args);
2248                handler.span_warn_with_code(
2249                    span,
2250                    &format!("path.resolve({args}) is very dynamic{hints}",),
2251                    DiagnosticId::Lint(
2252                        errors::failed_to_analyze::ecmascript::PATH_METHOD.to_string(),
2253                    ),
2254                );
2255                if ignore_dynamic_requests {
2256                    return Ok(());
2257                }
2258            }
2259            analysis.add_reference(
2260                DirAssetReference::new(
2261                    get_traced_project_dir().await?,
2262                    Pattern::new(pat),
2263                    get_issue_source(),
2264                )
2265                .to_resolved()
2266                .await?,
2267            );
2268            return Ok(());
2269        }
2270
2271        WellKnownFunctionKind::PathJoin if analysis.analyze_mode.is_tracing() => {
2272            let context_path = source.ident().path().await?;
2273            // ignore path.join in `node-gyp`, it will includes too many files
2274            if context_path.path.contains("node_modules/node-gyp") {
2275                return Ok(());
2276            }
2277            let args = linked_args().await?;
2278            let linked_func_call = state
2279                .link_value(
2280                    JsValue::call(
2281                        Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::PathJoin)),
2282                        args.clone(),
2283                    ),
2284                    ImportAttributes::empty_ref(),
2285                )
2286                .await?;
2287            let pat = js_value_to_pattern(&linked_func_call);
2288            if !pat.has_constant_parts() {
2289                let (args, hints) = explain_args(args);
2290                handler.span_warn_with_code(
2291                    span,
2292                    &format!("path.join({args}) is very dynamic{hints}",),
2293                    DiagnosticId::Lint(
2294                        errors::failed_to_analyze::ecmascript::PATH_METHOD.to_string(),
2295                    ),
2296                );
2297                if ignore_dynamic_requests {
2298                    return Ok(());
2299                }
2300            }
2301            analysis.add_reference(
2302                DirAssetReference::new(
2303                    get_traced_project_dir().await?,
2304                    Pattern::new(pat),
2305                    get_issue_source(),
2306                )
2307                .to_resolved()
2308                .await?,
2309            );
2310            return Ok(());
2311        }
2312        WellKnownFunctionKind::ChildProcessSpawnMethod(name)
2313            if analysis.analyze_mode.is_tracing() =>
2314        {
2315            let args = linked_args().await?;
2316
2317            // Is this specifically `spawn(process.argv[0], ['-e', ...])`?
2318            if is_invoking_node_process_eval(args) {
2319                return Ok(());
2320            }
2321
2322            if !args.is_empty() {
2323                let mut show_dynamic_warning = false;
2324                let pat = js_value_to_pattern(&args[0]);
2325                if pat.is_match_ignore_dynamic("node") && args.len() >= 2 {
2326                    let first_arg =
2327                        JsValue::member(Box::new(args[1].clone()), Box::new(0_f64.into()));
2328                    let first_arg = state
2329                        .link_value(first_arg, ImportAttributes::empty_ref())
2330                        .await?;
2331                    let pat = js_value_to_pattern(&first_arg);
2332                    let dynamic = !pat.has_constant_parts();
2333                    if dynamic {
2334                        show_dynamic_warning = true;
2335                    }
2336                    if !dynamic || !ignore_dynamic_requests {
2337                        analysis.add_reference(
2338                            CjsAssetReference::new(
2339                                *origin,
2340                                Request::parse(pat),
2341                                issue_source(source, span),
2342                                in_try,
2343                            )
2344                            .to_resolved()
2345                            .await?,
2346                        );
2347                    }
2348                }
2349                let dynamic = !pat.has_constant_parts();
2350                if dynamic {
2351                    show_dynamic_warning = true;
2352                }
2353                if !dynamic || !ignore_dynamic_requests {
2354                    analysis.add_reference(
2355                        FileSourceReference::new(
2356                            get_traced_project_dir().await?,
2357                            Pattern::new(pat),
2358                            collect_affecting_sources,
2359                            IssueSource::from_swc_offsets(
2360                                source,
2361                                span.lo.to_u32(),
2362                                span.hi.to_u32(),
2363                            ),
2364                        )
2365                        .to_resolved()
2366                        .await?,
2367                    );
2368                }
2369                if show_dynamic_warning {
2370                    let (args, hints) = explain_args(args);
2371                    handler.span_warn_with_code(
2372                        span,
2373                        &format!("child_process.{name}({args}) is very dynamic{hints}",),
2374                        DiagnosticId::Lint(
2375                            errors::failed_to_analyze::ecmascript::CHILD_PROCESS_SPAWN.to_string(),
2376                        ),
2377                    );
2378                }
2379                return Ok(());
2380            }
2381            let (args, hints) = explain_args(args);
2382            handler.span_warn_with_code(
2383                span,
2384                &format!("child_process.{name}({args}) is not statically analyze-able{hints}",),
2385                DiagnosticId::Error(
2386                    errors::failed_to_analyze::ecmascript::CHILD_PROCESS_SPAWN.to_string(),
2387                ),
2388            )
2389        }
2390        WellKnownFunctionKind::ChildProcessFork if analysis.analyze_mode.is_tracing() => {
2391            let args = linked_args().await?;
2392            if !args.is_empty() {
2393                let first_arg = &args[0];
2394                let pat = js_value_to_pattern(first_arg);
2395                if !pat.has_constant_parts() {
2396                    let (args, hints) = explain_args(args);
2397                    handler.span_warn_with_code(
2398                        span,
2399                        &format!("child_process.fork({args}) is very dynamic{hints}",),
2400                        DiagnosticId::Lint(
2401                            errors::failed_to_analyze::ecmascript::CHILD_PROCESS_SPAWN.to_string(),
2402                        ),
2403                    );
2404                    if ignore_dynamic_requests {
2405                        return Ok(());
2406                    }
2407                }
2408                analysis.add_reference(
2409                    CjsAssetReference::new(
2410                        *origin,
2411                        Request::parse(pat),
2412                        issue_source(source, span),
2413                        in_try,
2414                    )
2415                    .to_resolved()
2416                    .await?,
2417                );
2418                return Ok(());
2419            }
2420            let (args, hints) = explain_args(args);
2421            handler.span_warn_with_code(
2422                span,
2423                &format!("child_process.fork({args}) is not statically analyze-able{hints}",),
2424                DiagnosticId::Error(
2425                    errors::failed_to_analyze::ecmascript::CHILD_PROCESS_SPAWN.to_string(),
2426                ),
2427            )
2428        }
2429        WellKnownFunctionKind::NodePreGypFind if analysis.analyze_mode.is_tracing() => {
2430            use turbopack_resolve::node_native_binding::NodePreGypConfigReference;
2431
2432            let args = linked_args().await?;
2433            if args.len() == 1 {
2434                let first_arg = &args[0];
2435                let pat = js_value_to_pattern(first_arg);
2436                if !pat.has_constant_parts() {
2437                    let (args, hints) = explain_args(args);
2438                    handler.span_warn_with_code(
2439                        span,
2440                        &format!("node-pre-gyp.find({args}) is very dynamic{hints}",),
2441                        DiagnosticId::Lint(
2442                            errors::failed_to_analyze::ecmascript::NODE_PRE_GYP_FIND.to_string(),
2443                        ),
2444                    );
2445                    // Always ignore this dynamic request
2446                    return Ok(());
2447                }
2448                analysis.add_reference(
2449                    NodePreGypConfigReference::new(
2450                        origin.origin_path().await?.parent(),
2451                        Pattern::new(pat),
2452                        compile_time_info.environment().compile_target(),
2453                        collect_affecting_sources,
2454                    )
2455                    .to_resolved()
2456                    .await?,
2457                );
2458                return Ok(());
2459            }
2460            let (args, hints) = explain_args(args);
2461            handler.span_warn_with_code(
2462                span,
2463                &format!(
2464                    "require('@mapbox/node-pre-gyp').find({args}) is not statically \
2465                     analyze-able{hints}",
2466                ),
2467                DiagnosticId::Error(
2468                    errors::failed_to_analyze::ecmascript::NODE_PRE_GYP_FIND.to_string(),
2469                ),
2470            )
2471        }
2472        WellKnownFunctionKind::NodeGypBuild if analysis.analyze_mode.is_tracing() => {
2473            use turbopack_resolve::node_native_binding::NodeGypBuildReference;
2474
2475            let args = linked_args().await?;
2476            if args.len() == 1 {
2477                let first_arg = state
2478                    .link_value(args[0].clone(), ImportAttributes::empty_ref())
2479                    .await?;
2480                if let Some(s) = first_arg.as_str() {
2481                    // TODO this resolving should happen within Vc<NodeGypBuildReference>
2482                    let current_context = origin
2483                        .origin_path()
2484                        .await?
2485                        .root()
2486                        .await?
2487                        .join(s.trim_start_matches("/ROOT/"))?;
2488                    analysis.add_reference(
2489                        NodeGypBuildReference::new(
2490                            current_context,
2491                            collect_affecting_sources,
2492                            compile_time_info.environment().compile_target(),
2493                        )
2494                        .to_resolved()
2495                        .await?,
2496                    );
2497                    return Ok(());
2498                }
2499            }
2500            let (args, hints) = explain_args(args);
2501            handler.span_warn_with_code(
2502                    span,
2503                    &format!(
2504                        "require('node-gyp-build')({args}) is not statically analyze-able{hints}",
2505                    ),
2506                    DiagnosticId::Error(
2507                        errors::failed_to_analyze::ecmascript::NODE_GYP_BUILD.to_string(),
2508                    ),
2509                )
2510        }
2511        WellKnownFunctionKind::NodeBindings if analysis.analyze_mode.is_tracing() => {
2512            use turbopack_resolve::node_native_binding::NodeBindingsReference;
2513
2514            let args = linked_args().await?;
2515            if args.len() == 1 {
2516                let first_arg = state
2517                    .link_value(args[0].clone(), ImportAttributes::empty_ref())
2518                    .await?;
2519                if let Some(s) = first_arg.as_str() {
2520                    analysis.add_reference(
2521                        NodeBindingsReference::new(
2522                            origin.origin_path().owned().await?,
2523                            s.into(),
2524                            collect_affecting_sources,
2525                        )
2526                        .to_resolved()
2527                        .await?,
2528                    );
2529                    return Ok(());
2530                }
2531            }
2532            let (args, hints) = explain_args(args);
2533            handler.span_warn_with_code(
2534                span,
2535                &format!("require('bindings')({args}) is not statically analyze-able{hints}",),
2536                DiagnosticId::Error(
2537                    errors::failed_to_analyze::ecmascript::NODE_BINDINGS.to_string(),
2538                ),
2539            )
2540        }
2541        WellKnownFunctionKind::NodeExpressSet if analysis.analyze_mode.is_tracing() => {
2542            let args = linked_args().await?;
2543            if args.len() == 2
2544                && let Some(s) = args.first().and_then(|arg| arg.as_str())
2545            {
2546                let pkg_or_dir = args.get(1).unwrap();
2547                let pat = js_value_to_pattern(pkg_or_dir);
2548                if !pat.has_constant_parts() {
2549                    let (args, hints) = explain_args(args);
2550                    handler.span_warn_with_code(
2551                        span,
2552                        &format!("require('express')().set({args}) is very dynamic{hints}",),
2553                        DiagnosticId::Lint(
2554                            errors::failed_to_analyze::ecmascript::NODE_EXPRESS.to_string(),
2555                        ),
2556                    );
2557                    // Always ignore this dynamic request
2558                    return Ok(());
2559                }
2560                match s {
2561                    "views" => {
2562                        if let Pattern::Constant(p) = &pat {
2563                            let abs_pattern = if p.starts_with("/ROOT/") {
2564                                pat
2565                            } else {
2566                                let linked_func_call = state
2567                                    .link_value(
2568                                        JsValue::call(
2569                                            Box::new(JsValue::WellKnownFunction(
2570                                                WellKnownFunctionKind::PathJoin,
2571                                            )),
2572                                            vec![
2573                                                JsValue::FreeVar(atom!("__dirname")),
2574                                                pkg_or_dir.clone(),
2575                                            ],
2576                                        ),
2577                                        ImportAttributes::empty_ref(),
2578                                    )
2579                                    .await?;
2580                                js_value_to_pattern(&linked_func_call)
2581                            };
2582                            analysis.add_reference(
2583                                DirAssetReference::new(
2584                                    get_traced_project_dir().await?,
2585                                    Pattern::new(abs_pattern),
2586                                    get_issue_source(),
2587                                )
2588                                .to_resolved()
2589                                .await?,
2590                            );
2591                            return Ok(());
2592                        }
2593                    }
2594                    "view engine" => {
2595                        if let Some(pkg) = pkg_or_dir.as_str() {
2596                            if pkg != "html" {
2597                                let pat = js_value_to_pattern(pkg_or_dir);
2598                                analysis.add_reference(
2599                                    CjsAssetReference::new(
2600                                        *origin,
2601                                        Request::parse(pat),
2602                                        issue_source(source, span),
2603                                        in_try,
2604                                    )
2605                                    .to_resolved()
2606                                    .await?,
2607                                );
2608                            }
2609                            return Ok(());
2610                        }
2611                    }
2612                    _ => {}
2613                }
2614            }
2615            let (args, hints) = explain_args(args);
2616            handler.span_warn_with_code(
2617                span,
2618                &format!("require('express')().set({args}) is not statically analyze-able{hints}",),
2619                DiagnosticId::Error(
2620                    errors::failed_to_analyze::ecmascript::NODE_EXPRESS.to_string(),
2621                ),
2622            )
2623        }
2624        WellKnownFunctionKind::NodeStrongGlobalizeSetRootDir
2625            if analysis.analyze_mode.is_tracing() =>
2626        {
2627            let args = linked_args().await?;
2628            if let Some(p) = args.first().and_then(|arg| arg.as_str()) {
2629                let abs_pattern = if p.starts_with("/ROOT/") {
2630                    Pattern::Constant(format!("{p}/intl").into())
2631                } else {
2632                    let linked_func_call = state
2633                        .link_value(
2634                            JsValue::call(
2635                                Box::new(JsValue::WellKnownFunction(
2636                                    WellKnownFunctionKind::PathJoin,
2637                                )),
2638                                vec![
2639                                    JsValue::FreeVar(atom!("__dirname")),
2640                                    p.into(),
2641                                    atom!("intl").into(),
2642                                ],
2643                            ),
2644                            ImportAttributes::empty_ref(),
2645                        )
2646                        .await?;
2647                    js_value_to_pattern(&linked_func_call)
2648                };
2649                analysis.add_reference(
2650                    DirAssetReference::new(
2651                        get_traced_project_dir().await?,
2652                        Pattern::new(abs_pattern),
2653                        get_issue_source(),
2654                    )
2655                    .to_resolved()
2656                    .await?,
2657                );
2658                return Ok(());
2659            }
2660            let (args, hints) = explain_args(args);
2661            handler.span_warn_with_code(
2662                span,
2663                &format!(
2664                    "require('strong-globalize').SetRootDir({args}) is not statically \
2665                     analyze-able{hints}",
2666                ),
2667                DiagnosticId::Error(
2668                    errors::failed_to_analyze::ecmascript::NODE_GYP_BUILD.to_string(),
2669                ),
2670            )
2671        }
2672        WellKnownFunctionKind::NodeResolveFrom if analysis.analyze_mode.is_tracing() => {
2673            let args = linked_args().await?;
2674            if args.len() == 2 && args.get(1).and_then(|arg| arg.as_str()).is_some() {
2675                analysis.add_reference(
2676                    CjsAssetReference::new(
2677                        *origin,
2678                        Request::parse(js_value_to_pattern(&args[1])),
2679                        issue_source(source, span),
2680                        in_try,
2681                    )
2682                    .to_resolved()
2683                    .await?,
2684                );
2685                return Ok(());
2686            }
2687            let (args, hints) = explain_args(args);
2688            handler.span_warn_with_code(
2689                span,
2690                &format!("require('resolve-from')({args}) is not statically analyze-able{hints}",),
2691                DiagnosticId::Error(
2692                    errors::failed_to_analyze::ecmascript::NODE_RESOLVE_FROM.to_string(),
2693                ),
2694            )
2695        }
2696        WellKnownFunctionKind::NodeProtobufLoad if analysis.analyze_mode.is_tracing() => {
2697            let args = linked_args().await?;
2698            if args.len() == 2
2699                && let Some(JsValue::Object { parts, .. }) = args.get(1)
2700            {
2701                let context_dir = get_traced_project_dir().await?;
2702                let resolved_dirs = parts
2703                    .iter()
2704                    .filter_map(|object_part| match object_part {
2705                        ObjectPart::KeyValue(
2706                            JsValue::Constant(key),
2707                            JsValue::Array { items: dirs, .. },
2708                        ) if key.as_str() == Some("includeDirs") => {
2709                            Some(dirs.iter().filter_map(|dir| dir.as_str()))
2710                        }
2711                        _ => None,
2712                    })
2713                    .flatten()
2714                    .map(|dir| {
2715                        DirAssetReference::new(
2716                            context_dir.clone(),
2717                            Pattern::new(Pattern::Constant(dir.into())),
2718                            get_issue_source(),
2719                        )
2720                        .to_resolved()
2721                    })
2722                    .try_join()
2723                    .await?;
2724
2725                for resolved_dir_ref in resolved_dirs {
2726                    analysis.add_reference(resolved_dir_ref);
2727                }
2728
2729                return Ok(());
2730            }
2731            let (args, hints) = explain_args(args);
2732            handler.span_warn_with_code(
2733                span,
2734                &format!(
2735                    "require('@grpc/proto-loader').load({args}) is not statically \
2736                     analyze-able{hints}",
2737                ),
2738                DiagnosticId::Error(
2739                    errors::failed_to_analyze::ecmascript::NODE_PROTOBUF_LOADER.to_string(),
2740                ),
2741            )
2742        }
2743        _ => {}
2744    };
2745    Ok(())
2746}
2747async fn handle_member(
2748    ast_path: &[AstParentKind],
2749    link_obj: impl Future<Output = Result<JsValue>> + Send + Sync,
2750    prop: JsValue,
2751    span: Span,
2752    state: &AnalysisState<'_>,
2753    analysis: &mut AnalyzeEcmascriptModuleResultBuilder,
2754) -> Result<()> {
2755    if let Some(prop) = prop.as_str() {
2756        let prop_seg = DefinableNameSegment::Name(prop.into());
2757
2758        let references = state.free_var_references.get(&prop_seg);
2759        let is_prop_cache = prop == "cache";
2760
2761        // This isn't pretty, but we cannot await the future twice in the two branches below.
2762        let obj = if references.is_some() || is_prop_cache {
2763            Some(link_obj.await?)
2764        } else {
2765            None
2766        };
2767
2768        if let Some(references) = references {
2769            let obj = obj.as_ref().unwrap();
2770            if let Some(def_name_len) = obj.get_definable_name_len() {
2771                for (name, value) in &references.0 {
2772                    if name.len() != def_name_len {
2773                        continue;
2774                    }
2775
2776                    let it = name.iter().map(Cow::Borrowed).rev();
2777                    if it.eq(obj.iter_definable_name_rev())
2778                        && handle_free_var_reference(
2779                            ast_path,
2780                            &*value.await?,
2781                            span,
2782                            state,
2783                            analysis,
2784                        )
2785                        .await?
2786                    {
2787                        return Ok(());
2788                    }
2789                }
2790            }
2791        }
2792
2793        if is_prop_cache
2794            && let JsValue::WellKnownFunction(WellKnownFunctionKind::Require) =
2795                obj.as_ref().unwrap()
2796        {
2797            analysis.add_code_gen(CjsRequireCacheAccess::new(ast_path.to_vec().into()));
2798        }
2799    }
2800
2801    Ok(())
2802}
2803
2804async fn handle_typeof(
2805    ast_path: &[AstParentKind],
2806    arg: JsValue,
2807    span: Span,
2808    state: &AnalysisState<'_>,
2809    analysis: &mut AnalyzeEcmascriptModuleResultBuilder,
2810) -> Result<()> {
2811    if let Some(value) = arg.match_free_var_reference(
2812        state.var_graph,
2813        &state.free_var_references,
2814        &DefinableNameSegment::TypeOf,
2815    ) {
2816        handle_free_var_reference(ast_path, &*value.await?, span, state, analysis).await?;
2817    }
2818
2819    Ok(())
2820}
2821
2822async fn handle_free_var(
2823    ast_path: &[AstParentKind],
2824    var: JsValue,
2825    span: Span,
2826    state: &AnalysisState<'_>,
2827    analysis: &mut AnalyzeEcmascriptModuleResultBuilder,
2828) -> Result<()> {
2829    if let Some(def_name_len) = var.get_definable_name_len() {
2830        let first = var.iter_definable_name_rev().next().unwrap();
2831        if let Some(references) = state.free_var_references.get(&*first) {
2832            for (name, value) in &references.0 {
2833                if name.len() + 1 != def_name_len {
2834                    continue;
2835                }
2836
2837                let it = name.iter().map(Cow::Borrowed).rev();
2838                if it.eq(var.iter_definable_name_rev().skip(1)) {
2839                    handle_free_var_reference(ast_path, &*value.await?, span, state, analysis)
2840                        .await?;
2841                    return Ok(());
2842                }
2843            }
2844        }
2845    }
2846
2847    Ok(())
2848}
2849
2850async fn handle_free_var_reference(
2851    ast_path: &[AstParentKind],
2852    value: &FreeVarReference,
2853    span: Span,
2854    state: &AnalysisState<'_>,
2855    analysis: &mut AnalyzeEcmascriptModuleResultBuilder,
2856) -> Result<bool> {
2857    // We don't want to replace assignments as this would lead to invalid code.
2858    if matches!(
2859        ast_path,
2860        // Matches assignments to members
2861        [
2862            ..,
2863            AstParentKind::AssignExpr(AssignExprField::Left),
2864            AstParentKind::AssignTarget(AssignTargetField::Simple),
2865            AstParentKind::SimpleAssignTarget(SimpleAssignTargetField::Member),
2866        ] |
2867        // Matches assignments to identifiers
2868        [
2869            ..,
2870            AstParentKind::AssignExpr(AssignExprField::Left),
2871            AstParentKind::AssignTarget(AssignTargetField::Simple),
2872            AstParentKind::SimpleAssignTarget(SimpleAssignTargetField::Ident),
2873            AstParentKind::BindingIdent(BindingIdentField::Id),
2874        ]
2875    ) {
2876        return Ok(false);
2877    }
2878
2879    match value {
2880        FreeVarReference::Error(error_message) => state.handler.span_err_with_code(
2881            span,
2882            error_message,
2883            DiagnosticId::Error(
2884                errors::failed_to_analyze::ecmascript::FREE_VAR_REFERENCE.to_string(),
2885            ),
2886        ),
2887        FreeVarReference::Value(value) => {
2888            analysis.add_code_gen(ConstantValueCodeGen::new(
2889                value.clone(),
2890                ast_path.to_vec().into(),
2891            ));
2892        }
2893        FreeVarReference::Ident(value) => {
2894            analysis.add_code_gen(IdentReplacement::new(
2895                value.clone(),
2896                ast_path.to_vec().into(),
2897            ));
2898        }
2899        FreeVarReference::Member(key, value) => {
2900            analysis.add_code_gen(MemberReplacement::new(
2901                key.clone(),
2902                value.clone(),
2903                ast_path.to_vec().into(),
2904            ));
2905        }
2906        FreeVarReference::EcmaScriptModule {
2907            request,
2908            lookup_path,
2909            export,
2910        } => {
2911            let esm_reference = analysis
2912                .add_esm_reference_free_var(request.clone(), async || {
2913                    // There would be no import in the first place if you don't reference the given
2914                    // free var (e.g. `process`). This means that it's also fine to remove the
2915                    // import again if the variable reference turns out be dead code in some later
2916                    // stage of the build, thus mark the import call as /*@__PURE__*/.
2917                    Ok(EsmAssetReference::new_pure(
2918                        state.module,
2919                        if let Some(lookup_path) = lookup_path {
2920                            ResolvedVc::upcast(
2921                                PlainResolveOrigin::new(
2922                                    state.origin.asset_context(),
2923                                    lookup_path.clone(),
2924                                )
2925                                .to_resolved()
2926                                .await?,
2927                            )
2928                        } else {
2929                            state.origin
2930                        },
2931                        request.clone(),
2932                        IssueSource::from_swc_offsets(
2933                            state.source,
2934                            span.lo.to_u32(),
2935                            span.hi.to_u32(),
2936                        ),
2937                        Default::default(),
2938                        match state.tree_shaking_mode {
2939                            Some(
2940                                TreeShakingMode::ModuleFragments | TreeShakingMode::ReexportsOnly,
2941                            ) => export.clone().map(ModulePart::export),
2942                            None => None,
2943                        },
2944                        ImportUsage::SideEffects,
2945                        state.import_externals,
2946                        state.tree_shaking_mode,
2947                    )
2948                    .resolved_cell())
2949                })
2950                .await?;
2951
2952            analysis.add_code_gen(EsmBinding::new(
2953                esm_reference,
2954                export.clone(),
2955                ast_path.to_vec().into(),
2956            ));
2957        }
2958        FreeVarReference::InputRelative(kind) => {
2959            let source_path = (*state.source).ident().path().owned().await?;
2960            let source_path = match kind {
2961                InputRelativeConstant::DirName => source_path.parent(),
2962                InputRelativeConstant::FileName => source_path,
2963            };
2964            analysis.add_code_gen(ConstantValueCodeGen::new(
2965                as_abs_path(source_path).into(),
2966                ast_path.to_vec().into(),
2967            ));
2968        }
2969    }
2970    Ok(true)
2971}
2972
2973fn issue_source(source: ResolvedVc<Box<dyn Source>>, span: Span) -> IssueSource {
2974    IssueSource::from_swc_offsets(source, span.lo.to_u32(), span.hi.to_u32())
2975}
2976
2977async fn analyze_amd_define(
2978    source: ResolvedVc<Box<dyn Source>>,
2979    analysis: &mut AnalyzeEcmascriptModuleResultBuilder,
2980    origin: ResolvedVc<Box<dyn ResolveOrigin>>,
2981    handler: &Handler,
2982    span: Span,
2983    ast_path: &[AstParentKind],
2984    args: &[JsValue],
2985    in_try: bool,
2986) -> Result<()> {
2987    match args {
2988        [JsValue::Constant(id), JsValue::Array { items: deps, .. }, _] if id.as_str().is_some() => {
2989            analyze_amd_define_with_deps(
2990                source,
2991                analysis,
2992                origin,
2993                handler,
2994                span,
2995                ast_path,
2996                id.as_str(),
2997                deps,
2998                in_try,
2999            )
3000            .await?;
3001        }
3002        [JsValue::Array { items: deps, .. }, _] => {
3003            analyze_amd_define_with_deps(
3004                source, analysis, origin, handler, span, ast_path, None, deps, in_try,
3005            )
3006            .await?;
3007        }
3008        [JsValue::Constant(id), JsValue::Function(..)] if id.as_str().is_some() => {
3009            analysis.add_code_gen(AmdDefineWithDependenciesCodeGen::new(
3010                vec![
3011                    AmdDefineDependencyElement::Require,
3012                    AmdDefineDependencyElement::Exports,
3013                    AmdDefineDependencyElement::Module,
3014                ],
3015                origin,
3016                ast_path.to_vec().into(),
3017                AmdDefineFactoryType::Function,
3018                issue_source(source, span),
3019                in_try,
3020            ));
3021        }
3022        [JsValue::Constant(id), _] if id.as_str().is_some() => {
3023            analysis.add_code_gen(AmdDefineWithDependenciesCodeGen::new(
3024                vec![
3025                    AmdDefineDependencyElement::Require,
3026                    AmdDefineDependencyElement::Exports,
3027                    AmdDefineDependencyElement::Module,
3028                ],
3029                origin,
3030                ast_path.to_vec().into(),
3031                AmdDefineFactoryType::Unknown,
3032                issue_source(source, span),
3033                in_try,
3034            ));
3035        }
3036        [JsValue::Function(..)] => {
3037            analysis.add_code_gen(AmdDefineWithDependenciesCodeGen::new(
3038                vec![
3039                    AmdDefineDependencyElement::Require,
3040                    AmdDefineDependencyElement::Exports,
3041                    AmdDefineDependencyElement::Module,
3042                ],
3043                origin,
3044                ast_path.to_vec().into(),
3045                AmdDefineFactoryType::Function,
3046                issue_source(source, span),
3047                in_try,
3048            ));
3049        }
3050        [JsValue::Object { .. }] => {
3051            analysis.add_code_gen(AmdDefineWithDependenciesCodeGen::new(
3052                vec![],
3053                origin,
3054                ast_path.to_vec().into(),
3055                AmdDefineFactoryType::Value,
3056                issue_source(source, span),
3057                in_try,
3058            ));
3059        }
3060        [_] => {
3061            analysis.add_code_gen(AmdDefineWithDependenciesCodeGen::new(
3062                vec![
3063                    AmdDefineDependencyElement::Require,
3064                    AmdDefineDependencyElement::Exports,
3065                    AmdDefineDependencyElement::Module,
3066                ],
3067                origin,
3068                ast_path.to_vec().into(),
3069                AmdDefineFactoryType::Unknown,
3070                issue_source(source, span),
3071                in_try,
3072            ));
3073        }
3074        _ => {
3075            handler.span_err_with_code(
3076                span,
3077                "unsupported AMD define() form",
3078                DiagnosticId::Error(errors::failed_to_analyze::ecmascript::AMD_DEFINE.to_string()),
3079            );
3080        }
3081    }
3082
3083    Ok(())
3084}
3085
3086async fn analyze_amd_define_with_deps(
3087    source: ResolvedVc<Box<dyn Source>>,
3088    analysis: &mut AnalyzeEcmascriptModuleResultBuilder,
3089    origin: ResolvedVc<Box<dyn ResolveOrigin>>,
3090    handler: &Handler,
3091    span: Span,
3092    ast_path: &[AstParentKind],
3093    id: Option<&str>,
3094    deps: &[JsValue],
3095    in_try: bool,
3096) -> Result<()> {
3097    let mut requests = Vec::new();
3098    for dep in deps {
3099        if let Some(dep) = dep.as_str() {
3100            match dep {
3101                "exports" => {
3102                    requests.push(AmdDefineDependencyElement::Exports);
3103                }
3104                "require" => {
3105                    handler.span_warn_with_code(
3106                        span,
3107                        "using \"require\" as dependency in an AMD define() is not yet supported",
3108                        DiagnosticId::Error(
3109                            errors::failed_to_analyze::ecmascript::AMD_DEFINE.to_string(),
3110                        ),
3111                    );
3112                    requests.push(AmdDefineDependencyElement::Require);
3113                }
3114                "module" => {
3115                    requests.push(AmdDefineDependencyElement::Module);
3116                }
3117                _ => {
3118                    let request = Request::parse_string(dep.into()).to_resolved().await?;
3119                    let reference = AmdDefineAssetReference::new(
3120                        *origin,
3121                        *request,
3122                        issue_source(source, span),
3123                        in_try,
3124                    )
3125                    .to_resolved()
3126                    .await?;
3127                    requests.push(AmdDefineDependencyElement::Request {
3128                        request,
3129                        request_str: dep.to_string(),
3130                    });
3131                    analysis.add_reference(reference);
3132                }
3133            }
3134        } else {
3135            handler.span_err_with_code(
3136                // TODO(alexkirsz) It'd be best to highlight the argument's span, but
3137                // `JsValue`s do not keep a hold of their original span.
3138                span,
3139                "unsupported AMD define() dependency element form",
3140                DiagnosticId::Error(errors::failed_to_analyze::ecmascript::AMD_DEFINE.to_string()),
3141            );
3142        }
3143    }
3144
3145    if id.is_some() {
3146        handler.span_warn_with_code(
3147            span,
3148            "passing an ID to AMD define() is not yet fully supported",
3149            DiagnosticId::Lint(errors::failed_to_analyze::ecmascript::AMD_DEFINE.to_string()),
3150        );
3151    }
3152
3153    analysis.add_code_gen(AmdDefineWithDependenciesCodeGen::new(
3154        requests,
3155        origin,
3156        ast_path.to_vec().into(),
3157        AmdDefineFactoryType::Function,
3158        issue_source(source, span),
3159        in_try,
3160    ));
3161
3162    Ok(())
3163}
3164
3165/// Used to generate the "root" path to a __filename/__dirname/import.meta.url
3166/// reference.
3167pub fn as_abs_path(path: FileSystemPath) -> String {
3168    // TODO: This should be updated to generate a real system path on the fly
3169    // during runtime, so that the generated code is constant between systems
3170    // but the runtime evaluation can take into account the project's
3171    // actual root directory.
3172    require_resolve(path)
3173}
3174
3175/// Generates an absolute path usable for `require.resolve()` calls.
3176fn require_resolve(path: FileSystemPath) -> String {
3177    format!("/ROOT/{}", path.path.as_str())
3178}
3179
3180async fn early_value_visitor(mut v: JsValue) -> Result<(JsValue, bool)> {
3181    let modified = early_replace_builtin(&mut v);
3182    Ok((v, modified))
3183}
3184
3185async fn value_visitor(
3186    origin: Vc<Box<dyn ResolveOrigin>>,
3187    v: JsValue,
3188    compile_time_info: Vc<CompileTimeInfo>,
3189    free_var_references: &FxIndexMap<DefinableNameSegment, FreeVarReferenceVcs>,
3190    var_graph: &VarGraph,
3191    attributes: &ImportAttributes,
3192    allow_project_root_tracing: bool,
3193) -> Result<(JsValue, bool)> {
3194    let (mut v, modified) = value_visitor_inner(
3195        origin,
3196        v,
3197        compile_time_info,
3198        free_var_references,
3199        var_graph,
3200        attributes,
3201        allow_project_root_tracing,
3202    )
3203    .await?;
3204    v.normalize_shallow();
3205    Ok((v, modified))
3206}
3207
3208async fn value_visitor_inner(
3209    origin: Vc<Box<dyn ResolveOrigin>>,
3210    v: JsValue,
3211    compile_time_info: Vc<CompileTimeInfo>,
3212    free_var_references: &FxIndexMap<DefinableNameSegment, FreeVarReferenceVcs>,
3213    var_graph: &VarGraph,
3214    attributes: &ImportAttributes,
3215    allow_project_root_tracing: bool,
3216) -> Result<(JsValue, bool)> {
3217    let ImportAttributes { ignore, .. } = *attributes;
3218    // This check is just an optimization
3219    if v.get_definable_name_len().is_some() {
3220        let compile_time_info = compile_time_info.await?;
3221        if let JsValue::TypeOf(_, arg) = &v
3222            && let Some(value) = arg.match_free_var_reference(
3223                var_graph,
3224                free_var_references,
3225                &DefinableNameSegment::TypeOf,
3226            )
3227        {
3228            return Ok(((&*value.await?).try_into()?, true));
3229        }
3230
3231        if let Some(value) = v.match_define(&*compile_time_info.defines.individual().await?) {
3232            return Ok(((&*value.await?).try_into()?, true));
3233        }
3234    }
3235    let value = match v {
3236        JsValue::Call(
3237            _,
3238            box JsValue::WellKnownFunction(WellKnownFunctionKind::RequireResolve),
3239            args,
3240        ) => require_resolve_visitor(origin, args).await?,
3241        JsValue::Call(
3242            _,
3243            box JsValue::WellKnownFunction(WellKnownFunctionKind::RequireContext),
3244            args,
3245        ) => require_context_visitor(origin, args).await?,
3246        JsValue::Call(
3247            _,
3248            box JsValue::WellKnownFunction(
3249                WellKnownFunctionKind::RequireContextRequire(..)
3250                | WellKnownFunctionKind::RequireContextRequireKeys(..)
3251                | WellKnownFunctionKind::RequireContextRequireResolve(..),
3252            ),
3253            _,
3254        ) => {
3255            // TODO: figure out how to do static analysis without invalidating the whole
3256            // analysis when a new file gets added
3257            v.into_unknown(
3258                true,
3259                "require.context() static analysis is currently limited",
3260            )
3261        }
3262        JsValue::Call(
3263            _,
3264            box JsValue::WellKnownFunction(WellKnownFunctionKind::CreateRequire),
3265            ref args,
3266        ) => {
3267            // Only support `createRequire(import.meta.url)` for now
3268            if let [
3269                JsValue::Member(
3270                    _,
3271                    box JsValue::WellKnownObject(WellKnownObjectKind::ImportMeta),
3272                    box JsValue::Constant(super::analyzer::ConstantValue::Str(prop)),
3273                ),
3274            ] = &args[..]
3275                && prop.as_str() == "url"
3276            {
3277                JsValue::WellKnownFunction(WellKnownFunctionKind::Require)
3278            } else {
3279                v.into_unknown(true, "createRequire() non constant")
3280            }
3281        }
3282        JsValue::New(
3283            _,
3284            box JsValue::WellKnownFunction(WellKnownFunctionKind::URLConstructor),
3285            ref args,
3286        ) => {
3287            if let [
3288                JsValue::Constant(super::analyzer::ConstantValue::Str(url)),
3289                JsValue::Member(
3290                    _,
3291                    box JsValue::WellKnownObject(WellKnownObjectKind::ImportMeta),
3292                    box JsValue::Constant(super::analyzer::ConstantValue::Str(prop)),
3293                ),
3294            ] = &args[..]
3295            {
3296                if prop.as_str() == "url" {
3297                    JsValue::Url(url.clone(), JsValueUrlKind::Relative)
3298                } else {
3299                    v.into_unknown(true, "new URL() non constant")
3300                }
3301            } else {
3302                v.into_unknown(true, "new non constant")
3303            }
3304        }
3305        JsValue::WellKnownFunction(
3306            WellKnownFunctionKind::PathJoin
3307            | WellKnownFunctionKind::PathResolve(_)
3308            | WellKnownFunctionKind::FsReadMethod(_),
3309        ) => {
3310            if ignore {
3311                return Ok((
3312                    JsValue::unknown(v, true, "ignored well known function"),
3313                    true,
3314                ));
3315            } else {
3316                return Ok((v, false));
3317            }
3318        }
3319        JsValue::FreeVar(ref kind) => match &**kind {
3320            "__dirname" => as_abs_path(origin.origin_path().owned().await?.parent()).into(),
3321            "__filename" => as_abs_path(origin.origin_path().owned().await?).into(),
3322
3323            "require" => JsValue::unknown_if(
3324                ignore,
3325                JsValue::WellKnownFunction(WellKnownFunctionKind::Require),
3326                true,
3327                "ignored require",
3328            ),
3329            "import" => JsValue::unknown_if(
3330                ignore,
3331                JsValue::WellKnownFunction(WellKnownFunctionKind::Import),
3332                true,
3333                "ignored import",
3334            ),
3335            "Worker" => JsValue::unknown_if(
3336                ignore,
3337                JsValue::WellKnownFunction(WellKnownFunctionKind::WorkerConstructor),
3338                true,
3339                "ignored Worker constructor",
3340            ),
3341            "define" => JsValue::WellKnownFunction(WellKnownFunctionKind::Define),
3342            "URL" => JsValue::WellKnownFunction(WellKnownFunctionKind::URLConstructor),
3343            "process" => JsValue::WellKnownObject(WellKnownObjectKind::NodeProcessModule),
3344            "Object" => JsValue::WellKnownObject(WellKnownObjectKind::GlobalObject),
3345            "Buffer" => JsValue::WellKnownObject(WellKnownObjectKind::NodeBuffer),
3346            _ => return Ok((v, false)),
3347        },
3348        JsValue::Module(ref mv) => compile_time_info
3349            .environment()
3350            .node_externals()
3351            .await?
3352            // TODO check externals
3353            .then(|| module_value_to_well_known_object(mv))
3354            .flatten()
3355            .unwrap_or_else(|| v.into_unknown(true, "cross module analyzing is not yet supported")),
3356        JsValue::Argument(..) => {
3357            v.into_unknown(true, "cross function analyzing is not yet supported")
3358        }
3359        _ => {
3360            let (mut v, mut modified) =
3361                replace_well_known(v, compile_time_info, allow_project_root_tracing).await?;
3362            modified = replace_builtin(&mut v) || modified;
3363            modified = modified || v.make_nested_operations_unknown();
3364            return Ok((v, modified));
3365        }
3366    };
3367    Ok((value, true))
3368}
3369
3370async fn require_resolve_visitor(
3371    origin: Vc<Box<dyn ResolveOrigin>>,
3372    args: Vec<JsValue>,
3373) -> Result<JsValue> {
3374    Ok(if args.len() == 1 {
3375        let pat = js_value_to_pattern(&args[0]);
3376        let request = Request::parse(pat.clone());
3377        let resolved = cjs_resolve_source(
3378            origin,
3379            request,
3380            CommonJsReferenceSubType::Undefined,
3381            None,
3382            true,
3383        )
3384        .resolve()
3385        .await?;
3386        let mut values = resolved
3387            .primary_sources()
3388            .await?
3389            .iter()
3390            .map(|&source| async move {
3391                Ok(require_resolve(source.ident().path().owned().await?).into())
3392            })
3393            .try_join()
3394            .await?;
3395
3396        match values.len() {
3397            0 => JsValue::unknown(
3398                JsValue::call(
3399                    Box::new(JsValue::WellKnownFunction(
3400                        WellKnownFunctionKind::RequireResolve,
3401                    )),
3402                    args,
3403                ),
3404                false,
3405                "unresolvable request",
3406            ),
3407            1 => values.pop().unwrap(),
3408            _ => JsValue::alternatives(values),
3409        }
3410    } else {
3411        JsValue::unknown(
3412            JsValue::call(
3413                Box::new(JsValue::WellKnownFunction(
3414                    WellKnownFunctionKind::RequireResolve,
3415                )),
3416                args,
3417            ),
3418            true,
3419            "only a single argument is supported",
3420        )
3421    })
3422}
3423
3424async fn require_context_visitor(
3425    origin: Vc<Box<dyn ResolveOrigin>>,
3426    args: Vec<JsValue>,
3427) -> Result<JsValue> {
3428    let options = match parse_require_context(&args) {
3429        Ok(options) => options,
3430        Err(err) => {
3431            return Ok(JsValue::unknown(
3432                JsValue::call(
3433                    Box::new(JsValue::WellKnownFunction(
3434                        WellKnownFunctionKind::RequireContext,
3435                    )),
3436                    args,
3437                ),
3438                true,
3439                PrettyPrintError(&err).to_string(),
3440            ));
3441        }
3442    };
3443
3444    let dir = origin
3445        .origin_path()
3446        .owned()
3447        .await?
3448        .parent()
3449        .join(options.dir.as_str())?;
3450
3451    let map = RequireContextMap::generate(
3452        origin,
3453        dir,
3454        options.include_subdirs,
3455        options.filter.cell(),
3456        None,
3457        true,
3458    );
3459
3460    Ok(JsValue::WellKnownFunction(
3461        WellKnownFunctionKind::RequireContextRequire(
3462            RequireContextValue::from_context_map(map).await?,
3463        ),
3464    ))
3465}
3466
3467#[derive(Debug)]
3468enum StaticExpr {
3469    String(String),
3470    FreeVar(Vec<String>),
3471    ImportedVar(String, Vec<String>),
3472    Unknown,
3473}
3474
3475// TODO get rid of that
3476#[derive(Default)]
3477struct StaticAnalyser {
3478    imports: FxHashMap<String, (String, Vec<String>)>,
3479}
3480
3481impl StaticAnalyser {
3482    fn prop_to_name(&self, prop: &MemberProp) -> Option<String> {
3483        match prop {
3484            MemberProp::Ident(ident) => Some(ident.sym.to_string()),
3485            MemberProp::PrivateName(_) => None,
3486            MemberProp::Computed(ComputedPropName { expr, .. }) => match self.evaluate_expr(expr) {
3487                StaticExpr::String(str) => Some(str),
3488                _ => None,
3489            },
3490        }
3491    }
3492
3493    fn evaluate_expr(&self, expr: &Expr) -> StaticExpr {
3494        match expr {
3495            Expr::Lit(Lit::Str(str)) => {
3496                StaticExpr::String(str.value.to_string_lossy().into_owned())
3497            }
3498            Expr::Ident(ident) => {
3499                let str = ident.sym.to_string();
3500                match self.imports.get(&str) {
3501                    Some((module, import)) => {
3502                        StaticExpr::ImportedVar(module.clone(), import.clone())
3503                    }
3504                    None => StaticExpr::FreeVar(vec![str]),
3505                }
3506            }
3507            Expr::Member(member) => match self.evaluate_expr(&member.obj) {
3508                StaticExpr::FreeVar(mut vec) => match self.prop_to_name(&member.prop) {
3509                    Some(name) => {
3510                        vec.push(name);
3511                        StaticExpr::FreeVar(vec)
3512                    }
3513                    None => StaticExpr::Unknown,
3514                },
3515                StaticExpr::ImportedVar(module, mut vec) => match self.prop_to_name(&member.prop) {
3516                    Some(name) => {
3517                        vec.push(name);
3518                        StaticExpr::ImportedVar(module, vec)
3519                    }
3520                    None => StaticExpr::Unknown,
3521                },
3522                _ => StaticExpr::Unknown,
3523            },
3524            _ => StaticExpr::Unknown,
3525        }
3526    }
3527}
3528
3529/// A visitor that walks the AST and collects information about the various
3530/// references a module makes to other parts of the code.
3531struct ModuleReferencesVisitor<'a> {
3532    analyze_mode: AnalyzeMode,
3533    eval_context: &'a EvalContext,
3534    old_analyzer: StaticAnalyser,
3535    import_references: &'a [ResolvedVc<EsmAssetReference>],
3536    analysis: &'a mut AnalyzeEcmascriptModuleResultBuilder,
3537    esm_exports: BTreeMap<RcStr, EsmExport>,
3538    webpack_runtime: Option<(RcStr, Span)>,
3539    webpack_entry: bool,
3540    webpack_chunks: Vec<Lit>,
3541}
3542
3543impl<'a> ModuleReferencesVisitor<'a> {
3544    fn new(
3545        eval_context: &'a EvalContext,
3546        import_references: &'a [ResolvedVc<EsmAssetReference>],
3547        analysis: &'a mut AnalyzeEcmascriptModuleResultBuilder,
3548        analyze_mode: AnalyzeMode,
3549    ) -> Self {
3550        Self {
3551            analyze_mode,
3552            eval_context,
3553            old_analyzer: StaticAnalyser::default(),
3554            import_references,
3555            analysis,
3556            esm_exports: BTreeMap::new(),
3557            webpack_runtime: None,
3558            webpack_entry: false,
3559            webpack_chunks: Vec::new(),
3560        }
3561    }
3562}
3563
3564fn as_parent_path(ast_path: &AstNodePath<AstParentNodeRef<'_>>) -> Vec<AstParentKind> {
3565    ast_path.iter().map(|n| n.kind()).collect()
3566}
3567
3568pub(crate) fn for_each_ident_in_pat(pat: &Pat, f: &mut impl FnMut(&Atom, SyntaxContext)) {
3569    match pat {
3570        Pat::Ident(BindingIdent { id, .. }) => {
3571            f(&id.sym, id.ctxt);
3572        }
3573        Pat::Array(ArrayPat { elems, .. }) => elems.iter().for_each(|e| {
3574            if let Some(e) = e {
3575                for_each_ident_in_pat(e, f);
3576            }
3577        }),
3578        Pat::Rest(RestPat { arg, .. }) => {
3579            for_each_ident_in_pat(arg, f);
3580        }
3581        Pat::Object(ObjectPat { props, .. }) => {
3582            props.iter().for_each(|p| match p {
3583                ObjectPatProp::KeyValue(KeyValuePatProp { value, .. }) => {
3584                    for_each_ident_in_pat(value, f);
3585                }
3586                ObjectPatProp::Assign(AssignPatProp { key, .. }) => {
3587                    f(&key.sym, key.ctxt);
3588                }
3589                ObjectPatProp::Rest(RestPat { arg, .. }) => {
3590                    for_each_ident_in_pat(arg, f);
3591                }
3592            });
3593        }
3594        Pat::Assign(AssignPat { left, .. }) => {
3595            for_each_ident_in_pat(left, f);
3596        }
3597        Pat::Invalid(_) | Pat::Expr(_) => {
3598            panic!("Unexpected pattern while enumerating idents");
3599        }
3600    }
3601}
3602
3603impl VisitAstPath for ModuleReferencesVisitor<'_> {
3604    fn visit_export_all<'ast: 'r, 'r>(
3605        &mut self,
3606        export: &'ast ExportAll,
3607        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
3608    ) {
3609        if self.analyze_mode.is_code_gen() {
3610            self.analysis
3611                .add_code_gen(EsmModuleItem::new(as_parent_path(ast_path).into()));
3612        }
3613        export.visit_children_with_ast_path(self, ast_path);
3614    }
3615
3616    fn visit_named_export<'ast: 'r, 'r>(
3617        &mut self,
3618        export: &'ast NamedExport,
3619        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
3620    ) {
3621        // We create mutable exports for fake ESMs generated by module splitting
3622        let is_fake_esm = export
3623            .with
3624            .as_deref()
3625            .map(find_turbopack_part_id_in_asserts)
3626            .is_some();
3627
3628        if export.src.is_none() {
3629            for spec in export.specifiers.iter() {
3630                fn to_rcstr(name: &ModuleExportName) -> RcStr {
3631                    name.atom().as_str().into()
3632                }
3633                match spec {
3634                    ExportSpecifier::Namespace(_) => {
3635                        panic!(
3636                            "ExportNamespaceSpecifier will not happen in combination with src == \
3637                             None"
3638                        );
3639                    }
3640                    ExportSpecifier::Default(_) => {
3641                        panic!(
3642                            "ExportDefaultSpecifier will not happen in combination with src == \
3643                             None"
3644                        );
3645                    }
3646                    ExportSpecifier::Named(ExportNamedSpecifier { orig, exported, .. }) => {
3647                        let key = to_rcstr(exported.as_ref().unwrap_or(orig));
3648                        let binding_name = to_rcstr(orig);
3649                        let export = {
3650                            let imported_binding = if let ModuleExportName::Ident(ident) = orig {
3651                                self.eval_context.imports.get_binding(&ident.to_id())
3652                            } else {
3653                                None
3654                            };
3655                            if let Some((index, export)) = imported_binding {
3656                                let esm_ref = self.import_references[index];
3657                                self.analysis.add_esm_reexport_reference(index);
3658                                if let Some(export) = export {
3659                                    EsmExport::ImportedBinding(
3660                                        ResolvedVc::upcast(esm_ref),
3661                                        export,
3662                                        is_fake_esm,
3663                                    )
3664                                } else {
3665                                    EsmExport::ImportedNamespace(ResolvedVc::upcast(esm_ref))
3666                                }
3667                            } else {
3668                                EsmExport::LocalBinding(
3669                                    binding_name,
3670                                    if is_fake_esm {
3671                                        Liveness::Mutable
3672                                    } else {
3673                                        // If this is `export {foo} from 'mod'` and `foo` is a const
3674                                        // in mod then we could export as Const here.
3675                                        Liveness::Live
3676                                    },
3677                                )
3678                            }
3679                        };
3680                        self.esm_exports.insert(key, export);
3681                    }
3682                }
3683            }
3684        }
3685
3686        if self.analyze_mode.is_code_gen() {
3687            self.analysis
3688                .add_code_gen(EsmModuleItem::new(as_parent_path(ast_path).into()));
3689        }
3690        export.visit_children_with_ast_path(self, ast_path);
3691    }
3692
3693    fn visit_export_decl<'ast: 'r, 'r>(
3694        &mut self,
3695        export: &'ast ExportDecl,
3696        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
3697    ) {
3698        {
3699            let decl: &Decl = &export.decl;
3700            let insert_export_binding = &mut |name: RcStr, liveness: Liveness| {
3701                self.esm_exports
3702                    .insert(name.clone(), EsmExport::LocalBinding(name, liveness));
3703            };
3704            match decl {
3705                Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
3706                    // TODO: examine whether the value is ever mutated rather than just checking
3707                    // 'const'
3708                    insert_export_binding(ident.sym.as_str().into(), Liveness::Live);
3709                }
3710                Decl::Var(var_decl) => {
3711                    // TODO: examine whether the value is ever mutated rather than just checking
3712                    // 'const'
3713                    let liveness = match var_decl.kind {
3714                        VarDeclKind::Var => Liveness::Live,
3715                        VarDeclKind::Let => Liveness::Live,
3716                        VarDeclKind::Const => Liveness::Constant,
3717                    };
3718                    let decls = &*var_decl.decls;
3719                    decls.iter().for_each(|VarDeclarator { name, .. }| {
3720                        for_each_ident_in_pat(name, &mut |name, _| {
3721                            insert_export_binding(name.as_str().into(), liveness)
3722                        })
3723                    });
3724                }
3725                Decl::Using(_) => {
3726                    // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#:~:text=You%20cannot%20use%20export%20on%20a%20using%20or%20await%20using%20declaration
3727                    unreachable!("using declarations can not be exported");
3728                }
3729                Decl::TsInterface(_)
3730                | Decl::TsTypeAlias(_)
3731                | Decl::TsEnum(_)
3732                | Decl::TsModule(_) => {
3733                    // ignore typescript for code generation
3734                }
3735            }
3736        };
3737        if self.analyze_mode.is_code_gen() {
3738            self.analysis
3739                .add_code_gen(EsmModuleItem::new(as_parent_path(ast_path).into()));
3740        }
3741        export.visit_children_with_ast_path(self, ast_path);
3742    }
3743
3744    fn visit_export_default_expr<'ast: 'r, 'r>(
3745        &mut self,
3746        export: &'ast ExportDefaultExpr,
3747        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
3748    ) {
3749        self.esm_exports.insert(
3750            rcstr!("default"),
3751            EsmExport::LocalBinding(
3752                magic_identifier::mangle("default export").into(),
3753                // The expression passed to `export default` cannot be mutated
3754                Liveness::Constant,
3755            ),
3756        );
3757        if self.analyze_mode.is_code_gen() {
3758            self.analysis
3759                .add_code_gen(EsmModuleItem::new(as_parent_path(ast_path).into()));
3760        }
3761        export.visit_children_with_ast_path(self, ast_path);
3762    }
3763
3764    fn visit_export_default_decl<'ast: 'r, 'r>(
3765        &mut self,
3766        export: &'ast ExportDefaultDecl,
3767        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
3768    ) {
3769        match &export.decl {
3770            DefaultDecl::Class(ClassExpr { ident, .. }) | DefaultDecl::Fn(FnExpr { ident, .. }) => {
3771                self.esm_exports.insert(
3772                    rcstr!("default"),
3773                    EsmExport::LocalBinding(
3774                        ident
3775                            .as_ref()
3776                            .map(|i| i.sym.as_str().into())
3777                            .unwrap_or_else(|| magic_identifier::mangle("default export").into()),
3778                        // Default export declarations can only be mutated if they have a name.
3779                        if ident.is_some() {
3780                            Liveness::Live
3781                        } else {
3782                            Liveness::Constant
3783                        },
3784                    ),
3785                );
3786            }
3787            DefaultDecl::TsInterfaceDecl(..) => {
3788                // ignore
3789            }
3790        }
3791        if self.analyze_mode.is_code_gen() {
3792            self.analysis
3793                .add_code_gen(EsmModuleItem::new(as_parent_path(ast_path).into()));
3794        }
3795        export.visit_children_with_ast_path(self, ast_path);
3796    }
3797
3798    fn visit_import_decl<'ast: 'r, 'r>(
3799        &mut self,
3800        import: &'ast ImportDecl,
3801        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
3802    ) {
3803        let path = as_parent_path(ast_path).into();
3804        let src = import.src.value.to_string_lossy().into_owned();
3805        import.visit_children_with_ast_path(self, ast_path);
3806        if import.type_only {
3807            return;
3808        }
3809        for specifier in &import.specifiers {
3810            match specifier {
3811                ImportSpecifier::Named(named) => {
3812                    if !named.is_type_only {
3813                        self.old_analyzer.imports.insert(
3814                            named.local.sym.to_string(),
3815                            (
3816                                src.clone(),
3817                                vec![match &named.imported {
3818                                    Some(ModuleExportName::Ident(ident)) => ident.sym.to_string(),
3819                                    Some(ModuleExportName::Str(str)) => {
3820                                        str.value.to_string_lossy().into_owned()
3821                                    }
3822                                    None => named.local.sym.to_string(),
3823                                }],
3824                            ),
3825                        );
3826                    }
3827                }
3828                ImportSpecifier::Default(default_import) => {
3829                    self.old_analyzer.imports.insert(
3830                        default_import.local.sym.to_string(),
3831                        (src.clone(), vec!["default".to_string()]),
3832                    );
3833                }
3834                ImportSpecifier::Namespace(namespace) => {
3835                    self.old_analyzer
3836                        .imports
3837                        .insert(namespace.local.sym.to_string(), (src.clone(), Vec::new()));
3838                }
3839            }
3840        }
3841        if self.analyze_mode.is_code_gen() {
3842            self.analysis.add_code_gen(EsmModuleItem::new(path));
3843        }
3844    }
3845
3846    fn visit_var_declarator<'ast: 'r, 'r>(
3847        &mut self,
3848        decl: &'ast VarDeclarator,
3849        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
3850    ) {
3851        if let Some(ident) = decl.name.as_ident()
3852            && &*ident.id.sym == "__webpack_require__"
3853            && let Some(init) = &decl.init
3854            && let Some(call) = init.as_call()
3855            && let Some(expr) = call.callee.as_expr()
3856            && let Some(ident) = expr.as_ident()
3857            && &*ident.sym == "require"
3858            && let [ExprOrSpread { spread: None, expr }] = &call.args[..]
3859            && let Some(Lit::Str(str)) = expr.as_lit()
3860        {
3861            self.webpack_runtime =
3862                Some((str.value.to_string_lossy().into_owned().into(), call.span));
3863            return;
3864        }
3865        decl.visit_children_with_ast_path(self, ast_path);
3866    }
3867
3868    fn visit_call_expr<'ast: 'r, 'r>(
3869        &mut self,
3870        call: &'ast CallExpr,
3871        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
3872    ) {
3873        if let Callee::Expr(expr) = &call.callee
3874            && let StaticExpr::FreeVar(var) = self.old_analyzer.evaluate_expr(expr)
3875        {
3876            match &var[..] {
3877                [webpack_require, property]
3878                    if webpack_require == "__webpack_require__" && property == "C" =>
3879                {
3880                    self.webpack_entry = true;
3881                }
3882                [webpack_require, property]
3883                    if webpack_require == "__webpack_require__" && property == "X" =>
3884                {
3885                    if let [
3886                        _,
3887                        ExprOrSpread {
3888                            spread: None,
3889                            expr: chunk_ids,
3890                        },
3891                        _,
3892                    ] = &call.args[..]
3893                        && let Some(array) = chunk_ids.as_array()
3894                    {
3895                        for elem in array.elems.iter().flatten() {
3896                            if let ExprOrSpread { spread: None, expr } = elem
3897                                && let Some(lit) = expr.as_lit()
3898                            {
3899                                self.webpack_chunks.push(lit.clone());
3900                            }
3901                        }
3902                    }
3903                }
3904                _ => {}
3905            }
3906        }
3907        call.visit_children_with_ast_path(self, ast_path);
3908    }
3909}
3910
3911#[turbo_tasks::function]
3912async fn resolve_as_webpack_runtime(
3913    origin: Vc<Box<dyn ResolveOrigin>>,
3914    request: Vc<Request>,
3915    transforms: Vc<EcmascriptInputTransforms>,
3916) -> Result<Vc<WebpackRuntime>> {
3917    let ty = ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined);
3918    let options = origin.resolve_options(ty.clone());
3919
3920    let options = apply_cjs_specific_options(options);
3921
3922    let resolved = resolve(
3923        origin.origin_path().await?.parent(),
3924        ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined),
3925        request,
3926        options,
3927    );
3928
3929    if let Some(source) = *resolved.first_source().await? {
3930        Ok(webpack_runtime(*source, transforms))
3931    } else {
3932        Ok(WebpackRuntime::None.cell())
3933    }
3934}
3935
3936#[derive(Hash, Debug, Clone, Eq, PartialEq, TraceRawVcs, Encode, Decode)]
3937pub struct AstPath(
3938    #[bincode(with_serde)]
3939    #[turbo_tasks(trace_ignore)]
3940    Vec<AstParentKind>,
3941);
3942
3943impl TaskInput for AstPath {
3944    fn is_transient(&self) -> bool {
3945        false
3946    }
3947}
3948unsafe impl NonLocalValue for AstPath {}
3949
3950impl Deref for AstPath {
3951    type Target = [AstParentKind];
3952
3953    fn deref(&self) -> &Self::Target {
3954        &self.0
3955    }
3956}
3957
3958impl From<Vec<AstParentKind>> for AstPath {
3959    fn from(v: Vec<AstParentKind>) -> Self {
3960        Self(v)
3961    }
3962}
3963
3964pub static TURBOPACK_HELPER: Lazy<Atom> = Lazy::new(|| atom!("__turbopack-helper__"));
3965pub static TURBOPACK_HELPER_WTF8: Lazy<Wtf8Atom> =
3966    Lazy::new(|| atom!("__turbopack-helper__").into());
3967
3968pub fn is_turbopack_helper_import(import: &ImportDecl) -> bool {
3969    let annotations = ImportAnnotations::parse(import.with.as_deref());
3970
3971    annotations.get(&TURBOPACK_HELPER_WTF8).is_some()
3972}
3973
3974pub fn is_swc_helper_import(import: &ImportDecl) -> bool {
3975    import.src.value.starts_with("@swc/helpers/")
3976}
3977
3978#[derive(Debug)]
3979enum DetectedDynamicExportType {
3980    CommonJs,
3981    Namespace,
3982    Value,
3983    None,
3984    UsingModuleDeclarations,
3985}
3986
3987fn detect_dynamic_export(p: &Program) -> DetectedDynamicExportType {
3988    use swc_core::ecma::visit::{Visit, VisitWith, visit_obj_and_computed};
3989
3990    if let Program::Module(m) = p {
3991        // Check for imports/exports
3992        if m.body.iter().any(|item| {
3993            item.as_module_decl().is_some_and(|module_decl| {
3994                module_decl.as_import().is_none_or(|import| {
3995                    !is_turbopack_helper_import(import) && !is_swc_helper_import(import)
3996                })
3997            })
3998        }) {
3999            return DetectedDynamicExportType::UsingModuleDeclarations;
4000        }
4001    }
4002
4003    struct Visitor {
4004        cjs: bool,
4005        value: bool,
4006        namespace: bool,
4007        found: bool,
4008    }
4009
4010    impl Visit for Visitor {
4011        visit_obj_and_computed!();
4012
4013        fn visit_ident(&mut self, i: &Ident) {
4014            // The detection is not perfect, it might have some false positives, e. g. in
4015            // cases where `module` is used in some other way. e. g. `const module = 42;`.
4016            // But a false positive doesn't break anything, it only opts out of some
4017            // optimizations, which is acceptable.
4018            if &*i.sym == "module" || &*i.sym == "exports" {
4019                self.cjs = true;
4020                self.found = true;
4021            }
4022            if &*i.sym == "__turbopack_export_value__" {
4023                self.value = true;
4024                self.found = true;
4025            }
4026            if &*i.sym == "__turbopack_export_namespace__" {
4027                self.namespace = true;
4028                self.found = true;
4029            }
4030        }
4031
4032        fn visit_expr(&mut self, n: &Expr) {
4033            if self.found {
4034                return;
4035            }
4036
4037            if let Expr::Member(member) = n
4038                && member.obj.is_ident_ref_to("__turbopack_context__")
4039                && let MemberProp::Ident(prop) = &member.prop
4040            {
4041                const TURBOPACK_EXPORT_VALUE_SHORTCUT: &str = TURBOPACK_EXPORT_VALUE.shortcut;
4042                const TURBOPACK_EXPORT_NAMESPACE_SHORTCUT: &str =
4043                    TURBOPACK_EXPORT_NAMESPACE.shortcut;
4044                match &*prop.sym {
4045                    TURBOPACK_EXPORT_VALUE_SHORTCUT => {
4046                        self.value = true;
4047                        self.found = true;
4048                    }
4049                    TURBOPACK_EXPORT_NAMESPACE_SHORTCUT => {
4050                        self.namespace = true;
4051                        self.found = true;
4052                    }
4053                    _ => {}
4054                }
4055            }
4056
4057            n.visit_children_with(self);
4058        }
4059
4060        fn visit_stmt(&mut self, n: &Stmt) {
4061            if self.found {
4062                return;
4063            }
4064            n.visit_children_with(self);
4065        }
4066    }
4067
4068    let mut v = Visitor {
4069        cjs: false,
4070        value: false,
4071        namespace: false,
4072        found: false,
4073    };
4074    p.visit_with(&mut v);
4075    if v.cjs {
4076        DetectedDynamicExportType::CommonJs
4077    } else if v.value {
4078        DetectedDynamicExportType::Value
4079    } else if v.namespace {
4080        DetectedDynamicExportType::Namespace
4081    } else {
4082        DetectedDynamicExportType::None
4083    }
4084}
4085
4086/// Detects whether a list of arguments is specifically
4087/// `(process.argv[0], ['-e', ...])`. This is useful for detecting if a node
4088/// process is being spawned to interpret a string of JavaScript code, and does
4089/// not require static analysis.
4090fn is_invoking_node_process_eval(args: &[JsValue]) -> bool {
4091    if args.len() < 2 {
4092        return false;
4093    }
4094
4095    if let JsValue::Member(_, obj, constant) = &args[0] {
4096        // Is the first argument to spawn `process.argv[]`?
4097        if let (
4098            box JsValue::WellKnownObject(WellKnownObjectKind::NodeProcessArgv),
4099            box JsValue::Constant(JsConstantValue::Num(ConstantNumber(num))),
4100        ) = (obj, constant)
4101        {
4102            // Is it specifically `process.argv[0]`?
4103            if num.is_zero()
4104                && let JsValue::Array {
4105                    total_nodes: _,
4106                    items,
4107                    mutable: _,
4108                } = &args[1]
4109            {
4110                // Is `-e` one of the arguments passed to the program?
4111                if items.iter().any(|e| {
4112                    if let JsValue::Constant(JsConstantValue::Str(ConstantString::Atom(arg))) = e {
4113                        arg == "-e"
4114                    } else {
4115                        false
4116                    }
4117                }) {
4118                    // If so, this is likely spawning node to evaluate a string, and
4119                    // does not need to be statically analyzed.
4120                    return true;
4121                }
4122            }
4123        }
4124    }
4125
4126    false
4127}