Skip to main content

turbopack_ecmascript/analyzer/graph/
mod.rs

1use rustc_hash::FxHashMap;
2use swc_core::{
3    atoms::Atom,
4    common::BytePos,
5    ecma::{ast::*, visit::VisitWithAstPath},
6};
7use turbopack_core::resolve::ExportUsage;
8
9pub use crate::analyzer::graph::{
10    effects::{
11        AssignmentScope, AssignmentScopes, ConditionalKind, Effect, EffectArg, EffectsBlock,
12    },
13    eval_context::EvalContext,
14};
15use crate::{
16    AnalyzeMode, SpecifiedModuleType,
17    analyzer::{Bump, JsValue, graph::visitor::Analyzer},
18    chunk::CjsStaticExports,
19    code_gen::CodeGen,
20};
21
22mod effects;
23mod eval_context;
24pub(crate) mod visitor;
25
26#[derive(Debug)]
27pub struct VarGraph<'a> {
28    pub values: FxHashMap<Id, JsValue<'a>>,
29
30    /// Map [`JsValue::FreeVar`] names to their [`Id`] to facilitate lookups into [`Self::values`].
31    ///
32    /// Doesn't necessarily contain every [`FreeVar`][JsValue::FreeVar], just those who have
33    /// non-trivial values.
34    pub free_var_ids: FxHashMap<Atom, Id>,
35
36    pub effects: Vec<Effect<'a>>,
37    // Some unconditional codegens, usually for ESM items.
38    pub code_gens: Vec<CodeGen>,
39
40    /// [`ExportUsage`] per `require("…")` call, keyed by call position; absent
41    /// calls fall back to `ExportUsage::All`.
42    pub require_usage: FxHashMap<BytePos, ExportUsage>,
43
44    /// Present when the module is a statically-analyzable CommonJS module (no
45    /// dynamic exports); carries its named exports for scope hoisting.
46    pub cjs_static_exports: Option<CjsStaticExports>,
47}
48
49impl<'a> VarGraph<'a> {
50    pub fn normalize(&mut self, arena: &'a Bump) {
51        for value in self.values.values_mut() {
52            value.normalize(arena);
53        }
54        for effect in self.effects.iter_mut() {
55            effect.normalize(arena);
56        }
57    }
58}
59
60pub fn create_graph<'a>(
61    arena: &'a Bump,
62    m: &Program,
63    eval_context: &EvalContext,
64    analyze_mode: AnalyzeMode,
65    supports_block_scoping: bool,
66    specified_module_type: SpecifiedModuleType,
67    cjs_tree_shaking: bool,
68    cjs_scope_hoisting: bool,
69) -> VarGraph<'a> {
70    let mut analyzer = Analyzer {
71        arena,
72        analyze_mode,
73        data: VarGraph {
74            values: Default::default(),
75            free_var_ids: Default::default(),
76            effects: Default::default(),
77            code_gens: Default::default(),
78            require_usage: Default::default(),
79            cjs_static_exports: Default::default(),
80        },
81        eval_context,
82        state: Default::default(),
83        effects: Default::default(),
84        hoisted_effects: Default::default(),
85        code_gens: Default::default(),
86        supports_block_scoping,
87    };
88
89    // CommonJS export recognition runs for a CommonJS module that emits code when either CJS
90    // tree-shaking or CJS scope hoisting is enabled (both consume the static export analysis).
91    if (cjs_tree_shaking || cjs_scope_hoisting)
92        && analyze_mode.is_code_gen()
93        && eval_context.is_cjs(specified_module_type)
94    {
95        analyzer.enable_cjs_exports();
96    }
97
98    if cjs_tree_shaking && analyze_mode.is_code_gen() {
99        analyzer.enable_require_usage(&eval_context.imports);
100    }
101
102    m.visit_with_ast_path(&mut analyzer, &mut Default::default());
103
104    let mut graph = analyzer.data;
105    graph.normalize(arena);
106
107    graph
108}