Files
rose-ash/web/deps.sx
giles c0665ba58e Adopt Step 7 language features across SX codebase
112 conversions across 19 .sx files using match, let-match, and pipe operators:

match (17): type/value dispatch replacing cond/if chains
  - lib/vm.sx: HO form dispatch (for-each/map/filter/reduce/some/every?)
  - lib/tree-tools.sx: node-display, node-matches?, rename, count, replace, free-symbols
  - lib/types.sx: narrow-type, substitute-in-type, infer-type, resolve-type
  - web/engine.sx: default-trigger, resolve-target, classify-trigger
  - web/deps.sx: scan-refs-walk, scan-io-refs-walk

let-match (89): dict destructuring replacing (get d "key") patterns
  - shared/page-functions.sx (20), blog/admin.sx (17), pub-api.sx (13)
  - events/ layouts/page/tickets/entries/forms (27 total)
  - specs-explorer.sx (7), federation/social.sx (3), lib/ small files (3)

-> pipes (6): replacing triple-chained gets in lib/vm.sx
  - frame-closure → closure-code → code-bytecode chains

Also: lib/vm.sx accessor upgrades (get vm "sp" → vm-sp vm throughout)

2650/2650 tests pass, zero regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:49:02 +00:00

363 lines
11 KiB
Plaintext

(define-library
(web deps)
(export
scan-refs
scan-refs-walk
transitive-deps-walk
transitive-deps
compute-all-deps
scan-components-from-source
components-needed
page-component-bundle
page-css-classes
scan-io-refs-walk
scan-io-refs
transitive-io-refs-walk
transitive-io-refs
compute-all-io-refs
component-io-refs-cached
component-pure?
render-target
page-render-plan
env-components)
(begin
(define
scan-refs
:effects ()
(fn (node) (let ((refs (list))) (scan-refs-walk node refs) refs)))
(define
scan-refs-walk
:effects ()
(fn
(node (refs :as list))
(match
(type-of node)
("symbol"
(let
((name (symbol-name node)))
(when
(starts-with? name "~")
(when (not (contains? refs name)) (append! refs name)))))
("list" (for-each (fn (child) (scan-refs-walk child refs)) node))
("dict"
(for-each
(fn (key) (scan-refs-walk (dict-get node key) refs))
(keys node)))
(_ nil))))
(define
transitive-deps-walk
:effects ()
(fn
((n :as string) (seen :as list) (env :as dict))
(when
(not (contains? seen n))
(append! seen n)
(let
((val (env-get env n)))
(cond
(or (= (type-of val) "component") (= (type-of val) "island"))
(for-each
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
(scan-refs (component-body val)))
(= (type-of val) "macro")
(for-each
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
(scan-refs (macro-body val)))
:else nil)))))
(define
transitive-deps
:effects ()
(fn
((name :as string) (env :as dict))
(let
((seen (list))
(key (if (starts-with? name "~") name (str "~" name))))
(transitive-deps-walk key seen env)
(filter (fn ((x :as string)) (not (= x key))) seen))))
(define
compute-all-deps
:effects (mutation)
(fn
((env :as dict))
(for-each
(fn
((name :as string))
(let
((val (env-get env name)))
(when
(or
(= (type-of val) "component")
(= (type-of val) "island"))
(component-set-deps! val (transitive-deps name env)))))
(env-components env))))
(define
scan-components-from-source
:effects ()
(fn
((source :as string))
(let
((matches (regex-find-all "\\(~([a-zA-Z_][a-zA-Z0-9_\\-:/]*)" source)))
(map (fn ((m :as string)) (str "~" m)) matches))))
(define
components-needed
:effects ()
(fn
((page-source :as string) (env :as dict))
(let
((direct (scan-components-from-source page-source))
(all-needed (list)))
(for-each
(fn
((name :as string))
(when
(not (contains? all-needed name))
(append! all-needed name))
(let
((val (env-get env name)))
(let
((deps (if (and (= (type-of val) "component") (not (empty? (component-deps val)))) (component-deps val) (transitive-deps name env))))
(for-each
(fn
((dep :as string))
(when
(not (contains? all-needed dep))
(append! all-needed dep)))
deps))))
direct)
all-needed)))
(define
page-component-bundle
:effects ()
(fn
((page-source :as string) (env :as dict))
(components-needed page-source env)))
(define
page-css-classes
:effects ()
(fn
((page-source :as string) (env :as dict))
(let
((needed (components-needed page-source env)) (classes (list)))
(for-each
(fn
((name :as string))
(let
((val (env-get env name)))
(when
(= (type-of val) "component")
(for-each
(fn
((cls :as string))
(when
(not (contains? classes cls))
(append! classes cls)))
(component-css-classes val)))))
needed)
(for-each
(fn
((cls :as string))
(when (not (contains? classes cls)) (append! classes cls)))
(scan-css-classes page-source))
classes)))
(define
scan-io-refs-walk
:effects ()
(fn
(node (io-names :as list) (refs :as list))
(match
(type-of node)
("symbol"
(let
((name (symbol-name node)))
(when
(contains? io-names name)
(when (not (contains? refs name)) (append! refs name)))))
("list"
(for-each
(fn (item) (scan-io-refs-walk item io-names refs))
node))
("dict"
(for-each
(fn
(key)
(scan-io-refs-walk (dict-get node key) io-names refs))
(keys node)))
(_ nil))))
(define
scan-io-refs
:effects ()
(fn
(node (io-names :as list))
(let ((refs (list))) (scan-io-refs-walk node io-names refs) refs)))
(define
transitive-io-refs-walk
:effects ()
(fn
((n :as string)
(seen :as list)
(all-refs :as list)
(env :as dict)
(io-names :as list))
(when
(not (contains? seen n))
(append! seen n)
(let
((val (env-get env n)))
(cond
(= (type-of val) "component")
(do
(for-each
(fn
((ref :as string))
(when
(not (contains? all-refs ref))
(append! all-refs ref)))
(scan-io-refs (component-body val) io-names))
(for-each
(fn
((dep :as string))
(transitive-io-refs-walk dep seen all-refs env io-names))
(scan-refs (component-body val))))
(= (type-of val) "macro")
(do
(for-each
(fn
((ref :as string))
(when
(not (contains? all-refs ref))
(append! all-refs ref)))
(scan-io-refs (macro-body val) io-names))
(for-each
(fn
((dep :as string))
(transitive-io-refs-walk dep seen all-refs env io-names))
(scan-refs (macro-body val))))
:else nil)))))
(define
transitive-io-refs
:effects ()
(fn
((name :as string) (env :as dict) (io-names :as list))
(let
((all-refs (list))
(seen (list))
(key (if (starts-with? name "~") name (str "~" name))))
(transitive-io-refs-walk key seen all-refs env io-names)
all-refs)))
(define
compute-all-io-refs
:effects (mutation)
(fn
((env :as dict) (io-names :as list))
(for-each
(fn
((name :as string))
(let
((val (env-get env name)))
(when
(= (type-of val) "component")
(component-set-io-refs!
val
(transitive-io-refs name env io-names)))))
(env-components env))))
(define
component-io-refs-cached
:effects ()
(fn
((name :as string) (env :as dict) (io-names :as list))
(let
((key (if (starts-with? name "~") name (str "~" name))))
(let
((val (env-get env key)))
(if
(and
(= (type-of val) "component")
(not (nil? (component-io-refs val)))
(not (empty? (component-io-refs val))))
(component-io-refs val)
(transitive-io-refs name env io-names))))))
(define
component-pure?
:effects ()
(fn
(name (env :as dict) (io-names :as list))
(let
((key (if (starts-with? name "~") name (str "~" name))))
(let
((val (if (env-has? env key) (env-get env key) nil)))
(if
(and
(= (type-of val) "component")
(not (nil? (component-io-refs val)))
(not (empty? (component-io-refs val))))
false
(empty? (transitive-io-refs name env io-names)))))))
(define
render-target
:effects ()
(fn
(name (env :as dict) (io-names :as list))
(let
((key (if (starts-with? name "~") name (str "~" name))))
(let
((val (if (env-has? env key) (env-get env key) nil)))
(if
(not (= (type-of val) "component"))
"server"
(let
((affinity (component-affinity val)))
(cond
(= affinity "server")
"server"
(= affinity "client")
"client"
(not (component-pure? name env io-names))
"server"
:else "client")))))))
(define
page-render-plan
:effects ()
(fn
((page-source :as string) (env :as dict) (io-names :as list))
(let
((needed (components-needed page-source env))
(comp-targets (dict))
(server-list (list))
(client-list (list))
(io-deps (list)))
(for-each
(fn
((name :as string))
(let
((target (render-target name env io-names)))
(dict-set! comp-targets name target)
(if
(= target "server")
(do
(append! server-list name)
(for-each
(fn
((io-ref :as string))
(when
(not (contains? io-deps io-ref))
(append! io-deps io-ref)))
(component-io-refs-cached name env io-names)))
(append! client-list name))))
needed)
{:io-deps io-deps :server server-list :components comp-targets :client client-list})))
(define
env-components
:effects ()
(fn
((env :as dict))
(filter
(fn
((k :as string))
(let ((v (env-get env k))) (or (component? v) (macro? v))))
(keys env)))))) ;; end define-library
;; Re-export to global namespace for backward compatibility
(import (web deps))