jsxPieces
uses tcmts.core.ts.parse, which uses tcmts.core.ts.compiler, which uses process
The parts of JSX that highlighting takes from the syntax tree rather than the scanner, by where each starts: text
between tags (which the scanner would read as code), closing tags, and tag and attribute names (aria-label is
one name). A built-in tag (div) is coloured as a tag (g), a component as a type (t), linked if it's in refs;
an attribute name as an attribute (a).
import { esc } from "#codebase/tcmts/cli/pages"; import type { HighlightRef } from "#codebase/tcmts/cli/pages"; import { parse } from "#codebase/tcmts/core/ts"; import { isJsxAttribute as ts_isJsxAttribute, isJsxClosingElement as ts_isJsxClosingElement, isJsxClosingFragment as ts_isJsxClosingFragment, isJsxOpeningElement as ts_isJsxOpeningElement, isJsxSelfClosingElement as ts_isJsxSelfClosingElement, isJsxText as ts_isJsxText, } from "typescript/ast"; import type { Node as ts_Node } from "typescript/ast"; export function jsxPieces(text: string, refs: Map<number, HighlightRef>): Map<number, { end: number; html: string }> { const sf = parse("code.tsx", text); const out = new Map<number, { end: number; html: string }>(); const tag = (n: ts_Node) => { const start = n.getStart(sf); const raw = text.slice(start, n.end); const ref = refs.get(start); if (ref?.hash) return `<a class="ref t" data-hash="${ref.hash}">${esc(raw)}</a>`; return `<span class="${/^[a-z][\w-]*$/.test(raw) ? "g" : "t"}">${esc(raw)}</span>`; }; const walk = (n: ts_Node): void => { if (ts_isJsxText(n)) { if (n.end > n.pos) out.set(n.pos, { end: n.end, html: esc(text.slice(n.pos, n.end)) }); return; } if (ts_isJsxClosingElement(n)) { const start = n.getStart(sf); const name = n.tagName; out.set(start, { end: n.end, html: esc(text.slice(start, name.getStart(sf))) + tag(name) + esc(text.slice(name.end, n.end)) }); return; } if (ts_isJsxClosingFragment(n)) { const start = n.getStart(sf); out.set(start, { end: n.end, html: esc(text.slice(start, n.end)) }); return; } if (ts_isJsxOpeningElement(n) || ts_isJsxSelfClosingElement(n)) out.set(n.tagName.getStart(sf), { end: n.tagName.end, html: tag(n.tagName) }); if (ts_isJsxAttribute(n)) { const start = n.name.getStart(sf); out.set(start, { end: n.name.end, html: `<span class="a">${esc(text.slice(start, n.name.end))}</span>` }); } n.forEachChild(walk, (arr) => void arr.forEach(walk)); }; walk(sf); return out; }