@@ -8,102 +8,220 @@ use rspack_cacheable::{
8
8
cacheable,
9
9
with:: { AsString , AsStringConverter } ,
10
10
} ;
11
- use rspack_error:: Error ;
11
+ use rspack_error:: { Error , error } ;
12
12
use swc_core:: ecma:: ast:: Regex as SwcRegex ;
13
13
14
14
use self :: algo:: Algo ;
15
15
16
- /// Using wrapper type required by [TryFrom] trait
17
16
#[ cacheable( with=AsString ) ]
18
- #[ derive( Clone ) ]
19
- pub struct RspackRegex {
20
- algo : Box < Algo > ,
21
- pub flags : String ,
22
- pub source : String ,
23
- }
24
-
25
- impl PartialEq for RspackRegex {
26
- fn eq ( & self , other : & Self ) -> bool {
27
- self . flags == other. flags && self . source == other. source
28
- }
29
- }
30
-
31
- impl Eq for RspackRegex { }
32
-
33
- impl std:: hash:: Hash for RspackRegex {
34
- fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
35
- self . flags . hash ( state) ;
36
- self . source . hash ( state) ;
37
- }
38
- }
39
-
40
- impl Debug for RspackRegex {
41
- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
42
- f. debug_struct ( "RspackRegex" )
43
- . field ( "flags" , & self . flags )
44
- . field ( "source" , & self . source )
45
- . finish ( )
46
- }
17
+ #[ derive( Clone , PartialEq , Eq , Hash , Debug ) ]
18
+ pub enum RspackRegex {
19
+ Regex ( RspackNativeRegex ) ,
20
+ Regress ( RspackRegressRegex ) ,
47
21
}
48
22
49
23
impl RspackRegex {
50
24
#[ inline]
51
25
pub fn test ( & self , text : & str ) -> bool {
52
- self . algo . test ( text)
26
+ match self {
27
+ Self :: Regex ( regex) => regex. regex . is_match ( text) ,
28
+ Self :: Regress ( regress) => regress. algo . test ( text) ,
29
+ }
53
30
}
54
31
55
32
#[ inline]
56
33
pub fn global ( & self ) -> bool {
57
- self . algo . global ( )
34
+ match self {
35
+ // return false for native regex otherwise context options will emit warning
36
+ // but it is safe to do so because we can not use regex to capture multiple matches
37
+ Self :: Regex ( regex) => regex. flags . contains ( 'g' ) ,
38
+ Self :: Regress ( regress) => regress. algo . global ( ) ,
39
+ }
58
40
}
59
41
60
42
#[ inline]
61
43
pub fn sticky ( & self ) -> bool {
62
- self . algo . sticky ( )
44
+ match self {
45
+ Self :: Regex ( regex) => regex. flags . contains ( 'y' ) ,
46
+ Self :: Regress ( regress) => regress. algo . sticky ( ) ,
47
+ }
63
48
}
64
49
65
50
#[ inline]
66
51
pub fn source ( & self ) -> & str {
67
- & self . source
52
+ match self {
53
+ Self :: Regex ( regex) => & regex. source ,
54
+ Self :: Regress ( regress) => & regress. source ,
55
+ }
68
56
}
69
57
70
58
#[ inline]
71
59
pub fn flags ( & self ) -> & str {
72
- & self . flags
60
+ match self {
61
+ Self :: Regex ( regex) => & regex. flags ,
62
+ Self :: Regress ( regress) => & regress. flags ,
63
+ }
73
64
}
74
65
75
66
#[ inline]
76
67
pub fn new ( expr : & str ) -> Result < Self , Error > {
77
- Self :: with_flags ( expr, "" )
68
+ match RspackNativeRegex :: with_flags ( expr, "" ) {
69
+ Ok ( regex) => Ok ( Self :: Regex ( regex) ) ,
70
+ Err ( e) => {
71
+ println ! ( "create native regex failed: {expr} {e}" ) ;
72
+ let regress = RspackRegressRegex :: with_flags ( expr, "" ) ?;
73
+ Ok ( Self :: Regress ( regress) )
74
+ }
75
+ }
78
76
}
79
77
80
78
pub fn with_flags ( expr : & str , flags : & str ) -> Result < Self , Error > {
81
- let mut chars = flags. chars ( ) . collect :: < Vec < char > > ( ) ;
82
- chars. sort_unstable ( ) ;
83
- Ok ( Self {
84
- flags : chars. into_iter ( ) . collect :: < String > ( ) ,
85
- source : expr. to_string ( ) ,
86
- algo : Box :: new ( Algo :: new ( expr, flags) ?) ,
87
- } )
79
+ match RspackNativeRegex :: with_flags ( expr, flags) {
80
+ Ok ( regex) => Ok ( Self :: Regex ( regex) ) ,
81
+ Err ( e) => {
82
+ println ! ( "create native regex failed: {expr} with {flags} {e}" ) ;
83
+ let regress = RspackRegressRegex :: with_flags ( expr, flags) ?;
84
+ Ok ( Self :: Regress ( regress) )
85
+ }
86
+ }
88
87
}
89
88
90
89
// https://github.com/webpack/webpack/blob/4baf1c075d59babd028f8201526cb8c4acfd24a0/lib/dependencies/ContextDependency.js#L30
91
90
#[ inline]
92
91
pub fn to_source_string ( & self ) -> String {
93
- format ! ( "/{}/{}" , self . source, self . flags)
92
+ match self {
93
+ Self :: Regex ( regex) => format ! ( "/{}/{}" , regex. source, regex. flags) ,
94
+ Self :: Regress ( regress) => format ! ( "/{}/{}" , regress. source, regress. flags) ,
95
+ }
94
96
}
95
97
96
98
// https://github.com/webpack/webpack/blob/4baf1c075d59babd028f8201526cb8c4acfd24a0/lib/ContextModule.js#L192
97
99
#[ inline]
98
100
pub fn to_pretty_string ( & self , strip_slash : bool ) -> String {
99
- if strip_slash {
100
- format ! ( "{}{}" , self . source, self . flags)
101
+ let res = if strip_slash {
102
+ match self {
103
+ Self :: Regex ( regex) => format ! ( "{}{}" , regex. source, regex. flags) ,
104
+ Self :: Regress ( regress) => format ! ( "{}{}" , regress. source, regress. flags) ,
105
+ }
101
106
} else {
102
107
self . to_source_string ( )
108
+ } ;
109
+
110
+ res
111
+ . cow_replace ( '!' , "%21" )
112
+ . cow_replace ( '|' , "%7C" )
113
+ . into_owned ( )
114
+ }
115
+ }
116
+
117
+ #[ derive( Clone , Debug ) ]
118
+ pub struct RspackNativeRegex {
119
+ regex : regex:: Regex ,
120
+ flags : String ,
121
+ source : String ,
122
+ }
123
+
124
+ impl std:: hash:: Hash for RspackNativeRegex {
125
+ fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
126
+ self . flags . hash ( state) ;
127
+ self . source . hash ( state) ;
128
+ }
129
+ }
130
+
131
+ impl PartialEq for RspackNativeRegex {
132
+ fn eq ( & self , other : & Self ) -> bool {
133
+ self . flags == other. flags && self . source == other. source
134
+ }
135
+ }
136
+
137
+ impl Eq for RspackNativeRegex { }
138
+
139
+ impl RspackNativeRegex {
140
+ pub fn with_flags ( expr : & str , raw_flags : & str ) -> Result < Self , Error > {
141
+ let pattern = expr. replace ( "\\ /" , "/" ) ;
142
+
143
+ let mut flags = raw_flags. chars ( ) . collect :: < Vec < char > > ( ) ;
144
+ flags. sort_unstable ( ) ;
145
+ let mut applied_flags = String :: new ( ) ;
146
+ // https://github.com/vercel/next.js/blob/203adbd5d054609812d1f3666184875dcca13f3a/turbopack/crates/turbo-esregex/src/lib.rs#L71-L94
147
+ for flag in & flags {
148
+ match flag {
149
+ // indices for substring matches: not relevant for the regex itself
150
+ 'd' => { }
151
+ // global: default in rust, ignore
152
+ 'g' => { }
153
+ // case-insensitive: letters match both upper and lower case
154
+ 'i' => applied_flags. push ( 'i' ) ,
155
+ // multi-line mode: ^ and $ match begin/end of line
156
+ 'm' => applied_flags. push ( 'm' ) ,
157
+ // allow . to match \n
158
+ 's' => applied_flags. push ( 's' ) ,
159
+ // Unicode support (enabled by default)
160
+ 'u' => applied_flags. push ( 'u' ) ,
161
+ // sticky search: not relevant for the regex itself
162
+ 'y' => { }
163
+ _ => {
164
+ return Err ( error ! (
165
+ "unsupported flag `{flag}` in regex: `{pattern}` with flags: `{raw_flags}`"
166
+ ) ) ;
167
+ }
168
+ }
103
169
}
104
- . cow_replace ( '!' , "%21" )
105
- . cow_replace ( '|' , "%7C" )
106
- . into_owned ( )
170
+
171
+ let regex = if applied_flags. is_empty ( ) {
172
+ regex:: Regex :: new ( & pattern) . map_err ( |e| error ! ( e) ) ?
173
+ } else {
174
+ regex:: Regex :: new ( & format ! ( "(?{applied_flags}){pattern}" ) ) . map_err ( |e| error ! ( e) ) ?
175
+ } ;
176
+
177
+ Ok ( Self {
178
+ regex,
179
+ flags : flags. into_iter ( ) . collect :: < String > ( ) ,
180
+ source : expr. to_string ( ) ,
181
+ } )
182
+ }
183
+ }
184
+
185
+ #[ derive( Clone ) ]
186
+ pub struct RspackRegressRegex {
187
+ algo : Box < Algo > ,
188
+ pub flags : String ,
189
+ pub source : String ,
190
+ }
191
+
192
+ impl PartialEq for RspackRegressRegex {
193
+ fn eq ( & self , other : & Self ) -> bool {
194
+ self . flags == other. flags && self . source == other. source
195
+ }
196
+ }
197
+
198
+ impl Eq for RspackRegressRegex { }
199
+
200
+ impl std:: hash:: Hash for RspackRegressRegex {
201
+ fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
202
+ self . flags . hash ( state) ;
203
+ self . source . hash ( state) ;
204
+ }
205
+ }
206
+
207
+ impl Debug for RspackRegressRegex {
208
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
209
+ f. debug_struct ( "RspackRegressRegex" )
210
+ . field ( "flags" , & self . flags )
211
+ . field ( "source" , & self . source )
212
+ . finish ( )
213
+ }
214
+ }
215
+
216
+ impl RspackRegressRegex {
217
+ pub fn with_flags ( expr : & str , flags : & str ) -> Result < Self , Error > {
218
+ let mut chars = flags. chars ( ) . collect :: < Vec < char > > ( ) ;
219
+ chars. sort_unstable ( ) ;
220
+ Ok ( Self {
221
+ flags : chars. into_iter ( ) . collect :: < String > ( ) ,
222
+ source : expr. to_string ( ) ,
223
+ algo : Box :: new ( Algo :: new ( expr, flags) ?) ,
224
+ } )
107
225
}
108
226
}
109
227
@@ -125,7 +243,10 @@ impl TryFrom<SwcRegex> for RspackRegex {
125
243
126
244
impl AsStringConverter for RspackRegex {
127
245
fn to_string ( & self ) -> Result < String , rspack_cacheable:: SerializeError > {
128
- Ok ( format ! ( "{}#{}" , self . flags, self . source) )
246
+ match self {
247
+ Self :: Regex ( regex) => Ok ( format ! ( "{}#{}" , regex. flags, regex. source) ) ,
248
+ Self :: Regress ( regress) => Ok ( format ! ( "{}#{}" , regress. flags, regress. source) ) ,
249
+ }
129
250
}
130
251
fn from_str ( s : & str ) -> Result < Self , rspack_cacheable:: DeserializeError >
131
252
where
0 commit comments