What the compiler makes from your component
The Markless compiler runs a chain of 15 passes that turn Counter.tsrx into render modules, state and view records, and one symbol per handler and text update.
Last page listed what the compiler decides before anything runs. Here is the real output for Counter.tsrx.
One file in, several outputs
That planner is @markless/compiler. It is not one big walk over your code. It is a chain of passes: small steps, each with named inputs and named outputs.
Think of an assembly line. That is an analogy. Each station takes the tray, adds one labeled part, and passes it on. For Counter.tsrx, the chain runs 15 passes, from tsrx-semantic-graph to symbol-resolver-module.
Press Next to step through the passes.
What does each compiler pass make from Counter.tsrx?
Press Next, or pick a step. Watch which part of the source each pass reads and what it writes.
The compiler parses Counter.tsrx without running it. It finds one state, one element, one click handler, and one text that reads count.
import { state } from '@markless/core'; export function Counter() @{ let count = state(0); (state) <button onClick={() => count++}>Count {count}</button> (event) (text read)}graphBindings: [{ id: "state:count", kind: "state", initialValue: 0 }]hostNodes: [{ id: "h0", tagName: "button" }]events: [{ hostNodeId: "h0", eventName: "click", handlerSource: "() => count++" }]templateReads: [{ hostNodeId: "h0", source: "count", target: { kind: "text", prefix: "Count " } }]- 1
tsrx-semantic-graph(this step) - 2
state-lowering - 3
payload-arena - 4
symbol-resolver - 5
render-data - 6
public-render-plan - 7
capture-analysis - 8
protocol-state - 9
protocol-view - 10
public-render-module - 11
payload-scripts - 12
symbol-modules - 13
runtime-demand-map - 14
trigger-groups - 15
symbol-resolver-module
Real output of compileTsrxModule() on this file. Pass order is passGraph.orderedPassIds. Grouping passes into steps is ours. Long values are cut to …. Server HTML is one way to deliver the records. The browser module needs no server.
Notice that the render step emits a browser module and a server module. The click and the text update leave as two separate symbols.
The real output
I compiled this component with compileTsrxModule() from the repo:
import { state } from '@markless/core';
export function Counter() @{
let count = state(0);
<button onClick={() => count++}>Count {count}</button>
}
The click became symbol:0. Your count++ is now an update to the graph node state:count:
import { marklessWriteScalar } from "@markless/web/fns/write-scalar";
/* scalar leaf marker: context.graph.update({ */
export function symbol_0(context) {
return marklessWriteScalar(context, { graphNodeId: "state:count", returnValue: "next", update(value) {
return Number(value) + 1;
} });
}
The text Count {count} became symbol:1. It writes one text node and nothing else:
export function symbol_1(context) {
return { type: "setText", locator: context.domUpdate?.hostNodeId ?? "h0", value: "Count " + (context.value == null ? "" : String(context.value)) + "" };
}
The view record ties them together. It says: a click on h0 runs symbol:0, and a change to state:count runs symbol:1 on h0. The same compile also emits a browser render module for render() and a server render module for renderToString().
Next: When a server renders the page, how does this plan reach the browser? The payload →
