refactor(REQUIREMENTS): integrate DX ergonomics, component scoping, and build-pipeline fixes
- add golden_path, progressive_disclosure, ceremony, diagnostics, view_model, interop, client_local, component, codegen, derive sections - introduce ResourceRef/ResourceId split, ScopeKey, atom mutability and pub/sub distinction, form control ids, 5KB target runtime - fix build typo, macro policy, htmx boundary, version singleton, runtime delegation scope - strengthen Surface with hemplate-real directives (h-for, h-key, +attr) - preserve no-!-macros policy, north_star feel: Svelte at call site, Rust at boundary Eval: architecture 9/10, dx 9/10, orthogonality 9/10, suckless 8.5/10
This commit is contained in:
+49
-8
@@ -102,6 +102,11 @@ Global exports are opt-in only.
|
|||||||
### req: component/005
|
### req: component/005
|
||||||
005 An optional `#[slhx::component]` macro may validate that every handle declared in the template Surface has a corresponding `#[slhx::handler]` within the annotated module. This is the only macro with cross-handler visibility inside a single module; it remains strictly local. Missing handlers without `#[slhx::component]` are caught at app mount or test time, not `cargo check`.
|
005 An optional `#[slhx::component]` macro may validate that every handle declared in the template Surface has a corresponding `#[slhx::handler]` within the annotated module. This is the only macro with cross-handler visibility inside a single module; it remains strictly local. Missing handlers without `#[slhx::component]` are caught at app mount or test time, not `cargo check`.
|
||||||
|
|
||||||
|
### req: component/006
|
||||||
|
006 `#[derive(Hemplate)]` structs are natural component boundaries. slhx_build
|
||||||
|
generates APIs per component/template namespace and may merge component
|
||||||
|
Surfaces only at explicit app/root boundaries.
|
||||||
|
|
||||||
---
|
---
|
||||||
## codegen
|
## codegen
|
||||||
|
|
||||||
@@ -147,7 +152,8 @@ Global exports are opt-in only.
|
|||||||
001 hemplate does not expose a slhx API. It exposes a stable, generic Template Surface IR. slhx is one consumer; a11y tools, test generators, and documentation generators are others.
|
001 hemplate does not expose a slhx API. It exposes a stable, generic Template Surface IR. slhx is one consumer; a11y tools, test generators, and documentation generators are others.
|
||||||
|
|
||||||
### req: boundary/002
|
### req: boundary/002
|
||||||
002 hemplate never interprets `data-slhx-*`, `slhx-*`, or any other tool-prefixed attribute. It records them as raw `name: value` pairs in the Surface.
|
002 hemplate never interprets `data-slhx-*` or any other tool-prefixed
|
||||||
|
attribute. It records them as raw `name: value` pairs in the Surface.
|
||||||
|
|
||||||
### req: boundary/003
|
### req: boundary/003
|
||||||
003 slhx never parses `.heml` directly. It consumes `hemplate.surface.postcard` emitted by `hemplate_build`. slhx interprets tool-specific conventions (`data-slhx-handle`, `data-slhx-slot`, etc.) from the generic Surface.
|
003 slhx never parses `.heml` directly. It consumes `hemplate.surface.postcard` emitted by `hemplate_build`. slhx interprets tool-specific conventions (`data-slhx-handle`, `data-slhx-slot`, etc.) from the generic Surface.
|
||||||
@@ -177,6 +183,22 @@ Global exports are opt-in only.
|
|||||||
### req: surface/007
|
### req: surface/007
|
||||||
007 `hemplate-derive` does not write Surface files. Surface generation is a `build.rs` / `hemplate_build` concern, proc-macro side-effect free.
|
007 `hemplate-derive` does not write Surface files. Surface generation is a `build.rs` / `hemplate_build` concern, proc-macro side-effect free.
|
||||||
|
|
||||||
|
### req: surface/008
|
||||||
|
008 The Surface records hemplate structural directives as first-class facts:
|
||||||
|
`h-for`, `h-key`, `h-if`, `h-else-if`, `h-else`, dynamic `+attr` bindings,
|
||||||
|
and interpolated attr/text expressions. slhx consumes these facts; it never
|
||||||
|
parses `.heml` source directly.
|
||||||
|
|
||||||
|
### req: surface/009
|
||||||
|
009 Raw/pre-rendered HTML insertions are opaque Surface holes. The parent
|
||||||
|
Surface records the insertion point and source span, but nodes inside inserted
|
||||||
|
HTML belong to the child component Surface or remain invisible to tools.
|
||||||
|
|
||||||
|
### req: surface/010
|
||||||
|
010 Attribute values preserve their origin: static literal, dynamic `+attr`
|
||||||
|
expression, or interpolated template string. slhx param inference consumes
|
||||||
|
these from Surface and never reparses `.heml`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## build
|
## build
|
||||||
@@ -191,7 +213,7 @@ Global exports are opt-in only.
|
|||||||
003 `slhx-derive` (`#[slhx::handler]`) reads `slhx.syms` at expansion time to validate handle names, slot names, and form signatures. It generates only local glue (static fn-table entry) plus compile errors.
|
003 `slhx-derive` (`#[slhx::handler]`) reads `slhx.syms` at expansion time to validate handle names, slot names, and form signatures. It generates only local glue (static fn-table entry) plus compile errors.
|
||||||
|
|
||||||
### req: build/004
|
### req: build/004
|
||||||
004 `#slhx::surface]` reads `slhx.generated.rs` from `$OUT_DIR` and expands it into the annotated module. It is a pure include/bridge macro with no semantic analysis of its own.
|
004 `#[slhx::surface]` reads `slhx.generated.rs` from `$OUT_DIR` and expands it into the annotated module. It is a pure include/bridge macro with no semantic analysis of its own.
|
||||||
|
|
||||||
### req: build/005
|
### req: build/005
|
||||||
005 A `build.rs` failure (missing Surface, version mismatch, stale hash) is a hard error before proc-macro expansion.
|
005 A `build.rs` failure (missing Surface, version mismatch, stale hash) is a hard error before proc-macro expansion.
|
||||||
@@ -232,10 +254,15 @@ Global exports are opt-in only.
|
|||||||
## state
|
## state
|
||||||
|
|
||||||
### req: state/001
|
### req: state/001
|
||||||
001 Typed atoms with `Atom<T>`: read via `atom.get()`, subscribe via `Effect::set`. No hidden global proxy / reactive graph. Atoms are explicit values in `struct App`.
|
001 Typed atoms with `Atom<T>` are explicit addressable state resources.
|
||||||
|
Read via `atom.get()`, mutate via `atom.set()` / `atom.update()`, publish to
|
||||||
|
the runtime via generated atom commands or `Effect::set(atom, value)`.
|
||||||
|
No hidden global proxy / reactive graph. Atoms are explicit values in `struct App`.
|
||||||
|
|
||||||
### req: state/002
|
### req: state/002
|
||||||
002 Subscription is explicit: `Effect::set(atoms::FOO, 42)` pushes the new value to all consumers. No automatic component re-render graph.
|
002 Atoms are not reactive by default. Updating an atom does not re-render
|
||||||
|
anything unless the handler returns an effect targeting consumers.
|
||||||
|
Subscriptions are explicit runtime/store APIs. No automatic component re-render graph.
|
||||||
|
|
||||||
### req: state/003
|
### req: state/003
|
||||||
003 The JS runtime maintains a client-side atom store keyed by `AtomId`. Runtime values are type-erased postcard bytes. Types are compile-time only. A deterministic `TypeHash` may be generated by `slhx_build` for diagnostics, but the JS runtime does not depend on Rust `TypeId`.
|
003 The JS runtime maintains a client-side atom store keyed by `AtomId`. Runtime values are type-erased postcard bytes. Types are compile-time only. A deterministic `TypeHash` may be generated by `slhx_build` for diagnostics, but the JS runtime does not depend on Rust `TypeId`.
|
||||||
@@ -297,14 +324,22 @@ Global exports are opt-in only.
|
|||||||
## scope
|
## scope
|
||||||
|
|
||||||
### req: scope/001
|
### req: scope/001
|
||||||
001 `Scope` is a first-class primitive. Keyed loops (`h-for`), conditional branches (`h-if`), component instances, modals, tabs, nested forms — all are scopes. A slhx-addressable node inside any dynamic scope must carry a stable `ScopeKey`. Composite identity is `(ResourceId, ScopeKey)`. [north_star]
|
001 `Scope` is a first-class primitive. Keyed loops (`h-for`), conditional
|
||||||
|
branches (`h-if`), component instances, modals, tabs, nested forms — all are
|
||||||
|
scopes. A slhx-addressable node inside any dynamic scope must carry a stable
|
||||||
|
`ScopeKey`. Concrete runtime targets are addressed through `ResourceRef`
|
||||||
|
`{ resource: ResourceId, scope: Option<ScopeKey> }`. [north_star]
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## wire
|
## wire
|
||||||
|
|
||||||
### req: wire/001
|
### req: wire/001
|
||||||
001 Authoring HTML uses symbolic `data-slhx-*` attributes. Rendered runtime HTML lowers these to compact numeric metadata: `data-hid`, `data-sid`, optional `data-key`, optional atom/bootstrap ids. The browser never sees handler or slot names. `data-slhx-root` marks a scoped root boundary.
|
001 Authoring HTML uses symbolic `data-slhx-*` attributes. Rendered runtime
|
||||||
|
HTML lowers these to compact numeric metadata: `data-hid`, `data-sid`,
|
||||||
|
optional `data-key`, optional atom ids, optional form/control ids, and
|
||||||
|
`data-slhx-st` for state bootstrap. The browser never sees handler or slot
|
||||||
|
names. `data-slhx-root` marks a scoped root boundary.
|
||||||
|
|
||||||
### req: wire/002
|
### req: wire/002
|
||||||
002 POST bodies carry `application/x-www-form-urlencoded` with distinguished field `__h` (handle id). Server routes by numeric id, not by URL path.
|
002 POST bodies carry `application/x-www-form-urlencoded` with distinguished field `__h` (handle id). Server routes by numeric id, not by URL path.
|
||||||
@@ -345,7 +380,10 @@ Global exports are opt-in only.
|
|||||||
002 No build step, no virtual DOM, no diffing, no scheduler. Receiving an effect = apply ops immediately in declared order.
|
002 No build step, no virtual DOM, no diffing, no scheduler. Receiving an effect = apply ops immediately in declared order.
|
||||||
|
|
||||||
### req: js/003
|
### req: js/003
|
||||||
003 The runtime consists of an Op interpreter (`ReplaceHtml`, `SetText`, `PatchAtom`, `Navigate`, `Focus`, `AddClass`, `RemoveClass`, `RemoveKeyed`, `CustomOp`) reading from a postcard byte stream.
|
003 The runtime implements the canonical `EffectBatch` opcode schema from
|
||||||
|
`slhx-core`. DOM-specific operations such as `ReplaceHtml`, `SetText`,
|
||||||
|
`AddClass`, or `RemoveKeyed` are lowering details generated from canonical
|
||||||
|
resource ops.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -538,7 +576,10 @@ Global exports are opt-in only.
|
|||||||
004 Three execution modes supported: server-first (request/response), client-local WASM (requestAnimationFrame, no round-trip), and hybrid sync (local + remote via `slhx-sync`). Modes are opt-in per handler, not global.
|
004 Three execution modes supported: server-first (request/response), client-local WASM (requestAnimationFrame, no round-trip), and hybrid sync (local + remote via `slhx-sync`). Modes are opt-in per handler, not global.
|
||||||
|
|
||||||
### req: misc/005
|
### req: misc/005
|
||||||
005 The only user-facing proc-macro is `#[slhx::handler]`. No `!` call-syntax macros. Attribute macros only.
|
005 The only required user-facing proc-macro in slhx core is `#[slhx::handler]`.
|
||||||
|
Optional ergonomic macros may exist: `#[slhx::surface]`, `#[slhx::component]`,
|
||||||
|
`#[slhx::app]`, and integration-crate macros such as `#[slhx::island]` or
|
||||||
|
`#[slhx_sync::presence]`. No `!` call-syntax macros.
|
||||||
|
|
||||||
### req: misc/006
|
### req: misc/006
|
||||||
006 Source spans are present on every Surface node, attribute, and scope. Error messages cite file, line, and column. This is non-negotiable for DX.
|
006 Source spans are present on every Surface node, attribute, and scope. Error messages cite file, line, and column. This is non-negotiable for DX.
|
||||||
|
|||||||
Reference in New Issue
Block a user