oxc / oxc_ast
This crate deserves a chapter of itself.
It is split in 3 parts:
- The structs that describe each node of the AST.
- A few implementations for those structs.
- Generated implementations under
oxc_ast/src/generated
generated by scripts intasks/ast_tools/src
, based on decorators like#[ast(...)]
A few concepts/keywords to be known:
The Oxc AST differs slightly from the estree AST by removing ambiguous nodes and introducing distinct types. For example, instead of using a generic estree
Identifier
, the Oxc AST provides specific types such asBindingIdentifier
,IdentifierReference
, andIdentifierName
. This clear distinction greatly enhances the development experience by aligning more closely with the ECMAScript specification.
- AST: Abstract Syntax Tree
- estree: one of the standard for representing an AST for JavaScript programs
- There are other kinds of ASTs
Structs
The oxc_ast
module exposes multiple structs such as Program
, IdentifierName
, ObjectProperty
...
They represent each node of the AST of a modern JavaScript program. Since oxc supports jsx and TypeScript by default, those nodes specific to their syntax are also present, like: JSXElement
, JSXFragment
, JSXExpression
, TSEnumDeclaration
, TSUnionType
...
https://github.com/oxc-project/oxc/tree/main/crates/oxc_ast/src/ast
Implementations
A few implementations specific to each of these structs are handcoded in crates/oxc_ast/src/ast_impl
.
Generated implementations
You can read this comment on top of the structs
#![allow(unused)] fn main() { // NB: `#[span]`, `#[scope(...)]`,`#[visit(...)]` and `#[generate_derive(...)]` do NOT do anything to the code. // They are purely markers for codegen used in `tasks/ast_tools` and `crates/oxc_traverse/scripts`. See docs in those crates. // Read [`macro@oxc_ast_macros::ast`] for more information. }
Here is an example of structs where you can see some macros applied:
#![allow(unused)] fn main() { #[ast(visit)] #[derive(Debug, Clone)] #[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ContentHash, ESTree)] #[estree(type = "Identifier")] pub struct BindingIdentifier<'a> { pub span: Span, pub name: Atom<'a>, #[estree(skip)] #[clone_in(default)] pub symbol_id: Cell<Option<SymbolId>>, } }
Like said in the comment above, those macros don't really do anything by themselves (like regular macros would), they are markers for the internal tool tasks/ast_tools
which:
- goes through the structs using the macros above, in different crates like
oxc_ast
,oxc_regular_expression
,oxc_span
,oxc_syntax
... - generate the implementations for those structs in a
./generated
folder for those structs
The generators are located in tasks/ast_tools/src
.
Why was it done like that, instead of regular use of macros ?
Because of performance and maintenance reasons. See the following links for more informations: