mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(linter): skip linting vue <script> where lang is not js / ts (#10740)
This commit is contained in:
1 parent
44f19526aa
commit
48c542d4d4
2 files changed
+72
-52
No files matched your search
@@ -1,4 +1,5 @@
|
||||
use memchr::memmem::Finder;
|
||||
|
||||
use oxc_span::SourceType;
|
||||
|
||||
use super::{JavaScriptSource, SCRIPT_END, SCRIPT_START, find_script_closing_angle};
|
||||
@@ -48,8 +49,21 @@ impl<'a> VuePartialLoader<'a> {
|
||||
|
||||
// get ts and jsx attribute
|
||||
let content = &self.source_text[*pointer..*pointer + offset];
|
||||
let is_ts = content.contains("ts");
|
||||
let is_jsx = content.contains("tsx") || content.contains("jsx");
|
||||
|
||||
// parse `lang`
|
||||
let lang = content.split_once("lang").map_or(Some("mjs"), |(_, s)| {
|
||||
const QUOTES: [char; 2] = ['"', '\''];
|
||||
s.trim_start()
|
||||
.trim_start_matches('=')
|
||||
.trim_start()
|
||||
.trim_start_matches(QUOTES)
|
||||
.split_once(QUOTES)
|
||||
.map(|(s, _)| s)
|
||||
})?;
|
||||
let Ok(mut source_type) = SourceType::from_extension(lang) else { return None };
|
||||
if !lang.contains('x') {
|
||||
source_type = source_type.with_standard(true);
|
||||
}
|
||||
|
||||
*pointer += offset + 1;
|
||||
let js_start = *pointer;
|
||||
@@ -61,7 +75,6 @@ impl<'a> VuePartialLoader<'a> {
|
||||
*pointer += offset + SCRIPT_END.len();
|
||||
|
||||
let source_text = &self.source_text[js_start..js_end];
|
||||
let source_type = SourceType::mjs().with_typescript(is_ts).with_jsx(is_jsx);
|
||||
// NOTE: loader checked that source_text.len() is less than u32::MAX
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
Some(JavaScriptSource::partial(source_text, source_type, js_start as u32))
|
||||
@@ -70,6 +83,8 @@ impl<'a> VuePartialLoader<'a> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_span::SourceType;
|
||||
|
||||
use super::{JavaScriptSource, VuePartialLoader};
|
||||
|
||||
fn parse_vue(source_text: &str) -> JavaScriptSource<'_> {
|
||||
@@ -99,20 +114,7 @@ mod test {
|
||||
"#;
|
||||
|
||||
let result = parse_vue(source_text);
|
||||
assert!(result.source_type.is_typescript());
|
||||
assert_eq!(result.source_text.trim(), "1/1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_vue_with_ts_flag_2() {
|
||||
let source_text = r"
|
||||
<script lang=ts setup>
|
||||
1/1
|
||||
</script>
|
||||
";
|
||||
|
||||
let result = parse_vue(source_text);
|
||||
assert!(result.source_type.is_typescript());
|
||||
assert_eq!(result.source_type, SourceType::ts());
|
||||
assert_eq!(result.source_text.trim(), "1/1");
|
||||
}
|
||||
|
||||
@@ -125,21 +127,7 @@ mod test {
|
||||
";
|
||||
|
||||
let result = parse_vue(source_text);
|
||||
assert!(result.source_type.is_typescript());
|
||||
assert_eq!(result.source_text.trim(), "1/1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_vue_with_tsx_flag() {
|
||||
let source_text = r"
|
||||
<script lang=tsx setup>
|
||||
1/1
|
||||
</script>
|
||||
";
|
||||
|
||||
let result = parse_vue(source_text);
|
||||
assert!(result.source_type.is_jsx());
|
||||
assert!(result.source_type.is_typescript());
|
||||
assert_eq!(result.source_type, SourceType::ts());
|
||||
assert_eq!(result.source_text.trim(), "1/1");
|
||||
}
|
||||
|
||||
@@ -252,4 +240,28 @@ mod test {
|
||||
assert_eq!(sources.len(), 1);
|
||||
assert_eq!(sources[0].source_text, "a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lang() {
|
||||
let cases = [
|
||||
("<script>debugger</script>", Some(SourceType::mjs())),
|
||||
("<script lang = 'tsx' >debugger</script>", Some(SourceType::tsx())),
|
||||
(r#"<script lang = "cjs" >debugger</script>"#, Some(SourceType::cjs())),
|
||||
("<script lang = 'xxx'>debugger</script>", None),
|
||||
(r#"<script lang = "xxx">debugger</script>"#, None),
|
||||
("<script lang='xxx'>debugger</script>", None),
|
||||
(r#"<script lang="xxx">debugger</script>"#, None),
|
||||
("<script lang=tsx>debugger</script>", None), // this is valid but too compliated to parse
|
||||
];
|
||||
|
||||
for (source_text, source_type) in cases {
|
||||
let sources = VuePartialLoader::new(source_text).parse();
|
||||
if let Some(expected) = source_type {
|
||||
assert_eq!(sources.len(), 1);
|
||||
assert_eq!(sources[0].source_type, expected);
|
||||
} else {
|
||||
assert_eq!(sources.len(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -462,36 +462,44 @@ impl SourceType {
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut source_type = Self::from_extension(extension)?;
|
||||
|
||||
#[expect(clippy::case_sensitive_file_extension_comparisons)]
|
||||
if match extension {
|
||||
"ts" if file_name[..file_name.len() - 3].split('.').rev().take(2).any(|c| c == "d") => {
|
||||
true
|
||||
}
|
||||
"mts" | "cts" if file_name[..file_name.len() - 4].ends_with(".d") => true,
|
||||
_ => false,
|
||||
} {
|
||||
source_type.language = Language::TypeScriptDefinition;
|
||||
}
|
||||
|
||||
Ok(source_type)
|
||||
}
|
||||
|
||||
/// Converts a file extension to [`SourceType`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`UnknownExtension`] if:
|
||||
/// * the file extension is not one of "js", "mjs", "cjs", "jsx", "ts",
|
||||
/// "mts", "cts", "tsx". See [`VALID_EXTENSIONS`] for the list of valid
|
||||
/// extensions.
|
||||
pub fn from_extension(extension: &str) -> Result<Self, UnknownExtension> {
|
||||
let module_kind = match extension {
|
||||
"js" | "tsx" | "ts" | "jsx" | "mts" | "mjs" => ModuleKind::Module,
|
||||
"cjs" | "cts" => ModuleKind::Script,
|
||||
_ => unreachable!(),
|
||||
_ => return Err(UnknownExtension::new("Unknown extension.")),
|
||||
};
|
||||
|
||||
let language = match extension {
|
||||
// https://www.typescriptlang.org/tsconfig/#allowArbitraryExtensions
|
||||
// `{file basename}.d.{extension}.ts`
|
||||
// https://github.com/microsoft/TypeScript/issues/50133
|
||||
"ts" => {
|
||||
if file_name[..file_name.len() - 3].split('.').rev().take(2).any(|c| c == "d") {
|
||||
Language::TypeScriptDefinition
|
||||
} else {
|
||||
Language::TypeScript
|
||||
}
|
||||
}
|
||||
"js" | "cjs" | "mjs" | "jsx" => Language::JavaScript,
|
||||
"tsx" => Language::TypeScript,
|
||||
#[expect(clippy::case_sensitive_file_extension_comparisons)]
|
||||
"mts" | "cts" => {
|
||||
if file_name[..file_name.len() - 4].ends_with(".d") {
|
||||
Language::TypeScriptDefinition
|
||||
} else {
|
||||
Language::TypeScript
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
"ts" | "tsx" | "mts" | "cts" => Language::TypeScript,
|
||||
_ => return Err(UnknownExtension::new("Unknown extension.")),
|
||||
};
|
||||
|
||||
let variant = match extension {
|
||||
|
||||
Reference in new issue
Block a user