diff --git a/crates/turbopack-core/src/resolve/mod.rs b/crates/turbopack-core/src/resolve/mod.rs index 5ad119726fda4..5f8a139fc66cf 100644 --- a/crates/turbopack-core/src/resolve/mod.rs +++ b/crates/turbopack-core/src/resolve/mod.rs @@ -1920,21 +1920,6 @@ async fn resolve_relative_request( let mut new_path = path_pattern.clone(); let fragment_val = fragment.await?; - - if !options_value.fully_specified && options_value.enable_js_ts_rewriting { - // TODO path_pattern might not be a constant? - // TODO extension might be empty, or dots in filepath - if let Pattern::Constant(s) = path_pattern { - if let Some((base, ext)) = s.rsplit_once(".") { - if ext == "js" { - new_path = Pattern::Alternatives(vec![Pattern::Constant(base.into()), new_path]) - } - } - } else { - todo!("enable_js_ts_rewriting"); - } - } - if !fragment_val.is_empty() { new_path.push(Pattern::Alternatives( once(Pattern::Constant("".into())) @@ -1957,6 +1942,36 @@ async fn resolve_relative_request( .collect(), )); + if options_value.enable_js_ts_rewriting { + let mut rewritten_path = path_pattern.clone(); + let rewritten_path_modified = + rewritten_path.replace_final_constants(&|c: &RcStr| -> Option { + let result = match c.rsplit_once(".") { + Some((base, "js")) => Some(( + base, + vec![ + Pattern::Constant(".ts".into()), + Pattern::Constant(".tsx".into()), + ], + )), + Some((base, "mjs")) => Some((base, vec![Pattern::Constant(".mts".into())])), + Some((base, "cjs")) => Some((base, vec![Pattern::Constant(".cts".into())])), + _ => None, + }; + result.map(|(base, replacement)| { + Pattern::Concatenation(vec![ + Pattern::Constant(base.into()), + Pattern::Alternatives(replacement), + ]) + }) + }); + + if rewritten_path_modified { + // Prepend the rewritten pattern to give it higher priority + new_path = Pattern::Alternatives(vec![rewritten_path, new_path]) + } + } + new_path.normalize(); }; diff --git a/crates/turbopack-core/src/resolve/pattern.rs b/crates/turbopack-core/src/resolve/pattern.rs index 825a3c766ff2d..337f0e02bb8b0 100644 --- a/crates/turbopack-core/src/resolve/pattern.rs +++ b/crates/turbopack-core/src/resolve/pattern.rs @@ -627,6 +627,33 @@ impl Pattern { new.normalize(); Pattern::alternatives([self.clone(), new]) } + + /// Calls `cb` on all constants that are at the end of the pattern and + /// replaces the given final constant with the returned pattern. Returns + /// true if replacements were performed. + pub fn replace_final_constants(&mut self, cb: &impl Fn(&RcStr) -> Option) -> bool { + let mut replaced = false; + match self { + Pattern::Constant(c) => { + if let Some(replacement) = cb(c) { + *self = replacement; + replaced = true; + } + } + Pattern::Dynamic => {} + Pattern::Alternatives(list) => { + for i in list { + replaced = i.replace_final_constants(cb) || replaced; + } + } + Pattern::Concatenation(list) => { + if let Some(c @ Pattern::Constant(_)) = list.last_mut() { + replaced = c.replace_final_constants(cb) || replaced; + } + } + } + replaced + } } impl Pattern { @@ -1097,6 +1124,7 @@ pub async fn read_matches( #[cfg(test)] mod tests { use rstest::*; + use turbo_tasks::RcStr; use super::Pattern; @@ -1344,4 +1372,68 @@ mod tests { ) { assert_eq!(pat.next_constants(value), expected); } + + #[test] + fn replace_final_constants() { + fn f(mut p: Pattern, cb: &impl Fn(&RcStr) -> Option) -> Pattern { + p.replace_final_constants(cb); + p + } + + let js_to_ts_tsx = |c: &RcStr| -> Option { + c.strip_suffix(".js").map(|rest| { + let new_ending = Pattern::Alternatives(vec![ + Pattern::Constant(".ts".into()), + Pattern::Constant(".tsx".into()), + ]); + if !rest.is_empty() { + Pattern::Concatenation(vec![Pattern::Constant(rest.into()), new_ending]) + } else { + new_ending + } + }) + }; + + assert_eq!( + f( + Pattern::Concatenation(vec![ + Pattern::Constant(".".into()), + Pattern::Constant("/".into()), + Pattern::Dynamic, + Pattern::Constant(".js".into()), + ]), + &js_to_ts_tsx + ), + Pattern::Concatenation(vec![ + Pattern::Constant(".".into()), + Pattern::Constant("/".into()), + Pattern::Dynamic, + Pattern::Alternatives(vec![ + Pattern::Constant(".ts".into()), + Pattern::Constant(".tsx".into()), + ]), + ]) + ); + assert_eq!( + f( + Pattern::Concatenation(vec![ + Pattern::Constant(".".into()), + Pattern::Constant("/".into()), + Pattern::Constant("abc.js".into()), + ]), + &js_to_ts_tsx + ), + Pattern::Concatenation(vec![ + Pattern::Constant(".".into()), + Pattern::Constant("/".into()), + Pattern::Concatenation(vec![ + Pattern::Constant("abc".into()), + Pattern::Alternatives(vec![ + Pattern::Constant(".ts".into()), + Pattern::Constant(".tsx".into()), + ]) + ]), + ]) + ); + } }