feat(linter): add support for multiple script tags from vue and stro (#1897)

part of #1896
This commit is contained in:
Boshen 2024-01-05 16:30:32 +08:00 committed by GitHub
parent 064296cd43
commit 4c5c61e5f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 69 deletions

View file

@ -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(

View file

@ -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<PartialLoaderValue<'a>> {
self.parse();
if self.end <= self.start {
pub fn parse(self) -> Vec<JavaScriptSource<'a>> {
let mut results = vec![];
results.extend(self.parse_frontmatter());
results
}
/// Parse `---` front matter block
#[allow(clippy::cast_possible_truncation)]
fn parse_frontmatter(&self) -> Option<JavaScriptSource<'a>> {
let split = "---";
let indexes = memmem::find_iter(self.source_text.as_bytes(), split).collect::<Vec<_>>();
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::<Vec<_>>();
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),
))
}
}

View file

@ -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<PartialLoaderValue<'a>> {
pub fn build<'a>(&self, source_text: &'a str) -> Vec<JavaScriptSource<'a>> {
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(),
}
}
}

View file

@ -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<PartialLoaderValue<'a>> {
pub fn build(mut self) -> Option<JavaScriptSource<'a>> {
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) {

View file

@ -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<JavaScriptSource<'a>> {
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();
}
}
}