Skip to main content

Overview

Oxc Parser is a blazing-fast JavaScript and TypeScript parser written in Rust. It provides complete support for the latest ECMAScript standards, TypeScript syntax, JSX, and TSX with an easy-to-use API.

Full Language Support

ES2024+, TypeScript 5.x, JSX, and TSX with complete syntax coverage.

Spec Compliant

Passes all Test262, Babel, and TypeScript test suites.

High Performance

Optimized for speed with arena allocation and efficient algorithms.

Rich AST

Comprehensive AST with source locations, comments, and scope information.

Features

Language Support

  • ECMAScript: Full ES2024+ support including all stage 3+ proposals
  • TypeScript: Complete TypeScript 5.x syntax support
  • JSX/TSX: React JSX and TypeScript JSX
  • Decorators: Stage 3 decorators with metadata
  • Import Attributes: JSON modules and other import assertions

Parser Capabilities

  • Error Recovery: Continues parsing after syntax errors
  • Source Locations: Precise span information for every AST node
  • Comments: Preserves all comments with attachment information
  • Tokens: Optional token stream for syntax highlighting
  • Module Records: Import/export tracking for bundlers

Performance Optimizations

  • Arena Allocation: Fast AST allocation and deallocation
  • Compact Spans: Uses u32 offsets instead of usize
  • Lazy Parsing: Defers semantic analysis to separate phase
  • Zero-copy Strings: Efficient string handling
The parser can handle files up to 4 GiB due to u32 span offsets. This limitation is not a concern for virtually all real-world use cases.

Node.js API

Installation

npm
npm install oxc-parser

Basic Usage

import { parseSync } from 'oxc-parser';

// Parse JavaScript/TypeScript code
const result = parseSync('const x: number = 1;', {
  sourceFilename: 'example.ts'
});

if (result.errors.length === 0) {
  console.log('Parse successful!');
  console.log(result.program);
} else {
  console.error('Parse errors:', result.errors);
}

Async Parsing

import { parse } from 'oxc-parser';

const result = await parse(sourceCode, {
  sourceFilename: 'example.ts',
  sourceType: 'module'
});

Parser Options

interface ParseOptions {
  /** Source filename for error messages and source maps */
  sourceFilename?: string;
  
  /** Source type: 'script', 'module', 'unambiguous' */
  sourceType?: 'script' | 'module' | 'unambiguous';
  
  /** Enable JSX parsing */
  jsx?: boolean;
  
  /** Enable TypeScript parsing */
  typescript?: boolean;
  
  /** TypeScript definition file (.d.ts) */
  typescriptDefinition?: boolean;
  
  /** Include all tokens in output */
  includeTokens?: boolean;
  
  /** Preserve parenthesized expressions */
  preserveParens?: boolean;
}

Complete Example

import { parseSync } from 'oxc-parser';
import * as fs from 'fs';

const sourceCode = fs.readFileSync('example.tsx', 'utf-8');

const result = parseSync(sourceCode, {
  sourceFilename: 'example.tsx',
  sourceType: 'module',
  jsx: true,
  typescript: true
});

if (result.errors.length > 0) {
  result.errors.forEach(error => {
    console.error(`${error.message} at ${error.start}`);
  });
  process.exit(1);
}

// Access the AST
const { program } = result;

// program.body contains all top-level statements
for (const statement of program.body) {
  console.log(statement.type);
}

// Access comments
if (result.comments) {
  for (const comment of result.comments) {
    console.log(`Comment: ${comment.value}`);
  }
}

Rust API

Adding to Cargo.toml

Cargo.toml
[dependencies]
oxc_parser = "0.37"
oxc_allocator = "0.37"
oxc_span = "0.37"

Basic Usage

use oxc_allocator::Allocator;
use oxc_parser::Parser;
use oxc_span::SourceType;

fn main() {
    let allocator = Allocator::default();
    let source_text = "const x: number = 1;";
    let source_type = SourceType::from_path("example.ts").unwrap();
    
    let parser_return = Parser::new(
        &allocator,
        source_text,
        source_type
    ).parse();
    
    if parser_return.errors.is_empty() {
        println!("Parse successful!");
        println!("AST: {:#?}", parser_return.program);
    } else {
        for error in parser_return.errors {
            eprintln!("Error: {:?}", error);
        }
    }
}

Working with AST

use oxc_allocator::Allocator;
use oxc_parser::Parser;
use oxc_span::SourceType;
use oxc_ast::ast::*;

fn main() {
    let allocator = Allocator::default();
    let source_text = r#"
        function add(a, b) {
            return a + b;
        }
    "#;
    let source_type = SourceType::default();
    
    let ret = Parser::new(&allocator, source_text, source_type).parse();
    let program = ret.program;
    
    // Iterate through statements
    for stmt in &program.body {
        match stmt {
            Statement::FunctionDeclaration(func) => {
                println!("Function: {:?}", func.id.as_ref().unwrap().name);
            }
            _ => {}
        }
    }
}

Parser Configuration

use oxc_parser::config::{ParserConfig, LexerConfig};

// Configure lexer
let lexer_config = LexerConfig {
    allow_return_outside_function: false,
};

// Configure parser  
let parser_config = ParserConfig {
    preserve_parens: true,
    ..ParserConfig::default()
};

let parser_return = Parser::new(&allocator, source_text, source_type)
    .with_config(parser_config)
    .with_lexer_config(lexer_config)
    .parse();

Error Handling

let ret = Parser::new(&allocator, source_text, source_type).parse();

if !ret.errors.is_empty() {
    for error in ret.errors {
        let span = error.span();
        eprintln!(
            "Parse error at {}:{}: {}",
            span.start, span.end, error.message()
        );
    }
}

// Check if parser panicked (unrecoverable error)
if ret.panicked {
    eprintln!("Parser encountered unrecoverable error");
}

AST Structure

AST Node Types

The AST follows the ESTree specification with TypeScript extensions:
  • Program - Root node containing all statements
  • BlockStatement - { ... }
  • VariableDeclaration - const, let, var
  • FunctionDeclaration - Function declarations
  • ClassDeclaration - Class declarations
  • ImportDeclaration - Import statements
  • ExportNamedDeclaration - Named exports
  • Identifier - Variable names
  • Literal - String, number, boolean, null
  • BinaryExpression - a + b, x * y
  • CallExpression - Function calls
  • MemberExpression - Property access
  • ArrowFunctionExpression - Arrow functions
  • ObjectExpression - Object literals
  • ArrayExpression - Array literals
  • TSTypeAnnotation - Type annotations
  • TSInterfaceDeclaration - Interfaces
  • TSTypeAliasDeclaration - Type aliases
  • TSEnumDeclaration - Enums
  • TSModuleDeclaration - Namespaces
  • TSAsExpression - Type assertions
  • JSXElement - JSX elements
  • JSXFragment - JSX fragments
  • JSXAttribute - JSX props
  • JSXSpreadAttribute - Spread props
  • JSXText - Text content

Span Information

Every AST node includes precise source location:
interface Span {
  start: number;  // Byte offset of start
  end: number;    // Byte offset of end
}

interface Node {
  type: string;
  span: Span;
  // ... node-specific properties
}

Comments

Comments are preserved separately:
interface Comment {
  type: 'Line' | 'Block';
  value: string;
  span: Span;
}

Advanced Usage

Visitor Pattern (Rust)

Traverse the AST using the visitor pattern:
use oxc_ast_visit::Visit;
use oxc_ast::ast::*;

struct MyVisitor;

impl<'a> Visit<'a> for MyVisitor {
    fn visit_function_declaration(
        &mut self, 
        decl: &FunctionDeclaration<'a>
    ) {
        if let Some(id) = &decl.id {
            println!("Found function: {}", id.name);
        }
    }
    
    fn visit_call_expression(
        &mut self,
        expr: &CallExpression<'a>
    ) {
        println!("Found function call");
    }
}

// Use the visitor
let mut visitor = MyVisitor;
visitor.visit_program(&program);

Semantic Analysis

Combine parser with semantic analyzer:
use oxc_semantic::SemanticBuilder;

let parser_return = Parser::new(&allocator, source_text, source_type).parse();
let program = parser_return.program;

// Build semantic information (scopes, symbols, references)
let semantic = SemanticBuilder::new()
    .build(&program)
    .semantic;

// Access scope information
for scope_id in semantic.scopes().iter_scope_ids() {
    let scope = semantic.scopes().get_scope(scope_id);
    println!("Scope flags: {:?}", scope.flags);
}

// Access symbol information
for symbol_id in semantic.symbols().iter_symbol_ids() {
    let symbol = semantic.symbols().get_symbol(symbol_id);
    println!("Symbol: {}", symbol.name());
}

Module Records

Track imports and exports:
let parser_return = Parser::new(&allocator, source_text, source_type).parse();
let module_record = parser_return.module_record;

// Check import statements
for import in &module_record.import_entries {
    println!(
        "Import {} from {}",
        import.import_name,
        import.module_request
    );
}

// Check export statements
for export in &module_record.export_entries {
    println!("Export: {}", export.export_name);
}

Source Types

The parser needs to know what kind of file it’s parsing:
// Automatic detection from filename
const tsResult = parseSync(code, {
  sourceFilename: 'example.ts'
});

// Explicit source type
const jsxResult = parseSync(code, {
  jsx: true,
  typescript: false
});
// From path
let source_type = SourceType::from_path("example.tsx").unwrap();

// Manual construction
let source_type = SourceType::default()
    .with_module(true)
    .with_jsx(true)
    .with_typescript(true);

// Specific types
let ts = SourceType::ts();
let tsx = SourceType::tsx();
let jsx = SourceType::jsx();
let mjs = SourceType::mjs();

Performance Characteristics

Benchmarks

The parser is highly optimized:
  • Speed: Parses large files (100KB+) in milliseconds
  • Memory: Uses arena allocation for minimal overhead
  • Scalability: Linear time complexity

Best Practices

Reuse Allocator

Create one allocator and reuse for multiple parses in Rust.

Batch Parsing

Parse multiple files in parallel for maximum throughput.

Error Recovery

Parser continues after errors - check errors array.

Lazy Analysis

Parser doesn’t do semantic analysis - use oxc_semantic separately.

Use Cases

Build custom linting rules:
const result = parseSync(code, { sourceFilename: 'file.ts' });

// Check for specific patterns
function findConsoleUsage(program) {
  // Traverse AST and find console.* calls
}

Compatibility

Test Coverage

The parser passes conformance tests from:
  • Test262: Official ECMAScript test suite
  • Babel: Comprehensive JavaScript/JSX tests
  • TypeScript: Official TypeScript compiler tests

Standards Compliance

  • ECMAScript: ES2024 and all stage 3+ proposals
  • TypeScript: Version 5.x syntax
  • JSX: React JSX specification
  • ESTree: Follows ESTree AST specification

Comparison with Other Parsers

FeatureOxc ParserBabelTypeScriptSWC
LanguageRustJavaScriptTypeScriptRust
SpeedVery FastSlowMediumVery Fast
ES SupportLatestLatestLatestLatest
TS SupportFullVia pluginFullFull
Error RecoveryYesYesYesLimited
AST FormatESTreeESTreeTS ASTESTree

Limitations

  • Max file size: 4 GiB (due to u32 spans)
  • No semantic analysis: Use oxc_semantic separately
  • Syntax only: Doesn’t validate TypeScript types

Resources

GitHub Repository

Source code and examples

Rust Docs

Complete Rust API documentation

npm Package

Node.js bindings

AST Explorer

Explore AST interactively

FAQ

oxc_parser is just the parser. The full oxc crate includes parser, semantic analysis, linter, transformer, and minifier.
No, the parser produces an AST but doesn’t have a Babel plugin system. However, you can use the transformer for many common transformations.
No, it only parses TypeScript syntax. For type checking, use the TypeScript compiler.
Use oxc_codegen crate (Rust) or the transformer API (Node.js) to generate code from the AST.
The AST follows ESTree with TypeScript extensions. While we aim for stability, breaking changes may occur in major versions.

Build docs developers (and LLMs) love