rspack_plugin_dynamic_entry/
lib.rs1use std::sync::Arc;
2
3use atomic_refcell::AtomicRefCell;
4use derive_more::Debug;
5use futures::future::BoxFuture;
6use rspack_core::{
7 BoxDependency, Compilation, CompilationParams, CompilerCompilation, CompilerMake, Context,
8 DependencyId, DependencyType, EntryDependency, EntryOptions, Plugin,
9 incremental::IncrementalPasses, internal,
10};
11use rspack_error::Result;
12use rspack_hook::{plugin, plugin_hook};
13use rspack_util::fx_hash::FxHashMap;
14
15pub struct EntryDynamicResult {
16 pub import: Vec<String>,
17 pub options: EntryOptions,
18}
19
20type EntryDynamic =
21 Box<dyn for<'a> Fn() -> BoxFuture<'static, Result<Vec<EntryDynamicResult>>> + Sync + Send>;
22
23pub struct DynamicEntryPluginOptions {
24 pub context: Context,
25 pub entry: EntryDynamic,
26}
27
28#[plugin]
29#[derive(Debug)]
30pub struct DynamicEntryPlugin {
31 context: Context,
32 #[debug(skip)]
33 entry: EntryDynamic,
34 imported_dependencies: AtomicRefCell<FxHashMap<Arc<str>, FxHashMap<EntryOptions, DependencyId>>>,
37}
38
39impl DynamicEntryPlugin {
40 pub fn new(options: DynamicEntryPluginOptions) -> Self {
41 Self::new_inner(options.context, options.entry, Default::default())
42 }
43}
44
45#[plugin_hook(CompilerCompilation for DynamicEntryPlugin)]
46async fn compilation(
47 &self,
48 compilation: &mut Compilation,
49 params: &mut CompilationParams,
50) -> Result<()> {
51 compilation.set_dependency_factory(DependencyType::Entry, params.normal_module_factory.clone());
52 Ok(())
53}
54
55#[plugin_hook(CompilerMake for DynamicEntryPlugin)]
56async fn make(&self, compilation: &mut Compilation) -> Result<()> {
57 let entry_fn = &self.entry;
58 let decs = entry_fn().await?;
59
60 if compilation
61 .incremental
62 .mutations_readable(IncrementalPasses::BUILD_MODULE_GRAPH)
63 {
64 let mut imported_dependencies = self.imported_dependencies.borrow_mut();
65 let mut next_imported_dependencies: FxHashMap<Arc<str>, FxHashMap<EntryOptions, DependencyId>> =
66 Default::default();
67
68 for EntryDynamicResult { import, options } in decs {
69 for entry in import {
70 let module_graph = compilation.get_module_graph();
71
72 let entry_dependency: BoxDependency = if let Some(map) =
73 imported_dependencies.get(entry.as_str())
74 && let Some(dependency_id) = map.get(&options)
75 && let Some(dependency) = internal::try_dependency_by_id(module_graph, dependency_id)
76 {
77 next_imported_dependencies
78 .entry(entry.into())
79 .or_default()
80 .insert(options.clone(), *dependency_id);
81 dependency.clone()
82 } else {
83 let dependency: BoxDependency = Box::new(EntryDependency::new(
84 entry.clone(),
85 self.context.clone(),
86 options.layer.clone(),
87 false,
88 ));
89 next_imported_dependencies
90 .entry(entry.into())
91 .or_default()
92 .insert(options.clone(), *dependency.id());
93 dependency
94 };
95 compilation
96 .add_entry(entry_dependency, options.clone())
97 .await?;
98 }
99 }
100
101 *imported_dependencies = next_imported_dependencies;
102 } else {
103 let mut imported_dependencies = self.imported_dependencies.borrow_mut();
107 let mut next_imported_dependencies: FxHashMap<Arc<str>, FxHashMap<EntryOptions, DependencyId>> =
108 Default::default();
109 for EntryDynamicResult { import, options } in decs {
110 for entry in import {
111 let entry_dependency: BoxDependency = Box::new(EntryDependency::new(
112 entry.clone(),
113 self.context.clone(),
114 options.layer.clone(),
115 false,
116 ));
117 next_imported_dependencies
118 .entry(entry.clone().into())
119 .or_default()
120 .insert(options.clone(), *entry_dependency.id());
121 compilation
122 .add_entry(entry_dependency, options.clone())
123 .await?;
124 }
125 }
126 *imported_dependencies = next_imported_dependencies;
127 }
128
129 Ok(())
130}
131
132impl Plugin for DynamicEntryPlugin {
133 fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> Result<()> {
134 ctx.compiler_hooks.compilation.tap(compilation::new(self));
135 ctx.compiler_hooks.make.tap(make::new(self));
136 Ok(())
137 }
138}