Svelte → D IRThe IR contract

The IR contract

Every page in this section is an answer to the same question: when I write this Svelte, what D actually lands in svelte-engine-ws/src-d/, and why that D and not some other D? The answer is never “because the official compiler said so.” svelte-d does not run the official compiler. The answer is: because libwasm already has a compile-time struct graph, and the printer pretty-prints that graph.

A .svelte file is walked — Pegged SvelteKit.Document when it succeeds, a named markup scan when it does not — into an AST whose node kinds are the libwasm D IR. An element is not an HtmlElement in a private JSON model that later becomes D. It is mixin NodeDef!"tag". A child component is @child. A mustache is @prop on the NamedNode that owns that text. An on:click is Slot plus @callback plus @connect. A {#if} is @visible plus setVisible. A {#each} is UnorderedList plus put. A {#await} is three @visible children plus wireAwait in App.ready (.await on the fork-asyncified module, JsPromise.then otherwise). The lang=d body is not lowered at all; it is already methods and fields on the same struct.

.svelte (kit path, author names)
    │  Pegged / markup scan + libdparse

AST ≡ libwasm D IR
    Element      → mixin NodeDef!"tag"
    Component    → @child ClickField clickField
    {ident}      → @prop on that NamedNode
    on:click={h} → Slot + @callback!"click" + @connect!"hButton.click"
    {#if ident}  → @visible!"child" bool ident + setVisible
    {#each xs}   → UnorderedList!X + put
    {#await job} → pending/then/catch @visible + wireAwait (.await / .then)
    lang=d proc  → methods / fields on the same struct
    lang=ts export greet → greet(ARGS...) → callTs!("ident.greet")
    extern(C) export add → exportDelegate("ident.add")
    │  pretty-print

src-d/lib/ClickField.d

JSON under .svelte-d/ir/ is a cache key of those nodes. It is how reprint-skip knows the dest would not change. It is not a language. Editing it by hand is editing a hash input.

Names are part of the contract. ClickField.svelte becomes module lib.ClickField / struct ClickField. The author’s go is still go; the printer adds goButton and @connect!"goButton.click" around it. {msg} is a child whose field is still msg. Dest directories sanitize only what D and LDC forbid (+, [). A printer that emitted Button3 would make the debug map a lie and this section unreadable.

Lodash, moment, and document() / window() appear only in procedural arms — a void go() body, a load helper, a PgLite query. They are not how a <button> is represented. A {#each} printed as _.map would be a different IR.

A construct that cannot be compile!()’d is a diagnostic, not a JavaScript fallback. If libwasm has no idiom yet, that is a titled seam in libwasm, then the engine, then the printer. svelte-d does not grow a third runtime to paper over the seam.

The rest of this section is the interactive language, construct by construct. Read them in order if you are learning; skip to the construct you are about to write if you already know ClickField. Cross-calling is the D ↔ TS convention (callTs / exportDelegate, optional args, dest npm). Engine goldens live in svelte-engine/src-svelte/lib/Combo*.svelte, ClickField.svelte, and IfToggle.svelte. They are the idiom library, not your application.

When you need the library those idioms call — compile!, this.update, UnorderedList, JsPromise, the handle table — open libwasm. Host files are the other cell: vibe.0.