Skip to content

Commit 9dc140a

Browse files
committed
feat: some sort of Astro support
1 parent b929110 commit 9dc140a

36 files changed

Lines changed: 3091 additions & 788 deletions

File tree

crates/oxc_ast/src/ast/astro.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//! [Astro](https://astro.build) AST Definitions
2+
//!
3+
//! Astro files have a frontmatter section (TypeScript) delimited by `---` and
4+
//! an HTML body that can contain JSX expressions.
5+
6+
// NB: `#[span]`, `#[scope(...)]`,`#[visit(...)]` and `#[generate_derive(...)]` do NOT do anything to the code.
7+
// They are purely markers for codegen used in `tasks/ast_tools` and `crates/oxc_traverse/scripts`. See docs in those crates.
8+
// Read [`macro@oxc_ast_macros::ast`] for more information.
9+
10+
use oxc_allocator::{Box, CloneIn, Dummy, TakeIn, UnstableAddress, Vec};
11+
use oxc_ast_macros::ast;
12+
use oxc_estree::ESTree;
13+
use oxc_span::{ContentEq, GetSpan, GetSpanMut, Span};
14+
15+
use super::js::Program;
16+
use super::jsx::*;
17+
18+
/// Astro Root Node
19+
///
20+
/// The root node of an Astro file, containing optional frontmatter and an HTML body.
21+
///
22+
/// ## Example
23+
///
24+
/// ```astro
25+
/// ---
26+
/// const name = "World";
27+
/// ---
28+
/// <h1>Hello {name}!</h1>
29+
/// ```
30+
#[ast(visit)]
31+
#[derive(Debug)]
32+
#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree, UnstableAddress)]
33+
pub struct AstroRoot<'a> {
34+
/// Node location in source code
35+
pub span: Span,
36+
/// The frontmatter section between `---` delimiters, containing TypeScript code.
37+
/// This is `None` if the file has no frontmatter.
38+
pub frontmatter: Option<Box<'a, AstroFrontmatter<'a>>>,
39+
/// The HTML body of the Astro file, which can contain JSX expressions.
40+
/// Represented as JSX children since it behaves like an implicit fragment.
41+
pub body: Vec<'a, JSXChild<'a>>,
42+
}
43+
44+
/// Astro Frontmatter
45+
///
46+
/// The frontmatter section of an Astro file, delimited by `---`.
47+
/// Contains TypeScript code that runs at build time.
48+
///
49+
/// ## Example
50+
///
51+
/// ```astro
52+
/// ---
53+
/// import Component from './Component.astro';
54+
/// const items = ["a", "b", "c"];
55+
/// ---
56+
/// ```
57+
#[ast(visit)]
58+
#[derive(Debug)]
59+
#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree, UnstableAddress)]
60+
pub struct AstroFrontmatter<'a> {
61+
/// Node location in source code (includes the `---` delimiters)
62+
pub span: Span,
63+
/// The parsed TypeScript program from the frontmatter content
64+
pub program: Program<'a>,
65+
}
66+
67+
/// Astro Script Element
68+
///
69+
/// A `<script>` element in an Astro file containing TypeScript/JavaScript code.
70+
/// Unlike regular HTML script tags, Astro scripts are processed at build time.
71+
///
72+
/// ## Example
73+
///
74+
/// ```astro
75+
/// <script>
76+
/// console.log("Hello from Astro!");
77+
/// </script>
78+
/// ```
79+
#[ast(visit)]
80+
#[derive(Debug)]
81+
#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree, UnstableAddress)]
82+
pub struct AstroScript<'a> {
83+
/// Node location in source code (includes the `<script>` tags)
84+
pub span: Span,
85+
86+
/// The parsed TypeScript/JavaScript program from the script content
87+
pub program: Program<'a>,
88+
}

crates/oxc_ast/src/ast/jsx.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use oxc_ast_macros::ast;
99
use oxc_estree::ESTree;
1010
use oxc_span::{Atom, ContentEq, GetSpan, GetSpanMut, Span};
1111

12-
use super::{inherit_variants, js::*, literal::*, ts::*};
12+
use super::{astro::AstroScript, inherit_variants, js::*, literal::*, ts::*};
1313

1414
// 1.2 JSX Elements
1515

@@ -446,6 +446,9 @@ pub enum JSXChild<'a> {
446446
ExpressionContainer(Box<'a, JSXExpressionContainer<'a>>) = 3,
447447
/// `<Foo>{...spread}</Foo>`
448448
Spread(Box<'a, JSXSpreadChild<'a>>) = 4,
449+
/// Astro `<script>` element with parsed TypeScript content
450+
/// This is only used in Astro files.
451+
AstroScript(Box<'a, AstroScript<'a>>) = 5,
449452
}
450453

451454
/// JSX Spread Child.

crates/oxc_ast/src/ast/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,15 @@ pub use oxc_syntax::{
182182
},
183183
};
184184

185+
pub(crate) mod astro;
185186
pub(crate) mod comment;
186187
pub(crate) mod js;
187188
pub(crate) mod jsx;
188189
pub(crate) mod literal;
189190
pub(crate) mod macros;
190191
pub(crate) mod ts;
191192

193+
pub use astro::*;
192194
pub use comment::*;
193195
pub use js::*;
194196
pub use jsx::*;

crates/oxc_ast/src/ast_kind_impl.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,10 @@ impl AstKind<'_> {
604604
}
605605
Self::AssignmentTargetPropertyProperty(_) => "AssignmentTargetPropertyProperty".into(),
606606
Self::TSImportTypeQualifiedName(_) => "TSImportTypeQualifiedName".into(),
607+
608+
Self::AstroRoot(_) => "AstroRoot".into(),
609+
Self::AstroFrontmatter(_) => "AstroFrontmatter".into(),
610+
Self::AstroScript(_) => "AstroScript".into(),
607611
}
608612
}
609613
}

crates/oxc_ast/src/generated/assert_layouts.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,25 @@ use crate::ast::*;
99

1010
#[cfg(target_pointer_width = "64")]
1111
const _: () = {
12+
// Padding: 0 bytes
13+
assert!(size_of::<AstroRoot>() == 40);
14+
assert!(align_of::<AstroRoot>() == 8);
15+
assert!(offset_of!(AstroRoot, span) == 0);
16+
assert!(offset_of!(AstroRoot, frontmatter) == 8);
17+
assert!(offset_of!(AstroRoot, body) == 16);
18+
19+
// Padding: 0 bytes
20+
assert!(size_of::<AstroFrontmatter>() == 136);
21+
assert!(align_of::<AstroFrontmatter>() == 8);
22+
assert!(offset_of!(AstroFrontmatter, span) == 0);
23+
assert!(offset_of!(AstroFrontmatter, program) == 8);
24+
25+
// Padding: 0 bytes
26+
assert!(size_of::<AstroScript>() == 136);
27+
assert!(align_of::<AstroScript>() == 8);
28+
assert!(offset_of!(AstroScript, span) == 0);
29+
assert!(offset_of!(AstroScript, program) == 8);
30+
1231
// Padding: 1 bytes
1332
assert!(size_of::<Program>() == 128);
1433
assert!(align_of::<Program>() == 8);
@@ -1631,6 +1650,25 @@ const _: () = {
16311650

16321651
#[cfg(target_pointer_width = "32")]
16331652
const _: () = if cfg!(target_family = "wasm") || align_of::<u64>() == 8 {
1653+
// Padding: 0 bytes
1654+
assert!(size_of::<AstroRoot>() == 28);
1655+
assert!(align_of::<AstroRoot>() == 4);
1656+
assert!(offset_of!(AstroRoot, span) == 0);
1657+
assert!(offset_of!(AstroRoot, frontmatter) == 8);
1658+
assert!(offset_of!(AstroRoot, body) == 12);
1659+
1660+
// Padding: 0 bytes
1661+
assert!(size_of::<AstroFrontmatter>() == 96);
1662+
assert!(align_of::<AstroFrontmatter>() == 4);
1663+
assert!(offset_of!(AstroFrontmatter, span) == 0);
1664+
assert!(offset_of!(AstroFrontmatter, program) == 8);
1665+
1666+
// Padding: 0 bytes
1667+
assert!(size_of::<AstroScript>() == 96);
1668+
assert!(align_of::<AstroScript>() == 4);
1669+
assert!(offset_of!(AstroScript, span) == 0);
1670+
assert!(offset_of!(AstroScript, program) == 8);
1671+
16341672
// Padding: 1 bytes
16351673
assert!(size_of::<Program>() == 88);
16361674
assert!(align_of::<Program>() == 4);

crates/oxc_ast/src/generated/ast_builder.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,81 @@ use oxc_span::{Atom, Ident};
2222
use crate::{AstBuilder, ast::*};
2323

2424
impl<'a> AstBuilder<'a> {
25+
/// Build an [`AstroRoot`].
26+
///
27+
/// ## Parameters
28+
/// * `span`: Node location in source code
29+
/// * `frontmatter`: The frontmatter section between `---` delimiters, containing TypeScript code.
30+
/// * `body`: The HTML body of the Astro file, which can contain JSX expressions.
31+
#[inline]
32+
pub fn astro_root<T1>(
33+
self,
34+
span: Span,
35+
frontmatter: T1,
36+
body: Vec<'a, JSXChild<'a>>,
37+
) -> AstroRoot<'a>
38+
where
39+
T1: IntoIn<'a, Option<Box<'a, AstroFrontmatter<'a>>>>,
40+
{
41+
AstroRoot { span, frontmatter: frontmatter.into_in(self.allocator), body }
42+
}
43+
44+
/// Build an [`AstroFrontmatter`].
45+
///
46+
/// If you want the built node to be allocated in the memory arena,
47+
/// use [`AstBuilder::alloc_astro_frontmatter`] instead.
48+
///
49+
/// ## Parameters
50+
/// * `span`: Node location in source code (includes the `---` delimiters)
51+
/// * `program`: The parsed TypeScript program from the frontmatter content
52+
#[inline]
53+
pub fn astro_frontmatter(self, span: Span, program: Program<'a>) -> AstroFrontmatter<'a> {
54+
AstroFrontmatter { span, program }
55+
}
56+
57+
/// Build an [`AstroFrontmatter`], and store it in the memory arena.
58+
///
59+
/// Returns a [`Box`] containing the newly-allocated node.
60+
/// If you want a stack-allocated node, use [`AstBuilder::astro_frontmatter`] instead.
61+
///
62+
/// ## Parameters
63+
/// * `span`: Node location in source code (includes the `---` delimiters)
64+
/// * `program`: The parsed TypeScript program from the frontmatter content
65+
#[inline]
66+
pub fn alloc_astro_frontmatter(
67+
self,
68+
span: Span,
69+
program: Program<'a>,
70+
) -> Box<'a, AstroFrontmatter<'a>> {
71+
Box::new_in(self.astro_frontmatter(span, program), self.allocator)
72+
}
73+
74+
/// Build an [`AstroScript`].
75+
///
76+
/// If you want the built node to be allocated in the memory arena,
77+
/// use [`AstBuilder::alloc_astro_script`] instead.
78+
///
79+
/// ## Parameters
80+
/// * `span`: Node location in source code (includes the `<script>` tags)
81+
/// * `program`: The parsed TypeScript/JavaScript program from the script content
82+
#[inline]
83+
pub fn astro_script(self, span: Span, program: Program<'a>) -> AstroScript<'a> {
84+
AstroScript { span, program }
85+
}
86+
87+
/// Build an [`AstroScript`], and store it in the memory arena.
88+
///
89+
/// Returns a [`Box`] containing the newly-allocated node.
90+
/// If you want a stack-allocated node, use [`AstBuilder::astro_script`] instead.
91+
///
92+
/// ## Parameters
93+
/// * `span`: Node location in source code (includes the `<script>` tags)
94+
/// * `program`: The parsed TypeScript/JavaScript program from the script content
95+
#[inline]
96+
pub fn alloc_astro_script(self, span: Span, program: Program<'a>) -> Box<'a, AstroScript<'a>> {
97+
Box::new_in(self.astro_script(span, program), self.allocator)
98+
}
99+
25100
/// Build a [`Program`].
26101
///
27102
/// ## Parameters
@@ -9734,6 +9809,18 @@ impl<'a> AstBuilder<'a> {
97349809
JSXChild::Spread(self.alloc_jsx_spread_child(span, expression))
97359810
}
97369811

9812+
/// Build a [`JSXChild::AstroScript`].
9813+
///
9814+
/// This node contains an [`AstroScript`] that will be stored in the memory arena.
9815+
///
9816+
/// ## Parameters
9817+
/// * `span`: Node location in source code (includes the `<script>` tags)
9818+
/// * `program`: The parsed TypeScript/JavaScript program from the script content
9819+
#[inline]
9820+
pub fn jsx_child_astro_script(self, span: Span, program: Program<'a>) -> JSXChild<'a> {
9821+
JSXChild::AstroScript(self.alloc_astro_script(span, program))
9822+
}
9823+
97379824
/// Build a [`JSXSpreadChild`].
97389825
///
97399826
/// If you want the built node to be allocated in the memory arena,

0 commit comments

Comments
 (0)