mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
feat(span): store file extension in SourceType (#18893)
## Summary Store the original file extension in `SourceType` when parsed from a path. This allows downstream code to distinguish between e.g. `.mts` and `.ts` files that resolved to `ModuleKind::Module`. - Adds `extension: Option<FileExtension>` field to `SourceType` - Adds `FileExtension` to AST schema with `#[ast]` attribute - Adds `SourceType::extension()` getter method related https://github.com/oxc-project/oxc/issues/15184 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
4 files changed
+62
-16
No files matched your search
@@ -9,7 +9,7 @@ use crate::ast::*;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const _: () = {
|
||||
// Padding: 1 bytes
|
||||
// Padding: 0 bytes
|
||||
assert!(size_of::<Program>() == 128);
|
||||
assert!(align_of::<Program>() == 8);
|
||||
assert!(offset_of!(Program, span) == 0);
|
||||
@@ -1631,7 +1631,7 @@ const _: () = {
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const _: () = if cfg!(target_family = "wasm") || align_of::<u64>() == 8 {
|
||||
// Padding: 1 bytes
|
||||
// Padding: 0 bytes
|
||||
assert!(size_of::<Program>() == 88);
|
||||
assert!(align_of::<Program>() == 4);
|
||||
assert!(offset_of!(Program, span) == 0);
|
||||
|
||||
@@ -16,11 +16,12 @@ const _: () = {
|
||||
assert!(offset_of!(Span, end) == 4);
|
||||
|
||||
// Padding: 0 bytes
|
||||
assert!(size_of::<SourceType>() == 3);
|
||||
assert!(size_of::<SourceType>() == 4);
|
||||
assert!(align_of::<SourceType>() == 1);
|
||||
assert!(offset_of!(SourceType, language) == 0);
|
||||
assert!(offset_of!(SourceType, module_kind) == 1);
|
||||
assert!(offset_of!(SourceType, variant) == 2);
|
||||
assert!(offset_of!(SourceType, extension) == 3);
|
||||
|
||||
assert!(size_of::<Language>() == 1);
|
||||
assert!(align_of::<Language>() == 1);
|
||||
@@ -30,6 +31,9 @@ const _: () = {
|
||||
|
||||
assert!(size_of::<LanguageVariant>() == 1);
|
||||
assert!(align_of::<LanguageVariant>() == 1);
|
||||
|
||||
assert!(size_of::<FileExtension>() == 1);
|
||||
assert!(align_of::<FileExtension>() == 1);
|
||||
};
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
@@ -41,11 +45,12 @@ const _: () = if cfg!(target_family = "wasm") || align_of::<u64>() == 8 {
|
||||
assert!(offset_of!(Span, end) == 4);
|
||||
|
||||
// Padding: 0 bytes
|
||||
assert!(size_of::<SourceType>() == 3);
|
||||
assert!(size_of::<SourceType>() == 4);
|
||||
assert!(align_of::<SourceType>() == 1);
|
||||
assert!(offset_of!(SourceType, language) == 0);
|
||||
assert!(offset_of!(SourceType, module_kind) == 1);
|
||||
assert!(offset_of!(SourceType, variant) == 2);
|
||||
assert!(offset_of!(SourceType, extension) == 3);
|
||||
|
||||
assert!(size_of::<Language>() == 1);
|
||||
assert!(align_of::<Language>() == 1);
|
||||
@@ -55,6 +60,9 @@ const _: () = if cfg!(target_family = "wasm") || align_of::<u64>() == 8 {
|
||||
|
||||
assert!(size_of::<LanguageVariant>() == 1);
|
||||
assert!(align_of::<LanguageVariant>() == 1);
|
||||
|
||||
assert!(size_of::<FileExtension>() == 1);
|
||||
assert!(align_of::<FileExtension>() == 1);
|
||||
};
|
||||
|
||||
#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
|
||||
|
||||
@@ -16,6 +16,7 @@ impl<'a> Dummy<'a> for SourceType {
|
||||
language: Dummy::dummy(allocator),
|
||||
module_kind: Dummy::dummy(allocator),
|
||||
variant: Dummy::dummy(allocator),
|
||||
extension: Dummy::dummy(allocator),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,3 +50,13 @@ impl<'a> Dummy<'a> for LanguageVariant {
|
||||
Self::Standard
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Dummy<'a> for FileExtension {
|
||||
/// Create a dummy [`FileExtension`].
|
||||
///
|
||||
/// Does not allocate any data into arena.
|
||||
#[inline(always)]
|
||||
fn dummy(allocator: &'a Allocator) -> Self {
|
||||
Self::Js
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,10 @@ pub struct SourceType {
|
||||
/// Support JSX for JavaScript and TypeScript? default without JSX
|
||||
#[estree(skip)]
|
||||
pub(super) variant: LanguageVariant,
|
||||
|
||||
/// The original file extension, if parsed from a path.
|
||||
#[estree(skip)]
|
||||
pub(super) extension: Option<FileExtension>,
|
||||
}
|
||||
|
||||
/// JavaScript or TypeScript
|
||||
@@ -115,24 +119,26 @@ impl ContentEq for SourceType {
|
||||
pub const VALID_EXTENSIONS: &[&str] = &["js", "mjs", "cjs", "jsx", "ts", "mts", "cts", "tsx"];
|
||||
|
||||
/// Valid file extension.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[ast]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[generate_derive(Dummy)]
|
||||
pub enum FileExtension {
|
||||
/// `.js` file extension
|
||||
Js,
|
||||
Js = 0,
|
||||
/// `.mjs` file extension
|
||||
Mjs,
|
||||
Mjs = 1,
|
||||
/// `.cjs` file extension
|
||||
Cjs,
|
||||
Cjs = 2,
|
||||
/// `.jsx` file extension
|
||||
Jsx,
|
||||
Jsx = 3,
|
||||
/// `.ts` file extension
|
||||
Ts,
|
||||
Ts = 4,
|
||||
/// `.mts` file extension
|
||||
Mts,
|
||||
Mts = 5,
|
||||
/// `.cts` file extension
|
||||
Cts,
|
||||
Cts = 6,
|
||||
/// `.tsx` file extension
|
||||
Tsx,
|
||||
Tsx = 7,
|
||||
}
|
||||
|
||||
impl FileExtension {
|
||||
@@ -206,7 +212,7 @@ impl From<FileExtension> for SourceType {
|
||||
Js | Mjs | Cjs | Ts | Mts | Cts => LanguageVariant::Standard,
|
||||
};
|
||||
|
||||
SourceType { language, module_kind, variant }
|
||||
SourceType { language, module_kind, variant, extension: Some(file_ext) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,6 +240,7 @@ impl SourceType {
|
||||
language: Language::JavaScript,
|
||||
module_kind: ModuleKind::CommonJS,
|
||||
variant: LanguageVariant::Standard,
|
||||
extension: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,6 +259,7 @@ impl SourceType {
|
||||
language: Language::JavaScript,
|
||||
module_kind: ModuleKind::Module,
|
||||
variant: LanguageVariant::Standard,
|
||||
extension: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +279,7 @@ impl SourceType {
|
||||
language: Language::JavaScript,
|
||||
module_kind: ModuleKind::Script,
|
||||
variant: LanguageVariant::Standard,
|
||||
extension: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +295,7 @@ impl SourceType {
|
||||
language: Language::JavaScript,
|
||||
module_kind: ModuleKind::Unambiguous,
|
||||
variant: LanguageVariant::Standard,
|
||||
extension: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,6 +340,7 @@ impl SourceType {
|
||||
language: Language::TypeScript,
|
||||
module_kind: ModuleKind::Unambiguous,
|
||||
variant: LanguageVariant::Standard,
|
||||
extension: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,6 +380,7 @@ impl SourceType {
|
||||
language: Language::TypeScriptDefinition,
|
||||
module_kind: ModuleKind::Module,
|
||||
variant: LanguageVariant::Standard,
|
||||
extension: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,6 +449,11 @@ impl SourceType {
|
||||
self.is_module()
|
||||
}
|
||||
|
||||
/// Returns the original file extension if this source type was created from a path.
|
||||
pub fn extension(self) -> Option<FileExtension> {
|
||||
self.extension
|
||||
}
|
||||
|
||||
/// Mark this [`SourceType`] as a [script] if `yes` is `true`. No change
|
||||
/// will occur if `yes` is `false`.
|
||||
///
|
||||
@@ -655,7 +672,7 @@ impl Error for UnknownExtension {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SourceType;
|
||||
use super::{FileExtension, SourceType};
|
||||
|
||||
#[test]
|
||||
fn test_ts_from_path() {
|
||||
@@ -674,6 +691,12 @@ mod tests {
|
||||
assert!(!ty.is_javascript());
|
||||
}
|
||||
|
||||
// Verify extension is captured
|
||||
assert_eq!(ts.extension(), Some(FileExtension::Ts));
|
||||
assert_eq!(mts.extension(), Some(FileExtension::Mts));
|
||||
assert_eq!(cts.extension(), Some(FileExtension::Cts));
|
||||
assert_eq!(tsx.extension(), Some(FileExtension::Tsx));
|
||||
|
||||
// .ts and .tsx use Unambiguous (content-based detection)
|
||||
assert!(ts.is_unambiguous());
|
||||
assert!(tsx.is_unambiguous());
|
||||
@@ -741,7 +764,11 @@ mod tests {
|
||||
assert!(!ty.is_typescript(), "{ty:?}");
|
||||
}
|
||||
|
||||
assert_eq!(SourceType::mjs(), mjs);
|
||||
// Verify extension is captured
|
||||
assert_eq!(mjs.extension(), Some(FileExtension::Mjs));
|
||||
assert_eq!(cjs.extension(), Some(FileExtension::Cjs));
|
||||
assert_eq!(js.extension(), Some(FileExtension::Js));
|
||||
assert_eq!(jsx.extension(), Some(FileExtension::Jsx));
|
||||
|
||||
// .js and .jsx use Unambiguous (content-based detection)
|
||||
assert!(js.is_unambiguous());
|
||||
|
||||
Reference in new issue
Block a user