rspack_ids/
occurrence_chunk_ids_plugin.rs1use 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}