Skip to content

MESH v0.11.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 14:42
· 68 commits to main since this release
5b48220

MESH v0.11.0 release notes

Prepared 2026-10-13 (not yet released: v0.10.0 is the latest release).

v0.11 makes control flow part of the language. Conditional and repeated structure are written as blocks in an element's content, not as reserved elements, so a tool that lists a model's components has nothing to offer for control flow and a model declares nothing for it.

Highlights

  • Blocks. {#if condition} … {:else if condition} … {:else} … {/if}, and {#each items as item (key)} … {/each}. A chain of {:else if} branches replaces the multi-way choice. The key of a repeat is mandatory.

    <list>
      {#if loading}
        <spinner />
      {:else if failed}
        <note>Could not load</note>
      {:else}
        {#each users as user (user.id)}
          <user-card user={user} />
        {/each}
      {/if}
    </list>
  • A branch or body is one element. If it is exactly one element it is that element; anything else (text, several nodes, none, a lone block or slot) is wrapped in a mesh-fragment for you.

  • Nothing to declare. The constructs a block lowers to (mesh-if, mesh-each, mesh-fragment) are built in. A manifest need not declare them, one that does is used as declared, and the model fingerprint covers only what is declared.

  • Same templates. The runtime, template-v1, render-v1, node identities and handler identifiers do not change. A program written with the old elements compiles to the same templates, apart from source spans.

  • Better messages. A mistake in a block names the block ("the condition of {#if}"). A repeat without a key is missing-repeat-key. Syntax errors in blocks say what is wrong and where: never closed, no open block, malformed opener.

  • Editors. The Tree-sitter grammar and highlight queries, the TextMate grammar, completion after the openers, and hover and go-to-definition on a repeat's binding.

Upgrading

  1. Move @valancex/mesh-runtime and @valancex/mesh-compiler (and mesh-lsp) to ^0.11.0 together. Before 1.0 a minor is a new range.

  2. Rewrite control flow as blocks. retired-element tells you which block to write.

    Before After
    <mesh-if when={a}>X</mesh-if> {#if a}X{/if}
    <mesh-if when={a}>X Y</mesh-if> (two alternatives) {#if a}X{:else}Y{/if}
    <mesh-each items={xs} as="x" key={x.id}>X</mesh-each> {#each xs as x (x.id)}X{/each}
    <mesh-switch><mesh-case when={a}>X</mesh-case><mesh-default>Y</mesh-default></mesh-switch> {#if a}X{:else}Y{/if}

    A two-alternative mesh-if and an {#if}/{:else} are both conditional sites, but an {:else} is written as a second conditional (as a mesh-switch was), so node identities under the else branch differ from the second alternative of the old element.

  3. A model that declared mesh-if, mesh-each, mesh-switch, mesh-case or mesh-default can drop them; one that keeps them is used as declared.

Compatibility

  • Breaking, before 1.0. The reserved elements are errors. Templates already compiled keep their meaning: stored renders and handler identifiers are unchanged.
  • Tooling. Tag completion and component listing offer nothing for a block. Target::Binding is a new resolution target (a Rust match on Target needs an arm).
  • Diagnostics. New codes retired-element and missing-repeat-key. invalid-switch is no longer reported and stays in the catalogue.
  • npm. All packages move to 0.11.0 together.

Stability

Blocks, retired-element and missing-repeat-key are stable on release. The runtime's behavior for the constructs a template names (mesh-if, mesh-each, mesh-fragment and their identity steps) stays provisional.