agentsclimarketplace

Solidjs v2 reviewer

Skill khmm12/solidjs-v2-skills/skills/solidjs-v2-reviewer

Claude Code skills for SolidJS 2.0 — write, migrate, and review solid-js 2.x code without React/1.x reflexes

Install
npx -y skills add khmm12/solidjs-v2-skills --skill solidjs-v2-reviewer

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Review SolidJS 2.0 code for React-isms, Solid 1.x-isms, and reactivity bugs. Use when reviewing diffs, PRs, or files in a project that depends on solid-js 2.x / @solidjs/web — including self-review after generating Solid 2.0 code.

SKILL.md

9.6 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it

Review Solid 2.0 code

Hunt the two prior-knowledge bug classes — React reflexes and Solid 1.x reflexes — plus 2.0-specific reactivity mistakes. Severity guide: 🔴 broken behavior, 🟡 dev-mode diagnostic / lost reactivity, 🔵 style drift.

Confirm the project is actually v2 first (solid-js major 2 in package.json / @solidjs/web in deps). Reviewing a 1.x project against this list produces garbage findings.

Pass 1 — greppable smells

Run these over the changed files; each hit needs a fix or a justification.

Solid 1.x-isms

GrepVerdictFix
from ['"]solid-js/(web|store|h|html|universal)🔴 module not found@solidjs/web, store APIs from solid-js, @solidjs/h
createResource|useTransition|startTransition🔴 removedasync memo + <Loading>; built-in transitions / isPending
\bbatch\s*\(🔴 removeddelete wrapper; flush() only for sync read-after-write
createComputed|createMutable|modifyMutable|createDeferred🔴 removedmemo / split effect / createSignal(fn); createStore drafts
\bon\s*\( as effect dep helper, onMount|onError|catchError🔴 removedsplit effect compute; onSettled; <Errored> / effect error
<Suspense|<SuspenseList|<ErrorBoundary|<Index\b🔴 removed<Loading> / <Reveal> / <Errored> / <For keyed={false}>
mergeProps|splitProps|unwrap\s*\(|createSelector🔴 removedmerge / omit / snapshot / createProjection
\.Provider\b🔴 removed<Ctx value={...}> — context is the provider
classList=🔴 removedclass={{...}} / class={[...]}
use:[a-zA-Z]|attr:|bool:|on:[a-z]|oncapture: in JSX🔴 removedref factories; standard attributes; onClick + ref for native opts
produce\s*\( in setters🟡 redundantdrafts are the default
setStore\s*\(\s*["'] (path-style first arg)🔴 wrong APIdraft setter or storePath(...)
/\*@once\*/🟡 ignored markerreactive read / defaultValue / untrack
\.loading\b|\.error\b on async values🔴 no such props<Loading>/isPending(() => x()) for loading (bare refresh() is silent — pair with affects() for a loud reload) / <Errored> for error

React-isms

Grep / patternVerdictFix
function \w+\(\s*\{ (destructured props)🟡 reactivity dead + warnsprops.x access
useState|useEffect|useMemo|useRef|useCallback🔴 wrong frameworkSolid primitives
<X value={count} /> passing an accessor where a value is expected🔴 child gets a functionvalue={count()} — collapse at the JSX boundary
key= prop on list items🟡 no-op<For keyed={...}> modes
className|`${...}`/.join(" ") class building🔵 reflexclass array/object form
deps-array thinking: effect re-created per "render"🟡 model errorcomponents run once; compute phase = deps

2.0-specific

PatternVerdictFix
Single-callback createEffect(fn)🔴 throwssplit (compute, apply)
createEffect(fn, 0) / createMemo(fn, 0) initial values🔴 wrong argoptions object; prev default parameter
Setter then immediate read of same signal/DOM🔴 stale readflush() or restructure
Signal/store write inside memo/compute/component body🔴 throws in devderive, or move write to handler/action
actionFn() invoked inside memo/compute/component body🔴 dev error (ACTION_CALLED_IN_OWNED_SCOPE, beta.17); may livelock in prodinvoke from handler/effect callback/onSettled
ownedWrite: true on app state🟡 escape-hatch abusederive instead; ownedWrite is for internal flags
Top-level const x = props.x / store read in component body🟡 warns, staleread in JSX/memo; untrack if deliberate
onCleanup inside onSettled/createTrackedEffect🔴 throwsreturn cleanup
Cleanup returned from onSettled fired out of band (event handler/tracked effect/nested onSettled)🔴 dev error (beta.16), dropped in prodcall the setup helper from the component body (owned scope)
Primitives created inside onSettled/tracked effect🔴 throwscreate in component body
Store proxy passed compute→apply, read in apply🟡 warns, won't re-runextract plain values / deep(store) in compute
Async read with no <Loading> ancestor🟡 root mount deferredadd boundary where fallback UI is wanted
async function* memo over a socket/emitter/observable with no up-front onCleanup🔴 leaks on dispose/re-runonCleanup (before the first await/yield) that cancels the source; try/finally/.return() can't unwind a parked generator
refresh() called inside a computation🔴 throwscall from handlers/actions
serverFn.GET property access, serverFn.withOptions( on a server function reference🔴 removedGET(fn) wrapper at declaration site; withMeta(fn, meta) for metadata; prepareRequest for session-dynamic transport (see solidjs-v2 skill, references/server-functions.md)
isRefreshing( call (or imported from solid-js)🔴 removed in beta.15gone from solid-js exports; detect a refresh re-run by key comparison, or use isPending/<Loading>
<For> callback shape vs keying mode mismatch (item() on keyed, i() on keyed={false})🔴 type/runtime errorcheck the mode table
Dynamic boolean keyed={cond()} with function children🟡 ambiguous shapeliteral mode or key function
useX-with-throw context wrapper hooks🔵 dead boilerplatedirect useContext (throws by itself)
camelCase DOM attributes (tabIndex, readOnly)🟡 wrong attributelowercase; handlers stay camelCase
merge(..., maybeUndefined) assuming skip semantics🔴 silently overridesfilter keys or restructure defaults

Pass 2 — judgement checks (not greppable)

  • Derive vs write-back: any effect whose apply phase sets reactive state is suspect — usually a memo/projection in disguise.
  • Boundary ownership: isPending reads placed under the Loading boundary that owns the data read? Pending indicators outside can never fire.
  • Mutation shape: server writes wrapped in action() with optimistic state and a final refresh()? Ad-hoc async handlers flipping flags are the 1.x smell in new clothes.
  • Action call site: an action may be defined in a component, but is it invoked only from an imperative scope? A component-body/computation call is a transaction-starting write and throws in dev mode (since beta.17).
  • Optimistic spinner off isPending: a "Saving…" indicator driven by isPending on data the same action just wrote optimistically can never show — not because the optimistic write masks it (that mask is removed as of beta.21; optimistic writes are verdict-inert), but because a bare refresh() after the write is a silent same-question re-ask and was never going to flip isPending. The flag belongs in the data (co-written pending: true or a separate createOptimistic(false)); if the reload itself should read pending, that needs an explicit affects(target) before the refresh().
  • Stale beta.17–20 mask assumptions: code (or comments) that reason about an optimistic write "masking" isPending store-wide, or that treat a bare refresh() as if it were pending on its own — both were beta.17–beta.20 behavior, removed/superseded in beta.21 (question-scoped-pending-affects). On beta.21+ typings this silently changes UI (a spinner that used to show now doesn't, or vice versa) with no compiler error to catch it — flag any isPending use next to an optimistic write and check it against the current rule, not habit.
  • Granularity: selection/derived caches notifying whole collections → createProjection. Fixed-slot lists diffed with <For><Repeat>.
  • Ownership: module-scope effects/roots intentional? Detached lifetime must be explicit (runWithOwner(null, ...)).
  • Composable naming: a createX/useX prefix should match lifecycle, not React habit — createX makes a fresh instance owned by the caller, useX is a shared singleton or accesses an already-created thing (useContext). useX is not wrong by itself (singletons are legit); flag only a per-call instance named useX, or every composable defaulting to useX out of reflex.
  • Layout lane: DOM-geometry reads (getBoundingClientRect/offset*) belong in a createRenderEffect (render lane), not in a ref callback (node may be pre-insert/pre-layout there). Beware the inverse "fix" too: moving a layout measure out of createRenderEffect into createEffect/a ref on the false theory that render effects read a disconnected node — they don't; the trigger is flush-scheduled and runs after insertion.
  • Tests: flush() after writes; createRoot wrappers; resolve() for async settling.

Reporting

Report findings ordered by severity with file:line, the broken expectation (one line), and the concrete 2.0 fix. Note clean areas that were checked. For deep API verification during review, the solidjs-v2 skill's references cover signatures; installed typings in node_modules are final word — the betas churn the public API freely (e.g. isRefreshing was a public solid-js export from beta.0 through beta.14, then removed in beta.15).

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.