Skip to main content

rspack_core/stats/
utils.rs

1use std::borrow::Cow;
2
3use itertools::Itertools;
4use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
5use rspack_collections::Identifier;
6use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
7
8use super::{
9  Stats, StatsChunkGroup, StatsErrorModuleTraceDependency, StatsErrorModuleTraceModule,
10  StatsModule, StatsModuleTrace,
11};
12use crate::{
13  BoxModule, Chunk, ChunkByUkey, ChunkGraph, ChunkGroupByUkey, ChunkGroupOrderKey, ChunkGroupUkey,
14  Compilation, CompilerOptions, ModuleGraph, ModuleId,
15};
16
17pub fn get_asset_size(file: &str, compilation: &Compilation) -> usize {
18  compilation
19    .assets()
20    .get(file)
21    .and_then(|asset| asset.get_source().map(|s| s.size()))
22    .unwrap_or(0)
23}
24
25pub fn sort_modules(modules: &mut [StatsModule]) {
26  modules.sort_unstable_by(|a, b| {
27    // align with MODULES_SORTER
28    // https://github.com/webpack/webpack/blob/ab3e93b19ead869727592d09d36f94e649eb9d83/lib/stats/DefaultStatsFactoryPlugin.js#L1546
29    if a.depth != b.depth {
30      a.depth.cmp(&b.depth)
31    } else if a.pre_order_index != b.pre_order_index {
32      a.pre_order_index.cmp(&b.pre_order_index)
33    } else if let (Some(a_name), Some(b_name)) = (&a.name, &b.name)
34      && a_name.len() != b_name.len()
35    {
36      a_name.len().cmp(&b_name.len())
37    } else {
38      a.name.cmp(&b.name)
39    }
40  });
41}
42
43pub fn get_stats_module_name_and_id<'s>(
44  module: &'s BoxModule,
45  compilation: &Compilation,
46) -> (Cow<'s, str>, Option<ModuleId>) {
47  let identifier = module.identifier();
48  let name = module.readable_identifier(&compilation.options.context);
49  let id = ChunkGraph::get_module_id(&compilation.module_ids_artifact, identifier).cloned();
50  (name, id)
51}
52
53pub fn get_chunk_group_ordered_children<'a>(
54  stats: &'a Stats,
55  ordered_children: &HashMap<ChunkGroupOrderKey, Vec<ChunkGroupUkey>>,
56  order_key: &'a ChunkGroupOrderKey,
57  chunk_group_by_ukey: &'a ChunkGroupByUkey,
58  chunk_group_auxiliary: bool,
59) -> Vec<StatsChunkGroup<'a>> {
60  ordered_children
61    .get(order_key)
62    .unwrap_or_else(|| panic!("should have {order_key} chunk groups"))
63    .par_iter()
64    .map(|ukey| {
65      let cg = chunk_group_by_ukey.expect_get(ukey);
66      stats.get_chunk_group(
67        cg.name().unwrap_or_default(),
68        ukey,
69        chunk_group_auxiliary,
70        false,
71      )
72    })
73    .collect::<Vec<_>>()
74}
75
76pub fn get_chunk_group_oreded_child_assets<'a>(
77  ordered_children: &HashMap<ChunkGroupOrderKey, Vec<ChunkGroupUkey>>,
78  order_key: &ChunkGroupOrderKey,
79  chunk_group_by_ukey: &ChunkGroupByUkey,
80  chunk_by_ukey: &'a ChunkByUkey,
81) -> Vec<&'a str> {
82  ordered_children
83    .get(&ChunkGroupOrderKey::Preload)
84    .unwrap_or_else(|| panic!("should have {order_key} chunk groups"))
85    .iter()
86    .flat_map(|ukey| {
87      chunk_group_by_ukey
88        .expect_get(ukey)
89        .chunks
90        .iter()
91        .flat_map(|c| {
92          chunk_by_ukey
93            .expect_get(c)
94            .files()
95            .iter()
96            .map(|file| file.as_str())
97        })
98        .collect::<Vec<_>>()
99    })
100    .unique()
101    .collect::<Vec<_>>()
102}
103
104pub fn get_chunk_relations<'a>(
105  chunk: &Chunk,
106  compilation: &'a Compilation,
107) -> (Vec<&'a str>, Vec<&'a str>, Vec<&'a str>) {
108  let mut parents = HashSet::default();
109  let mut children = HashSet::default();
110  let mut siblings = HashSet::default();
111
112  for cg in chunk.groups() {
113    if let Some(cg) = compilation.chunk_group_by_ukey.get(cg) {
114      for p in &cg.parents {
115        if let Some(pg) = compilation.chunk_group_by_ukey.get(p) {
116          for c in &pg.chunks {
117            if let Some(c) = compilation.chunk_by_ukey.get(c)
118              && let Some(id) = c.id()
119            {
120              parents.insert(id.as_str());
121            }
122          }
123        }
124      }
125
126      for p in &cg.children {
127        if let Some(pg) = compilation.chunk_group_by_ukey.get(p) {
128          for c in &pg.chunks {
129            if let Some(c) = compilation.chunk_by_ukey.get(c)
130              && let Some(id) = c.id()
131            {
132              children.insert(id.as_str());
133            }
134          }
135        }
136      }
137
138      for c in &cg.chunks {
139        if let Some(c) = compilation.chunk_by_ukey.get(c)
140          && c.id() != chunk.id()
141          && let Some(id) = c.id()
142        {
143          siblings.insert(id.as_str());
144        }
145      }
146    }
147  }
148
149  let mut parents = Vec::from_iter(parents);
150  let mut children = Vec::from_iter(children);
151  let mut siblings = Vec::from_iter(siblings);
152
153  parents.sort_unstable();
154  children.sort_unstable();
155  siblings.sort_unstable();
156
157  (parents, children, siblings)
158}
159
160pub fn get_module_trace<'a>(
161  module_identifier: Option<Identifier>,
162  module_graph: &'a ModuleGraph,
163  compilation: &'a Compilation,
164  options: &CompilerOptions,
165) -> Vec<StatsModuleTrace<'a>> {
166  let mut module_trace = vec![];
167  let mut visited_modules = HashSet::<Identifier>::default();
168  let mut current_module_identifier = module_identifier;
169  while let Some(module_identifier) = current_module_identifier {
170    if visited_modules.contains(&module_identifier) {
171      break;
172    }
173    visited_modules.insert(module_identifier);
174    let Some(origin_module) = module_graph.get_issuer(&module_identifier) else {
175      break;
176    };
177    let Some(current_module) = compilation.module_by_identifier(&module_identifier) else {
178      break;
179    };
180    let origin_stats_module = StatsErrorModuleTraceModule {
181      identifier: origin_module.identifier(),
182      name: origin_module.readable_identifier(&options.context),
183      id: ChunkGraph::get_module_id(&compilation.module_ids_artifact, origin_module.identifier())
184        .cloned(),
185    };
186
187    let current_stats_module = StatsErrorModuleTraceModule {
188      identifier: current_module.identifier(),
189      name: current_module.readable_identifier(&options.context),
190      id: ChunkGraph::get_module_id(
191        &compilation.module_ids_artifact,
192        current_module.identifier(),
193      )
194      .cloned(),
195    };
196    let dependencies = module_graph
197      .get_incoming_connections(&module_identifier)
198      .filter_map(|c| {
199        let dep = module_graph.dependency_by_id(&c.dependency_id);
200        let loc = dep.loc().map(|loc| loc.to_string())?;
201        Some(StatsErrorModuleTraceDependency { loc })
202      })
203      .collect::<Vec<_>>();
204
205    module_trace.push(StatsModuleTrace {
206      origin: origin_stats_module,
207      module: current_stats_module,
208      dependencies,
209    });
210
211    current_module_identifier = Some(origin_module.identifier());
212  }
213
214  module_trace
215}