rspack_plugin_javascript/visitors/dependency/
util.rs1use 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}