scrml.dev v0.7.1
Reference › Contexts

^{ … } — 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 as emit but 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 @var bindings 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 registerMessages call 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 .