Skip to main content

svelte_syntax/ast/
modern.rs

1use std::ops::ControlFlow;
2use std::sync::Arc;
3
4use oxc_ast::ast::{
5    BindingIdentifier, BindingPattern, Expression as OxcExpression, FormalParameter,
6    FormalParameterRest, Program as OxcProgram, Statement as OxcStatement,
7    VariableDeclaration as OxcVariableDeclaration,
8};
9use oxc_span::GetSpan;
10use serde::{Deserialize, Serialize, ser::SerializeMap};
11
12use crate::ast::common::Span;
13pub use crate::ast::common::{
14    AttrError, AttrErrorKind, AttributeValueSyntax, DirectiveValueSyntax,
15    FragmentType, LiteralValue, Loc, NameLocation, ParseError, Position, RootCommentType,
16    ScriptContext, ScriptType, SnippetHeaderError, SnippetHeaderErrorKind,
17};
18use crate::js::{ParsedJsExpression, ParsedJsParameters, ParsedJsPattern, ParsedJsProgram};
19
20fn empty_parsed_js_program() -> Arc<ParsedJsProgram> {
21    Arc::new(ParsedJsProgram::parse("", oxc_span::SourceType::mjs()))
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25pub enum RootType {
26    Root,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub struct Fragment {
31    pub r#type: FragmentType,
32    pub nodes: Box<[Node]>,
33}
34
35impl Fragment {
36    pub fn empty() -> Self {
37        Self {
38            r#type: FragmentType::Fragment,
39            nodes: Box::new([]),
40        }
41    }
42}
43
44impl Default for Fragment {
45    fn default() -> Self {
46        Self::empty()
47    }
48}
49
50pub enum Search<T> {
51    Continue,
52    Skip,
53    Found(T),
54}
55
56#[derive(Clone, Copy)]
57pub enum Entry<'a> {
58    Node(&'a Node),
59    IfBlock(&'a IfBlock),
60}
61
62impl<'a> Entry<'a> {
63    pub fn as_node(self) -> Option<&'a Node> {
64        match self {
65            Self::Node(node) => Some(node),
66            Self::IfBlock(_) => None,
67        }
68    }
69
70    pub fn as_if_block(self) -> Option<&'a IfBlock> {
71        match self {
72            Self::Node(Node::IfBlock(block)) | Self::IfBlock(block) => Some(block),
73            Self::Node(_) => None,
74        }
75    }
76}
77
78impl Fragment {
79    /// Depth-first walk over descendant template nodes and else-if branches.
80    pub fn walk<'a, T, S, E, L>(&'a self, state: &mut S, enter: E, leave: L) -> Option<T>
81    where
82        E: FnMut(Entry<'a>, &mut S) -> Search<T>,
83        L: FnMut(Entry<'a>, &mut S),
84    {
85        fn walk_fragment<'a, T, S, E, L>(
86            fragment: &'a Fragment,
87            state: &mut S,
88            enter: &mut E,
89            leave: &mut L,
90        ) -> Option<T>
91        where
92            E: FnMut(Entry<'a>, &mut S) -> Search<T>,
93            L: FnMut(Entry<'a>, &mut S),
94        {
95            for node in fragment.nodes.iter() {
96                if let Some(found) = walk_node(node, state, enter, leave) {
97                    return Some(found);
98                }
99            }
100            None
101        }
102
103        fn walk_entry<'a, T, S, E, L>(
104            entry: Entry<'a>,
105            state: &mut S,
106            enter: &mut E,
107            leave: &mut L,
108        ) -> Option<T>
109        where
110            E: FnMut(Entry<'a>, &mut S) -> Search<T>,
111            L: FnMut(Entry<'a>, &mut S),
112        {
113            match enter(entry, state) {
114                Search::Found(found) => return Some(found),
115                Search::Skip => {
116                    leave(entry, state);
117                    return None;
118                }
119                Search::Continue => {}
120            }
121
122            let found = match entry {
123                Entry::Node(node) => walk_node_children(node, state, enter, leave),
124                Entry::IfBlock(block) => walk_if_block_children(block, state, enter, leave),
125            };
126            if found.is_none() {
127                leave(entry, state);
128            }
129            found
130        }
131
132        fn walk_alternate<'a, T, S, E, L>(
133            alternate: &'a Alternate,
134            state: &mut S,
135            enter: &mut E,
136            leave: &mut L,
137        ) -> Option<T>
138        where
139            E: FnMut(Entry<'a>, &mut S) -> Search<T>,
140            L: FnMut(Entry<'a>, &mut S),
141        {
142            match alternate {
143                Alternate::Fragment(fragment) => walk_fragment(fragment, state, enter, leave),
144                Alternate::IfBlock(block) => walk_entry(Entry::IfBlock(block), state, enter, leave),
145            }
146        }
147
148        fn walk_if_block_children<'a, T, S, E, L>(
149            block: &'a IfBlock,
150            state: &mut S,
151            enter: &mut E,
152            leave: &mut L,
153        ) -> Option<T>
154        where
155            E: FnMut(Entry<'a>, &mut S) -> Search<T>,
156            L: FnMut(Entry<'a>, &mut S),
157        {
158            walk_fragment(&block.consequent, state, enter, leave).or_else(|| {
159                block
160                    .alternate
161                    .as_deref()
162                    .and_then(|alternate| walk_alternate(alternate, state, enter, leave))
163            })
164        }
165
166        fn walk_node<'a, T, S, E, L>(
167            node: &'a Node,
168            state: &mut S,
169            enter: &mut E,
170            leave: &mut L,
171        ) -> Option<T>
172        where
173            E: FnMut(Entry<'a>, &mut S) -> Search<T>,
174            L: FnMut(Entry<'a>, &mut S),
175        {
176            walk_entry(Entry::Node(node), state, enter, leave)
177        }
178
179        fn walk_node_children<'a, T, S, E, L>(
180            node: &'a Node,
181            state: &mut S,
182            enter: &mut E,
183            leave: &mut L,
184        ) -> Option<T>
185        where
186            E: FnMut(Entry<'a>, &mut S) -> Search<T>,
187            L: FnMut(Entry<'a>, &mut S),
188        {
189            match node {
190                Node::IfBlock(block) => walk_if_block_children(block, state, enter, leave),
191                Node::EachBlock(block) => {
192                    walk_fragment(&block.body, state, enter, leave).or_else(|| {
193                        block
194                            .fallback
195                            .as_ref()
196                            .and_then(|fragment| walk_fragment(fragment, state, enter, leave))
197                    })
198                }
199                Node::KeyBlock(block) => walk_fragment(&block.fragment, state, enter, leave),
200                Node::AwaitBlock(block) => {
201                    for fragment in [
202                        block.pending.as_ref(),
203                        block.then.as_ref(),
204                        block.catch.as_ref(),
205                    ] {
206                        if let Some(fragment) = fragment
207                            && let Some(found) = walk_fragment(fragment, state, enter, leave)
208                        {
209                            return Some(found);
210                        }
211                    }
212                    None
213                }
214                Node::SnippetBlock(block) => walk_fragment(&block.body, state, enter, leave),
215                _ => node
216                    .as_element()
217                    .and_then(|element| walk_fragment(element.fragment(), state, enter, leave)),
218            }
219        }
220
221        let mut enter = enter;
222        let mut leave = leave;
223        walk_fragment(self, state, &mut enter, &mut leave)
224    }
225
226    pub fn search<'a, T, F>(&'a self, visit: F) -> Option<T>
227    where
228        F: FnMut(Entry<'a>, &mut ()) -> Search<T>,
229    {
230        self.walk(&mut (), visit, |_, _| {})
231    }
232
233    pub fn find_map<'a, T, F>(&'a self, mut find: F) -> Option<T>
234    where
235        F: FnMut(Entry<'a>) -> Option<T>,
236    {
237        self.search(|entry, _| match find(entry) {
238            Some(found) => Search::Found(found),
239            None => Search::Continue,
240        })
241    }
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
245pub struct Script {
246    pub r#type: ScriptType,
247    pub start: usize,
248    pub end: usize,
249    /// Byte range of the script content (between open tag `>` and `</script>`).
250    #[serde(skip_serializing, default)]
251    pub content_start: usize,
252    #[serde(skip_serializing, default)]
253    pub content_end: usize,
254    pub context: ScriptContext,
255    #[serde(
256        skip_serializing,
257        skip_deserializing,
258        default = "empty_parsed_js_program"
259    )]
260    pub content: Arc<ParsedJsProgram>,
261    pub attributes: Box<[Attribute]>,
262}
263
264impl Script {
265    pub fn parsed_program(&self) -> &ParsedJsProgram {
266        &self.content
267    }
268
269    pub fn oxc_program(&self) -> &OxcProgram<'_> {
270        self.parsed_program().program()
271    }
272}
273
274#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
275pub struct EachBlock {
276    pub start: usize,
277    pub end: usize,
278    pub expression: Expression,
279    pub body: Fragment,
280    #[serde(skip_serializing, default)]
281    pub has_as_clause: bool,
282    #[serde(skip_serializing, default)]
283    pub invalid_key_without_as: bool,
284    pub context: Option<Expression>,
285    #[serde(skip_serializing, default)]
286    pub context_error: Option<ParseError>,
287    #[serde(skip_serializing_if = "Option::is_none")]
288    pub index: Option<Arc<str>>,
289    #[serde(skip_serializing_if = "Option::is_none")]
290    pub key: Option<Expression>,
291    #[serde(skip_serializing_if = "Option::is_none")]
292    pub fallback: Option<Fragment>,
293}
294
295#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
296pub struct KeyBlock {
297    pub start: usize,
298    pub end: usize,
299    pub expression: Expression,
300    pub fragment: Fragment,
301}
302
303#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
304pub struct AwaitBlock {
305    pub start: usize,
306    pub end: usize,
307    pub expression: Expression,
308    pub value: Option<Expression>,
309    pub error: Option<Expression>,
310    pub pending: Option<Fragment>,
311    pub then: Option<Fragment>,
312    pub catch: Option<Fragment>,
313}
314
315#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
316pub struct SnippetBlock {
317    pub start: usize,
318    pub end: usize,
319    pub expression: Expression,
320    #[serde(rename = "typeParams", skip_serializing_if = "Option::is_none")]
321    pub type_params: Option<Arc<str>>,
322    pub parameters: Box<[Expression]>,
323    pub body: Fragment,
324    #[serde(skip_serializing_if = "Option::is_none")]
325    pub header_error: Option<SnippetHeaderError>,
326}
327
328#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
329pub struct RenderTag {
330    pub start: usize,
331    pub end: usize,
332    pub expression: Expression,
333}
334
335#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
336pub struct HtmlTag {
337    pub start: usize,
338    pub end: usize,
339    pub expression: Expression,
340}
341
342#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
343pub struct ConstTag {
344    pub start: usize,
345    pub end: usize,
346    pub declaration: Expression,
347}
348
349#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
350pub struct DebugTag {
351    pub start: usize,
352    pub end: usize,
353    pub arguments: Box<[Expression]>,
354    pub identifiers: Box<[Identifier]>,
355}
356
357#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
358pub struct ExpressionTag {
359    pub r#type: ExpressionTagType,
360    pub start: usize,
361    pub end: usize,
362    pub expression: Expression,
363}
364
365#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
366pub enum ExpressionTagType {
367    ExpressionTag,
368}
369
370#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
371pub struct Comment {
372    pub start: usize,
373    pub end: usize,
374    pub data: Arc<str>,
375}
376
377#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
378pub struct RegularElement {
379    pub start: usize,
380    pub end: usize,
381    pub name: Arc<str>,
382    pub name_loc: NameLocation,
383    #[serde(skip_serializing, default)]
384    pub self_closing: bool,
385    #[serde(skip_serializing, default)]
386    pub has_end_tag: bool,
387    pub attributes: Box<[Attribute]>,
388    pub fragment: Fragment,
389}
390
391#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
392pub struct Component {
393    pub start: usize,
394    pub end: usize,
395    pub name: Arc<str>,
396    pub name_loc: NameLocation,
397    pub attributes: Box<[Attribute]>,
398    pub fragment: Fragment,
399}
400
401#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
402pub struct SlotElement {
403    pub start: usize,
404    pub end: usize,
405    pub name: Arc<str>,
406    pub name_loc: NameLocation,
407    pub attributes: Box<[Attribute]>,
408    pub fragment: Fragment,
409}
410
411#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
412pub struct SvelteHead {
413    pub start: usize,
414    pub end: usize,
415    pub name: Arc<str>,
416    pub name_loc: NameLocation,
417    pub attributes: Box<[Attribute]>,
418    pub fragment: Fragment,
419}
420
421#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
422pub struct SvelteBody {
423    pub start: usize,
424    pub end: usize,
425    pub name: Arc<str>,
426    pub name_loc: NameLocation,
427    pub attributes: Box<[Attribute]>,
428    pub fragment: Fragment,
429}
430
431#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
432pub struct SvelteWindow {
433    pub start: usize,
434    pub end: usize,
435    pub name: Arc<str>,
436    pub name_loc: NameLocation,
437    pub attributes: Box<[Attribute]>,
438    pub fragment: Fragment,
439}
440
441#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
442pub struct SvelteDocument {
443    pub start: usize,
444    pub end: usize,
445    pub name: Arc<str>,
446    pub name_loc: NameLocation,
447    pub attributes: Box<[Attribute]>,
448    pub fragment: Fragment,
449}
450
451#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
452pub struct SvelteComponent {
453    pub start: usize,
454    pub end: usize,
455    pub name: Arc<str>,
456    pub name_loc: NameLocation,
457    pub attributes: Box<[Attribute]>,
458    pub fragment: Fragment,
459    #[serde(skip_serializing_if = "Option::is_none")]
460    pub expression: Option<Expression>,
461}
462
463#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
464pub struct SvelteElement {
465    pub start: usize,
466    pub end: usize,
467    pub name: Arc<str>,
468    pub name_loc: NameLocation,
469    pub attributes: Box<[Attribute]>,
470    pub fragment: Fragment,
471    #[serde(skip_serializing_if = "Option::is_none")]
472    pub expression: Option<Expression>,
473}
474
475#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
476pub struct SvelteSelf {
477    pub start: usize,
478    pub end: usize,
479    pub name: Arc<str>,
480    pub name_loc: NameLocation,
481    pub attributes: Box<[Attribute]>,
482    pub fragment: Fragment,
483}
484
485#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
486pub struct SvelteFragment {
487    pub start: usize,
488    pub end: usize,
489    pub name: Arc<str>,
490    pub name_loc: NameLocation,
491    pub attributes: Box<[Attribute]>,
492    pub fragment: Fragment,
493}
494
495#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
496pub struct SvelteBoundary {
497    pub start: usize,
498    pub end: usize,
499    pub name: Arc<str>,
500    pub name_loc: NameLocation,
501    pub attributes: Box<[Attribute]>,
502    pub fragment: Fragment,
503}
504
505#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
506pub struct TitleElement {
507    pub start: usize,
508    pub end: usize,
509    pub name: Arc<str>,
510    pub name_loc: NameLocation,
511    pub attributes: Box<[Attribute]>,
512    pub fragment: Fragment,
513}
514
515#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
516#[serde(tag = "type")]
517pub enum Attribute {
518    Attribute(NamedAttribute),
519    SpreadAttribute(SpreadAttribute),
520    BindDirective(DirectiveAttribute),
521    OnDirective(DirectiveAttribute),
522    ClassDirective(DirectiveAttribute),
523    LetDirective(DirectiveAttribute),
524    StyleDirective(StyleDirective),
525    TransitionDirective(TransitionDirective),
526    AnimateDirective(DirectiveAttribute),
527    UseDirective(DirectiveAttribute),
528    AttachTag(AttachTag),
529}
530
531#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
532pub struct SpreadAttribute {
533    pub start: usize,
534    pub end: usize,
535    pub expression: Expression,
536}
537
538#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
539pub struct NamedAttribute {
540    pub start: usize,
541    pub end: usize,
542    pub name: Arc<str>,
543    pub name_loc: NameLocation,
544    pub value: AttributeValueList,
545    #[serde(skip_serializing, default)]
546    pub value_syntax: AttributeValueSyntax,
547    #[serde(skip_serializing, default)]
548    pub error: Option<AttrError>,
549}
550
551#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
552#[serde(untagged)]
553pub enum AttributeValueList {
554    Boolean(bool),
555    Values(Box<[AttributeValue]>),
556    ExpressionTag(ExpressionTag),
557}
558
559#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
560#[serde(tag = "type")]
561pub enum AttributeValue {
562    Text(Text),
563    ExpressionTag(ExpressionTag),
564}
565
566#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
567pub struct DirectiveAttribute {
568    pub start: usize,
569    pub end: usize,
570    pub name: Arc<str>,
571    pub name_loc: NameLocation,
572    pub expression: Expression,
573    pub modifiers: Box<[Arc<str>]>,
574    #[serde(skip_serializing, default)]
575    pub value_syntax: DirectiveValueSyntax,
576    #[serde(skip_serializing, default)]
577    pub value_start: usize,
578}
579
580#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
581pub struct StyleDirective {
582    pub start: usize,
583    pub end: usize,
584    pub name: Arc<str>,
585    pub name_loc: NameLocation,
586    pub modifiers: Box<[Arc<str>]>,
587    pub value: AttributeValueList,
588    #[serde(skip_serializing, default)]
589    pub value_syntax: AttributeValueSyntax,
590}
591
592#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
593pub struct TransitionDirective {
594    pub start: usize,
595    pub end: usize,
596    pub name: Arc<str>,
597    pub name_loc: NameLocation,
598    pub expression: Expression,
599    pub modifiers: Box<[Arc<str>]>,
600    pub intro: bool,
601    pub outro: bool,
602    #[serde(skip_serializing, default)]
603    pub value_syntax: DirectiveValueSyntax,
604    #[serde(skip_serializing, default)]
605    pub value_start: usize,
606}
607
608#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
609pub struct AttachTag {
610    pub start: usize,
611    pub end: usize,
612    pub expression: Expression,
613}
614
615#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
616pub struct IfBlock {
617    pub elseif: bool,
618    pub start: usize,
619    pub end: usize,
620    pub test: Expression,
621    pub consequent: Fragment,
622    pub alternate: Option<Box<Alternate>>,
623}
624
625#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
626#[serde(untagged)]
627pub enum Alternate {
628    Fragment(Fragment),
629    IfBlock(IfBlock),
630}
631
632#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
633pub struct ExpressionSyntax {
634    #[serde(skip_serializing, default)]
635    pub parens: u16,
636}
637
638#[derive(Debug, Clone, PartialEq, Eq)]
639pub enum JsNodeHandle {
640    Expression(Arc<ParsedJsExpression>),
641    SequenceItem {
642        root: Arc<ParsedJsExpression>,
643        index: usize,
644    },
645    Pattern(Arc<ParsedJsPattern>),
646    ParameterItem {
647        parameters: Arc<ParsedJsParameters>,
648        index: usize,
649    },
650    RestParameter(Arc<ParsedJsParameters>),
651    StatementInProgram {
652        program: Arc<ParsedJsProgram>,
653        index: usize,
654    },
655}
656
657#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
658pub struct Expression {
659    pub start: usize,
660    pub end: usize,
661    #[serde(skip_serializing, default)]
662    pub syntax: ExpressionSyntax,
663    #[serde(skip_serializing, skip_deserializing, default)]
664    pub node: Option<JsNodeHandle>,
665}
666
667impl Expression {
668    pub fn empty(start: usize, end: usize) -> Self {
669        Self {
670            start,
671            end,
672            syntax: Default::default(),
673            node: None,
674        }
675    }
676
677    /// Return `true` if this expression has no content (zero-length span, no node).
678    pub fn is_empty(&self) -> bool {
679        self.node.is_none() && self.start == self.end
680    }
681
682    pub fn from_expression(parsed: Arc<ParsedJsExpression>, start: usize, end: usize) -> Self {
683        Self {
684            start,
685            end,
686            syntax: Default::default(),
687            node: Some(JsNodeHandle::Expression(parsed)),
688        }
689    }
690
691    pub fn from_sequence_item(
692        root: Arc<ParsedJsExpression>,
693        index: usize,
694        start: usize,
695        end: usize,
696    ) -> Self {
697        Self {
698            start,
699            end,
700            syntax: Default::default(),
701            node: Some(JsNodeHandle::SequenceItem { root, index }),
702        }
703    }
704
705    pub fn from_pattern(parsed: Arc<ParsedJsPattern>, start: usize, end: usize) -> Self {
706        Self {
707            start,
708            end,
709            syntax: Default::default(),
710            node: Some(JsNodeHandle::Pattern(parsed)),
711        }
712    }
713
714    pub fn from_parameter_item(
715        parameters: Arc<ParsedJsParameters>,
716        index: usize,
717        start: usize,
718        end: usize,
719    ) -> Self {
720        Self {
721            start,
722            end,
723            syntax: Default::default(),
724            node: Some(JsNodeHandle::ParameterItem { parameters, index }),
725        }
726    }
727
728    pub fn from_rest_parameter(parameters: Arc<ParsedJsParameters>, start: usize, end: usize) -> Self {
729        Self {
730            start,
731            end,
732            syntax: Default::default(),
733            node: Some(JsNodeHandle::RestParameter(parameters)),
734        }
735    }
736
737    pub fn from_statement(
738        program: Arc<ParsedJsProgram>,
739        index: usize,
740        start: usize,
741        end: usize,
742    ) -> Self {
743        Self {
744            start,
745            end,
746            syntax: Default::default(),
747            node: Some(JsNodeHandle::StatementInProgram { program, index }),
748        }
749    }
750
751    pub fn parens(&self) -> u16 {
752        self.syntax.parens.max(self.outer_parens())
753    }
754
755    pub fn is_parenthesized(&self) -> bool {
756        self.parens() != 0
757    }
758
759    fn outer_parens(&self) -> u16 {
760        let mut parens = 0u16;
761        let mut current = match &self.node {
762            Some(JsNodeHandle::Expression(parsed)) => parsed.expression(),
763            Some(JsNodeHandle::SequenceItem { root, .. }) => root.expression(),
764            Some(JsNodeHandle::Pattern(_))
765            | Some(JsNodeHandle::ParameterItem { .. })
766            | Some(JsNodeHandle::RestParameter(_))
767            | Some(JsNodeHandle::StatementInProgram { .. })
768            | None => return 0,
769        };
770
771        while let OxcExpression::ParenthesizedExpression(parenthesized) = current {
772            parens = parens.saturating_add(1);
773            current = &parenthesized.expression;
774        }
775
776        parens
777    }
778
779    pub fn parsed(&self) -> Option<&ParsedJsExpression> {
780        match &self.node {
781            Some(JsNodeHandle::Expression(parsed)) => Some(parsed),
782            Some(JsNodeHandle::SequenceItem { root, .. }) => Some(root),
783            Some(JsNodeHandle::Pattern(_))
784            | Some(JsNodeHandle::ParameterItem { .. })
785            | Some(JsNodeHandle::RestParameter(_))
786            | Some(JsNodeHandle::StatementInProgram { .. })
787            | None => None,
788        }
789    }
790
791    pub fn oxc_expression_raw(&self) -> Option<&OxcExpression<'_>> {
792        match &self.node {
793            Some(JsNodeHandle::Expression(parsed)) => Some(parsed.expression()),
794            Some(JsNodeHandle::SequenceItem { root, index }) => {
795                let OxcExpression::SequenceExpression(sequence) = root.expression() else {
796                    return None;
797                };
798                sequence.expressions.get(*index)
799            }
800            Some(JsNodeHandle::Pattern(_))
801            | Some(JsNodeHandle::ParameterItem { .. })
802            | Some(JsNodeHandle::RestParameter(_))
803            | Some(JsNodeHandle::StatementInProgram { .. })
804            | None => None,
805        }
806    }
807
808    pub fn oxc_expression(&self) -> Option<&OxcExpression<'_>> {
809        let mut expression = self.oxc_expression_raw()?;
810
811        while let OxcExpression::ParenthesizedExpression(parenthesized) = expression {
812            expression = &parenthesized.expression;
813        }
814
815        Some(expression)
816    }
817
818    pub fn oxc_pattern(&self) -> Option<&BindingPattern<'_>> {
819        match &self.node {
820            Some(JsNodeHandle::Pattern(parsed)) => Some(parsed.pattern()),
821            Some(JsNodeHandle::ParameterItem { parameters, index }) => {
822                Some(&parameters.parameter(*index)?.pattern)
823            }
824            Some(JsNodeHandle::RestParameter(parameters)) => {
825                Some(&parameters.rest_parameter()?.rest.argument)
826            }
827            _ => None,
828        }
829    }
830
831    pub fn oxc_parameter(&self) -> Option<&FormalParameter<'_>> {
832        match &self.node {
833            Some(JsNodeHandle::ParameterItem { parameters, index }) => parameters.parameter(*index),
834            _ => None,
835        }
836    }
837
838    pub fn oxc_rest_parameter(&self) -> Option<&FormalParameterRest<'_>> {
839        match &self.node {
840            Some(JsNodeHandle::RestParameter(parameters)) => parameters.rest_parameter(),
841            _ => None,
842        }
843    }
844
845    pub fn oxc_statement(&self) -> Option<&OxcStatement<'_>> {
846        match &self.node {
847            Some(JsNodeHandle::StatementInProgram { program, index }) => program.statement(*index),
848            _ => None,
849        }
850    }
851
852    pub fn oxc_variable_declaration(&self) -> Option<&OxcVariableDeclaration<'_>> {
853        match &self.node {
854            Some(JsNodeHandle::StatementInProgram { program, index }) => {
855                program.variable_declaration(*index)
856            }
857            _ => None,
858        }
859    }
860
861    pub fn is_rest_parameter(&self) -> bool {
862        matches!(self.node, Some(JsNodeHandle::RestParameter(_)))
863    }
864
865    pub fn source_snippet(&self) -> Option<&str> {
866        match &self.node {
867            Some(JsNodeHandle::Expression(parsed)) => Some(parsed.source()),
868            Some(JsNodeHandle::SequenceItem { root, index }) => {
869                let OxcExpression::SequenceExpression(sequence) = root.expression() else {
870                    return None;
871                };
872                let node = sequence.expressions.get(*index)?;
873                root.source().get(node.span().start as usize..node.span().end as usize)
874            }
875            Some(JsNodeHandle::Pattern(parsed)) => Some(parsed.source()),
876            Some(JsNodeHandle::ParameterItem { parameters, index }) => {
877                let parameter = parameters.parameter(*index)?;
878                parameters
879                    .source()
880                    .get(parameter.span.start as usize - 1..parameter.span.end as usize - 1)
881            }
882            Some(JsNodeHandle::RestParameter(parameters)) => {
883                let parameter = parameters.rest_parameter()?;
884                parameters
885                    .source()
886                    .get(parameter.span.start as usize - 1..parameter.span.end as usize - 1)
887            }
888            Some(JsNodeHandle::StatementInProgram { program, index }) => program.statement_source(*index),
889            None => None,
890        }
891    }
892
893    pub fn identifier_name(&self) -> Option<Arc<str>> {
894        if let Some(identifier) = self
895            .oxc_expression()
896            .and_then(OxcExpression::get_identifier_reference)
897        {
898            return Some(Arc::from(identifier.name.as_str()));
899        }
900
901        match self.oxc_pattern()? {
902            BindingPattern::BindingIdentifier(identifier) => {
903                Some(Arc::from(identifier.name.as_str()))
904            }
905            _ => None,
906        }
907    }
908
909    pub fn binding_identifier(&self) -> Option<&BindingIdentifier<'_>> {
910        if let Some(declaration) = self.oxc_variable_declaration() {
911            let [declarator] = declaration.declarations.as_slice() else {
912                return None;
913            };
914            return declarator.id.get_binding_identifier();
915        }
916
917        match self.oxc_pattern()? {
918            BindingPattern::BindingIdentifier(identifier) => Some(identifier),
919            _ => None,
920        }
921    }
922}
923
924impl Span for Expression {
925    fn start(&self) -> usize {
926        self.start
927    }
928
929    fn end(&self) -> usize {
930        self.end
931    }
932}
933
934impl Serialize for Expression {
935    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
936        if let Some(raw_json) = self.to_estree_json() {
937            // Embed pre-serialized JSON directly via RawValue — no re-parsing.
938            let raw = serde_json::value::RawValue::from_string(raw_json)
939                .map_err(serde::ser::Error::custom)?;
940            raw.serialize(serializer)
941        } else {
942            // Fallback: just emit start/end when no OXC node is available
943            let mut map = serializer.serialize_map(Some(2))?;
944            map.serialize_entry("start", &self.start)?;
945            map.serialize_entry("end", &self.end)?;
946            map.end()
947        }
948    }
949}
950
951impl Expression {
952    /// Serialize this expression to an ESTree JSON string using OXC's serializer,
953    /// with span offsets adjusted from OXC-local to Svelte source coordinates.
954    /// Single-pass string-level adjustment, no intermediate tree parsing.
955    ///
956    /// Returns `None` if no OXC node handle is attached.
957    pub fn to_estree_json(&self) -> Option<String> {
958        let json = self.serialize_oxc_node()?;
959        let offset = self.oxc_span_offset();
960        if offset == 0 {
961            Some(json)
962        } else {
963            Some(adjust_estree_span_offsets(&json, offset))
964        }
965    }
966
967    /// Serialize the underlying OXC node to a JSON string via oxc_estree.
968    fn serialize_oxc_node(&self) -> Option<String> {
969        use oxc_estree::{CompactJSSerializer, ESTree};
970
971        match &self.node {
972            Some(JsNodeHandle::Expression(parsed)) => {
973                let mut ser = CompactJSSerializer::new(false);
974                serialize_oxc_expression_unwrapped(parsed.expression(), &mut ser);
975                Some(ser.into_string())
976            }
977            Some(JsNodeHandle::SequenceItem { root, index }) => {
978                let OxcExpression::SequenceExpression(seq) = root.expression() else {
979                    return None;
980                };
981                let expr = seq.expressions.get(*index)?;
982                let mut ser = CompactJSSerializer::new(false);
983                expr.serialize(&mut ser);
984                Some(ser.into_string())
985            }
986            Some(JsNodeHandle::Pattern(parsed)) => {
987                let mut ser = CompactJSSerializer::new(false);
988                parsed.pattern().serialize(&mut ser);
989                Some(ser.into_string())
990            }
991            Some(JsNodeHandle::ParameterItem { parameters, index }) => {
992                let param = parameters.parameter(*index)?;
993                let mut ser = CompactJSSerializer::new(false);
994                param.serialize(&mut ser);
995                Some(ser.into_string())
996            }
997            Some(JsNodeHandle::RestParameter(parameters)) => {
998                let rest = parameters.rest_parameter()?;
999                let mut ser = CompactJSSerializer::new(false);
1000                rest.serialize(&mut ser);
1001                Some(ser.into_string())
1002            }
1003            Some(JsNodeHandle::StatementInProgram { program, index }) => {
1004                let stmt = program.statement(*index)?;
1005                let mut ser = CompactJSSerializer::new(false);
1006                stmt.serialize(&mut ser);
1007                Some(ser.into_string())
1008            }
1009            None => None,
1010        }
1011    }
1012
1013    /// Compute the offset to add to OXC span values to get Svelte source positions.
1014    fn oxc_span_offset(&self) -> i64 {
1015        match &self.node {
1016            Some(JsNodeHandle::Expression(parsed)) => {
1017                self.start as i64 - parsed.expression().span().start as i64
1018            }
1019            Some(JsNodeHandle::SequenceItem { root, index }) => {
1020                if let OxcExpression::SequenceExpression(seq) = root.expression() {
1021                    if let Some(expr) = seq.expressions.get(*index) {
1022                        return self.start as i64 - expr.span().start as i64;
1023                    }
1024                }
1025                0
1026            }
1027            Some(JsNodeHandle::Pattern(parsed)) => {
1028                self.start as i64 - parsed.pattern().span().start as i64
1029            }
1030            Some(JsNodeHandle::ParameterItem { parameters, index }) => {
1031                if let Some(param) = parameters.parameter(*index) {
1032                    return self.start as i64 - param.span.start as i64;
1033                }
1034                0
1035            }
1036            Some(JsNodeHandle::RestParameter(parameters)) => {
1037                if let Some(rest) = parameters.rest_parameter() {
1038                    return self.start as i64 - rest.span.start as i64;
1039                }
1040                0
1041            }
1042            Some(JsNodeHandle::StatementInProgram { program, index }) => {
1043                if let Some(stmt) = program.statement(*index) {
1044                    return self.start as i64 - stmt.span().start as i64;
1045                }
1046                0
1047            }
1048            None => 0,
1049        }
1050    }
1051}
1052
1053/// Serialize an OXC expression, unwrapping ParenthesizedExpression nodes.
1054/// Svelte tracks parens via `Expression.syntax.parens`, not in the AST.
1055fn serialize_oxc_expression_unwrapped(
1056    expr: &OxcExpression<'_>,
1057    ser: &mut oxc_estree::CompactJSSerializer,
1058) {
1059    use oxc_estree::ESTree;
1060    let mut inner = expr;
1061    while let OxcExpression::ParenthesizedExpression(paren) = inner {
1062        inner = &paren.expression;
1063    }
1064    inner.serialize(ser);
1065}
1066
1067/// Single-pass adjustment of `"start":N` and `"end":N` span values in a JSON string.
1068///
1069/// In ESTree JSON from oxc_estree, these keys with numeric values always represent
1070/// byte-position spans. This is safe because string values containing `"start":` are
1071/// escaped (`\"start\":`) in JSON, so the pattern cannot appear inside string values.
1072fn adjust_estree_span_offsets(json: &str, offset: i64) -> String {
1073    let bytes = json.as_bytes();
1074    let len = bytes.len();
1075    let mut result = String::with_capacity(len + 64);
1076    let mut i = 0;
1077
1078    while i < len {
1079        let remaining = &bytes[i..];
1080
1081        let key_len = if remaining.starts_with(b"\"start\":") {
1082            8 // "start":
1083        } else if remaining.starts_with(b"\"end\":") {
1084            6 // "end":
1085        } else {
1086            0
1087        };
1088
1089        if key_len > 0 {
1090            // Verify preceding char is a struct delimiter (not inside a string value)
1091            let before_ok = i == 0 || matches!(bytes[i - 1], b',' | b'{' | b'\n' | b' ');
1092            let num_start = i + key_len;
1093
1094            if before_ok && num_start < len && bytes[num_start].is_ascii_digit() {
1095                let mut num_end = num_start;
1096                while num_end < len && bytes[num_end].is_ascii_digit() {
1097                    num_end += 1;
1098                }
1099
1100                if let Ok(val) = json[num_start..num_end].parse::<i64>() {
1101                    let adjusted = (val + offset).max(0) as u64;
1102                    result.push_str(&json[i..i + key_len]);
1103                    result.push_str(&adjusted.to_string());
1104                    i = num_end;
1105                    continue;
1106                }
1107            }
1108        }
1109
1110        // Copy single byte (ASCII in JSON keys/structure)
1111        result.push(bytes[i] as char);
1112        i += 1;
1113    }
1114
1115    result
1116}
1117
1118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1119pub struct Identifier {
1120    pub start: usize,
1121    pub end: usize,
1122    #[serde(skip_serializing_if = "Option::is_none")]
1123    pub loc: Option<Loc>,
1124    pub name: Arc<str>,
1125}
1126
1127#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1128pub struct Literal {
1129    pub start: usize,
1130    pub end: usize,
1131    #[serde(skip_serializing_if = "Option::is_none")]
1132    pub loc: Option<Loc>,
1133    pub value: LiteralValue,
1134    pub raw: Arc<str>,
1135}
1136
1137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1138pub struct BinaryExpression {
1139    pub start: usize,
1140    pub end: usize,
1141    #[serde(skip_serializing_if = "Option::is_none")]
1142    pub loc: Option<Loc>,
1143    pub left: Box<Expression>,
1144    pub operator: Arc<str>,
1145    pub right: Box<Expression>,
1146}
1147
1148#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1149pub struct CallExpression {
1150    pub start: usize,
1151    pub end: usize,
1152    #[serde(skip_serializing_if = "Option::is_none")]
1153    pub loc: Option<Loc>,
1154    pub callee: Box<Expression>,
1155    pub arguments: Box<[Expression]>,
1156    pub optional: bool,
1157}
1158
1159#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1160pub struct Text {
1161    pub start: usize,
1162    pub end: usize,
1163    pub raw: Arc<str>,
1164    pub data: Arc<str>,
1165}
1166
1167#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1168pub struct Css {
1169    pub r#type: CssType,
1170    pub start: usize,
1171    pub end: usize,
1172    pub attributes: Box<[Attribute]>,
1173    pub children: Box<[CssNode]>,
1174    pub content: CssContent,
1175}
1176
1177#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1178pub enum CssType {
1179    StyleSheet,
1180}
1181
1182#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1183pub struct CssContent {
1184    pub start: usize,
1185    pub end: usize,
1186    pub styles: Arc<str>,
1187    pub comment: Option<Arc<str>>,
1188}
1189
1190#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1191#[serde(tag = "type")]
1192pub enum CssNode {
1193    Rule(CssRule),
1194    Atrule(CssAtrule),
1195}
1196
1197#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1198pub struct CssRule {
1199    pub prelude: CssSelectorList,
1200    pub block: CssBlock,
1201    pub start: usize,
1202    pub end: usize,
1203}
1204
1205#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1206pub struct CssAtrule {
1207    pub start: usize,
1208    pub end: usize,
1209    pub name: Arc<str>,
1210    pub prelude: Arc<str>,
1211    pub block: Option<CssBlock>,
1212}
1213
1214#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1215pub struct CssBlock {
1216    pub r#type: CssBlockType,
1217    pub start: usize,
1218    pub end: usize,
1219    pub children: Box<[CssBlockChild]>,
1220}
1221
1222#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1223pub enum CssBlockType {
1224    Block,
1225}
1226
1227#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1228#[serde(tag = "type")]
1229pub enum CssBlockChild {
1230    Declaration(CssDeclaration),
1231    Rule(CssRule),
1232    Atrule(CssAtrule),
1233}
1234
1235#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1236pub struct CssDeclaration {
1237    pub start: usize,
1238    pub end: usize,
1239    pub property: Arc<str>,
1240    pub value: Arc<str>,
1241}
1242
1243#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1244pub struct CssSelectorList {
1245    pub r#type: CssSelectorListType,
1246    pub start: usize,
1247    pub end: usize,
1248    pub children: Box<[CssComplexSelector]>,
1249}
1250
1251#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1252pub enum CssSelectorListType {
1253    SelectorList,
1254}
1255
1256#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1257pub struct CssComplexSelector {
1258    pub r#type: CssComplexSelectorType,
1259    pub start: usize,
1260    pub end: usize,
1261    pub children: Box<[CssRelativeSelector]>,
1262}
1263
1264#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1265pub enum CssComplexSelectorType {
1266    ComplexSelector,
1267}
1268
1269#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1270pub struct CssRelativeSelector {
1271    pub r#type: CssRelativeSelectorType,
1272    pub combinator: Option<CssCombinator>,
1273    pub selectors: Box<[CssSimpleSelector]>,
1274    pub start: usize,
1275    pub end: usize,
1276}
1277
1278#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1279pub enum CssRelativeSelectorType {
1280    RelativeSelector,
1281}
1282
1283#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1284pub struct CssCombinator {
1285    pub r#type: CssCombinatorType,
1286    pub name: Arc<str>,
1287    pub start: usize,
1288    pub end: usize,
1289}
1290
1291#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1292pub enum CssCombinatorType {
1293    Combinator,
1294}
1295
1296#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1297#[serde(tag = "type")]
1298pub enum CssSimpleSelector {
1299    TypeSelector(CssNameSelector),
1300    IdSelector(CssNameSelector),
1301    ClassSelector(CssNameSelector),
1302    PseudoElementSelector(CssNameSelector),
1303    PseudoClassSelector(CssPseudoClassSelector),
1304    AttributeSelector(CssAttributeSelector),
1305    Nth(CssValueSelector),
1306    Percentage(CssValueSelector),
1307    NestingSelector(CssNameSelector),
1308}
1309
1310#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1311pub struct CssNameSelector {
1312    pub name: Arc<str>,
1313    pub start: usize,
1314    pub end: usize,
1315}
1316
1317#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1318pub struct CssValueSelector {
1319    pub value: Arc<str>,
1320    pub start: usize,
1321    pub end: usize,
1322}
1323
1324#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1325pub struct CssPseudoClassSelector {
1326    pub name: Arc<str>,
1327    pub args: Option<CssSelectorList>,
1328    pub start: usize,
1329    pub end: usize,
1330}
1331
1332#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1333pub struct CssAttributeSelector {
1334    pub start: usize,
1335    pub end: usize,
1336    pub name: Arc<str>,
1337    pub matcher: Option<Arc<str>>,
1338    pub value: Option<Arc<str>>,
1339    pub flags: Option<Arc<str>>,
1340}
1341
1342#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1343pub struct Options {
1344    pub start: usize,
1345    pub end: usize,
1346    pub attributes: Box<[Attribute]>,
1347    #[serde(skip_serializing, skip_deserializing, default)]
1348    pub fragment: Fragment,
1349    #[serde(skip_serializing_if = "Option::is_none")]
1350    #[serde(rename = "customElement")]
1351    pub custom_element: Option<CustomElement>,
1352    #[serde(skip_serializing_if = "Option::is_none")]
1353    pub runes: Option<bool>,
1354}
1355
1356#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1357pub struct CustomElement {
1358    pub tag: Arc<str>,
1359}
1360
1361#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1362#[serde(tag = "type")]
1363pub enum Node {
1364    Text(Text),
1365    IfBlock(IfBlock),
1366    EachBlock(EachBlock),
1367    KeyBlock(KeyBlock),
1368    AwaitBlock(AwaitBlock),
1369    SnippetBlock(SnippetBlock),
1370    RenderTag(RenderTag),
1371    HtmlTag(HtmlTag),
1372    ConstTag(ConstTag),
1373    DebugTag(DebugTag),
1374    ExpressionTag(ExpressionTag),
1375    Comment(Comment),
1376    RegularElement(RegularElement),
1377    Component(Component),
1378    SlotElement(SlotElement),
1379    SvelteHead(SvelteHead),
1380    SvelteBody(SvelteBody),
1381    SvelteWindow(SvelteWindow),
1382    SvelteDocument(SvelteDocument),
1383    SvelteComponent(SvelteComponent),
1384    SvelteElement(SvelteElement),
1385    SvelteSelf(SvelteSelf),
1386    SvelteFragment(SvelteFragment),
1387    SvelteBoundary(SvelteBoundary),
1388    TitleElement(TitleElement),
1389}
1390
1391pub trait HasFragment {
1392    fn fragment(&self) -> &Fragment;
1393}
1394
1395pub trait Element: Span + HasFragment {
1396    fn name(&self) -> &str;
1397    fn name_loc(&self) -> &NameLocation;
1398    fn attributes(&self) -> &[Attribute];
1399    fn expression(&self) -> Option<&Expression> {
1400        None
1401    }
1402    fn self_closing(&self) -> bool {
1403        false
1404    }
1405}
1406
1407macro_rules! impl_element {
1408    ($($ty:ty),* $(,)?) => {
1409        $(
1410            impl HasFragment for $ty {
1411                fn fragment(&self) -> &Fragment { &self.fragment }
1412            }
1413
1414            impl Element for $ty {
1415                fn name(&self) -> &str { &self.name }
1416                fn name_loc(&self) -> &NameLocation { &self.name_loc }
1417                fn attributes(&self) -> &[Attribute] { &self.attributes }
1418            }
1419        )*
1420    };
1421}
1422
1423impl_element!(
1424    Component,
1425    SlotElement,
1426    SvelteHead,
1427    SvelteBody,
1428    SvelteWindow,
1429    SvelteDocument,
1430    SvelteSelf,
1431    SvelteFragment,
1432    SvelteBoundary,
1433    TitleElement,
1434);
1435
1436impl Element for RegularElement {
1437    fn name(&self) -> &str {
1438        &self.name
1439    }
1440    fn name_loc(&self) -> &NameLocation {
1441        &self.name_loc
1442    }
1443    fn attributes(&self) -> &[Attribute] {
1444        &self.attributes
1445    }
1446    fn self_closing(&self) -> bool {
1447        self.self_closing
1448    }
1449}
1450
1451impl HasFragment for RegularElement {
1452    fn fragment(&self) -> &Fragment {
1453        &self.fragment
1454    }
1455}
1456
1457impl HasFragment for SvelteComponent {
1458    fn fragment(&self) -> &Fragment {
1459        &self.fragment
1460    }
1461}
1462
1463impl Element for SvelteComponent {
1464    fn name(&self) -> &str {
1465        &self.name
1466    }
1467    fn name_loc(&self) -> &NameLocation {
1468        &self.name_loc
1469    }
1470    fn attributes(&self) -> &[Attribute] {
1471        &self.attributes
1472    }
1473    fn expression(&self) -> Option<&Expression> {
1474        self.expression.as_ref()
1475    }
1476}
1477
1478impl HasFragment for SvelteElement {
1479    fn fragment(&self) -> &Fragment {
1480        &self.fragment
1481    }
1482}
1483
1484impl Element for SvelteElement {
1485    fn name(&self) -> &str {
1486        &self.name
1487    }
1488    fn name_loc(&self) -> &NameLocation {
1489        &self.name_loc
1490    }
1491    fn attributes(&self) -> &[Attribute] {
1492        &self.attributes
1493    }
1494    fn expression(&self) -> Option<&Expression> {
1495        self.expression.as_ref()
1496    }
1497}
1498
1499impl HasFragment for Root {
1500    fn fragment(&self) -> &Fragment {
1501        &self.fragment
1502    }
1503}
1504
1505impl HasFragment for KeyBlock {
1506    fn fragment(&self) -> &Fragment {
1507        &self.fragment
1508    }
1509}
1510
1511impl HasFragment for SnippetBlock {
1512    fn fragment(&self) -> &Fragment {
1513        &self.body
1514    }
1515}
1516
1517impl Alternate {
1518    pub fn try_for_each_fragment<B>(
1519        &self,
1520        mut visit: impl FnMut(&Fragment) -> ControlFlow<B>,
1521    ) -> ControlFlow<B> {
1522        match self {
1523            Self::Fragment(fragment) => visit(fragment),
1524            Self::IfBlock(block) => {
1525                visit(&block.consequent)?;
1526                if let Some(alternate) = block.alternate.as_deref() {
1527                    alternate.try_for_each_fragment(visit)
1528                } else {
1529                    ControlFlow::Continue(())
1530                }
1531            }
1532        }
1533    }
1534
1535    pub fn for_each_fragment(&self, mut visit: impl FnMut(&Fragment)) {
1536        let _ = self.try_for_each_fragment(|fragment| {
1537            visit(fragment);
1538            ControlFlow::<()>::Continue(())
1539        });
1540    }
1541}
1542
1543impl Node {
1544    pub fn as_element(&self) -> Option<&dyn Element> {
1545        match self {
1546            Node::RegularElement(el) => Some(el),
1547            Node::Component(el) => Some(el),
1548            Node::SlotElement(el) => Some(el),
1549            Node::SvelteHead(el) => Some(el),
1550            Node::SvelteBody(el) => Some(el),
1551            Node::SvelteWindow(el) => Some(el),
1552            Node::SvelteDocument(el) => Some(el),
1553            Node::SvelteComponent(el) => Some(el),
1554            Node::SvelteElement(el) => Some(el),
1555            Node::SvelteSelf(el) => Some(el),
1556            Node::SvelteFragment(el) => Some(el),
1557            Node::SvelteBoundary(el) => Some(el),
1558            Node::TitleElement(el) => Some(el),
1559            _ => None,
1560        }
1561    }
1562
1563    pub fn try_for_each_child_fragment<B>(
1564        &self,
1565        mut visit: impl FnMut(&Fragment) -> ControlFlow<B>,
1566    ) -> ControlFlow<B> {
1567        match self {
1568            Self::IfBlock(block) => {
1569                visit(&block.consequent)?;
1570                if let Some(alternate) = block.alternate.as_deref() {
1571                    alternate.try_for_each_fragment(visit)
1572                } else {
1573                    ControlFlow::Continue(())
1574                }
1575            }
1576            Self::EachBlock(block) => {
1577                visit(&block.body)?;
1578                if let Some(fallback) = block.fallback.as_ref() {
1579                    visit(fallback)?;
1580                }
1581                ControlFlow::Continue(())
1582            }
1583            Self::KeyBlock(block) => visit(block.fragment()),
1584            Self::AwaitBlock(block) => {
1585                for fragment in [
1586                    block.pending.as_ref(),
1587                    block.then.as_ref(),
1588                    block.catch.as_ref(),
1589                ]
1590                .into_iter()
1591                .flatten()
1592                {
1593                    visit(fragment)?;
1594                }
1595                ControlFlow::Continue(())
1596            }
1597            Self::SnippetBlock(block) => visit(block.fragment()),
1598            _ => self
1599                .as_element()
1600                .map_or(ControlFlow::Continue(()), |element| {
1601                    visit(element.fragment())
1602                }),
1603        }
1604    }
1605
1606    pub fn for_each_child_fragment(&self, mut visit: impl FnMut(&Fragment)) {
1607        let _ = self.try_for_each_child_fragment(|fragment| {
1608            visit(fragment);
1609            ControlFlow::<()>::Continue(())
1610        });
1611    }
1612}
1613
1614#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1615pub struct RootComment {
1616    pub r#type: RootCommentType,
1617    pub start: usize,
1618    pub end: usize,
1619    pub value: Arc<str>,
1620    pub loc: NameLocation,
1621}
1622
1623#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1624pub struct Root {
1625    pub css: Option<Css>,
1626    #[serde(skip_serializing, default)]
1627    pub styles: Box<[Css]>,
1628    pub js: Box<[Script]>,
1629    #[serde(skip_serializing, default)]
1630    pub scripts: Box<[Script]>,
1631    pub start: usize,
1632    pub end: usize,
1633    pub r#type: RootType,
1634    pub fragment: Fragment,
1635    pub options: Option<Options>,
1636    #[serde(skip_serializing_if = "Option::is_none")]
1637    pub module: Option<Script>,
1638    #[serde(skip_serializing_if = "Option::is_none")]
1639    pub instance: Option<Script>,
1640    #[serde(skip_serializing_if = "Option::is_none")]
1641    pub comments: Option<Box<[RootComment]>>,
1642    #[serde(skip_serializing, default)]
1643    pub errors: Box<[crate::ast::common::ParseError]>,
1644}
1645
1646macro_rules! impl_span_for_struct {
1647    ($($ty:ty),* $(,)?) => {
1648        $(
1649            impl Span for $ty {
1650                fn start(&self) -> usize {
1651                    self.start
1652                }
1653
1654                fn end(&self) -> usize {
1655                    self.end
1656                }
1657            }
1658        )*
1659    };
1660}
1661
1662impl_span_for_struct!(
1663    Script,
1664    EachBlock,
1665    KeyBlock,
1666    AwaitBlock,
1667    SnippetBlock,
1668    RenderTag,
1669    HtmlTag,
1670    ConstTag,
1671    DebugTag,
1672    ExpressionTag,
1673    Comment,
1674    RegularElement,
1675    Component,
1676    SlotElement,
1677    SvelteHead,
1678    SvelteBody,
1679    SvelteWindow,
1680    SvelteDocument,
1681    SvelteComponent,
1682    SvelteElement,
1683    SvelteSelf,
1684    SvelteFragment,
1685    SvelteBoundary,
1686    TitleElement,
1687    IfBlock,
1688    Text,
1689    Css,
1690    CssContent,
1691    CssRule,
1692    CssAtrule,
1693    CssBlock,
1694    CssDeclaration,
1695    CssSelectorList,
1696    CssComplexSelector,
1697    CssRelativeSelector,
1698    CssCombinator,
1699    CssNameSelector,
1700    CssValueSelector,
1701    CssPseudoClassSelector,
1702    CssAttributeSelector,
1703    Options,
1704    RootComment,
1705    Root
1706);
1707
1708impl Span for Node {
1709    fn start(&self) -> usize {
1710        match self {
1711            Node::Text(node) => node.start,
1712            Node::IfBlock(node) => node.start,
1713            Node::EachBlock(node) => node.start,
1714            Node::KeyBlock(node) => node.start,
1715            Node::AwaitBlock(node) => node.start,
1716            Node::SnippetBlock(node) => node.start,
1717            Node::RenderTag(node) => node.start,
1718            Node::HtmlTag(node) => node.start,
1719            Node::ConstTag(node) => node.start,
1720            Node::DebugTag(node) => node.start,
1721            Node::ExpressionTag(node) => node.start,
1722            Node::Comment(node) => node.start,
1723            Node::RegularElement(node) => node.start,
1724            Node::Component(node) => node.start,
1725            Node::SlotElement(node) => node.start,
1726            Node::SvelteHead(node) => node.start,
1727            Node::SvelteBody(node) => node.start,
1728            Node::SvelteWindow(node) => node.start,
1729            Node::SvelteDocument(node) => node.start,
1730            Node::SvelteComponent(node) => node.start,
1731            Node::SvelteElement(node) => node.start,
1732            Node::SvelteSelf(node) => node.start,
1733            Node::SvelteFragment(node) => node.start,
1734            Node::SvelteBoundary(node) => node.start,
1735            Node::TitleElement(node) => node.start,
1736        }
1737    }
1738
1739    fn end(&self) -> usize {
1740        match self {
1741            Node::Text(node) => node.end,
1742            Node::IfBlock(node) => node.end,
1743            Node::EachBlock(node) => node.end,
1744            Node::KeyBlock(node) => node.end,
1745            Node::AwaitBlock(node) => node.end,
1746            Node::SnippetBlock(node) => node.end,
1747            Node::RenderTag(node) => node.end,
1748            Node::HtmlTag(node) => node.end,
1749            Node::ConstTag(node) => node.end,
1750            Node::DebugTag(node) => node.end,
1751            Node::ExpressionTag(node) => node.end,
1752            Node::Comment(node) => node.end,
1753            Node::RegularElement(node) => node.end,
1754            Node::Component(node) => node.end,
1755            Node::SlotElement(node) => node.end,
1756            Node::SvelteHead(node) => node.end,
1757            Node::SvelteBody(node) => node.end,
1758            Node::SvelteWindow(node) => node.end,
1759            Node::SvelteDocument(node) => node.end,
1760            Node::SvelteComponent(node) => node.end,
1761            Node::SvelteElement(node) => node.end,
1762            Node::SvelteSelf(node) => node.end,
1763            Node::SvelteFragment(node) => node.end,
1764            Node::SvelteBoundary(node) => node.end,
1765            Node::TitleElement(node) => node.end,
1766        }
1767    }
1768}
1769
1770#[cfg(test)]
1771mod tests {
1772    use super::*;
1773
1774    #[test]
1775    fn adjust_estree_span_offsets_adds_offset() {
1776        let json = r#"{"type":"Identifier","name":"foo","start":0,"end":3}"#;
1777        let adjusted = adjust_estree_span_offsets(json, 10);
1778        assert_eq!(
1779            adjusted,
1780            r#"{"type":"Identifier","name":"foo","start":10,"end":13}"#
1781        );
1782    }
1783
1784    #[test]
1785    fn adjust_estree_span_offsets_handles_nested() {
1786        let json = r#"{"type":"BinaryExpression","left":{"type":"Identifier","name":"a","start":0,"end":1},"right":{"type":"NumericLiteral","value":1,"raw":"1","start":4,"end":5},"start":0,"end":5}"#;
1787        let adjusted = adjust_estree_span_offsets(json, 20);
1788        assert!(adjusted.contains(r#""start":20"#));
1789        assert!(adjusted.contains(r#""end":21"#));
1790        assert!(adjusted.contains(r#""start":24"#));
1791        assert!(adjusted.contains(r#""end":25"#));
1792    }
1793
1794    #[test]
1795    fn adjust_estree_span_offsets_preserves_string_values() {
1796        // "name":"start" should NOT be adjusted
1797        let json = r#"{"type":"Identifier","name":"start","start":0,"end":5}"#;
1798        let adjusted = adjust_estree_span_offsets(json, 10);
1799        assert_eq!(
1800            adjusted,
1801            r#"{"type":"Identifier","name":"start","start":10,"end":15}"#
1802        );
1803    }
1804
1805    #[test]
1806    fn expression_serializes_oxc_identifier_with_offset() {
1807        let parsed = crate::js::ParsedJsExpression::parse(
1808            "foo",
1809            oxc_span::SourceType::mjs(),
1810        )
1811        .expect("valid expression");
1812
1813        let expr = Expression::from_expression(Arc::new(parsed), 42, 45);
1814        let json = serde_json::to_string(&expr).expect("serialize");
1815        let value: serde_json::Value = serde_json::from_str(&json).expect("valid json");
1816
1817        assert_eq!(value["type"], "Identifier");
1818        assert_eq!(value["name"], "foo");
1819        assert_eq!(value["start"], 42);
1820        assert_eq!(value["end"], 45);
1821    }
1822
1823    #[test]
1824    fn expression_serializes_fallback_without_node() {
1825        let expr = Expression::empty(10, 20);
1826        let json = serde_json::to_string(&expr).expect("serialize");
1827        let value: serde_json::Value = serde_json::from_str(&json).expect("valid json");
1828
1829        assert_eq!(value["start"], 10);
1830        assert_eq!(value["end"], 20);
1831        assert!(value.get("type").is_none());
1832    }
1833}