Skip to content

Commit 46e4d6a

Browse files
authored
fix(Turbopack): Give intercept routes correct regex in generated manifest files (#83128)
1 parent abfc98f commit 46e4d6a

File tree

4 files changed

+56
-12
lines changed

4 files changed

+56
-12
lines changed

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ quote = "1.0.23"
406406
rand = "0.9.0"
407407
rayon = "1.10.0"
408408
regex = "1.10.6"
409-
regress = "0.10.3"
409+
regress = "0.10.4"
410410
reqwest = { version = "0.12.22", default-features = false }
411411
ringmap = "0.1.3"
412412
roaring = "0.10.10"

crates/next-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ allsorts = { workspace = true }
2828
futures = { workspace = true }
2929
thiserror = { workspace = true }
3030
tracing = { workspace = true }
31+
regress = { workspace = true }
3132
rustc-hash = { workspace = true }
3233
react_remove_properties = { workspace = true }
3334
remove_console = { workspace = true }

crates/next-core/src/next_edge/route_regex.rs

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
44
use once_cell::sync::Lazy;
55
use regex::Regex;
6+
use regress;
67
use rustc_hash::FxHashMap;
78

89
const INTERCEPTION_ROUTE_MARKERS: [&str; 4] = ["(..)(..)", "(.)", "(..)", "(...)"];
@@ -64,7 +65,7 @@ fn parse_parameter(param: &str) -> ParsedParameter {
6465
}
6566

6667
fn escape_string_regexp(segment: &str) -> String {
67-
regex::escape(segment)
68+
regress::escape(segment)
6869
}
6970

7071
/// Removes the trailing slash for a given route or page path. Preserves the
@@ -164,6 +165,7 @@ fn get_safe_key_from_segment(
164165
segment: &str,
165166
route_keys: &mut FxHashMap<String, String>,
166167
key_prefix: Option<&'static str>,
168+
intercept_prefix: Option<&str>,
167169
) -> String {
168170
let ParsedParameter {
169171
key,
@@ -195,11 +197,13 @@ fn get_safe_key_from_segment(
195197
} else {
196198
route_keys.insert(cleaned_key.clone(), key);
197199
}
200+
201+
let intercept_prefix = intercept_prefix.map_or_else(String::new, escape_string_regexp);
198202
match (repeat, optional) {
199-
(true, true) => format!(r"(?:/(?P<{cleaned_key}>.+?))?"),
200-
(true, false) => format!(r"/(?P<{cleaned_key}>.+?)"),
201-
(false, true) => format!(r"(?:/(?P<{cleaned_key}>[^/]+?))?"),
202-
(false, false) => format!(r"/(?P<{cleaned_key}>[^/]+?)"),
203+
(true, true) => format!(r"(?:/{intercept_prefix}(?P<{cleaned_key}>.+?))?"),
204+
(true, false) => format!(r"/{intercept_prefix}(?P<{cleaned_key}>.+?)"),
205+
(false, true) => format!(r"(?:/{intercept_prefix}(?P<{cleaned_key}>[^/]+?))?"),
206+
(false, false) => format!(r"/{intercept_prefix}(?P<{cleaned_key}>[^/]+?)"),
203207
}
204208
}
205209

@@ -213,11 +217,12 @@ fn get_named_parametrized_route(
213217
let parameterized_route = segments
214218
.iter()
215219
.map(|segment| {
220+
let interception_marker = INTERCEPTION_ROUTE_MARKERS
221+
.iter()
222+
.find(|&m| segment.starts_with(m))
223+
.copied();
216224
let key_prefix = if prefix_route_keys {
217-
let has_interception_marker = INTERCEPTION_ROUTE_MARKERS
218-
.iter()
219-
.any(|&m| segment.starts_with(m));
220-
if has_interception_marker {
225+
if interception_marker.is_some() {
221226
Some(NEXT_INTERCEPTION_MARKER_PREFIX)
222227
} else {
223228
Some(NEXT_QUERY_PARAM_PREFIX)
@@ -233,6 +238,7 @@ fn get_named_parametrized_route(
233238
&matches[1],
234239
&mut route_keys,
235240
key_prefix,
241+
interception_marker,
236242
);
237243
}
238244
format!("/{}", escape_string_regexp(segment))
@@ -265,3 +271,39 @@ pub fn get_named_middleware_regex(normalized_route: &str) -> String {
265271
let (parameterized_route, _route_keys) = get_named_parametrized_route(normalized_route, true);
266272
format!("^{parameterized_route}(?:/)?$")
267273
}
274+
275+
#[cfg(test)]
276+
mod test {
277+
use super::get_named_middleware_regex;
278+
279+
#[test]
280+
fn should_properly_handle_intercept_routes() {
281+
let tests = [
282+
(
283+
"/[locale]/example/(...)[locale]/intercepted",
284+
"^/(?P<nxtPlocale>[^/]+?)/example/\\(\\.\\.\\.\\)(?P<nxtIlocale>[^/]+?)/\
285+
intercepted(?:/)?$",
286+
),
287+
(
288+
"/[locale]/example/(..)[locale]/intercepted",
289+
"^/(?P<nxtPlocale>[^/]+?)/example/\\(\\.\\.\\)(?P<nxtIlocale>[^/]+?)/intercepted(?\
290+
:/)?$",
291+
),
292+
(
293+
"/[locale]/example/(.)[locale]/intercepted",
294+
"^/(?P<nxtPlocale>[^/]+?)/example/\\(\\.\\)(?P<nxtIlocale>[^/]+?)/intercepted(?:/\
295+
)?$",
296+
),
297+
(
298+
"/[locale]/example/(..)(..)[locale]/intercepted",
299+
"^/(?P<nxtPlocale>[^/]+?)/example/\\(\\.\\.\\)\\(\\.\\.\\)(?P<nxtIlocale>[^/]+?)/\
300+
intercepted(?:/)?$",
301+
),
302+
];
303+
304+
for test in tests {
305+
let intercept_route = get_named_middleware_regex(test.0);
306+
assert_eq!(intercept_route, test.1);
307+
}
308+
}
309+
}

0 commit comments

Comments
 (0)