Skip to content

WIP: Upgrade to solid 2 beta - #2091

Draft
brenelz wants to merge 72 commits into
mainfrom
upgrade-to-solid-2-beta
Draft

WIP: Upgrade to solid 2 beta#2091
brenelz wants to merge 72 commits into
mainfrom
upgrade-to-solid-2-beta

Conversation

@brenelz

@brenelz brenelz commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

PR Checklist

Seeing what It take to update SolidStart to use Solid 2.0 beta. The examples work even with solid-router

@netlify

netlify Bot commented Mar 4, 2026

Copy link
Copy Markdown

Deploy Preview for solid-start-landing-page ready!

Name Link
🔨 Latest commit 8f9ac94
🔍 Latest deploy log https://app.netlify.com/projects/solid-start-landing-page/deploys/6a6c47c12352d40008938047
😎 Deploy Preview https://deploy-preview-2091--solid-start-landing-page.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@changeset-bot

changeset-bot Bot commented Mar 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8f9ac94

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@solidjs/start Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Mar 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@solidjs/start@2091

commit: f1be731

Comment thread packages/start/src/server/assets/index.ts Outdated
Comment thread packages/start/src/server/assets/render.tsx Outdated
Comment thread packages/start/package.json Outdated
Comment thread packages/start/src/fns/handler.ts Outdated
Comment thread packages/start/src/shared/dev-overlay/DevOverlayDialog.tsx Outdated
Comment thread packages/start/package.json Outdated
@lxsmnsyc

Copy link
Copy Markdown
Member

We can probably use ^2.0.0-beta.3 based on my tests

@lxsmnsyc

Copy link
Copy Markdown
Member

needs rebasing

ryansolid and others added 8 commits July 20, 2026 14:36
… 3.0.0-next.14

The published releases now carry the flight bridge and the router's
server-function integration, so the TEMPORARY workspace links to the
sibling solid/solid-router checkouts are removed in favor of pinned
versions.

Co-authored-by: Cursor <cursoragent@cursor.com>
…rror test

Solid pins move to beta.22 across packages/start, apps/tests, and the
fixtures; the minimumReleaseAge exclusions follow (babel-plugin-jsx to
0.50.0-next.25 via babel-preset-solid). vite-plugin-solid stays at
3.0.0-next.14. The runtime now percent-encodes non-latin1 messages on
the X-Server-Function-Error header behind the =?1? marker with a
symmetric client decoder, so the unicode error propagation test is
unskipped and tightened to assert the message round-trips exactly.

Co-authored-by: Cursor <cursoragent@cursor.com>
…tory

The router 1.0 removes the component API, so @solidjs/start/router now
exports the file-system route tree as a value — fileRoutes — instead of
the FileRoutes component; apps pass it to createRouter({ routes }) and
render the returned instance with their root layout as the render-prop
child. Routes are immutable per instance, so one shared tree replaces
the per-request build: PageEvent.routes is gone and the single-flight
collector consumes fileRoutes directly. The tests app and fixtures move
to the factory (rootPreload becomes the factory's preload option,
<Navigate> becomes useNavigate during setup, <A> becomes plain anchors
with active styling on the router's [data-active] attribute).

Known blocker: client hydration fails against this router build because
setupLinkClaims creates a client-only createRenderEffect under the
id-carrying router owner, consuming a hydration child id the server
never allocates — every subsequent id shifts and lazy route modules are
not preloaded before hydration. 19 of 26 e2e tests fail on it; needs an
upstream router fix (the claims effect must not consume hydration ids).

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>


Vite's bundledDev serves only bundled output filenames and provides no
API to map a module id to its served URL, so Start's streamed-SSR
preload/hydration hints emit URLs that 404 and hydration of code-split
routes fails. Warn at dev-server startup instead of degrading silently,
and drop the e2e:bundled-dev leg from test:all until Vite ships the
module-URL API (vitejs/vite#22991). The
script and its playwright config are kept for when it does.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@lxsmnsyc lxsmnsyc mentioned this pull request Jul 25, 2026
2 tasks
ryansolid and others added 20 commits July 27, 2026 08:55
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Solid pins move to beta.28 and @solidjs/router to 2.0.0-next.12 (the
solid-2 next line, renumbered after 1.0.0 shipped stable for solid 1)
across packages/start, the fixtures, and apps/tests, plus the
minimumReleaseAge exclusions.

Router next.12 handed the router-agnostic no-JS form convention back to
core: createNoJSHandler now comes from
@solidjs/web/server-functions/server instead of @solidjs/router/server,
so the server-function handler's import moves accordingly (same options
and handler shape; createFlightDataCollector stays the router's).

Verification: packages build, unit 5/5, e2e 26 passed / 3 skipped.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Brings the v2 rc line (through 2.0.0-rc.6) into the Solid 2 branch.

Ported main features onto the Solid 2 architecture:
- Server-function error handler (#2262): wired through
  handleServerFunctionRequest's transformResult hook and the
  solid-start:server-fn-error-handler virtual module.
- Custom seroval plugins (#2250): serialization.plugins now feeds the
  @solidjs/web codec on both the client transport and the server handler.
- ~ alias fix for workspace packages (#2239): adopted appRootAlias in
  place of the global resolve.alias entry.
- Dev toolbar (#2049, replaces the dev overlay): ported to Solid 2
  (Errored/Loading, onSettled, two-arg createEffect, draft-mutation
  stores, terracotta 2 subpath imports, @solidjs/web Portal/Dynamic/JSX,
  createMemo(async) instead of createResource). Protocol-specific pieces
  (SerovalChunkReader, body-format markers) moved local to the toolbar.
- Deferred stream response via h3 iterable() (#2231), StartHandler type
  (#2234), routerLoad restore (#2228), nonce on streaming redirect
  script (#2252), ResizeObserver-loop filter (#2240) all kept.

Kept deleted (superseded by vite-plugin-solid / @solidjs/web /
@solidjs/router server-function integration): fs-routes tree-shake,
manifest.ts, directives, fns serialization/registration/shared and their
specs. Fixes main landed in those files (#2249, #2238, #2243, #2245,
#2241, #2261) live upstream now and are tracked for porting there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The inspector came over from #2049 built against Start's old fetch
transport; on this branch the transport lives in @solidjs/web, so the
toolbar's tracker never received a call and its viewers decoded a wire
format that no longer exists.

- fns/client.ts: in dev, hook the transport's prepareRequest to push
  each outgoing call (function id + the transport's per-call instance
  header) and responseHandler.handle to push each response, passing both
  through untouched.
- fns/handler.ts: in dev, echo X-Server-Function-Instance onto the
  response — the transport doesn't, and it is what lets the inspector
  pair a response with its request.
- body-format.ts: track @solidjs/web's markers (X-Server-Function-Format,
  the Json format, the new file form key) instead of the old X-Start-Type
  protocol; the Serialized chunk framing is unchanged, so SerovalViewer
  keeps working as-is.
- functions/index.tsx: add a JsonViewer for plain-JSON bodies (JSON-safe
  argument lists go over the wire as raw JSON).
- dev-toolbar/index.tsx: keep captured instances in a signal-backed plain
  record instead of a store — Request/Response methods throw "Illegal
  invocation" when reached through Solid 2's store proxy.

Calls made during SSR/hydration are served from the SSR payload without
a client fetch, so only real client-initiated calls appear — which is
what the inspector is for.

Verified against the tests app dev server: onclick and onSettled calls
both appear in the panel, request tab shows URL/headers/FormData body,
response tab decodes the Serialized stream with status and timing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Query/action calls made through @solidjs/router never reached the
inspector, for two stacked reasons:

1. Split transport state. The router is served as source in dev and its
   import of @solidjs/web/server-functions fell through to a raw /@fs
   URL, while app code got the optimized /client bundle — two module
   instances, each with its own transport config. Everything set through
   configureServerFunctionsClient (endpoint, codec plugins, the dev
   hooks) silently never applied to router-initiated calls. Pre-bundle
   both specifiers in one optimizer pass so they share one instance
   (they now re-export a single common chunk), mirroring what
   vite-plugin-solid already does when server components are enabled.

2. Reactive write in an owned scope. The toolbar's tracker listener
   wrote a signal synchronously from inside the transport call path,
   which for router queries runs inside a preload computation — Solid 2
   rejects the write (REACTIVE_WRITE_IN_OWNED_SCOPE) and the throw broke
   the navigation itself. Defer the write with queueMicrotask, and make
   the tracker swallow listener errors so a dev tool can never take down
   the call it is observing.

Verified on the hackernews fixture: client-side navigation now shows the
GET query call in the panel, paired with its response (real URL with
encoded args, timing, and the decoded story array), and navigation
renders normally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@solidjs/web's transport sends argument lists as plain JSON and throws
for anything JSON can't carry, deferring to an `enableRichArguments()`
entry that beta.26 does not actually ship. That made every server
function taking a rich argument unusable — including router form
actions, where `action.with(id)` puts a bound value next to FormData:
the notes fixture could not save a note at all, and the throw landed in
swallowed submission state, so the form silently did nothing.

Supply the missing encoder via the transport's `serializeArgs` seam,
using @solidjs/web's own serializeJSON with the app's configured plugins
and the same `;0x{len};` chunk framing the server handler already
decodes as BodyFormat.Serialized. Nothing changes for JSON-safe
arguments — those still go as plain JSON, no codec in the bundle.

Also migrate the server-function test routes still using Solid 1's
single-argument createEffect, which throws MISSING_EFFECT_FN on Solid 2:
they were silently never running, quietly disabling their assertions —
including the active custom-class e2e test that covers
`serialization.plugins`.

Verified on the notes fixture: create, edit, and redirect all work and
persist across reload. Verified on the tests app: custom-class now
round-trips a custom class in both directions (matching the e2e
expectation exactly), and the plugin, file, iterator, form-data, blob
and ping routes all pass.

Known gap: server-function-readable-stream still fails, for an unrelated
upstream reason — @solidjs/web pins seroval and seroval-plugins at
`~1.5.4`, which resolves seroval-plugins 1.5.5 whose ReadableStreamPlugin
calls `ctx.addCleanup`, an API added in seroval core 1.5.6. Needs an
upstream range fix or a local override.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Picks up the solid 2.0.0-beta.28 / router 2.0.0-next.12 /
vite-plugin-solid 3.0.0-next.18 bumps pushed to the branch while the
main merge and server-function fixes were in progress.

Conflicts were the dependency pins and fns/handler.ts:
- Solid-ecosystem versions take the remote's newer bumps; the tooling
  pins (vite 8.1.5, vitest 4.1.10, seroval 1.5.6, shiki 4, jest-dom
  6.10) keep the values that came in from main, which the remote
  commits predate.
- fns/handler.ts: `createNoJSHandler` moved from @solidjs/router/server
  into core as of router 2.0.0-next.12; combined that relocation with
  the dev-only INSTANCE_HEADER echo added for the toolbar inspector.

Re-verified against beta.28 rather than assuming the bump is inert:
`enableRichArguments()` is still not shipped (only referenced in an
error message), so the local serializeArgs encoder is still required;
INSTANCE_HEADER is still exported. Notes fixture saves and persists,
the custom-class route still round-trips through serialization.plugins
byte-for-byte, and the toolbar inspector still captures calls.

@solidjs/web still pins seroval/seroval-plugins at `~1.5.4` in beta.28,
so the ReadableStream argument failure is unchanged and still needs an
upstream range fix or a local override.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v2 rc merge brought main's newer test routes and a newer oxfmt onto
this branch without adapting either, breaking the Tests and Format jobs.

Build (Tests job):
- http-header / single-flight-cookie used `createAsync`, dropped in
  router 2.0.0-next.12. Switched to `createMemo(fn, { deferStream: true })`,
  the pattern the css fixture already uses here.
- no-js-action used `useSubmission` (now `useSubmissions`, plural, which
  returns every submission) and `json()`/`redirect()`/`reload()` from the
  router — the response helpers are core in v2 and `json()` is `respond()`.

Format: main bumped oxfmt 0.36 -> 0.60, whose rules differ, so files that
only live on this branch had never been formatted under it. Ran the
formatter; the diff outside the files above is purely cosmetic.

serverFunctions.onError: the merge wired this to the core's
`transformResult`, but that seam only sees thrown Responses and
envelopes — a plain Error, the case the option exists for, never reaches
it, so the test asserting the substituted error failed. Moved the handler
around the function call itself (the `provideEvent` seam), which covers
every thrown value exactly once.

Set-Cookie on redirect: dropped `produceResponseWithEventHeaders`, which
the merge restored from this branch. Main removed it deliberately —
the framework merges event response headers itself now — and with it
present the event cookie was appended twice, failing main's RFC 6265 test.

Skipped, with reasons, two suites that the merge silently enabled and
that cannot pass here yet: `http-header` (already skipped on this branch
before the merge for the same cause) and `no-js-action`. Both fail for
one root cause — Solid 2 streams route content into a `<template>` the
hydration script swaps in, so it lands after the response headers flush
and never appears at all without JavaScript. The server half of the no-JS
flow does work: the action 303s back with the outcome in the flash
cookie and SSR renders it, verified by hand.

Verified locally: format, typecheck, both builds, and e2e (29 passed,
8 skipped). Three `.server.test.ts` cases that inspect `.output/public`
fail on this machine because nitro's public step replaces that directory
after the client build; they are untouched by this branch and passed in
CI as recently as 2026-07-20, so pushing to confirm against CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deletes leftovers from capabilities hoisted into Solid core /
vite-plugin-solid: the unused debounce util, the unconsumed
StartClientTanstack export, islands vestiges (START_ISLANDS branch,
$islands, experimental.islands option, createClientReference),
commented-out FETCH_EVENT and redirect-status corpses, and a stale
comment pointing at the deleted config/lazy.ts. Drops dependencies
with zero imports (@babel/*, micromatch, oxc-parser, path-to-regexp
and their @types) and moves @solidjs/meta out of the package since
only fixtures use it.

Co-authored-by: Cursor <cursoragent@cursor.com>
…d by vite-plugin-solid

The plugin serves the bridge endpoint itself (/@vite-plugin-solid/dev-manifest)
and its virtual:solid-manifest dev module transparently falls back to fetching
it from isolated SSR runners (nitro's dev worker), so the handler now uses the
manifest as-is — resolveDevAssets and the HTTP plumbing are gone. The
server-only/client-only marker modules are always-on inside solid(), and their
ambient declarations ship at vite-plugin-solid/boundary-modules (referenced
from env.d.ts). A slim spec keeps the #2068 guard that Start's server-only
entry points carry the marker.

NOTE: requires a vite-plugin-solid newer than 3.0.0-next.18 — bump the exact
pin to the release carrying the hoisted pieces before merging.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
vite-plugin-solid's `ssr: true` registers client-build-first buildApp
ordering hooks whose pre-order hook built the client environment before
nitro's own pre-order hook wiped `.output/`, leaving production builds
with no client assets and a manifest-less server bundle that 500s.
Start already orders client-before-server in `builder.buildApp`, so
filter the redundant hooks out of the solid() plugin set.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…te-plugin-solid

vite-plugin-solid 3.0.0-next.20 moves the client-build-first buildApp
hook from pre to normal order and makes its /complete companion defer
to nitro's post-order orchestrator, so the hooks no longer race
nitro:prepare's output wipe. The filter workaround from 6839776 is no
longer needed; the changeset now credits the upstream fix.

Co-authored-by: Cursor <cursoragent@cursor.com>
…0-next.21

Co-authored-by: Cursor <cursoragent@cursor.com>
clientOnly re-exports @solidjs/web's implementation (import-once dedupe,
{ lazy } option); HttpStatusCode/HttpHeader become thin deprecated
component wrappers over the new httpStatus/httpHeader primitives;
getServerFunctionMeta delegates to getServerFunctionInvocation now that
invocation state moved off event.locals into core-private state; the
streaming handler consumes renderToStream's .readable view instead of
the hand-rolled web-stream adapter; and the handler marks
response.committed once the head can no longer change so the
primitives' cleanup-time retractions no-op past that point.

Co-authored-by: Cursor <cursoragent@cursor.com>
next.13's RequestEvent augmentation still declares the response stub
without the `committed` flag, so the handler's cast stays (comment
updated to reflect the version checked).

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants