mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(linter): add support for multiple script tags from vue and stro (#1897)
part of #1896
This commit is contained in:
parent
064296cd43
commit
4c5c61e5f9
5 changed files with 77 additions and 69 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue