oxc_ast/ast/
jsx.rs

1//! [JSX](https://facebook.github.io/jsx)
2
3// NB: `#[span]`, `#[scope(...)]`,`#[visit(...)]` and `#[generate_derive(...)]` do NOT do anything to the code.
4// They are purely markers for codegen used in `tasks/ast_tools` and `crates/oxc_traverse/scripts`. See docs in those crates.
5// Read [`macro@oxc_ast_macros::ast`] for more information.
6
7use oxc_allocator::{Box, CloneIn, Dummy, GetAddress, TakeIn, Vec};
8use oxc_ast_macros::ast;
9use oxc_estree::ESTree;
10use oxc_span::{Atom, ContentEq, GetSpan, GetSpanMut, Span};
11
12use super::{inherit_variants, js::*, literal::*, ts::*};
13
14// 1.2 JSX Elements
15
16/// JSX Element
17///
18/// Note that fragments (`<></>`) are represented as [`JSXFragment`], unless they are written as
19/// members of React (e.g. `<React.Fragment></React.Fragment>`).
20/// ## Examples
21///
22/// ```tsx
23/// <Foo>        // <- opening_element
24///   some text  // <- children
25/// </Foo>       // <- closing_element
26/// ```
27///
28/// ```tsx
29/// <Foo />     // <- opening_element, no closing_element
30/// ```
31///
32/// See: [JSX Syntax](https://facebook.github.io/jsx/)
33#[ast(visit)]
34#[derive(Debug)]
35#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
36pub struct JSXElement<'a> {
37    /// Node location in source code
38    pub span: Span,
39    /// Opening tag of the element.
40    #[estree(via = JSXElementOpeningElement)]
41    pub opening_element: Box<'a, JSXOpeningElement<'a>>,
42    /// Children of the element.
43    /// This can be text, other elements, or expressions.
44    pub children: Vec<'a, JSXChild<'a>>,
45    /// Closing tag of the element.
46    /// [`None`] for self-closing tags.
47    pub closing_element: Option<Box<'a, JSXClosingElement<'a>>>,
48}
49
50/// JSX Opening Element
51///
52/// Opening tag in a [`JSXElement`].
53///
54/// ## Examples
55/// ```tsx
56/// // element with opening and closing tags (self_closing = false)
57/// //   ___ name
58///     <Foo bar baz={4}>
59/// //       ^^^^^^^^^^^ attributes
60///
61/// // element with self-closing tag (self_closing = true)
62/// <Component<T> />
63/// //         ^ type_arguments
64/// ```
65#[ast(visit)]
66#[derive(Debug)]
67#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
68#[estree(add_fields(selfClosing = JSXOpeningElementSelfClosing))]
69pub struct JSXOpeningElement<'a> {
70    /// Node location in source code
71    pub span: Span,
72    /// The possibly-namespaced tag name, e.g. `Foo` in `<Foo />`.
73    pub name: JSXElementName<'a>,
74    /// Type parameters for generic JSX elements.
75    #[ts]
76    pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
77    /// List of JSX attributes. In React-like applications, these become props.
78    pub attributes: Vec<'a, JSXAttributeItem<'a>>,
79}
80
81/// JSX Closing Element
82///
83/// Closing tag in a [`JSXElement`]. Self-closing tags do not have closing elements.
84///
85/// ## Example
86///
87/// ```tsx
88/// <Foo>Hello, World!</Foo>
89/// //                  ^^^ name
90/// <Bar /> // <- no closing element
91/// ```
92#[ast(visit)]
93#[derive(Debug)]
94#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
95pub struct JSXClosingElement<'a> {
96    /// Node location in source code
97    pub span: Span,
98    /// The tag name, e.g. `Foo` in `</Foo>`.
99    pub name: JSXElementName<'a>,
100}
101
102/// JSX Fragment
103///
104/// A fragment written with the special `<></>` syntax. When written as a `<Fragment>` component,
105/// fragments will be represented as [`JSXElement`]s.
106///
107/// Note that fragments cannot have attributes or type parameters.
108///
109/// See: [`React.Fragment`](https://react.dev/reference/react/Fragment)
110#[ast(visit)]
111#[derive(Debug)]
112#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
113pub struct JSXFragment<'a> {
114    /// Node location in source code
115    pub span: Span,
116    /// `<>`
117    pub opening_fragment: JSXOpeningFragment,
118    /// Elements inside the fragment.
119    pub children: Vec<'a, JSXChild<'a>>,
120    /// `</>`
121    pub closing_fragment: JSXClosingFragment,
122}
123
124/// JSX Opening Fragment (`<>`)
125#[ast(visit)]
126#[derive(Debug)]
127#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
128#[estree(add_fields(attributes = JsEmptyArray, selfClosing = JsFalse))]
129pub struct JSXOpeningFragment {
130    /// Node location in source code
131    pub span: Span,
132}
133
134/// JSX Closing Fragment (`</>`)
135#[ast(visit)]
136#[derive(Debug)]
137#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
138pub struct JSXClosingFragment {
139    /// Node location in source code
140    pub span: Span,
141}
142
143/// JSX Element Name
144#[ast(visit)]
145#[derive(Debug)]
146#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, GetAddress, ContentEq, ESTree)]
147pub enum JSXElementName<'a> {
148    /// `<div />`
149    Identifier(Box<'a, JSXIdentifier<'a>>) = 0,
150    /// `<Apple />`
151    #[estree(via = JSXElementIdentifierReference)]
152    IdentifierReference(Box<'a, IdentifierReference<'a>>) = 1,
153    /// `<Apple:Orange />`
154    NamespacedName(Box<'a, JSXNamespacedName<'a>>) = 2,
155    /// `<Apple.Orange />`
156    MemberExpression(Box<'a, JSXMemberExpression<'a>>) = 3,
157    /// `<this />`
158    #[estree(via = JSXElementThisExpression)]
159    ThisExpression(Box<'a, ThisExpression>) = 4,
160}
161
162/// JSX Namespaced Name
163///
164/// ## Example
165///
166/// ```tsx
167/// <Apple:Orange />
168/// ```
169#[ast(visit)]
170#[derive(Debug)]
171#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
172pub struct JSXNamespacedName<'a> {
173    /// Node location in source code
174    pub span: Span,
175    /// Namespace portion of the name, e.g. `Apple` in `<Apple:Orange />`
176    pub namespace: JSXIdentifier<'a>,
177    /// Name portion of the name, e.g. `Orange` in `<Apple:Orange />`
178    pub name: JSXIdentifier<'a>,
179}
180
181/// JSX Member Expression
182///
183/// Used in [`JSXElementName`]. Multiple member expressions may be chained together. In this case,
184/// [`object`] will be a [`member expression`].
185///
186/// ## Example
187///
188/// ```tsx
189/// // <object.property />
190/// <Apple.Orange />
191/// <Foo.Bar.Baz.Bang />
192/// ```
193///
194/// [`object`]: JSXMemberExpression::object
195/// [`member expression`]: JSXMemberExpressionObject::MemberExpression
196#[ast(visit)]
197#[derive(Debug)]
198#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
199pub struct JSXMemberExpression<'a> {
200    /// Node location in source code
201    pub span: Span,
202    /// The object being accessed. This is everything before the last `.`.
203    pub object: JSXMemberExpressionObject<'a>,
204    /// The property being accessed. This is everything after the last `.`.
205    pub property: JSXIdentifier<'a>,
206}
207
208/// JSX Member Expression Object
209///
210/// Part of a [`JSXMemberExpression`]. This is the object being accessed in
211/// namespace-like JSX tag names.
212///
213/// ## Example
214/// ```tsx
215/// const x = <Apple.Orange />
216/// //         ^^^^^ IdentifierReference
217///
218/// const y = <Apple.Orange.Banana />
219/// //         ^^^^^^^^^^^^ MemberExpression
220///
221/// const z = <this.Orange />
222/// //         ^^^^ ThisExpression
223/// ```
224#[ast(visit)]
225#[derive(Debug)]
226#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, GetAddress, ContentEq, ESTree)]
227pub enum JSXMemberExpressionObject<'a> {
228    /// `<Apple.Orange />`
229    #[estree(via = JSXElementIdentifierReference)]
230    IdentifierReference(Box<'a, IdentifierReference<'a>>) = 0,
231    /// `<Apple.Orange.Banana />`
232    MemberExpression(Box<'a, JSXMemberExpression<'a>>) = 1,
233    /// `<this.Orange />`
234    #[estree(via = JSXElementThisExpression)]
235    ThisExpression(Box<'a, ThisExpression>) = 2,
236}
237
238/// JSX Expression Container
239///
240/// Expression containers wrap [`JSXExpression`]s in JSX attributes and children using `{}`.
241///
242/// ## Example
243///
244/// ```tsx
245/// // boolean-like and string-like expressions are not wrapped in containers.
246/// // Here, only `container` is a JSXExpressionContainer.
247/// <Foo bar baz="bang" container={4}/>
248///   {4}  // <- wrapped in container
249/// </Foo>
250/// ```
251#[ast(visit)]
252#[derive(Debug)]
253#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
254pub struct JSXExpressionContainer<'a> {
255    /// Node location in source code
256    pub span: Span,
257    /// The expression inside the container.
258    pub expression: JSXExpression<'a>,
259}
260
261inherit_variants! {
262/// JSX Expression
263///
264/// Gets wrapped by a [`JSXExpressionContainer`]. Inherits variants from [`Expression`]. See [`ast`
265/// module docs] for explanation of inheritance.
266///
267/// [`ast` module docs]: `super`
268#[ast(visit)]
269#[derive(Debug)]
270#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
271pub enum JSXExpression<'a> {
272    /// An empty expression
273    ///
274    /// ## Example
275    /// ```tsx
276    /// <Foo>{}</Foo>
277    /// //   ^^
278    /// ```
279    EmptyExpression(JSXEmptyExpression) = 64,
280    // `Expression` variants added here by `inherit_variants!` macro
281    @inherit Expression
282}
283}
284
285/// An empty JSX expression (`{}`)
286#[ast(visit)]
287#[derive(Debug)]
288#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
289pub struct JSXEmptyExpression {
290    /// Node location in source code
291    pub span: Span,
292}
293
294// 1.3 JSX Attributes
295
296/// JSX Attributes
297///
298/// ## Example
299///
300/// ```tsx
301/// <Component foo="bar" baz={4} {...rest} />
302/// //         ^^^^^^^^^ ^^^^^^^ ^^^^^^^^^
303/// //             Attribute     SpreadAttribute
304/// ```
305#[ast(visit)]
306#[derive(Debug)]
307#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, GetAddress, ContentEq, ESTree)]
308pub enum JSXAttributeItem<'a> {
309    /// A `key="value"` attribute
310    Attribute(Box<'a, JSXAttribute<'a>>) = 0,
311    /// a `{...spread}` attribute
312    SpreadAttribute(Box<'a, JSXSpreadAttribute<'a>>) = 1,
313}
314
315/// JSX Attribute
316///
317/// An attribute in a JSX opening tag. May or may not have a value. Part of
318/// [`JSXAttributeItem`].
319///
320/// ## Example
321///
322/// ```tsx
323/// // `has-no-value` is a JSXAttribute with no value.
324/// <Component has-no-value foo="foo" />
325/// //                 name ^^^ ^^^^ value
326#[ast(visit)]
327#[derive(Debug)]
328#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
329pub struct JSXAttribute<'a> {
330    /// Node location in source code
331    pub span: Span,
332    /// The name of the attribute. This is a prop in React-like applications.
333    pub name: JSXAttributeName<'a>,
334    /// The value of the attribute. This can be a string literal, an expression,
335    /// or an element. Will be [`None`] for boolean-like attributes (e.g.
336    /// `<button disabled />`).
337    pub value: Option<JSXAttributeValue<'a>>,
338}
339
340/// JSX Spread Attribute
341///
342/// ## Example
343/// ```tsx
344/// <Component {...props} />
345/// //          ^^^^^^^^ argument
346/// ```
347#[ast(visit)]
348#[derive(Debug)]
349#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
350pub struct JSXSpreadAttribute<'a> {
351    /// Node location in source code
352    pub span: Span,
353    /// The expression being spread.
354    pub argument: Expression<'a>,
355}
356
357/// JSX Attribute Name
358///
359/// Part of a [`JSXAttribute`].
360///
361/// "Normal" attributes will be a [`JSXIdentifier`], while namespaced attributes
362/// will be a [`JSXNamespacedName`].
363///
364/// ## Example
365///
366/// ```tsx
367/// const Foo = <Component foo="bar" />;
368/// //                     ^^^ Identifier
369/// const Bar = <Component foo:bar="baz" />;
370/// //                     ^^^^^^^ NamespacedName
371/// ```
372#[ast(visit)]
373#[derive(Debug)]
374#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, GetAddress, ContentEq, ESTree)]
375pub enum JSXAttributeName<'a> {
376    /// An attribute name without a namespace prefix, e.g. `foo` in `foo="bar"`.
377    Identifier(Box<'a, JSXIdentifier<'a>>) = 0,
378    /// An attribute name with a namespace prefix, e.g. `foo:bar` in `foo:bar="baz"`.
379    NamespacedName(Box<'a, JSXNamespacedName<'a>>) = 1,
380}
381
382/// JSX Attribute Value
383///
384/// Part of a [`JSXAttribute`].
385///
386/// You're most likely interested in [`StringLiteral`] and
387/// [`JSXExpressionContainer`].
388///
389/// ## Example
390///
391/// ```tsx
392/// //                        v ExpressionContainer storing a NumericLiteral
393/// <Component foo="bar" baz={4} />
394/// //              ^^^ StringLiteral
395///
396/// // not a very common case, but it is valid syntax. Could also be a fragment.
397/// <Component foo=<Element /> />
398/// //             ^^^^^^^^^^^ Element
399/// ```
400#[ast(visit)]
401#[derive(Debug)]
402#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, GetAddress, ContentEq, ESTree)]
403pub enum JSXAttributeValue<'a> {
404    /// `<Component foo="bar" />`
405    StringLiteral(Box<'a, StringLiteral<'a>>) = 0,
406    /// `<Component foo={someExpr} />`
407    ExpressionContainer(Box<'a, JSXExpressionContainer<'a>>) = 1,
408    /// `<Component foo=<Element /> />`
409    Element(Box<'a, JSXElement<'a>>) = 2,
410    /// `<Component foo=<></> />`
411    Fragment(Box<'a, JSXFragment<'a>>) = 3,
412}
413
414/// JSX Identifier
415///
416/// Similar to [`IdentifierName`], but used in JSX elements.
417///
418/// [`IdentifierName`]: super::IdentifierName
419#[ast(visit)]
420#[derive(Debug)]
421#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
422pub struct JSXIdentifier<'a> {
423    /// Node location in source code
424    pub span: Span,
425    /// The name of the identifier.
426    #[estree(json_safe)]
427    pub name: Atom<'a>,
428}
429
430// 1.4 JSX Children
431
432/// JSX Child
433///
434/// Part of a [`JSXElement`].
435#[ast(visit)]
436#[derive(Debug)]
437#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, GetAddress, ContentEq, ESTree)]
438pub enum JSXChild<'a> {
439    /// `<Foo>Some Text</Foo>`
440    Text(Box<'a, JSXText<'a>>) = 0,
441    /// `<Foo><Child /></Foo>`
442    Element(Box<'a, JSXElement<'a>>) = 1,
443    /// `<Foo><></></Foo>`
444    Fragment(Box<'a, JSXFragment<'a>>) = 2,
445    /// `<Foo>{expression}</Foo>`
446    ExpressionContainer(Box<'a, JSXExpressionContainer<'a>>) = 3,
447    /// `<Foo>{...spread}</Foo>`
448    Spread(Box<'a, JSXSpreadChild<'a>>) = 4,
449}
450
451/// JSX Spread Child.
452///
453/// Variant of [`JSXChild`] that represents an object spread (`{...expression}`).
454#[ast(visit)]
455#[derive(Debug)]
456#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
457pub struct JSXSpreadChild<'a> {
458    /// Node location in source code
459    pub span: Span,
460    /// The expression being spread.
461    pub expression: Expression<'a>,
462}
463
464/// Text inside a JSX element.
465///
466/// Not to be confused with a [`StringLiteral`].
467///
468/// ## Example
469///
470/// ```tsx
471/// <Foo>Some text</Foo>     // `Some Text` is a JSXText,
472/// <Foo>"Some string"</Foo> // but `"Some string"` is a StringLiteral.
473/// ```
474#[ast(visit)]
475#[derive(Debug)]
476#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
477pub struct JSXText<'a> {
478    /// Node location in source code
479    pub span: Span,
480    /// The text content.
481    pub value: Atom<'a>,
482
483    /// The raw string as it appears in source code.
484    ///
485    /// `None` when this ast node is not constructed from the parser.
486    #[content_eq(skip)]
487    pub raw: Option<Atom<'a>>,
488}