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#[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 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}