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, 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_deserializing, default)]
252    pub content_start: usize,
253    #[serde(skip_deserializing, default)]
254    pub content_end: usize,
255    pub context: ScriptContext,
256    #[serde(skip_deserializing, default = "empty_parsed_js_program")]
257    pub content: Arc<JsProgram>,
258    /// Pre-serialized ESTree JSON for the content Program node.
259    #[serde(skip_deserializing, default)]
260    pub content_json: Option<Arc<str>>,
261    pub attributes: Box<[Attribute]>,
262}
263
264impl Serialize for Script {
265    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
266        use serde::ser::SerializeMap;
267        let mut map = serializer.serialize_map(None)?;
268        map.serialize_entry("type", &self.r#type)?;
269        map.serialize_entry("start", &self.start)?;
270        map.serialize_entry("end", &self.end)?;
271        map.serialize_entry("context", &self.context)?;
272        if let Some(ref json) = self.content_json {
273            let raw = serde_json::value::RawValue::from_string(json.to_string())
274                .map_err(serde::ser::Error::custom)?;
275            map.serialize_entry("content", &raw)?;
276        }
277        map.serialize_entry("attributes", &self.attributes)?;
278        map.end()
279    }
280}
281
282impl Script {
283    pub fn parsed_program(&self) -> &JsProgram {
284        &self.content
285    }
286
287    pub fn oxc_program(&self) -> &OxcProgram<'_> {
288        self.parsed_program().program()
289    }
290}
291
292#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
293pub struct EachBlock {
294    pub start: usize,
295    pub end: usize,
296    pub expression: Expression,
297    pub body: Fragment,
298    #[serde(skip_serializing, default)]
299    pub has_as_clause: bool,
300    #[serde(skip_serializing, default)]
301    pub invalid_key_without_as: bool,
302    pub context: Option<Expression>,
303    #[serde(skip_serializing, default)]
304    pub context_error: Option<ParseError>,
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub index: Option<Arc<str>>,
307    #[serde(skip_serializing_if = "Option::is_none")]
308    pub key: Option<Expression>,
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub fallback: Option<Fragment>,
311}
312
313#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314pub struct KeyBlock {
315    pub start: usize,
316    pub end: usize,
317    pub expression: Expression,
318    pub fragment: Fragment,
319}
320
321#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
322pub struct AwaitBlock {
323    pub start: usize,
324    pub end: usize,
325    pub expression: Expression,
326    pub value: Option<Expression>,
327    pub error: Option<Expression>,
328    pub pending: Option<Fragment>,
329    pub then: Option<Fragment>,
330    pub catch: Option<Fragment>,
331}
332
333#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
334pub struct SnippetBlock {
335    pub start: usize,
336    pub end: usize,
337    pub expression: Expression,
338    #[serde(rename = "typeParams", skip_serializing_if = "Option::is_none")]
339    pub type_params: Option<Arc<str>>,
340    pub parameters: Box<[Expression]>,
341    pub body: Fragment,
342    #[serde(skip_serializing_if = "Option::is_none")]
343    pub header_error: Option<SnippetHeaderError>,
344}
345
346#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
347pub struct RenderTag {
348    pub start: usize,
349    pub end: usize,
350    pub expression: Expression,
351}
352
353#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
354pub struct HtmlTag {
355    pub start: usize,
356    pub end: usize,
357    pub expression: Expression,
358}
359
360#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
361pub struct ConstTag {
362    pub start: usize,
363    pub end: usize,
364    pub declaration: Expression,
365}
366
367#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
368pub struct DebugTag {
369    pub start: usize,
370    pub end: usize,
371    pub arguments: Box<[Expression]>,
372    pub identifiers: Box<[Identifier]>,
373}
374
375#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
376pub struct ExpressionTag {
377    pub r#type: ExpressionTagType,
378    pub start: usize,
379    pub end: usize,
380    pub expression: Expression,
381}
382
383/// Serde discriminant for `"type": "ExpressionTag"` in the JSON AST.
384#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
385pub enum ExpressionTagType {
386    ExpressionTag,
387}
388
389#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
390pub struct Comment {
391    pub start: usize,
392    pub end: usize,
393    pub data: Arc<str>,
394}
395
396#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
397pub struct RegularElement {
398    pub start: usize,
399    pub end: usize,
400    pub name: Arc<str>,
401    pub name_loc: SourceRange,
402    #[serde(skip_serializing, default)]
403    pub self_closing: bool,
404    #[serde(skip_serializing, default)]
405    pub has_end_tag: bool,
406    pub attributes: Box<[Attribute]>,
407    pub fragment: Fragment,
408}
409
410#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
411pub struct Component {
412    pub start: usize,
413    pub end: usize,
414    pub name: Arc<str>,
415    pub name_loc: SourceRange,
416    pub attributes: Box<[Attribute]>,
417    pub fragment: Fragment,
418}
419
420#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
421pub struct SlotElement {
422    pub start: usize,
423    pub end: usize,
424    pub name: Arc<str>,
425    pub name_loc: SourceRange,
426    pub attributes: Box<[Attribute]>,
427    pub fragment: Fragment,
428}
429
430#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
431pub struct SvelteHead {
432    pub start: usize,
433    pub end: usize,
434    pub name: Arc<str>,
435    pub name_loc: SourceRange,
436    pub attributes: Box<[Attribute]>,
437    pub fragment: Fragment,
438}
439
440#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
441pub struct SvelteBody {
442    pub start: usize,
443    pub end: usize,
444    pub name: Arc<str>,
445    pub name_loc: SourceRange,
446    pub attributes: Box<[Attribute]>,
447    pub fragment: Fragment,
448}
449
450#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
451pub struct SvelteWindow {
452    pub start: usize,
453    pub end: usize,
454    pub name: Arc<str>,
455    pub name_loc: SourceRange,
456    pub attributes: Box<[Attribute]>,
457    pub fragment: Fragment,
458}
459
460#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
461pub struct SvelteDocument {
462    pub start: usize,
463    pub end: usize,
464    pub name: Arc<str>,
465    pub name_loc: SourceRange,
466    pub attributes: Box<[Attribute]>,
467    pub fragment: Fragment,
468}
469
470#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
471pub struct SvelteComponent {
472    pub start: usize,
473    pub end: usize,
474    pub name: Arc<str>,
475    pub name_loc: SourceRange,
476    pub attributes: Box<[Attribute]>,
477    pub fragment: Fragment,
478    #[serde(skip_serializing_if = "Option::is_none")]
479    pub expression: Option<Expression>,
480}
481
482#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
483pub struct SvelteElement {
484    pub start: usize,
485    pub end: usize,
486    pub name: Arc<str>,
487    pub name_loc: SourceRange,
488    pub attributes: Box<[Attribute]>,
489    pub fragment: Fragment,
490    #[serde(skip_serializing_if = "Option::is_none")]
491    pub expression: Option<Expression>,
492}
493
494#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
495pub struct SvelteSelf {
496    pub start: usize,
497    pub end: usize,
498    pub name: Arc<str>,
499    pub name_loc: SourceRange,
500    pub attributes: Box<[Attribute]>,
501    pub fragment: Fragment,
502}
503
504#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
505pub struct SvelteFragment {
506    pub start: usize,
507    pub end: usize,
508    pub name: Arc<str>,
509    pub name_loc: SourceRange,
510    pub attributes: Box<[Attribute]>,
511    pub fragment: Fragment,
512}
513
514#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
515pub struct SvelteBoundary {
516    pub start: usize,
517    pub end: usize,
518    pub name: Arc<str>,
519    pub name_loc: SourceRange,
520    pub attributes: Box<[Attribute]>,
521    pub fragment: Fragment,
522}
523
524#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
525pub struct TitleElement {
526    pub start: usize,
527    pub end: usize,
528    pub name: Arc<str>,
529    pub name_loc: SourceRange,
530    pub attributes: Box<[Attribute]>,
531    pub fragment: Fragment,
532}
533
534#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
535#[serde(tag = "type")]
536pub enum Attribute {
537    Attribute(NamedAttribute),
538    SpreadAttribute(SpreadAttribute),
539    BindDirective(DirectiveAttribute),
540    OnDirective(DirectiveAttribute),
541    ClassDirective(DirectiveAttribute),
542    LetDirective(DirectiveAttribute),
543    StyleDirective(StyleDirective),
544    TransitionDirective(TransitionDirective),
545    AnimateDirective(DirectiveAttribute),
546    UseDirective(DirectiveAttribute),
547    AttachTag(AttachTag),
548}
549
550#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
551pub struct SpreadAttribute {
552    pub start: usize,
553    pub end: usize,
554    pub expression: Expression,
555}
556
557#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
558pub struct NamedAttribute {
559    pub start: usize,
560    pub end: usize,
561    pub name: Arc<str>,
562    pub name_loc: SourceRange,
563    pub value: AttributeValueKind,
564    #[serde(skip_serializing, default)]
565    pub value_syntax: AttributeValueSyntax,
566    #[serde(skip_serializing, default)]
567    pub error: Option<AttrError>,
568}
569
570#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
571#[serde(untagged)]
572pub enum AttributeValueKind {
573    Boolean(bool),
574    Values(Box<[AttributeValue]>),
575    ExpressionTag(ExpressionTag),
576}
577
578#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
579#[serde(tag = "type")]
580pub enum AttributeValue {
581    Text(Text),
582    ExpressionTag(ExpressionTag),
583}
584
585#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
586pub struct DirectiveAttribute {
587    pub start: usize,
588    pub end: usize,
589    pub name: Arc<str>,
590    pub name_loc: SourceRange,
591    pub expression: Expression,
592    pub modifiers: Box<[Arc<str>]>,
593    #[serde(skip_serializing, default)]
594    pub value_syntax: DirectiveValueSyntax,
595    #[serde(skip_serializing, default)]
596    pub value_start: usize,
597}
598
599#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
600pub struct StyleDirective {
601    pub start: usize,
602    pub end: usize,
603    pub name: Arc<str>,
604    pub name_loc: SourceRange,
605    pub modifiers: Box<[Arc<str>]>,
606    pub value: AttributeValueKind,
607    #[serde(skip_serializing, default)]
608    pub value_syntax: AttributeValueSyntax,
609}
610
611#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
612pub struct TransitionDirective {
613    pub start: usize,
614    pub end: usize,
615    pub name: Arc<str>,
616    pub name_loc: SourceRange,
617    pub expression: Expression,
618    pub modifiers: Box<[Arc<str>]>,
619    pub intro: bool,
620    pub outro: bool,
621    #[serde(skip_serializing, default)]
622    pub value_syntax: DirectiveValueSyntax,
623    #[serde(skip_serializing, default)]
624    pub value_start: usize,
625}
626
627#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
628pub struct AttachTag {
629    pub start: usize,
630    pub end: usize,
631    pub expression: Expression,
632}
633
634#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
635pub struct IfBlock {
636    pub elseif: bool,
637    pub start: usize,
638    pub end: usize,
639    pub test: Expression,
640    pub consequent: Fragment,
641    pub alternate: Option<Box<Alternate>>,
642}
643
644#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
645#[serde(untagged)]
646pub enum Alternate {
647    Fragment(Fragment),
648    IfBlock(IfBlock),
649}
650
651#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
652pub struct ExpressionSyntax {
653    #[serde(skip_serializing, default)]
654    pub parens: u16,
655}
656
657#[derive(Debug, Clone, PartialEq, Eq)]
658pub enum JsNodeHandle {
659    Expression(Arc<JsExpression>),
660    SequenceItem {
661        root: Arc<JsExpression>,
662        index: usize,
663    },
664    Pattern(Arc<JsPattern>),
665    ParameterItem {
666        parameters: Arc<JsParameters>,
667        index: usize,
668    },
669    RestParameter(Arc<JsParameters>),
670    StatementInProgram {
671        program: Arc<JsProgram>,
672        index: usize,
673    },
674}
675
676/// A JS comment extracted from expression source text.
677#[derive(Debug, Clone, PartialEq, Eq)]
678pub struct JsComment {
679    pub kind: JsCommentKind,
680    pub value: Arc<str>,
681    /// Absolute source position (start). `None` for synthetic comments (e.g. HTML comments).
682    pub start: Option<usize>,
683    /// Absolute source position (end). `None` for synthetic comments.
684    pub end: Option<usize>,
685}
686
687#[derive(Debug, Clone, Copy, PartialEq, Eq)]
688pub enum JsCommentKind {
689    Line,
690    Block,
691}
692
693#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
694pub struct Expression {
695    pub start: usize,
696    pub end: usize,
697    #[serde(skip_serializing, default)]
698    pub syntax: ExpressionSyntax,
699    #[serde(skip_serializing, skip_deserializing, default)]
700    pub node: Option<JsNodeHandle>,
701    /// Pre-computed ESTree JSON with loc fields injected from the full source.
702    /// Set by `Root::enrich_expressions` after parsing.
703    #[serde(skip_serializing, skip_deserializing, default)]
704    pub enriched_json: Option<Arc<str>>,
705    /// Leading JS comments extracted from the expression source text.
706    #[serde(skip_serializing, skip_deserializing, default)]
707    pub leading_comments: Vec<JsComment>,
708    /// Trailing JS comments extracted from the expression source text.
709    #[serde(skip_serializing, skip_deserializing, default)]
710    pub trailing_comments: Vec<JsComment>,
711}
712
713impl Expression {
714    pub fn empty(start: usize, end: usize) -> Self {
715        Self {
716            start,
717            end,
718            syntax: Default::default(),
719            node: None,
720            enriched_json: None,
721            leading_comments: Vec::new(),
722            trailing_comments: Vec::new(),
723        }
724    }
725
726    /// Return `true` if this expression has no content (zero-length span, no node).
727    pub fn is_empty(&self) -> bool {
728        self.node.is_none() && self.start == self.end
729    }
730
731    pub fn from_expression(parsed: Arc<JsExpression>, start: usize, end: usize) -> Self {
732        Self {
733            start,
734            end,
735            syntax: Default::default(),
736            node: Some(JsNodeHandle::Expression(parsed)),
737            enriched_json: None,
738            leading_comments: Vec::new(),
739            trailing_comments: Vec::new(),
740        }
741    }
742
743    pub fn from_sequence_item(
744        root: Arc<JsExpression>,
745        index: usize,
746        start: usize,
747        end: usize,
748    ) -> Self {
749        Self {
750            start,
751            end,
752            syntax: Default::default(),
753            node: Some(JsNodeHandle::SequenceItem { root, index }),
754            enriched_json: None,
755            leading_comments: Vec::new(),
756            trailing_comments: Vec::new(),
757        }
758    }
759
760    pub fn from_pattern(parsed: Arc<JsPattern>, start: usize, end: usize) -> Self {
761        Self {
762            start,
763            end,
764            syntax: Default::default(),
765            node: Some(JsNodeHandle::Pattern(parsed)),
766            enriched_json: None,
767            leading_comments: Vec::new(),
768            trailing_comments: Vec::new(),
769        }
770    }
771
772    pub fn from_parameter_item(
773        parameters: Arc<JsParameters>,
774        index: usize,
775        start: usize,
776        end: usize,
777    ) -> Self {
778        Self {
779            start,
780            end,
781            syntax: Default::default(),
782            node: Some(JsNodeHandle::ParameterItem { parameters, index }),
783            enriched_json: None,
784            leading_comments: Vec::new(),
785            trailing_comments: Vec::new(),
786        }
787    }
788
789    pub fn from_rest_parameter(parameters: Arc<JsParameters>, start: usize, end: usize) -> Self {
790        Self {
791            start,
792            end,
793            syntax: Default::default(),
794            node: Some(JsNodeHandle::RestParameter(parameters)),
795            enriched_json: None,
796            leading_comments: Vec::new(),
797            trailing_comments: Vec::new(),
798        }
799    }
800
801    pub fn from_statement(
802        program: Arc<JsProgram>,
803        index: usize,
804        start: usize,
805        end: usize,
806    ) -> Self {
807        Self {
808            start,
809            end,
810            syntax: Default::default(),
811            node: Some(JsNodeHandle::StatementInProgram { program, index }),
812            enriched_json: None,
813            leading_comments: Vec::new(),
814            trailing_comments: Vec::new(),
815        }
816    }
817
818    pub fn parens(&self) -> u16 {
819        self.syntax.parens.max(self.outer_parens())
820    }
821
822    pub fn is_parenthesized(&self) -> bool {
823        self.parens() != 0
824    }
825
826    fn outer_parens(&self) -> u16 {
827        let mut parens = 0u16;
828        let mut current = match &self.node {
829            Some(JsNodeHandle::Expression(parsed)) => parsed.expression(),
830            Some(JsNodeHandle::SequenceItem { root, .. }) => root.expression(),
831            Some(JsNodeHandle::Pattern(_))
832            | Some(JsNodeHandle::ParameterItem { .. })
833            | Some(JsNodeHandle::RestParameter(_))
834            | Some(JsNodeHandle::StatementInProgram { .. })
835            | None => return 0,
836        };
837
838        while let OxcExpression::ParenthesizedExpression(parenthesized) = current {
839            parens = parens.saturating_add(1);
840            current = &parenthesized.expression;
841        }
842
843        parens
844    }
845
846    pub fn parsed(&self) -> Option<&JsExpression> {
847        match &self.node {
848            Some(JsNodeHandle::Expression(parsed)) => Some(parsed),
849            Some(JsNodeHandle::SequenceItem { root, .. }) => Some(root),
850            Some(JsNodeHandle::Pattern(_))
851            | Some(JsNodeHandle::ParameterItem { .. })
852            | Some(JsNodeHandle::RestParameter(_))
853            | Some(JsNodeHandle::StatementInProgram { .. })
854            | None => None,
855        }
856    }
857
858    pub fn oxc_expression_raw(&self) -> Option<&OxcExpression<'_>> {
859        match &self.node {
860            Some(JsNodeHandle::Expression(parsed)) => Some(parsed.expression()),
861            Some(JsNodeHandle::SequenceItem { root, index }) => {
862                let OxcExpression::SequenceExpression(sequence) = root.expression() else {
863                    return None;
864                };
865                sequence.expressions.get(*index)
866            }
867            Some(JsNodeHandle::Pattern(_))
868            | Some(JsNodeHandle::ParameterItem { .. })
869            | Some(JsNodeHandle::RestParameter(_))
870            | Some(JsNodeHandle::StatementInProgram { .. })
871            | None => None,
872        }
873    }
874
875    pub fn oxc_expression(&self) -> Option<&OxcExpression<'_>> {
876        let mut expression = self.oxc_expression_raw()?;
877
878        while let OxcExpression::ParenthesizedExpression(parenthesized) = expression {
879            expression = &parenthesized.expression;
880        }
881
882        Some(expression)
883    }
884
885    /// Returns `true` if this expression represents a destructured pattern
886    /// (ObjectPattern or ArrayPattern).
887    pub fn is_destructured_pattern(&self) -> bool {
888        self.oxc_pattern().is_some_and(|p| {
889            matches!(
890                p,
891                BindingPattern::ObjectPattern(_) | BindingPattern::ArrayPattern(_)
892            )
893        })
894    }
895
896    pub fn oxc_pattern(&self) -> Option<&BindingPattern<'_>> {
897        match &self.node {
898            Some(JsNodeHandle::Pattern(parsed)) => Some(parsed.pattern()),
899            Some(JsNodeHandle::ParameterItem { parameters, index }) => {
900                Some(&parameters.parameter(*index)?.pattern)
901            }
902            Some(JsNodeHandle::RestParameter(parameters)) => {
903                Some(&parameters.rest_parameter()?.rest.argument)
904            }
905            _ => None,
906        }
907    }
908
909    pub fn oxc_parameter(&self) -> Option<&FormalParameter<'_>> {
910        match &self.node {
911            Some(JsNodeHandle::ParameterItem { parameters, index }) => parameters.parameter(*index),
912            _ => None,
913        }
914    }
915
916    pub fn oxc_rest_parameter(&self) -> Option<&FormalParameterRest<'_>> {
917        match &self.node {
918            Some(JsNodeHandle::RestParameter(parameters)) => parameters.rest_parameter(),
919            _ => None,
920        }
921    }
922
923    pub fn oxc_statement(&self) -> Option<&OxcStatement<'_>> {
924        match &self.node {
925            Some(JsNodeHandle::StatementInProgram { program, index }) => program.statement(*index),
926            _ => None,
927        }
928    }
929
930    pub fn oxc_variable_declaration(&self) -> Option<&OxcVariableDeclaration<'_>> {
931        match &self.node {
932            Some(JsNodeHandle::StatementInProgram { program, index }) => {
933                program.variable_declaration(*index)
934            }
935            _ => None,
936        }
937    }
938
939    pub fn is_rest_parameter(&self) -> bool {
940        matches!(self.node, Some(JsNodeHandle::RestParameter(_)))
941    }
942
943    pub fn source_snippet(&self) -> Option<&str> {
944        match &self.node {
945            Some(JsNodeHandle::Expression(parsed)) => Some(parsed.source()),
946            Some(JsNodeHandle::SequenceItem { root, index }) => {
947                let OxcExpression::SequenceExpression(sequence) = root.expression() else {
948                    return None;
949                };
950                let node = sequence.expressions.get(*index)?;
951                root.source().get(node.span().start as usize..node.span().end as usize)
952            }
953            Some(JsNodeHandle::Pattern(parsed)) => Some(parsed.source()),
954            Some(JsNodeHandle::ParameterItem { parameters, index }) => {
955                let parameter = parameters.parameter(*index)?;
956                parameters
957                    .source()
958                    .get(parameter.span.start as usize - 1..parameter.span.end as usize - 1)
959            }
960            Some(JsNodeHandle::RestParameter(parameters)) => {
961                let parameter = parameters.rest_parameter()?;
962                parameters
963                    .source()
964                    .get(parameter.span.start as usize - 1..parameter.span.end as usize - 1)
965            }
966            Some(JsNodeHandle::StatementInProgram { program, index }) => program.statement_source(*index),
967            None => None,
968        }
969    }
970
971    pub fn identifier_name(&self) -> Option<Arc<str>> {
972        if let Some(identifier) = self
973            .oxc_expression()
974            .and_then(OxcExpression::get_identifier_reference)
975        {
976            return Some(Arc::from(identifier.name.as_str()));
977        }
978
979        match self.oxc_pattern()? {
980            BindingPattern::BindingIdentifier(identifier) => {
981                Some(Arc::from(identifier.name.as_str()))
982            }
983            _ => None,
984        }
985    }
986
987    pub fn literal_string(&self) -> Option<Arc<str>> {
988        match self.oxc_expression()? {
989            OxcExpression::StringLiteral(value) => Some(Arc::from(value.value.as_str())),
990            _ => None,
991        }
992    }
993
994    pub fn literal_bool(&self) -> Option<bool> {
995        match self.oxc_expression()? {
996            OxcExpression::BooleanLiteral(value) => Some(value.value),
997            _ => None,
998        }
999    }
1000
1001    pub fn binding_identifier(&self) -> Option<&BindingIdentifier<'_>> {
1002        if let Some(declaration) = self.oxc_variable_declaration() {
1003            let [declarator] = declaration.declarations.as_slice() else {
1004                return None;
1005            };
1006            return declarator.id.get_binding_identifier();
1007        }
1008
1009        match self.oxc_pattern()? {
1010            BindingPattern::BindingIdentifier(identifier) => Some(identifier),
1011            _ => None,
1012        }
1013    }
1014}
1015
1016impl Span for Expression {
1017    fn start(&self) -> usize {
1018        self.start
1019    }
1020
1021    fn end(&self) -> usize {
1022        self.end
1023    }
1024}
1025
1026impl Serialize for Expression {
1027    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1028        // Prefer enriched JSON (with loc fields) if available.
1029        if let Some(ref enriched) = self.enriched_json {
1030            let raw = serde_json::value::RawValue::from_string(enriched.to_string())
1031                .map_err(serde::ser::Error::custom)?;
1032            return raw.serialize(serializer);
1033        }
1034        if let Some(raw_json) = self.to_estree_json() {
1035            // Embed pre-serialized JSON directly via RawValue — no re-parsing.
1036            let raw = serde_json::value::RawValue::from_string(raw_json)
1037                .map_err(serde::ser::Error::custom)?;
1038            raw.serialize(serializer)
1039        } else {
1040            // Fallback: emit empty Identifier when no OXC node is available.
1041            // Upstream produces {type: "Identifier", name: "", start, end} for
1042            // broken or empty expressions in loose mode.
1043            let mut map = serializer.serialize_map(Some(4))?;
1044            map.serialize_entry("type", "Identifier")?;
1045            map.serialize_entry("name", "")?;
1046            map.serialize_entry("start", &self.start)?;
1047            map.serialize_entry("end", &self.end)?;
1048            map.end()
1049        }
1050    }
1051}
1052
1053impl Expression {
1054    /// Compute and store the enriched ESTree JSON with `loc` fields.
1055    /// Uses the full source to compute line/column information.
1056    /// `column_offset` is added to loc columns for nodes not on line 1
1057    /// (used for destructured patterns to match upstream's wrapping behavior).
1058    pub fn enrich_with_source(&mut self, full_source: &str) {
1059        self.enrich_inner(full_source, 0, false);
1060    }
1061
1062    pub fn enrich_with_source_and_column_offset(
1063        &mut self,
1064        full_source: &str,
1065        column_offset: usize,
1066    ) {
1067        self.enrich_inner(full_source, column_offset, false);
1068    }
1069
1070    /// Like `enrich_with_source` but adds `character` (byte offset) to loc objects.
1071    /// Used for SnippetBlock expression names where upstream includes character.
1072    pub fn enrich_with_character(&mut self, full_source: &str) {
1073        self.enrich_inner(full_source, 0, true);
1074    }
1075
1076    fn enrich_inner(
1077        &mut self,
1078        full_source: &str,
1079        column_offset: usize,
1080        with_character: bool,
1081    ) {
1082        let Some(raw_json) = self.serialize_oxc_node() else {
1083            // For empty expressions (no OXC node), we can still produce enriched JSON
1084            // with loc fields if character mode is requested (loose mode).
1085            if with_character {
1086                self.enrich_empty_with_character(full_source);
1087            }
1088            return;
1089        };
1090        let offset = self.oxc_span_offset();
1091        let Ok(mut value) = serde_json::from_str::<serde_json::Value>(&raw_json) else {
1092            return;
1093        };
1094        // Fix TemplateElement spans for TS-serialized expressions.
1095        // Only Expression handles use the TS serializer; Pattern/Statement use JS.
1096        if self.uses_ts_serializer() {
1097            crate::js::fix_template_element_spans(&mut value);
1098        }
1099        if with_character {
1100            crate::js::adjust_expression_json_with_character(
1101                &mut value,
1102                full_source,
1103                offset,
1104            );
1105        } else {
1106            crate::js::adjust_expression_json_with_column_offset(
1107                &mut value,
1108                full_source,
1109                offset,
1110                column_offset,
1111            );
1112        }
1113        // Inject leadingComments if present
1114        if !self.leading_comments.is_empty() {
1115            if let serde_json::Value::Object(ref mut map) = value {
1116                map.insert(
1117                    "leadingComments".to_string(),
1118                    serde_json::Value::Array(crate::estree::make_comment_json(&self.leading_comments)),
1119                );
1120            }
1121        }
1122        // Inject trailingComments if present
1123        if !self.trailing_comments.is_empty() {
1124            if let serde_json::Value::Object(ref mut map) = value {
1125                map.insert(
1126                    "trailingComments".to_string(),
1127                    serde_json::Value::Array(crate::estree::make_comment_json(&self.trailing_comments)),
1128                );
1129            }
1130        }
1131        // Attach internal comments (comments within function bodies, etc.)
1132        let internal_comments = self.extract_internal_comments();
1133        if !internal_comments.is_empty() {
1134            crate::js::attach_comments_to_json_tree(&mut value, &internal_comments, full_source);
1135        }
1136        if let Ok(enriched) = serde_json::to_string(&value) {
1137            self.enriched_json = Some(Arc::from(enriched));
1138        }
1139    }
1140
1141    /// Check if this expression uses the TS serializer (for TemplateElement span fixing).
1142    fn uses_ts_serializer(&self) -> bool {
1143        matches!(
1144            &self.node,
1145            Some(JsNodeHandle::Expression(_))
1146                | Some(JsNodeHandle::SequenceItem { .. })
1147                | Some(JsNodeHandle::ParameterItem { .. })
1148                | Some(JsNodeHandle::RestParameter(_))
1149                | Some(JsNodeHandle::StatementInProgram { .. })
1150        )
1151    }
1152
1153    /// Generate enriched JSON for an empty expression (no OXC node) with loc+character.
1154    fn enrich_empty_with_character(&mut self, full_source: &str) {
1155        let start = self.start;
1156        let end = self.end;
1157        let (sl, sc) = crate::line_column_at_offset(full_source, start);
1158        let (el, ec) = crate::line_column_at_offset(full_source, end);
1159        let json = format!(
1160            r#"{{"type":"Identifier","name":"","start":{},"end":{},"loc":{{"start":{{"line":{},"column":{},"character":{}}},"end":{{"line":{},"column":{},"character":{}}}}}}}"#,
1161            start, end, sl, sc, start, el, ec, end
1162        );
1163        self.enriched_json = Some(Arc::from(json));
1164    }
1165
1166    /// Serialize this expression to an ESTree JSON string using OXC's serializer,
1167    /// with span offsets adjusted from OXC-local to Svelte source coordinates.
1168    /// Single-pass string-level adjustment, no intermediate tree parsing.
1169    ///
1170    /// Returns `None` if no OXC node handle is attached.
1171    pub fn to_estree_json(&self) -> Option<String> {
1172        let json = self.serialize_oxc_node()?;
1173        let offset = self.oxc_span_offset();
1174        if offset == 0 {
1175            Some(json)
1176        } else {
1177            Some(adjust_estree_span_offsets(&json, offset))
1178        }
1179    }
1180
1181    /// Serialize the underlying OXC node to a JSON string via oxc_estree.
1182    fn serialize_oxc_node(&self) -> Option<String> {
1183        use oxc_estree::ESTree;
1184
1185        match &self.node {
1186            Some(JsNodeHandle::Expression(parsed)) => {
1187                let mut ser = oxc_estree::CompactTSSerializer::new(false);
1188                serialize_oxc_expression_unwrapped_ts(parsed.expression(), &mut ser);
1189                Some(ser.into_string())
1190            }
1191            Some(JsNodeHandle::SequenceItem { root, index }) => {
1192                let OxcExpression::SequenceExpression(seq) = root.expression() else {
1193                    return None;
1194                };
1195                let expr = seq.expressions.get(*index)?;
1196                let mut ser = oxc_estree::CompactTSSerializer::new(false);
1197                expr.serialize(&mut ser);
1198                Some(ser.into_string())
1199            }
1200            Some(JsNodeHandle::Pattern(parsed)) => {
1201                let mut ser = oxc_estree::CompactJSSerializer::new(false);
1202                parsed.pattern().serialize(&mut ser);
1203                Some(ser.into_string())
1204            }
1205            Some(JsNodeHandle::ParameterItem { parameters, index }) => {
1206                let param = parameters.parameter(*index)?;
1207                // Use TS serializer so typeAnnotation is included
1208                let mut ser = oxc_estree::CompactTSSerializer::new(false);
1209                param.serialize(&mut ser);
1210                Some(ser.into_string())
1211            }
1212            Some(JsNodeHandle::RestParameter(parameters)) => {
1213                let rest = parameters.rest_parameter()?;
1214                // Use TS serializer so typeAnnotation is included
1215                let mut ser = oxc_estree::CompactTSSerializer::new(false);
1216                rest.serialize(&mut ser);
1217                Some(ser.into_string())
1218            }
1219            Some(JsNodeHandle::StatementInProgram { program, index }) => {
1220                let stmt = program.statement(*index)?;
1221                let mut ser = oxc_estree::CompactTSSerializer::new(false);
1222                stmt.serialize(&mut ser);
1223                Some(ser.into_string())
1224            }
1225            None => None,
1226        }
1227    }
1228
1229    /// Compute the offset to add to OXC span values to get Svelte source positions.
1230    fn oxc_span_offset(&self) -> i64 {
1231        match &self.node {
1232            Some(JsNodeHandle::Expression(parsed)) => {
1233                self.start as i64 - parsed.expression().span().start as i64
1234            }
1235            Some(JsNodeHandle::SequenceItem { root, index }) => {
1236                if let OxcExpression::SequenceExpression(seq) = root.expression()
1237                    && let Some(expr) = seq.expressions.get(*index)
1238                {
1239                    return self.start as i64 - expr.span().start as i64;
1240                }
1241                0
1242            }
1243            Some(JsNodeHandle::Pattern(parsed)) => {
1244                self.start as i64 - parsed.pattern().span().start as i64
1245            }
1246            Some(JsNodeHandle::ParameterItem { parameters, index }) => {
1247                if let Some(param) = parameters.parameter(*index) {
1248                    return self.start as i64 - param.span.start as i64;
1249                }
1250                0
1251            }
1252            Some(JsNodeHandle::RestParameter(parameters)) => {
1253                if let Some(rest) = parameters.rest_parameter() {
1254                    return self.start as i64 - rest.span.start as i64;
1255                }
1256                0
1257            }
1258            Some(JsNodeHandle::StatementInProgram { program, index }) => {
1259                if let Some(stmt) = program.statement(*index) {
1260                    return self.start as i64 - stmt.span().start as i64;
1261                }
1262                0
1263            }
1264            None => 0,
1265        }
1266    }
1267}
1268
1269impl Expression {
1270    /// Scan the expression's source text and extract all internal JS comments.
1271    /// Returns (absolute_start, absolute_end, json_value) tuples suitable for
1272    /// `attach_comments_to_json_tree`.
1273    pub fn extract_internal_comments(&self) -> Vec<(u32, u32, serde_json::Value)> {
1274        let source = match &self.node {
1275            Some(JsNodeHandle::Expression(parsed)) => parsed.source(),
1276            _ => return Vec::new(),
1277        };
1278        let offset = self.oxc_span_offset();
1279        let mut comments = Vec::new();
1280        let bytes = source.as_bytes();
1281        let mut i = 0;
1282
1283        while i < bytes.len() {
1284            match bytes[i] {
1285                b'/' if i + 1 < bytes.len() && bytes[i + 1] == b'/' => {
1286                    let start = i;
1287                    i += 2;
1288                    let value_start = i;
1289                    while i < bytes.len() && bytes[i] != b'\n' {
1290                        i += 1;
1291                    }
1292                    let value = &source[value_start..i];
1293                    let abs_start = (start as i64 + offset) as u32;
1294                    let abs_end = (i as i64 + offset) as u32;
1295                    comments.push((
1296                        abs_start,
1297                        abs_end,
1298                        crate::estree::EstreeComment {
1299                            kind: crate::estree::EstreeCommentKind::Line,
1300                            value: value.to_string(),
1301                            start: Some(abs_start as usize),
1302                            end: Some(abs_end as usize),
1303                        }
1304                        .to_json_value(),
1305                    ));
1306                    if i < bytes.len() {
1307                        i += 1;
1308                    }
1309                }
1310                b'/' if i + 1 < bytes.len() && bytes[i + 1] == b'*' => {
1311                    let start = i;
1312                    i += 2;
1313                    let value_start = i;
1314                    while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1315                        i += 1;
1316                    }
1317                    let value_end = i;
1318                    if i + 1 < bytes.len() {
1319                        i += 2;
1320                    } else {
1321                        i = bytes.len();
1322                    }
1323                    let value = &source[value_start..value_end];
1324                    let abs_start = (start as i64 + offset) as u32;
1325                    let abs_end = (i as i64 + offset) as u32;
1326                    comments.push((
1327                        abs_start,
1328                        abs_end,
1329                        crate::estree::EstreeComment {
1330                            kind: crate::estree::EstreeCommentKind::Block,
1331                            value: value.to_string(),
1332                            start: Some(abs_start as usize),
1333                            end: Some(abs_end as usize),
1334                        }
1335                        .to_json_value(),
1336                    ));
1337                }
1338                b'"' => {
1339                    // Skip string literals
1340                    i += 1;
1341                    while i < bytes.len() && bytes[i] != b'"' {
1342                        if bytes[i] == b'\\' {
1343                            i += 1;
1344                        }
1345                        i += 1;
1346                    }
1347                    if i < bytes.len() {
1348                        i += 1;
1349                    }
1350                }
1351                b'\'' => {
1352                    i += 1;
1353                    while i < bytes.len() && bytes[i] != b'\'' {
1354                        if bytes[i] == b'\\' {
1355                            i += 1;
1356                        }
1357                        i += 1;
1358                    }
1359                    if i < bytes.len() {
1360                        i += 1;
1361                    }
1362                }
1363                b'`' => {
1364                    i += 1;
1365                    while i < bytes.len() && bytes[i] != b'`' {
1366                        if bytes[i] == b'\\' {
1367                            i += 1;
1368                        }
1369                        i += 1;
1370                    }
1371                    if i < bytes.len() {
1372                        i += 1;
1373                    }
1374                }
1375                _ => {
1376                    i += 1;
1377                }
1378            }
1379        }
1380
1381        comments
1382    }
1383}
1384
1385/// Serialize an OXC expression, unwrapping ParenthesizedExpression nodes.
1386/// Svelte tracks parens via `Expression.syntax.parens`, not in the AST.
1387/// Uses TS serializer to emit typeAnnotation on params/identifiers.
1388fn serialize_oxc_expression_unwrapped_ts(
1389    expr: &OxcExpression<'_>,
1390    ser: &mut oxc_estree::CompactTSSerializer,
1391) {
1392    use oxc_estree::ESTree;
1393    let mut inner = expr;
1394    while let OxcExpression::ParenthesizedExpression(paren) = inner {
1395        inner = &paren.expression;
1396    }
1397    inner.serialize(ser);
1398}
1399
1400
1401/// Single-pass adjustment of `"start":N` and `"end":N` span values in a JSON string.
1402///
1403/// In ESTree JSON from oxc_estree, these keys with numeric values always represent
1404/// byte-position spans. This is safe because string values containing `"start":` are
1405/// escaped (`\"start\":`) in JSON, so the pattern cannot appear inside string values.
1406fn adjust_estree_span_offsets(json: &str, offset: i64) -> String {
1407    let bytes = json.as_bytes();
1408    let len = bytes.len();
1409    let mut result = String::with_capacity(len + 64);
1410    let mut i = 0;
1411
1412    while i < len {
1413        let remaining = &bytes[i..];
1414
1415        let key_len = if remaining.starts_with(b"\"start\":") {
1416            8 // "start":
1417        } else if remaining.starts_with(b"\"end\":") {
1418            6 // "end":
1419        } else {
1420            0
1421        };
1422
1423        if key_len > 0 {
1424            // Verify preceding char is a struct delimiter (not inside a string value)
1425            let before_ok = i == 0 || matches!(bytes[i - 1], b',' | b'{' | b'\n' | b' ');
1426            let num_start = i + key_len;
1427
1428            if before_ok && num_start < len && bytes[num_start].is_ascii_digit() {
1429                let mut num_end = num_start;
1430                while num_end < len && bytes[num_end].is_ascii_digit() {
1431                    num_end += 1;
1432                }
1433
1434                if let Ok(val) = json[num_start..num_end].parse::<i64>() {
1435                    let adjusted = (val + offset).max(0) as u64;
1436                    result.push_str(&json[i..i + key_len]);
1437                    result.push_str(&adjusted.to_string());
1438                    i = num_end;
1439                    continue;
1440                }
1441            }
1442        }
1443
1444        // Copy single byte (ASCII in JSON keys/structure)
1445        result.push(bytes[i] as char);
1446        i += 1;
1447    }
1448
1449    result
1450}
1451
1452#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1453pub struct Identifier {
1454    pub start: usize,
1455    pub end: usize,
1456    #[serde(skip_serializing_if = "Option::is_none")]
1457    pub loc: Option<Loc>,
1458    pub name: Arc<str>,
1459}
1460
1461#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1462pub struct Literal {
1463    pub start: usize,
1464    pub end: usize,
1465    #[serde(skip_serializing_if = "Option::is_none")]
1466    pub loc: Option<Loc>,
1467    pub value: LiteralValue,
1468    pub raw: Arc<str>,
1469}
1470
1471#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1472pub struct BinaryExpression {
1473    pub start: usize,
1474    pub end: usize,
1475    #[serde(skip_serializing_if = "Option::is_none")]
1476    pub loc: Option<Loc>,
1477    pub left: Box<Expression>,
1478    pub operator: Arc<str>,
1479    pub right: Box<Expression>,
1480}
1481
1482#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1483pub struct CallExpression {
1484    pub start: usize,
1485    pub end: usize,
1486    #[serde(skip_serializing_if = "Option::is_none")]
1487    pub loc: Option<Loc>,
1488    pub callee: Box<Expression>,
1489    pub arguments: Box<[Expression]>,
1490    pub optional: bool,
1491}
1492
1493#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1494pub struct Text {
1495    pub start: usize,
1496    pub end: usize,
1497    pub raw: Arc<str>,
1498    pub data: Arc<str>,
1499}
1500
1501#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1502pub struct Css {
1503    pub r#type: CssType,
1504    pub start: usize,
1505    pub end: usize,
1506    pub attributes: Box<[Attribute]>,
1507    pub children: Box<[CssNode]>,
1508    pub content: CssContent,
1509}
1510
1511#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1512pub enum CssType {
1513    StyleSheet,
1514}
1515
1516#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1517pub struct CssContent {
1518    pub start: usize,
1519    pub end: usize,
1520    pub styles: Arc<str>,
1521    pub comment: Option<Arc<str>>,
1522}
1523
1524#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1525#[serde(tag = "type")]
1526pub enum CssNode {
1527    Rule(CssRule),
1528    Atrule(CssAtrule),
1529}
1530
1531#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1532pub struct CssRule {
1533    pub prelude: CssSelectorList,
1534    pub block: CssBlock,
1535    pub start: usize,
1536    pub end: usize,
1537}
1538
1539#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1540pub struct CssAtrule {
1541    pub start: usize,
1542    pub end: usize,
1543    pub name: Arc<str>,
1544    pub prelude: Arc<str>,
1545    pub block: Option<CssBlock>,
1546}
1547
1548#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1549pub struct CssBlock {
1550    pub r#type: CssBlockType,
1551    pub start: usize,
1552    pub end: usize,
1553    pub children: Box<[CssBlockChild]>,
1554}
1555
1556#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1557pub enum CssBlockType {
1558    Block,
1559}
1560
1561#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1562#[serde(tag = "type")]
1563pub enum CssBlockChild {
1564    Declaration(CssDeclaration),
1565    Rule(CssRule),
1566    Atrule(CssAtrule),
1567}
1568
1569#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1570pub struct CssDeclaration {
1571    pub start: usize,
1572    pub end: usize,
1573    pub property: Arc<str>,
1574    pub value: Arc<str>,
1575}
1576
1577#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1578pub struct CssSelectorList {
1579    pub r#type: CssSelectorListType,
1580    pub start: usize,
1581    pub end: usize,
1582    pub children: Box<[CssComplexSelector]>,
1583}
1584
1585#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1586pub enum CssSelectorListType {
1587    SelectorList,
1588}
1589
1590#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1591pub struct CssComplexSelector {
1592    pub r#type: CssComplexSelectorType,
1593    pub start: usize,
1594    pub end: usize,
1595    pub children: Box<[CssRelativeSelector]>,
1596}
1597
1598#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1599pub enum CssComplexSelectorType {
1600    ComplexSelector,
1601}
1602
1603#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1604pub struct CssRelativeSelector {
1605    pub r#type: CssRelativeSelectorType,
1606    pub combinator: Option<CssCombinator>,
1607    pub selectors: Box<[CssSimpleSelector]>,
1608    pub start: usize,
1609    pub end: usize,
1610}
1611
1612#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1613pub enum CssRelativeSelectorType {
1614    RelativeSelector,
1615}
1616
1617#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1618pub struct CssCombinator {
1619    pub r#type: CssCombinatorType,
1620    pub name: Arc<str>,
1621    pub start: usize,
1622    pub end: usize,
1623}
1624
1625#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1626pub enum CssCombinatorType {
1627    Combinator,
1628}
1629
1630#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1631#[serde(tag = "type")]
1632pub enum CssSimpleSelector {
1633    TypeSelector(CssNameSelector),
1634    IdSelector(CssNameSelector),
1635    ClassSelector(CssNameSelector),
1636    PseudoElementSelector(CssNameSelector),
1637    PseudoClassSelector(CssPseudoClassSelector),
1638    AttributeSelector(CssAttributeSelector),
1639    Nth(CssValueSelector),
1640    Percentage(CssValueSelector),
1641    NestingSelector(CssNameSelector),
1642}
1643
1644#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1645pub struct CssNameSelector {
1646    pub name: Arc<str>,
1647    pub start: usize,
1648    pub end: usize,
1649}
1650
1651#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1652pub struct CssValueSelector {
1653    pub value: Arc<str>,
1654    pub start: usize,
1655    pub end: usize,
1656}
1657
1658#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1659pub struct CssPseudoClassSelector {
1660    pub name: Arc<str>,
1661    pub args: Option<CssSelectorList>,
1662    pub start: usize,
1663    pub end: usize,
1664}
1665
1666#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1667pub struct CssAttributeSelector {
1668    pub start: usize,
1669    pub end: usize,
1670    pub name: Arc<str>,
1671    pub matcher: Option<Arc<str>>,
1672    pub value: Option<Arc<str>>,
1673    pub flags: Option<Arc<str>>,
1674}
1675
1676#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1677pub struct Options {
1678    pub start: usize,
1679    pub end: usize,
1680    pub attributes: Box<[Attribute]>,
1681    #[serde(skip_serializing, skip_deserializing, default)]
1682    pub fragment: Fragment,
1683    #[serde(skip_serializing_if = "Option::is_none")]
1684    #[serde(rename = "customElement")]
1685    pub custom_element: Option<CustomElement>,
1686    #[serde(skip_serializing_if = "Option::is_none")]
1687    pub runes: Option<bool>,
1688}
1689
1690#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1691pub struct CustomElement {
1692    pub tag: Arc<str>,
1693}
1694
1695#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1696#[serde(tag = "type")]
1697pub enum Node {
1698    Text(Text),
1699    IfBlock(IfBlock),
1700    EachBlock(EachBlock),
1701    KeyBlock(KeyBlock),
1702    AwaitBlock(AwaitBlock),
1703    SnippetBlock(SnippetBlock),
1704    RenderTag(RenderTag),
1705    HtmlTag(HtmlTag),
1706    ConstTag(ConstTag),
1707    DebugTag(DebugTag),
1708    ExpressionTag(ExpressionTag),
1709    Comment(Comment),
1710    RegularElement(RegularElement),
1711    Component(Component),
1712    SlotElement(SlotElement),
1713    SvelteHead(SvelteHead),
1714    SvelteBody(SvelteBody),
1715    SvelteWindow(SvelteWindow),
1716    SvelteDocument(SvelteDocument),
1717    SvelteComponent(SvelteComponent),
1718    SvelteElement(SvelteElement),
1719    SvelteSelf(SvelteSelf),
1720    SvelteFragment(SvelteFragment),
1721    SvelteBoundary(SvelteBoundary),
1722    TitleElement(TitleElement),
1723}
1724
1725pub trait HasFragment {
1726    fn fragment(&self) -> &Fragment;
1727}
1728
1729pub trait Element: Span + HasFragment {
1730    fn name(&self) -> &str;
1731    fn name_loc(&self) -> &SourceRange;
1732    fn attributes(&self) -> &[Attribute];
1733    fn expression(&self) -> Option<&Expression> {
1734        None
1735    }
1736    fn self_closing(&self) -> bool {
1737        false
1738    }
1739}
1740
1741macro_rules! impl_element {
1742    ($($ty:ty),* $(,)?) => {
1743        $(
1744            impl HasFragment for $ty {
1745                fn fragment(&self) -> &Fragment { &self.fragment }
1746            }
1747
1748            impl Element for $ty {
1749                fn name(&self) -> &str { &self.name }
1750                fn name_loc(&self) -> &SourceRange { &self.name_loc }
1751                fn attributes(&self) -> &[Attribute] { &self.attributes }
1752            }
1753        )*
1754    };
1755}
1756
1757impl_element!(
1758    Component,
1759    SlotElement,
1760    SvelteHead,
1761    SvelteBody,
1762    SvelteWindow,
1763    SvelteDocument,
1764    SvelteSelf,
1765    SvelteFragment,
1766    SvelteBoundary,
1767    TitleElement,
1768);
1769
1770impl Element for RegularElement {
1771    fn name(&self) -> &str {
1772        &self.name
1773    }
1774    fn name_loc(&self) -> &SourceRange {
1775        &self.name_loc
1776    }
1777    fn attributes(&self) -> &[Attribute] {
1778        &self.attributes
1779    }
1780    fn self_closing(&self) -> bool {
1781        self.self_closing
1782    }
1783}
1784
1785impl HasFragment for RegularElement {
1786    fn fragment(&self) -> &Fragment {
1787        &self.fragment
1788    }
1789}
1790
1791impl HasFragment for SvelteComponent {
1792    fn fragment(&self) -> &Fragment {
1793        &self.fragment
1794    }
1795}
1796
1797impl Element for SvelteComponent {
1798    fn name(&self) -> &str {
1799        &self.name
1800    }
1801    fn name_loc(&self) -> &SourceRange {
1802        &self.name_loc
1803    }
1804    fn attributes(&self) -> &[Attribute] {
1805        &self.attributes
1806    }
1807    fn expression(&self) -> Option<&Expression> {
1808        self.expression.as_ref()
1809    }
1810}
1811
1812impl HasFragment for SvelteElement {
1813    fn fragment(&self) -> &Fragment {
1814        &self.fragment
1815    }
1816}
1817
1818impl Element for SvelteElement {
1819    fn name(&self) -> &str {
1820        &self.name
1821    }
1822    fn name_loc(&self) -> &SourceRange {
1823        &self.name_loc
1824    }
1825    fn attributes(&self) -> &[Attribute] {
1826        &self.attributes
1827    }
1828    fn expression(&self) -> Option<&Expression> {
1829        self.expression.as_ref()
1830    }
1831}
1832
1833impl HasFragment for Root {
1834    fn fragment(&self) -> &Fragment {
1835        &self.fragment
1836    }
1837}
1838
1839impl HasFragment for KeyBlock {
1840    fn fragment(&self) -> &Fragment {
1841        &self.fragment
1842    }
1843}
1844
1845impl HasFragment for SnippetBlock {
1846    fn fragment(&self) -> &Fragment {
1847        &self.body
1848    }
1849}
1850
1851impl Alternate {
1852    pub fn try_for_each_fragment<B>(
1853        &self,
1854        mut visit: impl FnMut(&Fragment) -> ControlFlow<B>,
1855    ) -> ControlFlow<B> {
1856        match self {
1857            Self::Fragment(fragment) => visit(fragment),
1858            Self::IfBlock(block) => {
1859                visit(&block.consequent)?;
1860                if let Some(alternate) = block.alternate.as_deref() {
1861                    alternate.try_for_each_fragment(visit)
1862                } else {
1863                    ControlFlow::Continue(())
1864                }
1865            }
1866        }
1867    }
1868
1869    pub fn for_each_fragment(&self, mut visit: impl FnMut(&Fragment)) {
1870        let _ = self.try_for_each_fragment(|fragment| {
1871            visit(fragment);
1872            ControlFlow::<()>::Continue(())
1873        });
1874    }
1875}
1876
1877impl Node {
1878    pub fn as_element(&self) -> Option<&dyn Element> {
1879        match self {
1880            Node::RegularElement(el) => Some(el),
1881            Node::Component(el) => Some(el),
1882            Node::SlotElement(el) => Some(el),
1883            Node::SvelteHead(el) => Some(el),
1884            Node::SvelteBody(el) => Some(el),
1885            Node::SvelteWindow(el) => Some(el),
1886            Node::SvelteDocument(el) => Some(el),
1887            Node::SvelteComponent(el) => Some(el),
1888            Node::SvelteElement(el) => Some(el),
1889            Node::SvelteSelf(el) => Some(el),
1890            Node::SvelteFragment(el) => Some(el),
1891            Node::SvelteBoundary(el) => Some(el),
1892            Node::TitleElement(el) => Some(el),
1893            _ => None,
1894        }
1895    }
1896
1897    pub fn try_for_each_child_fragment<B>(
1898        &self,
1899        mut visit: impl FnMut(&Fragment) -> ControlFlow<B>,
1900    ) -> ControlFlow<B> {
1901        match self {
1902            Self::IfBlock(block) => {
1903                visit(&block.consequent)?;
1904                if let Some(alternate) = block.alternate.as_deref() {
1905                    alternate.try_for_each_fragment(visit)
1906                } else {
1907                    ControlFlow::Continue(())
1908                }
1909            }
1910            Self::EachBlock(block) => {
1911                visit(&block.body)?;
1912                if let Some(fallback) = block.fallback.as_ref() {
1913                    visit(fallback)?;
1914                }
1915                ControlFlow::Continue(())
1916            }
1917            Self::KeyBlock(block) => visit(block.fragment()),
1918            Self::AwaitBlock(block) => {
1919                for fragment in [
1920                    block.pending.as_ref(),
1921                    block.then.as_ref(),
1922                    block.catch.as_ref(),
1923                ]
1924                .into_iter()
1925                .flatten()
1926                {
1927                    visit(fragment)?;
1928                }
1929                ControlFlow::Continue(())
1930            }
1931            Self::SnippetBlock(block) => visit(block.fragment()),
1932            _ => self
1933                .as_element()
1934                .map_or(ControlFlow::Continue(()), |element| {
1935                    visit(element.fragment())
1936                }),
1937        }
1938    }
1939
1940    pub fn for_each_child_fragment(&self, mut visit: impl FnMut(&Fragment)) {
1941        let _ = self.try_for_each_child_fragment(|fragment| {
1942            visit(fragment);
1943            ControlFlow::<()>::Continue(())
1944        });
1945    }
1946}
1947
1948/// Visitor trait for mutable AST walks.
1949///
1950/// Default implementations recurse into children. Override `visit_node` or
1951/// `visit_attribute` for per-variant customisation, calling `walk_node_children`
1952/// / `walk_attribute_expressions` for the default recursion.
1953pub trait NodeVisitorMut {
1954    /// Called for every `Expression` leaf.
1955    fn visit_expression(&mut self, expr: &mut Expression);
1956
1957    /// Called for every `Node`. Default: recurse into children.
1958    fn visit_node(&mut self, node: &mut Node) {
1959        self.walk_node_children(node);
1960    }
1961
1962    /// Called for every `Fragment`. Default: visit each child node.
1963    fn visit_fragment(&mut self, fragment: &mut Fragment) {
1964        for node in fragment.nodes.iter_mut() {
1965            self.visit_node(node);
1966        }
1967    }
1968
1969    /// Called for every `Attribute`. Default: visit contained expressions.
1970    fn visit_attribute(&mut self, attr: &mut Attribute) {
1971        self.walk_attribute_expressions(attr);
1972    }
1973
1974    /// Walk all children of a node, dispatching to `visit_expression`,
1975    /// `visit_fragment`, and `visit_attribute` as appropriate.
1976    ///
1977    /// This is the single canonical match over all `Node` variants.
1978    fn walk_node_children(&mut self, node: &mut Node) {
1979        match node {
1980            Node::ExpressionTag(tag) => {
1981                self.visit_expression(&mut tag.expression);
1982            }
1983            Node::IfBlock(block) => {
1984                self.visit_expression(&mut block.test);
1985                self.visit_fragment(&mut block.consequent);
1986                if let Some(ref mut alt) = block.alternate {
1987                    self.walk_alternate(alt);
1988                }
1989            }
1990            Node::EachBlock(block) => {
1991                self.visit_expression(&mut block.expression);
1992                if let Some(ref mut ctx) = block.context {
1993                    self.visit_expression(ctx);
1994                }
1995                if let Some(ref mut key) = block.key {
1996                    self.visit_expression(key);
1997                }
1998                self.visit_fragment(&mut block.body);
1999                if let Some(ref mut fallback) = block.fallback {
2000                    self.visit_fragment(fallback);
2001                }
2002            }
2003            Node::KeyBlock(block) => {
2004                self.visit_expression(&mut block.expression);
2005                self.visit_fragment(&mut block.fragment);
2006            }
2007            Node::AwaitBlock(block) => {
2008                self.visit_expression(&mut block.expression);
2009                if let Some(ref mut v) = block.value {
2010                    self.visit_expression(v);
2011                }
2012                if let Some(ref mut e) = block.error {
2013                    self.visit_expression(e);
2014                }
2015                if let Some(ref mut f) = block.pending {
2016                    self.visit_fragment(f);
2017                }
2018                if let Some(ref mut f) = block.then {
2019                    self.visit_fragment(f);
2020                }
2021                if let Some(ref mut f) = block.catch {
2022                    self.visit_fragment(f);
2023                }
2024            }
2025            Node::SnippetBlock(block) => {
2026                self.visit_expression(&mut block.expression);
2027                for param in block.parameters.iter_mut() {
2028                    self.visit_expression(param);
2029                }
2030                self.visit_fragment(&mut block.body);
2031            }
2032            Node::RenderTag(tag) => {
2033                self.visit_expression(&mut tag.expression);
2034            }
2035            Node::HtmlTag(tag) => {
2036                self.visit_expression(&mut tag.expression);
2037            }
2038            Node::ConstTag(tag) => {
2039                self.visit_expression(&mut tag.declaration);
2040            }
2041            Node::DebugTag(tag) => {
2042                for expr in tag.arguments.iter_mut() {
2043                    self.visit_expression(expr);
2044                }
2045            }
2046            // Element-like nodes: attributes + fragment
2047            Node::RegularElement(el) => {
2048                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2049                self.visit_fragment(&mut el.fragment);
2050            }
2051            Node::Component(el) => {
2052                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2053                self.visit_fragment(&mut el.fragment);
2054            }
2055            Node::SlotElement(el) => {
2056                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2057                self.visit_fragment(&mut el.fragment);
2058            }
2059            Node::SvelteHead(el) => {
2060                // SvelteHead: only fragment, no attribute enrichment
2061                self.visit_fragment(&mut el.fragment);
2062            }
2063            Node::SvelteBody(el) => {
2064                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2065                self.visit_fragment(&mut el.fragment);
2066            }
2067            Node::SvelteWindow(el) => {
2068                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2069                self.visit_fragment(&mut el.fragment);
2070            }
2071            Node::SvelteDocument(el) => {
2072                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2073                self.visit_fragment(&mut el.fragment);
2074            }
2075            Node::SvelteComponent(el) => {
2076                if let Some(ref mut expr) = el.expression {
2077                    self.visit_expression(expr);
2078                }
2079                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2080                self.visit_fragment(&mut el.fragment);
2081            }
2082            Node::SvelteElement(el) => {
2083                if let Some(ref mut expr) = el.expression {
2084                    self.visit_expression(expr);
2085                }
2086                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2087                self.visit_fragment(&mut el.fragment);
2088            }
2089            Node::SvelteSelf(el) => {
2090                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2091                self.visit_fragment(&mut el.fragment);
2092            }
2093            Node::SvelteFragment(el) => {
2094                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2095                self.visit_fragment(&mut el.fragment);
2096            }
2097            Node::SvelteBoundary(el) => {
2098                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2099                self.visit_fragment(&mut el.fragment);
2100            }
2101            Node::TitleElement(el) => {
2102                for attr in el.attributes.iter_mut() { self.visit_attribute(attr); }
2103                self.visit_fragment(&mut el.fragment);
2104            }
2105            Node::Text(_) | Node::Comment(_) => {}
2106        }
2107    }
2108
2109    /// Walk an `Alternate` branch (else / else-if chain).
2110    fn walk_alternate(&mut self, alt: &mut Alternate) {
2111        match alt {
2112            Alternate::Fragment(f) => self.visit_fragment(f),
2113            Alternate::IfBlock(block) => {
2114                self.visit_expression(&mut block.test);
2115                self.visit_fragment(&mut block.consequent);
2116                if let Some(ref mut inner) = block.alternate {
2117                    self.walk_alternate(inner);
2118                }
2119            }
2120        }
2121    }
2122
2123    /// Walk all expressions inside an `Attribute`.
2124    ///
2125    /// This is the single canonical match over all `Attribute` variants.
2126    fn walk_attribute_expressions(&mut self, attr: &mut Attribute) {
2127        match attr {
2128            Attribute::Attribute(a) => {
2129                match &mut a.value {
2130                    AttributeValueKind::ExpressionTag(tag) => {
2131                        self.visit_expression(&mut tag.expression);
2132                    }
2133                    AttributeValueKind::Values(values) => {
2134                        for val in values.iter_mut() {
2135                            if let AttributeValue::ExpressionTag(tag) = val {
2136                                self.visit_expression(&mut tag.expression);
2137                            }
2138                        }
2139                    }
2140                    AttributeValueKind::Boolean(_) => {}
2141                }
2142            }
2143            Attribute::SpreadAttribute(a) => {
2144                self.visit_expression(&mut a.expression);
2145            }
2146            Attribute::OnDirective(d)
2147            | Attribute::BindDirective(d)
2148            | Attribute::ClassDirective(d)
2149            | Attribute::LetDirective(d)
2150            | Attribute::AnimateDirective(d)
2151            | Attribute::UseDirective(d) => {
2152                self.visit_expression(&mut d.expression);
2153            }
2154            Attribute::StyleDirective(d) => {
2155                match &mut d.value {
2156                    AttributeValueKind::ExpressionTag(tag) => {
2157                        self.visit_expression(&mut tag.expression);
2158                    }
2159                    AttributeValueKind::Values(values) => {
2160                        for val in values.iter_mut() {
2161                            if let AttributeValue::ExpressionTag(tag) = val {
2162                                self.visit_expression(&mut tag.expression);
2163                            }
2164                        }
2165                    }
2166                    AttributeValueKind::Boolean(_) => {}
2167                }
2168            }
2169            Attribute::TransitionDirective(d) => {
2170                self.visit_expression(&mut d.expression);
2171            }
2172            Attribute::AttachTag(a) => {
2173                self.visit_expression(&mut a.expression);
2174            }
2175        }
2176    }
2177}
2178
2179#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2180pub struct RootComment {
2181    pub r#type: RootCommentType,
2182    pub start: usize,
2183    pub end: usize,
2184    pub value: Arc<str>,
2185    pub loc: SourceRange,
2186}
2187
2188#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2189pub struct Root {
2190    pub css: Option<Css>,
2191    #[serde(skip_serializing, default)]
2192    pub styles: Box<[Css]>,
2193    pub js: Box<[Script]>,
2194    #[serde(skip_serializing, default)]
2195    pub scripts: Box<[Script]>,
2196    pub start: usize,
2197    pub end: usize,
2198    pub r#type: RootType,
2199    pub fragment: Fragment,
2200    pub options: Option<Options>,
2201    #[serde(skip_serializing_if = "Option::is_none")]
2202    pub module: Option<Script>,
2203    #[serde(skip_serializing_if = "Option::is_none")]
2204    pub instance: Option<Script>,
2205    #[serde(skip_serializing_if = "Option::is_none")]
2206    pub comments: Option<Box<[RootComment]>>,
2207    #[serde(skip_serializing, default)]
2208    pub errors: Box<[crate::ast::common::ParseError]>,
2209}
2210
2211impl Root {
2212    /// Walk all expressions in the AST and compute enriched ESTree JSON with `loc` fields.
2213    /// When `loose` is true, certain expression types get `character` in their loc.
2214    pub fn enrich_expressions(&mut self, source: &str, loose: bool) {
2215        enrich_fragment_expressions(&mut self.fragment, source, loose);
2216        if let Some(ref mut options) = self.options {
2217            enrich_options_expressions(options, source, loose);
2218        }
2219        // Inject HTML comments as leadingComments on Script Program nodes.
2220        self.inject_html_comments_into_scripts();
2221    }
2222
2223    /// Find HTML Comment nodes in fragment that immediately precede a Script tag,
2224    /// and inject them as `leadingComments` on the Script's Program content_json.
2225    fn inject_html_comments_into_scripts(&mut self) {
2226        // Collect HTML comments from the fragment that appear before scripts.
2227        let mut html_comments_before_instance: Vec<JsComment> = Vec::new();
2228        let mut html_comments_before_module: Vec<JsComment> = Vec::new();
2229
2230        let instance_start = self.instance.as_ref().map(|s| s.start);
2231        let module_start = self.module.as_ref().map(|s| s.start);
2232
2233        for node in self.fragment.nodes.iter() {
2234            if let Node::Comment(comment) = node {
2235                // Check if this comment immediately precedes the instance script
2236                if let Some(inst_start) = instance_start {
2237                    if comment.end <= inst_start {
2238                        html_comments_before_instance.push(JsComment {
2239                            kind: JsCommentKind::Line,
2240                            value: comment.data.clone(),
2241                            start: None,
2242                            end: None,
2243                        });
2244                    }
2245                }
2246                // Check if this comment immediately precedes the module script
2247                if let Some(mod_start) = module_start {
2248                    if comment.end <= mod_start {
2249                        html_comments_before_module.push(JsComment {
2250                            kind: JsCommentKind::Line,
2251                            value: comment.data.clone(),
2252                            start: None,
2253                            end: None,
2254                        });
2255                    }
2256                }
2257            }
2258        }
2259
2260        // Inject into instance content_json
2261        if !html_comments_before_instance.is_empty() {
2262            if let Some(ref mut script) = self.instance {
2263                inject_leading_comments_into_content_json(
2264                    &mut script.content_json,
2265                    &html_comments_before_instance,
2266                );
2267            }
2268        }
2269        if !html_comments_before_module.is_empty() {
2270            if let Some(ref mut script) = self.module {
2271                inject_leading_comments_into_content_json(
2272                    &mut script.content_json,
2273                    &html_comments_before_module,
2274                );
2275            }
2276        }
2277    }
2278}
2279
2280fn inject_leading_comments_into_content_json(
2281    content_json: &mut Option<Arc<str>>,
2282    comments: &[JsComment],
2283) {
2284    let Some(json_str) = content_json.as_ref() else {
2285        return;
2286    };
2287    let Ok(mut value) = serde_json::from_str::<serde_json::Value>(json_str.as_ref()) else {
2288        return;
2289    };
2290    if let serde_json::Value::Object(ref mut map) = value {
2291        map.insert(
2292            "leadingComments".to_string(),
2293            serde_json::Value::Array(crate::estree::make_comment_json(comments)),
2294        );
2295    }
2296    if let Ok(enriched) = serde_json::to_string(&value) {
2297        *content_json = Some(Arc::from(enriched));
2298    }
2299}
2300
2301fn enrich_expression(expr: &mut Expression, source: &str) {
2302    if expr.node.is_some() && expr.enriched_json.is_none() {
2303        expr.enrich_with_source(source);
2304    }
2305}
2306
2307fn enrich_expression_with_character(expr: &mut Expression, source: &str) {
2308    if expr.enriched_json.is_none() {
2309        expr.enrich_with_character(source);
2310    }
2311}
2312
2313/// Visitor that enriches all expressions in the AST with ESTree JSON + loc fields.
2314struct EnrichVisitor<'a> {
2315    source: &'a str,
2316    loose: bool,
2317}
2318
2319impl NodeVisitorMut for EnrichVisitor<'_> {
2320    fn visit_expression(&mut self, expr: &mut Expression) {
2321        enrich_expression(expr, self.source);
2322    }
2323
2324    fn visit_node(&mut self, node: &mut Node) {
2325        // Variants with special enrichment logic that differs from the default
2326        // `visit_expression` path (character mode, column offsets, etc.).
2327        match node {
2328            Node::EachBlock(block) => {
2329                self.visit_expression(&mut block.expression);
2330                if let Some(ref mut ctx) = block.context {
2331                    if ctx.is_destructured_pattern() {
2332                        // Destructured patterns need +1 column offset (matching upstream wrapping)
2333                        ctx.enrich_with_source_and_column_offset(self.source, 1);
2334                    } else if self.loose {
2335                        enrich_expression_with_character(ctx, self.source);
2336                    } else {
2337                        enrich_expression(ctx, self.source);
2338                    }
2339                }
2340                if let Some(ref mut key) = block.key {
2341                    self.visit_expression(key);
2342                }
2343                self.visit_fragment(&mut block.body);
2344                if let Some(ref mut fallback) = block.fallback {
2345                    self.visit_fragment(fallback);
2346                }
2347            }
2348            Node::AwaitBlock(block) => {
2349                self.visit_expression(&mut block.expression);
2350                // value and error need character mode in loose
2351                for opt_expr in [&mut block.value, &mut block.error] {
2352                    if let Some(expr) = opt_expr {
2353                        if self.loose {
2354                            enrich_expression_with_character(expr, self.source);
2355                        } else {
2356                            enrich_expression(expr, self.source);
2357                        }
2358                    }
2359                }
2360                if let Some(ref mut f) = block.pending {
2361                    self.visit_fragment(f);
2362                }
2363                if let Some(ref mut f) = block.then {
2364                    self.visit_fragment(f);
2365                }
2366                if let Some(ref mut f) = block.catch {
2367                    self.visit_fragment(f);
2368                }
2369            }
2370            Node::SnippetBlock(block) => {
2371                // SnippetBlock expression (name) uses loc with `character` field
2372                block.expression.enrich_with_character(self.source);
2373                for param in block.parameters.iter_mut() {
2374                    self.visit_expression(param);
2375                }
2376                self.visit_fragment(&mut block.body);
2377            }
2378            // All other variants use the default walk.
2379            _ => self.walk_node_children(node),
2380        }
2381    }
2382
2383    fn visit_attribute(&mut self, attr: &mut Attribute) {
2384        // In loose mode, shorthand empty expressions like `<div {}>` get loc with `character`
2385        if self.loose {
2386            if let Attribute::Attribute(a) = attr {
2387                if let AttributeValueKind::ExpressionTag(tag) = &mut a.value {
2388                    if tag.expression.node.is_none() && a.name.is_empty() {
2389                        enrich_expression_with_character(&mut tag.expression, self.source);
2390                        return;
2391                    }
2392                }
2393            }
2394        }
2395        self.walk_attribute_expressions(attr);
2396    }
2397}
2398
2399fn enrich_fragment_expressions(fragment: &mut Fragment, source: &str, loose: bool) {
2400    let mut visitor = EnrichVisitor { source, loose };
2401    visitor.visit_fragment(fragment);
2402}
2403
2404fn enrich_options_expressions(options: &mut Options, source: &str, loose: bool) {
2405    let mut visitor = EnrichVisitor { source, loose };
2406    for attr in options.attributes.iter_mut() {
2407        visitor.visit_attribute(attr);
2408    }
2409}
2410
2411macro_rules! impl_span_for_struct {
2412    ($($ty:ty),* $(,)?) => {
2413        $(
2414            impl Span for $ty {
2415                fn start(&self) -> usize {
2416                    self.start
2417                }
2418
2419                fn end(&self) -> usize {
2420                    self.end
2421                }
2422            }
2423        )*
2424    };
2425}
2426
2427impl_span_for_struct!(
2428    Script,
2429    EachBlock,
2430    KeyBlock,
2431    AwaitBlock,
2432    SnippetBlock,
2433    RenderTag,
2434    HtmlTag,
2435    ConstTag,
2436    DebugTag,
2437    ExpressionTag,
2438    Comment,
2439    RegularElement,
2440    Component,
2441    SlotElement,
2442    SvelteHead,
2443    SvelteBody,
2444    SvelteWindow,
2445    SvelteDocument,
2446    SvelteComponent,
2447    SvelteElement,
2448    SvelteSelf,
2449    SvelteFragment,
2450    SvelteBoundary,
2451    TitleElement,
2452    IfBlock,
2453    Text,
2454    Css,
2455    CssContent,
2456    CssRule,
2457    CssAtrule,
2458    CssBlock,
2459    CssDeclaration,
2460    CssSelectorList,
2461    CssComplexSelector,
2462    CssRelativeSelector,
2463    CssCombinator,
2464    CssNameSelector,
2465    CssValueSelector,
2466    CssPseudoClassSelector,
2467    CssAttributeSelector,
2468    Options,
2469    RootComment,
2470    Root
2471);
2472
2473impl Span for Node {
2474    fn start(&self) -> usize {
2475        match self {
2476            Node::Text(node) => node.start,
2477            Node::IfBlock(node) => node.start,
2478            Node::EachBlock(node) => node.start,
2479            Node::KeyBlock(node) => node.start,
2480            Node::AwaitBlock(node) => node.start,
2481            Node::SnippetBlock(node) => node.start,
2482            Node::RenderTag(node) => node.start,
2483            Node::HtmlTag(node) => node.start,
2484            Node::ConstTag(node) => node.start,
2485            Node::DebugTag(node) => node.start,
2486            Node::ExpressionTag(node) => node.start,
2487            Node::Comment(node) => node.start,
2488            Node::RegularElement(node) => node.start,
2489            Node::Component(node) => node.start,
2490            Node::SlotElement(node) => node.start,
2491            Node::SvelteHead(node) => node.start,
2492            Node::SvelteBody(node) => node.start,
2493            Node::SvelteWindow(node) => node.start,
2494            Node::SvelteDocument(node) => node.start,
2495            Node::SvelteComponent(node) => node.start,
2496            Node::SvelteElement(node) => node.start,
2497            Node::SvelteSelf(node) => node.start,
2498            Node::SvelteFragment(node) => node.start,
2499            Node::SvelteBoundary(node) => node.start,
2500            Node::TitleElement(node) => node.start,
2501        }
2502    }
2503
2504    fn end(&self) -> usize {
2505        match self {
2506            Node::Text(node) => node.end,
2507            Node::IfBlock(node) => node.end,
2508            Node::EachBlock(node) => node.end,
2509            Node::KeyBlock(node) => node.end,
2510            Node::AwaitBlock(node) => node.end,
2511            Node::SnippetBlock(node) => node.end,
2512            Node::RenderTag(node) => node.end,
2513            Node::HtmlTag(node) => node.end,
2514            Node::ConstTag(node) => node.end,
2515            Node::DebugTag(node) => node.end,
2516            Node::ExpressionTag(node) => node.end,
2517            Node::Comment(node) => node.end,
2518            Node::RegularElement(node) => node.end,
2519            Node::Component(node) => node.end,
2520            Node::SlotElement(node) => node.end,
2521            Node::SvelteHead(node) => node.end,
2522            Node::SvelteBody(node) => node.end,
2523            Node::SvelteWindow(node) => node.end,
2524            Node::SvelteDocument(node) => node.end,
2525            Node::SvelteComponent(node) => node.end,
2526            Node::SvelteElement(node) => node.end,
2527            Node::SvelteSelf(node) => node.end,
2528            Node::SvelteFragment(node) => node.end,
2529            Node::SvelteBoundary(node) => node.end,
2530            Node::TitleElement(node) => node.end,
2531        }
2532    }
2533}
2534
2535#[cfg(test)]
2536mod tests {
2537    use super::*;
2538
2539    #[test]
2540    fn adjust_estree_span_offsets_adds_offset() {
2541        let json = r#"{"type":"Identifier","name":"foo","start":0,"end":3}"#;
2542        let adjusted = adjust_estree_span_offsets(json, 10);
2543        assert_eq!(
2544            adjusted,
2545            r#"{"type":"Identifier","name":"foo","start":10,"end":13}"#
2546        );
2547    }
2548
2549    #[test]
2550    fn adjust_estree_span_offsets_handles_nested() {
2551        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}"#;
2552        let adjusted = adjust_estree_span_offsets(json, 20);
2553        assert!(adjusted.contains(r#""start":20"#));
2554        assert!(adjusted.contains(r#""end":21"#));
2555        assert!(adjusted.contains(r#""start":24"#));
2556        assert!(adjusted.contains(r#""end":25"#));
2557    }
2558
2559    #[test]
2560    fn adjust_estree_span_offsets_preserves_string_values() {
2561        // "name":"start" should NOT be adjusted
2562        let json = r#"{"type":"Identifier","name":"start","start":0,"end":5}"#;
2563        let adjusted = adjust_estree_span_offsets(json, 10);
2564        assert_eq!(
2565            adjusted,
2566            r#"{"type":"Identifier","name":"start","start":10,"end":15}"#
2567        );
2568    }
2569
2570    #[test]
2571    fn expression_serializes_oxc_identifier_with_offset() {
2572        let parsed = crate::js::JsExpression::parse(
2573            "foo",
2574            oxc_span::SourceType::mjs(),
2575        )
2576        .expect("valid expression");
2577
2578        let expr = Expression::from_expression(Arc::new(parsed), 42, 45);
2579        let json = serde_json::to_string(&expr).expect("serialize");
2580        let value: serde_json::Value = serde_json::from_str(&json).expect("valid json");
2581
2582        assert_eq!(value["type"], "Identifier");
2583        assert_eq!(value["name"], "foo");
2584        assert_eq!(value["start"], 42);
2585        assert_eq!(value["end"], 45);
2586    }
2587
2588    #[test]
2589    fn expression_serializes_fallback_without_node() {
2590        let expr = Expression::empty(10, 20);
2591        let json = serde_json::to_string(&expr).expect("serialize");
2592        let value: serde_json::Value = serde_json::from_str(&json).expect("valid json");
2593
2594        assert_eq!(value["type"], "Identifier");
2595        assert_eq!(value["name"], "");
2596        assert_eq!(value["start"], 10);
2597        assert_eq!(value["end"], 20);
2598    }
2599
2600    #[test]
2601    fn ts_serializer_emits_type_annotation_on_arrow_params() {
2602        let parsed = crate::js::JsExpression::parse(
2603            "(e: MouseEvent) => e",
2604            oxc_span::SourceType::ts().with_module(true),
2605        )
2606        .expect("valid expression");
2607
2608        let expr = Expression::from_expression(Arc::new(parsed), 0, 20);
2609        let raw = expr.serialize_oxc_node().expect("should serialize");
2610        let value: serde_json::Value = serde_json::from_str(&raw).expect("valid json");
2611        assert_eq!(value["type"], "ArrowFunctionExpression");
2612        let param = &value["params"][0];
2613        assert!(param.get("typeAnnotation").is_some(), "param should have typeAnnotation");
2614    }
2615}