Skip to content

Releases: solidjs/solid-vite-plugin

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 18 Sep 10:37
c94fcf3

Minor Changes

  • 22858a3: start.node: the build emits a ready-to-run Node server. With start: { node: true } the ssr build writes dist/server/node.js beside server.jsnode dist/server/node.js (env PORT, default 3000, and HOST) serves the client build statically (files under build.assetsDir as Cache-Control: public, max-age=31536000, immutable, everything else public, max-age=0, must-revalidate with Last-Modified; a reasonable MIME table, HEAD, dot-segment paths and .. traversal refused) and hands every other request to handleRequest with the raw Node request as nativeEvent, so getRequestEvent().nativeEvent answers the same as under vite dev and vite preview. The node<->web bridge is the plugin's own src/http.ts — the code the dev and preview middlewares already run (HTTP/2 pseudo-headers, https: on TLS sockets, client disconnects as the request's AbortSignal, HEAD short-circuit, set-cookie split, backpressure that also settles on close) — shipped as a separate build artifact of the package (dist/node-entry.mjs) that the plugin reads at build time and emits under a small generated header carrying the emit-time constants (client dir relative to the server dir, assetsDir, base, the mode). The file is ESM with no dependency beyond node:* and ./server.js, listens only when run directly, and exports listener (the (req, res) function, mountable into http.createServer, Express, Fastify), createListener({ static?, event? })static: false leaves files and the client-mode index.html fallback to a framework such as express.static or a CDN and keeps only the bridge; event: (req) => fields merges extra request-event fields over { nativeEvent: req } — and serve({ port?, host?, static?, event? }).

    Why: the fullstack templates shipped a hand-written server.js whose bulk was this generic Node bridge, copied into every scaffold and baked into the CLI, so bridge fixes never reached users — and it looked custom-made. Node is the one mainstream runtime without a fetch-shaped server API (Workers, Deno, Bun, Netlify, Nitro consume the server bundle's { fetch } directly), so the gap is Node-only and belongs in the build output. It is a start.* option, not ssr.* (ssr stays boolean-only), and applies to both start modes: SSR mode renders pages; client mode with serverFunctions (which keeps dist/server) serves the static client with an index.html history fallback for HTML navigations plus the endpoint. node.js is an emitted asset, never a second build input — server.js and its handleRequest / { fetch } contracts are unchanged, and nothing changes without the option. Where no server bundle exists (client mode without serverFunctions, or start.external) the build warns and emits nothing. Compression/proxy stance unchanged: plain HTTP, put a reverse proxy or CDN in front. Follow-ups this unlocks: the templates drop server.js and point their start script at node dist/server/node.js; create-solid drops its baked SERVER_JS constant.

Patch Changes

  • 7fa5aa3: Avoid overriding environments configured in Vitest workspaces and projects with the jsdom default (forward-port of #323 by @carloitaben, fixes #205). A root config that defines test.projects (or the pre-Vitest-4 test.workspace) runs no tests itself, so it no longer gets test.environment: 'jsdom' injected — which made Vitest probe for (and prompt to install) jsdom at startup even when every project runs under node or in browser mode. Each project keeps controlling its own environment.

  • d30f051: Only inject the @testing-library/jest-dom Vitest setup file when the package resolves from the project root (forward-port of #364 by @brenelz, fixes #231). Previously the check ran from the plugin's own location, so with pnpm a transitive jest-dom (for example via Storybook) made Vitest fail with Failed to load url .../@testing-library/jest-dom/vitest. The probe now walks node_modules up from the Vite root the way Vitest resolves bare setupFiles — deliberately ignoring NODE_PATH, which pnpm's bin shims (pnpm vitest) point at the hoisted virtual store where every transitive dependency is reachable.

  • 2e79544: Require solid-js / @solidjs/web 2.0.0-rc.9 (peer floor) and compile with @solidjs/compiler / @solidjs/babel-plugin rc.9 — runtime and compiler move in lockstep. The rc.9 compilers emit output only the rc.9 runtime understands, so this floor is a hard requirement, not policy: native elements with several spread sources compile to the runtimes' array form (spread(el, [a, b], …) on the client, ssrElement(tag, [a, b], …) on the server — no mergeProps() proxy, no memo, no hydration id; #3418/#3419/#3423) which only rc.9's @solidjs/web accepts; delegated event handlers move off Solid 1's $$<type> element key onto _$$<type> in both compiled output and the runtime's document delegate, so an rc.8 runtime would never fire an rc.9-compiled onClick (and vice versa); and under componentNames — which the plugin already turns on for its dev and observe postures — SSR output keeps createComponent(Comp, props, "Comp") so the server runtime's observe/dev tier labels the owner and a server finding's ownerPath reads <App> › <Page> like the client's. Nothing in the plugin itself had to change for rc.9: the new observe / development export conditions on @solidjs/web's server-functions and frames client entries (and observe on every server entry) are picked up by the condition lists the plugin already installs, and the new solid-js/internal subpath is covered by the existing solid-js inlining.

  • 256c25d: start.instrument: a server-only module the plugin runs to completion before anything else in the server graph loads — the app, the middleware, @solidjs/web, every dependency. The seam for instrumentation that must patch the runtime before the modules it patches are loaded (an APM's OpenTelemetry setup, a profiler, a module.register hook), honored on every surface: vite dev, vite build, vite preview, and a host consuming the handler entry. Replaces the per-host node --import instrument.mjs dance.

    Import order cannot do this in ESM — static imports are hoisted and evaluated in dependency order — so the generated handler entry becomes await import(instrument); await import(handler), with the handler's surface (handleRequest, the fetch default) re-declared by name. The module may be async and needs no exports; the server build must keep code splitting on (the default).

    Also: the componentNames note in the compiler options no longer calls the labels DOM-only — the SSR generate emits them too from the compilers that carry solidjs/solid#3441 (2.0.0-rc.9), and the start-ssr suite gains an observe mode that asserts an observe: true production build resolves the observe artifacts and carries component labels (the SSR half asserted once the workspace rides an rc that emits them).

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 11 Sep 17:10
5b6612b

Patch Changes

  • 4fb1af3: Honor the host's resolve.noExternal patterns when adding vitefu's externals to the SSR environment. The plugin already refused to re-externalize anything noExternal inlines, but it compared literal names only, while Vite treats string entries as picomatch patterns and RegExp entries as tests (createFilter(undefined, noExternal, { resolve: false })). Since 3.0.0-next.41 the crawl also reaches the packages that consume the Solid runtime, and their non-Solid dependencies land in ssr.external — so a host that inlines its packages by pattern (TanStack Start's @tanstack/start**, whose @tanstack/start-server-core resolves its #tanstack-* imports only when Vite processes it) saw them re-externalized, and vite dev failed with ERR_PACKAGE_IMPORT_NOT_DEFINED: Package import specifier "#tanstack-router-entry" is not defined. The externals are now filtered with the same matcher Vite uses, and a single string or RegExp noExternal value is kept instead of being dropped.
  • b173c94: New observe option: resolve Solid's observe builds for production observability. observe: true adds the observe export condition to every environment — client and server, inlined and externalized (resolve.externalConditions), inlining the core runtime and its consumers for server builds the way the dev posture already does so one build is loaded end to end — and turns on the compiler's componentNames option, so component owner labels (<Home>) survive minification in diagnostics and attribution paths. componentNames is also enabled under the dev posture, where lazy() and HMR wrappers otherwise hide the tag name. Requires @solidjs/compiler / @solidjs/babel-plugin ≥ 2.0.0-rc.8 (the release that adds the option).

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 10 Sep 08:46
4236073

Patch Changes

  • ed9b798: Allow @testing-library/jest-dom v7 in the optional peer dependency range (ports #287 from main). The Solid 2.0 templates pin @testing-library/jest-dom@^7.0.0, and npm 7+ enforces peer ranges, so a clean npm install of a freshly created project failed with ERESOLVE against the ^6.* range (solidjs/solid#3341). v7 keeps the @testing-library/jest-dom/vitest subpath the plugin auto-injects into test.setupFiles, so only the range changes.

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 10 Sep 00:24
e796386

Patch Changes

  • 38434e1: Fix the start-mode handler booting the wrong chunk when the client build has several configured inputs (#353, a regression of 3.0.0-next.40 / #347). With filesystem-routing's fileRoutes({ routers: { client }, buildInputs: 'client' }) every route module is a build.rollupOptions.input, and since #347 those records rightly keep isEntry in virtual:solid-manifest. The generated handler resolved the client entry by scanning for the first isEntry record, so a route key sorting ahead of the plugin's own virtual:solid-ssr-entry-client.tsx won: the document's <script type="module"> pointed at the route chunk (the page never hydrated) and <head> linked that route's CSS while the entry graph's global stylesheet was never linked. The manifest module now names the client entry explicitly — _entry carries its key (the entry start mode injects, or the single configured input outside start mode) and its record is serialized first — and the handler reads _entry before falling back to the isEntry scan for hand-rolled manifests. @solidjs/web's registerEntryAssets, which links the entry graph's stylesheets and modulepreloads by the first isEntry record, therefore agrees on the same chunk. Other configured inputs keep isEntry; they are genuine entries, just not the one the document boots.
  • bd04c66: Packages that consume the Solid runtime without declaring a solid export condition are now inlined in dev server environments too, closing the remaining half of the two-instance split. Inlining solid-js and @solidjs/web fixes every resolution those two perform, and vitefu inlines packages that advertise a solid export condition — but a package that does neither is still externalized, and Node resolves its own import "solid-js" without the development condition, so it loads the production server build while the inlined graph holds the dev one. @solidjs/meta is the first-party example: it has no solid condition, so under solid-js 2.0.0-rc.7 an app rendering a <Title> still died in useContext on a second sharedConfig even with the core packages inlined. The crawl now also classifies any package declaring solid-js or @solidjs/web in its dependencies or peerDependencies as a semi-framework package — ssr.noExternal without optimizeDeps.exclude, since these hold no raw Solid components — so third-party component libraries and metadata helpers reach the same copy as everything else. Gated on the dev-condition swap (and off under vitest, which manages inlining itself), leaving builds unchanged. Two guards keep the rule narrow: tooling that declares solid-js as a peer but never runs inside the SSR module runner — @solidjs/vite-plugin itself, vite, vitest, eslint-plugin-*, vite-plugin-*, prettier-plugin-*, @types/* — is skipped entirely (classifying the plugin would also crawl its dependencies and pre-bundle @babel/core and @solidjs/babel-plugin into the browser's optimizeDeps, several megabytes of dead weight per cold start); and the ssr.external list vitefu derives from framework packages' non-framework dependencies is filtered against the final noExternal list, because Vite gives external precedence — a framework package listing @solidjs/web under dependencies (e.g. @tanstack/solid-router) would otherwise re-externalize a core the plugin just inlined and split the runtime again.

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Sep 17:47
09d73a9

Minor Changes

  • 0985ce8: start.renderMode: 'stream' | 'async' (default 'stream'), plus a per-request form and a runtime override — the fix for streaming SSR leaving <Loading> fallbacks unresolved for clients that never run JavaScript (solidjs/solid#3280). 'async' makes the generated handler adopt the renderToStream result's thenable, which resolves with the complete HTML once every boundary has settled: nothing has flushed, so each boundary's content is spliced in place of its placeholder — no fallback markup, no swap templates or scripts — while hydration data still serializes and JavaScript clients hydrate as before. The string then takes createSSRResponse's string path: the response head commits, the document gets the doctype and client-entry injection, and a Location written mid-render becomes a real 3xx instead of the post-flush script redirect. The tradeoffs are inherent and documented: time-to-first-byte waits for the slowest boundary and the whole page buffers in memory; deferStream is moot (everything defers). The per-request form follows the middleware/setup convention — renderMode: './src/render-mode.ts', a module default-exporting (event) => 'stream' | 'async' | Promise<...> run inside the request scope after the middleware chain — for policies like "complete documents for crawler user agents or ?nojs, streaming for everyone else". Hosts driving the handler directly pass handleRequest(request, { renderMode }); precedence is that runtime option, then the module function, then the static config, and an invalid value from any source is rejected with an actionable error (unknown literals and missing module paths fail at config time). Works identically for authored entries; stream mode is unchanged. Requires solid-js / @solidjs/web ^2.0.0-rc.7, which freezes the response head when the awaited render completes so httpStatus / httpHeader declarations reach the response (solidjs/solid#3292).

Patch Changes

  • a810d09: Dev servers now inline solid-js and @solidjs/web into every server environment instead of externalizing them. resolve.externalConditions only governs the imports Vite's module runner resolves itself; an externalized package's own imports are resolved by Node with Node's conditions, never development. Since solid 2.0.0-rc.7 both core packages ship a dist/server.dev.* behind that condition, so under vite dev the framework split in two: the app's solid-js was the runner's dev copy while @solidjs/web's import "solid-js" landed on Node's production copy. renderToStream installed the asset resolver on one sharedConfig and lazy() read the other — every dev SSR page with a lazy() component failed with lazy() called with moduleUrl "…" but no asset manifest is set — and every other module-level singleton (owner tracking, request events, hydration keys) was divided the same way. With the two packages in resolve.noExternal every resolution, theirs included, goes through the environment's conditions and a single dev build is loaded end to end. Applies whenever the plugin injects dev mode into a server environment; vitest projects (which manage their own inlining) and hosts that set noExternal: true are left as they are.

  • a3cc782: Fix vite dev breaking after a mid-session dependency re-optimization when the development toolbar is installed. The generated entries' @solidjs/start-devtools import reused the id captured when the toolbar was detected; in the client environment that id is the optimizer's pre-bundled URL, stamped with the browserHash of the pass that produced it. Any dependency discovered after the initial scan re-optimizes — the toolbar's chunks are re-emitted under new names and the hash moves on — and the frozen id kept the entry on the previous pass: its lazy chunks answered 504 Outdated Optimize Dep and the stale bundle brought a second solid-js instance into the page (hydration key misses, REACTIVITY_HALTED). The import is now resolved afresh on every request, so it always follows the current optimizer pass.

    The most common trigger is also removed: the agent diagnostics bridge (@solidjs/diagnostics/browser and /protocol) reaches the page through a virtual module the dependency scanner never crawls, so its first load discovered the two imports and forced exactly that re-optimize + reload. The diagnostics plugin now pre-bundles them up front whenever the surface is enabled.

  • c16985a: Provide the deployment secret to server builds (solidjs/solid#3239): the generated server-function handler module now leads with globalThis.__SOLID_SECRET__ ??= "<random-per-build>", giving the runtime's encrypted no-JS flash cookie a key with zero configuration. One value is generated per plugin instance, so a production build bakes a single secret into the emitted server chunk (shared by every instance of that deployment) and a dev session holds one for its lifetime. Server output only — the handler module is already hard-gated against client graphs — and an explicit configureServerFunctionsServer({ secret }) still outranks it.

  • 1f5f6ca: Never strip isEntry from a genuine configured entry when reclassifying emitted lazy facade chunks. The normalization used to demote every chunk that is a dynamic-import target, which misfires once the real client entry absorbs a module that is also imported dynamically: with Solid 2, @solidjs/web/frames/client lazily imports the serialization decoder, so a static import of @solidjs/web/serialization/decode anywhere in the client graph merges the decoder into the entry chunk and the entry ends up listing itself under dynamicImports. Demoting it left the bundle and manifest.json with no entry at all ("No entry file found" in downstream manifest capture such as TanStack Start's). Chunks whose facade matches a configured build.rollupOptions.input (or the default index.html / the start-mode client entry) now keep isEntry in the raw bundle and in virtual:solid-manifest, a chunk's dynamic import of itself is ignored, emitted lazy() facades are still reclassified, and a demotion the plugin cannot attribute to one of its own emitted chunks is reported with a warning describing the graph shape. The virtual manifest also repairs isDynamicEntry on lazy facades, which rolldown drops when syncing generateBundle mutations back.

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 05 Sep 08:49
3d6411e

Patch Changes

  • 9fbbef6: The persisted server-function manifest (dist/client/.vite/solid-server-functions.json) now records every server function the client build can reach, by wire id, alongside the module list: { modules: string[], functions: Array<{ id, name, module }> }. Build tooling that needs the client-reachable set — a static-site prerenderer verifying that each reachable function was captured at build time, for example — reads it from here instead of re-deriving it from compiled output. The previous array shape is still accepted when read (the type is exported as PersistedServerFunctionManifest).

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 02 Sep 21:05
66ce052

Patch Changes

  • dfabe22: Auto-enable the agent diagnostics surface (dev serve only) when @solidjs/diagnostics is installed in the app — installing the dev dependency is now the whole setup. The diagnostics option becomes an override: true forces it on (erroring if the package is missing), false opts out entirely, omitted auto-detects. Start mode's generated/authored client entries follow the same detection for the bridge import.
  • f463de5: Diagnostics auto-detection now requires the app to declare @solidjs/diagnostics in its own package.json (presence in ancestor node_modules surprise-enabled the surface for monorepo fixture apps), and the surface never activates in test mode (vitest browser mode runs a dev serve and was getting the bridge injected into test pages).
  • cf13314: serverFunctions.components now also accepts 'external': identical to true, but declares that a composing host (e.g. the Astro adapter or TanStack Start's Solid integration) owns the document wiring — render plugin + client-side installServerComponents() call — itself, so the without-SSR-start-mode warning is skipped instead of printing on every host build. The remaining warning text is also updated: it listed "the bootstrap script" as a required app-side piece, but head bootstrap injection was removed (serialized references self-bootstrap the registry), and it now points hosts at components: 'external'.
  • e8ffe62: Add experimental .tsrx compilation with native and Babel backends, scoped CSS sidecars, HMR and SSR asset integration, and function-level server functions.

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 01 Sep 18:43
022d5c2

Patch Changes

  • 73751ca: Only attach a request body in the dev middlewares' Node-to-web bridging when the incoming request actually carries one (Content-Length/Transfer-Encoding, or the h2 END_STREAM flag). An unconditionally attached empty stream made bodyless POSTs — zero-argument scripted server function calls, synthetic dispatches — parse as a present-but-unusable body, which @solidjs/web 2.0.0-rc.5 rejects as malformed (400) instead of ignoring.
  • 16245b6: Dev middleware recognizes the scripted transport's data address. Scripted server-function calls now go to <endpoint>/data/<id> (solidjs/solid#3094), and the middleware's module-preload step assumed exactly one path segment after the mount — a cold function only client code references would never be evaluated in the SSR environment for a data-addressed call, answering 404 under vite dev. Dispatch itself was unaffected (mount matching is prefix-based). The id now parses from behind the literal data segment too; a function id spelled data still parses at the bare address, since an id occupies exactly one segment.
  • 3a7ff44: Document shell edits now trigger a full page reload instead of being silently absorbed (solidjs/solid#3151). Two sides: the resolved start.document / src/Document.* module declines HMR in its client compile (it hydrates the whole document, so no component swap can ever apply — self-accept + invalidate makes Vite reload instead), and server-environment updates for files with no client-graph counterpart (client-mode documents, authored entry-server, middleware) send a browser full-reload rather than staying suppressed — the suppression exists to protect client HMR from full-reload races, but a server-only file has no client update to race with.
  • fb9f447: Drop the retired X-Server-Function-Id header and ?id= addressing fallback from the dev middleware's module-preload path. Addressing is path-only (<endpoint>/<id> and <endpoint>/data/<id>), matching the runtime's removal of its own transitional shims during the RC.
  • e9b2a39: Read the file hash from the second id segment. Server-function ids are now identity-keyed <name>-<hash>[-<ordinal>] (solidjs/solid#3109) instead of positional <hash>-<ordinal>, so the dev middleware's id-to-module lookup takes the hash from split('-')[1] rather than the first segment.

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 29 Aug 09:08
7914afa

Patch Changes

  • 9f0ec40: Dev servers now resolve the development export condition for externalized server deps. Externalized SSR imports are resolved with resolve.externalConditions (default ['node', 'module-sync']), so packages selecting their dev build through the development condition — @solidjs/web's server-functions runtime among them — loaded their production copy under vite dev: thrown server errors reached the client sanitized to "Internal Server Error" instead of carrying the real message, and dev-only diagnostics vanished. The plugin now prepends development to each server environment's externalConditions whenever it injects dev mode, matching the treatment resolve.conditions already got.

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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Aug 22:10
3ffde4a

Minor Changes

  • 2f0384a: Route path-addressed server function calls (solidjs/solid#3076). A call's address is now <endpoint>/<id> with arguments in the query, so the dev middleware and the generated request-dispatch gate match the endpoint by mount prefix instead of exact pathname, and the dev middleware's module-preload id comes from the path segment. The retired X-Server-Function-Id header and ?id= forms remain as transitional fallbacks for the RC window only — they will be dropped before the stable release.

Patch Changes

  • 5c50681: Announce the diagnostics surface in the dev-server startup block when diagnostics: true is set: two extra lines after Vite's URLs naming the /__solid/diagnostics endpoint (with its method vocabulary) and the agent skill documents shipped in node_modules. Startup output is the one channel agents reliably read even in projects with no AGENTS.md, making this the discovery path for existing apps and ports. Dev-serve only.