Crate oxc_parser

Source
Expand description

Oxc Parser for JavaScript and TypeScript

Oxc’s Parser has full support for

§Usage

The parser has a minimal API with three inputs (a memory arena, a source string, and a SourceType) and one return struct (a ParserReturn).

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

§Abstract Syntax Tree (AST)

Oxc’s AST is located in a separate oxc_ast crate. You can find type definitions for AST nodes here.

§Performance

The following optimization techniques are used:

  • AST is allocated in a memory arena (bumpalo) for fast AST drop
  • oxc_span::Span offsets uses u32 instead of usize
  • Scope binding, symbol resolution and complicated syntax errors are not done in the parser, they are delegated to the semantic analyzer
Because [`oxc_span::Span`] uses `u32` instead of `usize`, Oxc can only parse files up to 4 GiB in size. This shouldn't be a limitation in almost all cases.

§Examples

https://github.com/oxc-project/oxc/blob/main/crates/oxc_parser/examples/parser.rs

#![expect(clippy::print_stdout)]
use std::{fs, path::Path};

use oxc_allocator::Allocator;
use oxc_ast_visit::utf8_to_utf16::Utf8ToUtf16;
use oxc_parser::{ParseOptions, Parser};
use oxc_span::SourceType;
use pico_args::Arguments;

// Instruction:
// create a `test.js`,
// run `cargo run -p oxc_parser --example parser`
// or `just watch "cargo run -p oxc_parser --example parser"`

fn main() -> Result<(), String> {
    let mut args = Arguments::from_env();

    let show_ast = args.contains("--ast");
    let show_estree = args.contains("--estree");
    let show_comments = args.contains("--comments");
    let name = args.free_from_str().unwrap_or_else(|_| "test.js".to_string());

    let path = Path::new(&name);
    let source_text = fs::read_to_string(path).map_err(|_| format!("Missing '{name}'"))?;
    let source_type = SourceType::from_path(path).unwrap();

    let allocator = Allocator::default();
    let ret = Parser::new(&allocator, &source_text, source_type)
        .with_options(ParseOptions { parse_regular_expression: true, ..ParseOptions::default() })
        .parse();
    let mut program = ret.program;

    if show_comments {
        println!("Comments:");
        for comment in &program.comments {
            let s = comment.content_span().source_text(&source_text);
            println!("{s}");
        }
    }

    if show_ast {
        println!("AST:");
        println!("{program:#?}");
    }

    if show_estree {
        Utf8ToUtf16::new(&source_text).convert_program(&mut program);
        if source_type.is_javascript() {
            println!("ESTree AST:");
            println!("{}", program.to_pretty_estree_js_json());
        } else {
            println!("TS-ESTree AST:");
            println!("{}", program.to_pretty_estree_ts_json());
        }
    }

    if ret.errors.is_empty() {
        println!("Parsed Successfully.");
    } else {
        for error in ret.errors {
            let error = error.with_source_code(source_text.clone());
            println!("{error:?}");
        }
        println!("Parsed with Errors.");
    }

    Ok(())
}

§Parsing TSX

use oxc_allocator::Allocator;
use oxc_parser::{Parser, ParserReturn};
use oxc_span::SourceType;

fn main() {
    let source_text = r"
import React from 'react';

/**
 * A simple counter component
 */
export const Counter: React.FC = () => {
    const [count, setCount] = React.useState(0);

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
            <button onClick={() => setCount(count - 1)}>Decrement</button>
        </div>
    )
}";

    // Memory arena where AST nodes get stored
    let allocator = Allocator::default();
    // Infers TypeScript + JSX + ESM modules
    let source_type = SourceType::from_path("Counter.tsx").unwrap();

    let ParserReturn {
        program,  // AST
        errors,   // Syntax errors
        panicked, // Parser encountered an error it couldn't recover from
        ..
    } = Parser::new(&allocator, source_text, source_type).parse();

    assert!(!panicked);
    assert!(errors.is_empty());
    assert!(!program.body.is_empty());
    assert_eq!(program.comments.len(), 1);
}

§Visitor

See Visit and VisitMut.

§Visiting without a visitor

For ad-hoc tasks, the semantic analyzer can be used to get a parent pointing tree with untyped nodes, the nodes can be iterated through a sequential loop.

for node in semantic.nodes().iter() {
    match node.kind() {
        // check node
    }
}

See full linter example

Structs§

ParseOptions
Parse options
Parser
Recursive Descent Parser for ECMAScript and TypeScript
ParserReturn
Return value of Parser::parse consisting of AST, errors and comments