feat(linter): parse multiple script tags in astro file (#1898)

This commit is contained in:
Boshen 2024-01-05 16:36:31 +08:00 committed by GitHub
parent 4c5c61e5f9
commit 8a3eff1bee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 74 additions and 9 deletions

View file

@ -1,3 +1,20 @@
---
debugger
---
<!-- Store the message prop as a data attribute. -->
<astro-greet data-message={message}>
<button>Say hi!</button>
</astro-greet>
<script asdf >
debugger
</script>
<script asdf>
debugger
</script>
<script>
debugger
</script>

View file

@ -370,7 +370,7 @@ mod test {
let args = &["fixtures/astro/debugger.astro"];
let result = test(args);
assert_eq!(result.number_of_files, 1);
assert_eq!(result.number_of_warnings, 1);
assert_eq!(result.number_of_warnings, 4);
assert_eq!(result.number_of_errors, 0);
}
}

View file

@ -1,9 +1,13 @@
use memchr::memmem;
use memchr::memmem::Finder;
use oxc_span::{SourceType, Span};
use super::JavaScriptSource;
const ASTRO_SPLIT: &str = "---";
const SCRIPT_START: &str = "<script";
const SCRIPT_END: &str = "</script>";
pub struct AstroPartialLoader<'a> {
source_text: &'a str,
}
@ -15,28 +19,72 @@ impl<'a> AstroPartialLoader<'a> {
pub fn parse(self) -> Vec<JavaScriptSource<'a>> {
let mut results = vec![];
results.extend(self.parse_frontmatter());
let frontmatter = self.parse_frontmatter();
let start = frontmatter.as_ref().map_or(0, |r| r.source_text.len() + ASTRO_SPLIT.len() * 2);
results.extend(frontmatter);
results.extend(self.parse_scripts(start));
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 {
let split_finder = Finder::new(ASTRO_SPLIT);
let offsets = split_finder.find_iter(self.source_text.as_bytes()).collect::<Vec<_>>();
if offsets.len() <= 1 {
return None;
}
let start = indexes.first()?;
let end = indexes.last()?;
let start = offsets.first()?;
let end = offsets.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);
let js_code =
Span::new(start + ASTRO_SPLIT.len() as u32, end).source_text(self.source_text);
Some(JavaScriptSource::new(
js_code,
SourceType::default().with_typescript(true).with_module(true),
))
}
/// In .astro files, you can add client-side JavaScript by adding one (or more) <script> tags.
/// https://docs.astro.build/en/guides/client-side-scripts/#using-script-in-astro
fn parse_scripts(&self, start: usize) -> Vec<JavaScriptSource<'a>> {
let script_start_finder = Finder::new(SCRIPT_START);
let script_end_finder = Finder::new(SCRIPT_END);
let mut results = vec![];
let mut pointer = start;
loop {
let js_start;
let js_end;
// find opening "<script"
if let Some(offset) = script_start_finder.find(self.source_text[pointer..].as_bytes()) {
pointer += offset + SCRIPT_START.len();
} else {
break;
};
// find closing ">"
if let Some(offset) = self.source_text[pointer..].find('>') {
pointer += offset + 1;
js_start = pointer;
} else {
break;
};
// find "</script>"
if let Some(offset) = script_end_finder.find(self.source_text[pointer..].as_bytes()) {
js_end = pointer + offset;
pointer += offset + SCRIPT_END.len();
} else {
break;
};
results.push(JavaScriptSource::new(
&self.source_text[js_start..js_end],
SourceType::default().with_typescript(true).with_module(true),
));
}
results
}
}