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}