# JHS template syntax
Source of truth: `src/jhs/{Node,Nodes,Loop,Test,Expression,Variable,Func,View,Printer}.php` (the compiler) and
`src/classes/tNodes.php` (tag→class mapping). Everything below compiles to chained calls on a
`MMO.elements.View` (`$view` in generated JS — see the **views-and-data** resource).
## Expressions: `{path}` and `{=js}`
Anywhere in text content or an attribute value:
- `{some/path}` — reactive one-way bind to `this.view.get('some/path')`. Re-renders automatically when that
path changes in the view's `Map`. Compiles to `.$html('some/path')` (text content) or
`.$attr('name','some/path')` (attribute). **In text content the value is inserted as HTML, not escaped**:
`$html` → `linkHtml` → `Map.getHTML()` → `MMO.core.Printer.Shared().render(value, {html: true})` (through
the key's schema first, when it has one), which has no `'string'` renderer and returns a string as-is, then
the view marks it with `MMO.core.dom.html()` (`appendChildren` alone inserts a string as text) and `addHTML`
sets it with `innerHTML`. An `
` in the data runs; a plain `a c` loses its ``.
- `{=}` — inline JavaScript. Any `{path}` *inside* the JS is extracted as a function parameter (not
string-substituted), so `{= {firstname} + ' ' + {lastname} }` compiles to a function
`(firstname, lastname) => firstname + ' ' + lastname` that re-runs whenever either path changes, wired via
`.$htmlFn(...)` (text content — the return value goes to `innerHTML` the same way, without even the
Printer) / `.$attrFn(...)` (attribute). Paths containing `/` (not usable as JS identifiers) get an internal
`_v0_`, `_v1_`... alias.
- If your JS has no `return`, the whole expression is treated as the return value
(`{= {value} + 5 }` ≈ `return value + 5`).
- If it has statements, end with an explicit `return`: `{= let x = {value}; return x + 5 }`.
- Both land in the HTML sink because `compileText()` in `src/jhs/Printer.php` picks `$opts['html']??true` and
nothing in the compiler passes `false`. While that default stands, **untrusted (user/API) text must not go
through `{path}` or `{=...}` in text content** — `docs/SECURITY_REVIEW.md` §1–§2 records this, with the
proposed compiler fix. What does keep a value as text, checked against the compiled output and
`View.js`/`Template.js`:
- `bind-text="path"` on the element that should hold it → `.$text('path', true)` → a text node, set through
`nodeValue`. Strings only: `Map.getText()` renders a number as the Printer's ``, which shows up as
`[object HTMLSpanElement]`. It also writes the text back to `path` on `blur`, which only fires on a
focusable/editable element.
- `set-textContent="{path}"` → `.$attr('set-textContent','path')` → `node.textContent = value` (the `set-`
branch of `Template.setAttribute`). One-way on a plain element, any type — but it replaces everything the
element holds, so give it an element of its own.
- `$view.$text('path')` from a ``; `.text(string)` or `textContent` from a `
```
or
```html
...markup...
```
`