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