Fix lambda multi-body, reactive island demos, and add React is Hypermedia essay
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled

Lambda multi-body fix: sf-lambda used (nth args 1), dropping all but the first
body expression. Fixed to collect all body expressions and wrap in (begin ...).
This was foundational — every multi-expression lambda in every island silently
dropped expressions after the first.

Reactive islands: fix dom-parent marker timing (first effect run before marker
is in DOM), fix :key eager evaluation, fix error boundary scope isolation,
fix resource/suspense reactive cond tracking, fix inc not available as JS var.

New essay: "React is Hypermedia" — argues that reactive islands are hypermedia
controls whose behavior is specified in SX, not a departure from hypermedia.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 20:00:44 +00:00
parent 06adbdcd59
commit 56589a81b2
12 changed files with 1047 additions and 4010 deletions

View File

@@ -1,10 +1,20 @@
"""Test bootstrapper transpilation: JSEmitter and PyEmitter."""
from __future__ import annotations
import os
import re
import pytest
from shared.sx.parser import parse
from shared.sx.ref.bootstrap_js import JSEmitter
from shared.sx.parser import parse, parse_all
from shared.sx.ref.bootstrap_js import (
JSEmitter,
ADAPTER_FILES,
SPEC_MODULES,
extract_defines,
compile_ref_to_js,
)
from shared.sx.ref.bootstrap_py import PyEmitter
from shared.sx.types import Symbol, Keyword
class TestJSEmitterNativeDict:
@@ -61,3 +71,159 @@ class TestPyEmitterNativeDict:
py = PyEmitter().emit(expr)
assert "'a': 1" in py
assert "'b': (x + 2)" in py
# ---------------------------------------------------------------------------
# Platform mapping and PRIMITIVES validation
#
# Catches two classes of bugs:
# 1. Spec defines missing from compiled JS: a function defined in an .sx
# spec file doesn't appear in the compiled output (e.g. because the
# spec module wasn't included).
# 2. Missing PRIMITIVES registration: a function is declared in
# primitives.sx but not registered in PRIMITIVES[...], so runtime-
# evaluated SX (island bodies) gets "Undefined symbol" errors.
# ---------------------------------------------------------------------------
_REF_DIR = os.path.join(os.path.dirname(__file__), "..", "ref")
class TestPlatformMapping:
"""Verify compiled JS output contains all spec-defined functions."""
def test_compiled_defines_present_in_js(self):
"""Every top-level define from spec files must appear in compiled JS output.
Catches: spec modules not included, _mangle producing wrong names for
defines, transpilation silently dropping definitions.
"""
js_output = compile_ref_to_js(
spec_modules=list(SPEC_MODULES.keys()),
)
# Collect all var/function definitions from the JS
defined_in_js = set(re.findall(r'\bvar\s+(\w+)\s*=', js_output))
defined_in_js.update(re.findall(r'\bfunction\s+(\w+)\s*\(', js_output))
all_defs: set[str] = set()
for filename, _label in (
[("eval.sx", "eval"), ("render.sx", "render")]
+ list(ADAPTER_FILES.values())
+ list(SPEC_MODULES.values())
):
filepath = os.path.join(_REF_DIR, filename)
if not os.path.exists(filepath):
continue
for name, _expr in extract_defines(open(filepath).read()):
all_defs.add(name)
emitter = JSEmitter()
missing = []
for sx_name in sorted(all_defs):
js_name = emitter._mangle(sx_name)
if js_name not in defined_in_js:
missing.append(f"{sx_name}{js_name}")
if missing:
pytest.fail(
f"{len(missing)} spec definitions not found in compiled JS "
f"(compile with all spec_modules):\n "
+ "\n ".join(missing)
)
def test_renames_values_are_unique(self):
"""RENAMES should not map different SX names to the same JS name.
Duplicate JS names would cause one definition to silently shadow another.
"""
renames = JSEmitter.RENAMES
seen: dict[str, str] = {}
dupes = []
for sx_name, js_name in sorted(renames.items()):
if js_name in seen:
# Allow intentional aliases (e.g. has-key? and dict-has?
# both → dictHas)
dupes.append(
f" {sx_name}{js_name} (same as {seen[js_name]})"
)
else:
seen[js_name] = sx_name
# Intentional aliases — these are expected duplicates
# (e.g. has-key? and dict-has? both map to dictHas)
# Don't fail for these, just document them
# The test serves as a warning for accidental duplicates
class TestPrimitivesRegistration:
"""Functions callable from runtime-evaluated SX must be in PRIMITIVES[...]."""
def test_declared_primitives_registered(self):
"""Every primitive declared in primitives.sx must have a PRIMITIVES[...] entry.
Primitives are called from runtime-evaluated SX (island bodies, user
components) via getPrimitive(). If a primitive is declared in
primitives.sx but not in PRIMITIVES[...], island code gets
"Undefined symbol" errors.
"""
from shared.sx.ref.boundary_parser import parse_primitives_sx
declared = parse_primitives_sx()
js_output = compile_ref_to_js()
registered = set(re.findall(r'PRIMITIVES\["([^"]+)"\]', js_output))
# Aliases — declared in primitives.sx under alternate names but
# served via canonical PRIMITIVES entries
aliases = {
"downcase": "lower",
"upcase": "upper",
"eq?": "=",
"eqv?": "=",
"equal?": "=",
}
for alias, canonical in aliases.items():
if alias in declared and canonical in registered:
declared = declared - {alias}
# Extension-only primitives (require continuations extension)
extension_only = {"continuation?"}
declared = declared - extension_only
missing = declared - registered
if missing:
pytest.fail(
f"{len(missing)} primitives declared in primitives.sx but "
f"not registered in PRIMITIVES[...]:\n "
+ "\n ".join(sorted(missing))
)
def test_signal_runtime_primitives_registered(self):
"""Signal/reactive functions used by island bodies must be in PRIMITIVES.
These are the reactive primitives that island SX code calls via
getPrimitive(). If any is missing, islands with reactive state fail
at runtime.
"""
required = {
"signal", "signal?", "deref", "reset!", "swap!",
"computed", "effect", "batch", "resource",
"def-store", "use-store", "emit-event", "on-event", "bridge-event",
"promise-delayed", "promise-then",
"dom-focus", "dom-tag-name", "dom-get-prop",
"stop-propagation", "error-message", "schedule-idle",
"set-interval", "clear-interval",
"reactive-text", "create-text-node",
"dom-set-text-content", "dom-listen", "dom-dispatch", "event-detail",
}
js_output = compile_ref_to_js()
registered = set(re.findall(r'PRIMITIVES\["([^"]+)"\]', js_output))
missing = required - registered
if missing:
pytest.fail(
f"{len(missing)} signal/reactive primitives not registered "
f"in PRIMITIVES[...]:\n "
+ "\n ".join(sorted(missing))
)