Skip to main content

svelte_syntax/ast/
legacy.rs

1use std::sync::Arc;
2
3use serde::{Deserialize, Serialize};
4
5use crate::ast::common::Span;
6pub use crate::ast::common::{
7    FragmentType, LiteralValue, Loc as ExpressionLoc, NameLocation,
8    Position as ExpressionPoint, RootCommentType, ScriptContext, ScriptType, SnippetHeaderError,
9    SnippetHeaderErrorKind,
10};
11use crate::ast::modern;
12use crate::js::ParsedJsProgram;
13use crate::parse::legacy_expression_from_modern_expression;
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub struct Script {
17    pub r#type: ScriptType,
18    pub start: usize,
19    pub end: usize,
20    pub context: ScriptContext,
21    #[serde(
22        skip_serializing,
23        skip_deserializing,
24        default = "empty_parsed_js_program"
25    )]
26    pub content: Arc<ParsedJsProgram>,
27    #[serde(skip_serializing, default)]
28    pub content_start: usize,
29    #[serde(skip_serializing, default)]
30    pub content_end: usize,
31}
32
33fn empty_parsed_js_program() -> Arc<ParsedJsProgram> {
34    Arc::new(ParsedJsProgram::parse("", oxc_span::SourceType::mjs()))
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38pub struct Fragment {
39    pub r#type: FragmentType,
40    pub start: Option<usize>,
41    pub end: Option<usize>,
42    pub children: Box<[Node]>,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub struct ProgramComment {
47    pub r#type: RootCommentType,
48    pub value: Arc<str>,
49    pub start: usize,
50    pub end: usize,
51    pub loc: ExpressionLoc,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
55pub struct Element {
56    pub start: usize,
57    pub end: usize,
58    pub name: Arc<str>,
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub tag: Option<ElementTag>,
61    pub attributes: Box<[Attribute]>,
62    pub children: Box<[Node]>,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
66pub struct Head {
67    pub start: usize,
68    pub end: usize,
69    pub name: Arc<str>,
70    pub attributes: Box<[Attribute]>,
71    pub children: Box<[Node]>,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75pub struct InlineComponent {
76    pub start: usize,
77    pub end: usize,
78    pub name: Arc<str>,
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub expression: Option<Expression>,
81    pub attributes: Box<[Attribute]>,
82    pub children: Box<[Node]>,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
86#[serde(untagged)]
87pub enum ElementTag {
88    String(Arc<str>),
89    Expression(Expression),
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93#[serde(tag = "type")]
94pub enum Attribute {
95    Attribute(NamedAttribute),
96    Spread(SpreadAttribute),
97    Transition(TransitionDirective),
98    StyleDirective(StyleDirective),
99    Let(DirectiveAttribute),
100    Action(DirectiveAttribute),
101    Binding(DirectiveAttribute),
102    Class(DirectiveAttribute),
103    Animation(DirectiveAttribute),
104    EventHandler(DirectiveAttribute),
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub struct NamedAttribute {
109    pub start: usize,
110    pub end: usize,
111    pub name: Arc<str>,
112    pub name_loc: NameLocation,
113    pub value: AttributeValueList,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub struct SpreadAttribute {
118    pub start: usize,
119    pub end: usize,
120    pub expression: Expression,
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
124pub struct StyleDirective {
125    pub start: usize,
126    pub end: usize,
127    pub name: Arc<str>,
128    pub name_loc: NameLocation,
129    pub modifiers: Box<[Arc<str>]>,
130    pub value: AttributeValueList,
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub struct DirectiveAttribute {
135    pub start: usize,
136    pub end: usize,
137    pub name: Arc<str>,
138    pub name_loc: NameLocation,
139    pub expression: Option<Expression>,
140    pub modifiers: Box<[Arc<str>]>,
141}
142
143#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
144pub struct TransitionDirective {
145    pub start: usize,
146    pub end: usize,
147    pub name: Arc<str>,
148    pub name_loc: NameLocation,
149    pub expression: Option<Expression>,
150    pub modifiers: Box<[Arc<str>]>,
151    pub intro: bool,
152    pub outro: bool,
153}
154
155#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
156#[serde(tag = "type")]
157pub enum AttributeValue {
158    Text(Text),
159    MustacheTag(MustacheTag),
160    AttributeShorthand(AttributeShorthand),
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
164#[serde(untagged)]
165pub enum AttributeValueList {
166    Boolean(bool),
167    Values(Box<[AttributeValue]>),
168}
169
170#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
171pub struct MustacheTag {
172    pub start: usize,
173    pub end: usize,
174    pub expression: Expression,
175}
176
177#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
178pub struct RawMustacheTag {
179    pub start: usize,
180    pub end: usize,
181    pub expression: Expression,
182}
183
184#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
185pub struct DebugTag {
186    pub start: usize,
187    pub end: usize,
188    pub arguments: Box<[Expression]>,
189    pub identifiers: Box<[IdentifierExpression]>,
190}
191
192#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct AttributeShorthand {
194    pub start: usize,
195    pub end: usize,
196    pub expression: Expression,
197}
198
199/// Legacy expression representation.
200///
201/// Typed variants exist for the handful of expression shapes that legacy tests
202/// and consumers inspect structurally. All other expression types are
203/// represented via `Other`, which wraps the modern `Expression` (OXC-backed).
204#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
205#[serde(untagged)]
206pub enum Expression {
207    Identifier(IdentifierExpression),
208    Literal(LiteralExpression),
209    CallExpression(CallExpressionNode),
210    BinaryExpression(BinaryExpressionNode),
211    /// Any expression type not covered above. Delegates serialization to the
212    /// modern Expression which uses OXC ESTree serialization.
213    Other(modern::Expression),
214}
215
216#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
217pub struct IdentifierExpression {
218    pub name: Arc<str>,
219    pub start: usize,
220    pub end: usize,
221    #[serde(skip_serializing_if = "Option::is_none")]
222    pub loc: Option<ExpressionLoc>,
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
226pub struct LiteralExpression {
227    pub start: usize,
228    pub end: usize,
229    pub loc: Option<ExpressionLoc>,
230    pub value: LiteralValue,
231    pub raw: Arc<str>,
232}
233
234#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235pub struct CallExpressionNode {
236    pub start: usize,
237    pub end: usize,
238    pub loc: Option<ExpressionLoc>,
239    pub callee: Box<Expression>,
240    pub arguments: Box<[Expression]>,
241    pub optional: bool,
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
245pub struct BinaryExpressionNode {
246    pub start: usize,
247    pub end: usize,
248    pub loc: Option<ExpressionLoc>,
249    pub left: Box<Expression>,
250    pub operator: Arc<str>,
251    pub right: Box<Expression>,
252}
253
254#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
255pub struct Text {
256    pub start: usize,
257    pub end: usize,
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub raw: Option<Arc<str>>,
260    pub data: Arc<str>,
261}
262
263#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
264pub struct Comment {
265    pub start: usize,
266    pub end: usize,
267    pub data: Arc<str>,
268    pub ignores: Box<[Arc<str>]>,
269}
270
271#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
272pub struct IfBlock {
273    pub start: usize,
274    pub end: usize,
275    pub expression: Expression,
276    pub children: Box<[Node]>,
277    #[serde(rename = "else", skip_serializing_if = "Option::is_none")]
278    pub else_block: Option<ElseBlock>,
279    #[serde(skip_serializing_if = "Option::is_none")]
280    pub elseif: Option<bool>,
281}
282
283#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
284pub struct EachBlock {
285    pub start: usize,
286    pub end: usize,
287    pub children: Box<[Node]>,
288    pub context: Option<Expression>,
289    pub expression: Expression,
290    #[serde(skip_serializing_if = "Option::is_none")]
291    pub index: Option<Arc<str>>,
292    #[serde(skip_serializing_if = "Option::is_none")]
293    pub key: Option<Expression>,
294    #[serde(rename = "else", skip_serializing_if = "Option::is_none")]
295    pub else_block: Option<ElseBlock>,
296}
297
298#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
299pub struct KeyBlock {
300    pub start: usize,
301    pub end: usize,
302    pub expression: Expression,
303    pub children: Box<[Node]>,
304}
305
306#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
307pub struct AwaitBlock {
308    pub start: usize,
309    pub end: usize,
310    pub expression: Expression,
311    pub value: Option<Expression>,
312    pub error: Option<Expression>,
313    pub pending: PendingBlock,
314    pub then: ThenBlock,
315    pub catch: CatchBlock,
316}
317
318#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
319pub struct SnippetBlock {
320    pub start: usize,
321    pub end: usize,
322    pub expression: Expression,
323    #[serde(rename = "typeParams", skip_serializing_if = "Option::is_none")]
324    pub type_params: Option<Arc<str>>,
325    pub parameters: Box<[Expression]>,
326    pub children: Box<[Node]>,
327    #[serde(skip_serializing_if = "Option::is_none")]
328    pub header_error: Option<SnippetHeaderError>,
329}
330
331#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
332pub struct PendingBlock {
333    pub r#type: PendingBlockType,
334    pub start: Option<usize>,
335    pub end: Option<usize>,
336    pub children: Box<[Node]>,
337    pub skip: bool,
338}
339
340#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
341pub struct ThenBlock {
342    pub r#type: ThenBlockType,
343    pub start: Option<usize>,
344    pub end: Option<usize>,
345    pub children: Box<[Node]>,
346    pub skip: bool,
347}
348
349#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
350pub struct CatchBlock {
351    pub r#type: CatchBlockType,
352    pub start: Option<usize>,
353    pub end: Option<usize>,
354    pub children: Box<[Node]>,
355    pub skip: bool,
356}
357
358#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
359pub enum PendingBlockType {
360    PendingBlock,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
364pub enum ThenBlockType {
365    ThenBlock,
366}
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
369pub enum CatchBlockType {
370    CatchBlock,
371}
372
373#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
374pub struct Style {
375    pub r#type: StyleType,
376    pub start: usize,
377    pub end: usize,
378    pub attributes: Box<[crate::ast::modern::Attribute]>,
379    pub children: Box<[StyleNode]>,
380    pub content: crate::ast::modern::CssContent,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
384pub enum StyleType {
385    Style,
386}
387
388#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
389#[serde(tag = "type")]
390pub enum StyleNode {
391    Rule(StyleRule),
392    Atrule(StyleAtrule),
393}
394
395#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
396pub struct StyleRule {
397    pub prelude: StyleSelectorList,
398    pub block: crate::ast::modern::CssBlock,
399    pub start: usize,
400    pub end: usize,
401}
402
403#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
404pub struct StyleAtrule {
405    pub start: usize,
406    pub end: usize,
407    pub name: Arc<str>,
408    pub prelude: Arc<str>,
409    pub block: Option<crate::ast::modern::CssBlock>,
410}
411
412#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
413pub struct StyleSelectorList {
414    pub r#type: crate::ast::modern::CssSelectorListType,
415    pub start: usize,
416    pub end: usize,
417    pub children: Box<[StyleSelector]>,
418}
419
420#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
421pub struct StyleSelector {
422    pub r#type: StyleSelectorType,
423    pub start: usize,
424    pub end: usize,
425    pub children: Box<[crate::ast::modern::CssSimpleSelector]>,
426}
427
428#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
429pub enum StyleSelectorType {
430    Selector,
431}
432
433#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
434pub struct ElseBlock {
435    pub r#type: ElseBlockType,
436    pub start: usize,
437    pub end: usize,
438    pub children: Box<[Node]>,
439}
440
441#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
442pub enum ElseBlockType {
443    ElseBlock,
444}
445
446#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
447#[serde(tag = "type")]
448pub enum Node {
449    Element(Element),
450    Head(Head),
451    InlineComponent(InlineComponent),
452    Text(Text),
453    MustacheTag(MustacheTag),
454    RawMustacheTag(RawMustacheTag),
455    DebugTag(DebugTag),
456    Comment(Comment),
457    IfBlock(IfBlock),
458    EachBlock(EachBlock),
459    KeyBlock(KeyBlock),
460    AwaitBlock(AwaitBlock),
461    SnippetBlock(SnippetBlock),
462}
463
464impl From<modern::DirectiveAttribute> for DirectiveAttribute {
465    fn from(directive: modern::DirectiveAttribute) -> Self {
466        Self {
467            start: directive.start,
468            end: directive.end,
469            name: directive.name,
470            name_loc: directive.name_loc,
471            expression: Some(legacy_expression_from_modern_or_empty(directive.expression)),
472            modifiers: directive.modifiers,
473        }
474    }
475}
476
477impl From<modern::StyleDirective> for StyleDirective {
478    fn from(directive: modern::StyleDirective) -> Self {
479        Self {
480            start: directive.start,
481            end: directive.end,
482            name: directive.name,
483            name_loc: directive.name_loc,
484            modifiers: directive.modifiers,
485            value: directive.value.into(),
486        }
487    }
488}
489
490impl From<modern::TransitionDirective> for TransitionDirective {
491    fn from(directive: modern::TransitionDirective) -> Self {
492        Self {
493            start: directive.start,
494            end: directive.end,
495            name: directive.name,
496            name_loc: directive.name_loc,
497            expression: Some(legacy_expression_from_modern_or_empty(directive.expression)),
498            modifiers: directive.modifiers,
499            intro: directive.intro,
500            outro: directive.outro,
501        }
502    }
503}
504
505impl From<modern::AttributeValueList> for AttributeValueList {
506    fn from(value: modern::AttributeValueList) -> Self {
507        match value {
508            modern::AttributeValueList::Boolean(flag) => Self::Boolean(flag),
509            modern::AttributeValueList::Values(values) => Self::Values(
510                values
511                    .into_vec()
512                    .into_iter()
513                    .map(Into::into)
514                    .collect::<Vec<_>>()
515                    .into_boxed_slice(),
516            ),
517            modern::AttributeValueList::ExpressionTag(tag) => Self::Values(
518                vec![AttributeValue::MustacheTag(MustacheTag {
519                    start: tag.start,
520                    end: tag.end,
521                    expression: legacy_expression_from_modern_or_empty(tag.expression),
522                })]
523                .into_boxed_slice(),
524            ),
525        }
526    }
527}
528
529impl From<modern::AttributeValue> for AttributeValue {
530    fn from(value: modern::AttributeValue) -> Self {
531        match value {
532            modern::AttributeValue::Text(text) => Self::Text(Text {
533                start: text.start,
534                end: text.end,
535                raw: Some(text.raw),
536                data: text.data,
537            }),
538            modern::AttributeValue::ExpressionTag(tag) => Self::MustacheTag(MustacheTag {
539                start: tag.start,
540                end: tag.end,
541                expression: legacy_expression_from_modern_or_empty(tag.expression),
542            }),
543        }
544    }
545}
546
547impl From<modern::Script> for Script {
548    fn from(script: modern::Script) -> Self {
549        Self {
550            r#type: script.r#type,
551            start: script.start,
552            end: script.end,
553            context: script.context,
554            content_start: script.content_start,
555            content_end: script.content_end,
556            content: script.content,
557        }
558    }
559}
560
561fn legacy_expression_from_modern_or_empty(expression: modern::Expression) -> Expression {
562    if let Some(converted) = legacy_expression_from_modern_expression(expression.clone(), false) {
563        return converted;
564    }
565    let (start, end) = modern_expression_bounds(&expression).unwrap_or((0, 0));
566    legacy_empty_identifier_expression(start, end, None)
567}
568
569fn modern_expression_bounds(expression: &modern::Expression) -> Option<(usize, usize)> {
570    Some((expression.start, expression.end))
571}
572
573fn legacy_empty_identifier_expression(
574    start: usize,
575    end: usize,
576    loc: Option<ExpressionLoc>,
577) -> Expression {
578    Expression::Identifier(IdentifierExpression {
579        name: Arc::from(""),
580        start,
581        end,
582        loc,
583    })
584}
585
586#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
587pub struct Root {
588    pub html: Fragment,
589    #[serde(skip_serializing_if = "Option::is_none")]
590    pub css: Option<Style>,
591    #[serde(skip_serializing_if = "Option::is_none")]
592    pub instance: Option<Script>,
593    #[serde(skip_serializing_if = "Option::is_none")]
594    pub module: Option<Script>,
595    #[serde(rename = "_comments", skip_serializing_if = "Option::is_none")]
596    pub comments: Option<Box<[ProgramComment]>>,
597}
598
599macro_rules! impl_span_for_struct {
600    ($($ty:ty),* $(,)?) => {
601        $(
602            impl Span for $ty {
603                fn start(&self) -> usize {
604                    self.start
605                }
606
607                fn end(&self) -> usize {
608                    self.end
609                }
610            }
611        )*
612    };
613}
614
615impl_span_for_struct!(
616    Script,
617    ProgramComment,
618    Element,
619    Head,
620    InlineComponent,
621    NamedAttribute,
622    SpreadAttribute,
623    StyleDirective,
624    DirectiveAttribute,
625    TransitionDirective,
626    MustacheTag,
627    RawMustacheTag,
628    DebugTag,
629    AttributeShorthand,
630    IdentifierExpression,
631    LiteralExpression,
632    CallExpressionNode,
633    BinaryExpressionNode,
634    Text,
635    Comment,
636    IfBlock,
637    EachBlock,
638    KeyBlock,
639    AwaitBlock,
640    SnippetBlock,
641    Style,
642    StyleRule,
643    StyleAtrule,
644    StyleSelectorList,
645    StyleSelector,
646    ElseBlock
647);
648
649impl Span for Node {
650    fn start(&self) -> usize {
651        match self {
652            Node::Element(node) => node.start,
653            Node::Head(node) => node.start,
654            Node::InlineComponent(node) => node.start,
655            Node::Text(node) => node.start,
656            Node::MustacheTag(node) => node.start,
657            Node::RawMustacheTag(node) => node.start,
658            Node::DebugTag(node) => node.start,
659            Node::Comment(node) => node.start,
660            Node::IfBlock(node) => node.start,
661            Node::EachBlock(node) => node.start,
662            Node::KeyBlock(node) => node.start,
663            Node::AwaitBlock(node) => node.start,
664            Node::SnippetBlock(node) => node.start,
665        }
666    }
667
668    fn end(&self) -> usize {
669        match self {
670            Node::Element(node) => node.end,
671            Node::Head(node) => node.end,
672            Node::InlineComponent(node) => node.end,
673            Node::Text(node) => node.end,
674            Node::MustacheTag(node) => node.end,
675            Node::RawMustacheTag(node) => node.end,
676            Node::DebugTag(node) => node.end,
677            Node::Comment(node) => node.end,
678            Node::IfBlock(node) => node.end,
679            Node::EachBlock(node) => node.end,
680            Node::KeyBlock(node) => node.end,
681            Node::AwaitBlock(node) => node.end,
682            Node::SnippetBlock(node) => node.end,
683        }
684    }
685}