API

Everything the package exports, straight from npm/yuku-tsrx/index.d.ts (opens in new tab). The package is ESM.

import {
  analyze,
  decode,
  decodeAnalyzer,
  encode,
  generate,
  isEventAttribute,
  normalizeEventName,
  parse,
  parseModule,
  parseWire,
  walk,
} from "yuku-tsrx";

Eleven functions. Nothing else is exported at runtime; the rest of the file is types.

Function Group
parse Parsing
parseModule Parsing
parseWire Parsing
analyze Analysis
generate Generation
decode Wire and decoders
decodeAnalyzer Wire and decoders
encode Wire and decoders
walk walk
isEventAttribute Helpers
normalizeEventName Helpers

Parsing#

export function parse(source: string | Uint8Array, options?: ParseOptions): ParseResult;

Parses source and returns the program, its comments, and its diagnostics. It does not throw for bad source; problems come back as diagnostics.

export function parseModule(
	source: string | Uint8Array,
	filename: string,
	options?: ParseModuleOptions,
): Program;

Parses source as a module and returns the Program. The language is inferred from filename, sourceType is "module", and semanticErrors defaults to true. Throws a SyntaxError on the first diagnostic of severity "error", unless collect or loose is set, in which case the fatal diagnostics are pushed into the errors array you passed and the program is returned anyway.

export function parseWire(source: string | Uint8Array, options?: ParseOptions): ArrayBuffer;

Parses source and returns the raw transfer buffer, undecoded.

ParseOptions#

export type SourceLang = "js" | "jsx" | "ts" | "tsx" | "dts";
export type SourceType = "script" | "module" | "commonjs";

export interface ParseOptions {
	lang?: SourceLang;
	sourceType?: SourceType;
	preserveParens?: boolean;
	semanticErrors?: boolean;
	attachComments?: boolean;
	loose?: boolean;
}

ParseModuleOptions#

export interface ParseModuleOptions extends Omit<ParseOptions, "sourceType"> {
	collect?: boolean;
	errors?: Diagnostic[];
	comments?: Comment[];
}

ParseResult#

export interface ParseResult {
	program: Program;
	comments: BaseNode[];
	diagnostics: Diagnostic[];
}

Diagnostic#

export interface DiagnosticLabel {
	message?: string;
	start?: number;
	end?: number;
}

export interface Diagnostic {
	severity: "error" | "warning" | "hint" | "info";
	message: string;
	start: number;
	end: number;
	help: string | null;
	labels: DiagnosticLabel[];
}

Parser has what each option does and how diagnostics are split between fatal and recoverable.

Analysis#

export function analyze(source: string | Uint8Array, options?: ParseOptions): AnalyzeResult;

Parses source and resolves its scopes, symbols, and references.

export interface AnalyzeResult extends ParseResult {
	readonly semantic: SemanticView;
}

export interface SemanticView {
	reference: { count: number; name(index: number): string; symbolId(index: number): number | null };
	scope: { count: number; kind(index: number): string };
	symbol: { count: number; name(index: number): string };
}

Analyzer has the details.

Generation#

export function generate(program: Program, options?: GenerateOptions): GenerateResult;

Prints program back to source. Throws a TypeError if the argument is not a Program node from this parser.

export interface GenerateOptions {
	strip?: boolean;
	minify?: boolean | { whitespace?: boolean; syntax?: boolean; quotes?: boolean };
	format?: "pretty" | "compact";
	indent?: number;
	quotes?: "preserve" | "double" | "single" | "shortest";
	comments?: boolean | "all" | "some" | "none" | "line" | "block";
}

export interface GenerateResult {
	code: string;
	errors: Array<{ message: string; start: number; end: number }>;
	map: unknown | null;
}

Code Generator has what each option does.

Wire and decoders#

The native addon returns a buffer. These three are the boundary between that buffer and JavaScript objects.

export function decode(buffer: ArrayBuffer, source: string): ParseResult;

Decodes a buffer from parseWire. source is the text the buffer was produced from; node positions index into it.

export function decodeAnalyzer(buffer: ArrayBuffer, source: string): unknown;

Decodes an analyzer buffer. Typed unknown because the analyzer buffer carries more than the SemanticView interface promises. analyze uses it internally and gives you the typed result.

export function encode(program: Program): ArrayBuffer;

Encodes a program into a buffer the native side can read. generate uses it on the way in.

walk#

export type WalkVisitor = (
	node: BaseNode,
	context: { parent: BaseNode | null; state: unknown },
) => void;

export type Visitors = Record<
	string,
	WalkVisitor | { enter?: WalkVisitor; leave?: WalkVisitor }
> & {
	enter?: WalkVisitor;
	leave?: WalkVisitor;
};

export function walk<T extends BaseNode>(root: T, visitors: Visitors, state?: unknown): T;

Visits every node under root and returns root. A key is a node type, or enter / leave for every node. It descends into every property except comments.

Helpers#

Two string functions for JSX event attributes. Neither one touches the AST.

export function isEventAttribute(attribute: string): boolean;

true when the attribute name starts with on, is longer than two characters, and has an uppercase third character. So onClick is an event attribute and once is not.

export function normalizeEventName(attribute: string): string;

Drops the leading on, drops a trailing Capture, and lowercases the rest. onClick gives click and onClickCapture gives click. Two names keep their suffix because it is part of the event name rather than a capture marker: onGotPointerCapture gives gotpointercapture and onLostPointerCapture gives lostpointercapture.

Node types#

Every node has type, start, and end. start and end are offsets into the source string you passed in.

export interface BaseNode {
	type: string;
	start: number;
	end: number;
}

TSRX nodes#

export interface JSXCodeBlock extends Expression {
	type: "JSXCodeBlock";
	body: Statement[];
	render: Expression | TSRXExpression | null;
}

export interface JSXIfExpression extends Expression {
	type: "JSXIfExpression";
	test: Expression;
	consequent: BlockStatement;
	alternate: JSXIfExpression | BlockStatement | null;
}

export interface JSXForExpression extends Expression {
	type: "JSXForExpression";
	statement: ForOfStatement | ForStatement;
	empty: BlockStatement | null;
}

export interface JSXSwitchExpression extends Expression {
	type: "JSXSwitchExpression";
	statement: SwitchStatement;
}

export interface JSXTryExpression extends Expression {
	type: "JSXTryExpression";
	statement: TryStatement;
	pending: BlockStatement | null;
}

export interface TSRXExpression extends Expression {
	type: "TSRXExpression";
	expression: Expression;
}

export interface StyleSheet extends BaseNode {
	type: "StyleSheet";
	source: string;
}

export interface JSXStyleElement extends Expression {
	type: "JSXStyleElement";
	openingElement: TSRXJSXOpeningElement;
	children: StyleSheet[];
	closingElement: TSRXJSXClosingElement;
	css: string;
}

Ordinary nodes with TSRX fields#

ForOfStatement gains index and key for the ; index and ; key tail clauses:

export interface ForOfStatement extends Statement {
	type: "ForOfStatement";
	left: VariableDeclaration | Pattern;
	right: Expression;
	body: Statement;
	await: boolean;
	index: Expression | undefined;
	key: Expression | undefined;
}

Program and BlockStatement admit TSRX expressions and JSX directly in their bodies:

export interface Program extends BaseNode {
	type: "Program";
	body: Array<Statement | TSRXExpression | TSRXJSXElement | TSRXJSXFragment>;
	sourceType: "script" | "module";
	hashbang?: string | null;
}

TSRXJSXElement and TSRXJSXFragment are the ordinary JSX nodes, still typed "JSXElement" and "JSXFragment", widened to admit TSRX children:

export type TSRXJSXChild = BaseNode | TSRXExpression | JSXStyleElement;

export interface TSRXJSXElement extends Expression {
	type: "JSXElement";
	openingElement: TSRXJSXOpeningElement;
	children: TSRXJSXChild[];
	closingElement: TSRXJSXClosingElement | null;
}

The rest of the node types, VariableDeclaration, SwitchStatement, TryStatement, CatchClause, the JSX opening and closing elements, and Comment, are the ordinary ones and are all in index.d.ts.