rspack_core/options/
module.rs

1use std::{
2  fmt::{self, Debug},
3  ops::{Deref, DerefMut},
4  sync::Arc,
5};
6
7use async_recursion::async_recursion;
8use bitflags::bitflags;
9use futures::future::BoxFuture;
10use rspack_cacheable::{cacheable, with::Unsupported};
11use rspack_error::Result;
12use rspack_macros::MergeFrom;
13use rspack_regex::RspackRegex;
14use rspack_util::{MergeFrom, try_all, try_any};
15use rustc_hash::FxHashMap as HashMap;
16use tokio::sync::OnceCell;
17
18use crate::{Compilation, Filename, Module, ModuleType, PublicPath, Resolve};
19
20#[derive(Debug, Default)]
21pub struct ParserOptionsMap(HashMap<String, ParserOptions>);
22
23impl Deref for ParserOptionsMap {
24  type Target = HashMap<String, ParserOptions>;
25
26  fn deref(&self) -> &Self::Target {
27    &self.0
28  }
29}
30
31impl DerefMut for ParserOptionsMap {
32  fn deref_mut(&mut self) -> &mut Self::Target {
33    &mut self.0
34  }
35}
36
37impl FromIterator<(String, ParserOptions)> for ParserOptionsMap {
38  fn from_iter<I: IntoIterator<Item = (String, ParserOptions)>>(i: I) -> Self {
39    Self(HashMap::from_iter(i))
40  }
41}
42
43impl ParserOptionsMap {
44  pub fn get<'a>(&'a self, key: &'a str) -> Option<&'a ParserOptions> {
45    self.0.get(key)
46  }
47}
48
49#[cacheable]
50#[derive(Debug, Clone, MergeFrom)]
51pub enum ParserOptions {
52  Asset(AssetParserOptions),
53  Css(CssParserOptions),
54  CssAuto(CssAutoParserOptions),
55  CssModule(CssModuleParserOptions),
56  Javascript(JavascriptParserOptions),
57  JavascriptAuto(JavascriptParserOptions),
58  JavascriptEsm(JavascriptParserOptions),
59  JavascriptDynamic(JavascriptParserOptions),
60  Json(JsonParserOptions),
61  Unknown,
62}
63
64macro_rules! get_variant {
65  ($fn_name:ident, $variant:ident, $ret_ty:ident) => {
66    pub fn $fn_name(&self) -> Option<&$ret_ty> {
67      match self {
68        Self::$variant(value) => Some(value),
69        _ => None,
70      }
71    }
72  };
73}
74
75impl ParserOptions {
76  get_variant!(get_asset, Asset, AssetParserOptions);
77  get_variant!(get_css, Css, CssParserOptions);
78  get_variant!(get_css_auto, CssAuto, CssAutoParserOptions);
79  get_variant!(get_css_module, CssModule, CssModuleParserOptions);
80  get_variant!(get_javascript, Javascript, JavascriptParserOptions);
81  get_variant!(get_javascript_auto, JavascriptAuto, JavascriptParserOptions);
82  get_variant!(get_javascript_esm, JavascriptEsm, JavascriptParserOptions);
83  get_variant!(
84    get_javascript_dynamic,
85    JavascriptDynamic,
86    JavascriptParserOptions
87  );
88  get_variant!(get_json, Json, JsonParserOptions);
89}
90
91#[cacheable]
92#[derive(Debug, Clone, Copy, MergeFrom)]
93pub enum DynamicImportMode {
94  Lazy,
95  Weak,
96  Eager,
97  LazyOnce,
98}
99
100impl From<&str> for DynamicImportMode {
101  fn from(value: &str) -> Self {
102    match value {
103      "weak" => DynamicImportMode::Weak,
104      "eager" => DynamicImportMode::Eager,
105      "lazy" => DynamicImportMode::Lazy,
106      "lazy-once" => DynamicImportMode::LazyOnce,
107      _ => {
108        // TODO: warning
109        DynamicImportMode::Lazy
110      }
111    }
112  }
113}
114
115#[cacheable]
116#[derive(Debug, Clone, Copy, MergeFrom, PartialEq, Eq, Hash, PartialOrd, Ord)]
117pub enum DynamicImportFetchPriority {
118  Low,
119  High,
120  Auto,
121}
122
123impl From<&str> for DynamicImportFetchPriority {
124  fn from(value: &str) -> Self {
125    match value {
126      "low" => DynamicImportFetchPriority::Low,
127      "high" => DynamicImportFetchPriority::High,
128      "auto" => DynamicImportFetchPriority::Auto,
129      _ => DynamicImportFetchPriority::Auto,
130    }
131  }
132}
133
134impl fmt::Display for DynamicImportFetchPriority {
135  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136    match self {
137      DynamicImportFetchPriority::Low => write!(f, "low"),
138      DynamicImportFetchPriority::High => write!(f, "high"),
139      DynamicImportFetchPriority::Auto => write!(f, "auto"),
140    }
141  }
142}
143
144#[cacheable]
145#[derive(Debug, Clone, Copy, MergeFrom)]
146pub enum JavascriptParserUrl {
147  Enable,
148  Disable,
149  Relative,
150}
151
152impl From<&str> for JavascriptParserUrl {
153  fn from(value: &str) -> Self {
154    match value {
155      "false" => Self::Disable,
156      "relative" => Self::Relative,
157      _ => Self::Enable,
158    }
159  }
160}
161
162#[cacheable]
163#[derive(Debug, Clone, Copy, MergeFrom)]
164pub enum JavascriptParserOrder {
165  Disable,
166  Order(i32),
167}
168
169impl JavascriptParserOrder {
170  pub fn get_order(&self) -> Option<i32> {
171    match self {
172      Self::Disable => None,
173      Self::Order(o) => Some(*o),
174    }
175  }
176}
177
178impl From<&str> for JavascriptParserOrder {
179  fn from(value: &str) -> Self {
180    match value {
181      "false" => Self::Disable,
182      "true" => Self::Order(0),
183      _ => {
184        if let Ok(order) = value.parse::<i32>() {
185          Self::Order(order)
186        } else {
187          Self::Order(0)
188        }
189      }
190    }
191  }
192}
193
194#[cacheable]
195#[derive(Debug, Clone, Copy, MergeFrom)]
196pub enum ExportPresenceMode {
197  None,
198  Warn,
199  Auto,
200  Error,
201}
202
203impl From<&str> for ExportPresenceMode {
204  fn from(value: &str) -> Self {
205    match value {
206      "false" => Self::None,
207      "warn" => Self::Warn,
208      "error" => Self::Error,
209      _ => Self::Auto,
210    }
211  }
212}
213
214impl ExportPresenceMode {
215  pub fn get_effective_export_presence(&self, module: &dyn Module) -> Option<bool> {
216    match self {
217      ExportPresenceMode::None => None,
218      ExportPresenceMode::Warn => Some(false),
219      ExportPresenceMode::Error => Some(true),
220      ExportPresenceMode::Auto => Some(module.build_meta().strict_esm_module),
221    }
222  }
223}
224
225#[cacheable]
226#[derive(Debug, Default, Clone, Copy, MergeFrom)]
227pub enum TypeReexportPresenceMode {
228  #[default]
229  NoTolerant,
230  Tolerant,
231  TolerantNoCheck,
232}
233
234impl From<&str> for TypeReexportPresenceMode {
235  fn from(value: &str) -> Self {
236    match value {
237      "tolerant" => Self::Tolerant,
238      "tolerant-no-check" => Self::TolerantNoCheck,
239      _ => Self::NoTolerant,
240    }
241  }
242}
243
244#[cacheable]
245#[derive(Debug, Clone, Copy, MergeFrom)]
246pub enum OverrideStrict {
247  Strict,
248  NoneStrict,
249}
250
251impl From<&str> for OverrideStrict {
252  fn from(value: &str) -> Self {
253    match value {
254      "strict" => Self::Strict,
255      "non-strict" => Self::NoneStrict,
256      _ => unreachable!("parser.overrideStrict should be 'strict' or 'non-strict'"),
257    }
258  }
259}
260
261#[cacheable]
262#[derive(Debug, Clone, MergeFrom, Default)]
263pub struct JavascriptParserOptions {
264  pub dynamic_import_mode: Option<DynamicImportMode>,
265  pub dynamic_import_preload: Option<JavascriptParserOrder>,
266  pub dynamic_import_prefetch: Option<JavascriptParserOrder>,
267  pub dynamic_import_fetch_priority: Option<DynamicImportFetchPriority>,
268  pub url: Option<JavascriptParserUrl>,
269  pub unknown_context_critical: Option<bool>,
270  pub expr_context_critical: Option<bool>,
271  pub wrapped_context_critical: Option<bool>,
272  pub wrapped_context_reg_exp: Option<RspackRegex>,
273  pub exports_presence: Option<ExportPresenceMode>,
274  pub import_exports_presence: Option<ExportPresenceMode>,
275  pub reexport_exports_presence: Option<ExportPresenceMode>,
276  pub strict_export_presence: Option<bool>,
277  pub type_reexports_presence: Option<TypeReexportPresenceMode>,
278  pub worker: Option<Vec<String>>,
279  pub override_strict: Option<OverrideStrict>,
280  pub import_meta: Option<bool>,
281  pub require_as_expression: Option<bool>,
282  pub require_dynamic: Option<bool>,
283  pub require_resolve: Option<bool>,
284  pub import_dynamic: Option<bool>,
285  pub inline_const: Option<bool>,
286}
287
288#[cacheable]
289#[derive(Debug, Clone, MergeFrom)]
290pub struct AssetParserOptions {
291  pub data_url_condition: Option<AssetParserDataUrl>,
292}
293
294#[cacheable]
295#[derive(Debug, Clone, MergeFrom)]
296pub enum AssetParserDataUrl {
297  Options(AssetParserDataUrlOptions),
298  // TODO: Function
299}
300
301#[cacheable]
302#[derive(Debug, Clone, MergeFrom)]
303pub struct AssetParserDataUrlOptions {
304  pub max_size: Option<f64>,
305}
306
307#[cacheable]
308#[derive(Debug, Clone, MergeFrom)]
309pub struct CssParserOptions {
310  pub named_exports: Option<bool>,
311  pub url: Option<bool>,
312}
313
314#[cacheable]
315#[derive(Debug, Clone, MergeFrom)]
316pub struct CssAutoParserOptions {
317  pub named_exports: Option<bool>,
318  pub url: Option<bool>,
319}
320
321impl From<CssParserOptions> for CssAutoParserOptions {
322  fn from(value: CssParserOptions) -> Self {
323    Self {
324      named_exports: value.named_exports,
325      url: value.url,
326    }
327  }
328}
329
330#[cacheable]
331#[derive(Debug, Clone, MergeFrom)]
332pub struct CssModuleParserOptions {
333  pub named_exports: Option<bool>,
334  pub url: Option<bool>,
335}
336
337impl From<CssParserOptions> for CssModuleParserOptions {
338  fn from(value: CssParserOptions) -> Self {
339    Self {
340      named_exports: value.named_exports,
341      url: value.url,
342    }
343  }
344}
345
346pub type JsonParseFn = Arc<dyn Fn(String) -> BoxFuture<'static, Result<String>> + Sync + Send>;
347
348#[cacheable]
349pub enum ParseOption {
350  Func(#[cacheable(with=Unsupported)] JsonParseFn),
351  None,
352}
353
354impl Debug for ParseOption {
355  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356    match self {
357      Self::Func(_) => write!(f, "ParseOption::Func(...)"),
358      _ => write!(f, "ParseOption::None"),
359    }
360  }
361}
362
363impl Clone for ParseOption {
364  fn clone(&self) -> Self {
365    match self {
366      Self::Func(f) => Self::Func(f.clone()),
367      Self::None => Self::None,
368    }
369  }
370}
371
372impl MergeFrom for ParseOption {
373  fn merge_from(self, other: &Self) -> Self {
374    other.clone()
375  }
376}
377
378#[cacheable]
379#[derive(Debug, Clone, MergeFrom)]
380pub struct JsonParserOptions {
381  pub exports_depth: Option<u32>,
382  pub parse: ParseOption,
383}
384
385#[derive(Debug, Default)]
386pub struct GeneratorOptionsMap(HashMap<String, GeneratorOptions>);
387
388impl Deref for GeneratorOptionsMap {
389  type Target = HashMap<String, GeneratorOptions>;
390
391  fn deref(&self) -> &Self::Target {
392    &self.0
393  }
394}
395
396impl DerefMut for GeneratorOptionsMap {
397  fn deref_mut(&mut self) -> &mut Self::Target {
398    &mut self.0
399  }
400}
401
402impl FromIterator<(String, GeneratorOptions)> for GeneratorOptionsMap {
403  fn from_iter<I: IntoIterator<Item = (String, GeneratorOptions)>>(i: I) -> Self {
404    Self(HashMap::from_iter(i))
405  }
406}
407
408impl GeneratorOptionsMap {
409  pub fn get(&self, key: &str) -> Option<&GeneratorOptions> {
410    self.0.get(key)
411  }
412}
413
414#[cacheable]
415#[derive(Debug, Clone, MergeFrom)]
416pub enum GeneratorOptions {
417  Asset(AssetGeneratorOptions),
418  AssetInline(AssetInlineGeneratorOptions),
419  AssetResource(AssetResourceGeneratorOptions),
420  Css(CssGeneratorOptions),
421  CssAuto(CssAutoGeneratorOptions),
422  CssModule(CssModuleGeneratorOptions),
423  Json(JsonGeneratorOptions),
424  Unknown,
425}
426
427impl GeneratorOptions {
428  get_variant!(get_asset, Asset, AssetGeneratorOptions);
429  get_variant!(get_asset_inline, AssetInline, AssetInlineGeneratorOptions);
430  get_variant!(
431    get_asset_resource,
432    AssetResource,
433    AssetResourceGeneratorOptions
434  );
435  get_variant!(get_css, Css, CssGeneratorOptions);
436  get_variant!(get_css_auto, CssAuto, CssAutoGeneratorOptions);
437  get_variant!(get_css_module, CssModule, CssModuleGeneratorOptions);
438  get_variant!(get_json, Json, JsonGeneratorOptions);
439
440  pub fn asset_filename(&self) -> Option<&Filename> {
441    self
442      .get_asset()
443      .and_then(|x| x.filename.as_ref())
444      .or_else(|| self.get_asset_resource().and_then(|x| x.filename.as_ref()))
445  }
446
447  pub fn asset_output_path(&self) -> Option<&Filename> {
448    self
449      .get_asset()
450      .and_then(|x| x.output_path.as_ref())
451      .or_else(|| {
452        self
453          .get_asset_resource()
454          .and_then(|x| x.output_path.as_ref())
455      })
456  }
457
458  pub fn asset_public_path(&self) -> Option<&PublicPath> {
459    self
460      .get_asset()
461      .and_then(|x| x.public_path.as_ref())
462      .or_else(|| {
463        self
464          .get_asset_resource()
465          .and_then(|x| x.public_path.as_ref())
466      })
467  }
468
469  pub fn asset_data_url(&self) -> Option<&AssetGeneratorDataUrl> {
470    self
471      .get_asset()
472      .and_then(|x| x.data_url.as_ref())
473      .or_else(|| self.get_asset_inline().and_then(|x| x.data_url.as_ref()))
474  }
475
476  pub fn asset_emit(&self) -> Option<bool> {
477    self
478      .get_asset()
479      .and_then(|x| x.emit)
480      .or_else(|| self.get_asset_resource().and_then(|x| x.emit))
481  }
482}
483
484#[cacheable]
485#[derive(Debug, Clone, MergeFrom)]
486pub struct AssetInlineGeneratorOptions {
487  pub data_url: Option<AssetGeneratorDataUrl>,
488  pub binary: Option<bool>,
489}
490
491impl From<AssetGeneratorOptions> for AssetInlineGeneratorOptions {
492  fn from(value: AssetGeneratorOptions) -> Self {
493    Self {
494      data_url: value.data_url,
495      binary: value.binary,
496    }
497  }
498}
499
500#[cacheable]
501#[derive(Debug, Clone, Copy, MergeFrom)]
502struct AssetGeneratorImportModeFlags(u8);
503bitflags! {
504  impl AssetGeneratorImportModeFlags: u8 {
505    const URL = 1 << 0;
506    const PRESERVE = 1 << 1;
507  }
508}
509
510#[cacheable]
511#[derive(Debug, Clone, Copy, MergeFrom)]
512pub struct AssetGeneratorImportMode(AssetGeneratorImportModeFlags);
513
514impl AssetGeneratorImportMode {
515  pub fn is_url(&self) -> bool {
516    self.0.contains(AssetGeneratorImportModeFlags::URL)
517  }
518  pub fn is_preserve(&self) -> bool {
519    self.0.contains(AssetGeneratorImportModeFlags::PRESERVE)
520  }
521}
522
523impl From<String> for AssetGeneratorImportMode {
524  fn from(s: String) -> Self {
525    match s.as_str() {
526      "url" => Self(AssetGeneratorImportModeFlags::URL),
527      "preserve" => Self(AssetGeneratorImportModeFlags::PRESERVE),
528      _ => unreachable!("AssetGeneratorImportMode error"),
529    }
530  }
531}
532
533impl Default for AssetGeneratorImportMode {
534  fn default() -> Self {
535    Self(AssetGeneratorImportModeFlags::URL)
536  }
537}
538
539#[cacheable]
540#[derive(Debug, Clone, MergeFrom)]
541pub struct AssetResourceGeneratorOptions {
542  pub emit: Option<bool>,
543  pub filename: Option<Filename>,
544  pub output_path: Option<Filename>,
545  pub public_path: Option<PublicPath>,
546  pub import_mode: Option<AssetGeneratorImportMode>,
547  pub binary: Option<bool>,
548}
549
550impl From<AssetGeneratorOptions> for AssetResourceGeneratorOptions {
551  fn from(value: AssetGeneratorOptions) -> Self {
552    Self {
553      emit: value.emit,
554      filename: value.filename,
555      output_path: value.output_path,
556      public_path: value.public_path,
557      import_mode: value.import_mode,
558      binary: value.binary,
559    }
560  }
561}
562
563#[cacheable]
564#[derive(Debug, Clone, MergeFrom)]
565pub struct AssetGeneratorOptions {
566  pub emit: Option<bool>,
567  pub filename: Option<Filename>,
568  pub output_path: Option<Filename>,
569  pub public_path: Option<PublicPath>,
570  pub data_url: Option<AssetGeneratorDataUrl>,
571  pub import_mode: Option<AssetGeneratorImportMode>,
572  pub binary: Option<bool>,
573}
574
575pub struct AssetGeneratorDataUrlFnCtx<'a> {
576  pub filename: String,
577  pub module: &'a dyn Module,
578  pub compilation: &'a Compilation,
579}
580
581pub type AssetGeneratorDataUrlFn = Arc<
582  dyn Fn(Vec<u8>, AssetGeneratorDataUrlFnCtx) -> BoxFuture<'static, Result<String>> + Sync + Send,
583>;
584
585#[cacheable]
586pub enum AssetGeneratorDataUrl {
587  Options(AssetGeneratorDataUrlOptions),
588  Func(#[cacheable(with=Unsupported)] AssetGeneratorDataUrlFn),
589}
590
591impl fmt::Debug for AssetGeneratorDataUrl {
592  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
593    match self {
594      Self::Options(i) => i.fmt(f),
595      Self::Func(_) => "Func(...)".fmt(f),
596    }
597  }
598}
599
600impl Clone for AssetGeneratorDataUrl {
601  fn clone(&self) -> Self {
602    match self {
603      Self::Options(i) => Self::Options(i.clone()),
604      Self::Func(i) => Self::Func(i.clone()),
605    }
606  }
607}
608
609impl MergeFrom for AssetGeneratorDataUrl {
610  fn merge_from(self, other: &Self) -> Self {
611    other.clone()
612  }
613}
614
615#[cacheable]
616#[derive(Debug, Clone, MergeFrom, Hash)]
617pub struct AssetGeneratorDataUrlOptions {
618  pub encoding: Option<DataUrlEncoding>,
619  pub mimetype: Option<String>,
620}
621
622#[cacheable]
623#[derive(Debug, Clone, MergeFrom, Hash)]
624pub enum DataUrlEncoding {
625  None,
626  Base64,
627}
628
629impl fmt::Display for DataUrlEncoding {
630  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
631    match self {
632      DataUrlEncoding::None => write!(f, ""),
633      DataUrlEncoding::Base64 => write!(f, "base64"),
634    }
635  }
636}
637
638impl From<String> for DataUrlEncoding {
639  fn from(value: String) -> Self {
640    match value.as_str() {
641      "base64" => Self::Base64,
642      "false" => Self::None,
643      _ => unreachable!("DataUrlEncoding should be base64 or false"),
644    }
645  }
646}
647
648#[cacheable]
649#[derive(Debug, Clone, MergeFrom)]
650pub struct CssGeneratorOptions {
651  pub exports_only: Option<bool>,
652  pub es_module: Option<bool>,
653}
654
655#[cacheable]
656#[derive(Default, Debug, Clone, MergeFrom)]
657pub struct CssAutoGeneratorOptions {
658  pub exports_convention: Option<CssExportsConvention>,
659  pub exports_only: Option<bool>,
660  pub local_ident_name: Option<LocalIdentName>,
661  pub es_module: Option<bool>,
662}
663
664impl From<CssGeneratorOptions> for CssAutoGeneratorOptions {
665  fn from(value: CssGeneratorOptions) -> Self {
666    Self {
667      exports_only: value.exports_only,
668      es_module: value.es_module,
669      ..Default::default()
670    }
671  }
672}
673
674#[cacheable]
675#[derive(Default, Debug, Clone, MergeFrom)]
676pub struct CssModuleGeneratorOptions {
677  pub exports_convention: Option<CssExportsConvention>,
678  pub exports_only: Option<bool>,
679  pub local_ident_name: Option<LocalIdentName>,
680  pub es_module: Option<bool>,
681}
682
683impl From<CssGeneratorOptions> for CssModuleGeneratorOptions {
684  fn from(value: CssGeneratorOptions) -> Self {
685    Self {
686      exports_only: value.exports_only,
687      es_module: value.es_module,
688      ..Default::default()
689    }
690  }
691}
692
693#[cacheable]
694#[derive(Default, Debug, Clone, MergeFrom)]
695pub struct JsonGeneratorOptions {
696  pub json_parse: Option<bool>,
697}
698
699#[cacheable]
700#[derive(Debug, Clone, MergeFrom)]
701pub struct LocalIdentName {
702  pub template: Filename,
703}
704
705impl From<String> for LocalIdentName {
706  fn from(value: String) -> Self {
707    Self {
708      template: value.into(),
709    }
710  }
711}
712
713impl From<&str> for LocalIdentName {
714  fn from(value: &str) -> Self {
715    Self {
716      template: crate::Filename::from(value),
717    }
718  }
719}
720
721#[cacheable]
722#[derive(Debug, Clone, Copy)]
723struct ExportsConventionFlags(u8);
724bitflags! {
725  impl ExportsConventionFlags: u8 {
726    const ASIS = 1 << 0;
727    const CAMELCASE = 1 << 1;
728    const DASHES = 1 << 2;
729  }
730}
731
732impl MergeFrom for ExportsConventionFlags {
733  fn merge_from(self, other: &Self) -> Self {
734    *other
735  }
736}
737
738#[cacheable]
739#[derive(Debug, Clone, Copy, MergeFrom)]
740pub struct CssExportsConvention(ExportsConventionFlags);
741
742impl CssExportsConvention {
743  pub fn as_is(&self) -> bool {
744    self.0.contains(ExportsConventionFlags::ASIS)
745  }
746
747  pub fn camel_case(&self) -> bool {
748    self.0.contains(ExportsConventionFlags::CAMELCASE)
749  }
750
751  pub fn dashes(&self) -> bool {
752    self.0.contains(ExportsConventionFlags::DASHES)
753  }
754}
755
756impl From<String> for CssExportsConvention {
757  fn from(s: String) -> Self {
758    match s.as_str() {
759      "as-is" => Self(ExportsConventionFlags::ASIS),
760      "camel-case" => Self(ExportsConventionFlags::ASIS | ExportsConventionFlags::CAMELCASE),
761      "camel-case-only" => Self(ExportsConventionFlags::CAMELCASE),
762      "dashes" => Self(ExportsConventionFlags::ASIS | ExportsConventionFlags::DASHES),
763      "dashes-only" => Self(ExportsConventionFlags::DASHES),
764      _ => unreachable!("css exportsConventions error"),
765    }
766  }
767}
768
769impl Default for CssExportsConvention {
770  fn default() -> Self {
771    Self(ExportsConventionFlags::ASIS)
772  }
773}
774
775pub type DescriptionData = HashMap<String, RuleSetConditionWithEmpty>;
776pub type With = HashMap<String, RuleSetConditionWithEmpty>;
777
778pub type RuleSetConditionFnMatcher =
779  Box<dyn Fn(DataRef) -> BoxFuture<'static, Result<bool>> + Sync + Send>;
780
781pub enum RuleSetCondition {
782  String(String),
783  Regexp(RspackRegex),
784  Logical(Box<RuleSetLogicalConditions>),
785  Array(Vec<RuleSetCondition>),
786  Func(RuleSetConditionFnMatcher),
787}
788
789impl fmt::Debug for RuleSetCondition {
790  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
791    match self {
792      Self::String(i) => i.fmt(f),
793      Self::Regexp(i) => i.fmt(f),
794      Self::Logical(i) => i.fmt(f),
795      Self::Array(i) => i.fmt(f),
796      Self::Func(_) => "Func(...)".fmt(f),
797    }
798  }
799}
800
801#[derive(Copy, Clone)]
802pub enum DataRef<'a> {
803  Str(&'a str),
804  Value(&'a serde_json::Value),
805}
806
807impl<'s> From<&'s str> for DataRef<'s> {
808  fn from(value: &'s str) -> Self {
809    Self::Str(value)
810  }
811}
812
813impl<'s> From<&'s serde_json::Value> for DataRef<'s> {
814  fn from(value: &'s serde_json::Value) -> Self {
815    Self::Value(value)
816  }
817}
818
819impl DataRef<'_> {
820  pub fn as_str(&self) -> Option<&str> {
821    match self {
822      Self::Str(s) => Some(s),
823      Self::Value(v) => v.as_str(),
824    }
825  }
826
827  pub fn to_value(&self) -> serde_json::Value {
828    match self {
829      Self::Str(s) => serde_json::Value::String((*s).to_owned()),
830      Self::Value(v) => (*v).to_owned(),
831    }
832  }
833}
834
835impl RuleSetCondition {
836  #[async_recursion]
837  pub async fn try_match(&self, data: DataRef<'async_recursion>) -> Result<bool> {
838    match self {
839      Self::String(s) => Ok(
840        data
841          .as_str()
842          .map(|data| data.starts_with(s))
843          .unwrap_or_default(),
844      ),
845      Self::Regexp(r) => Ok(data.as_str().map(|data| r.test(data)).unwrap_or_default()),
846      Self::Logical(g) => g.try_match(data).await,
847      Self::Array(l) => try_any(l, |i| async { i.try_match(data).await }).await,
848      Self::Func(f) => f(data).await,
849    }
850  }
851
852  #[async_recursion]
853  async fn match_when_empty(&self) -> Result<bool> {
854    let res = match self {
855      RuleSetCondition::String(s) => s.is_empty(),
856      RuleSetCondition::Regexp(rspack_regex) => rspack_regex.test(""),
857      RuleSetCondition::Logical(logical) => logical.match_when_empty().await?,
858      RuleSetCondition::Array(arr) => {
859        arr.is_empty() && try_any(arr, |c| async move { c.match_when_empty().await }).await?
860      }
861      RuleSetCondition::Func(func) => func("".into()).await?,
862    };
863    Ok(res)
864  }
865}
866
867#[derive(Debug)]
868pub struct RuleSetConditionWithEmpty {
869  condition: RuleSetCondition,
870  match_when_empty: OnceCell<bool>,
871}
872
873impl RuleSetConditionWithEmpty {
874  pub fn new(condition: RuleSetCondition) -> Self {
875    Self {
876      condition,
877      match_when_empty: OnceCell::new(),
878    }
879  }
880
881  pub async fn try_match(&self, data: DataRef<'_>) -> Result<bool> {
882    self.condition.try_match(data).await
883  }
884
885  pub async fn match_when_empty(&self) -> Result<bool> {
886    self
887      .match_when_empty
888      .get_or_try_init(|| async { self.condition.match_when_empty().await })
889      .await
890      .copied()
891  }
892}
893
894impl From<RuleSetCondition> for RuleSetConditionWithEmpty {
895  fn from(condition: RuleSetCondition) -> Self {
896    Self::new(condition)
897  }
898}
899
900#[derive(Debug, Default)]
901pub struct RuleSetLogicalConditions {
902  pub and: Option<Vec<RuleSetCondition>>,
903  pub or: Option<Vec<RuleSetCondition>>,
904  pub not: Option<RuleSetCondition>,
905}
906
907impl RuleSetLogicalConditions {
908  #[async_recursion]
909  pub async fn try_match(&self, data: DataRef<'async_recursion>) -> Result<bool> {
910    if let Some(and) = &self.and
911      && try_any(and, |i| async { i.try_match(data).await.map(|i| !i) }).await?
912    {
913      return Ok(false);
914    }
915    if let Some(or) = &self.or
916      && try_all(or, |i| async { i.try_match(data).await.map(|i| !i) }).await?
917    {
918      return Ok(false);
919    }
920    if let Some(not) = &self.not
921      && not.try_match(data).await?
922    {
923      return Ok(false);
924    }
925    Ok(true)
926  }
927
928  pub async fn match_when_empty(&self) -> Result<bool> {
929    let mut has_condition = false;
930    let mut match_when_empty = true;
931    if let Some(and) = &self.and {
932      has_condition = true;
933      match_when_empty &= try_all(and, |i| async { i.match_when_empty().await }).await?;
934    }
935    if let Some(or) = &self.or {
936      has_condition = true;
937      match_when_empty &= try_any(or, |i| async { i.match_when_empty().await }).await?;
938    }
939    if let Some(not) = &self.not {
940      has_condition = true;
941      match_when_empty &= !not.match_when_empty().await?;
942    }
943    Ok(has_condition && match_when_empty)
944  }
945}
946
947pub struct FuncUseCtx {
948  pub resource: Option<String>,
949  pub real_resource: Option<String>,
950  pub resource_query: Option<String>,
951  pub resource_fragment: Option<String>,
952  pub issuer: Option<Box<str>>,
953  pub issuer_layer: Option<String>,
954}
955
956#[derive(Debug, Clone)]
957pub struct ModuleRuleUseLoader {
958  /// Loader identifier with query and fragments
959  /// Loader ident or query will be appended if it exists.
960  pub loader: String,
961  /// Loader options
962  /// This only exists if the loader is a built-in loader.
963  pub options: Option<String>,
964}
965
966pub type FnUse =
967  Box<dyn Fn(FuncUseCtx) -> BoxFuture<'static, Result<Vec<ModuleRuleUseLoader>>> + Sync + Send>;
968
969#[derive(Debug, Default)]
970pub struct ModuleRule {
971  /// A conditional match matching an absolute path + query + fragment.
972  /// Note:
973  ///   This is a custom matching rule not initially designed by webpack.
974  ///   Only for single-threaded environment interoperation purpose.
975  pub rspack_resource: Option<RuleSetCondition>,
976  /// A condition matcher matching an absolute path.
977  pub test: Option<RuleSetCondition>,
978  pub include: Option<RuleSetCondition>,
979  pub exclude: Option<RuleSetCondition>,
980  /// A condition matcher matching an absolute path.
981  pub resource: Option<RuleSetCondition>,
982  /// A condition matcher against the resource query.
983  pub resource_query: Option<RuleSetConditionWithEmpty>,
984  pub resource_fragment: Option<RuleSetConditionWithEmpty>,
985  pub dependency: Option<RuleSetCondition>,
986  pub issuer: Option<RuleSetConditionWithEmpty>,
987  pub issuer_layer: Option<RuleSetConditionWithEmpty>,
988  pub scheme: Option<RuleSetConditionWithEmpty>,
989  pub mimetype: Option<RuleSetConditionWithEmpty>,
990  pub description_data: Option<DescriptionData>,
991  pub with: Option<With>,
992  pub one_of: Option<Vec<ModuleRule>>,
993  pub rules: Option<Vec<ModuleRule>>,
994  pub effect: ModuleRuleEffect,
995}
996
997#[derive(Debug, Default)]
998pub struct ModuleRuleEffect {
999  pub side_effects: Option<bool>,
1000  /// The `ModuleType` to use for the matched resource.
1001  pub r#type: Option<ModuleType>,
1002  pub layer: Option<String>,
1003  pub r#use: ModuleRuleUse,
1004  pub parser: Option<ParserOptions>,
1005  pub generator: Option<GeneratorOptions>,
1006  pub resolve: Option<Resolve>,
1007  pub enforce: ModuleRuleEnforce,
1008}
1009
1010pub enum ModuleRuleUse {
1011  Array(Vec<ModuleRuleUseLoader>),
1012  Func(FnUse),
1013}
1014
1015impl Default for ModuleRuleUse {
1016  fn default() -> Self {
1017    Self::Array(vec![])
1018  }
1019}
1020
1021impl Debug for ModuleRuleUse {
1022  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
1023    match self {
1024      ModuleRuleUse::Array(array_use) => write!(
1025        f,
1026        "{}",
1027        array_use
1028          .iter()
1029          .map(|l| &*l.loader)
1030          .collect::<Vec<_>>()
1031          .join("!")
1032      ),
1033      ModuleRuleUse::Func(_) => write!(f, "Fn(...)"),
1034    }
1035  }
1036}
1037
1038pub type ModuleNoParseTestFn =
1039  Box<dyn Fn(String) -> BoxFuture<'static, Result<bool>> + Sync + Send>;
1040
1041pub enum ModuleNoParseRule {
1042  AbsPathPrefix(String),
1043  Regexp(RspackRegex),
1044  TestFn(ModuleNoParseTestFn),
1045}
1046
1047impl Debug for ModuleNoParseRule {
1048  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1049    match self {
1050      Self::TestFn(_) => "Fn(...)".fmt(f),
1051      _ => self.fmt(f),
1052    }
1053  }
1054}
1055
1056impl ModuleNoParseRule {
1057  pub async fn try_match(&self, request: &str) -> Result<bool> {
1058    match self {
1059      Self::AbsPathPrefix(s) => Ok(request.starts_with(s)),
1060      Self::Regexp(reg) => Ok(reg.test(request)),
1061      Self::TestFn(func) => func(request.to_string()).await,
1062    }
1063  }
1064}
1065
1066#[derive(Debug)]
1067pub enum ModuleNoParseRules {
1068  Rule(ModuleNoParseRule),
1069  Rules(Vec<ModuleNoParseRule>),
1070}
1071
1072impl ModuleNoParseRules {
1073  #[async_recursion]
1074  pub async fn try_match(&self, request: &str) -> Result<bool> {
1075    match self {
1076      Self::Rule(r) => r.try_match(request).await,
1077      Self::Rules(list) => try_any(list, |r| r.try_match(request)).await,
1078    }
1079  }
1080}
1081
1082#[derive(Debug, Default)]
1083pub enum ModuleRuleEnforce {
1084  Post,
1085  #[default]
1086  Normal,
1087  Pre,
1088}
1089
1090// BE CAREFUL:
1091// Add more fields to this struct should result in adding new fields to options builder.
1092// `impl From<ModuleOptions> for ModuleOptionsBuilder` should be updated.
1093#[derive(Debug, Default)]
1094pub struct ModuleOptions {
1095  pub rules: Vec<ModuleRule>,
1096  pub parser: Option<ParserOptionsMap>,
1097  pub generator: Option<GeneratorOptionsMap>,
1098  pub no_parse: Option<ModuleNoParseRules>,
1099}