Skip to content

API Reference

Eric San edited this page Jun 14, 2026 · 2 revisions

API Reference

Who this is for: consumers. This is the whole supported surface; you do not need any other page to use es-parser (though Building covers installation and the gotchas are flagged inline below).

The supported surface (src/root.zig). The Stable API is the pipeline plus the data types; the Advanced modules expose semantic internals for tooling that reaches below the semantic facade.

// Stable
pub const ast        = @import("ast.zig");
pub const token      = @import("token.zig");
pub const span       = @import("span.zig");
pub const diagnostic = @import("diagnostic.zig");
pub const Lexer      = @import("lexer.zig");
pub const Parser     = @import("parser.zig").Parser;
pub const scope      = @import("scope.zig");
pub const symbol     = @import("symbol.zig");
pub const reference  = @import("reference.zig");
pub const semantic   = @import("semantic.zig");

// Advanced
pub const debug, code_path, layout, parent_builder,
          scope_events, event_resolver, scalar_lexer;

Languages

pub const token.Language = enum { js, ts, jsx, tsx, dts };
Language.isTs(self) bool        // ts, tsx, dts
Language.isJsx(self) bool       // jsx, tsx
Language.fromExtension(name) ?Language

fromExtension recognizes .tsx; .d.ts/.d.mts/.d.ctsdts; .ts/.mts/.ctsts; .jsxjsx; .js/.mjs/.cjsjs.

Lexer

Lexer.tokenize(alloc, source) !TokenizeResult                        // .js, defaults
Lexer.tokenizeWithLanguage(alloc, source, lang) !TokenizeResult
Lexer.tokenizeWithOptions(alloc, source, lang, is_module: bool) !TokenizeResult
Lexer.tokenizeWithAllOptions(alloc, source, lang, opts: TokenizeOptions) !TokenizeResult

TokenizeResult { tokens: TokenList, comment_starts/ends/kinds, comment_count } — call lr.deinit(alloc). Pass lr.tokens.slice() to the parser. The token list is not owned by the Ast; free it after the Ast. See Lexer and Tokens.

Parser

Parser.parse(alloc, source, tokens: TokenList.Slice) !Ast            // events on by default
Parser.parseWithOptions(alloc, source, tokens, opts: ParseOptions) !Ast
Parser.parseWithLanguage(alloc, source, tokens, language, is_module_file) !Ast
Parser.parseWithLanguageOpts(alloc, source, tokens, language, is_module_file, annex_b) !Ast

ParseOptions: language, is_module, global_return, is_strict (?bool, defaults to is_module), annex_b (default true), experimental_decorators, events_out (emit into a caller-owned ScopeEventStream), emit_events (emit into Ast.scope_events), streaming (?StreamingHooks). Call tree.deinit(alloc). See Parser and Performance and Concurrency.

Reading the AST

Ast exposes nodeTag, nodeMainToken, nodeData, tokenTag, tokenStart, tokenText, extraData(T, index), extraSlice(range), nodeSpan, nodeName. ESTree type strings come from layout.tag_names[ordinal] (or the C-ABI ez_tag_name). End positions for compound nodes come from node_end_toks. See AST and Memory Layout.

Semantic analysis

semantic.SemanticAnalyzer.analyze(alloc, &tree) !SemanticResult              // module mode
semantic.SemanticAnalyzer.analyzeModule(alloc, &tree, is_module: bool) !SemanticResult
semantic.SemanticAnalyzer.analyzeWithGlobals(alloc, &tree, globals: []const u8) !SemanticResult
semantic.SemanticAnalyzer.analyzeWithOptions(alloc, &tree, opts: Options) !SemanticResult

Options: is_module (default true), globals (null-separated names), build_parents (default false), build_ref_ranges (default true), need_cfg (default true), diagnose_redeclare (default false), annex_b (default true).

Returns error.MissingScopeEvents if the AST carries no events (parse with emit_events). The allocator must zero-fill fresh allocations — use an arena over page_allocator or semantic.ZeroingAllocator. Call sem.deinit(alloc).

SemanticResult fields: scopes: ScopeTree, symbols: SymbolTable, references: ReferenceTable, diagnostics, node_reachable, loop_exit_reachable, code_path_result (?CodePathBuilder.Result), parent_indices (iff build_parents), ref_by_sym (iff build_ref_ranges). See Semantic Analysis.

Querying scopes / symbols / references

  • ScopeTree: parent, kind, getFlags, nodeId, nearestVarScope (O(1)), nearestFunctionScope, isStrictMode, depth, isAncestor, getBindingsStart/getBindingsCount (range into the symbol table).
  • SymbolTable: getName, getFlags, getScope, getBindingKind, getDeclNode, getRefRange, isUsed, isInTDZ, isImmutable, isImplicitGlobal, allNames, allFlags.
  • ReferenceTable: getSymbol, isResolved, getKind, getNode, getScope, count, unresolvedCount.

Diagnostics

pub const diagnostic.Severity = enum { @"error", warning, info, hint };
pub const diagnostic.Diagnostic = struct { message: []const u8, span: Span, severity: Severity };

Diagnostic.format(line_starts, source, file_path, writer)file:line:col: severity: message; formatContext(...) adds a caret line; diagnostic.formatJson(...) emits a JSON array. Line/column come from span.Location.fromLineStarts over a line-start table — build one lazily with span.LineIndex.init(alloc, source) and li.locate(offset), or call span.computeLineStarts(alloc, source) directly. Parse diagnostics are in tree.errors; semantic diagnostics in sem.diagnostics. Filter by .severity.

End-to-end example

const es = @import("es_parser");

var lr = try es.Lexer.tokenizeWithLanguage(allocator, source, .ts);
defer lr.deinit(allocator);

var tree = try es.Parser.parseWithOptions(allocator, source, lr.tokens.slice(), .{
    .language = .ts,
    .is_module = true,
    .emit_events = true,
});
defer tree.deinit(allocator);

// Use an arena over page_allocator for the zeroing contract.
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
var sem = try es.semantic.SemanticAnalyzer.analyzeWithOptions(arena.allocator(), &tree, .{
    .is_module = true,
    .build_parents = true,
    .diagnose_redeclare = true,
});
defer sem.deinit(arena.allocator());

Next: Building and Integration · Architecture

Clone this wiki locally