Skip to content

Commit e5b39ba

Browse files
committed
snap
1 parent 330321c commit e5b39ba

File tree

1 file changed

+121
-37
lines changed
  • turbopack/crates/turbopack-core/src/resolve

1 file changed

+121
-37
lines changed

turbopack/crates/turbopack-core/src/resolve/mod.rs

Lines changed: 121 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ use std::{
88

99
use anyhow::{Result, bail};
1010
use auto_hash_map::AutoSet;
11+
use once_cell::sync::Lazy;
1112
use rustc_hash::{FxHashMap, FxHashSet};
1213
use serde::{Deserialize, Serialize};
14+
use smallvec::SmallVec;
1315
use tracing::{Instrument, Level};
1416
use turbo_rcstr::{RcStr, rcstr};
1517
use turbo_tasks::{
@@ -2246,17 +2248,43 @@ async fn resolve_relative_request(
22462248

22472249
let mut new_path = path_pattern.clone();
22482250

2251+
#[derive(Eq, PartialEq, Clone, Hash)]
2252+
enum PatternModification {
2253+
AddedFragment,
2254+
AddedExtension { ext: RcStr },
2255+
ReplacedExtension { ext: RcStr },
2256+
}
2257+
2258+
let mut modifications = Vec::new();
2259+
modifications.push(SmallVec::<[PatternModification; 3]>::new());
2260+
2261+
// If we parsed the pattern with a fragment, it is possible the user was actually attempting to
2262+
// match a literal `#` in a filename
2263+
// So handle that as an alternative here.
22492264
if !fragment.is_empty() {
2250-
// 'fragments' should not be part of the result, however it could be that the user is
2251-
// attempting to match a file with a '#' character in it. In that case we need
2252-
// to look for it which is what this alternation does
2265+
modifications.push(SmallVec::from_vec(vec![PatternModification::AddedFragment]));
22532266
new_path.push(Pattern::Alternatives(vec![
22542267
Pattern::Constant(RcStr::default()),
22552268
Pattern::Constant(fragment.clone()),
22562269
]));
22572270
}
22582271

22592272
if !options_value.fully_specified {
2273+
// For each current set of modifications append an extension modification
2274+
modifications = options_value
2275+
.extensions
2276+
.iter()
2277+
.map(|ext| Some(ext.clone()))
2278+
.chain(once(None))
2279+
.flat_map(|ext| {
2280+
modifications.iter().cloned().map(move |mut mods| {
2281+
if let Some(ext) = &ext {
2282+
mods.push(PatternModification::AddedExtension { ext: ext.clone() });
2283+
}
2284+
mods
2285+
})
2286+
})
2287+
.collect();
22602288
// Add the extensions as alternatives to the path
22612289
// read_matches keeps the order of alternatives intact
22622290
new_path.push(Pattern::Alternatives(
@@ -2272,48 +2300,100 @@ async fn resolve_relative_request(
22722300
new_path.normalize();
22732301
};
22742302

2303+
struct ExtensionReplacements {
2304+
forward: FxHashMap<RcStr, SmallVec<[RcStr; 3]>>,
2305+
reverse: FxHashMap<RcStr, RcStr>,
2306+
}
2307+
static TS_EXTENSION_REPLACEMENTS: Lazy<ExtensionReplacements> = Lazy::new(|| {
2308+
let mut forward = FxHashMap::default();
2309+
let mut reverse = FxHashMap::default();
2310+
forward.insert(
2311+
rcstr!(".js"),
2312+
SmallVec::from_vec(vec![rcstr!(".ts"), rcstr!(".tsx"), rcstr!(".js")]),
2313+
);
2314+
reverse.insert(rcstr!(".ts"), rcstr!(".js"));
2315+
reverse.insert(rcstr!(".tsx"), rcstr!(".js"));
2316+
2317+
forward.insert(
2318+
rcstr!(".mjs"),
2319+
SmallVec::from_vec(vec![rcstr!(".mts"), rcstr!(".mjs")]),
2320+
);
2321+
2322+
reverse.insert(rcstr!(".mts"), rcstr!(".mjs"));
2323+
forward.insert(
2324+
rcstr!(".cjs"),
2325+
SmallVec::from_vec(vec![rcstr!(".cts"), rcstr!(".cjs")]),
2326+
);
2327+
reverse.insert(rcstr!(".cts"), rcstr!(".cjs"));
2328+
ExtensionReplacements { forward, reverse }
2329+
});
2330+
22752331
if options_value.enable_typescript_with_output_extension {
2276-
new_path.replace_final_constants(&|c: &RcStr| -> Option<Pattern> {
2277-
let (base, replacement) = match c.rsplit_once(".") {
2278-
Some((base, "js")) => (
2279-
base,
2280-
vec![
2281-
Pattern::Constant(rcstr!(".ts")),
2282-
Pattern::Constant(rcstr!(".tsx")),
2283-
Pattern::Constant(rcstr!(".js")),
2284-
],
2285-
),
2286-
Some((base, "mjs")) => (
2287-
base,
2288-
vec![
2289-
Pattern::Constant(rcstr!(".mts")),
2290-
Pattern::Constant(rcstr!(".mjs")),
2291-
],
2292-
),
2293-
Some((base, "cjs")) => (
2294-
base,
2295-
vec![
2296-
Pattern::Constant(rcstr!(".cts")),
2297-
Pattern::Constant(rcstr!(".cjs")),
2298-
],
2299-
),
2300-
_ => {
2301-
return None;
2302-
}
2332+
// there are at most 4 possible replacements (the size of the reverse map)
2333+
let mut replaced_extensions = SmallVec::<[RcStr; 4]>::new();
2334+
let replaced = new_path.replace_final_constants(&mut |c: &RcStr| -> Option<Pattern> {
2335+
let Some(dot) = c.rfind('.') else {
2336+
return None;
2337+
};
2338+
let (base, ext) = c.split_at(dot);
2339+
2340+
let Some((ext, replacements)) = TS_EXTENSION_REPLACEMENTS.forward.get_key_value(ext)
2341+
else {
2342+
return None;
23032343
};
2344+
for replacement in replacements {
2345+
if replacement != ext && !replaced_extensions.contains(replacement) {
2346+
replaced_extensions.push(replacement.clone());
2347+
debug_assert!(replaced_extensions.len() <= replaced_extensions.inline_size());
2348+
}
2349+
}
2350+
2351+
let replacements = replacements
2352+
.iter()
2353+
.cloned()
2354+
.map(Pattern::Constant)
2355+
.collect();
2356+
23042357
if base.is_empty() {
2305-
Some(Pattern::Alternatives(replacement))
2358+
Some(Pattern::Alternatives(replacements))
23062359
} else {
23072360
Some(Pattern::Concatenation(vec![
23082361
Pattern::Constant(base.into()),
2309-
Pattern::Alternatives(replacement),
2362+
Pattern::Alternatives(replacements),
23102363
]))
23112364
}
23122365
});
2313-
new_path.normalize();
2366+
if replaced {
2367+
// For each current set of modifications append an extension replacement modification
2368+
modifications = replaced_extensions
2369+
.into_iter()
2370+
.map(|ext| Some(ext))
2371+
.chain(once(None))
2372+
.flat_map(|ext| {
2373+
modifications.iter().cloned().map({
2374+
let ext = ext.clone();
2375+
move |mut mods| {
2376+
if let Some(ext) = &ext {
2377+
mods.push(PatternModification::ReplacedExtension {
2378+
ext: ext.clone(),
2379+
});
2380+
}
2381+
mods
2382+
}
2383+
})
2384+
})
2385+
.collect();
2386+
new_path.normalize();
2387+
}
23142388
}
23152389

2316-
let mut results = Vec::new();
2390+
// The pattern expansions above can lead to duplicates
2391+
let modifications = modifications
2392+
.into_iter()
2393+
.collect::<FxIndexSet<_>>()
2394+
.into_iter()
2395+
.collect::<Vec<_>>();
2396+
23172397
let matches = read_matches(
23182398
lookup_path.clone(),
23192399
rcstr!(""),
@@ -2324,11 +2404,15 @@ async fn resolve_relative_request(
23242404

23252405
// This loop is necessary to 'undo' the modifications to 'new_path' that were performed above.
23262406
// e.g. we added extensions but these shouldn't be part of the request key so remove them
2327-
// TODO: this logic is not completely correct because it fails to account for the extension
23282407
// replacement logic that we perform conditionally.
23292408

2409+
// For each match we need to consider if it was produced by one of the pattern modifications
2410+
// below and if so we will need to reverse the transform on the request key. Then alternate
2411+
let mut results = Vec::new();
2412+
23302413
for m in matches.iter() {
2331-
if let PatternMatch::File(matched_pattern, path) = m {
2414+
// for mods in modifications {}
2415+
23322416
let mut pushed = false;
23332417
if !options_value.fully_specified {
23342418
for ext in options_value.extensions.iter() {
@@ -2338,7 +2422,7 @@ async fn resolve_relative_request(
23382422

23392423
if !fragment.is_empty() {
23402424
// If the fragment is not empty, we need to strip it from the matched
2341-
// pattern so it matches path_pattern
2425+
// pattern
23422426
if let Some(matched_pattern) =
23432427
matched_pattern.strip_suffix(fragment.as_str())
23442428
&& path_pattern.is_match(matched_pattern)

0 commit comments

Comments
 (0)