Skip to content
These are temporary community docs. The official docs are in progress.Help improve them
Markless
Esc
↑↓navigate↵open⌘Jpreview
On this page

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.

1. Read the file into a semantic graph

The compiler parses Counter.tsrx without running it. It finds one state, one element, one click handler, and one text that reads count.

Your code: Counter.tsrx
import { state } from '@markless/core'; export function Counter() @{ let count = state(0); (state) <button onClick={() => count++}>Count {count}</button> (event) (text read)}
What this step wrote
semanticGraph (excerpt)
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 " } }]
The pass chain
Passes run1 of 15
  1. 1tsrx-semantic-graph (this step)
  2. 2state-lowering
  3. 3payload-arena
  4. 4symbol-resolver
  5. 5render-data
  6. 6public-render-plan
  7. 7capture-analysis
  8. 8protocol-state
  9. 9protocol-view
  10. 10public-render-module
  11. 11payload-scripts
  12. 12symbol-modules
  13. 13runtime-demand-map
  14. 14trigger-groups
  15. 15symbol-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 →

Was this page helpful?