From 0ef9b9fe28acd09409acff6c407032383a77a8f0 Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Mon, 22 Jul 2024 13:56:52 -0400 Subject: [PATCH 01/10] docs(ast): add doc comments to more AST nodes --- crates/oxc_ast/src/ast/js.rs | 303 +++++++++++++++++++++++++++++- crates/oxc_ast/src/ast/ts.rs | 40 ++++ crates/oxc_ast/src/ast_impl/js.rs | 91 ++++++++- 3 files changed, 424 insertions(+), 10 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 85b37fa582f3c..32ffd3242744a 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -160,6 +160,8 @@ macro_rules! match_expression { pub use match_expression; /// Identifier Name +/// +/// See: [13.1 Identifiers](https://tc39.es/ecma262/#sec-identifiers) #[ast(visit)] #[derive(Debug, Clone, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize))] @@ -171,6 +173,8 @@ pub struct IdentifierName<'a> { } /// Identifier Reference +/// +/// See: [13.1 Identifiers](https://tc39.es/ecma262/#sec-identifiers) #[ast(visit)] #[derive(Debug, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] @@ -178,14 +182,26 @@ pub struct IdentifierName<'a> { pub struct IdentifierReference<'a> { #[serde(flatten)] pub span: Span, + /// The name of the identifier being referenced. pub name: Atom<'a>, + /// Reference ID + /// + /// Identifies what identifier this refers to, and how it is used. This is + /// set in the bind step of semantic analysis, and will always be [`None`] + /// immediately after parsing. #[serde(skip)] pub reference_id: Cell>, + /// Flags indicating how the reference is used. + /// + /// This gets set in the bind step of semantic analysis, and will always be + /// [`ReferenceFlag::empty`] immediately after parsing. #[serde(skip)] pub reference_flag: ReferenceFlag, } /// Binding Identifier +/// +/// See: [13.1 Identifiers](https://tc39.es/ecma262/#sec-identifiers) #[ast(visit)] #[derive(Debug, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] @@ -193,12 +209,21 @@ pub struct IdentifierReference<'a> { pub struct BindingIdentifier<'a> { #[serde(flatten)] pub span: Span, + /// The identifier name being bound. pub name: Atom<'a>, + /// Unique identifier for this binding. + /// + /// This gets initialized during [`semantic analysis`] in the bind step. If + /// you choose to skip semantic analysis, this will always be [`None`]. + /// + /// [`semantic analysis`]: #[serde(skip)] pub symbol_id: Cell>, } /// Label Identifier +/// +/// See: [13.1 Identifiers](https://tc39.es/ecma262/#sec-identifiers) #[ast(visit)] #[derive(Debug, Clone, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize))] @@ -210,6 +235,8 @@ pub struct LabelIdentifier<'a> { } /// This Expression +/// +/// Corresponds to the `this` keyword. #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -266,6 +293,12 @@ pub struct Elision { } /// Object Expression +/// +/// ## Example +/// ```ts +/// const x = { foo: 'foo' } +/// // ^^^^^^^^^^^^^^ +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -273,6 +306,7 @@ pub struct Elision { pub struct ObjectExpression<'a> { #[serde(flatten)] pub span: Span, + /// Properties declared in the object pub properties: Vec<'a, ObjectPropertyKind<'a>>, #[serde(skip)] pub trailing_comma: Option, @@ -283,7 +317,22 @@ pub struct ObjectExpression<'a> { #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[serde(untagged)] pub enum ObjectPropertyKind<'a> { + /// Object Property + /// + /// ## Example + /// ```ts + /// const foo = 'foo' + /// const x = { foo, bar: 'bar' } + /// // ^^^ ^^^^^^^^^ ObjectProperty(Box<'a, ObjectProperty<'a>>), + /// Object Spread Property + /// + /// ## Example + /// ```ts + /// const obj = { foo: 'foo' } + /// const obj2 = { ...obj, bar: 'bar' } + /// // ^^^^^^ + /// ``` SpreadProperty(Box<'a, SpreadElement<'a>>), } @@ -438,6 +487,14 @@ pub struct StaticMemberExpression<'a> { } /// `MemberExpression[?Yield, ?Await] . PrivateIdentifier` +/// +/// ## Example +/// ```ts +/// // _______ object +/// const foo.bar?.#baz +/// // ↑ ^^^^ field +/// // optional +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -451,6 +508,19 @@ pub struct PrivateFieldExpression<'a> { } /// Call Expression +/// +/// ## Examples +/// ```ts +/// // ___ callee +/// const x = foo(1, 2) +/// +/// // ^^^^ arguments +/// const y = foo.bar?.(1, 2) +/// // ^ optional +/// +/// const z = foo(1, 2) +/// // ^^^^^^^^^^^^^^ type parameters +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -465,6 +535,15 @@ pub struct CallExpression<'a> { } /// New Expression +/// +/// ## Example +/// ```ts +/// // callee arguments +/// // ↓↓↓ ↓↓↓↓ +/// const foo = new Foo(1, 2) +/// // ↑↑↑↑↑↑↑↑ +/// // type parameters +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -490,6 +569,16 @@ pub struct MetaProperty<'a> { } /// Spread Element +/// +/// An array or object spread. Could be used in unpacking or a declaration. +/// +/// ## Example +/// ```ts +/// const [first, ...rest] = arr +/// // ^^^^^^^ +/// const obj = { foo: 'foo', ...obj2 } +/// // ^^^^^^^ +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -497,6 +586,7 @@ pub struct MetaProperty<'a> { pub struct SpreadElement<'a> { #[serde(flatten)] pub span: Span, + /// The expression being spread. pub argument: Expression<'a>, } @@ -1578,17 +1668,65 @@ pub struct Class<'a> { pub r#type: ClassType, #[serde(flatten)] pub span: Span, + /// Decorators applied to the class. + /// + /// Decorators are currently a stage 3 proposal. Oxc handles both TC39 and + /// legacy TypeScript decorators. + /// + /// ## Example + /// ```ts + /// @Bar() // <-- Decorator + /// class Foo {} + /// ``` pub decorators: Vec<'a, Decorator<'a>>, + /// Class identifier, AKA the name pub id: Option>, #[scope(enter_before)] pub type_parameters: Option>>, + /// Super class. When present, this will usually be an [`IdentifierReference`]. + /// + /// ## Example + /// ```ts + /// class Foo extends Bar {} + /// // ^^^ + /// ``` #[visit(as(ClassHeritage))] pub super_class: Option>, + /// Type parameters passed to super class. + /// + /// ## Example + /// ```ts + /// class Foo extends Bar {} + /// // ^ + /// ``` pub super_type_parameters: Option>>, + /// Interface implementation clause for TypeScript classes. + /// + /// ## Example + /// ```ts + /// interface Bar {} + /// class Foo implements Bar {} + /// // ^^^ + /// ``` pub implements: Option>>, pub body: Box<'a, ClassBody<'a>>, + /// Whether the class is abstract + /// + /// ## Example + /// ```ts + /// class Foo {} // true + /// abstract class Bar {} // false + /// ``` pub r#abstract: bool, + /// Whether the class was `declare`ed + /// + /// ## Example + /// ```ts + /// declare class Foo {} + /// ``` pub declare: bool, + /// Id of the scope created by the [`Class`], including type parameters and + /// statements within the [`ClassBody`]. pub scope_id: Cell>, } @@ -1596,7 +1734,16 @@ pub struct Class<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub enum ClassType { + /// Class declaration statement + /// ```ts + /// class Foo { } + /// ``` ClassDeclaration, + /// Class expression + /// + /// ```ts + /// const Foo = class {} + /// ``` ClassExpression, } @@ -1610,15 +1757,45 @@ pub struct ClassBody<'a> { pub body: Vec<'a, ClassElement<'a>>, } +/// Class Body Element +/// +/// ## Example +/// ```ts +/// class Foo { +/// [prop: string]: string // ClassElement::TSIndexSignature +/// +/// public x: number // ClassElement::PropertyDefinition +/// +/// accessor z() { return 5 } // ClassElement::AccessorProperty +/// +/// +/// // These are all ClassElement::MethodDefinitions +/// get y() { return 5 } +/// set y(value) { } +/// static foo() {} +/// bar() {} +/// } +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[serde(untagged)] pub enum ClassElement<'a> { StaticBlock(Box<'a, StaticBlock<'a>>), + /// Class Methods + /// + /// Includes static and non-static methods, constructors, getters, and setters. MethodDefinition(Box<'a, MethodDefinition<'a>>), PropertyDefinition(Box<'a, PropertyDefinition<'a>>), AccessorProperty(Box<'a, AccessorProperty<'a>>), + /// Index Signature + /// + /// ## Example + /// ```ts + /// class Foo { + /// [keys: string]: string + /// } + /// ``` TSIndexSignature(Box<'a, TSIndexSignature<'a>>), } @@ -1627,6 +1804,9 @@ pub enum ClassElement<'a> { #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[serde(rename_all = "camelCase")] pub struct MethodDefinition<'a> { + /// Method definition type + /// + /// This will always be true when an `abstract` modifier is used on the method. pub r#type: MethodDefinitionType, #[serde(flatten)] pub span: Span, @@ -1663,17 +1843,76 @@ pub struct PropertyDefinition<'a> { pub r#type: PropertyDefinitionType, #[serde(flatten)] pub span: Span, + /// Decorators applied to the property. + /// + /// See [`Decorator`] for more information. pub decorators: Vec<'a, Decorator<'a>>, + /// The expression used to declare the property. pub key: PropertyKey<'a>, + /// Initialized value in the declaration. + /// + /// ## Example + /// ``` + /// class Foo { + /// x = 5 // Some(NumericLiteral) + /// y: string // None + /// + /// constructor() { + /// this.y = "hello" + /// } + /// } + /// ``` pub value: Option>, + /// Property was declared with a computed key + /// + /// ## Example + /// ```ts + /// class Foo { + /// ["a"]: string // true + /// b: number // false + /// } + /// ``` pub computed: bool, + /// Property was declared with a `static` modifier pub r#static: bool, + /// Property is declared with a `declare` modifier. + /// + /// ## Example + /// ```ts + /// class Foo { + /// x: number // false + /// declare y: string // true + /// } + /// + /// declare class Bar { + /// x: number // false + /// } + /// ``` pub declare: bool, pub r#override: bool, + /// `true` when created with an optional modifier (`?`) pub optional: bool, pub definite: bool, + /// `true` when declared with a `readonly` modifier pub readonly: bool, + /// Type annotation on the property. + /// + /// Will only ever be [`Some`] for TypeScript files. pub type_annotation: Option>>, + /// Accessibility modifier. + /// + /// Only ever [`Some`] for TypeScript files. + /// + /// ## Example + /// + /// ```ts + /// class Foo { + /// public w: number // Some(TSAccessibility::Public) + /// private x: string // Some(TSAccessibility::Private) + /// protected y: boolean // Some(TSAccessibility::Protected) + /// readonly z // None + /// } + /// ``` pub accessibility: Option, } @@ -1690,12 +1929,19 @@ pub enum PropertyDefinitionType { #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[serde(rename_all = "camelCase")] pub enum MethodDefinitionKind { + /// Class constructor Constructor, + /// Static or instance method Method, + /// Getter method Get, + /// Setter method Set, } +/// An identifier for a private class member. +/// +/// See: [MDN - Private class fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields) #[ast(visit)] #[derive(Debug, Clone, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1763,6 +2009,14 @@ pub enum AccessorPropertyType { TSAbstractAccessorProperty, } +/// Class Accessor Property +/// +/// ## Example +/// ```ts +/// class Foo { +/// accessor y: string +/// } +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1770,8 +2024,13 @@ pub struct AccessorProperty<'a> { pub r#type: AccessorPropertyType, #[serde(flatten)] pub span: Span, + /// Decorators applied to the accessor property. + /// + /// See [`Decorator`] for more information. pub decorators: Vec<'a, Decorator<'a>>, + /// The expression used to declare the property. pub key: PropertyKey<'a>, + /// Initialized value in the declaration, if present. pub value: Option>, pub computed: bool, pub r#static: bool, @@ -1828,11 +2087,28 @@ pub struct ImportSpecifier<'a> { #[serde(flatten)] pub span: Span, pub imported: ModuleExportName<'a>, + /// The name of the imported symbol. + /// + /// ## Example + /// ```ts + /// // local and imported name are the same + /// import { Foo } from 'foo' + /// // ^^^ + /// // imports can be renamed, changing the local name + /// import { Foo as Bar } from 'foo' + /// // ^^^ + /// ``` pub local: BindingIdentifier<'a>, pub import_kind: ImportOrExportKind, } -// import local from "source" +/// Default Import Specifier +/// +/// ## Example +/// ```ts +/// import local from "source" +/// ``` +/// #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1840,10 +2116,16 @@ pub struct ImportSpecifier<'a> { pub struct ImportDefaultSpecifier<'a> { #[serde(flatten)] pub span: Span, + /// The name of the imported symbol. pub local: BindingIdentifier<'a>, } -// import * as local from "source" +/// Namespace import specifier +/// +/// ## Example +/// ```ts +/// import * as local from "source" +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1885,6 +2167,13 @@ pub enum ImportAttributeKey<'a> { StringLiteral(StringLiteral<'a>), } +/// Named Export Declaration +/// +/// ## Example +/// ```ts +/// export { Foo }; +/// export type { Bar }; +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1902,9 +2191,12 @@ pub struct ExportNamedDeclaration<'a> { } /// Export Default Declaration +/// ## Example +/// ```ts /// export default HoistableDeclaration /// export default ClassDeclaration /// export default AssignmentExpression +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1925,8 +2217,9 @@ pub struct ExportAllDeclaration<'a> { pub span: Span, pub exported: Option>, pub source: StringLiteral<'a>, + /// Will be `Some(vec![])` for empty assertion pub with_clause: Option>, // Some(vec![]) for empty assertion - pub export_kind: ImportOrExportKind, // `export type *` + pub export_kind: ImportOrExportKind, // `export type *` } #[ast(visit)] @@ -1965,7 +2258,9 @@ pub enum ExportDefaultDeclarationKind<'a> { } } -/// Support: +/// Module Export Name +/// +/// Supports: /// * `import {"\0 any unicode" as foo} from ""` /// * `export {foo as "\0 any unicode"}` /// * es2022: diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 1164906c940b3..9996dd56668e3 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -654,6 +654,7 @@ pub struct TSClassImplements<'a> { pub struct TSInterfaceDeclaration<'a> { #[serde(flatten)] pub span: Span, + /// The identifier (name) of the interface. pub id: BindingIdentifier<'a>, #[scope(enter_before)] pub extends: Option>>, @@ -699,6 +700,17 @@ pub enum TSSignature<'a> { TSMethodSignature(Box<'a, TSMethodSignature<'a>>), } +/// An index signature within a class, type alias, etc. +/// +/// ## Example +/// [playground link](https://oxc-project.github.io/oxc/playground/?code=3YCAAIC9gICAgICAgIC6nsrEgtem3AB/pQsrWlLnujiFhkHVtfeFMq5RMD7X5AzJnZ5R/ecQ5KG1FUFjzXvrxFXH0m6HpS+Ob3TC8gQXeRQygA%3D%3D) +/// ```ts +/// type MapOf = { +/// // _________ parameters (vec with 1 element) +/// [K: string]: T +/// // - type_annotation +/// } +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1127,6 +1139,30 @@ pub struct TSNonNullExpression<'a> { pub expression: Expression<'a>, } +/// Decorator +/// +/// Decorators are annotations on classes, methods, properties, and parameters. +/// They are usually either an [`IdentifierReference`] or an [`CallExpression`]. +/// +/// ## Example +/// ```ts +/// @Foo // class decorator +/// @Bar() // class decorator factory +/// class SomeClass { +/// @Freeze // property decorator +/// public x: number; +/// +/// @MethodDecorator // method decorator +/// public method( +/// @LogParam x: number // parameter decorator +/// ) { +/// // ... +/// } +/// } +/// ``` +/// +/// [`IdentifierReference`]: crate::ast::js::IdentifierReference +/// [`CallExpression`]: crate::ast::js::CallExpression #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -1174,12 +1210,15 @@ pub struct TSInstantiationExpression<'a> { pub type_parameters: Box<'a, TSTypeParameterInstantiation<'a>>, } +/// See [TypeScript - Type-Only Imports and Exports](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html) #[ast] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[serde(rename_all = "camelCase")] pub enum ImportOrExportKind { + /// `import { foo } from './foo'` Value, + /// `import type { foo } from './foo'` Type, } @@ -1194,6 +1233,7 @@ pub struct JSDocNullableType<'a> { #[serde(flatten)] pub span: Span, pub type_annotation: TSType<'a>, + /// Was `?` after the type annotation? pub postfix: bool, } diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index 5017ec8c13e84..d13a6e7f14b5d 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -1,6 +1,6 @@ use crate::ast::*; -use std::{borrow::Cow, cell::Cell, fmt, hash::Hash}; +use std::{borrow::Cow, cell::Cell, fmt, hash::Hash, ops::Deref}; use oxc_allocator::{Box, FromIn, Vec}; use oxc_span::{Atom, GetSpan, SourceType, Span}; @@ -84,6 +84,12 @@ impl<'a> Expression<'a> { ) } + /// `true` if this [`Expression`] is a literal expression for a primitive value. + /// + /// Does not include [`TemplateLiteral`]s, [`object literals`], or [`array literals`]. + /// + /// [`object literals`]: ObjectExpression + /// [`array literals`]: ArrayExpression pub fn is_literal(&self) -> bool { // Note: TemplateLiteral is not `Literal` matches!( @@ -157,6 +163,8 @@ impl<'a> Expression<'a> { } /// Determines whether the given expr is a `null` or `undefined` or `void 0` + /// + /// Corresponds to a [nullish value check](https://developer.mozilla.org/en-US/docs/Glossary/Nullish). pub fn is_null_or_undefined(&self) -> bool { self.is_null() || self.evaluate_to_undefined() } @@ -291,6 +299,12 @@ impl<'a> Hash for IdentifierReference<'a> { } } +impl<'a> PartialEq> for IdentifierReference<'a> { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + impl<'a> IdentifierReference<'a> { pub fn new(span: Span, name: Atom<'a>) -> Self { Self { span, name, reference_id: Cell::default(), reference_flag: ReferenceFlag::default() } @@ -318,6 +332,21 @@ impl<'a> BindingIdentifier<'a> { } } +impl<'a> Deref for BindingIdentifier<'a> { + type Target = Atom<'a>; + fn deref(&self) -> &Self::Target { + &self.name + } +} + +impl<'a> Deref for ArrayExpression<'a> { + type Target = Vec<'a, ArrayExpressionElement<'a>>; + + fn deref(&self) -> &Self::Target { + &self.elements + } +} + impl<'a> ArrayExpressionElement<'a> { pub fn is_elision(&self) -> bool { matches!(self, Self::Elision(_)) @@ -325,6 +354,7 @@ impl<'a> ArrayExpressionElement<'a> { } impl<'a> ObjectExpression<'a> { + /// Returns `true` if this object has a property named `__proto__` pub fn has_proto(&self) -> bool { use crate::syntax_directed_operations::PropName; self.properties.iter().any(|p| p.prop_name().is_some_and(|name| name.0 == "__proto__")) @@ -345,7 +375,7 @@ impl<'a> PropertyKey<'a> { .is_empty() .then(|| lit.quasi()) .flatten() - .map(std::convert::Into::into), + .map(|quasi| Cow::Borrowed(quasi.as_str())), _ => None, } } @@ -1149,6 +1179,17 @@ impl<'a> Class<'a> { } } + /// Get the name of the [`Class`]. + /// + /// ```ts + /// class Foo { /* ... */ } // Foo + /// // Not all classes have names, e.g. + /// var Foo = class {} + /// ``` + pub fn name(&self) -> Option<&Atom<'a>> { + self.id.as_ref().map(|id| &id.name) + } + /// `true` if this [`Class`] is an expression. /// /// For example, @@ -1176,6 +1217,13 @@ impl<'a> Class<'a> { } } +impl<'a> Deref for ClassBody<'a> { + type Target = Vec<'a, ClassElement<'a>>; + fn deref(&self) -> &Self::Target { + &self.body + } +} + impl<'a> Hash for Class<'a> { fn hash(&self, state: &mut H) { self.r#type.hash(state); @@ -1192,6 +1240,8 @@ impl<'a> Hash for Class<'a> { } impl<'a> ClassElement<'a> { + /// Returns `true` if this [`ClassElement`] is a static block or has a + /// static modifier. pub fn r#static(&self) -> bool { match self { Self::TSIndexSignature(_) | Self::StaticBlock(_) => false, @@ -1246,6 +1296,7 @@ impl<'a> ClassElement<'a> { } } + /// Returns `true` if this [`ClassElement`] is a property or accessor pub fn is_property(&self) -> bool { matches!(self, Self::PropertyDefinition(_) | Self::AccessorProperty(_)) } @@ -1297,6 +1348,30 @@ impl<'a> ClassElement<'a> { Self::StaticBlock(_) | Self::TSIndexSignature(_) => false, } } + + pub fn is_constructor(&self) -> bool { + matches!(self, Self::MethodDefinition(method) if method.kind.is_constructor()) + } + + /// Try to cast this [`ClassElement`] into a [`MethodDefinition`]. + /// + /// Returns [`None`] if `self` is any other kind of [`ClassElement`]. + pub fn as_method(&self) -> Option<&MethodDefinition<'a>> { + match self { + Self::MethodDefinition(method) => Some(method), + _ => None, + } + } + + /// Try to cast this [`ClassElement`] into a [`PropertyDefinition`]. + /// + /// Returns [`None`] if `self` is any other kind of [`ClassElement`]. + pub fn as_property(&self) -> Option<&PropertyDefinition<'a>> { + match self { + Self::PropertyDefinition(property) => Some(property), + _ => None, + } + } } impl PropertyDefinitionType { @@ -1306,28 +1381,32 @@ impl PropertyDefinitionType { } impl MethodDefinitionKind { + #[inline] pub fn is_constructor(&self) -> bool { matches!(self, Self::Constructor) } + #[inline] pub fn is_method(&self) -> bool { matches!(self, Self::Method) } + #[inline] pub fn is_set(&self) -> bool { matches!(self, Self::Set) } + #[inline] pub fn is_get(&self) -> bool { matches!(self, Self::Get) } - pub fn scope_flags(self) -> ScopeFlags { + pub const fn scope_flags(self) -> ScopeFlags { match self { - Self::Constructor => ScopeFlags::Constructor | ScopeFlags::Function, + Self::Constructor => ScopeFlags::Constructor.union(ScopeFlags::Function), Self::Method => ScopeFlags::Function, - Self::Get => ScopeFlags::GetAccessor | ScopeFlags::Function, - Self::Set => ScopeFlags::SetAccessor | ScopeFlags::Function, + Self::Get => ScopeFlags::GetAccessor.union(ScopeFlags::Function), + Self::Set => ScopeFlags::SetAccessor.union(ScopeFlags::Function), } } } From f4f1929746e19cbfddad6fa6ee1ae99be681f3a2 Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Mon, 22 Jul 2024 13:58:00 -0400 Subject: [PATCH 02/10] style: cargo fmt --- crates/oxc_ast/src/ast/js.rs | 22 +++++++++++----------- crates/oxc_ast/src/ast/ts.rs | 8 ++++---- crates/oxc_ast/src/ast_impl/js.rs | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 32ffd3242744a..35387c7c74a53 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -235,7 +235,7 @@ pub struct LabelIdentifier<'a> { } /// This Expression -/// +/// /// Corresponds to the `this` keyword. #[ast(visit)] #[derive(Debug, Hash)] @@ -293,7 +293,7 @@ pub struct Elision { } /// Object Expression -/// +/// /// ## Example /// ```ts /// const x = { foo: 'foo' } @@ -318,7 +318,7 @@ pub struct ObjectExpression<'a> { #[serde(untagged)] pub enum ObjectPropertyKind<'a> { /// Object Property - /// + /// /// ## Example /// ```ts /// const foo = 'foo' @@ -326,7 +326,7 @@ pub enum ObjectPropertyKind<'a> { /// // ^^^ ^^^^^^^^^ ObjectProperty(Box<'a, ObjectProperty<'a>>), /// Object Spread Property - /// + /// /// ## Example /// ```ts /// const obj = { foo: 'foo' } @@ -487,7 +487,7 @@ pub struct StaticMemberExpression<'a> { } /// `MemberExpression[?Yield, ?Await] . PrivateIdentifier` -/// +/// /// ## Example /// ```ts /// // _______ object @@ -508,16 +508,16 @@ pub struct PrivateFieldExpression<'a> { } /// Call Expression -/// +/// /// ## Examples /// ```ts /// // ___ callee /// const x = foo(1, 2) -/// +/// /// // ^^^^ arguments /// const y = foo.bar?.(1, 2) /// // ^ optional -/// +/// /// const z = foo(1, 2) /// // ^^^^^^^^^^^^^^ type parameters /// ``` @@ -535,7 +535,7 @@ pub struct CallExpression<'a> { } /// New Expression -/// +/// /// ## Example /// ```ts /// // callee arguments @@ -569,9 +569,9 @@ pub struct MetaProperty<'a> { } /// Spread Element -/// +/// /// An array or object spread. Could be used in unpacking or a declaration. -/// +/// /// ## Example /// ```ts /// const [first, ...rest] = arr diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 9996dd56668e3..094bbb421836c 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -1140,10 +1140,10 @@ pub struct TSNonNullExpression<'a> { } /// Decorator -/// +/// /// Decorators are annotations on classes, methods, properties, and parameters. /// They are usually either an [`IdentifierReference`] or an [`CallExpression`]. -/// +/// /// ## Example /// ```ts /// @Foo // class decorator @@ -1151,7 +1151,7 @@ pub struct TSNonNullExpression<'a> { /// class SomeClass { /// @Freeze // property decorator /// public x: number; -/// +/// /// @MethodDecorator // method decorator /// public method( /// @LogParam x: number // parameter decorator @@ -1160,7 +1160,7 @@ pub struct TSNonNullExpression<'a> { /// } /// } /// ``` -/// +/// /// [`IdentifierReference`]: crate::ast::js::IdentifierReference /// [`CallExpression`]: crate::ast::js::CallExpression #[ast(visit)] diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index d13a6e7f14b5d..94e1d4ef8a9cd 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -343,7 +343,7 @@ impl<'a> Deref for ArrayExpression<'a> { type Target = Vec<'a, ArrayExpressionElement<'a>>; fn deref(&self) -> &Self::Target { - &self.elements + &self.elements } } From 1bb9e8e2c94bd0d857d5f9b54963a20844e5888b Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Wed, 24 Jul 2024 13:30:40 -0400 Subject: [PATCH 03/10] fix: address pr review comments --- crates/oxc_ast/src/ast/js.rs | 17 ++++++++--------- crates/oxc_ast/src/ast/ts.rs | 4 ++-- crates/oxc_ast/src/ast_impl/js.rs | 27 +++++++-------------------- 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 35387c7c74a53..8fa4ca5737145 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -194,7 +194,7 @@ pub struct IdentifierReference<'a> { /// Flags indicating how the reference is used. /// /// This gets set in the bind step of semantic analysis, and will always be - /// [`ReferenceFlag::empty`] immediately after parsing. + /// [`ReferenceFlag::None`] immediately after parsing. #[serde(skip)] pub reference_flag: ReferenceFlag, } @@ -323,7 +323,7 @@ pub enum ObjectPropertyKind<'a> { /// ```ts /// const foo = 'foo' /// const x = { foo, bar: 'bar' } - /// // ^^^ ^^^^^^^^^ + /// // ^^^ ^^^^^^^^^^ ObjectProperty(Box<'a, ObjectProperty<'a>>), /// Object Spread Property /// @@ -519,7 +519,7 @@ pub struct PrivateFieldExpression<'a> { /// // ^ optional /// /// const z = foo(1, 2) -/// // ^^^^^^^^^^^^^^ type parameters +/// // ^^^^^^^^^^^^^^ type_parameters /// ``` #[ast(visit)] #[derive(Debug, Hash)] @@ -542,7 +542,7 @@ pub struct CallExpression<'a> { /// // ↓↓↓ ↓↓↓↓ /// const foo = new Foo(1, 2) /// // ↑↑↑↑↑↑↑↑ -/// // type parameters +/// // type_parameters /// ``` #[ast(visit)] #[derive(Debug, Hash)] @@ -1768,7 +1768,6 @@ pub struct ClassBody<'a> { /// /// accessor z() { return 5 } // ClassElement::AccessorProperty /// -/// /// // These are all ClassElement::MethodDefinitions /// get y() { return 5 } /// set y(value) { } @@ -2092,10 +2091,10 @@ pub struct ImportSpecifier<'a> { /// ## Example /// ```ts /// // local and imported name are the same - /// import { Foo } from 'foo' + /// import { Foo } from 'foo'; /// // ^^^ /// // imports can be renamed, changing the local name - /// import { Foo as Bar } from 'foo' + /// import { Foo as Bar } from 'foo'; /// // ^^^ /// ``` pub local: BindingIdentifier<'a>, @@ -2106,7 +2105,7 @@ pub struct ImportSpecifier<'a> { /// /// ## Example /// ```ts -/// import local from "source" +/// import local from "source"; /// ``` /// #[ast(visit)] @@ -2124,7 +2123,7 @@ pub struct ImportDefaultSpecifier<'a> { /// /// ## Example /// ```ts -/// import * as local from "source" +/// import * as local from "source"; /// ``` #[ast(visit)] #[derive(Debug, Hash)] diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 094bbb421836c..b988fd832750b 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -1216,9 +1216,9 @@ pub struct TSInstantiationExpression<'a> { #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[serde(rename_all = "camelCase")] pub enum ImportOrExportKind { - /// `import { foo } from './foo'` + /// `import { foo } from './foo'`; Value, - /// `import type { foo } from './foo'` + /// `import type { foo } from './foo'`; Type, } diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index 1b44c72ef0946..c0ddac13bed06 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -299,12 +299,6 @@ impl<'a> Hash for IdentifierReference<'a> { } } -impl<'a> PartialEq> for IdentifierReference<'a> { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - } -} - impl<'a> IdentifierReference<'a> { pub fn new(span: Span, name: Atom<'a>) -> Self { Self { span, name, reference_id: Cell::default(), reference_flag: ReferenceFlag::default() } @@ -375,12 +369,9 @@ impl<'a> PropertyKey<'a> { Self::NumericLiteral(lit) => Some(Cow::Owned(lit.value.to_string())), Self::BigIntLiteral(lit) => Some(Cow::Borrowed(lit.raw.as_str())), Self::NullLiteral(_) => Some(Cow::Borrowed("null")), - Self::TemplateLiteral(lit) => lit - .expressions - .is_empty() - .then(|| lit.quasi()) - .flatten() - .map(|quasi| Cow::Borrowed(quasi.as_str())), + Self::TemplateLiteral(lit) => { + lit.expressions.is_empty().then(|| lit.quasi()).flatten().map(Into::into) + } _ => None, } } @@ -1386,32 +1377,28 @@ impl PropertyDefinitionType { } impl MethodDefinitionKind { - #[inline] pub fn is_constructor(&self) -> bool { matches!(self, Self::Constructor) } - #[inline] pub fn is_method(&self) -> bool { matches!(self, Self::Method) } - #[inline] pub fn is_set(&self) -> bool { matches!(self, Self::Set) } - #[inline] pub fn is_get(&self) -> bool { matches!(self, Self::Get) } - pub const fn scope_flags(self) -> ScopeFlags { + pub fn scope_flags(self) -> ScopeFlags { match self { - Self::Constructor => ScopeFlags::Constructor.union(ScopeFlags::Function), + Self::Constructor => ScopeFlags::Constructor | ScopeFlags::Function, Self::Method => ScopeFlags::Function, - Self::Get => ScopeFlags::GetAccessor.union(ScopeFlags::Function), - Self::Set => ScopeFlags::SetAccessor.union(ScopeFlags::Function), + Self::Get => ScopeFlags::GetAccessor | ScopeFlags::Function, + Self::Set => ScopeFlags::SetAccessor | ScopeFlags::Function, } } } From 3dc2c78bdeb7869c04926c2c31f478528bd10d59 Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Wed, 24 Jul 2024 13:50:12 -0400 Subject: [PATCH 04/10] docs(ast): add some docs for TS ast nodes --- crates/oxc_ast/src/ast/ts.rs | 109 +++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 5 deletions(-) diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index b988fd832750b..f8013a1a2ca02 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -47,6 +47,20 @@ pub struct TSThisParameter<'a> { /// Enum Declaration /// /// `const_opt` enum `BindingIdentifier` { `EnumBody_opt` } +/// +/// ## Examples +/// +/// ```ts +/// enum Foo { +/// A, +/// B +/// } +/// // `Bar` has `r#const` set to `true` +/// const enum Bar { +/// A, +/// B +/// } +/// ``` #[ast(visit)] #[scope] #[derive(Debug)] @@ -63,6 +77,19 @@ pub struct TSEnumDeclaration<'a> { pub scope_id: Cell>, } +/// Enum Member +/// +/// ## Example +/// +/// ```ts +/// enum Foo { +/// // _ id +/// A = 1, +/// // ^ initializer +/// B // initializer will be `None` +/// +/// } +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -96,16 +123,43 @@ pub enum TSEnumMemberName<'a> { } } +/// TypeScript Type Annotation +/// +/// An annotation on a variable declaration, parameter, etc. +/// +/// ## Example +/// ```ts +/// const x: number = 1; +/// // ^^^^^^^^ +/// +/// function foo(x: number): number { return x; } +/// // ^^^^^^^^ ^^^^^^^^ +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[serde(tag = "type", rename_all = "camelCase")] pub struct TSTypeAnnotation<'a> { #[serde(flatten)] + /// starts at the `:` token and ends at the end of the type annotation pub span: Span, pub type_annotation: TSType<'a>, } +/// TypeScript Literal Type +/// +/// A type that is a literal value. Wraps a [`TSLiteral`]. +/// +/// ## Example +/// ```ts +/// const x: 'foo' = 'foo'; +/// // ^^^^^ +/// +/// type NonZero = N extends 0 ? never : N; +/// // ^ +/// type Three = NonZero<3>; +/// // ^ +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -131,6 +185,10 @@ pub enum TSLiteral<'a> { UnaryExpression(Box<'a, UnaryExpression<'a>>), } +/// TypeScript Type +/// +/// This is the root-level type for TypeScript types, kind of like [`Expression`] is for +/// expressions. #[ast(visit)] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -226,7 +284,13 @@ macro_rules! match_ts_type { } pub use match_ts_type; -/// `SomeType extends OtherType ? TrueType : FalseType;` +/// TypeScript Conditional Type +/// +/// ## Example +/// +/// ```ts +/// SomeType extends OtherType ? TrueType : FalseType; +/// ``` /// /// #[ast(visit)] @@ -244,7 +308,13 @@ pub struct TSConditionalType<'a> { pub scope_id: Cell>, } -/// string | string[] | (() => string) | { s: string } +/// TypeScript Union Type +/// +/// ## Example +/// +/// ```ts +/// string | string[] | (() => string) | { s: string } +/// ``` /// /// #[ast(visit)] @@ -280,7 +350,12 @@ pub struct TSParenthesizedType<'a> { pub type_annotation: TSType<'a>, } -/// keyof unique readonly +/// TypeScript Type Operators +/// +/// Includes +/// - `keyof` +/// - `unique` +/// - `readonly` /// /// #[ast(visit)] @@ -304,7 +379,15 @@ pub enum TSTypeOperatorOperator { Readonly, } -/// `let myArray: string[] = ["hello", "world"];` +/// TypeScript Array Type +/// +/// Does not include tuple types, which are stored as [`TSTupleType`]. +/// +/// ## Example +/// +/// ```ts +/// let myArray: string[] = ["hello", "world"]; +/// ``` /// /// #[ast(visit)] @@ -317,7 +400,15 @@ pub struct TSArrayType<'a> { pub element_type: TSType<'a>, } -/// `type I1 = Person["age" | "name"];` +/// TypeScript Index Access Type +/// +/// This is the type equivalent to expression member access. +/// +/// ## Example +/// +/// ```ts +/// type I1 = Person["age" | "name"]; +/// ``` /// /// #[ast(visit)] @@ -331,7 +422,13 @@ pub struct TSIndexedAccessType<'a> { pub index_type: TSType<'a>, } +/// TypeScript Tuple Type +/// +/// ## Example +/// +/// ```ts /// type `StringNumberPair` = [string, number]; +/// ``` /// /// #[ast(visit)] @@ -381,6 +478,8 @@ inherit_variants! { /// /// Inherits variants from [`TSType`]. See [`ast` module docs] for explanation of inheritance. /// +/// See [`TSNamedTupleMember`] for named tuple elements. +/// /// [`ast` module docs]: `super` #[ast(visit)] #[repr(C, u8)] From b5ecb2774599d2ba0a796dd74469264d1d8d1c77 Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Thu, 25 Jul 2024 11:00:09 -0400 Subject: [PATCH 05/10] remove Deref impls --- crates/oxc_ast/src/ast_impl/js.rs | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index c0ddac13bed06..09472812b3366 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -1,6 +1,6 @@ use crate::ast::*; -use std::{borrow::Cow, cell::Cell, fmt, hash::Hash, ops::Deref}; +use std::{borrow::Cow, cell::Cell, fmt, hash::Hash}; use oxc_allocator::{Box, FromIn, Vec}; use oxc_span::{Atom, GetSpan, SourceType, Span}; @@ -331,21 +331,6 @@ impl<'a> BindingIdentifier<'a> { } } -impl<'a> Deref for BindingIdentifier<'a> { - type Target = Atom<'a>; - fn deref(&self) -> &Self::Target { - &self.name - } -} - -impl<'a> Deref for ArrayExpression<'a> { - type Target = Vec<'a, ArrayExpressionElement<'a>>; - - fn deref(&self) -> &Self::Target { - &self.elements - } -} - impl<'a> ArrayExpressionElement<'a> { pub fn is_elision(&self) -> bool { matches!(self, Self::Elision(_)) @@ -1213,13 +1198,6 @@ impl<'a> Class<'a> { } } -impl<'a> Deref for ClassBody<'a> { - type Target = Vec<'a, ClassElement<'a>>; - fn deref(&self) -> &Self::Target { - &self.body - } -} - impl<'a> Hash for Class<'a> { fn hash(&self, state: &mut H) { self.r#type.hash(state); From cde974a9af9aefa2236cc08088c210d99b246d74 Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Thu, 25 Jul 2024 11:02:10 -0400 Subject: [PATCH 06/10] remove other method impls --- crates/oxc_ast/src/ast_impl/js.rs | 35 ------------------------------- 1 file changed, 35 deletions(-) diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index 09472812b3366..8dd17b38b88e7 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -1160,17 +1160,6 @@ impl<'a> Class<'a> { } } - /// Get the name of the [`Class`]. - /// - /// ```ts - /// class Foo { /* ... */ } // Foo - /// // Not all classes have names, e.g. - /// var Foo = class {} - /// ``` - pub fn name(&self) -> Option<&Atom<'a>> { - self.id.as_ref().map(|id| &id.name) - } - /// `true` if this [`Class`] is an expression. /// /// For example, @@ -1322,30 +1311,6 @@ impl<'a> ClassElement<'a> { Self::StaticBlock(_) | Self::TSIndexSignature(_) => false, } } - - pub fn is_constructor(&self) -> bool { - matches!(self, Self::MethodDefinition(method) if method.kind.is_constructor()) - } - - /// Try to cast this [`ClassElement`] into a [`MethodDefinition`]. - /// - /// Returns [`None`] if `self` is any other kind of [`ClassElement`]. - pub fn as_method(&self) -> Option<&MethodDefinition<'a>> { - match self { - Self::MethodDefinition(method) => Some(method), - _ => None, - } - } - - /// Try to cast this [`ClassElement`] into a [`PropertyDefinition`]. - /// - /// Returns [`None`] if `self` is any other kind of [`ClassElement`]. - pub fn as_property(&self) -> Option<&PropertyDefinition<'a>> { - match self { - Self::PropertyDefinition(property) => Some(property), - _ => None, - } - } } impl PropertyDefinitionType { From e4640a6cb73b567379cd0cbd93ec602b45317bfe Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Thu, 25 Jul 2024 11:44:43 -0400 Subject: [PATCH 07/10] docs(ast): add more doc comments for ESM nodes --- crates/oxc_ast/src/ast/js.rs | 67 ++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 8fa4ca5737145..7ffde3e280afd 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1951,6 +1951,19 @@ pub struct PrivateIdentifier<'a> { pub name: Atom<'a>, } +/// Class Static Block +/// +/// See: [MDN - Static initialization blocks](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Static_initialization_blocks) +/// +/// ## Example +/// +/// ```ts +/// class Foo { +/// static { +/// this.someStaticProperty = 5; +/// } +/// } +/// ``` #[ast(visit)] #[scope(flags(ScopeFlags::ClassStaticBlock))] #[derive(Debug)] @@ -1963,6 +1976,29 @@ pub struct StaticBlock<'a> { pub scope_id: Cell>, } +/// ES6 Module Declaration +/// +/// An ESM import or export statement. +/// +/// ## Example +/// +/// ```ts +/// // ImportDeclaration +/// import { foo } from 'foo'; +/// import bar from 'bar'; +/// import * as baz from 'baz'; +/// +/// // Not a ModuleDeclaration +/// export const a = 5; +/// +/// const b = 6; +/// +/// export { b }; // ExportNamedDeclaration +/// export default b; // ExportDefaultDeclaration +/// export * as c from './c'; // ExportAllDeclaration +/// export = b; // TSExportAssignment +/// export as namespace d; // TSNamespaceExportDeclaration +/// ``` #[ast(visit)] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -2169,9 +2205,13 @@ pub enum ImportAttributeKey<'a> { /// Named Export Declaration /// /// ## Example +/// /// ```ts -/// export { Foo }; -/// export type { Bar }; +/// // ________ specifiers +/// export { Foo, Bar }; +/// export type { Baz } from 'baz'; +/// // ^^^^ ^^^^^ +/// // export_kind source /// ``` #[ast(visit)] #[derive(Debug, Hash)] @@ -2190,7 +2230,9 @@ pub struct ExportNamedDeclaration<'a> { } /// Export Default Declaration +/// /// ## Example +/// /// ```ts /// export default HoistableDeclaration /// export default ClassDeclaration @@ -2207,6 +2249,15 @@ pub struct ExportDefaultDeclaration<'a> { pub exported: ModuleExportName<'a>, // the `default` Keyword } +/// Export All Declaration +/// +/// ## Example +/// +/// ```ts +/// // _______ exported +/// export * as numbers from '../numbers.js'; +/// // ^^^^^^^^^^^^^^^ source +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -2214,6 +2265,7 @@ pub struct ExportDefaultDeclaration<'a> { pub struct ExportAllDeclaration<'a> { #[serde(flatten)] pub span: Span, + /// If this declaration is re-named pub exported: Option>, pub source: StringLiteral<'a>, /// Will be `Some(vec![])` for empty assertion @@ -2221,6 +2273,17 @@ pub struct ExportAllDeclaration<'a> { pub export_kind: ImportOrExportKind, // `export type *` } +/// Export Specifier +/// +/// Each [`ExportSpecifier`] is one of the named exports in an [`ExportNamedDeclaration`]. +/// +/// ## Example +/// +/// ```ts +/// // ____ export_kind +/// import { type Foo as Bar } from './foo'; +/// // exported ^^^ ^^^ local +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] From 1dd049a93a5ce299ae1973a84fc552d16a8f3b07 Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Thu, 25 Jul 2024 12:38:49 -0400 Subject: [PATCH 08/10] docs(ast): add jsx doc comments --- crates/oxc_ast/src/ast/jsx.rs | 132 +++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/crates/oxc_ast/src/ast/jsx.rs b/crates/oxc_ast/src/ast/jsx.rs index ba9bbb4228275..dae2e5261557f 100644 --- a/crates/oxc_ast/src/ast/jsx.rs +++ b/crates/oxc_ast/src/ast/jsx.rs @@ -20,6 +20,22 @@ use super::{inherit_variants, js::*, literal::*, ts::*}; // 1.2 JSX Elements /// JSX Element +/// +/// Note that fragments (`<>`) are represented as [`JSXFragment`], unless they are written as +/// members of React (e.g. ``). +/// ## Examples +/// +/// ```tsx +/// // <- opening_element +/// some text // <- children +/// // <- closing_element +/// ``` +/// +/// ```tsx +/// // <- opening_element, no closing_element +/// ``` +/// +/// See: [JSX Syntax](https://facebook.github.io/jsx/) #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -33,6 +49,20 @@ pub struct JSXElement<'a> { } /// JSX Opening Element +/// +/// Opening tag in a [`JSXElement`]. +/// +/// ## Examples +/// ```tsx +/// // element with opening and closing tags (self_closing = false) +/// // ___ name +/// +/// // ^^^^^^^^^^^ attributes +/// +/// // element with self-closing tag (self_closing = true) +/// /> +/// // ^ type_parameters +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -40,13 +70,24 @@ pub struct JSXElement<'a> { pub struct JSXOpeningElement<'a> { #[serde(flatten)] pub span: Span, + /// Is this tag self-closing? + /// + /// ## Examples + /// ```tsx + /// // <- self_closing = true + /// // <- self_closing = false + /// ``` pub self_closing: bool, pub name: JSXElementName<'a>, + /// List of JSX attributes. In React-like applications, these become props. pub attributes: Vec<'a, JSXAttributeItem<'a>>, + /// Type parameters for generic JSX elements. pub type_parameters: Option>>, } /// JSX Closing Element +/// +/// Closing tag in a [`JSXElement`]. Not all JSX elements have a closing tag. #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -58,6 +99,13 @@ pub struct JSXClosingElement<'a> { } /// JSX Fragment +/// +/// A fragment written with the special `<>` syntax. When written as a `` component, +/// fragments will be represented as [`JSXElement`]s. +/// +/// Note that fragments cannot have attributes or type parameters. +/// +/// See: [`React.Fragment`](https://react.dev/reference/react/Fragment) #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -70,6 +118,7 @@ pub struct JSXFragment<'a> { pub children: Vec<'a, JSXChild<'a>>, } +/// JSX Opening Fragment (`<>`) #[ast] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -79,6 +128,7 @@ pub struct JSXOpeningFragment { pub span: Span, } +//// JSX Closing Fragment (``) #[ast] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -103,6 +153,12 @@ pub enum JSXElementName<'a> { } /// JSX Namespaced Name +/// +/// ## Example +/// +/// ```tsx +/// +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -110,11 +166,27 @@ pub enum JSXElementName<'a> { pub struct JSXNamespacedName<'a> { #[serde(flatten)] pub span: Span, + /// Namespace portion of the name, e.g. `Apple` in `` pub namespace: JSXIdentifier<'a>, + /// Name portion of the name, e.g. `Orange` in `` pub property: JSXIdentifier<'a>, } /// JSX Member Expression +/// +/// Used in [`JSXElementName`]. Multiple member expressions may be chained together. In this case, +/// [`object`] will be a [`member expression`]. +/// +/// ## Example +/// +/// ```tsx +/// // +/// +/// +/// ``` +/// +/// [`object`]: JSXMemberExpression::object +/// [`member expression`]: JSXMemberExpressionObject::MemberExpression #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -122,7 +194,9 @@ pub struct JSXNamespacedName<'a> { pub struct JSXMemberExpression<'a> { #[serde(flatten)] pub span: Span, + /// The object being accessed. This is everything before the last `.`. pub object: JSXMemberExpressionObject<'a>, + /// The property being accessed. This is everything after the last `.`. pub property: JSXIdentifier<'a>, } @@ -135,6 +209,19 @@ pub enum JSXMemberExpressionObject<'a> { MemberExpression(Box<'a, JSXMemberExpression<'a>>), } +/// JSX Expression Container +/// +/// Expression containers wrap [`JSXExpression`]s in JSX attributes and children using `{}`. +/// +/// ## Example +/// +/// ```tsx +/// // boolean-like and string-like expressions are not wrapped in containers. +/// // Here, only `container` is a JSXExpressionContainer. +/// +/// {4} // <- wrapped in container +/// +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -148,7 +235,8 @@ pub struct JSXExpressionContainer<'a> { inherit_variants! { /// JSX Expression /// -/// Inherits variants from [`Expression`]. See [`ast` module docs] for explanation of inheritance. +/// Gets wrapped by a [`JSXExpressionContainer`]. Inherits variants from [`Expression`]. See [`ast` +/// module docs] for explanation of inheritance. /// /// [`ast` module docs]: `super` #[ast(visit)] @@ -163,6 +251,7 @@ pub enum JSXExpression<'a> { } } +/// An empty JSX expression (`{}`) #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -185,6 +274,12 @@ pub enum JSXAttributeItem<'a> { } /// JSX Attribute +/// +/// ## Example +/// +/// ```tsx +/// +/// // name ^^^ ^^^^ value #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -197,6 +292,12 @@ pub struct JSXAttribute<'a> { } /// JSX Spread Attribute +/// +/// ## Example +/// ```tsx +/// +/// // ^^^^^^^^ argument +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -208,6 +309,8 @@ pub struct JSXSpreadAttribute<'a> { } /// JSX Attribute Name +/// +/// Part of a [`JSXAttribute`]. #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -218,6 +321,8 @@ pub enum JSXAttributeName<'a> { } /// JSX Attribute Value +/// +/// Part of a [`JSXAttribute`]. #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -229,6 +334,11 @@ pub enum JSXAttributeValue<'a> { Fragment(Box<'a, JSXFragment<'a>>), } +/// JSX Identifier +/// +/// Similar to [`IdentifierName`], but used in JSX elements. +/// +/// [`IdentifierName`]: super::IdentifierName #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -242,18 +352,28 @@ pub struct JSXIdentifier<'a> { // 1.4 JSX Children /// JSX Child +/// +/// Part of a [`JSXElement`]. #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[serde(untagged)] pub enum JSXChild<'a> { + /// `Some Text` Text(Box<'a, JSXText<'a>>), + /// `` Element(Box<'a, JSXElement<'a>>), + /// `<>` Fragment(Box<'a, JSXFragment<'a>>), + /// `{expression}` ExpressionContainer(Box<'a, JSXExpressionContainer<'a>>), + /// `{...spread}` Spread(Box<'a, JSXSpreadChild<'a>>), } +/// JSX Spread Child. +/// +/// Variant of [`JSXChild`] that represents an object spread (`{...expression}`). #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] @@ -264,6 +384,16 @@ pub struct JSXSpreadChild<'a> { pub expression: Expression<'a>, } +/// Text inside a JSX element. +/// +/// Not to be confused with a [`StringLiteral`]. +/// +/// ## Example +/// +/// ```tsx +/// Some text // `Some Text` is a JSXText, +/// "Some string" // but `"Some string"` is a StringLiteral. +/// ``` #[ast(visit)] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] From 07893c5437fec06d56427bbeacbea7373096dcba Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Thu, 25 Jul 2024 12:53:13 -0400 Subject: [PATCH 09/10] docs(ast): add links to oxc_ast crate docs --- crates/oxc_ast/src/lib.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/oxc_ast/src/lib.rs b/crates/oxc_ast/src/lib.rs index e8cd2ad42cefb..67f453b73e7d8 100644 --- a/crates/oxc_ast/src/lib.rs +++ b/crates/oxc_ast/src/lib.rs @@ -5,12 +5,25 @@ //! # Oxc AST //! +//! Abstract Syntax Tree nodes for Oxc. Supports both TypeScript and JavaScript. +//! //! This is almost similar to [estree](https://github.com/estree/estree) except a few places: -//! * `Identifier` is replaced with explicit `BindingIdentifier`, `IdentifierReference`, `IdentifierName` per spec -//! * `AssignmentExpression`.`left` `Pattern` is replaced with `AssignmentTarget` +//! * `Identifier` is replaced with explicit [`BindingIdentifier`], [`IdentifierReference`], [`IdentifierName`] per spec +//! * `AssignmentExpression`.`left` `Pattern` is replaced with [`AssignmentTarget`] +//! +//! ## Parsing +//! +//! You can obtain an AST by parsing source code with a [`Parser`] from [`oxc_parser`]. //! //! ## Cargo Features //! * `"serde"` enables support for serde serialization +//! +//! [`BindingIdentifier`]: ast::BindingIdentifier +//! [`IdentifierReference`]: ast::IdentifierReference +//! [`IdentifierName`]: ast::IdentifierName +//! [`AssignmentTarget`]: ast::AssignmentTarget +//! [`oxc_parser`]: +//! [`Parser`]: #[cfg(feature = "serialize")] mod serialize; From c5aedf888cff6ecf27b7e57a6e4c412a4e7d6848 Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Thu, 25 Jul 2024 15:28:23 -0400 Subject: [PATCH 10/10] style: clippy fix --- crates/oxc_ast/src/ast/jsx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/oxc_ast/src/ast/jsx.rs b/crates/oxc_ast/src/ast/jsx.rs index dae2e5261557f..e59c63929fe4e 100644 --- a/crates/oxc_ast/src/ast/jsx.rs +++ b/crates/oxc_ast/src/ast/jsx.rs @@ -128,7 +128,7 @@ pub struct JSXOpeningFragment { pub span: Span, } -//// JSX Closing Fragment (``) +/// JSX Closing Fragment (``) #[ast] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]