Files
rose-ash/sx/sx/analyzer.sx
giles d40a9c6796 sx-tools: WASM kernel updates, TW/CSSX rework, content refresh, new debugging tools
Build tooling: updated OCaml bootstrapper, compile-modules, bundle.sh, sx-build-all.
WASM browser: rebuilt sx_browser.bc.js/wasm, sx-platform-2.js, .sxbc bytecode files.
CSSX/Tailwind: reworked cssx.sx templates and tw-layout, added tw-type support.
Content: refreshed essays, plans, geography, reactive islands, docs, demos, handlers.
New tools: bisect_sxbc.sh, test-spa.js, render-trace.sx, morph playwright spec.
Tests: added test-match.sx, test-examples.sx, updated test-tw.sx and web tests.

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

121 lines
7.2 KiB
Plaintext

;; Bundle analyzer — live demonstration of dependency analysis + IO detection.
;; Shows per-page component bundles vs total, visualizing payload savings.
;; Drill down into each bundle to see component tree; expand to see SX source.
;; @css bg-green-100 text-green-800 bg-violet-600 bg-stone-200 text-violet-600 text-stone-600 text-green-600 rounded-full h-2.5 grid-cols-3 bg-blue-100 text-blue-800 bg-amber-100 text-amber-800 grid-cols-4 marker:text-stone-400 bg-blue-50 bg-amber-50 text-blue-700 text-amber-700 border-blue-200 border-amber-200 bg-blue-500 bg-amber-500
(defcomp ~analyzer/bundle-analyzer-content (&key (pages :as list) (total-components :as number) (total-macros :as number)
(pure-count :as number) (io-count :as number))
(~docs/page :title "Page Bundle Analyzer"
(p (~tw :tokens "text-stone-600 mb-6")
"Live analysis of component dependency graphs and IO classification across all pages. "
"Each bar shows how many of the "
(strong (str total-components))
" total components a page actually needs, computed by the "
(a :href "/sx/(language.(spec.deps))" (~tw :tokens "text-violet-700 underline") "deps.sx")
" transitive closure algorithm. "
"Click a page to see its component tree; expand a component to see its SX source.")
(div (~tw :tokens "mb-8 grid grid-cols-4 gap-4")
(~analyzer/stat :label "Total Components" :value (str total-components)
:cls "text-violet-600")
(~analyzer/stat :label "Total Macros" :value (str total-macros)
:cls "text-stone-600")
(~analyzer/stat :label "Pure Components" :value (str pure-count)
:cls "text-blue-600")
(~analyzer/stat :label "IO-Dependent" :value (str io-count)
:cls "text-amber-600"))
(~docs/section :title "Per-Page Bundles" :id "bundles"
(div (~tw :tokens "space-y-3")
(map (fn (page)
(~analyzer/row
:name (get page "name")
:path (get page "path")
:needed (get page "needed")
:direct (get page "direct")
:total total-components
:pct (get page "pct")
:savings (get page "savings")
:io-refs (get page "io-refs")
:pure-in-page (get page "pure-in-page")
:io-in-page (get page "io-in-page")
:components (get page "components")))
pages)))
(~docs/section :title "How It Works" :id "how"
(ol (~tw :tokens "list-decimal pl-5 space-y-2 text-stone-700")
(li (strong "Scan: ") "Regex finds all " (code "(~plans/content-addressed-components/name") " patterns in the page's content expression.")
(li (strong "Resolve: ") "Each referenced component's body AST is walked to find transitive " (code "~") " references.")
(li (strong "Closure: ") "The full set is the union of direct + transitive deps, following chains through the component graph.")
(li (strong "Bundle: ") "Only these component definitions are serialized into the page payload. Everything else is omitted.")
(li (strong "IO detect: ") "Each component body is scanned for references to IO primitives (frag, query, service, etc.). Components with zero transitive IO refs are pure — safe for client rendering."))
(p (~tw :tokens "mt-4 text-stone-600")
"The analysis handles circular references (via seen-set), "
"walks all branches of control flow (if/when/cond/case), "
"and includes macro definitions shared across components."))))
(defcomp ~analyzer/stat (&key (label :as string) (value :as string) (cls :as string))
(div (~tw :tokens "rounded-lg border border-stone-200 p-4 text-center")
(div :class (str "text-3xl font-bold " cls) value)
(div (~tw :tokens "text-sm text-stone-500 mt-1") label)))
(defcomp ~analyzer/row (&key (name :as string) (path :as string) (needed :as number) (direct :as number) (total :as number) (pct :as number) (savings :as number)
(io-refs :as list) (pure-in-page :as number) (io-in-page :as number) (components :as list))
(details (~tw :tokens "rounded border border-stone-200")
(summary (~tw :tokens "p-4 cursor-pointer hover:bg-stone-50 transition-colors")
(div (~tw :tokens "flex items-center justify-between mb-2")
(div
(span (~tw :tokens "font-mono font-semibold text-stone-800") name)
(span (~tw :tokens "text-stone-400 text-sm ml-2") path))
(div (~tw :tokens "flex items-center gap-2")
(span (~tw :tokens "inline-block px-1.5 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800")
(str pure-in-page " pure"))
(span (~tw :tokens "inline-block px-1.5 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-800")
(str io-in-page " IO"))
(div (~tw :tokens "text-right")
(span (~tw :tokens "font-mono text-sm")
(span (~tw :tokens "text-violet-700 font-bold") (str needed))
(span (~tw :tokens "text-stone-400") (str " / " total)))
(span (~tw :tokens "ml-2 inline-block px-1.5 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800")
(str savings "% saved")))))
(div (~tw :tokens "w-full bg-stone-200 rounded-full h-2.5")
(div (~tw :tokens "bg-violet-600 h-2.5 rounded-full transition-all")
:style (str "width: " pct "%"))))
;; Component tree (shown when expanded)
(div (~tw :tokens "border-t border-stone-200 p-4 bg-stone-50")
(div (~tw :tokens "text-xs font-medium text-stone-500 uppercase tracking-wide mb-3")
(str needed " components in bundle"))
(div (~tw :tokens "space-y-1")
(map (fn (comp)
(~analyzer/component
:comp-name (get comp "name")
:is-pure (get comp "is-pure")
:io-refs (get comp "io-refs")
:deps (get comp "deps")
:source (get comp "source")))
components)))))
(defcomp ~analyzer/component (&key (comp-name :as string) (is-pure :as boolean) (io-refs :as list) (deps :as list) (source :as string))
(details :class (str "rounded border "
(if is-pure "border-blue-200 bg-blue-50" "border-amber-200 bg-amber-50"))
(summary (~tw :tokens "px-3 py-2 cursor-pointer hover:opacity-80 transition-opacity")
(div (~tw :tokens "flex items-center justify-between")
(div (~tw :tokens "flex items-center gap-2")
(span :class (str "inline-block w-2 h-2 rounded-full "
(if is-pure "bg-blue-500" "bg-amber-500")))
(span (~tw :tokens "font-mono text-sm font-medium text-stone-800") comp-name))
(div (~tw :tokens "flex items-center gap-2")
(when (not (empty? io-refs))
(span (~tw :tokens "text-xs text-amber-700")
(str "IO: " (join ", " io-refs))))
(when (not (empty? deps))
(span (~tw :tokens "text-xs text-stone-500")
(str (len deps) " deps"))))))
;; SX source (shown when component expanded)
(div (~tw :tokens "not-prose border-t border-stone-200 p-3 bg-stone-100 rounded-b")
(pre (~tw :tokens "text-xs leading-relaxed whitespace-pre-wrap overflow-x-auto")
(code (highlight source "lisp"))))))