^{ … } — meta context
The universal breakout mechanism. Captures all bindings in scope; classified as either compile-time or runtime by the compiler's dep analysis. Hosts type reflection, code emission, and runtime DOM inspection.
Syntax
Per §22.2:
meta-block ::= '^{' meta-body '}'
meta-body ::= (any sequence of scrml statements and expressions)
A file MAY contain zero or more top-level
^{}
blocks (file scope, outside any function, component,
markup, or nested block). Each is classified
independently as compile-time or runtime.
Two modes, automatically classified
The compiler's meta-checker pass classifies each
^{}
block based on its body. The classification determines
which capabilities are available and what code is
emitted.
Compile-time meta (§22.4)
A block is classified compile-time when its body uses one or more of these APIs and references no runtime-only values:
-
reflect(TypeName)— type introspection. Accepts either a compile-time-constant type name or a runtime variable holding one; the compiler picks the right resolution strategy (§22.4.2). -
emit(html)— inject generated markup. Normalises a small set of escape sequences (\n,\",\t,\\) before passing the string to the block splitter (§22.4.1). -
emit.raw(html)— same asemitbut verbatim, no escape-sequence normalisation. Use when the source already has the bytes you want emitted. -
bun.eval(…)— compile-time evaluation via Bun. The escape hatch for arbitrary build-time computation.
Top-level compile-time blocks evaluate in source order during compilation. The result is inlined into the output artifact.
Runtime meta (§22.5)
A block that references runtime-only values
(reactive cells, runtime DOM handles, runtime
meta
API surface) is classified as runtime. The compiler emits
an
_scrml_meta_effect
call that fires on
DOMContentLoaded
(or immediately, if DOMContentLoaded has already fired).
Top-level runtime blocks execute in source order.
Context preservation
Per §22.3, a
^{}
block preserves all bindings from its breakout point.
What's accessible inside:
-
All
let,const,lin, and@varbindings lexically in scope at the breakout point. - The type registry — user-declared types AND compiler-synthesised types (e.g. §14.8 generated table types from schema).
- The encoded variable name registry (per ADR-001).
The mechanism by which access is provided differs between compile-time and runtime mode, but the access guarantees are identical.
Worked example: type-driven code generation
Compile-time meta with
reflect
+
emit
— iterate over the fields of a struct type and
emit a label-and-input pair for each one:
type Signup:struct = {
email: string req
password: string req
name: string is some
}
^{
for (const field of reflect(Signup).fields) {
emit(`
<label>
${field.name}
<input type="${field.type === 'string' ? 'text' : field.type}"
name="${field.name}">
</label>
`)
}
}
At compile time, the block iterates the struct's three
fields and produces three
<label>
elements in the emitted artifact. The output is plain
markup — no runtime cost. For form-shaped types
specifically, the dedicated
formFor(StructType)
primitive (§41.14) is the canonical alternative; the
^{}
surface is for shapes that don't have a dedicated
primitive yet.
Phase-separation rules
Per §22.8, compile-time meta cannot reach forward into
the runtime — a compile-time block evaluating
@x
against a runtime cell is a compile error. Similarly,
runtime meta cannot reach back into the compile-time
stage — a runtime block calling
emit()
against the build artifact is invalid (the artifact has
already shipped). The compiler's classifier resolves the
phase per block; mis-mixed bodies fire one of the
E-META-*
errors at §22.11.
When to reach for
^{}
vs. dedicated primitives
scrml's strategy is to add named primitives for common
type-driven patterns rather than leaving them in the
^{}
surface. The L22 type-as-argument family (§53.14)
currently includes:
-
parseVariant(json, EnumType)(§41.13) — boundary-parsing for tagged-variant JSON. -
formFor(StructType)(§41.14) — type-driven form generation. -
schemaFor(StructType)(§41.15) — type-driven SQL DDL.
Reach for
^{}
when a dedicated primitive doesn't exist yet for the
shape you need. Reach for a dedicated primitive when one
does — the named primitive has better error
messages, better authority semantics, and a tighter
surface.
Related
- ${ … } — the logic context. Reactive computation, control flow, inline writes.
- ?{ … } — the SQL context.
-
<errors>
— the
registerMessagescall site for project-level error message catalogues is a^{}meta block.
Specification
Normative text: SPEC §22 in full
(overview §22.1, sigil §22.2, context-preservation
§22.3, compile-time meta §22.4, emit
normalisation §22.4.1,
reflect resolution
§22.4.2, runtime meta §22.5, phase-separation
§22.8, error codes §22.11). Spec lives at
compiler/SPEC.md
.