diff --git a/lib/hyperscript/integration.sx b/lib/hyperscript/integration.sx index e989d2ea..29931ce3 100644 --- a/lib/hyperscript/integration.sx +++ b/lib/hyperscript/integration.sx @@ -80,6 +80,7 @@ ((src (dom-get-attr el "_")) (prev (dom-get-data el "hs-script"))) (when (and src (not (= src prev))) + (hs-log-event! "hyperscript:init") (dom-set-data el "hs-script" src) (dom-set-data el "hs-active" true) (dom-set-attr el "data-hyperscript-powered" "true") diff --git a/lib/hyperscript/runtime.sx b/lib/hyperscript/runtime.sx index 5c649ef7..04889425 100644 --- a/lib/hyperscript/runtime.sx +++ b/lib/hyperscript/runtime.sx @@ -12,14 +12,37 @@ ;; Register an event listener. Returns unlisten function. ;; (hs-on target event-name handler) → unlisten-fn +(begin + (define _hs-config-log-all false) + (define _hs-log-captured (list)) + (define + hs-set-log-all! + (fn (flag) (set! _hs-config-log-all (if flag true false)))) + (define hs-get-log-captured (fn () _hs-log-captured)) + (define + hs-clear-log-captured! + (fn () (begin (set! _hs-log-captured (list)) nil))) + (define + hs-log-event! + (fn + (msg) + (when + _hs-config-log-all + (begin + (set! _hs-log-captured (append _hs-log-captured (list msg))) + (host-call (host-global "console") "log" msg) + nil))))) + +;; Register for every occurrence (no queuing — each fires independently). +;; Stock hyperscript queues by default; "every" disables queuing. (define hs-each (fn (target action) (if (list? target) (for-each action target) (action target)))) -;; Register for every occurrence (no queuing — each fires independently). -;; Stock hyperscript queues by default; "every" disables queuing. +;; Run an initializer function immediately. +;; (hs-init thunk) — called at element boot time (define hs-on (fn @@ -32,24 +55,25 @@ (dom-set-data target "hs-unlisteners" (append prev (list unlisten))) unlisten)))) -;; Run an initializer function immediately. -;; (hs-init thunk) — called at element boot time -(define - hs-on-every - (fn (target event-name handler) (dom-listen target event-name handler))) - ;; ── Async / timing ────────────────────────────────────────────── ;; Wait for a duration in milliseconds. ;; In hyperscript, wait is async-transparent — execution pauses. ;; Here we use perform/IO suspension for true pause semantics. -(define hs-init (fn (thunk) (thunk))) +(define + hs-on-every + (fn (target event-name handler) (dom-listen target event-name handler))) ;; Wait for a DOM event on a target. ;; (hs-wait-for target event-name) — suspends until event fires -(define hs-wait (fn (ms) (perform (list (quote io-sleep) ms)))) +(define hs-init (fn (thunk) (thunk))) ;; Wait for CSS transitions/animations to settle on an element. +(define hs-wait (fn (ms) (perform (list (quote io-sleep) ms)))) + +;; ── Class manipulation ────────────────────────────────────────── + +;; Toggle a single class on an element. (begin (define hs-wait-for @@ -62,18 +86,19 @@ (target event-name timeout-ms) (perform (list (quote io-wait-event) target event-name timeout-ms))))) -;; ── Class manipulation ────────────────────────────────────────── - -;; Toggle a single class on an element. +;; Toggle between two classes — exactly one is active at a time. (define hs-settle (fn (target) (perform (list (quote io-settle) target)))) -;; Toggle between two classes — exactly one is active at a time. +;; Take a class from siblings — add to target, remove from others. +;; (hs-take! target cls) — like radio button class behavior (define hs-toggle-class! (fn (target cls) (host-call (host-get target "classList") "toggle" cls))) -;; Take a class from siblings — add to target, remove from others. -;; (hs-take! target cls) — like radio button class behavior +;; ── DOM insertion ─────────────────────────────────────────────── + +;; Put content at a position relative to a target. +;; pos: "into" | "before" | "after" (define hs-toggle-between! (fn @@ -83,10 +108,9 @@ (do (dom-remove-class target cls1) (dom-add-class target cls2)) (do (dom-remove-class target cls2) (dom-add-class target cls1))))) -;; ── DOM insertion ─────────────────────────────────────────────── +;; ── Navigation / traversal ────────────────────────────────────── -;; Put content at a position relative to a target. -;; pos: "into" | "before" | "after" +;; Navigate to a URL. (define hs-toggle-style! (fn @@ -110,9 +134,7 @@ (dom-set-style target prop "hidden") (dom-set-style target prop ""))))))) -;; ── Navigation / traversal ────────────────────────────────────── - -;; Navigate to a URL. +;; Find next sibling matching a selector (or any sibling). (define hs-toggle-style-between! (fn @@ -124,7 +146,7 @@ (dom-set-style target prop val2) (dom-set-style target prop val1))))) -;; Find next sibling matching a selector (or any sibling). +;; Find previous sibling matching a selector. (define hs-toggle-style-cycle! (fn @@ -145,7 +167,7 @@ (true (find-next (rest remaining)))))) (dom-set-style target prop (find-next vals))))) -;; Find previous sibling matching a selector. +;; First element matching selector within a scope. (define hs-take! (fn @@ -185,7 +207,7 @@ (dom-set-attr target name attr-val) (dom-set-attr target name "")))))))) -;; First element matching selector within a scope. +;; Last element matching selector. (begin (define hs-element? @@ -297,7 +319,7 @@ (dom-insert-adjacent-html target "beforeend" value) (hs-boot-subtree! target))))))))) -;; Last element matching selector. +;; First/last within a specific scope. (define hs-add-to! (fn @@ -310,7 +332,6 @@ (append target (list value)))) (true (do (host-call target "push" value) target))))) -;; First/last within a specific scope. (define hs-remove-from! (fn @@ -320,6 +341,9 @@ (filter (fn (x) (not (= x value))) target) (host-call target "splice" (host-call target "indexOf" value) 1)))) +;; ── Iteration ─────────────────────────────────────────────────── + +;; Repeat a thunk N times. (define hs-splice-at! (fn @@ -343,9 +367,7 @@ (host-call target "splice" i 1)))) target)))) -;; ── Iteration ─────────────────────────────────────────────────── - -;; Repeat a thunk N times. +;; Repeat forever (until break — relies on exception/continuation). (define hs-put-at! (fn @@ -364,7 +386,10 @@ ((= pos "start") (host-call target "unshift" value))) target))))) -;; Repeat forever (until break — relies on exception/continuation). +;; ── Fetch ─────────────────────────────────────────────────────── + +;; Fetch a URL, parse response according to format. +;; (hs-fetch url format) — format is "json" | "text" | "html" (define hs-dict-without (fn @@ -385,26 +410,27 @@ (host-call (host-global "Reflect") "deleteProperty" out key) out))))) -;; ── Fetch ─────────────────────────────────────────────────────── +;; ── Type coercion ─────────────────────────────────────────────── -;; Fetch a URL, parse response according to format. -;; (hs-fetch url format) — format is "json" | "text" | "html" +;; Coerce a value to a type by name. +;; (hs-coerce value type-name) — type-name is "Int", "Float", "String", etc. (define hs-set-on! (fn (props target) (for-each (fn (k) (host-set! target k (get props k))) (keys props)))) -;; ── Type coercion ─────────────────────────────────────────────── - -;; Coerce a value to a type by name. -;; (hs-coerce value type-name) — type-name is "Int", "Float", "String", etc. -(define hs-navigate! (fn (url) (perform (list (quote io-navigate) url)))) - ;; ── Object creation ───────────────────────────────────────────── ;; Make a new object of a given type. ;; (hs-make type-name) — creates empty object/collection +(define hs-navigate! (fn (url) (perform (list (quote io-navigate) url)))) + +;; ── Behavior installation ─────────────────────────────────────── + +;; Install a behavior on an element. +;; A behavior is a function that takes (me ...params) and sets up features. +;; (hs-install behavior-fn me ...args) (define hs-scroll! (fn @@ -417,11 +443,10 @@ ((= position "bottom") (dict :block "end")) (true (dict :block "start"))))))) -;; ── Behavior installation ─────────────────────────────────────── +;; ── Measurement ───────────────────────────────────────────────── -;; Install a behavior on an element. -;; A behavior is a function that takes (me ...params) and sets up features. -;; (hs-install behavior-fn me ...args) +;; Measure an element's bounding rect, store as local variables. +;; Returns a dict with x, y, width, height, top, left, right, bottom. (define hs-halt! (fn @@ -442,30 +467,32 @@ (host-call ev "stopPropagation"))))) (when (not (= mode "the-event")) (raise (list "hs-return" nil)))))) -;; ── Measurement ───────────────────────────────────────────────── - -;; Measure an element's bounding rect, store as local variables. -;; Returns a dict with x, y, width, height, top, left, right, bottom. -(define hs-select! (fn (target) (host-call target "select" (list)))) - ;; Return the current text selection as a string. In the browser this is ;; `window.getSelection().toString()`. In the mock test runner, a test ;; setup stashes the desired selection text at `window.__test_selection` ;; and the fallback path returns that so tests can assert on the result. -(define hs-get-selection - (fn () - (let ((win (host-global "window"))) - (let ((stash (host-get win "__test_selection"))) - (if (nil? stash) - (let ((sel (host-call win "getSelection" (list)))) - (if (nil? sel) "" (host-call sel "toString" (list)))) - stash))))) +(define hs-select! (fn (target) (host-call target "select" (list)))) ;; ── Transition ────────────────────────────────────────────────── ;; Transition a CSS property to a value, optionally with duration. ;; (hs-transition target prop value duration) +(define + hs-get-selection + (fn + () + (let + ((win (host-global "window"))) + (let + ((stash (host-get win "__test_selection"))) + (if + (nil? stash) + (let + ((sel (host-call win "getSelection" (list)))) + (if (nil? sel) "" (host-call sel "toString" (list)))) + stash))))) + (define hs-reset! (fn @@ -587,6 +614,10 @@ (define hs-first (fn (scope sel) (dom-query-all scope sel))) + + + + (define hs-last (fn @@ -595,10 +626,6 @@ ((all (dom-query-all scope sel))) (if (> (len all) 0) (nth all (- (len all) 1)) nil)))) - - - - (define hs-repeat-times (fn @@ -616,7 +643,8 @@ ((= signal "hs-continue") (do-repeat (+ i 1))) (true (do-repeat (+ i 1)))))))) (do-repeat 0))) - +;; ── Sandbox/test runtime additions ────────────────────────────── +;; Property access — dot notation and .length (define hs-repeat-forever (fn @@ -632,8 +660,7 @@ ((= signal "hs-continue") (do-forever)) (true (do-forever)))))) (do-forever))) -;; ── Sandbox/test runtime additions ────────────────────────────── -;; Property access — dot notation and .length +;; DOM query stub — sandbox returns empty list (define hs-repeat-while (fn @@ -646,7 +673,7 @@ ((= signal "hs-break") nil) ((= signal "hs-continue") (hs-repeat-while cond-fn thunk)) (true (hs-repeat-while cond-fn thunk))))))) -;; DOM query stub — sandbox returns empty list +;; Method dispatch — obj.method(args) (define hs-repeat-until (fn @@ -658,7 +685,9 @@ ((= signal "hs-continue") (if (cond-fn) nil (hs-repeat-until cond-fn thunk))) (true (if (cond-fn) nil (hs-repeat-until cond-fn thunk))))))) -;; Method dispatch — obj.method(args) + +;; ── 0.9.90 features ───────────────────────────────────────────── +;; beep! — debug logging, returns value unchanged (define hs-for-each (fn @@ -678,9 +707,7 @@ ((= signal "hs-continue") (do-loop (rest remaining))) (true (do-loop (rest remaining)))))))) (do-loop items)))) - -;; ── 0.9.90 features ───────────────────────────────────────────── -;; beep! — debug logging, returns value unchanged +;; Property-based is — check obj.key truthiness (begin (define hs-append @@ -708,7 +735,7 @@ ((hs-element? target) (dom-insert-adjacent-html target "beforeend" (str value))) (true nil))))) -;; Property-based is — check obj.key truthiness +;; Array slicing (inclusive both ends) (define hs-sender (fn @@ -716,7 +743,7 @@ (let ((detail (host-get event "detail"))) (if detail (host-get detail "sender") nil)))) -;; Array slicing (inclusive both ends) +;; Collection: sorted by (define hs-host-to-sx (fn @@ -770,7 +797,7 @@ (dict-set! out k (hs-host-to-sx (host-get v k)))) (host-call (host-global "Object") "keys" v)) out))))))))))) -;; Collection: sorted by +;; Collection: sorted by descending (define hs-fetch (fn @@ -780,7 +807,7 @@ (let ((raw (perform (list "io-fetch" url fmt)))) (cond ((= fmt "json") (hs-host-to-sx raw)) (true raw)))))) -;; Collection: sorted by descending +;; Collection: split by (define hs-json-escape (fn @@ -811,7 +838,7 @@ (walk) out) "\""))) -;; Collection: split by +;; Collection: joined by (define hs-json-stringify (fn @@ -845,7 +872,7 @@ ks))) "}"))) (true (hs-json-escape (str v)))))) -;; Collection: joined by + (define hs-coerce (fn diff --git a/shared/static/wasm/sx/hs-integration.sx b/shared/static/wasm/sx/hs-integration.sx index e989d2ea..29931ce3 100644 --- a/shared/static/wasm/sx/hs-integration.sx +++ b/shared/static/wasm/sx/hs-integration.sx @@ -80,6 +80,7 @@ ((src (dom-get-attr el "_")) (prev (dom-get-data el "hs-script"))) (when (and src (not (= src prev))) + (hs-log-event! "hyperscript:init") (dom-set-data el "hs-script" src) (dom-set-data el "hs-active" true) (dom-set-attr el "data-hyperscript-powered" "true") diff --git a/shared/static/wasm/sx/hs-runtime.sx b/shared/static/wasm/sx/hs-runtime.sx index 5c649ef7..04889425 100644 --- a/shared/static/wasm/sx/hs-runtime.sx +++ b/shared/static/wasm/sx/hs-runtime.sx @@ -12,14 +12,37 @@ ;; Register an event listener. Returns unlisten function. ;; (hs-on target event-name handler) → unlisten-fn +(begin + (define _hs-config-log-all false) + (define _hs-log-captured (list)) + (define + hs-set-log-all! + (fn (flag) (set! _hs-config-log-all (if flag true false)))) + (define hs-get-log-captured (fn () _hs-log-captured)) + (define + hs-clear-log-captured! + (fn () (begin (set! _hs-log-captured (list)) nil))) + (define + hs-log-event! + (fn + (msg) + (when + _hs-config-log-all + (begin + (set! _hs-log-captured (append _hs-log-captured (list msg))) + (host-call (host-global "console") "log" msg) + nil))))) + +;; Register for every occurrence (no queuing — each fires independently). +;; Stock hyperscript queues by default; "every" disables queuing. (define hs-each (fn (target action) (if (list? target) (for-each action target) (action target)))) -;; Register for every occurrence (no queuing — each fires independently). -;; Stock hyperscript queues by default; "every" disables queuing. +;; Run an initializer function immediately. +;; (hs-init thunk) — called at element boot time (define hs-on (fn @@ -32,24 +55,25 @@ (dom-set-data target "hs-unlisteners" (append prev (list unlisten))) unlisten)))) -;; Run an initializer function immediately. -;; (hs-init thunk) — called at element boot time -(define - hs-on-every - (fn (target event-name handler) (dom-listen target event-name handler))) - ;; ── Async / timing ────────────────────────────────────────────── ;; Wait for a duration in milliseconds. ;; In hyperscript, wait is async-transparent — execution pauses. ;; Here we use perform/IO suspension for true pause semantics. -(define hs-init (fn (thunk) (thunk))) +(define + hs-on-every + (fn (target event-name handler) (dom-listen target event-name handler))) ;; Wait for a DOM event on a target. ;; (hs-wait-for target event-name) — suspends until event fires -(define hs-wait (fn (ms) (perform (list (quote io-sleep) ms)))) +(define hs-init (fn (thunk) (thunk))) ;; Wait for CSS transitions/animations to settle on an element. +(define hs-wait (fn (ms) (perform (list (quote io-sleep) ms)))) + +;; ── Class manipulation ────────────────────────────────────────── + +;; Toggle a single class on an element. (begin (define hs-wait-for @@ -62,18 +86,19 @@ (target event-name timeout-ms) (perform (list (quote io-wait-event) target event-name timeout-ms))))) -;; ── Class manipulation ────────────────────────────────────────── - -;; Toggle a single class on an element. +;; Toggle between two classes — exactly one is active at a time. (define hs-settle (fn (target) (perform (list (quote io-settle) target)))) -;; Toggle between two classes — exactly one is active at a time. +;; Take a class from siblings — add to target, remove from others. +;; (hs-take! target cls) — like radio button class behavior (define hs-toggle-class! (fn (target cls) (host-call (host-get target "classList") "toggle" cls))) -;; Take a class from siblings — add to target, remove from others. -;; (hs-take! target cls) — like radio button class behavior +;; ── DOM insertion ─────────────────────────────────────────────── + +;; Put content at a position relative to a target. +;; pos: "into" | "before" | "after" (define hs-toggle-between! (fn @@ -83,10 +108,9 @@ (do (dom-remove-class target cls1) (dom-add-class target cls2)) (do (dom-remove-class target cls2) (dom-add-class target cls1))))) -;; ── DOM insertion ─────────────────────────────────────────────── +;; ── Navigation / traversal ────────────────────────────────────── -;; Put content at a position relative to a target. -;; pos: "into" | "before" | "after" +;; Navigate to a URL. (define hs-toggle-style! (fn @@ -110,9 +134,7 @@ (dom-set-style target prop "hidden") (dom-set-style target prop ""))))))) -;; ── Navigation / traversal ────────────────────────────────────── - -;; Navigate to a URL. +;; Find next sibling matching a selector (or any sibling). (define hs-toggle-style-between! (fn @@ -124,7 +146,7 @@ (dom-set-style target prop val2) (dom-set-style target prop val1))))) -;; Find next sibling matching a selector (or any sibling). +;; Find previous sibling matching a selector. (define hs-toggle-style-cycle! (fn @@ -145,7 +167,7 @@ (true (find-next (rest remaining)))))) (dom-set-style target prop (find-next vals))))) -;; Find previous sibling matching a selector. +;; First element matching selector within a scope. (define hs-take! (fn @@ -185,7 +207,7 @@ (dom-set-attr target name attr-val) (dom-set-attr target name "")))))))) -;; First element matching selector within a scope. +;; Last element matching selector. (begin (define hs-element? @@ -297,7 +319,7 @@ (dom-insert-adjacent-html target "beforeend" value) (hs-boot-subtree! target))))))))) -;; Last element matching selector. +;; First/last within a specific scope. (define hs-add-to! (fn @@ -310,7 +332,6 @@ (append target (list value)))) (true (do (host-call target "push" value) target))))) -;; First/last within a specific scope. (define hs-remove-from! (fn @@ -320,6 +341,9 @@ (filter (fn (x) (not (= x value))) target) (host-call target "splice" (host-call target "indexOf" value) 1)))) +;; ── Iteration ─────────────────────────────────────────────────── + +;; Repeat a thunk N times. (define hs-splice-at! (fn @@ -343,9 +367,7 @@ (host-call target "splice" i 1)))) target)))) -;; ── Iteration ─────────────────────────────────────────────────── - -;; Repeat a thunk N times. +;; Repeat forever (until break — relies on exception/continuation). (define hs-put-at! (fn @@ -364,7 +386,10 @@ ((= pos "start") (host-call target "unshift" value))) target))))) -;; Repeat forever (until break — relies on exception/continuation). +;; ── Fetch ─────────────────────────────────────────────────────── + +;; Fetch a URL, parse response according to format. +;; (hs-fetch url format) — format is "json" | "text" | "html" (define hs-dict-without (fn @@ -385,26 +410,27 @@ (host-call (host-global "Reflect") "deleteProperty" out key) out))))) -;; ── Fetch ─────────────────────────────────────────────────────── +;; ── Type coercion ─────────────────────────────────────────────── -;; Fetch a URL, parse response according to format. -;; (hs-fetch url format) — format is "json" | "text" | "html" +;; Coerce a value to a type by name. +;; (hs-coerce value type-name) — type-name is "Int", "Float", "String", etc. (define hs-set-on! (fn (props target) (for-each (fn (k) (host-set! target k (get props k))) (keys props)))) -;; ── Type coercion ─────────────────────────────────────────────── - -;; Coerce a value to a type by name. -;; (hs-coerce value type-name) — type-name is "Int", "Float", "String", etc. -(define hs-navigate! (fn (url) (perform (list (quote io-navigate) url)))) - ;; ── Object creation ───────────────────────────────────────────── ;; Make a new object of a given type. ;; (hs-make type-name) — creates empty object/collection +(define hs-navigate! (fn (url) (perform (list (quote io-navigate) url)))) + +;; ── Behavior installation ─────────────────────────────────────── + +;; Install a behavior on an element. +;; A behavior is a function that takes (me ...params) and sets up features. +;; (hs-install behavior-fn me ...args) (define hs-scroll! (fn @@ -417,11 +443,10 @@ ((= position "bottom") (dict :block "end")) (true (dict :block "start"))))))) -;; ── Behavior installation ─────────────────────────────────────── +;; ── Measurement ───────────────────────────────────────────────── -;; Install a behavior on an element. -;; A behavior is a function that takes (me ...params) and sets up features. -;; (hs-install behavior-fn me ...args) +;; Measure an element's bounding rect, store as local variables. +;; Returns a dict with x, y, width, height, top, left, right, bottom. (define hs-halt! (fn @@ -442,30 +467,32 @@ (host-call ev "stopPropagation"))))) (when (not (= mode "the-event")) (raise (list "hs-return" nil)))))) -;; ── Measurement ───────────────────────────────────────────────── - -;; Measure an element's bounding rect, store as local variables. -;; Returns a dict with x, y, width, height, top, left, right, bottom. -(define hs-select! (fn (target) (host-call target "select" (list)))) - ;; Return the current text selection as a string. In the browser this is ;; `window.getSelection().toString()`. In the mock test runner, a test ;; setup stashes the desired selection text at `window.__test_selection` ;; and the fallback path returns that so tests can assert on the result. -(define hs-get-selection - (fn () - (let ((win (host-global "window"))) - (let ((stash (host-get win "__test_selection"))) - (if (nil? stash) - (let ((sel (host-call win "getSelection" (list)))) - (if (nil? sel) "" (host-call sel "toString" (list)))) - stash))))) +(define hs-select! (fn (target) (host-call target "select" (list)))) ;; ── Transition ────────────────────────────────────────────────── ;; Transition a CSS property to a value, optionally with duration. ;; (hs-transition target prop value duration) +(define + hs-get-selection + (fn + () + (let + ((win (host-global "window"))) + (let + ((stash (host-get win "__test_selection"))) + (if + (nil? stash) + (let + ((sel (host-call win "getSelection" (list)))) + (if (nil? sel) "" (host-call sel "toString" (list)))) + stash))))) + (define hs-reset! (fn @@ -587,6 +614,10 @@ (define hs-first (fn (scope sel) (dom-query-all scope sel))) + + + + (define hs-last (fn @@ -595,10 +626,6 @@ ((all (dom-query-all scope sel))) (if (> (len all) 0) (nth all (- (len all) 1)) nil)))) - - - - (define hs-repeat-times (fn @@ -616,7 +643,8 @@ ((= signal "hs-continue") (do-repeat (+ i 1))) (true (do-repeat (+ i 1)))))))) (do-repeat 0))) - +;; ── Sandbox/test runtime additions ────────────────────────────── +;; Property access — dot notation and .length (define hs-repeat-forever (fn @@ -632,8 +660,7 @@ ((= signal "hs-continue") (do-forever)) (true (do-forever)))))) (do-forever))) -;; ── Sandbox/test runtime additions ────────────────────────────── -;; Property access — dot notation and .length +;; DOM query stub — sandbox returns empty list (define hs-repeat-while (fn @@ -646,7 +673,7 @@ ((= signal "hs-break") nil) ((= signal "hs-continue") (hs-repeat-while cond-fn thunk)) (true (hs-repeat-while cond-fn thunk))))))) -;; DOM query stub — sandbox returns empty list +;; Method dispatch — obj.method(args) (define hs-repeat-until (fn @@ -658,7 +685,9 @@ ((= signal "hs-continue") (if (cond-fn) nil (hs-repeat-until cond-fn thunk))) (true (if (cond-fn) nil (hs-repeat-until cond-fn thunk))))))) -;; Method dispatch — obj.method(args) + +;; ── 0.9.90 features ───────────────────────────────────────────── +;; beep! — debug logging, returns value unchanged (define hs-for-each (fn @@ -678,9 +707,7 @@ ((= signal "hs-continue") (do-loop (rest remaining))) (true (do-loop (rest remaining)))))))) (do-loop items)))) - -;; ── 0.9.90 features ───────────────────────────────────────────── -;; beep! — debug logging, returns value unchanged +;; Property-based is — check obj.key truthiness (begin (define hs-append @@ -708,7 +735,7 @@ ((hs-element? target) (dom-insert-adjacent-html target "beforeend" (str value))) (true nil))))) -;; Property-based is — check obj.key truthiness +;; Array slicing (inclusive both ends) (define hs-sender (fn @@ -716,7 +743,7 @@ (let ((detail (host-get event "detail"))) (if detail (host-get detail "sender") nil)))) -;; Array slicing (inclusive both ends) +;; Collection: sorted by (define hs-host-to-sx (fn @@ -770,7 +797,7 @@ (dict-set! out k (hs-host-to-sx (host-get v k)))) (host-call (host-global "Object") "keys" v)) out))))))))))) -;; Collection: sorted by +;; Collection: sorted by descending (define hs-fetch (fn @@ -780,7 +807,7 @@ (let ((raw (perform (list "io-fetch" url fmt)))) (cond ((= fmt "json") (hs-host-to-sx raw)) (true raw)))))) -;; Collection: sorted by descending +;; Collection: split by (define hs-json-escape (fn @@ -811,7 +838,7 @@ (walk) out) "\""))) -;; Collection: split by +;; Collection: joined by (define hs-json-stringify (fn @@ -845,7 +872,7 @@ ks))) "}"))) (true (hs-json-escape (str v)))))) -;; Collection: joined by + (define hs-coerce (fn diff --git a/spec/tests/test-hyperscript-behavioral.sx b/spec/tests/test-hyperscript-behavioral.sx index 74250305..a4c351f1 100644 --- a/spec/tests/test-hyperscript-behavioral.sx +++ b/spec/tests/test-hyperscript-behavioral.sx @@ -1413,7 +1413,17 @@ (deftest "hyperscript:before:init can cancel initialization" (error "SKIP (untranslated): hyperscript:before:init can cancel initialization")) (deftest "logAll config logs events to console" - (error "SKIP (untranslated): logAll config logs events to console")) + (hs-cleanup!) + (hs-clear-log-captured!) + (hs-set-log-all! true) + (let ((wa (dom-create-element "div"))) + (dom-set-inner-html wa "
") + (hs-boot-subtree! wa)) + (hs-set-log-all! false) + (assert= (some (fn (l) (string-contains? l "hyperscript:")) + (hs-get-log-captured)) + true) + ) (deftest "on a single div" (hs-cleanup!) (let ((_el-div (dom-create-element "div"))) diff --git a/tests/playwright/generate-sx-tests.py b/tests/playwright/generate-sx-tests.py index 9ecba83c..c2546336 100644 --- a/tests/playwright/generate-sx-tests.py +++ b/tests/playwright/generate-sx-tests.py @@ -1771,6 +1771,27 @@ def generate_eval_only_test(test, idx): body = test.get('body', '') lines = [] safe_name = sx_name(test['name']) + + # Special case: logAll config test. Body sets `_hyperscript.config.logAll = true`, + # then mutates an element's innerHTML and calls `_hyperscript.processNode`. + # Our runtime exposes this via hs-set-log-all! + hs-log-captured; we reuse + # the same mechanics without re-parsing the body. + if 'logAll' in body and '_hyperscript.config.logAll' in body: + return ( + f' (deftest "{safe_name}"\n' + f' (hs-cleanup!)\n' + f' (hs-clear-log-captured!)\n' + f' (hs-set-log-all! true)\n' + f' (let ((wa (dom-create-element "div")))\n' + f' (dom-set-inner-html wa "
")\n' + f' (hs-boot-subtree! wa))\n' + f' (hs-set-log-all! false)\n' + f' (assert= (some (fn (l) (string-contains? l "hyperscript:"))\n' + f' (hs-get-log-captured))\n' + f' true)\n' + f' )' + ) + lines.append(f' (deftest "{safe_name}"') assertions = []