feat(linter): <script> part of svelte file (#1918)

closes #1914
This commit is contained in:
Boshen 2024-01-07 00:13:44 +08:00 committed by GitHub
parent 98a2d60cde
commit f2ed83ca3b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 82 additions and 32 deletions

View file

@ -0,0 +1,5 @@
<script>
debugger;
</script>
<h1>Hello {name}!</h1>

View file

@ -379,4 +379,13 @@ mod test {
assert_eq!(result.number_of_warnings, 4);
assert_eq!(result.number_of_errors, 0);
}
#[test]
fn lint_svelte_file() {
let args = &["fixtures/svelte/debugger.svelte"];
let result = test(args);
assert_eq!(result.number_of_files, 1);
assert_eq!(result.number_of_warnings, 1);
assert_eq!(result.number_of_errors, 0);
}
}

View file

@ -2,11 +2,9 @@ use memchr::memmem::Finder;
use oxc_span::{SourceType, Span};
use super::JavaScriptSource;
use super::{JavaScriptSource, SCRIPT_END, SCRIPT_START};
const ASTRO_SPLIT: &str = "---";
const SCRIPT_START: &str = "<script";
const SCRIPT_END: &str = "</script>";
pub struct AstroPartialLoader<'a> {
source_text: &'a str,

View file

@ -1,16 +1,15 @@
mod astro;
mod svelte;
mod vue;
use oxc_span::SourceType;
pub use self::{astro::AstroPartialLoader, vue::VuePartialLoader};
pub use self::{astro::AstroPartialLoader, svelte::SveltePartialLoader, vue::VuePartialLoader};
pub const LINT_PARTIAL_LOADER_EXT: &[&str] = &["vue", "astro"];
const SCRIPT_START: &str = "<script";
const SCRIPT_END: &str = "</script>";
pub enum PartialLoader {
Vue,
Astro,
}
pub const LINT_PARTIAL_LOADER_EXT: &[&str] = &["vue", "astro", "svelte"];
#[derive(Debug, Clone, Copy)]
pub struct JavaScriptSource<'a> {
@ -24,11 +23,17 @@ impl<'a> JavaScriptSource<'a> {
}
}
pub struct PartialLoader;
impl PartialLoader {
pub fn build<'a>(&self, source_text: &'a str) -> Vec<JavaScriptSource<'a>> {
match self {
Self::Vue => VuePartialLoader::new(source_text).parse(),
Self::Astro => AstroPartialLoader::new(source_text).parse(),
/// Extract js section of specifial files.
/// Returns `None` if the specifial file does not have a js section.
pub fn parse<'a>(ext: &str, source_text: &'a str) -> Option<Vec<JavaScriptSource<'a>>> {
match ext {
"vue" => Some(VuePartialLoader::new(source_text).parse()),
"astro" => Some(AstroPartialLoader::new(source_text).parse()),
"svelte" => Some(SveltePartialLoader::new(source_text).parse()),
_ => None,
}
}
}

View file

@ -0,0 +1,48 @@
use memchr::memmem::Finder;
use oxc_span::SourceType;
use super::{JavaScriptSource, SCRIPT_END, SCRIPT_START};
pub struct SveltePartialLoader<'a> {
source_text: &'a str,
}
impl<'a> SveltePartialLoader<'a> {
pub fn new(source_text: &'a str) -> Self {
Self { source_text }
}
pub fn parse(self) -> Vec<JavaScriptSource<'a>> {
self.parse_script().map_or_else(Vec::new, |source| vec![source])
}
fn parse_script(&self) -> Option<JavaScriptSource<'a>> {
let script_start_finder = Finder::new(SCRIPT_START);
let script_end_finder = Finder::new(SCRIPT_END);
let mut pointer = 0;
// find opening "<script"
let offset = script_start_finder.find(self.source_text[pointer..].as_bytes())?;
pointer += offset + SCRIPT_START.len();
// find closing ">"
let offset = self.source_text[pointer..].find('>')?;
// get lang="ts" attribute
let content = &self.source_text[pointer..pointer + offset];
let is_ts = content.contains("ts");
pointer += offset + 1;
let js_start = pointer;
// find "</script>"
let offset = script_end_finder.find(self.source_text[pointer..].as_bytes())?;
let js_end = pointer + offset;
let source_text = &self.source_text[js_start..js_end];
let source_type = SourceType::default().with_module(true).with_typescript(is_ts);
Some(JavaScriptSource::new(source_text, source_type))
}
}

View file

@ -2,10 +2,7 @@ use memchr::memmem::Finder;
use oxc_span::SourceType;
use super::JavaScriptSource;
const SCRIPT_START: &str = "<script";
const SCRIPT_END: &str = "</script>";
use super::{JavaScriptSource, SCRIPT_END, SCRIPT_START};
pub struct VuePartialLoader<'a> {
source_text: &'a str,

View file

@ -159,20 +159,6 @@ impl Runtime {
Some(Ok((source_type, source_text)))
}
/// Extract js section of specifial files.
/// Returns `None` if the specifial file does not have a js section.
fn extract_js<'a>(
source_text: &'a str,
source_type: SourceType,
ext: &str,
) -> Vec<JavaScriptSource<'a>> {
match ext {
"vue" => PartialLoader::Vue.build(source_text),
"astro" => PartialLoader::Astro.build(source_text),
_ => vec![JavaScriptSource::new(source_text, source_type)],
}
}
fn process_path(&self, path: &Path, tx_error: &DiagnosticSender) {
if self.init_cache_state(path) {
return;
@ -188,7 +174,9 @@ impl Runtime {
return;
}
};
let sources = Self::extract_js(&source_text, source_type, ext);
let sources = PartialLoader::parse(ext, &source_text)
.unwrap_or_else(|| vec![JavaScriptSource::new(&source_text, source_type)]);
if sources.is_empty() {
return;