Skip to main content

rspack_plugin_javascript/visitors/dependency/
util.rs

1use std::sync::LazyLock;
2
3use rspack_core::DependencyRange;
4use rspack_error::{Diagnostic, Error, Severity};
5use rspack_regex::RspackRegex;
6use swc_core::{
7  atoms::Atom,
8  ecma::ast::{Expr, MemberExpr, OptChainBase},
9};
10
11use super::JavascriptParser;
12
13static DEFAULT_CONTEXT_REGEXP: LazyLock<RspackRegex> =
14  LazyLock::new(|| RspackRegex::new(r"^\.\/.*$").expect("reg failed"));
15
16pub mod expr_name {
17  pub const MODULE: &str = "module";
18  pub const MODULE_HOT: &str = "module.hot";
19  pub const MODULE_HOT_ACCEPT: &str = "module.hot.accept";
20  pub const MODULE_HOT_DECLINE: &str = "module.hot.decline";
21  pub const MODULE_REQUIRE: &str = "module.require";
22  pub const REQUIRE: &str = "require";
23  pub const REQUIRE_RESOLVE: &str = "require.resolve";
24  pub const REQUIRE_RESOLVE_WEAK: &str = "require.resolveWeak";
25  pub const IMPORT_META: &str = "import.meta";
26  pub const IMPORT_META_FILENAME: &str = "import.meta.filename";
27  pub const IMPORT_META_DIRNAME: &str = "import.meta.dirname";
28  pub const IMPORT_META_URL: &str = "import.meta.url";
29  pub const IMPORT_META_RESOLVE: &str = "import.meta.resolve";
30  pub const IMPORT_META_VERSION: &str = "import.meta.webpack";
31  pub const IMPORT_META_MAIN: &str = "import.meta.main";
32  pub const IMPORT_META_RSPACK_RSC: &str = "import.meta.rspackRsc";
33  pub const IMPORT_META_HOT: &str = "import.meta.webpackHot";
34  pub const IMPORT_META_HOT_ACCEPT: &str = "import.meta.webpackHot.accept";
35  pub const IMPORT_META_HOT_DECLINE: &str = "import.meta.webpackHot.decline";
36  pub const IMPORT_META_CONTEXT: &str = "import.meta.webpackContext";
37}
38
39pub fn parse_order_string(x: &str) -> Option<i32> {
40  match x {
41    "true" => Some(0),
42    "false" => None,
43    _ => x.parse::<i32>().ok(),
44  }
45}
46
47pub fn create_traceable_error(
48  title: String,
49  message: String,
50  source: String,
51  span: DependencyRange,
52) -> Error {
53  Error::from_string(
54    Some(source),
55    span.start as usize,
56    span.end as usize,
57    title,
58    message,
59  )
60}
61
62#[inline]
63pub fn default_context_reg_exp() -> RspackRegex {
64  DEFAULT_CONTEXT_REGEXP.clone()
65}
66
67pub fn context_reg_exp(
68  expr: &str,
69  flags: &str,
70  error_span: Option<DependencyRange>,
71  parser: &mut JavascriptParser,
72) -> Option<RspackRegex> {
73  if expr.is_empty() {
74    return None;
75  }
76  let regexp = RspackRegex::with_flags(expr, flags).expect("reg failed");
77  clean_regexp_in_context_module(regexp, error_span, parser)
78}
79
80pub fn clean_regexp_in_context_module(
81  regexp: RspackRegex,
82  error_span: Option<DependencyRange>,
83  parser: &mut JavascriptParser,
84) -> Option<RspackRegex> {
85  if regexp.sticky() || regexp.global() {
86    if let Some(error_span) = error_span {
87      let mut error = create_traceable_error(
88        "Critical dependency".into(),
89        "Contexts can't use RegExps with the 'g' or 'y' flags".to_string(),
90        parser.source.to_string(),
91        error_span,
92      );
93      error.severity = Severity::Warning;
94      parser.add_warning(Diagnostic::from(error));
95    } else {
96      let mut err = Error::warning("Contexts can't use RegExps with the 'g' or 'y' flags".into());
97      err.code = Some("Critical dependency".into());
98      parser.add_warning(err.into());
99    }
100    None
101  } else {
102    Some(regexp)
103  }
104}
105
106pub fn get_non_optional_part<'a>(members: &'a [Atom], members_optionals: &[bool]) -> &'a [Atom] {
107  let mut i = 0;
108  while i < members.len() && matches!(members_optionals.get(i), Some(false)) {
109    i += 1;
110  }
111  if i != members.len() {
112    &members[0..i]
113  } else {
114    members
115  }
116}
117
118pub fn get_non_optional_member_chain_from_expr(mut expr: &Expr, mut count: i32) -> &Expr {
119  while count != 0 {
120    if let Expr::Member(member) = expr {
121      expr = &member.obj;
122      count -= 1;
123    } else if let Expr::OptChain(opt_chain) = expr {
124      expr = match &*opt_chain.base {
125        OptChainBase::Member(member) => &*member.obj,
126        OptChainBase::Call(call) if call.callee.as_member().is_some() => {
127          let member = call
128            .callee
129            .as_member()
130            .expect("`call.callee` is `MemberExpr` in `if_guard`");
131          &*member.obj
132        }
133        _ => unreachable!(),
134      };
135      count -= 1;
136    } else {
137      unreachable!()
138    }
139  }
140  expr
141}
142
143pub fn get_non_optional_member_chain_from_member(member: &MemberExpr, mut count: i32) -> &Expr {
144  count -= 1;
145  get_non_optional_member_chain_from_expr(&member.obj, count)
146}