rspack_plugin_dynamic_entry/
lib.rs

1use std::sync::Arc;
2
3use derive_more::Debug;
4use futures::future::BoxFuture;
5use rspack_core::{
6  BoxDependency, Compilation, CompilationParams, CompilerCompilation, CompilerMake, Context,
7  DependencyType, EntryDependency, EntryOptions, Plugin,
8};
9use rspack_error::Result;
10use rspack_hook::{plugin, plugin_hook};
11use rspack_util::fx_hash::{FxDashMap, FxHashMap};
12
13pub struct EntryDynamicResult {
14  pub import: Vec<String>,
15  pub options: EntryOptions,
16}
17
18type EntryDynamic =
19  Box<dyn for<'a> Fn() -> BoxFuture<'static, Result<Vec<EntryDynamicResult>>> + Sync + Send>;
20
21pub struct DynamicEntryPluginOptions {
22  pub context: Context,
23  pub entry: EntryDynamic,
24}
25
26#[plugin]
27#[derive(Debug)]
28pub struct DynamicEntryPlugin {
29  context: Context,
30  #[debug(skip)]
31  entry: EntryDynamic,
32  // Need "cache" the dependency to tell incremental that this entry dependency is not changed
33  // so it can be reused and skip the module make
34  dependencies_map: FxDashMap<Arc<str>, FxHashMap<EntryOptions, BoxDependency>>,
35}
36
37impl DynamicEntryPlugin {
38  pub fn new(options: DynamicEntryPluginOptions) -> Self {
39    Self::new_inner(options.context, options.entry, Default::default())
40  }
41}
42
43#[plugin_hook(CompilerCompilation for DynamicEntryPlugin)]
44async fn compilation(
45  &self,
46  compilation: &mut Compilation,
47  params: &mut CompilationParams,
48) -> Result<()> {
49  compilation.set_dependency_factory(DependencyType::Entry, params.normal_module_factory.clone());
50  Ok(())
51}
52
53#[plugin_hook(CompilerMake for DynamicEntryPlugin)]
54async fn make(&self, compilation: &mut Compilation) -> Result<()> {
55  let entry_fn = &self.entry;
56  let decs = entry_fn().await?;
57  for EntryDynamicResult { import, options } in decs {
58    for entry in import {
59      let dependency: BoxDependency = if let Some(map) = self.dependencies_map.get(entry.as_str())
60        && let Some(dependency) = map.get(&options)
61      {
62        dependency.clone()
63      } else {
64        let dependency: BoxDependency = Box::new(EntryDependency::new(
65          entry.clone(),
66          self.context.clone(),
67          options.layer.clone(),
68          false,
69        ));
70        if let Some(mut map) = self.dependencies_map.get_mut(entry.as_str()) {
71          map.insert(options.clone(), dependency.clone());
72        } else {
73          let mut map = FxHashMap::default();
74          map.insert(options.clone(), dependency.clone());
75          self.dependencies_map.insert(entry.into(), map);
76        }
77        dependency
78      };
79      compilation.add_entry(dependency, options.clone()).await?;
80    }
81  }
82  Ok(())
83}
84
85impl Plugin for DynamicEntryPlugin {
86  fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> Result<()> {
87    ctx.compiler_hooks.compilation.tap(compilation::new(self));
88    ctx.compiler_hooks.make.tap(make::new(self));
89    Ok(())
90  }
91}