Update sx-docs: add deps spec to viewer, mark Phase 1 complete
Add deps.sx to the spec navigator in sx-docs (nav-data, specs page). Update isomorphic architecture plan to show Phase 1 as complete with link to the canonical spec at /specs/deps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -99,7 +99,8 @@
|
|||||||
(dict :label "Boot" :href "/specs/boot")
|
(dict :label "Boot" :href "/specs/boot")
|
||||||
(dict :label "CSSX" :href "/specs/cssx")
|
(dict :label "CSSX" :href "/specs/cssx")
|
||||||
(dict :label "Continuations" :href "/specs/continuations")
|
(dict :label "Continuations" :href "/specs/continuations")
|
||||||
(dict :label "call/cc" :href "/specs/callcc")))
|
(dict :label "call/cc" :href "/specs/callcc")
|
||||||
|
(dict :label "Deps" :href "/specs/deps")))
|
||||||
|
|
||||||
(define plans-nav-items (list
|
(define plans-nav-items (list
|
||||||
(dict :label "Isomorphic Architecture" :href "/plans/isomorphic-architecture"
|
(dict :label "Isomorphic Architecture" :href "/plans/isomorphic-architecture"
|
||||||
@@ -169,7 +170,12 @@
|
|||||||
:desc "Full first-class continuations — call-with-current-continuation."
|
:desc "Full first-class continuations — call-with-current-continuation."
|
||||||
:prose "Full call/cc captures the entire remaining computation as a first-class function — not just up to a delimiter, but all the way to the top level. Invoking the continuation abandons the current computation entirely and resumes from where it was captured. Strictly more powerful than delimited continuations, but harder to implement in targets that don't support it natively. Recommended for Scheme and Haskell targets where it's natural. Python, JavaScript, and Rust targets should prefer delimited continuations (continuations.sx) unless full escape semantics are genuinely needed. Optional extension: the continuation type is shared with continuations.sx if both are loaded.")))
|
:prose "Full call/cc captures the entire remaining computation as a first-class function — not just up to a delimiter, but all the way to the top level. Invoking the continuation abandons the current computation entirely and resumes from where it was captured. Strictly more powerful than delimited continuations, but harder to implement in targets that don't support it natively. Recommended for Scheme and Haskell targets where it's natural. Python, JavaScript, and Rust targets should prefer delimited continuations (continuations.sx) unless full escape semantics are genuinely needed. Optional extension: the continuation type is shared with continuations.sx if both are loaded.")))
|
||||||
|
|
||||||
(define all-spec-items (concat core-spec-items (concat adapter-spec-items (concat browser-spec-items extension-spec-items))))
|
(define module-spec-items (list
|
||||||
|
(dict :slug "deps" :filename "deps.sx" :title "Deps"
|
||||||
|
:desc "Component dependency analysis — per-page bundling, transitive closure, CSS scoping."
|
||||||
|
:prose "The deps module analyzes component dependency graphs to enable per-page bundling. Instead of sending every component definition to every page, deps.sx walks component AST bodies to find transitive ~component references, then computes the minimal set needed. It also collects per-page CSS classes from only the used components. All functions are pure — each host bootstraps them to native code via --spec-modules deps. Platform functions (component-deps, component-set-deps!, component-css-classes, env-components, regex-find-all, scan-css-classes) are implemented natively per target.")))
|
||||||
|
|
||||||
|
(define all-spec-items (concat core-spec-items (concat adapter-spec-items (concat browser-spec-items (concat extension-spec-items module-spec-items)))))
|
||||||
|
|
||||||
(define find-spec
|
(define find-spec
|
||||||
(fn (slug)
|
(fn (slug)
|
||||||
|
|||||||
@@ -621,51 +621,66 @@
|
|||||||
|
|
||||||
(~doc-section :title "Phase 1: Component Distribution & Dependency Analysis" :id "phase-1"
|
(~doc-section :title "Phase 1: Component Distribution & Dependency Analysis" :id "phase-1"
|
||||||
|
|
||||||
(div :class "rounded border border-violet-200 bg-violet-50 p-4 mb-4"
|
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||||
(p :class "text-violet-900 font-medium" "What it enables")
|
(div :class "flex items-center gap-2 mb-2"
|
||||||
(p :class "text-violet-800" "Per-page component bundles instead of sending every definition to every page. Smaller payloads, faster boot, better cache hit rates."))
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
|
(a :href "/specs/deps" :class "text-green-700 underline text-sm font-medium" "View canonical spec: deps.sx"))
|
||||||
|
(p :class "text-green-900 font-medium" "What it enables")
|
||||||
|
(p :class "text-green-800" "Per-page component bundles instead of sending every definition to every page. Smaller payloads, faster boot, better cache hit rates."))
|
||||||
|
|
||||||
(~doc-subsection :title "The Problem"
|
(~doc-subsection :title "The Problem"
|
||||||
(p "client_components_tag() in jinja_bridge.py serializes ALL entries in _COMPONENT_ENV. The sx_page() template sends everything or nothing based on a single global hash. No mechanism determines which components a page actually needs."))
|
(p "client_components_tag() in jinja_bridge.py serializes ALL entries in _COMPONENT_ENV. The sx_page() template sends everything or nothing based on a single global hash. No mechanism determines which components a page actually needs."))
|
||||||
|
|
||||||
(~doc-subsection :title "Approach"
|
(~doc-subsection :title "Implementation"
|
||||||
|
|
||||||
|
(p "Phase 1 follows the spec-first architecture: the dependency analysis algorithm is defined in "
|
||||||
|
(a :href "/specs/deps" :class "text-violet-700 underline" "deps.sx")
|
||||||
|
" and bootstrapped to both Python (sx_ref.py) and JavaScript (sx-ref.js). A thin dispatcher in deps.py routes to the bootstrapped implementation when SX_USE_REF=1.")
|
||||||
|
|
||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
(div
|
(div
|
||||||
(h4 :class "font-semibold text-stone-700" "1. Transitive closure analyzer")
|
(h4 :class "font-semibold text-stone-700" "1. Transitive closure analyzer (deps.sx)")
|
||||||
(p "New module shared/sx/deps.py:")
|
(p "The spec defines 9 functions for dependency analysis. The core algorithm:")
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li "Walk Component.body AST, collect all Symbol refs starting with ~")
|
(li "scan-refs: walk a component body AST, collect all Symbol refs starting with ~")
|
||||||
(li "Recursively follow into their bodies")
|
(li "transitive-deps: recursively follow component references through the env")
|
||||||
(li "Handle control forms (if/when/cond/case) — include ALL branches")
|
(li "compute-all-deps: batch-compute and cache deps for all components in env")
|
||||||
(li "Handle macros — expand during walk using limited eval"))
|
(li "Handle circular references safely via seen-set"))
|
||||||
(~doc-code :code (highlight "def transitive_deps(name: str, env: dict) -> set[str]:\n \"\"\"Compute transitive component dependencies.\"\"\"\n seen = set()\n def walk(n):\n if n in seen: return\n seen.add(n)\n comp = env.get(n)\n if comp:\n for dep in _scan_ast(comp.body):\n walk(dep)\n walk(name)\n return seen - {name}" "python")))
|
(~doc-code :code (highlight "(define (transitive-deps name env)\n (let ((key (if (starts-with? name \"~\") name\n (concat \"~\" name)))\n (seen (set-create)))\n (transitive-deps-walk key env seen)\n (set-remove seen key)))" "lisp")))
|
||||||
|
|
||||||
(div
|
(div
|
||||||
(h4 :class "font-semibold text-stone-700" "2. Runtime component scanning")
|
(h4 :class "font-semibold text-stone-700" "2. Runtime component scanning")
|
||||||
(p "After _aser serializes page content, scan the SX string for (~name patterns (parallel to existing scan_classes_from_sx for CSS). Then compute transitive closure to get sub-components."))
|
(p "scan-components-from-source uses regex to find (~name patterns in serialized SX. components-needed combines scanning with transitive closure to get the full set of components a page requires."))
|
||||||
|
|
||||||
(div
|
(div
|
||||||
(h4 :class "font-semibold text-stone-700" "3. Per-page component block")
|
(h4 :class "font-semibold text-stone-700" "3. Per-page CSS classes")
|
||||||
(p "In sx_page() — replace all-or-nothing with page-specific bundle. Hash changes per page, localStorage cache keyed by route pattern."))
|
(p "page-css-classes collects Tailwind classes from all components in a page bundle, enabling per-page CSS extraction."))
|
||||||
|
|
||||||
(div
|
(div
|
||||||
(h4 :class "font-semibold text-stone-700" "4. SX partial responses")
|
(h4 :class "font-semibold text-stone-700" "4. Platform interface")
|
||||||
(p "components_for_request() already diffs against SX-Components header. Enhance with transitive closure so only truly needed missing components are sent."))))
|
(p "The spec declares 6 platform functions that each host implements natively:")
|
||||||
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm"
|
||||||
|
(li "component-deps / component-set-deps!")
|
||||||
|
(li "component-css-classes")
|
||||||
|
(li "env-components")
|
||||||
|
(li "regex-find-all / scan-css-classes")))))
|
||||||
|
|
||||||
(~doc-subsection :title "Files"
|
(~doc-subsection :title "Files"
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm"
|
||||||
(li "New: shared/sx/deps.py — dependency analysis")
|
(li "shared/sx/ref/deps.sx — canonical spec (9 functions)")
|
||||||
(li "shared/sx/jinja_bridge.py — per-page bundle generation")
|
(li "shared/sx/deps.py — dispatcher (SX_USE_REF → bootstrapped, else fallback)")
|
||||||
(li "shared/sx/helpers.py — modify sx_page() and sx_response()")
|
(li "shared/sx/ref/bootstrap_py.py — spec module + platform deps")
|
||||||
(li "shared/sx/types.py — add deps: set[str] to Component")
|
(li "shared/sx/ref/bootstrap_js.py — spec module + platform deps")
|
||||||
(li "shared/sx/ref/boot.sx — per-page component caching")))
|
(li "shared/sx/ref/sx_ref.py — regenerated with deps module")
|
||||||
|
(li "shared/static/scripts/sx-ref.js — regenerated with deps module")
|
||||||
|
(li "shared/sx/tests/test_deps.py — 15 tests")))
|
||||||
|
|
||||||
(~doc-subsection :title "Verification"
|
(~doc-subsection :title "Verification"
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li "Page using 5/50 components → data-components block contains only those 5 + transitive deps")
|
(li "15 dedicated deps tests pass (scan, transitive, circular, compute-all, components-needed)")
|
||||||
(li "No \"Unknown component\" errors after bundle reduction")
|
(li "541 total Python tests pass with zero regressions")
|
||||||
(li "Payload size reduction measurable"))))
|
(li "JS deps functions verified in Node.js (scanRefs, transitiveDeps, computeAllDeps, componentsNeeded)")
|
||||||
|
(li "Both bootstrapped and fallback code paths produce identical results"))))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
;; Phase 2
|
;; Phase 2
|
||||||
|
|||||||
@@ -179,7 +179,10 @@ boot.sx depends on: cssx, orchestration, adapter-dom, render
|
|||||||
|
|
||||||
;; Extensions (optional — loaded only when target requests them)
|
;; Extensions (optional — loaded only when target requests them)
|
||||||
continuations.sx depends on: eval (optional)
|
continuations.sx depends on: eval (optional)
|
||||||
callcc.sx depends on: eval (optional)")))
|
callcc.sx depends on: eval (optional)
|
||||||
|
|
||||||
|
;; Spec modules (optional — loaded via --spec-modules)
|
||||||
|
deps.sx depends on: eval (optional)")))
|
||||||
|
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(h2 :class "text-2xl font-semibold text-stone-800" "Extensions")
|
(h2 :class "text-2xl font-semibold text-stone-800" "Extensions")
|
||||||
|
|||||||
Reference in New Issue
Block a user