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::{ParsedJsExpression, ParsedJsProgram};
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<ParsedJsProgram> },
141 StyleSheet,
142 Expression { handle: Option<Arc<ParsedJsExpression>> },
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<&ParsedJsExpression> {
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<&ParsedJsProgram> {
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 fragment in [&block.pending, &block.then, &block.catch] {
747 if let Some(f) = fragment {
748 self.build_fragment(id, f);
749 }
750 }
751 }
752 ModernNode::KeyBlock(block) => {
753 let id = self.alloc(
754 Some(parent),
755 NodeKind::KeyBlock,
756 block.start as u32,
757 block.end as u32,
758 );
759 self.build_expression(id, &block.expression);
760 self.build_fragment(id, &block.fragment);
761 }
762 ModernNode::SnippetBlock(block) => {
763 let name = block
764 .expression
765 .identifier_name()
766 .unwrap_or_else(|| Arc::from(""));
767 let id = self.alloc(
768 Some(parent),
769 NodeKind::SnippetBlock { name },
770 block.start as u32,
771 block.end as u32,
772 );
773 self.build_expression(id, &block.expression);
774 for param in block.parameters.iter() {
775 self.build_expression(id, param);
776 }
777 self.build_fragment(id, &block.body);
778 }
779 ModernNode::RegularElement(el) => {
781 self.build_element(parent, NodeKind::RegularElement { name: el.name.clone() }, el);
782 }
783 ModernNode::Component(el) => {
784 self.build_element(parent, NodeKind::Component { name: el.name.clone() }, el);
785 }
786 ModernNode::SlotElement(el) => {
787 self.build_element(parent, NodeKind::SlotElement { name: el.name.clone() }, el);
788 }
789 ModernNode::SvelteHead(el) => self.build_element(parent, NodeKind::SvelteHead, el),
790 ModernNode::SvelteBody(el) => self.build_element(parent, NodeKind::SvelteBody, el),
791 ModernNode::SvelteWindow(el) => self.build_element(parent, NodeKind::SvelteWindow, el),
792 ModernNode::SvelteDocument(el) => {
793 self.build_element(parent, NodeKind::SvelteDocument, el);
794 }
795 ModernNode::SvelteComponent(el) => {
796 self.build_element(parent, NodeKind::SvelteComponent, el);
797 }
798 ModernNode::SvelteElement(el) => {
799 self.build_element(parent, NodeKind::SvelteElement, el);
800 }
801 ModernNode::SvelteSelf(el) => self.build_element(parent, NodeKind::SvelteSelf, el),
802 ModernNode::SvelteFragment(el) => {
803 self.build_element(parent, NodeKind::SvelteFragment, el);
804 }
805 ModernNode::SvelteBoundary(el) => {
806 self.build_element(parent, NodeKind::SvelteBoundary, el);
807 }
808 ModernNode::TitleElement(el) => {
809 self.build_element(parent, NodeKind::TitleElement, el);
810 }
811 }
812 }
813
814 fn build_if_block(&mut self, parent: NodeId, block: &modern::IfBlock) {
815 let id = self.alloc(
816 Some(parent),
817 NodeKind::IfBlock { elseif: block.elseif },
818 block.start as u32,
819 block.end as u32,
820 );
821 self.build_expression(id, &block.test);
822 self.build_fragment(id, &block.consequent);
823 if let Some(alt) = &block.alternate {
824 match alt.as_ref() {
825 modern::Alternate::Fragment(f) => {
826 let alt_start = f.nodes.first().map(|n| n.start()).unwrap_or(0) as u32;
827 let alt_end = f.nodes.last().map(|n| n.end()).unwrap_or(0) as u32;
828 let alt_id = self.alloc(Some(id), NodeKind::Alternate, alt_start, alt_end);
829 self.build_fragment(alt_id, f);
830 }
831 modern::Alternate::IfBlock(nested) => {
832 self.build_if_block(id, nested);
833 }
834 }
835 }
836 }
837
838 fn build_element<E: modern::Element>(&mut self, parent: NodeId, kind: NodeKind, el: &E) {
839 let id = self.alloc(Some(parent), kind, el.start() as u32, el.end() as u32);
840 for attr in el.attributes() {
841 self.build_attribute(id, attr);
842 }
843 self.build_fragment(id, el.fragment());
844 }
845
846 fn build_attribute(&mut self, parent: NodeId, attr: &modern::Attribute) {
847 match attr {
848 modern::Attribute::Attribute(a) => {
849 let id = self.alloc(
850 Some(parent),
851 NodeKind::Attribute { name: a.name.clone() },
852 a.start as u32,
853 a.end as u32,
854 );
855 self.build_attribute_values(id, &a.value);
856 }
857 modern::Attribute::SpreadAttribute(s) => {
858 let id = self.alloc(
859 Some(parent),
860 NodeKind::Attribute { name: Arc::from("...") },
861 s.start as u32,
862 s.end as u32,
863 );
864 self.build_expression(id, &s.expression);
865 }
866 modern::Attribute::BindDirective(d)
867 | modern::Attribute::OnDirective(d)
868 | modern::Attribute::ClassDirective(d)
869 | modern::Attribute::LetDirective(d)
870 | modern::Attribute::AnimateDirective(d)
871 | modern::Attribute::UseDirective(d) => {
872 let id = self.alloc(
873 Some(parent),
874 NodeKind::Attribute { name: d.name.clone() },
875 d.start as u32,
876 d.end as u32,
877 );
878 self.build_expression(id, &d.expression);
879 }
880 modern::Attribute::StyleDirective(d) => {
881 let id = self.alloc(
882 Some(parent),
883 NodeKind::Attribute { name: d.name.clone() },
884 d.start as u32,
885 d.end as u32,
886 );
887 self.build_attribute_values(id, &d.value);
888 }
889 modern::Attribute::TransitionDirective(d) => {
890 let id = self.alloc(
891 Some(parent),
892 NodeKind::Attribute { name: d.name.clone() },
893 d.start as u32,
894 d.end as u32,
895 );
896 self.build_expression(id, &d.expression);
897 }
898 modern::Attribute::AttachTag(a) => {
899 let id = self.alloc(
900 Some(parent),
901 NodeKind::Attribute { name: Arc::from("@attach") },
902 a.start as u32,
903 a.end as u32,
904 );
905 self.build_expression(id, &a.expression);
906 }
907 }
908 }
909
910 fn build_attribute_values(&mut self, parent: NodeId, value: &modern::AttributeValueList) {
911 match value {
912 modern::AttributeValueList::Boolean(_) => {}
913 modern::AttributeValueList::ExpressionTag(tag) => {
914 self.build_expression(parent, &tag.expression);
915 }
916 modern::AttributeValueList::Values(values) => {
917 for val in values.iter() {
918 match val {
919 modern::AttributeValue::ExpressionTag(tag) => {
920 self.build_expression(parent, &tag.expression);
921 }
922 modern::AttributeValue::Text(_) => {}
923 }
924 }
925 }
926 }
927 }
928
929 fn build_expression(&mut self, parent: NodeId, expr: &modern::Expression) {
930 let handle = match &expr.node {
931 Some(modern::JsNodeHandle::Expression(arc)) => Some(arc.clone()),
932 _ => None,
933 };
934
935 self.alloc(
936 Some(parent),
937 NodeKind::Expression { handle },
938 expr.start as u32,
939 expr.end as u32,
940 );
941 }
942}
943
944#[cfg(test)]
945mod tests {
946 use super::*;
947
948 #[test]
949 fn parse_simple_element() {
950 let ast = SvelteAst::parse("<div>hello</div>").unwrap();
951 assert!(!ast.is_empty());
952 assert!(matches!(ast.kind(ast.root()), NodeKind::Root));
953
954 let children = ast.children(ast.root());
955 assert!(!children.is_empty());
956 }
957
958 #[test]
959 fn parse_with_expression() {
960 let ast = SvelteAst::parse("<p>{count}</p>").unwrap();
961 let root = ast.root();
962
963 let expr_tag = ast
965 .descendants(root)
966 .find(|&id| matches!(ast.kind(id), NodeKind::ExpressionTag));
967 assert!(expr_tag.is_some(), "should find ExpressionTag");
968 }
969
970 #[test]
971 fn parent_pointers_work() {
972 let ast = SvelteAst::parse("<div><span>hi</span></div>").unwrap();
973 let root = ast.root();
974
975 assert!(ast.parent(root).is_none());
977
978 for &id in ast.children(root) {
980 assert_eq!(ast.parent(id), Some(root));
981 }
982 }
983
984 #[test]
985 fn ancestors_traverse_upward() {
986 let ast = SvelteAst::parse("<div><span>hi</span></div>").unwrap();
987 let root = ast.root();
988
989 let text = ast
991 .descendants(root)
992 .find(|&id| matches!(ast.kind(id), NodeKind::Text { .. }));
993 assert!(text.is_some());
994
995 let ancestors: Vec<_> = ast.ancestors(text.unwrap()).collect();
996 assert!(ancestors.len() >= 2); assert!(ancestors.contains(&root));
998 }
999
1000 #[test]
1001 fn innermost_at_offset_finds_deepest() {
1002 let ast = SvelteAst::parse("<div>hello</div>").unwrap();
1003 let node = ast.innermost_at_offset(6);
1004 assert!(node.is_some());
1005 assert!(matches!(ast.kind(node.unwrap()), NodeKind::Text { .. }));
1006 }
1007
1008 #[test]
1009 fn node_text_returns_source_slice() {
1010 let ast = SvelteAst::parse("<div>hello</div>").unwrap();
1011 let text_node = ast
1012 .descendants(ast.root())
1013 .find(|&id| matches!(ast.kind(id), NodeKind::Text { .. }))
1014 .unwrap();
1015 assert_eq!(ast.node_text(text_node), "hello");
1016 }
1017
1018 #[test]
1019 fn edit_updates_ast() {
1020 let mut ast = SvelteAst::parse("<p>hello</p>").unwrap();
1021 let result = ast.edit(TextEdit {
1022 range: 3..8,
1023 replacement: "world".to_string(),
1024 });
1025 assert!(result.is_ok());
1026 assert_eq!(ast.source(), "<p>world</p>");
1027 }
1028
1029 #[test]
1030 fn siblings_excludes_self() {
1031 let ast = SvelteAst::parse("<div><a/><b/><c/></div>").unwrap();
1032 let div = ast.children(ast.root())[0];
1033 let div_children = ast.children(div);
1034
1035 if div_children.len() >= 2 {
1036 let first = div_children[0];
1037 let sibs: Vec<_> = ast.siblings(first).collect();
1038 assert!(!sibs.contains(&first));
1039 assert!(!sibs.is_empty());
1040 }
1041 }
1042
1043 #[test]
1044 fn if_block_structure() {
1045 let ast = SvelteAst::parse("{#if condition}<p>yes</p>{:else}<p>no</p>{/if}").unwrap();
1046 let if_block = ast
1047 .descendants(ast.root())
1048 .find(|&id| matches!(ast.kind(id), NodeKind::IfBlock { .. }));
1049 assert!(if_block.is_some(), "should find IfBlock");
1050
1051 let if_id = if_block.unwrap();
1052 assert!(ast.is_block(if_id));
1053 assert!(!ast.is_element(if_id));
1054
1055 let children = ast.children(if_id);
1057 assert!(children.len() >= 2, "IfBlock should have children: {:?}", children.len());
1058
1059 let has_expr = children.iter().any(|&id| matches!(ast.kind(id), NodeKind::Expression { .. }));
1061 assert!(has_expr, "IfBlock should have an Expression child for the test");
1062 }
1063
1064 #[test]
1065 fn each_block_structure() {
1066 let ast = SvelteAst::parse("{#each items as item}<p>{item}</p>{/each}").unwrap();
1067 let each = ast
1068 .descendants(ast.root())
1069 .find(|&id| matches!(ast.kind(id), NodeKind::EachBlock));
1070 assert!(each.is_some(), "should find EachBlock");
1071 assert!(ast.is_block(each.unwrap()));
1072 }
1073
1074 #[test]
1075 fn script_contains_program() {
1076 let ast = SvelteAst::parse("<script>let count = 0;</script><p>{count}</p>").unwrap();
1077 let script = ast
1078 .descendants(ast.root())
1079 .find(|&id| matches!(ast.kind(id), NodeKind::Script { .. }));
1080 assert!(script.is_some(), "should find Script");
1081
1082 let prog = ast.js_program(script.unwrap());
1083 assert!(prog.is_some(), "Script should have a JS program");
1084 assert_eq!(prog.unwrap().program().body.len(), 1);
1085 }
1086
1087 #[test]
1088 fn expression_tag_has_js_handle() {
1089 let ast = SvelteAst::parse("<p>{count + 1}</p>").unwrap();
1090 let expr = ast
1091 .descendants(ast.root())
1092 .find(|&id| matches!(ast.kind(id), NodeKind::Expression { .. }));
1093 assert!(expr.is_some(), "should find Expression node");
1094
1095 let js = ast.js_expression(expr.unwrap());
1096 assert!(js.is_some(), "Expression should have OXC handle");
1097 }
1098
1099 #[test]
1100 fn attribute_expressions_are_traversed() {
1101 let ast = SvelteAst::parse("<button on:click={handler}>go</button>").unwrap();
1102 let attr = ast
1103 .descendants(ast.root())
1104 .find(|&id| matches!(ast.kind(id), NodeKind::Attribute { .. }));
1105 assert!(attr.is_some(), "should find Attribute");
1106
1107 let attr_children = ast.children(attr.unwrap());
1109 let has_expr = attr_children.iter().any(|&id| matches!(ast.kind(id), NodeKind::Expression { .. }));
1110 assert!(has_expr, "Directive attribute should have Expression child");
1111 }
1112
1113 #[test]
1114 fn spread_attribute_has_expression() {
1115 let ast = SvelteAst::parse("<div {...props}>hi</div>").unwrap();
1116 let spread = ast
1117 .descendants(ast.root())
1118 .find(|&id| {
1119 matches!(ast.kind(id), NodeKind::Attribute { name } if name.as_ref() == "...")
1120 });
1121 assert!(spread.is_some(), "should find spread attribute");
1122
1123 let children = ast.children(spread.unwrap());
1124 let has_expr = children.iter().any(|&id| matches!(ast.kind(id), NodeKind::Expression { .. }));
1125 assert!(has_expr, "Spread attribute should have Expression child");
1126 }
1127
1128 #[test]
1129 fn nodes_in_range_finds_overlapping() {
1130 let ast = SvelteAst::parse("<div><p>hello</p><span>world</span></div>").unwrap();
1131 let nodes = ast.nodes_in_range(8, 13);
1133 assert!(!nodes.is_empty(), "should find nodes in range");
1134 }
1135
1136 #[test]
1137 fn is_element_and_is_block_classify_correctly() {
1138 let ast = SvelteAst::parse("<div>{#if x}<span/>{/if}</div>").unwrap();
1139 for id in ast.descendants(ast.root()) {
1140 match ast.kind(id) {
1141 NodeKind::RegularElement { .. } => assert!(ast.is_element(id)),
1142 NodeKind::IfBlock { .. } => {
1143 assert!(ast.is_block(id));
1144 assert!(!ast.is_element(id));
1145 }
1146 _ => {}
1147 }
1148 }
1149 }
1150
1151 #[test]
1152 fn complex_template() {
1153 let src = r#"<script>
1154 let items = [1, 2, 3];
1155 let show = true;
1156</script>
1157
1158{#if show}
1159 {#each items as item}
1160 <p>{item}</p>
1161 {/each}
1162{/if}
1163"#;
1164 let ast = SvelteAst::parse(src).unwrap();
1165 assert!(ast.len() > 10, "complex template should have many nodes");
1166
1167 let descendants = || ast.descendants(ast.root());
1169 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::Script { .. })), "should have Script");
1170 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::IfBlock { .. })), "should have IfBlock");
1171 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::EachBlock)), "should have EachBlock");
1172 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::ExpressionTag)), "should have ExpressionTag");
1173 assert!(descendants().any(|id| matches!(ast.kind(id), NodeKind::RegularElement { .. })), "should have RegularElement");
1174 }
1175
1176 #[test]
1177 fn element_name_returns_tag_name() {
1178 let ast = SvelteAst::parse("<div><MyComponent/></div>").unwrap();
1179 let div = ast
1180 .descendants(ast.root())
1181 .find(|&id| matches!(ast.kind(id), NodeKind::RegularElement { name } if name.as_ref() == "div"))
1182 .unwrap();
1183 assert_eq!(ast.element_name(div), Some("div"));
1184
1185 let comp = ast
1186 .descendants(ast.root())
1187 .find(|&id| matches!(ast.kind(id), NodeKind::Component { .. }))
1188 .unwrap();
1189 assert_eq!(ast.element_name(comp), Some("MyComponent"));
1190
1191 assert_eq!(ast.element_name(ast.root()), None);
1193 }
1194
1195 #[test]
1196 fn next_and_prev_sibling() {
1197 let ast = SvelteAst::parse("<div><a/><b/><c/></div>").unwrap();
1198 let div = ast.children(ast.root())[0];
1199 let children = ast.children(div);
1200 assert!(children.len() >= 3);
1201
1202 let a = children[0];
1203 let b = children[1];
1204 let c = children[2];
1205
1206 assert_eq!(ast.next_sibling(a), Some(b));
1207 assert_eq!(ast.next_sibling(b), Some(c));
1208 assert_eq!(ast.next_sibling(c), None);
1209
1210 assert_eq!(ast.prev_sibling(a), None);
1211 assert_eq!(ast.prev_sibling(b), Some(a));
1212 assert_eq!(ast.prev_sibling(c), Some(b));
1213 }
1214
1215 #[test]
1216 fn depth_counts_ancestors() {
1217 let ast = SvelteAst::parse("<div><span>hi</span></div>").unwrap();
1218 assert_eq!(ast.depth(ast.root()), 0);
1219
1220 let text = ast
1221 .descendants(ast.root())
1222 .find(|&id| matches!(ast.kind(id), NodeKind::Text { .. }))
1223 .unwrap();
1224 assert!(ast.depth(text) >= 2);
1225 }
1226
1227 #[test]
1228 fn node_kind_display() {
1229 let ast = SvelteAst::parse("<div class='x'>hi</div>").unwrap();
1230 let div = ast
1231 .descendants(ast.root())
1232 .find(|&id| matches!(ast.kind(id), NodeKind::RegularElement { .. }))
1233 .unwrap();
1234 assert_eq!(format!("{}", ast.kind(div)), "RegularElement <div>");
1235
1236 let attr = ast
1237 .descendants(ast.root())
1238 .find(|&id| matches!(ast.kind(id), NodeKind::Attribute { .. }))
1239 .unwrap();
1240 assert_eq!(format!("{}", ast.kind(attr)), "Attribute class");
1241 }
1242
1243 #[test]
1244 fn node_kind_name() {
1245 assert_eq!(NodeKind::Root.name(), "Root");
1246 assert_eq!(NodeKind::ExpressionTag.name(), "ExpressionTag");
1247 assert_eq!(NodeKind::EachBlock.name(), "EachBlock");
1248 }
1249}