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
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
### 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
|
||||
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
|
||||
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
|
||||
@@ -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.
|
||||
|
||||
### 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
|
||||
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
|
||||
|
||||
### 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
|
||||
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
|
||||
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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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
|
||||
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