[rust-compiler] Drop the regex dependency from the napi binary (#36727)
The regex crate services exactly one pattern, the dynamic-gating directive `^use memo if\(([^\)]*)\)$`, while costing ~570KB of `.text` (regex + regex_automata + regex_syntax + aho_corasick) in the shipped binary; after LTO the removal saves ~1MB through dead-code cascade. Replaced with an exact hand parse: strip the `use memo if(` prefix and `)` suffix, reject conditions containing a close paren. Equivalence with the TS `DYNAMIC_GATING_DIRECTIVE` regex verified on the full gating fixture directory: 30/30 byte-identical TS-vs-Rust on the e2e comparison harness, dynamic-gating snap fixtures green. Independent of #36726; the two compose to take the default release binary from 11.2MB to 6.1MB.
lauren committed
Jun 9, 2026 at 15:38 UTC
34b78a2897cc208260a88e6b62ecaf9ca2a9dfe4
3 files changed
+25
-19
compiler/Cargo.lock
-1
@@ -1189,7 +1189,6 @@ dependencies = [
1189
"react_compiler_ssa",
1190
"react_compiler_typeinference",
1191
"react_compiler_validation",
1192
- "regex",
1192
"serde",
1193
"serde_json",
1194
]
compiler/crates/react_compiler/Cargo.toml
-1
@@ -15,6 +15,5 @@ react_compiler_ssa = { path = "../react_compiler_ssa" }
15
react_compiler_typeinference = { path = "../react_compiler_typeinference" }
16
react_compiler_validation = { path = "../react_compiler_validation" }
17
indexmap = "2"
18
-regex = "1"
18
serde = { version = "1", features = ["derive"] }
19
serde_json = { version = "1", features = ["raw_value"] }
compiler/crates/react_compiler/src/entrypoint/program.rs
+25
-17
@@ -43,7 +43,6 @@ use react_compiler_diagnostics::SourceLocation;
43
use react_compiler_hir::ReactFunctionType;
44
use react_compiler_hir::environment_config::EnvironmentConfig;
45
use react_compiler_lowering::FunctionNode;
46
-use regex::Regex;
46
47
use super::compile_result::BindingRenameInfo;
48
use super::compile_result::CodegenFunction;
@@ -175,26 +174,21 @@ fn find_directives_dynamic_gating<'a>(
174
None => return Ok(None),
175
};
176
178
- let pattern = Regex::new(r"^use memo if\(([^\)]*)\)$").expect("Invalid dynamic gating regex");
179
-
177
let mut errors: Vec<CompilerErrorDetail> = Vec::new();
178
let mut matches: Vec<(&'a Directive, String)> = Vec::new();
179
180
for directive in directives {
184
- if let Some(caps) = pattern.captures(&directive.value.value) {
185
- if let Some(m) = caps.get(1) {
186
- let ident = m.as_str();
187
- if is_valid_identifier(ident) {
188
- matches.push((directive, ident.to_string()));
189
- } else {
190
- let mut detail = CompilerErrorDetail::new(
191
- ErrorCategory::Gating,
192
- "Dynamic gating directive is not a valid JavaScript identifier",
193
- )
194
- .with_description(format!("Found '{}'", directive.value.value));
195
- detail.loc = directive.base.loc.as_ref().map(convert_loc);
196
- errors.push(detail);
197
- }
181
+ if let Some(ident) = parse_dynamic_gating_directive(&directive.value.value) {
182
+ if is_valid_identifier(ident) {
183
+ matches.push((directive, ident.to_string()));
184
+ } else {
185
+ let mut detail = CompilerErrorDetail::new(
186
+ ErrorCategory::Gating,
187
+ "Dynamic gating directive is not a valid JavaScript identifier",
188
+ )
189
+ .with_description(format!("Found '{}'", directive.value.value));
190
+ detail.loc = directive.base.loc.as_ref().map(convert_loc);
191
+ errors.push(detail);
192
}
193
}
194
}
@@ -236,6 +230,20 @@ fn find_directives_dynamic_gating<'a>(
230
}
231
}
232
233
+/// Parse a `use memo if(<condition>)` directive, returning the condition.
234
+/// Exact equivalent of the TS DYNAMIC_GATING_DIRECTIVE regex
235
+/// `^use memo if\(([^\)]*)\)$`: the condition may not contain `)` and the
236
+/// directive must end at the closing paren.
237
+fn parse_dynamic_gating_directive(value: &str) -> Option<&str> {
238
+ let condition = value
239
+ .strip_prefix("use memo if(")?
240
+ .strip_suffix(')')?;
241
+ if condition.contains(')') {
242
+ return None;
243
+ }
244
+ Some(condition)
245
+}
246
+
247
/// Simple check for valid JavaScript identifier (alphanumeric + underscore + $, starting with letter/$/_ )
248
/// Also rejects reserved words like `true`, `false`, `null`, etc.
249
fn is_valid_identifier(s: &str) -> bool {