Skip to content

Commit 04934e8

Browse files
committed
perf(span): faster conversion of path/extension to SourceType
1 parent 39063ce commit 04934e8

1 file changed

Lines changed: 75 additions & 35 deletions

File tree

  • crates/oxc_span/src/source_type

crates/oxc_span/src/source_type/mod.rs

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,63 @@ impl ContentEq for SourceType {
9797
}
9898
}
9999

100-
/// Valid file extensions
100+
/// Valid file extensions.
101101
pub const VALID_EXTENSIONS: &[&str] = &["js", "mjs", "cjs", "jsx", "ts", "mts", "cts", "tsx"];
102102

103+
/// Valid file extension.
104+
#[derive(Clone, Copy, PartialEq, Eq)]
105+
enum FileExtension {
106+
Js,
107+
Mjs,
108+
Cjs,
109+
Jsx,
110+
Ts,
111+
Mts,
112+
Cts,
113+
Tsx,
114+
}
115+
116+
impl FileExtension {
117+
fn from_str(extension: &str) -> Option<Self> {
118+
let file_ext = match extension {
119+
"js" => Self::Js,
120+
"mjs" => Self::Mjs,
121+
"cjs" => Self::Cjs,
122+
"jsx" => Self::Jsx,
123+
"ts" => Self::Ts,
124+
"mts" => Self::Mts,
125+
"cts" => Self::Cts,
126+
"tsx" => Self::Tsx,
127+
_ => return None,
128+
};
129+
Some(file_ext)
130+
}
131+
}
132+
133+
impl From<FileExtension> for SourceType {
134+
fn from(file_ext: FileExtension) -> SourceType {
135+
#[allow(clippy::enum_glob_use, clippy::allow_attributes)]
136+
use FileExtension::*;
137+
138+
let language = match file_ext {
139+
Js | Cjs | Mjs | Jsx => Language::JavaScript,
140+
Ts | Tsx | Mts | Cts => Language::TypeScript,
141+
};
142+
143+
let module_kind = match file_ext {
144+
Js | Tsx | Ts | Jsx | Mts | Mjs => ModuleKind::Module,
145+
Cjs | Cts => ModuleKind::Script,
146+
};
147+
148+
let variant = match file_ext {
149+
Js | Mjs | Cjs | Jsx | Tsx => LanguageVariant::Jsx,
150+
Ts | Mts | Cts => LanguageVariant::Standard,
151+
};
152+
153+
SourceType { language, module_kind, variant }
154+
}
155+
}
156+
103157
impl SourceType {
104158
/// Creates a [`SourceType`] representing a regular [`JavaScript`] file.
105159
///
@@ -450,28 +504,31 @@ impl SourceType {
450504
.and_then(std::ffi::OsStr::to_str)
451505
.ok_or_else(|| UnknownExtension::new("Please provide a valid file name."))?;
452506

453-
let extension = path
454-
.as_ref()
455-
.extension()
456-
.and_then(std::ffi::OsStr::to_str)
457-
.filter(|s| VALID_EXTENSIONS.contains(s))
458-
.ok_or_else(|| {
507+
let file_ext =
508+
path.as_ref().extension().and_then(std::ffi::OsStr::to_str).and_then(FileExtension::from_str).ok_or_else(|| {
459509
let path = path.as_ref().to_string_lossy();
460510
UnknownExtension::new(
461511
format!("Please provide a valid file extension for {path}: .js, .mjs, .jsx or .cjs for JavaScript, or .ts, .d.ts, .mts, .cts or .tsx for TypeScript"),
462512
)
463513
})?;
464514

465-
let mut source_type = Self::from_extension(extension)?;
515+
let mut source_type = SourceType::from(file_ext);
466516

467-
#[expect(clippy::case_sensitive_file_extension_comparisons)]
468-
if match extension {
469-
"ts" if file_name[..file_name.len() - 3].split('.').rev().take(2).any(|c| c == "d") => {
470-
true
517+
let is_dts = match file_ext {
518+
// https://www.typescriptlang.org/tsconfig/#allowArbitraryExtensions
519+
// `{file basename}.d.{extension}.ts`
520+
// https://github.com/microsoft/TypeScript/issues/50133
521+
FileExtension::Ts => {
522+
file_name[..file_name.len() - 3].split('.').rev().take(2).any(|c| c == "d")
523+
}
524+
FileExtension::Mts | FileExtension::Cts =>
525+
{
526+
#[expect(clippy::case_sensitive_file_extension_comparisons)]
527+
file_name[..file_name.len() - 4].ends_with(".d")
471528
}
472-
"mts" | "cts" if file_name[..file_name.len() - 4].ends_with(".d") => true,
473529
_ => false,
474-
} {
530+
};
531+
if is_dts {
475532
source_type.language = Language::TypeScriptDefinition;
476533
}
477534

@@ -487,27 +544,10 @@ impl SourceType {
487544
/// "mts", "cts", "tsx". See [`VALID_EXTENSIONS`] for the list of valid
488545
/// extensions.
489546
pub fn from_extension(extension: &str) -> Result<Self, UnknownExtension> {
490-
let module_kind = match extension {
491-
"js" | "tsx" | "ts" | "jsx" | "mts" | "mjs" => ModuleKind::Module,
492-
"cjs" | "cts" => ModuleKind::Script,
493-
_ => return Err(UnknownExtension::new("Unknown extension.")),
494-
};
495-
496-
let language = match extension {
497-
// https://www.typescriptlang.org/tsconfig/#allowArbitraryExtensions
498-
// `{file basename}.d.{extension}.ts`
499-
// https://github.com/microsoft/TypeScript/issues/50133
500-
"js" | "cjs" | "mjs" | "jsx" => Language::JavaScript,
501-
"ts" | "tsx" | "mts" | "cts" => Language::TypeScript,
502-
_ => return Err(UnknownExtension::new("Unknown extension.")),
503-
};
504-
505-
let variant = match extension {
506-
"js" | "mjs" | "cjs" | "jsx" | "tsx" => LanguageVariant::Jsx,
507-
_ => LanguageVariant::Standard,
508-
};
509-
510-
Ok(Self { language, module_kind, variant })
547+
match FileExtension::from_str(extension) {
548+
Some(file_ext) => Ok(SourceType::from(file_ext)),
549+
None => Err(UnknownExtension::new("Unknown extension.")),
550+
}
511551
}
512552
}
513553

0 commit comments

Comments
 (0)