Skip to content

Commit 34b78a2

Browse files
authored
[rust-compiler] Drop the regex dependency from the napi binary (react#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 react#36726; the two compose to take the default release binary from 11.2MB to 6.1MB.
1 parent 7e71552 commit 34b78a2

3 files changed

Lines changed: 25 additions & 19 deletions

File tree

compiler/Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/crates/react_compiler/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,5 @@ react_compiler_ssa = { path = "../react_compiler_ssa" }
1515
react_compiler_typeinference = { path = "../react_compiler_typeinference" }
1616
react_compiler_validation = { path = "../react_compiler_validation" }
1717
indexmap = "2"
18-
regex = "1"
1918
serde = { version = "1", features = ["derive"] }
2019
serde_json = { version = "1", features = ["raw_value"] }

compiler/crates/react_compiler/src/entrypoint/program.rs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ use react_compiler_diagnostics::SourceLocation;
4343
use react_compiler_hir::ReactFunctionType;
4444
use react_compiler_hir::environment_config::EnvironmentConfig;
4545
use react_compiler_lowering::FunctionNode;
46-
use regex::Regex;
4746

4847
use super::compile_result::BindingRenameInfo;
4948
use super::compile_result::CodegenFunction;
@@ -175,26 +174,21 @@ fn find_directives_dynamic_gating<'a>(
175174
None => return Ok(None),
176175
};
177176

178-
let pattern = Regex::new(r"^use memo if\(([^\)]*)\)$").expect("Invalid dynamic gating regex");
179-
180177
let mut errors: Vec<CompilerErrorDetail> = Vec::new();
181178
let mut matches: Vec<(&'a Directive, String)> = Vec::new();
182179

183180
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);
198192
}
199193
}
200194
}
@@ -236,6 +230,20 @@ fn find_directives_dynamic_gating<'a>(
236230
}
237231
}
238232

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+
239247
/// Simple check for valid JavaScript identifier (alphanumeric + underscore + $, starting with letter/$/_ )
240248
/// Also rejects reserved words like `true`, `false`, `null`, etc.
241249
fn is_valid_identifier(s: &str) -> bool {

0 commit comments

Comments
 (0)