use memchr::memmem::Finder;
use oxc_span::SourceType;
use super::{find_script_closing_angle, 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;
let offset = script_start_finder.find(&self.source_text.as_bytes()[pointer..])?;
pointer += offset + SCRIPT_START.len();
let offset = find_script_closing_angle(self.source_text, pointer)?;
let content = &self.source_text[pointer..pointer + offset];
let is_ts = content.contains("ts");
pointer += offset + 1;
let js_start = pointer;
let offset = script_end_finder.find(&self.source_text.as_bytes()[pointer..])?;
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, js_start))
}
}
#[cfg(test)]
mod test {
use super::{JavaScriptSource, SveltePartialLoader};
fn parse_svelte(source_text: &str) -> JavaScriptSource<'_> {
let sources = SveltePartialLoader::new(source_text).parse();
*sources.first().unwrap()
}
#[test]
fn test_parse_svelte() {
let source_text = r#"
<script>
console.log("hi");
</script>
<h1>Hello World</h1>
"#;
let result = parse_svelte(source_text);
assert_eq!(result.source_text.trim(), r#"console.log("hi");"#);
}
#[test]
fn test_parse_svelte_ts_with_generic() {
let source_text = r#"
<script lang="ts" generics="T extends Record<string, unknown>">
console.log("hi");
</script>
<h1>Hello World</h1>
"#;
let result = parse_svelte(source_text);
assert_eq!(result.source_text.trim(), r#"console.log("hi");"#);
}
}