Skip to content

Releases: solidjs/solid-vite-plugin

@solidjs/vite-plugin@3.0.0-next.34

Pre-release

Choose a tag to compare

@github-actions github-actions released this 26 Aug 07:59
447bbd9

Minor Changes

  • da12802: New diagnostics option (dev serve only): injects a client module that installs the in-page bridge from the app's own @solidjs/diagnostics and serves a /__solid/diagnostics endpoint on the dev server. Out-of-process consumers (agents, tests, curl) drive capture sessions (begin/end), whyDidRun, and cost queries over the Vite WebSocket. Works for plain index.html apps (transformIndexHtml injection) and start mode (generated/custom client entry injection). @solidjs/diagnostics is a type-only dependency of the plugin; the runtime bridge always comes from the app's installed copy.
  • da12802: Move to the renamed Solid 2.0 compiler packages: the native JSX/directives/lazy/refresh compiler is now @solidjs/compiler (was @dom-expressions/compiler) and the Babel escape hatch is @solidjs/babel-plugin (was babel-preset-solid — now a plugin rather than a preset, hosted in plugins with the same pass order: user plugins run before it, user presets after). Both backends now bake in the Solid defaults (moduleName: "@solidjs/web", the control-flow builtIns, contextToCustomElements, wrapConditionals), so the plugin only passes the posture it actually decides (generate/hydratable/dev/serverComponents) plus user solid options, which override the built-in defaults exactly as before.

Patch Changes

  • f0412e6: The server-only/client-only boundary guard no longer crashes when
    this.environment isn't available on resolve hooks, and now detects the
    target environment through the same getEnvironmentConsumer helper used
    by the rest of the plugin, falling back to the resolve hook's ssr flag
    when the environment is absent.

@solidjs/vite-plugin@3.0.0-next.33

Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Aug 18:07
6f926db

Patch Changes

  • c3d465a: serverFunctions.components now enables the SSR behavior-claims transform: ref and event-handler positions on intrinsic elements compile to guarded claim holes instead of dropping, so client behavior declared in server component markup survives serialization and morphs. SSR-only by construction (the dom generate ignores the flag); apps without the flag compile byte-for-byte as before. Compiler floor moves to @dom-expressions/compiler@0.50.0-next.44, which ships the serverComponents transform option and its types.

@solidjs/vite-plugin@3.0.0-next.32

Pre-release

Choose a tag to compare

@github-actions github-actions released this 23 Aug 00:54
7d21e27

Patch Changes

  • 2436bf5: Wire the Start development error boundary into server rendering and support direct @solidjs/start-devtools imports in apps with custom server and client entries.
  • 473afd2: Start devtools are no longer enabled when @solidjs/start-devtools is not installed. Detection falls back to resolving the package from the plugin's own file (for pnpm-isolated installs where it is only a dependency of the plugin), but the package is declared an optional peer dependency of the plugin, so when it is absent Vite answers that resolution with its __vite-optional-peer-dep: stub instead of null. The plugin took the stub as a successful resolution, wrapped the generated client entry in DevToolbar, and the browser failed with The requested module '/@id/__vite-optional-peer-dep:@solidjs/start-devtools:@solidjs/vite-plugin' does not provide an export named 'DevToolbar'. The stub is now treated as "not installed".
  • 791ae3e: Require Vite 8 or newer and remove the Vite 6 and 7 compatibility paths.

@solidjs/vite-plugin@3.0.0-next.31

Pre-release

Choose a tag to compare

@github-actions github-actions released this 19 Aug 22:47
639df4c

Minor Changes

  • 8459560: New start.devtools option: a development toolbar with runtime errors and a
    server function inspector, backed by the new optional-peer package
    @solidjs/start-devtools. By default the toolbar turns on in vite dev
    whenever the package resolves (install it as a dev dependency) and stays off
    otherwise; start: { devtools: true } makes the package required (a missing
    install becomes an error) and start: { devtools: false } opts out entirely.
    Generated client entries wrap the app in the toolbar's DevToolbar component,
    authored client entries get an injected mount import instead, and either way
    the wiring is dev-serve-only codegen — production builds and previews contain
    none of it. The package itself is resolved from the app graph first and from
    the plugin's own location as a fallback, and the virtual toolbar modules
    delegate their imports to that captured resolution, so pnpm-isolated installs
    work without the package being hoisted to the app root.

Patch Changes

  • c8615ed: Apply the request CSP nonce to start mode's generated client-entry script.
  • 7c10d3b: Add a generic production error boundary to generated Start entries. It returns a 500 response for uncaught SSR errors and provides a fallback for uncaught client errors. Set start.errorBoundary to false when application middleware owns error handling.
  • e607db7: Add start.css.filter to control which module graphs are traversed while collecting development CSS. exclude prunes matching graphs (defaults to /node_modules/; providing one replaces the default), and include opts matching files in on top of that baseline — e.g. { include: /node_modules\/some-ui-lib/ } server-inlines that dependency's CSS in dev. A file matching both patterns stays excluded.
  • 450d0e5: Add Vite 9 forward compatibility by using the per-environment consumer in plugin hooks and accepting Vite 9 as a peer.

@solidjs/vite-plugin@3.0.0-next.30

Pre-release

Choose a tag to compare

@github-actions github-actions released this 18 Aug 22:50
63fe297

Patch Changes

  • fbb5239: Unlock the @dom-expressions/compiler dependency from the exact 0.50.0-next.40 pin to the ^0.50.0-next.43 range — floors at next.43, auto-graduates to later next.N and stable 0.50.0, matching how babel-preset-solid is ranged. Absorbs the .41–.43 compiler fixes: dedicated <!> insertion markers for components boxed by static text (solidjs/solid#3004, content no longer lands after the trailing text), $key on intrinsic server JSX compiling to a _key attribute so the frame morph matches keyed elements by key instead of position, transformLazy annotating the module-URL placeholder in lazy()'s third argument for solid-js 2.0's { export } options bag (solidjs/solid#3011), HTML-escaping of static template-literal parts in attribute/style values, innerHTML/textContent holes no longer taking the _$scope id reservation (solidjs/solid#3015), and the Rust 1.95 / Oxc 0.144 toolchain upgrade with the WASI linking fix.
  • 5543386: Fixes the dev server crashing on every request under https/HTTP/2. Vite serves server.https through an HTTP/2 server (with HTTP/1 fallback), and the plugin's Node→web request bridge copied the h2 pseudo-headers (:path, :method, :authority, :scheme) into Headers, which throws TypeError: ":path" is an invalid header name — so start-mode SSR and server functions 500'd on every request over vite dev with https. Pseudo-headers are now skipped and the host derives from Host or the h2 :authority. Alongside it, three more bridge hardenings (techniques referenced from srvx's Node adapter, h3js/srvx): request.url now says https: on TLS sockets instead of always http: (secure-cookie logic, absolute redirects, and origin checks in app code saw the wrong protocol), client disconnects now fire the request's AbortSignal so handlers can cancel streamed renders and in-flight work, and HEAD requests end immediately with the response body cancelled instead of pumping the whole (possibly endless) stream into Node's discarded HEAD writes.

@solidjs/vite-plugin@3.0.0-next.29

Pre-release

Choose a tag to compare

@github-actions github-actions released this 18 Aug 07:25
77c1358

Minor Changes

  • 24747b7: options.event: the public wrapper→event extension seam on the generated handlers. Fields passed as handleRequest(request, { event }) (and handleServerFunctionRequest(request, { event }) on the standalone server-function handler, threaded through its createEvent option) spread into the request event at creation, so hosts and custom server entries can extend what getRequestEvent() answers with — no new convention beyond createRequestEvent's own init parameter. The conventional field is nativeEvent, the platform's raw request object: the plugin's own dev, preview, and server-function dev middlewares now pass event: { nativeEvent: req } (the Node IncomingMessage), so getRequestEvent().nativeEvent reads the same under vite dev/vite preview as behind a production Node entry that passes it. The event shape itself is unchanged ({ request, locals, response } plus whatever the wrapper spreads in); nothing is attached to the Request, and no client-address helper is added — on bare Node read event.nativeEvent.socket.remoteAddress, behind a trusted proxy read the forwarding headers off event.request.

Patch Changes

  • 40c6865: Housekeeping: add the MIT LICENSE file the license field has always declared but the repo never carried (#219), and document virtual:solid-manifest in the README — what it exports in dev (the live asset resolver) versus SSR builds (the baked client manifest with _base), and its role as the seam for frameworks doing their own asset gating (e.g. the TanStack Start integration).
  • ca4d221: start.env: the generated virtual:env/server module no longer contains
    top-level await, removing the esnext-target deploy requirement. Boot
    validation used to conditionally await each validator result (Standard
    Schema allows validate() to return a Promise), which put a TLA in the
    server env chunk whenever the schema had server keys — and any
    downstream bundler with a non-esnext target refuses a TLA chunk outright
    (Nitro's node-server preset is the one that bites in practice), forcing
    deployments to override the build target to esnext. Boot validation is
    now fully synchronous with identical semantics: same process.env read at
    module init, same per-key report, same fail-loud-at-boot before any
    importer's body runs, same frozen env export — and synchronous init is
    the only shape that can keep the "validated before first use" guarantee,
    since user server modules read env.KEY at their own top level (deferring
    the await to request entry cannot cover module-init consumers). The
    tradeoff is explicit: async validators (e.g. z.string().refine(async ...)) are no longer supported for server keys — they are rejected at
    config/build time with the fix in the message (they could only ever have
    failed at deploy boot otherwise), and boot backstops with the same report
    for schemas whose async-ness only surfaces on real values. client keys
    keep async support: their values are baked at build time, where the plugin
    awaits. The start-env suite now asserts every built server chunk
    transforms under esbuild target es2020 (which rejects TLA at parse time —
    exactly the check a downstream bundler applies) so this cannot regress.
  • 3257a97: Vite 8's dependency scan no longer breaks on .tsx files (issue #262).
    The plugin previously set optimizeDeps.rolldownOptions.transform.jsx: 'preserve' to stop Rolldown from injecting react/jsx-dev-runtime
    imports during the scan — but the scanner re-parses the transformed
    output as plain JS (import.meta.glob handling force-tags modules as
    js, and even without glob the oxc-preserved JSX is re-parsed without
    JSX enabled), so any .tsx with JSX was a hard PARSE_ERROR: Unexpected JSX expression that aborted the whole scan. Every dependency was then
    missed by pre-bundling and discovered at runtime instead ("new
    dependencies optimized" mid-session re-optimize/reload — the classic
    symptom for deps only reachable through import.meta.glob). The scan
    transform now uses the classic JSX runtime, which lowers JSX to bare
    React.createElement calls without injecting any import: the scan
    output is never executed, it only exists so rolldown can walk the import
    graph, so the undefined identifier is harmless. With this, the scan
    completes and glob-only dependencies are pre-bundled up front.

vite-plugin-solid@3.0.0-next.26

Pre-release

Choose a tag to compare

@github-actions github-actions released this 12 Aug 08:43
8c1f62a

Patch Changes

  • 6dbb7e8: Start mode's generated request handler now default-exports a Fetchable
    { fetch(request) } object alongside its named handleRequest export.
    Deployment integrations that follow the web-standard Fetchable convention
    can consume the virtual handler or built server entry without a wrapper.
    The fetch method intentionally ignores provider arguments after the request
    instead of forwarding them as Solid handler options.

    The normal ssr environment now exposes that handler as its index service
    entry. Provider Vite plugins can adopt the same environment for development
    and production without start.external, a custom source entry, or explicit
    Rollup input. Standalone builds continue to emit dist/server/server.js.

  • 06aadc6: Don't default test.environment to jsdom for vitest browser-mode projects (test.browser.enabled): vitest 4 probes for the environment's package at startup and sets a failing exit code when jsdom isn't installed, even though the suite runs (and passes) in the real browser. Browser-mode projects now fall back to vitest's own node default, which has no package probe — mirroring the existing jest-dom gate. Non-browser projects keep the jsdom default.

@solidjs/vite-plugin@3.0.0-next.28

Pre-release

Choose a tag to compare

@github-actions github-actions released this 12 Aug 22:55
79f622e

Patch Changes

  • fc5050f: update to solid 2.0.0-rc.0 — the solid-js/@solidjs/web peer ranges and the babel-preset-solid dependency move from >=2.0.0-beta.32 <2.0.0-experimental.0 to ^2.0.0-rc.0, admitting the rc line (which the old experimental-capped upper bound excluded, since experimental sorts before rc), still flooring above the hazardous 2.0.0-experimental.* publishes, and auto-graduating to stable 2.x

vite-plugin-solid@3.0.0-next.25

Pre-release

Choose a tag to compare

@github-actions github-actions released this 11 Aug 21:13
4837100

Patch Changes

  • 11b87a1: Custom extensions work with the native compiler again. The native compiler picks its parser dialect from the file extension, so the transform builds a borrowed-extension filename (foo.mdxfoo.mdx.jsx, or .tsx when the extension is registered as TypeScript) for exactly this case — but only the lazy and refresh passes used it; the JSX transform itself still received the raw id, and @dom-expressions/compiler rejected it with "Unknown file extension" (#297). compiler: 'babel' was unaffected because that path names the parser plugins explicitly. The JSX transform now receives the same borrowed filename as the other native passes.
  • 74fb28b: start.env now rejects server schema keys that carry the public env prefix at config time. Vite bakes every VITE_-prefixed variable (or whatever envPrefix selects) into the browser's import.meta.env regardless of which side of the schema declares it, so server: { VITE_API_SECRET: ... } silently shipped the secret to every client through Vite's own channel — the build-time leak scan does flag server values that land in client chunks as literals, but dev has no scan at all, and short or colliding values can evade the literal match. The prefix rule was previously enforced one-way (client keys must have it); the reverse guard now fails fast with a rename message, mirroring the existing client-side guard.
  • 2e7b63c: sendWebResponse no longer hangs forever when a client disconnects during backpressure. The write loop's 'drain' wait had no other way to settle, but a response whose client already went away never emits 'drain' — so every streamed SSR response aborted mid-stream (closed tab, slow mobile client) parked the promise chain, the body reader, and the Response object permanently, accumulating leaks over a start mode dev/preview session. The backpressure wait now also settles on 'close'/'error' and the loop bails out early once the response is destroyed, letting the existing close handler's reader cancellation finish cleanup.
  • 84a4cab: docs: "turnkey" is now "start mode" across user-facing language — the JSDoc on ssr/start/serverFunctions and on StartOptions/ServerFunctionsOptions, the READMEs, and the config-time error/warning strings all say start mode (SSR start mode / client start mode; the server-function dev middleware is "built-in", since it works without start). The components-without-start warning also names the right option (start, not app). Prose citing Solid 2.0 beta versions is reworded version-neutrally now that 2.0 is past beta — published dependency ranges are untouched.

vite-plugin-solid@3.0.0-next.24

Pre-release

Choose a tag to compare

@github-actions github-actions released this 10 Aug 19:48
519da14

Patch Changes

  • dea55b3: Base-path and lazy-asset URL handling fixed across the turnkey surfaces (#298, #299, #300). Vite's base middleware strips the configured base from req.url before post middlewares run, but the built handler compares the request pathname against the base-prefixed server-function endpoint and hands the URL to application code — so under a non-root base, vite preview never dispatched /base/_server (requests fell through to page rendering) and dev page SSR saw base-stripped URLs that production would never send. The plugin's node adapters (the preview middleware, the turnkey dev SSR middleware, and the server-function dev middleware when the stripped endpoint form matched) now restore the base before constructing the web Request, so the handler sees production-shaped URLs on every surface. The dev asset resolver's lazy module URLs get the same treatment: they were emitted as "/" + key, which Vite rejects outside a non-root base and which mis-normalizes for root-external modules — they are now base-prefixed, and keys outside the Vite root (../…, e.g. sibling workspace packages) resolve to /@fs/ URLs; the generated virtual:solid-manifest dev fallback mirrors the same logic. Finally, module query strings survive the lazy asset lookup: resolveLazyModuleUrls and the SSR $$moduleUrl injection kept only the queryless path while Rollup keys the facade chunk (and the Vite manifest entry) by the queried module id, so lazy(() => import('./Panel.tsx?variant=a')) missed its production manifest entry and could load different plugin output in dev — the query is now part of the asset key end to end, matching the manifest and the dev URL.

  • 3e5d8ff: Boundary guard no longer aborts Vite's dependency scan. The dep scanner
    (vite:dep-scan) crawls the raw import graph from the plugin's injected
    scan entries before any directive transform runs, so it walks straight
    through 'use server' modules into genuinely server-only code — and the
    server-only marker's client-graph guard treated that as a violation,
    failing the whole scan on every cold vite dev start ("Failed to run
    dependency scan. Skipping dependency pre-bundling.") for any app whose
    server functions reach server-only code. The graph is legal once
    transforms split it, so the guard now stands down on scanner resolves
    (the scan flag Vite sets on plugin-container resolve options, both the
    esbuild scanner in v6/7 and the rolldown one in v8) while still claiming
    the specifier — the scanner must not chase server-only/client-only as
    missing bare dependencies, which would abort the scan all the same. Real
    dev and build module graphs resolve without the flag and stay fully
    guarded: a client-side import of a server-only module is still a build
    error naming the importer.

  • e180576: The dev-manifest bridge resolver no longer caches failed lookups. The convergence cache introduced for the nested-lazy render-pass fix stored the null a bridge failure resolves to, so one transient miss (dev server briefly unreachable, non-OK response) silently stripped that module's client assets — and its hydration preload entry — for the rest of the dev session. Only successful answers are cached now; failures keep logging loudly and stay retryable, while in-flight dedupe still hands retries of the same render pass a stable promise, so convergence is unaffected.

  • 23965a2: The dev asset resolver now caches per module key and answers synchronously once a key's assets are known (in-flight walks are deduped; any watcher event drops the cache so dev CSS stays fresh). Server-side lazy() re-requests a module's assets on every retry of a suspended render pass — the router's nested outlets re-create the component per retry — and an always-async resolver suspends every retry on a brand-new promise, so the pass never converges: any nested route hung vite dev, or overflowed the render stack (one resume closure nests per cycle) and the escaped rejection killed the dev server. The build manifest never looped because it answers synchronously; dev now matches it after the first resolution. The HTTP bridge resolver for isolated SSR runners (nitro dev worker, workerd) gets the same convergence cache.

  • a2bd979: Turnkey dev middleware dispatches every request through the start.middleware chain — all methods and accept types, matching production and preview — instead of only HTML-accepting GETs. API routes (GET/POST exports served by a middleware like filesystem-routing's createAPIHandler) and no-JS form POSTs now work under vite dev. Non-page requests the chain does not handle fall back to Vite's own pipeline (its 404) rather than getting a page rendered at them, and without a configured middleware nothing changes: non-page requests never leave Vite's pipeline.

  • ec7543f: Doc examples for serverFunctions.configure import configureServerFunctionsServer from the type-correct @solidjs/web/server-functions/server subpath (the base subpath's types are the client surface and don't declare it).

  • 6e2d526: Turnkey typed env (start.env): first-party typed, validated environment
    variables for both turnkey modes. A schema file at the project root —
    env.ts/env.js, probed automatically (explicit via start.env: './path',
    off via false) — default-exports { server, client } maps of Standard
    Schema validators (zod, valibot, arktype, mixable per key; nothing imported
    from the plugin), and the validated values come back through two typed
    virtual modules: virtual:env/server (every var, server module graphs only
    — a client-graph import is a hard error naming the importer) and
    virtual:env/client (the VITE_-prefixed client side; the prefix — or
    envPrefix — is enforced at config time).

    Validation is node-only and layered, against Vite's loadEnv merge of the
    .env* files with process.env winning — and the plugin folds the
    file-loaded vars into process.env itself, so templates drop the
    process.env = { ...process.env, ...loadEnv(mode, root, '') } boilerplate.
    In dev every failure renders the error overlay with a per-key report and
    .env*/schema edits revalidate live (surviving Vite's own
    restart-on-.env-change). In a build, client failures fail the build;
    server failures only warn (a build machine may not have the production
    secrets) and boot validation enforces them.

    Client values are baked as validated plain JSON — defaults applied,
    coercions done, zero validator bytes in a client bundle (the
    runtime-library alternative ships its validator to the browser; t3-env
    costs ~13 kB gz of zod) — that's what the VITE_ prefix means. Server
    values are NOT baked anywhere: virtual:env/server reads process.env at
    server boot and validates through the user's own schema, imported into the
    server bundle only. Platform-injected vars that don't exist at build time
    work, secrets rotate without a rebuild, and no secret value exists in any
    dist artifact; an invalid server environment fails boot with the same
    per-key report. A client-build generateBundle scan additionally fails
    the build when a server var's literal value appears quoted in a client
    chunk, and a generated solid-env.d.ts (written next to the schema) types
    both virtual modules by inference from the user's own schema via the
    Standard Schema output type.

    Design credit: the feature's shape — the env.ts convention, the
    virtual:env/* names (kept identical deliberately), baked client values,
    the leak-scan heuristics — follows @vite-env/core by pyyupsk (MIT,
    https://github.com/pyyupsk/vite-env); the implementation is fresh on this
    plugin's machinery (Standard Schema as the only contract, no zod/jiti
    dependencies, consumer-based environment guarding, Vite's runnerImport
    for schema loading, runtime-read server values). Env is a turnkey feature:
    without start there is no env layer. See examples/start-env.

  • 1272d95: Turnkey per-request app setup: start.setup points at a server-only module default-exporting (event, App) => Component | void | Promise<Component | void>, awaited by the generated server entry after the middleware chain dispatches to the page render and immediately before renderToStream. The seam routers with async per-request preparation need for SSR (create a router bound to the request, await router.load(), then render) — return a component to render in the app's place, or nothing to render <App /> unchanged. The hook sees the same request event middleware decorated and runs inside the request scope. Zero-config entries are byte-identical without the option; authored server entries own render() already, so combining them is a config error.

  • 8b36370: Breaking (turnkey config reshape): Start is now a mode of the plugin. The
    turnkey options move from the object form of ssr to a new start option
    (start: true is the zero-config spelling, pure sugar for start: {}
    both mean the identical turnkey mode with defaults), and ssr is a boolean
    again with one meaning everywhere — "is the app server-rendered".
    ssr: { ... } is now a config-time error with a migration message: write
    start: { ... } (or start: true) and set ssr: true. Options are
    otherwise unchanged (start.document, start.entryServer,
    start.entryClient, start.middleware, start.external, start.app for
    the root component); a bare ssr: true without start keeps the
    transform-only behavior, and serverFunctions stays orthogonal. The
    SsrOptions type is renamed to StartOptions.

    The reason for the split is the new turnkey client mode: start without
    ssr: true serves the same conventions client-only. Dev streams the
    rendered document shell (without the app — history-fallback semantics,
    entry CSS inlined) and the generated client entry render()s the app into
    document.body; vite build prerenders the shell once through t...

Read more