From 4c5c61e5f9a42da004e0e0f9eb36628d234a4619 Mon Sep 17 00:00:00 2001 From: Boshen Date: Fri, 5 Jan 2024 16:30:32 +0800 Subject: [PATCH] feat(linter): add support for multiple script tags from vue and stro (#1897) part of #1896 --- crates/oxc_language_server/src/linter.rs | 15 +++--- .../partial_loader/astro_partial_loader.rs | 50 ++++++++++--------- crates/oxc_linter/src/partial_loader/mod.rs | 20 ++++---- .../src/partial_loader/vue_partial_loader.rs | 12 +++-- crates/oxc_linter/src/service.rs | 49 +++++++++--------- 5 files changed, 77 insertions(+), 69 deletions(-) diff --git a/crates/oxc_language_server/src/linter.rs b/crates/oxc_language_server/src/linter.rs index 0c2d23534..0993d0e30 100644 --- a/crates/oxc_language_server/src/linter.rs +++ b/crates/oxc_language_server/src/linter.rs @@ -15,7 +15,7 @@ use oxc_allocator::Allocator; use oxc_diagnostics::{miette, Error, Severity}; use oxc_linter::{ partial_loader::{ - vue_partial_loader::VuePartialLoader, PartialLoader, PartialLoaderValue, + vue_partial_loader::VuePartialLoader, JavaScriptSource, PartialLoader, LINT_PARTIAL_LOADER_EXT, }, LintContext, LintSettings, Linter, @@ -306,13 +306,12 @@ impl IsolatedLintHandler { source_text: &'a str, ext: &str, ) -> Option<(&'a str, SourceType)> { - match ext { - "vue" => PartialLoader::Vue.build(source_text).map(|r| (r.source_text, r.source_type)), - "astro" => { - PartialLoader::Astro.build(source_text).map(|r| (r.source_text, r.source_type)) - } - _ => None, - } + None + // match ext { + // "vue" => PartialLoader::Vue.build(source_text), + // "astro" => PartialLoader::Astro.build(source_text)), + // _ => None, + // } } fn lint_path( diff --git a/crates/oxc_linter/src/partial_loader/astro_partial_loader.rs b/crates/oxc_linter/src/partial_loader/astro_partial_loader.rs index 37cd6a593..c5a1005c4 100644 --- a/crates/oxc_linter/src/partial_loader/astro_partial_loader.rs +++ b/crates/oxc_linter/src/partial_loader/astro_partial_loader.rs @@ -1,40 +1,42 @@ use memchr::memmem; -use oxc_span::Span; -use super::PartialLoaderValue; +use oxc_span::{SourceType, Span}; + +use super::JavaScriptSource; pub struct AstroPartialLoader<'a> { source_text: &'a str, - /// JS code start position - start: u32, - /// JS code end position - end: u32, } impl<'a> AstroPartialLoader<'a> { pub fn new(source_text: &'a str) -> Self { - Self { source_text, start: 0, end: 0 } + Self { source_text } } - pub fn build(mut self) -> Option> { - self.parse(); - if self.end <= self.start { + pub fn parse(self) -> Vec> { + let mut results = vec![]; + results.extend(self.parse_frontmatter()); + results + } + + /// Parse `---` front matter block + #[allow(clippy::cast_possible_truncation)] + fn parse_frontmatter(&self) -> Option> { + let split = "---"; + let indexes = memmem::find_iter(self.source_text.as_bytes(), split).collect::>(); + if indexes.len() <= 1 { return None; } - let js_code = Span::new(self.start, self.end).source_text(self.source_text); - Some(PartialLoaderValue::new(js_code, /* is_ts */ true, /* self.is_jsx */ false)) - } - fn parse(&mut self) { - let indexes = memmem::find_iter(self.source_text.as_bytes(), "---").collect::>(); - if indexes.len() <= 1 { - return; - } - let Some(start) = indexes.first() else { return }; - let Some(end) = indexes.last() else { return }; - let Ok(start) = u32::try_from(*start) else { return }; - let Ok(end) = u32::try_from(*end) else { return }; - self.start = start + 3; - self.end = end; + let start = indexes.first()?; + let end = indexes.last()?; + let Ok(start) = u32::try_from(*start) else { return None }; + let Ok(end) = u32::try_from(*end) else { return None }; + + let js_code = Span::new(start + split.len() as u32, end).source_text(self.source_text); + Some(JavaScriptSource::new( + js_code, + SourceType::default().with_typescript(true).with_module(true), + )) } } diff --git a/crates/oxc_linter/src/partial_loader/mod.rs b/crates/oxc_linter/src/partial_loader/mod.rs index abfc9b959..c4a528a06 100644 --- a/crates/oxc_linter/src/partial_loader/mod.rs +++ b/crates/oxc_linter/src/partial_loader/mod.rs @@ -12,25 +12,27 @@ pub enum PartialLoader { Astro, } -#[derive(Default)] -pub struct PartialLoaderValue<'a> { +#[derive(Debug)] +pub struct JavaScriptSource<'a> { pub source_text: &'a str, pub source_type: SourceType, } -impl<'a> PartialLoaderValue<'a> { - pub fn new(source_text: &'a str, is_ts: bool, is_jsx: bool) -> Self { - let source_type = - SourceType::default().with_typescript(is_ts).with_module(true).with_jsx(is_jsx); +impl<'a> JavaScriptSource<'a> { + pub fn new(source_text: &'a str, source_type: SourceType) -> Self { Self { source_text, source_type } } } impl PartialLoader { - pub fn build<'a>(&self, source_text: &'a str) -> Option> { + pub fn build<'a>(&self, source_text: &'a str) -> Vec> { match self { - Self::Vue => VuePartialLoader::new(source_text).build(), - Self::Astro => AstroPartialLoader::new(source_text).build(), + Self::Vue => { + let mut results = vec![]; + results.extend(VuePartialLoader::new(source_text).build()); + results + } + Self::Astro => AstroPartialLoader::new(source_text).parse(), } } } diff --git a/crates/oxc_linter/src/partial_loader/vue_partial_loader.rs b/crates/oxc_linter/src/partial_loader/vue_partial_loader.rs index 0febb1acf..d53fdd110 100644 --- a/crates/oxc_linter/src/partial_loader/vue_partial_loader.rs +++ b/crates/oxc_linter/src/partial_loader/vue_partial_loader.rs @@ -1,8 +1,8 @@ use std::str::Chars; -use oxc_span::Span; +use oxc_span::{SourceType, Span}; -use super::PartialLoaderValue; +use super::JavaScriptSource; pub struct VuePartialLoader<'a> { source_text: &'a str, @@ -32,13 +32,17 @@ impl<'a> VuePartialLoader<'a> { } } - pub fn build(mut self) -> Option> { + pub fn build(mut self) -> Option> { self.parse(); if self.end <= self.start { return None; } let js_code = Span::new(self.start, self.end).source_text(self.source_text); - Some(PartialLoaderValue::new(js_code, self.is_ts, self.is_jsx)) + let source_type = SourceType::default() + .with_typescript(self.is_ts) + .with_jsx(self.is_jsx) + .with_module(true); + Some(JavaScriptSource::new(js_code, source_type)) } fn parse(&mut self) { diff --git a/crates/oxc_linter/src/service.rs b/crates/oxc_linter/src/service.rs index 35e085696..b03fa2a09 100644 --- a/crates/oxc_linter/src/service.rs +++ b/crates/oxc_linter/src/service.rs @@ -18,7 +18,7 @@ use oxc_semantic::{ModuleRecord, SemanticBuilder}; use oxc_span::{SourceType, VALID_EXTENSIONS}; use crate::{ - partial_loader::{PartialLoader, LINT_PARTIAL_LOADER_EXT}, + partial_loader::{JavaScriptSource, PartialLoader, LINT_PARTIAL_LOADER_EXT}, Fixer, LintContext, Linter, Message, }; @@ -165,13 +165,11 @@ impl Runtime { source_text: &'a str, source_type: SourceType, ext: &str, - ) -> Option<(&'a str, SourceType)> { + ) -> Vec> { match ext { - "vue" => PartialLoader::Vue.build(source_text).map(|r| (r.source_text, r.source_type)), - "astro" => { - PartialLoader::Astro.build(source_text).map(|r| (r.source_text, r.source_type)) - } - _ => Some((source_text, source_type)), + "vue" => PartialLoader::Vue.build(source_text), + "astro" => PartialLoader::Astro.build(source_text), + _ => vec![JavaScriptSource::new(source_text, source_type)], } } @@ -190,26 +188,29 @@ impl Runtime { return; } }; - let Some((source_text, source_type)) = Self::extract_js(&source_text, source_type, ext) - else { + let sources = Self::extract_js(&source_text, source_type, ext); + + if sources.is_empty() { return; - }; - - let allocator = Allocator::default(); - let mut messages = - self.process_source(path, &allocator, source_text, source_type, true, tx_error); - - if self.linter.options().fix { - let fix_result = Fixer::new(source_text, messages).fix(); - fs::write(path, fix_result.fixed_code.as_bytes()).unwrap(); - messages = fix_result.messages; } - if !messages.is_empty() { - let errors = messages.into_iter().map(|m| m.error).collect(); - let path = path.strip_prefix(&self.cwd).unwrap_or(path); - let diagnostics = DiagnosticService::wrap_diagnostics(path, source_text, errors); - tx_error.send(Some(diagnostics)).unwrap(); + for JavaScriptSource { source_text, source_type } in sources { + let allocator = Allocator::default(); + let mut messages = + self.process_source(path, &allocator, source_text, source_type, true, tx_error); + + if self.linter.options().fix { + let fix_result = Fixer::new(source_text, messages).fix(); + fs::write(path, fix_result.fixed_code.as_bytes()).unwrap(); + messages = fix_result.messages; + } + + if !messages.is_empty() { + let errors = messages.into_iter().map(|m| m.error).collect(); + let path = path.strip_prefix(&self.cwd).unwrap_or(path); + let diagnostics = DiagnosticService::wrap_diagnostics(path, source_text, errors); + tx_error.send(Some(diagnostics)).unwrap(); + } } }