solid2-agent-kit 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Supported agents: **Cursor** and **Claude Code**.
|
|
|
16
16
|
| Always-applied hard rules | `.cursor/rules/solid-2.mdc` (glob-attached to `*.tsx`/`*.jsx`) | managed block in `CLAUDE.md` | 23 hard rules + banned Solid 1.x API table + eslint-plugin-solid lint-heritage table. Injected without the agent having to decide to read anything |
|
|
17
17
|
| Agent skill | `.cursor/skills/solid-2/` | `.claude/skills/solid-2/` | Execution-model primer, decision tables, canonical patterns, a “still runs / write the other form” table, review checklist, official-doc URL index |
|
|
18
18
|
| Shared agent context | managed block in `AGENTS.md` | same (Claude Code users can reference it from `CLAUDE.md`) | Core principles and pointers, always in context |
|
|
19
|
-
| Mechanical gate | `solid2-kit check` | same | Whole-file regex guard over `src/**/*.{ts,tsx}`: fails on props destructuring (including multi-line signatures), `{ ...props }` rest copies, `{list().map(...)}` in JSX, React imports/hooks/JSX props/`React.lazy`/`useOptimistic`/`onDoubleClick`, Solid 1.x imports/APIs/components/JSX namespaces, `vite-plugin-solid`, old-router `<Route>`/`<A>`/`<HashRouter>`/`<FileRoutes>`, `MetaProvider`, `Context.Provider`, camelCase style keys, `key` props, `value()!` hand-narrowing, hand-rolled loading signals, `=== undefined` readiness branches, SolidStart leftovers (`@solidjs/start`, `h3`, `createAsync`, `useSubmission`, `"use client"`), Next.js imports/`useRouter`/`typeof window`, `history.pushState`,
|
|
19
|
+
| Mechanical gate | `solid2-kit check` | same | Whole-file regex guard over `src/**/*.{ts,tsx}`: fails on props destructuring (including multi-line signatures), `{ ...props }` rest copies, `{list().map(...)}` in JSX, React imports/hooks/JSX props/`React.lazy`/`useOptimistic`/`onDoubleClick`, Solid 1.x imports/APIs/components/JSX namespaces, `vite-plugin-solid`, old-router `<Route>`/`<A>`/`<HashRouter>`/`<FileRoutes>`, `MetaProvider`, `Context.Provider`, camelCase style keys, `key` props, `value()!` hand-narrowing, hand-rolled loading signals, `=== undefined` readiness branches, SolidStart leftovers (`@solidjs/start`, `h3`, `createAsync`, `useSubmission`, `"use client"`), Next.js imports/`useRouter`/`typeof window`, `history.pushState`, `render(<App />)` (pass a function), `isPending(user())` / `latest(user())` (pass the accessor), and `<Dynamic>` (use `dynamic()`) |
|
|
20
20
|
|
|
21
21
|
Managed blocks are delimited with `<!-- solid2-agent-kit:*:start/end -->` markers; everything
|
|
22
22
|
outside them is yours. Kit-owned files (`solid-2.mdc`, the skill directories) are overwritten
|
package/bin/solid2-kit.mjs
CHANGED
|
@@ -351,6 +351,20 @@ const CHECKS = [
|
|
|
351
351
|
message:
|
|
352
352
|
'Readiness branch on a zero-arg call. Read async values under <Loading> (first load) / isPending() (refetch) and let errors reach <Errored>. For non-reactive utils, assign to a variable and test that.',
|
|
353
353
|
},
|
|
354
|
+
{
|
|
355
|
+
// `isPending(user())` / `latest(data())` evaluate the read before the
|
|
356
|
+
// helper runs. `isPending(user)` and `isPending(() => user())` do not match.
|
|
357
|
+
id: 'pending-accessor-call',
|
|
358
|
+
pattern: /(?<![.\w])(?:isPending|latest)\(\s*[A-Za-z_$][\w$]*\(\s*\)/g,
|
|
359
|
+
message:
|
|
360
|
+
'Pass the accessor to isPending/latest, not the call: isPending(user), not isPending(user()). Calling it first evaluates the read before the helper runs.',
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
id: 'dynamic-jsx',
|
|
364
|
+
pattern: /<Dynamic[\s/>]/g,
|
|
365
|
+
message:
|
|
366
|
+
'`<Dynamic>` is a JSX convenience wrapper. Application code should use dynamic(() => ...) from "@solidjs/web" so the component identity stays stable.',
|
|
367
|
+
},
|
|
354
368
|
];
|
|
355
369
|
|
|
356
370
|
// .ts / .tsx sources, excluding .d.ts declaration files.
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
- All TSX in this project is Solid 2.0. Components run **once**; reactivity flows through signals/stores to JSX. React patterns (props destructuring, `className`, per-keystroke `onChange`, state-synced-by-effect, `.map()` lists) and Solid 1.x APIs (`createResource`, `onMount`, `solid-js/store`, `Suspense`, path setters) are bugs here.
|
|
4
4
|
- Before writing TSX, read the `solid-2` skill (`.cursor/skills/solid-2/SKILL.md` for Cursor, `.claude/skills/solid-2/SKILL.md` for Claude Code): patterns, decision tables, official doc URLs. Hard rules auto-attach from `.cursor/rules/solid-2.mdc` in Cursor and live in a managed block in `CLAUDE.md` for Claude Code.
|
|
5
5
|
- After editing TSX, run the `solid2-kit check` mechanical gate — it fails on React/Solid 1.x tokens and props destructuring. Use the project's `lint:solid` script if one exists; otherwise `npx solid2-agent-kit check` (with the kit as a devDependency) or `npx github:lightsound/solid2-agent-kit check`. Do not install `eslint-plugin-solid` (built for Solid 1.x; misreads Solid 2 idioms).
|
|
6
|
-
- Solid 2.0 shipped recently and breaks with 1.x — never trust pre-2.0 Solid docs, tutorials, or training knowledge. Verify APIs against https://v2-rebuild--solid-docs-v2.netlify.app/llms.txt (markdown mirror of https://v2.solidjs.com/, which blocks non-browser fetchers). If the project has `@solidjs/router`, server functions, or `@solidjs/meta`, use those official pages (see the skill's official-docs index) — not React Router, Next.js, SolidStart 1.x, or Solid Router 0.x/1.x JSX `<Route>`/`<A>`. Core `action` from `solid-js` is a generator transaction; router `action`/`query` from `@solidjs/router` are POST forms and a read cache. Pass a function to `render`/`hydrate`: `render(() => <App />, root)`. Component teardown is `onSettled`, not `onCleanup`. One router instance; in-app navigation is `useNavigate` / `<a href>`, not `window.location`.
|
|
6
|
+
- Solid 2.0 shipped recently and breaks with 1.x — never trust pre-2.0 Solid docs, tutorials, or training knowledge. Verify APIs against https://v2-rebuild--solid-docs-v2.netlify.app/llms.txt (markdown mirror of https://v2.solidjs.com/, which blocks non-browser fetchers). If the project has `@solidjs/router`, server functions, or `@solidjs/meta`, use those official pages (see the skill's official-docs index) — not React Router, Next.js, SolidStart 1.x, or Solid Router 0.x/1.x JSX `<Route>`/`<A>`. Core `action` from `solid-js` is a generator transaction; router `action`/`query` from `@solidjs/router` are POST forms and a read cache. Core `refresh(source)`, router `revalidate(key)`, and server-function `return reload(...)` are three different APIs. Pass the accessor to `isPending(user)`, not `isPending(user())`. Pass a function to `render`/`hydrate`: `render(() => <App />, root)`. Component teardown is `onSettled`, not `onCleanup`. One router instance; in-app navigation is `useNavigate` / `<a href>`, not `window.location`.
|
|
@@ -78,10 +78,14 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
|
|
|
78
78
|
accessor) only when that identity change should show the fallback again. Pass an async
|
|
79
79
|
memo to a child as the **accessor** (`user={user}`), and read `props.user()` under that
|
|
80
80
|
child's `<Loading>` — `user={user()}` throws not-ready at the parent, so the child's
|
|
81
|
-
boundary never sees it. Do not
|
|
81
|
+
boundary never sees it. Do not start `fetch` (or any request) at component-body top
|
|
82
|
+
level — that runs once at mount and is not a reactive source. Do not `try/catch` `NotReadyError` around a read, and do not
|
|
82
83
|
use `loadingValue` / `seedLoadingValue` as the default first-flight UI — those skip
|
|
83
|
-
`<Loading>`. `{latest(() => x())}` is a preview, not the visible answer
|
|
84
|
-
is the indicator
|
|
84
|
+
`<Loading>`. `{latest(() => x())}` is a preview, not the visible answer. `isPending`
|
|
85
|
+
is the *refetch* indicator after a settled answer exists — do not use it as the
|
|
86
|
+
first-load spinner (`<Loading>` owns that). Pass the accessor: `isPending(user)`,
|
|
87
|
+
not `isPending(user())` (that evaluates the read before `isPending` runs).
|
|
88
|
+
"Prop with local edits" = writable derivation:
|
|
85
89
|
`createSignal(() => props.value)` or `createStore(() => props.value, fallback)`.
|
|
86
90
|
`createOptimistic` is for an in-flight mutation, not a local editing session.
|
|
87
91
|
11. Context: the context object is the provider — `<MyContext value={{ theme, setTheme }}>`
|
|
@@ -139,7 +143,11 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
|
|
|
139
143
|
counter/version signal read inside the computation. Input-driven refetch is automatic
|
|
140
144
|
(tracked inputs re-run the computation) and subscriptions push. Legit uses: after a
|
|
141
145
|
mutation inside an `action`, and explicit reload buttons. Pair with `affects(source)`
|
|
142
|
-
when the reload should present as pending.
|
|
146
|
+
when the reload should present as pending — a bare `refresh()` re-asks quietly.
|
|
147
|
+
These are three different APIs: core `refresh(source)` reruns a reactive source;
|
|
148
|
+
router `revalidate(getUser.key)` invalidates the query cache; server-function
|
|
149
|
+
`return reload({ revalidate: "todos" })` asks the integration to refresh cached
|
|
150
|
+
data. Do not mix them.
|
|
143
151
|
20. **External collections flow into stores through reconciliation.** First choice:
|
|
144
152
|
function-form `createStore(() => source, fallback)` / `createProjection` (reconcile
|
|
145
153
|
automatically, keyed by `"id"`). Manual merge: `setStore(reconcile(fresh, "id"))` or
|
|
@@ -175,9 +183,13 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
|
|
|
175
183
|
fallback body), not event-time mutations. `JSON.stringify(store)`
|
|
176
184
|
/ `structuredClone(store)` without `snapshot(store)` can throw or leak proxies. Async SSR
|
|
177
185
|
without `<Loading>` *works* but blocks the HTML shell until every read settles — wrap
|
|
178
|
-
reads, or stream with `renderToStream`.
|
|
179
|
-
|
|
186
|
+
reads, or stream with `renderToStream`. `pipe` / `pipeTo` / `readable` each consume
|
|
187
|
+
a stream render — use exactly one. `createRoot` is for tests, libraries, and
|
|
188
|
+
non-render entry points; inside a component let `render` / the component owner
|
|
189
|
+
own the scope. Async reads inside `<Portal>` start on the client — hoist the
|
|
190
|
+
read above the portal. Navigation-shaped updates (a setter, then async computeds holding previous values)
|
|
180
191
|
do not need core `action` — reach for it only when writes happen *after* async work.
|
|
192
|
+
Invoke actions from handlers, not from memos or effects.
|
|
181
193
|
23. **Composition.** Pass `props.children` through when you only render them. Inspect or
|
|
182
194
|
iterate children with `children(() => props.children)` (then `.toArray()`), never a
|
|
183
195
|
setup-time snapshot. Code-split with `lazy(() => import("./X"))` under `<Loading>` —
|
|
@@ -199,8 +211,17 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
|
|
|
199
211
|
`window.location` / `history.pushState`. Trusted identity is
|
|
200
212
|
`getRequestEvent()`, never a caller-supplied user id. Unscripted forms use
|
|
201
213
|
the function `.url` (or a router `action`), not a hand-built `/_server/`
|
|
202
|
-
path.
|
|
203
|
-
`<
|
|
214
|
+
path. Client history adapters do not select the SSR URL — pass
|
|
215
|
+
`<Router url={request.url}>` (or rely on the request event). Secrets belong in
|
|
216
|
+
`virtual:env/server`, never the `client` env map / `import.meta.env`. Document head is
|
|
217
|
+
`<Title>` / `<Meta>` from `@solidjs/meta` with **no** `MetaProvider`. A static
|
|
218
|
+
`<title>` in the document shell is the fallback; do not hardcode other tags
|
|
219
|
+
(`<meta name="description">`) that Solid Meta should manage.
|
|
220
|
+
Return `respond(value, { status })` from a `"use server"` function, not
|
|
221
|
+
`Response.json` — a raw `Response` is HTTP-handler control flow; scripted
|
|
222
|
+
callers should receive the value. JSON-encodable server-function arguments
|
|
223
|
+
only, unless `enableRichArguments()` was called once in the client entry
|
|
224
|
+
(`Date` / `Map` / `Set` throw without it).
|
|
204
225
|
|
|
205
226
|
## Lint heritage: eslint-plugin-solid (Solid 1.x) intents carried into this file
|
|
206
227
|
|
|
@@ -264,6 +285,7 @@ When unsure about any API, verify against the official Solid 2.0 docs — fetcha
|
|
|
264
285
|
listed in `references/official-docs.md` next to the `solid-2` skill. Do not guess from
|
|
265
286
|
Solid 1.x or React memory.
|
|
266
287
|
|
|
267
|
-
`createTrackedEffect` and `storePath` exist in Solid 2 but
|
|
268
|
-
`createEffect` unless a single tracked callback is
|
|
269
|
-
`storePath` except while converting 1.x
|
|
288
|
+
`createTrackedEffect`, `createRenderEffect`, and `storePath` exist in Solid 2 but
|
|
289
|
+
are not defaults: use two-phase `createEffect` unless a single tracked callback is
|
|
290
|
+
required, and draft setters instead of `storePath` except while converting 1.x
|
|
291
|
+
path setters.
|
|
@@ -67,7 +67,8 @@ Most React `useEffect` code should NOT become `createEffect`:
|
|
|
67
67
|
| Inspect, count, or iterate children | `children(() => props.children)` then `.toArray()` |
|
|
68
68
|
| Code-split a component | `lazy(() => import("./X"))` read under `<Loading>` |
|
|
69
69
|
| Named export from a lazy module | `lazy(() => import("./pages"), { export: "About" })` |
|
|
70
|
-
| Pick a component/tag from reactive state | `dynamic(() => ...)` from `@solidjs/web` (stable identity) |
|
|
70
|
+
| Pick a component/tag from reactive state | `dynamic(() => ...)` from `@solidjs/web` (stable identity; prefer over `<Dynamic>`) |
|
|
71
|
+
| Overlay / modal | `<Portal>` — hoist async reads *above* the portal (reads inside start on the client) |
|
|
71
72
|
| Browser-only widget (charts, maps, `window`) | `clientOnly(() => import("./Chart"))` from `@solidjs/web` (`{ lazy: true }` defers the import until first render) |
|
|
72
73
|
| Server vs browser branch | `isServer` / `isDev` from `@solidjs/web` (build-time constants), not `typeof window` |
|
|
73
74
|
| SSR-stable `id` / `for` / `aria-*` pairing | `createUniqueId()` |
|
|
@@ -199,7 +200,8 @@ const Result = dynamic(() => (detailed() ? Detailed : Compact));
|
|
|
199
200
|
`dynamic()` returns a **stable** component whose source can be a component, an
|
|
200
201
|
intrinsic tag name, a promise, or empty. Prefer it over swapping a component
|
|
201
202
|
variable in JSX (`const View = tab() ? A : B` freezes the choice at setup).
|
|
202
|
-
`<Dynamic>` is the JSX spelling of the same primitive
|
|
203
|
+
`<Dynamic>` is the JSX spelling of the same primitive; application code should
|
|
204
|
+
use `dynamic()` so the component identity stays stable.
|
|
203
205
|
|
|
204
206
|
### Two-phase effect (imperative boundary only)
|
|
205
207
|
|
|
@@ -245,15 +247,18 @@ return (
|
|
|
245
247
|
where the memo was created, and not of page chrome (header/nav) that should stay mounted.
|
|
246
248
|
Boundary placement is purely a UX decision — it does not change when fetches start
|
|
247
249
|
(no waterfalls: nested components set up and fetch in parallel). After first paint the
|
|
248
|
-
boundary keeps settled content during refetch; `isPending(
|
|
250
|
+
boundary keeps settled content during refetch; `isPending(results)` (pass the accessor,
|
|
251
|
+
not `isPending(results())`) is the
|
|
249
252
|
indicator. Use `on={query()}` (the **value**, not the accessor `on={query}`) only when
|
|
250
253
|
that identity should put the fallback back on screen.
|
|
254
|
+
Do not use `isPending` as the first-load spinner.
|
|
251
255
|
- Read every reactive input **before the first `await`** in an async computation. A read
|
|
252
256
|
after `await` does not subscribe; production can sit pending with no retry.
|
|
253
257
|
- `<Errored>` function fallbacks receive an error **accessor** and a `reset` callback:
|
|
254
258
|
`fallback={(error, reset) => ...}`.
|
|
255
|
-
- Refetch: `refresh(results)`. In-flight indicator: `isPending(
|
|
256
|
-
Freshest in-flight value for
|
|
259
|
+
- Refetch: `refresh(results)`. In-flight indicator: `isPending(results)` (or
|
|
260
|
+
`isPending(() => results())`). Freshest in-flight value for a preview:
|
|
261
|
+
`latest(results)`. Do not start `fetch` at component-body top level.
|
|
257
262
|
- Coordinate reveal order of sibling `<Loading>` boundaries with `<Reveal order="sequential" | "together" | "natural">`.
|
|
258
263
|
|
|
259
264
|
**Never hand-roll these states.** No `[loading, setLoading]` signal, no
|
|
@@ -634,7 +639,10 @@ shared cache, no preload reuse, and no `revalidate`. Do not wrap mutations in
|
|
|
634
639
|
`query` — wrapping an undeclared server function makes it GET. `query.get(key)`
|
|
635
640
|
throws if there is no entry; `query.set` does not accept a promise. Cache is
|
|
636
641
|
request-scoped on the server and application-scoped in the browser. After a
|
|
637
|
-
router mutation, `revalidate(...)` — not core `refresh()
|
|
642
|
+
router mutation, `revalidate(...)` — not core `refresh()`, and not
|
|
643
|
+
`reload()` (that is a *server-function return* that asks the integration to
|
|
644
|
+
refresh cached data: `return reload({ revalidate: "todos" })`). `live()` sources
|
|
645
|
+
update through the open stream; do not `revalidate` them. Replace SolidStart /
|
|
638
646
|
Router 0.x leftovers: no `createAsync`, `useSubmission` (singular), `cache()`,
|
|
639
647
|
router `json()`, or `<FileRoutes />` (`fileRoutes(pageRoutes)` instead).
|
|
640
648
|
|
|
@@ -656,7 +664,11 @@ revalidate: `redirect` / `reload` from `@solidjs/web` (see the server-function
|
|
|
656
664
|
guides). For 404 during SSR: `httpStatus(404)` from `@solidjs/web`.
|
|
657
665
|
`throw new Error("…")` is stripped to `"Internal Server Error"` in production —
|
|
658
666
|
use `markSafeError` or `respond(..., { status })` for intentional client-facing
|
|
659
|
-
failures. `
|
|
667
|
+
failures. Do not `return Response.json(...)` from `"use server"` — that is
|
|
668
|
+
HTTP-handler control flow; `respond(value, { status })` is what a scripted
|
|
669
|
+
caller unwraps. JSON-encodable arguments only unless `enableRichArguments()`
|
|
670
|
+
was called once in the client entry (`Date` / `Map` / `Set` throw without it).
|
|
671
|
+
`GET()` is only for idempotent reads (URLs leak into logs/history);
|
|
660
672
|
import it from `@solidjs/web/server-functions`, never `@solidjs/start`.
|
|
661
673
|
`live()` yields **current state** (each yield replaces the last); do not treat
|
|
662
674
|
it as an append-only event log, and hoist the memo so consumers share one
|
|
@@ -676,8 +688,12 @@ import { Title, Link, Meta } from '@solidjs/meta';
|
|
|
676
688
|
```
|
|
677
689
|
|
|
678
690
|
Solid Meta 1.x has **no provider**. Render tags anywhere; later wins, unmount
|
|
679
|
-
restores. `useHead` from `@solidjs/web` is the lower-level registry
|
|
680
|
-
|
|
691
|
+
restores. `useHead` from `@solidjs/web` is the lower-level registry — prefer
|
|
692
|
+
`<Title>` / `<Meta>` / `<Script>` (JSON-LD) for application metadata. Several
|
|
693
|
+
tags that should swap as one unit belong in `<Head>`. A static `<title>`
|
|
694
|
+
in the document shell is the fallback when no `<Title>` is mounted; do not
|
|
695
|
+
hardcode a second `<meta name="description">` (the registry leaves foreign tags
|
|
696
|
+
alone, so both would coexist). Do not hardcode a second `<title>` in the document shell.
|
|
681
697
|
|
|
682
698
|
## These still run — write the other form
|
|
683
699
|
|
|
@@ -742,7 +758,7 @@ how to reuse. Prefer the form on the right.
|
|
|
742
758
|
| `<For each={resolved()}>` after `children()` | `{resolved.toArray()}` — resolved children are not a reactive data list |
|
|
743
759
|
| `<input onChange={...}>` for keystrokes | `onInput` — native `onChange` is blur/commit |
|
|
744
760
|
| `createOptimistic` for a local edit draft | writable derivation / plain store. Optimistic is for an in-flight mutation |
|
|
745
|
-
| `createMemo(async fn, { loadingValue })` as the default first-flight UI | `<Loading>` for first flight;
|
|
761
|
+
| `createMemo(async fn, { loadingValue })` / `{ seedLoadingValue: true }` as the default first-flight UI | `<Loading>` for first flight; those options are escape hatches (store projections use `seedLoadingValue`) |
|
|
746
762
|
| `setSubmitted(true)` + an effect that watches it | do the work in the handler or an `action` |
|
|
747
763
|
| `findUser(userId)` with the id from the client as identity | `getRequestEvent()!.locals.userId` |
|
|
748
764
|
| `action={"/_server/" + id}` in app JSX | `action={createTodo.url}` or a router `action` |
|
|
@@ -753,6 +769,27 @@ how to reuse. Prefer the form on the right.
|
|
|
753
769
|
| `{latest(() => results())}` as the visible list | settled read + `isPending()` for the indicator; `latest` is for previews |
|
|
754
770
|
| `typeof window !== "undefined"` | `isServer` / `isDev` from `@solidjs/web` |
|
|
755
771
|
| `Object.assign({}, props)` / expecting `merge` to skip `undefined` like 1.x `mergeProps` | `omit` / `merge` — explicit `undefined` **overrides** (Object.assign) |
|
|
772
|
+
| `isPending(user())` / `latest(user())` | `isPending(user)` / `latest(user)` — pass the accessor (or `() => user()`). Calling it first evaluates the read before the helper runs |
|
|
773
|
+
| `isPending(...)` as the first-load spinner | `<Loading>` owns first flight; `isPending` is the *refetch* indicator after a settled answer exists |
|
|
774
|
+
| `refresh(getUser.key)` / `revalidate(user)` / `return refresh()` from `"use server"` | three APIs: core `refresh(source)` reruns a reactive source; router `revalidate(getUser.key)` invalidates the query cache; `return reload({ revalidate: "todos" })` asks the integration to refresh cached data |
|
|
775
|
+
| `revalidate(liveSource)` | `live()` updates through the open stream; do not revalidate it |
|
|
776
|
+
| `refresh(user)` without `affects` when the reload should look pending | pair `affects(user)` with `refresh(user)` — a bare `refresh` re-asks quietly |
|
|
777
|
+
| `<Dynamic component={tab() ? A : B} />` | `const View = dynamic(() => (tab() ? A : B))` — `<Dynamic>` is a JSX convenience; app code should use `dynamic()` for stable identity |
|
|
778
|
+
| `<meta name="description" content="...">` hardcoded in the document shell | `<Meta>` from `@solidjs/meta`. A static `<title>` is the fallback; other hardcoded tags coexist with the registry |
|
|
779
|
+
| `useHead({ tag: "meta", ... })` for ordinary page tags | `<Title>` / `<Meta>` / `<Script>` (JSON-LD). `useHead` is the lower-level registry |
|
|
780
|
+
| two `<Meta property="og:image">` meant as one replacement set | wrap them in `<Head>` — a later group replaces the earlier set as one unit |
|
|
781
|
+
| `createRoot(() => ...)` inside a component | let `render` / the component owner own the scope. `createRoot` is for tests, libraries, and non-render entry points |
|
|
782
|
+
| `JSON.stringify(store)` / `structuredClone(store)` | `snapshot(store)` — proxies throw or leak reactivity |
|
|
783
|
+
| `import { env } from "virtual:env/client"` (or `import.meta.env`) for a signing secret | `import { env } from "virtual:env/server"` — client map values are public |
|
|
784
|
+
| `renderToStream(...).pipe(res)` and also `.readable` / `await` on the same result | exactly one consumer: `pipe` / `pipeTo` / `readable` / await |
|
|
785
|
+
| `return Response.json(user, { status: 201 })` from `"use server"` | `return respond(user, { status: 201 })` — scripted callers receive `user`, not a `Response` |
|
|
786
|
+
| `<Portal>{user().name}</Portal>` for an async read | hoist the read above the portal (children start on the client); async UI inside one wants its own `<Loading>` |
|
|
787
|
+
| a client history adapter to pick the SSR location | `<Router url={request.url}>` (or the request event). Client adapters do not select the server URL |
|
|
788
|
+
| `fetch('/api')` at component-body top level | `createMemo(async () => { ... fetch ... })` under `<Loading>` — a top-level fetch runs once at mount and is not a reactive source |
|
|
789
|
+
| `<input type="checkbox" value={on()} />` | `checked={on()}` for the toggle; `value="string"` only when grouping radios or listing submitted values |
|
|
790
|
+
| `createMemo(() => { void save(); return x(); })` / an effect that calls an action | invoke actions from handlers, not from memos or effects |
|
|
791
|
+
| `createSignal(props.value)` / `createStore(props.items)` | `createSignal(() => props.value)` / `createStore(() => props.items, fallback)` — a bare `props.x` at setup is a snapshot |
|
|
792
|
+
| `createRenderEffect` / `createTrackedEffect` as the default effect | two-phase `createEffect(compute, apply)` |
|
|
756
793
|
|
|
757
794
|
```tsx
|
|
758
795
|
// WRONG — post-await read never subscribes; production can hang pending
|
|
@@ -832,6 +869,16 @@ render(<App />, document.getElementById('app')!);
|
|
|
832
869
|
render(() => <App />, document.getElementById('app')!);
|
|
833
870
|
```
|
|
834
871
|
|
|
872
|
+
```tsx
|
|
873
|
+
// WRONG — user() runs before isPending, so the helper never sees the accessor
|
|
874
|
+
<article aria-busy={isPending(user())}>{user().name}</article>
|
|
875
|
+
|
|
876
|
+
// CORRECT — pass the accessor (or () => user()); <Loading> still owns first flight
|
|
877
|
+
<Loading fallback={<p>Loading…</p>}>
|
|
878
|
+
<article aria-busy={isPending(user) ? 'true' : 'false'}>{user().name}</article>
|
|
879
|
+
</Loading>
|
|
880
|
+
```
|
|
881
|
+
|
|
835
882
|
```tsx
|
|
836
883
|
// WRONG — tab() is read once at setup; View never changes
|
|
837
884
|
const View = tab() ? Detailed : Compact;
|
|
@@ -872,8 +919,10 @@ always-applied rules installed alongside this skill.
|
|
|
872
919
|
- [ ] Effects are two-phase and only at imperative boundaries; apply does not read stores.
|
|
873
920
|
- [ ] Async reads sit under `<Loading>` (the data slot, not chrome); errors under `<Errored>`.
|
|
874
921
|
Reactive inputs of an async memo are read before the first `await`. No hand-rolled
|
|
875
|
-
`loading`/`error` signals or `=== undefined` readiness branches
|
|
876
|
-
`
|
|
922
|
+
`loading`/`error` signals or `=== undefined` readiness branches. First load is
|
|
923
|
+
`<Loading>`; refetch indicator is `isPending(user)` (the accessor, not `user()`).
|
|
924
|
+
Core `refresh(source)`, router `revalidate(key)`, and `return reload(...)` are
|
|
925
|
+
different APIs — do not mix them.
|
|
877
926
|
- [ ] External collections reconcile into stores (function-form `createStore` /
|
|
878
927
|
`reconcile`), never wholesale draft assignment or `setTodos((t) => t.filter(...))`
|
|
879
928
|
as the identity-preserving update.
|
|
@@ -889,7 +938,9 @@ always-applied rules installed alongside this skill.
|
|
|
889
938
|
`window.location`. Router `action`/`query` come from `@solidjs/router` (POST forms + cache), not
|
|
890
939
|
core `action`/`refresh`. Forms: `<form action={save} method="post">`.
|
|
891
940
|
- [ ] `render(() => <App />, root)` — a function, not `render(<App />)`. `hydrate` when
|
|
892
|
-
HTML already exists. Stream async/lazy trees (`await renderToStream(...)`)
|
|
941
|
+
HTML already exists. Stream async/lazy trees (`await renderToStream(...)`) with
|
|
942
|
+
exactly one consumer (`pipe` / `pipeTo` / `readable`). Snapshot stores with
|
|
943
|
+
`snapshot(store)`, not `JSON.stringify`. Secrets live in `virtual:env/server`.
|
|
893
944
|
- [ ] `jsxImportSource` is `@solidjs/web`; Vite plugin is `@solidjs/vite-plugin`.
|
|
894
945
|
No `@solidjs/start`, `vinxi`, `"use client"`, or Next.js imports.
|
|
895
946
|
- [ ] Single return per component; no early returns on reactive conditions.
|
|
@@ -99,6 +99,7 @@ read these before writing route JSX.
|
|
|
99
99
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-js/lifecycle-actions/on-settled.md
|
|
100
100
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-js/lifecycle-actions/refresh.md
|
|
101
101
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-js/advanced/specialized-reactivity/on-cleanup.md
|
|
102
|
+
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-js/advanced/store-advanced/snapshot.md
|
|
102
103
|
|
|
103
104
|
## Reference: components and JSX (solid-js)
|
|
104
105
|
|
|
@@ -138,9 +139,12 @@ read these before writing route JSX.
|
|
|
138
139
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/jsx-properties/inner-html.md
|
|
139
140
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/server-functions.md
|
|
140
141
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/server-functions/get.md
|
|
142
|
+
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/server-functions/rich-arguments.md
|
|
141
143
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response.md
|
|
142
144
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response/get-request-event.md
|
|
143
145
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response/redirect.md
|
|
146
|
+
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response/reload.md
|
|
147
|
+
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response/respond.md
|
|
144
148
|
|
|
145
149
|
## Reference: @solidjs/router, @solidjs/meta, @solidjs/vite-plugin
|
|
146
150
|
|
|
@@ -148,10 +152,12 @@ read these before writing route JSX.
|
|
|
148
152
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/router-factory.md
|
|
149
153
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/navigation.md
|
|
150
154
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/data.md
|
|
155
|
+
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/history.md
|
|
151
156
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/filesystem.md
|
|
152
157
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/title.md
|
|
153
158
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/meta.md
|
|
154
159
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/link.md
|
|
160
|
+
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/script.md
|
|
155
161
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/head.md
|
|
156
162
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/vite-plugin-solid.md
|
|
157
163
|
- https://v2-rebuild--solid-docs-v2.netlify.app/reference/vite-plugin-solid/options.md
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solid2-agent-kit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "Teach AI coding agents (Cursor, Claude Code) to write correct Solid 2.0 instead of React or Solid 1.x — always-applied rules, an agent skill, and a mechanical pattern gate.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|