HS: put hyperscript reprocessing — generator fix (+1 test)

Partial fix. The generator's block-form `evaluate(() => { ... })`
swallowed blocks that weren't window-setup assignments (e.g. `const e =
new Event(...); elem.dispatchEvent(e);`). It now only `continue`s when
at least one window-setup pair was parsed; otherwise falls through to
downstream patterns. Also added a new pattern that recognises the
`evaluate(() => { const e = new Event(...); document.querySelector(SEL)
.dispatchEvent(e); })` shape and emits a `dom-dispatch` op.

Still failing: "at start of", "in a element target", "in a symbol
write" — root cause here is that the inserted-button's hyperscript
handler still isn't activating in the afterbegin / innerHTML paths.
Tracked separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 22:10:44 +00:00
parent 4800246b23
commit f21eb00878
2 changed files with 31 additions and 6 deletions

View File

@@ -9627,6 +9627,7 @@
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "b1")) "40")
(dom-dispatch (dom-query-by-id "b1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "b1")) "42")
))
(deftest "properly processes hyperscript at start of"
@@ -9638,6 +9639,7 @@
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "b1")) "40")
(dom-dispatch (dom-query-by-id "b1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "b1")) "42")
))
(deftest "properly processes hyperscript in before"
@@ -9661,6 +9663,7 @@
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "b1")) "40")
(dom-dispatch (dom-query-by-id "b1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "b1")) "42")
))
(deftest "properly processes hyperscript in new content in a symbol write"
@@ -9671,6 +9674,7 @@
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content (dom-query-by-id "b1")) "40")
(dom-dispatch (dom-query-by-id "b1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "b1")) "42")
))
(deftest "put null into attribute removes it"

View File

@@ -889,14 +889,19 @@ def parse_dev_body(body, elements, var_names):
continue
# evaluate(() => { window.X = Y; ... }) — block window setup.
# Only `continue` if at least one window-setup was parsed, otherwise
# fall through to other patterns that may match this `evaluate(...)`.
m = re.match(r'evaluate\(\s*\(\)\s*=>\s*\{(.+)\}\s*\)\s*$', stmt_na, re.DOTALL)
if m:
for name, sx_val in _window_setup_ops(m.group(1)):
if seen_html:
ops.append(f'(host-set! (host-global "window") "{name}" {sx_val})')
else:
pre_setups.append((name, sx_val))
continue
setups_here = list(_window_setup_ops(m.group(1)))
if setups_here:
for name, sx_val in setups_here:
if seen_html:
ops.append(f'(host-set! (host-global "window") "{name}" {sx_val})')
else:
pre_setups.append((name, sx_val))
continue
# fall through
# evaluate(() => document.querySelector(SEL).innerHTML = VAL) — DOM reset.
m = re.match(
@@ -954,6 +959,22 @@ def parse_dev_body(body, elements, var_names):
ops.append(f'(dom-dispatch {target} "{m.group(4)}" nil)')
continue
# evaluate(() => { const e = new Event(NAME, {...}); document.querySelector(SEL).dispatchEvent(e); })
# Common upstream pattern for dispatching a non-bubbling click.
m = re.match(
r"evaluate\(\s*\(\)\s*=>\s*\{\s*"
r"const\s+(\w+)\s*=\s*new\s+(?:Custom)?Event\(\s*(['\"])([^'\"]+)\2"
r"(?:\s*,\s*\{[^}]*\})?\s*\)\s*;\s*"
r"document\.querySelector\(\s*(['\"])([^'\"]+)\4\s*\)"
r"\.dispatchEvent\(\s*\1\s*\)\s*;?\s*\}\s*\)\s*$",
stmt_na, re.DOTALL,
)
if m and seen_html:
sel = re.sub(r'^#work-area\s+', '', m.group(5))
target = selector_to_sx(sel, elements, var_names)
ops.append(f'(dom-dispatch {target} "{m.group(3)}" nil)')
continue
# evaluate(() => document.getElementById(ID).METHOD()) — generic
# method dispatch (showModal, close, click, focus, blur, reset…).
m = re.match(