use vize_atelier_sfc::SfcDescriptor;
use vize_carton::Bump;
use super::{
ScriptCodeGenerator, StyleCodeGenerator, TemplateCodeGenerator, VirtualDocument,
VirtualDocuments, VirtualLanguage,
};
pub struct VirtualCodeGenerator {
template_gen: TemplateCodeGenerator,
script_gen: ScriptCodeGenerator,
style_gen: StyleCodeGenerator,
}
impl VirtualCodeGenerator {
#[inline]
pub fn new() -> Self {
Self {
template_gen: TemplateCodeGenerator::new(),
script_gen: ScriptCodeGenerator::new(),
style_gen: StyleCodeGenerator::new(),
}
}
pub fn generate<'a>(
&mut self,
descriptor: &SfcDescriptor<'a>,
base_uri: &str,
) -> VirtualDocuments {
let allocator = Bump::new();
let mut docs = VirtualDocuments::new();
if let Some(ref template) = descriptor.template {
let template_content = template.content.as_ref();
let (ast, _errors) = vize_armature::parse(&allocator, template_content);
self.template_gen
.set_block_offset(template.loc.start as u32);
let mut template_doc = self.template_gen.generate(&ast, template_content);
template_doc.uri = format!("{}.__template.ts", base_uri);
docs.template = Some(template_doc);
}
if let Some(ref script) = descriptor.script {
let mut script_doc = self.script_gen.generate(script, false);
script_doc.uri = format!("{}.__script.ts", base_uri);
docs.script = Some(script_doc);
}
if let Some(ref script_setup) = descriptor.script_setup {
let mut script_doc = self.script_gen.generate(script_setup, true);
script_doc.uri = format!("{}.__script_setup.ts", base_uri);
docs.script_setup = Some(script_doc);
}
for (i, style) in descriptor.styles.iter().enumerate() {
let mut style_doc = self.style_gen.generate(style, i);
let ext = style.lang.as_ref().map(|l| l.as_ref()).unwrap_or("css");
style_doc.uri = format!("{}.__style_{}.{}", base_uri, i, ext);
docs.styles.push(style_doc);
}
docs
}
pub fn generate_with_allocator<'a, 'alloc>(
&mut self,
descriptor: &SfcDescriptor<'a>,
base_uri: &str,
allocator: &'alloc Bump,
) -> VirtualDocuments {
let mut docs = VirtualDocuments::new();
if let Some(ref template) = descriptor.template {
let template_content = template.content.as_ref();
let (ast, _errors) = vize_armature::parse(allocator, template_content);
self.template_gen
.set_block_offset(template.loc.start as u32);
let mut template_doc = self.template_gen.generate(&ast, template_content);
template_doc.uri = format!("{}.__template.ts", base_uri);
docs.template = Some(template_doc);
}
if let Some(ref script) = descriptor.script {
let mut script_doc = self.script_gen.generate(script, false);
script_doc.uri = format!("{}.__script.ts", base_uri);
docs.script = Some(script_doc);
}
if let Some(ref script_setup) = descriptor.script_setup {
let mut script_doc = self.script_gen.generate(script_setup, true);
script_doc.uri = format!("{}.__script_setup.ts", base_uri);
docs.script_setup = Some(script_doc);
}
for (i, style) in descriptor.styles.iter().enumerate() {
let mut style_doc = self.style_gen.generate(style, i);
let ext = style.lang.as_ref().map(|l| l.as_ref()).unwrap_or("css");
style_doc.uri = format!("{}.__style_{}.{}", base_uri, i, ext);
docs.styles.push(style_doc);
}
docs
}
#[inline]
pub fn generate_template_only(&mut self, template_content: &str) -> Option<VirtualDocument> {
let allocator = Bump::new();
let (ast, _) = vize_armature::parse(&allocator, template_content);
let mut doc = self.template_gen.generate(&ast, template_content);
doc.uri = "__inline.__template.ts".to_string();
Some(doc)
}
}
impl Default for VirtualCodeGenerator {
fn default() -> Self {
Self::new()
}
}
pub struct BatchVirtualCodeGenerator {
generator: VirtualCodeGenerator,
allocator: Bump,
}
impl BatchVirtualCodeGenerator {
#[inline]
pub fn new() -> Self {
Self {
generator: VirtualCodeGenerator::new(),
allocator: Bump::new(),
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
generator: VirtualCodeGenerator::new(),
allocator: Bump::with_capacity(capacity),
}
}
pub fn generate<'a>(
&mut self,
descriptor: &SfcDescriptor<'a>,
base_uri: &str,
) -> VirtualDocuments {
self.allocator.reset();
self.generator
.generate_with_allocator(descriptor, base_uri, &self.allocator)
}
pub fn generate_batch<'a>(
&mut self,
files: &[(&SfcDescriptor<'a>, &str)],
) -> Vec<VirtualDocuments> {
files
.iter()
.map(|(descriptor, uri)| {
self.allocator.reset();
self.generator
.generate_with_allocator(descriptor, uri, &self.allocator)
})
.collect()
}
#[inline]
pub fn allocated_bytes(&self) -> usize {
self.allocator.allocated_bytes()
}
}
impl Default for BatchVirtualCodeGenerator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockType {
Template,
Script,
ScriptSetup,
Style(usize),
}
impl BlockType {
#[inline]
pub fn language(&self) -> VirtualLanguage {
match self {
BlockType::Template => VirtualLanguage::Template,
BlockType::Script => VirtualLanguage::Script,
BlockType::ScriptSetup => VirtualLanguage::ScriptSetup,
BlockType::Style(_) => VirtualLanguage::Style,
}
}
}
pub fn find_block_at_offset(descriptor: &SfcDescriptor, offset: usize) -> Option<BlockType> {
if let Some(ref template) = descriptor.template {
if offset >= template.loc.start && offset < template.loc.end {
return Some(BlockType::Template);
}
}
if let Some(ref script) = descriptor.script {
if offset >= script.loc.start && offset < script.loc.end {
return Some(BlockType::Script);
}
}
if let Some(ref script_setup) = descriptor.script_setup {
if offset >= script_setup.loc.start && offset < script_setup.loc.end {
return Some(BlockType::ScriptSetup);
}
}
for (i, style) in descriptor.styles.iter().enumerate() {
if offset >= style.loc.start && offset < style.loc.end {
return Some(BlockType::Style(i));
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_virtual_code_generator() {
let source = r#"<template>
<div>{{ message }}</div>
</template>
<script setup lang="ts">
const message = ref('hello')
</script>
<style scoped>
.container { color: red; }
</style>"#;
let descriptor = vize_atelier_sfc::parse_sfc(source, Default::default()).unwrap();
let mut gen = VirtualCodeGenerator::new();
let docs = gen.generate(&descriptor, "test.vue");
assert!(docs.template.is_some());
assert!(docs.script_setup.is_some());
assert_eq!(docs.styles.len(), 1);
let template = docs.template.unwrap();
assert!(template.content.contains("__VIZE_ctx.message"));
assert!(!template.source_map.is_empty());
}
#[test]
fn test_batch_generator() {
let source1 = "<template><div>{{ a }}</div></template>";
let source2 = "<template><div>{{ b }}</div></template>";
let desc1 = vize_atelier_sfc::parse_sfc(source1, Default::default()).unwrap();
let desc2 = vize_atelier_sfc::parse_sfc(source2, Default::default()).unwrap();
let mut batch = BatchVirtualCodeGenerator::new();
let results = batch.generate_batch(&[(&desc1, "file1.vue"), (&desc2, "file2.vue")]);
assert_eq!(results.len(), 2);
assert!(results[0].template.is_some());
assert!(results[1].template.is_some());
}
#[test]
fn test_find_block_at_offset() {
let source = r#"<template>
<div>test</div>
</template>
<script setup>
const x = 1
</script>"#;
let descriptor = vize_atelier_sfc::parse_sfc(source, Default::default()).unwrap();
assert_eq!(
find_block_at_offset(&descriptor, 15),
Some(BlockType::Template)
);
assert_eq!(
find_block_at_offset(&descriptor, 60),
Some(BlockType::ScriptSetup)
);
}
}