rspack_ids/
occurrence_chunk_ids_plugin.rs

1use std::collections::HashMap;
2
3use itertools::Itertools;
4use rspack_collections::DatabaseItem;
5use rspack_core::{Chunk, CompilationChunkIds, Plugin, incremental::IncrementalPasses};
6use rspack_error::Result;
7use rspack_hook::{plugin, plugin_hook};
8
9use crate::id_helpers::{assign_ascending_chunk_ids, compare_chunks_natural};
10
11#[derive(Debug)]
12pub struct OccurrenceChunkIdsPluginOptions {
13  pub prioritise_initial: bool,
14}
15
16#[plugin]
17#[derive(Debug, Default)]
18pub struct OccurrenceChunkIdsPlugin {
19  prioritise_initial: bool,
20}
21
22impl OccurrenceChunkIdsPlugin {
23  pub fn new(option: OccurrenceChunkIdsPluginOptions) -> Self {
24    Self::new_inner(option.prioritise_initial)
25  }
26}
27
28#[plugin_hook(CompilationChunkIds for OccurrenceChunkIdsPlugin)]
29async fn chunk_ids(&self, compilation: &mut rspack_core::Compilation) -> Result<()> {
30  if let Some(diagnostic) = compilation.incremental.disable_passes(
31    IncrementalPasses::CHUNK_IDS,
32    "OccurrenceChunkIdsPlugin (optimization.chunkIds = \"size\")",
33    "it requires calculating the id of all the chunks, which is a global effect",
34  ) {
35    if let Some(diagnostic) = diagnostic {
36      compilation.push_diagnostic(diagnostic);
37    }
38    compilation.chunk_ids_artifact.clear();
39  }
40
41  let chunk_graph = &compilation.chunk_graph;
42  let module_graph = &compilation.get_module_graph();
43  let chunk_group_by_ukey = &compilation.chunk_group_by_ukey;
44  let mut occurs_in_initial_chunks_map = HashMap::new();
45
46  for chunk in compilation.chunk_by_ukey.values() {
47    let mut occurs = 0;
48    for chunk_group_ukey in chunk.groups() {
49      if let Some(chunk_group) = chunk_group_by_ukey.get(chunk_group_ukey) {
50        for parent_ukey in &chunk_group.parents {
51          if let Some(parent) = chunk_group_by_ukey.get(parent_ukey)
52            && parent.is_initial()
53          {
54            occurs += 1;
55          }
56        }
57      }
58    }
59    occurs_in_initial_chunks_map.insert(chunk.ukey(), occurs);
60  }
61
62  let mut ordered_chunk_modules_cache = Default::default();
63  let chunks = compilation
64    .chunk_by_ukey
65    .values()
66    .map(|chunk| chunk as &Chunk)
67    .sorted_unstable_by(|a, b| {
68      if self.prioritise_initial {
69        let a_entry_occurs = occurs_in_initial_chunks_map.get(&a.ukey()).unwrap_or(&0);
70        let b_entry_occurs = occurs_in_initial_chunks_map.get(&b.ukey()).unwrap_or(&0);
71        if a_entry_occurs != b_entry_occurs {
72          return b_entry_occurs.cmp(a_entry_occurs);
73        }
74      }
75
76      let a_occurs = a.get_number_of_groups();
77      let b_occurs = b.get_number_of_groups();
78      if a_occurs != b_occurs {
79        return b_occurs.cmp(&a_occurs);
80      }
81
82      compare_chunks_natural(
83        chunk_graph,
84        module_graph,
85        &compilation.chunk_group_by_ukey,
86        &compilation.module_ids_artifact,
87        a,
88        b,
89        &mut ordered_chunk_modules_cache,
90      )
91    })
92    .map(|chunk| chunk.ukey())
93    .collect::<Vec<_>>();
94
95  assign_ascending_chunk_ids(&chunks, compilation);
96
97  Ok(())
98}
99
100impl Plugin for OccurrenceChunkIdsPlugin {
101  fn name(&self) -> &'static str {
102    "rspack.OccurrenceChunkIdsPlugin"
103  }
104
105  fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> Result<()> {
106    ctx.compilation_hooks.chunk_ids.tap(chunk_ids::new(self));
107    Ok(())
108  }
109}