1use std::sync::Arc;
32
33use smallvec::SmallVec;
34
35use crate::ast::common::{ScriptContext, Span};
36use crate::ast::modern::{self, Node as ModernNode};
37use crate::error::CompileError;
38use crate::js::{JsExpression, JsProgram};
39use crate::parse::{ParseMode, ParseOptions};
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
47pub struct NodeId(u32);
48
49impl NodeId {
50 fn new(index: usize) -> Self {
51 Self(index as u32)
52 }
53
54 fn index(self) -> usize {
55 self.0 as usize
56 }
57}
58
59pub struct SvelteAst {
86 nodes: Vec<ArenaNode>,
87 source: Arc<str>,
88 offset_index: Vec<(u32, NodeId)>,
90}
91
92#[derive(Debug)]
94pub struct ArenaNode {
95 pub id: NodeId,
99 pub parent: Option<NodeId>,
100 pub kind: NodeKind,
101 pub start: u32,
102 pub end: u32,
103 pub children: SmallVec<[NodeId; 4]>,
104}
105
106#[derive(Debug, Clone)]
109pub enum NodeKind {
110 Root,
111 Text { data: Arc<str> },
113 Comment { data: Arc<str> },
114 ExpressionTag,
115 HtmlTag,
116 ConstTag,
117 DebugTag,
118 RenderTag,
119 IfBlock { elseif: bool },
121 EachBlock,
122 AwaitBlock,
123 KeyBlock,
124 SnippetBlock { name: Arc<str> },
125 RegularElement { name: Arc<str> },
127 Component { name: Arc<str> },
128 SlotElement { name: Arc<str> },
129 SvelteHead,
130 SvelteBody,
131 SvelteWindow,
132 SvelteDocument,
133 SvelteComponent,
134 SvelteElement,
135 SvelteSelf,
136 SvelteFragment,
137 SvelteBoundary,
138 TitleElement,
139 Script { context: ScriptContext, program: Arc<JsProgram> },
141 StyleSheet,
142 Expression { handle: Option<Arc<JsExpression>> },
144 Attribute { name: Arc<str> },
146 Alternate,
147}
148
149impl NodeKind {
150 pub fn name(&self) -> &'static str {
152 match self {
153 Self::Root => "Root",
154 Self::Text { .. } => "Text",
155 Self::Comment { .. } => "Comment",
156 Self::ExpressionTag => "ExpressionTag",
157 Self::HtmlTag => "HtmlTag",
158 Self::ConstTag => "ConstTag",
159 Self::DebugTag => "DebugTag",
160 Self::RenderTag => "RenderTag",
161 Self::IfBlock { .. } => "IfBlock",
162 Self::EachBlock => "EachBlock",
163 Self::AwaitBlock => "AwaitBlock",
164 Self::KeyBlock => "KeyBlock",
165 Self::SnippetBlock { .. } => "SnippetBlock",
166 Self::RegularElement { .. } => "RegularElement",
167 Self::Component { .. } => "Component",
168 Self::SlotElement { .. } => "SlotElement",
169 Self::SvelteHead => "SvelteHead",
170 Self::SvelteBody => "SvelteBody",
171 Self::SvelteWindow => "SvelteWindow",
172 Self::SvelteDocument => "SvelteDocument",
173 Self::SvelteComponent => "SvelteComponent",
174 Self::SvelteElement => "SvelteElement",
175 Self::SvelteSelf => "SvelteSelf",
176 Self::SvelteFragment => "SvelteFragment",
177 Self::SvelteBoundary => "SvelteBoundary",
178 Self::TitleElement => "TitleElement",
179 Self::Script { .. } => "Script",
180 Self::StyleSheet => "StyleSheet",
181 Self::Expression { .. } => "Expression",
182 Self::Attribute { .. } => "Attribute",
183 Self::Alternate => "Alternate",
184 }
185 }
186}
187
188impl std::fmt::Display for NodeKind {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 match self {
191 Self::RegularElement { name } | Self::Component { name } | Self::SlotElement { name } => {
192 write!(f, "{} <{}>", self.name(), name)
193 }
194 Self::SnippetBlock { name } => write!(f, "SnippetBlock {}", name),
195 Self::Attribute { name } => write!(f, "Attribute {}", name),
196 Self::Script { context, .. } => write!(f, "Script ({:?})", context),
197 _ => f.write_str(self.name()),
198 }
199 }
200}
201
202#[derive(Debug, Clone)]
204pub struct TextEdit {
205 pub range: std::ops::Range<usize>,
206 pub replacement: String,
207}
208
209#[derive(Debug, Clone)]
211pub struct EditResult {
212 pub changed_nodes: Vec<NodeId>,
213 pub removed_nodes: Vec<NodeId>,
214 pub added_nodes: Vec<NodeId>,
215}
216
217impl SvelteAst {
220 pub fn parse(source: &str) -> Result<Self, CompileError> {
222 let document = crate::parse::parse(
223 source,
224 ParseOptions {
225 mode: ParseMode::Modern,
226 ..ParseOptions::default()
227 },
228 )?;
229
230 let crate::ast::Root::Modern(root) = document.root else {
231 return Err(CompileError::internal("arena AST requires modern parse mode"));
232 };
233
234 let mut builder = ArenaBuilder {
235 nodes: Vec::with_capacity(64),
236 };
237
238 builder.build_root(&root);
239
240 let mut ast = SvelteAst {
241 nodes: builder.nodes,
242 source: document.source,
243 offset_index: Vec::new(),
244 };
245 ast.rebuild_offset_index();
246 Ok(ast)
247 }
248
249 fn rebuild_offset_index(&mut self) {
250 self.offset_index.clear();
251 self.offset_index.reserve(self.nodes.len());
252 for node in &self.nodes {
253 self.offset_index.push((node.start, node.id));
254 }
255 self.offset_index.sort_by_key(|&(start, id)| (start, id.0));
256 }
257
258 pub fn root(&self) -> NodeId {
262 NodeId(0)
263 }
264
265 pub fn node(&self, id: NodeId) -> &ArenaNode {
267 &self.nodes[id.index()]
268 }
269
270 pub fn kind(&self, id: NodeId) -> &NodeKind {
272 &self.nodes[id.index()].kind
273 }
274
275 pub fn start(&self, id: NodeId) -> u32 {
277 self.nodes[id.index()].start
278 }
279
280 pub fn end(&self, id: NodeId) -> u32 {
282 self.nodes[id.index()].end
283 }
284
285 pub fn parent(&self, id: NodeId) -> Option<NodeId> {
287 self.nodes[id.index()].parent
288 }
289
290 pub fn children(&self, id: NodeId) -> &[NodeId] {
292 &self.nodes[id.index()].children
293 }
294
295 pub fn ancestors(&self, id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
297 let mut current = self.nodes[id.index()].parent;
298 std::iter::from_fn(move || {
299 let node = current?;
300 current = self.nodes[node.index()].parent;
301 Some(node)
302 })
303 }
304
305 pub fn descendants(&self, id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
307 let mut stack = vec![id];
308 std::iter::from_fn(move || {
309 let node = stack.pop()?;
310 let children = &self.nodes[node.index()].children;
312 for &child in children.iter().rev() {
313 stack.push(child);
314 }
315 Some(node)
316 })
317 }
318
319 pub fn siblings(&self, id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
321 let parent = self.nodes[id.index()].parent;
322 let children: &[NodeId] = parent
323 .map(|p| self.nodes[p.index()].children.as_slice())
324 .unwrap_or(&[]);
325 children.iter().copied().filter(move |&child| child != id)
326 }
327
328 pub fn node_at_offset(&self, offset: usize) -> Option<NodeId> {
332 let offset = offset as u32;
333 let idx = self
335 .offset_index
336 .partition_point(|&(start, _)| start <= offset);
337 if idx == 0 {
338 return None;
339 }
340 for &(_, id) in self.offset_index[..idx].iter().rev() {
342 let node = &self.nodes[id.index()];
343 if node.start <= offset && offset < node.end {
344 return Some(id);
345 }
346 if offset - node.start > 10000 {
348 break;
349 }
350 }
351 None
352 }
353
354 pub fn innermost_at_offset(&self, offset: usize) -> Option<NodeId> {
364 let mut current = self.node_at_offset(offset)?;
365 'outer: loop {
366 for &child in &self.nodes[current.index()].children {
367 let node = &self.nodes[child.index()];
368 if node.start <= offset as u32 && (offset as u32) < node.end {
369 current = child;
370 continue 'outer;
371 }
372 }
373 break;
374 }
375 Some(current)
376 }
377
378 pub fn nodes_in_range(&self, start: usize, end: usize) -> Vec<NodeId> {
380 let start = start as u32;
381 let end = end as u32;
382 self.nodes
383 .iter()
384 .filter(|node| node.start < end && node.end > start)
385 .map(|node| node.id)
386 .collect()
387 }
388
389 pub fn source(&self) -> &str {
393 &self.source
394 }
395
396 pub fn node_text(&self, id: NodeId) -> &str {
398 let node = &self.nodes[id.index()];
399 &self.source[node.start as usize..node.end as usize]
400 }
401
402 pub fn len(&self) -> usize {
404 self.nodes.len()
405 }
406
407 pub fn is_empty(&self) -> bool {
409 self.nodes.is_empty()
410 }
411
412 pub fn js_expression(&self, id: NodeId) -> Option<&JsExpression> {
416 match &self.nodes[id.index()].kind {
417 NodeKind::Expression { handle: Some(arc) } => Some(arc.as_ref()),
418 _ => None,
419 }
420 }
421
422 pub fn js_program(&self, id: NodeId) -> Option<&JsProgram> {
424 match &self.nodes[id.index()].kind {
425 NodeKind::Script { program, .. } => Some(program.as_ref()),
426 _ => None,
427 }
428 }
429
430 pub fn is_element(&self, id: NodeId) -> bool {
432 matches!(
433 self.nodes[id.index()].kind,
434 NodeKind::RegularElement { .. }
435 | NodeKind::Component { .. }
436 | NodeKind::SlotElement { .. }
437 | NodeKind::SvelteHead
438 | NodeKind::SvelteBody
439 | NodeKind::SvelteWindow
440 | NodeKind::SvelteDocument
441 | NodeKind::SvelteComponent
442 | NodeKind::SvelteElement
443 | NodeKind::SvelteSelf
444 | NodeKind::SvelteFragment
445 | NodeKind::SvelteBoundary
446 | NodeKind::TitleElement
447 )
448 }
449
450 pub fn is_block(&self, id: NodeId) -> bool {
452 matches!(
453 self.nodes[id.index()].kind,
454 NodeKind::IfBlock { .. }
455 | NodeKind::EachBlock
456 | NodeKind::AwaitBlock
457 | NodeKind::KeyBlock
458 | NodeKind::SnippetBlock { .. }
459 )
460 }
461
462 pub fn element_name(&self, id: NodeId) -> Option<&str> {
464 match &self.nodes[id.index()].kind {
465 NodeKind::RegularElement { name }
466 | NodeKind::Component { name }
467 | NodeKind::SlotElement { name } => Some(name),
468 NodeKind::SvelteHead => Some("svelte:head"),
469 NodeKind::SvelteBody => Some("svelte:body"),
470 NodeKind::SvelteWindow => Some("svelte:window"),
471 NodeKind::SvelteDocument => Some("svelte:document"),
472 NodeKind::SvelteComponent => Some("svelte:component"),
473 NodeKind::SvelteElement => Some("svelte:element"),
474 NodeKind::SvelteSelf => Some("svelte:self"),
475 NodeKind::SvelteFragment => Some("svelte:fragment"),
476 NodeKind::SvelteBoundary => Some("svelte:boundary"),
477 NodeKind::TitleElement => Some("title"),
478 _ => None,
479 }
480 }
481
482 pub fn next_sibling(&self, id: NodeId) -> Option<NodeId> {
484 let parent = self.nodes[id.index()].parent?;
485 let siblings = &self.nodes[parent.index()].children;
486 let pos = siblings.iter().position(|&c| c == id)?;
487 siblings.get(pos + 1).copied()
488 }
489
490 pub fn prev_sibling(&self, id: NodeId) -> Option<NodeId> {
492 let parent = self.nodes[id.index()].parent?;
493 let siblings = &self.nodes[parent.index()].children;
494 let pos = siblings.iter().position(|&c| c == id)?;
495 if pos > 0 { Some(siblings[pos - 1]) } else { None }
496 }
497
498 pub fn depth(&self, id: NodeId) -> usize {
500 self.ancestors(id).count()
501 }
502
503 pub fn edit(&mut self, edit: TextEdit) -> Result<EditResult, CompileError> {
523 let mut new_source = String::with_capacity(
524 self.source.len() - edit.range.len() + edit.replacement.len(),
525 );
526 new_source.push_str(&self.source[..edit.range.start]);
527 new_source.push_str(&edit.replacement);
528 new_source.push_str(&self.source[edit.range.end..]);
529
530 let old_ids: Vec<NodeId> = self.nodes.iter().map(|n| n.id).collect();
531
532 let new_ast = Self::parse(&new_source)?;
533 let new_ids: Vec<NodeId> = new_ast.nodes.iter().map(|n| n.id).collect();
534
535 let removed: Vec<NodeId> = old_ids
536 .iter()
537 .filter(|id| id.index() >= new_ast.nodes.len())
538 .copied()
539 .collect();
540 let added: Vec<NodeId> = new_ids
541 .iter()
542 .filter(|id| id.index() >= self.nodes.len())
543 .copied()
544 .collect();
545
546 let changed: Vec<NodeId> = new_ids
548 .iter()
549 .filter(|id| {
550 id.index() < self.nodes.len()
551 && (self.nodes[id.index()].start != new_ast.nodes[id.index()].start
552 || self.nodes[id.index()].end != new_ast.nodes[id.index()].end)
553 })
554 .copied()
555 .collect();
556
557 *self = new_ast;
558
559 Ok(EditResult {
560 changed_nodes: changed,
561 removed_nodes: removed,
562 added_nodes: added,
563 })
564 }
565}
566
567struct ArenaBuilder {
570 nodes: Vec<ArenaNode>,
571}
572
573impl ArenaBuilder {
574 fn alloc(&mut self, parent: Option<NodeId>, kind: NodeKind, start: u32, end: u32) -> NodeId {
575 let id = NodeId::new(self.nodes.len());
576 self.nodes.push(ArenaNode {
577 id,
578 parent,
579 kind,
580 start,
581 end,
582 children: SmallVec::new(),
583 });
584 if let Some(parent_id) = parent {
585 self.nodes[parent_id.index()].children.push(id);
586 }
587 id
588 }
589
590 fn build_root(&mut self, root: &modern::Root) {
591 let root_start = root
592 .fragment
593 .nodes
594 .first()
595 .map(|n| n.start())
596 .unwrap_or(0) as u32;
597 let root_end = root
598 .fragment
599 .nodes
600 .last()
601 .map(|n| n.end())
602 .unwrap_or(0) as u32;
603
604 let root_id = self.alloc(None, NodeKind::Root, root_start, root_end);
605
606 if let Some(script) = &root.instance {
608 self.build_script(root_id, script, ScriptContext::Default);
609 }
610
611 if let Some(script) = &root.module {
613 self.build_script(root_id, script, ScriptContext::Module);
614 }
615
616 self.build_fragment(root_id, &root.fragment);
618
619 if let Some(css) = &root.css {
621 self.alloc(
622 Some(root_id),
623 NodeKind::StyleSheet,
624 css.start as u32,
625 css.end as u32,
626 );
627 }
628 }
629
630 fn build_script(&mut self, parent: NodeId, script: &modern::Script, context: ScriptContext) {
631 self.alloc(
632 Some(parent),
633 NodeKind::Script {
634 context,
635 program: script.content.clone(),
636 },
637 script.start as u32,
638 script.end as u32,
639 );
640 }
641
642 fn build_fragment(&mut self, parent: NodeId, fragment: &modern::Fragment) {
643 for node in fragment.nodes.iter() {
644 self.build_node(parent, node);
645 }
646 }
647
648 fn build_node(&mut self, parent: NodeId, node: &ModernNode) {
649 match node {
650 ModernNode::Text(text) => {
651 self.alloc(
652 Some(parent),
653 NodeKind::Text { data: text.data.clone() },
654 text.start as u32,
655 text.end as u32,
656 );
657 }
658 ModernNode::Comment(comment) => {
659 self.alloc(
660 Some(parent),
661 NodeKind::Comment { data: comment.data.clone() },
662 comment.start as u32,
663 comment.end as u32,
664 );
665 }
666 ModernNode::ExpressionTag(tag) => {
667 let id = self.alloc(
668 Some(parent),
669 NodeKind::ExpressionTag,
670 tag.start as u32,
671 tag.end as u32,
672 );
673 self.build_expression(id, &tag.expression);
674 }
675 ModernNode::HtmlTag(tag) => {
676 let id = self.alloc(
677 Some(parent),
678 NodeKind::HtmlTag,
679 tag.start as u32,
680 tag.end as u32,
681 );
682 self.build_expression(id, &tag.expression);
683 }
684 ModernNode::ConstTag(tag) => {
685 let id = self.alloc(
686 Some(parent),
687 NodeKind::ConstTag,
688 tag.start as u32,
689 tag.end as u32,
690 );
691 self.build_expression(id, &tag.declaration);
692 }
693 ModernNode::DebugTag(tag) => {
694 self.alloc(
695 Some(parent),
696 NodeKind::DebugTag,
697 tag.start as u32,
698 tag.end as u32,
699 );
700 }
701 ModernNode::RenderTag(tag) => {
702 let id = self.alloc(
703 Some(parent),
704 NodeKind::RenderTag,
705 tag.start as u32,
706 tag.end as u32,
707 );
708 self.build_expression(id, &tag.expression);
709 }
710 ModernNode::IfBlock(block) => {
711 self.build_if_block(parent, block);
712 }
713 ModernNode::EachBlock(block) => {
714 let id = self.alloc(
715 Some(parent),
716 NodeKind::EachBlock,
717 block.start as u32,
718 block.end as u32,
719 );
720 self.build_expression(id, &block.expression);
721 if let Some(ctx) = &block.context {
722 self.build_expression(id, ctx);
723 }
724 if let Some(key) = &block.key {
725 self.build_expression(id, key);
726 }
727 self.build_fragment(id, &block.body);
728 if let Some(fallback) = &block.fallback {
729 self.build_fragment(id, fallback);
730 }
731 }
732 ModernNode::AwaitBlock(block) => {
733 let id = self.alloc(
734 Some(parent),
735 NodeKind::AwaitBlock,
736 block.start as u32,
737 block.end as u32,
738 );
739 self.build_expression(id, &block.expression);
740 if let Some(val) = &block.value {
741 self.build_expression(id, val);
742 }
743 if let Some(err) = &block.error {
744 self.build_expression(id, err);
745 }
746 for f in [&block.pending, &block.then, &block.catch].into_iter().flatten() {
747 self.build_fragment(id, f);
748 }
749 }
750 ModernNode::KeyBlock(block) => {
751 let id = self.alloc(
752 Some(parent),
753 NodeKind::KeyBlock,
754 block.start as u32,
755 block.end as u32,
756 );
757 self.build_expression(id, &block.expression);
758 self.build_fragment(id, &block.fragment);
759 }
760 ModernNode::SnippetBlock(block) => {
761 let name = block
762 .expression
763 .identifier_name()
764 .unwrap_or_else(|| Arc::from(""));
765 let id = self.alloc(
766 Some(parent),
767 NodeKind::SnippetBlock { name },
768 block.start as u32,
769 block.end as u32,
770 );
771 self.build_expression(id, &block.expression);
772 for param in block.parameters.iter() {
773 self.build_expression(id, param);
774 }
775 self.build_fragment(id, &block.body);
776 }
777 ModernNode::RegularElement(el) => {
779 self.build_element(parent, NodeKind::RegularElement { name: el.name.clone() }, el);
780 }
781 ModernNode::Component(el) => {
782 self.build_element(parent, NodeKind::Component { name: el.name.clone() }, el);
783 }
784 ModernNode::SlotElement(el) => {
785 self.build_element(parent, NodeKind::SlotElement { name: el.name.clone() }, el);
786 }
787 ModernNode::SvelteHead(el) => self.build_element(parent, NodeKind::SvelteHead, el),
788 ModernNode::SvelteBody(el) => self.build_element(parent, NodeKind::SvelteBody, el),
789 ModernNode::SvelteWindow(el) => self.build_element(parent, NodeKind::SvelteWindow, el),
790 ModernNode::SvelteDocument(el) => {
791 self.build_element(parent, NodeKind::SvelteDocument, el);
792 }
793 ModernNode::SvelteComponent(el) => {
794 self.build_element(parent, NodeKind::SvelteComponent, el);
795 }
796 ModernNode::SvelteElement(el) => {
797 self.build_element(parent, NodeKind::SvelteElement, el);
798 }
799 ModernNode::SvelteSelf(el) => self.build_element(parent, NodeKind::SvelteSelf, el),
800 ModernNode::SvelteFragment(el) => {
801 self.build_element(parent, NodeKind::SvelteFragment, el);
802 }
803 ModernNode::SvelteBoundary(el) => {
804 self.build_element(parent, NodeKind::SvelteBoundary, el);
805 }
806 ModernNode::TitleElement(el) => {
807 self.build_element(parent, NodeKind::TitleElement, el);
808 }
809 }
810 }
811
812 fn build_if_block(&mut self, parent: NodeId, block: &modern::IfBlock) {
813 let id = self.alloc(
814 Some(parent),
815 NodeKind::IfBlock { elseif: block.elseif },
816 block.start as u32,
817 block.end as u32,
818 );
819 self.build_expression(id, &block.test);
820 self.build_fragment(id, &block.consequent);
821 if let Some(alt) = &block.alternate {
822 match alt.as_ref() {
823 modern::Alternate::Fragment(f) => {
824 let alt_start = f.nodes.first().map(|n| n.start()).unwrap_or(0) as u32;
825 let alt_end = f.nodes.last().map(|n| n.end()).unwrap_or(0) as u32;
826 let alt_id = self.alloc(Some(id), NodeKind::Alternate, alt_start, alt_end);
827 self.build_fragment(alt_id, f);
828 }
829 modern::Alternate::IfBlock(nested) => {
830 self.build_if_block(id, nested);
831 }
832 }
833 }
834 }
835
836 fn build_element<E: modern::Element>(&mut self, parent: NodeId, kind: NodeKind, el: &E) {
837 let id = self.alloc(Some(parent), kind, el.start() as u32, el.end() as u32);
838 for attr in el.attributes() {
839 self.build_attribute(id, attr);
840 }
841 self.build_fragment(id, el.fragment());
842 }
843
844 fn build_attribute(&mut self, parent: NodeId, attr: &modern::Attribute) {
845 match attr {
846 modern::Attribute::Attribute(a) => {
847 let id = self.alloc(
848 Some(parent),
849 NodeKind::Attribute { name: a.name.clone() },
850 a.start as u32,
851 a.end as u32,
852 );
853 self.build_attribute_values(id, &a.value);
854 }
855 modern::Attribute::SpreadAttribute(s) => {
856 let id = self.alloc(
857 Some(parent),
858 NodeKind::Attribute { name: Arc::from("...") },
859 s.start as u32,
860 s.end as u32,
861 );
862 self.build_expression(id, &s.expression);
863 }
864 modern::Attribute::BindDirective(d)
865 | modern::Attribute::OnDirective(d)
866 | modern::Attribute::ClassDirective(d)
867 | modern::Attribute::LetDirective(d)
868 | modern::Attribute::AnimateDirective(d)
869 | modern::Attribute::UseDirective(d) => {
870 let id = self.alloc(
871 Some(parent),
872 NodeKind::Attribute { name: d.name.clone() },
873 d.start as u32,
874 d.end as u32,
875 );
876 self.build_expression(id, &d.expression);
877 }
878 modern::Attribute::StyleDirective(d) => {
879 let id = self.alloc(
880 Some(parent),
881 NodeKind::Attribute { name: d.name.clone() },
882 d.start as u32,
883 d.end as u32,
884 );
885 self.build_attribute_values(id, &d.value);
886 }
887 modern::Attribute::TransitionDirective(d) => {
888 let id = self.alloc(
889 Some(parent),
890 NodeKind::Attribute { name: d.name.clone() },
891 d.start as u32,
892 d.end as u32,
893 );
894 self.build_expression(id, &d.expression);
895 }
896 modern::Attribute::AttachTag(a) => {
897 let id = self.alloc(
898 Some(parent),
899 NodeKind::Attribute { name: Arc::from("@attach") },
900 a.start as u32,
901 a.end as u32,
902 );
903 self.build_expression(id, &a.expression);
904 }
905 }
906 }
907
908 fn build_attribute_values(&mut self, parent: NodeId, value: &modern::AttributeValueKind) {
909 match value {
910 modern::AttributeValueKind::Boolean(_) => {}
911 modern::AttributeValueKind::ExpressionTag(tag) => {
912 self.build_expression(parent, &tag.expression);
913 }
914 modern::AttributeValueKind::Values(values) => {
915 for val in values.iter() {
916 match val {
917 modern::AttributeValue::ExpressionTag(tag) => {
918 self.build_expression(parent, &tag.expression);
919 }
920 modern::AttributeValue::Text(_) => {}
921 }
922 }
923 }
924 }
925 }
926
927 fn build_expression(&mut self, parent: NodeId, expr: &modern::Expression) {
928 let handle = match &expr.node {
929 Some(modern::JsNodeHandle::Expression(arc)) => Some(arc.clone()),
930 _ => None,
931 };
932
933 self.alloc(
934 Some(parent),
935 NodeKind::Expression { handle },
936 expr.start as u32,
937 expr.end as u32,
938 );
939 }
940}
941
942#[cfg(test)]
943mod tests {
944 use super::*;
945
946 #[test]
947 fn parse_simple_element() {
948 let ast = SvelteAst::parse("<div>hello</div>").unwrap();
949 assert!(!ast.is_empty());
950 assert!(matches!(ast.kind(ast.root()), NodeKind::Root));
951
952 let children = ast.children(ast.root());
953 assert!(!children.is_empty());
954 }
955
956 #[test]
957 fn parse_with_expression() {
958 let ast = SvelteAst::parse("<p>{count}</p>").unwrap();
959 let root = ast.root();
960
961 let expr_tag = ast
963 .descendants(root)
964 .find(|&id| matches!(ast.kind(id), NodeKind::ExpressionTag));
965 assert!(expr_tag.is_some(), "should find ExpressionTag");
966 }
967
968 #[test]
969 fn parent_pointers_work() {
970 let ast = SvelteAst::parse("<div><span>hi</span></div>").unwrap();
971 let root = ast.root();
972
973 assert!(ast.parent(root).is_none());
975
976 for &id in ast.children(root) {
978 assert_eq!(ast.parent(id), Some(root));
979 }
980 }
981
982 #[test]
983 fn ancestors_traverse_upward() {
984 let ast = SvelteAst::parse("<div><span>hi</span></div>").unwrap();
985 let root = ast.root();
986
987 let text = ast
989 .descendants(root)
990 .find(|&id| matches!(ast.kind(id), NodeKind::Text { .. }));
991 assert!(text.is_some());
992
993 let ancestors: Vec<_> = ast.ancestors(text.unwrap()).collect();
994 assert!(ancestors.len() >= 2); assert!(ancestors.contains(&root));
996 }
997
998 #[test]
999 fn innermost_at_offset_finds_deepest() {
1000 let ast = SvelteAst::parse("<div>hello</div>").unwrap();
1001 let node = ast.innermost_at_offset(6);
1002 assert!(node.is_some());
1003 assert!(matches!(ast.kind(node.unwrap()), NodeKind::Text { .. }));
1004 }
1005
1006 #[test]
1007 fn node_text_returns_source_slice() {
1008 let ast = SvelteAst::parse("<div>hello</div>").unwrap();
1009 let text_node = ast
1010 .descendants(ast.root())
1011 .find(|&id| matches!(ast.kind(id), NodeKind::Text { .. }))
1012 .unwrap();
1013 assert_eq!(ast.node_text(text_node), "hello");
1014 }
1015
1016 #[test]
1017 fn edit_updates_ast() {
1018 let mut ast = SvelteAst::parse("<p>hello</p>").unwrap();
1019 let result = ast.edit(TextEdit {
1020 range: 3..8,
1021 replacement: "world".to_string(),
1022 });
1023 assert!(result.is_ok());
1024 assert_eq!(ast.source(), "<p>world</p>");
1025 }
1026
1027 #[test]
1028 fn siblings_excludes_self() {
1029 let ast = SvelteAst::parse("<div><a/><b/><c/></div>").unwrap();
1030 let div = ast.children(ast.root())[0];
1031 let div_children = ast.children(div);
1032
1033 if div_children.len() >= 2 {
1034 let first = div_children[0];
1035 let sibs: Vec<_> = ast.siblings(first).collect();
1036 assert!(!sibs.contains(&first));
1037 assert!(!sibs.is_empty());
1038 }
1039 }
1040
1041 #[test]
1042 fn if_block_structure() {
1043 let ast = SvelteAst::parse("{#if condition}<p>yes</p>{:else}<p>no</p>{/if}").unwrap();
1044 let if_block = ast
1045 .descendants(ast.root())
1046 .find(|&id| matches!(ast.kind(id), NodeKind::IfBlock { .. }));
1047 assert!(if_block.is_some(), "should find IfBlock");
1048
1049 let if_id = if_block.unwrap();
1050 assert!(ast.is_block(if_id));
1051 assert!(!ast.is_element(if_id));
1052
1053 let children = ast.children(if_id);
1055 assert!(children.len() >= 2, "IfBlock should have children: {:?}", children.len());
1056
1057 let has_expr = children.iter().any(|&id| matches!(ast.kind(id), NodeKind::Expression { .. }));
1059 assert!(has_expr, "IfBlock should have an Expression child for the test");
1060 }
1061
1062 #[test]
1063 fn each_block_structure() {
1064 let ast = SvelteAst::parse("{#each items as item}<p>{item}</p>{/each}").unwrap();
1065 let each = ast
1066 .descendants(ast.root())
1067 .find(|&id| matches!(ast.kind(id), NodeKind::EachBlock));
1068 assert!(each.is_some(), "should find EachBlock");
1069 assert!(ast.is_block(each.unwrap()));
1070 }
1071
1072 #[test]
1073 fn script_contains_program() {
1074 let ast = SvelteAst::parse("<script>let count = 0;</script><p>{count}</p>").unwrap();
1075 let script = ast
1076 .descendants(ast.root())
1077 .find(|&id| matches!(ast.kind(id), NodeKind::Script { .. }));
1078 assert!(script.is_some(), "should find Script");
1079
1080 let prog = ast.js_program(script.unwrap());
1081 assert!(prog.is_some(), "Script should have a JS program");
1082 assert_eq!(prog.unwrap().program().body.len(), 1);
1083 }
1084
1085 #[test]
1086 fn expression_tag_has_js_handle() {
1087 let ast = SvelteAst::parse("<p>{count + 1}</p>").unwrap();
1088 let expr = ast
1089 .descendants(ast.root())
1090 .find(|&id| matches!(ast.kind(id), NodeKind::Expression { .. }));
1091 assert!(expr.is_some(), "should find Expression node");
1092
1093 let js = ast.js_expression(expr.unwrap());
1094 assert!(js.is_some(), "Expression should have OXC handle");
1095 }
1096
1097 #[test]
1098 fn attribute_expressions_are_traversed() {
1099 let ast = SvelteAst::parse("<button on:click={handler}>go</button>").unwrap();
1100 let attr = ast
1101 .descendants(ast.root())
1102 .find(|&id| matches!(ast.kind(id), NodeKind::Attribute { .. }));
1103 assert!(attr.is_some(), "should find Attribute");
1104
1105 let attr_children = ast.children(attr.unwrap());
1107 let has_expr = attr_children.iter().any(|&id| matches!(ast.kind(id), NodeKind::Expression { .. }));
1108 assert!(has_expr, "Directive attribute should have Expression child");
1109 }
1110
1111 #[test]
1112 fn spread_attribute_has_expression() {
1113 let ast = SvelteAst::parse("<div {...props}>hi</div>").unwrap();
1114 let spread = ast
1115 .descendants(ast.root())
1116 .find(|&id| {
1117 matches!(ast.kind(id), NodeKind::Attribute { name } if name.as_ref() == "...")
1118 });
1119 assert!(spread.is_some(), "should find spread attribute");
1120
1121 let children = ast.children(spread.unwrap());
1122 let has_expr = children.iter().any(|&id| matches!(ast.kind(id), NodeKind::Expression { .. }));
1123 assert!(has_expr, "Spread attribute should have Expression child");
1124 }
1125
1126 #[test]
1127 fn nodes_in_range_finds_overlapping() {
1128 let ast = SvelteAst::parse("<div><p>hello</p><span>world</span></div>").unwrap();
1129 let nodes = ast.nodes_in_range(8, 13);
1131 assert!(!nodes.is_empty(), "should find nodes in range");
1132 }
1133
1134 #[test]
1135 fn is_element_and_is_block_classify_correctly() {
1136 let ast = SvelteAst::parse("<div>{#if x}<span/>{/if}</div>").unwrap();
1137 for id in ast.descendants(ast.root()) {
1138 match ast.kind(id) {
1139 NodeKind::RegularElement { .. } => assert!(ast.is_element(id)),
1140 NodeKind::IfBlock { .. } => {
1141 assert!(ast.is_block(id));
1142 assert!(!ast.is_element(id));
1143 }
1144 _ => {}
1145 }
1146 }
1147 }
1148
1149 #[test]
1150 fn complex_template() {
1151 let src = r#"<script>
1152 let items = [1, 2, 3];
1153 let show = true;
1154</script>
1155
1156{#if show}
1157 {#each items as item}
1158 <p>{item}</p>
1159 {/each}
1160{/if}
1161"#;
1162 let ast = SvelteAst::parse(src).unwrap();
1163 assert!(ast.len() > 10, "complex template should have many nodes");
1164
1165 let descendants = || ast.descendants(ast.root());
1167 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::Script { .. })), "should have Script");
1168 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::IfBlock { .. })), "should have IfBlock");
1169 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::EachBlock)), "should have EachBlock");
1170 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::ExpressionTag)), "should have ExpressionTag");
1171 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::RegularElement { .. })), "should have RegularElement");
1172 }
1173
1174 #[test]
1175 fn element_name_returns_tag_name() {
1176 let ast = SvelteAst::parse("<div><MyComponent/></div>").unwrap();
1177 let div = ast
1178 .descendants(ast.root())
1179 .find(|&id| matches!(ast.kind(id), NodeKind::RegularElement { name } if name.as_ref() == "div"))
1180 .unwrap();
1181 assert_eq!(ast.element_name(div), Some("div"));
1182
1183 let comp = ast
1184 .descendants(ast.root())
1185 .find(|&id| matches!(ast.kind(id), NodeKind::Component { .. }))
1186 .unwrap();
1187 assert_eq!(ast.element_name(comp), Some("MyComponent"));
1188
1189 assert_eq!(ast.element_name(ast.root()), None);
1191 }
1192
1193 #[test]
1194 fn next_and_prev_sibling() {
1195 let ast = SvelteAst::parse("<div><a/><b/><c/></div>").unwrap();
1196 let div = ast.children(ast.root())[0];
1197 let children = ast.children(div);
1198 assert!(children.len() >= 3);
1199
1200 let a = children[0];
1201 let b = children[1];
1202 let c = children[2];
1203
1204 assert_eq!(ast.next_sibling(a), Some(b));
1205 assert_eq!(ast.next_sibling(b), Some(c));
1206 assert_eq!(ast.next_sibling(c), None);
1207
1208 assert_eq!(ast.prev_sibling(a), None);
1209 assert_eq!(ast.prev_sibling(b), Some(a));
1210 assert_eq!(ast.prev_sibling(c), Some(b));
1211 }
1212
1213 #[test]
1214 fn depth_counts_ancestors() {
1215 let ast = SvelteAst::parse("<div><span>hi</span></div>").unwrap();
1216 assert_eq!(ast.depth(ast.root()), 0);
1217
1218 let text = ast
1219 .descendants(ast.root())
1220 .find(|&id| matches!(ast.kind(id), NodeKind::Text { .. }))
1221 .unwrap();
1222 assert!(ast.depth(text) >= 2);
1223 }
1224
1225 #[test]
1226 fn node_kind_display() {
1227 let ast = SvelteAst::parse("<div class='x'>hi</div>").unwrap();
1228 let div = ast
1229 .descendants(ast.root())
1230 .find(|&id| matches!(ast.kind(id), NodeKind::RegularElement { .. }))
1231 .unwrap();
1232 assert_eq!(format!("{}", ast.kind(div)), "RegularElement <div>");
1233
1234 let attr = ast
1235 .descendants(ast.root())
1236 .find(|&id| matches!(ast.kind(id), NodeKind::Attribute { .. }))
1237 .unwrap();
1238 assert_eq!(format!("{}", ast.kind(attr)), "Attribute class");
1239 }
1240
1241 #[test]
1242 fn node_kind_name() {
1243 assert_eq!(NodeKind::Root.name(), "Root");
1244 assert_eq!(NodeKind::ExpressionTag.name(), "ExpressionTag");
1245 assert_eq!(NodeKind::EachBlock.name(), "EachBlock");
1246 }
1247}