Parse

import { parse } from "@tsrx/yuku";

const { program, comments, diagnostics } = parse(source, { path: "App.tsrx" });

parse takes the source and returns the program, every comment in source order, and every diagnostic. It never throws on bad input: it recovers, and returns a tree of everything it could read. Nodes have start and end, UTF-16 offsets into source, and no loc.

Options#

Option Default Description
path none Names the file in diagnostics. A path ending in .tsrx, or no path, reads TSRX. Any other path parses standard JavaScript or TypeScript, by its extension.
sourceType "module" "module", "script", or "commonjs", which allows a top-level return.
preserveParens false Keep ParenthesizedExpression nodes. false, as in @tsrx/core, so (a) reads as a.
semanticErrors false Also report the early errors that need scopes, such as a redeclared let.
attachComments false Also give each comment to the node it belongs to, in node.comments.
tokens false Keep every token, in tokens.

These are Yuku's options (opens in new tab), without lang and wasm: the TSRX build of Yuku is passed for you, and a .tsrx file is TSX with TSRX in it.

The tree#

The tree is ESTree, and TypeScript-ESTree for types, with TSRX's own node types, as @tsrx/core names them.

TSRX Node
@{ statements output } JSXCodeBlock, with body and the one node it renders in render
@if (test) { } @else { } JSXIfExpression. An @else if is an IfStatement in its alternate
@for (const x of xs; index i; key k) { } @empty { } JSXForExpression, with left, right, index, key, body, and empty
@switch (value) { @case a: { } @default: { } } JSXSwitchExpression, whose cases are standard SwitchCase nodes
@try { } @pending { } @catch (error, reset) { } JSXTryExpression, with block, pending, and a handler with a resetParam
<style> JSXStyleElement, its CSS kept as written in one JSXText
<script> A plain JSXElement, its body kept as written in one JSXText
<{tag}> A JSXElement whose name is a JSXExpressionContainer

Everything else is the JavaScript, TypeScript, and JSX that Yuku parses. The types are exported: import type { JSXForExpression } from "@tsrx/yuku".

export function List({ items }) @{
  <ul>
    @for (const item of items; index i; key item.id) {
      <li>{i + 1}. {item.name}</li>
    } @empty {
      <li>No items</li>
    }
  </ul>
}

The function's body is a JSXCodeBlock that renders the <ul>, and the loop is a JSXForExpression among its children, with index and key set.

CSS in <style>

A <style> keeps its CSS as text, as @tsrx/core does. A CSS tree for each <style> is next, parsed in the same pass by yuku-css (opens in new tab), once yuku#280 (opens in new tab) lands in Yuku.

Walk the tree#

walk from yuku/ast (opens in new tab) visits TSRX nodes by their fields, like any other. Its visitors can name TSRX types. yuku is a dependency of @tsrx/yuku; add it to your own to import it.

import { parse } from "@tsrx/yuku";
import { walk } from "yuku/ast";

const { program } = parse(source, { path: "List.tsrx" });

walk(program, {
  JSXForExpression(node) {
    console.log(node.key?.type); // "MemberExpression"
  },
});

yuku/ast also builds and checks nodes. See AST (opens in new tab) on yuku.fyi.

Comments and tokens#

comments lists every comment as { type, value, start, end }, where type is "Line" or "Block". With attachComments: true each comment is also in the comments of its node, which generate prints. With tokens: true, tokens is a TokenList, as Yuku's tokens (opens in new tab) describe.