Diagnostics
parse never throws. Every error it finds is in diagnostics, and the program holds everything it could read around them.
import { parse } from "@tsrx/yuku";
const { diagnostics } = parse(`export function Pair() @{ <h1>Title</h1> <p>Body</p> }`, { path: "Pair.tsrx" });
diagnostics[0];
// {
// severity: "error",
// message: "TSRX2011: A code block renders a single node; wrap multiple nodes or text in a fragment '<>…</>'.",
// path: "Pair.tsrx",
// start: 41,
// end: 42,
// labels: [],
// help: null
// }| Field | Is |
|---|---|
severity |
"error", "warning", "hint", or "info" |
message |
What went wrong. A TSRX error starts with its code, worded as @tsrx/core words it |
path |
The path option, or null |
start, end |
UTF-16 offsets into the source, like the offsets of nodes |
labels |
Related spans, each { start, end, message } |
help |
A hint, or null |
Errors in the JavaScript and TypeScript around TSRX are Yuku's, without a code. semanticErrors: true also reports the early errors that need scopes, such as a let declared twice.
TSRX codes#
Each code is the one @tsrx/core reports for the same mistake. The examples below are parsed when this site is built, and the build fails if one stops reporting its code.
| Code | Means |
|---|---|
TSRX1004 |
A <script> body holds </script, which would end it in HTML |
TSRX1005 |
A namespaced element, such as <svg:rect> |
TSRX1008 |
A control-flow directive without its { |
TSRX1009 |
A clause written without its @, such as else after an @if block |
TSRX1010 |
An @try with neither @pending nor @catch |
TSRX2008 |
A break inside an @case |
TSRX2009 |
A return inside an @case |
TSRX2011 |
A code block that renders more than one node |
TSRX2012 |
A statement after a code block's output |
TSRX2014 |
A dynamic tag that is not an identifier, a member access, or a string |
TSRX2015 |
An @for that is not a for...of |
export function Toggle({ on }) @{
@if (on) { <b>On</b> } else { <i>Off</i> }
}export function Pair() @{
<h1>Title</h1>
<p>Body</p>
}Wrap the two in a fragment, and the code block renders one node:
export function Pair() @{
<>
<h1>Title</h1>
<p>Body</p>
</>
}export function Late() @{
<p>Ready</p>
const ready = true;
}export function Keys({ object }) @{
@for (const key in object) { <li>{key}</li> }
}Show them#
A diagnostic's offsets are UTF-16, as JavaScript strings index, so source.slice(start, end) is the code it points at. A line and column come from the text before it:
for (const { message, start } of diagnostics) {
const before = source.slice(0, start);
const line = before.split("\n").length;
const column = start - before.lastIndexOf("\n");
console.log(`${line}:${column} ${message}`);
}