Skip to content

@solidjs/testing-library resolves @solidjs/web to the server build (isServer true) in jsdom tests under a multi-project vitest setup #369

Open

Description

@ashtonjurgens

Environment

  • @solidjs/vite-plugin 3.0.0-next.44
  • vitest 5.0.1, multi-project (projects), pool: threads, isolate: false
  • solid-js / @solidjs/web 2.0.0-rc.9
  • @solidjs/testing-library 1.0.0-beta.3

Reproduction

A vitest config with two projects, one jsdom ("client") and one node ("server"):

test: {
  projects: [
    { test: { name: "client", environment: "jsdom", include: ["src/**/*.test.tsx"] } },
    { test: { name: "server", environment: "node", include: ["src/server/**/*.test.ts"] } },
  ],
}

In a test file, import { isServer } from "@solidjs/web" is false (correct browser build). But inside @solidjs/testing-library's render, @solidjs/web is the server build (isServer === true), so render/hydrate behave incorrectly.

Root cause

In the client/jsdom test posture the plugin injects browser into resolve.conditions (fixing inlined modules) but leaves @solidjs/web and @solidjs/testing-library externalized — and explicitly externalizes solid-js via test.server.deps.external: [/solid-js/]. Externalized modules are executed natively by Node, whose resolution conditions are ['node', 'import', 'default'] (no browser). So @solidjs/testing-library's own import { render, hydrate } from "@solidjs/web" resolves the node export (dist/server.js).

The plugin already handles the inverse case for the node/server posture by inlining /solid-js/ + /@solidjs[+/]web/, but the client posture gets no equivalent treatment.

Workaround

Inline the runtime packages so their imports resolve through Vite's browser condition:

test: {
  server: {
    deps: {
      inline: ["solid-js", "@solidjs/web", "@solidjs/testing-library"],
    },
  },
}

(deps.inline takes precedence over deps.external.)

Activity

  1. brenelz commented on Sep 20, 2026

    @brenelz
    Contributor

    Thanks for the report. I reproduced a failure with this workspace shape on vitest@5.0.1, and the cause is different from the one described. Vitest 5 defaults test.sharedViteServer to true, so inline projects reuse the root Vite server and never run the plugin's config hook. They lose the per-project client or server posture, the jest-dom setup file, and the server.deps handling. The same config passes on Vitest 4.1.11.

    Until a release includes #370, setting the option in the root config restores the Vitest 4 behavior without the deps.inline list:

    test: {
      sharedViteServer: false,
      projects: [/* ... */],
    }

    I could not reproduce the exact symptom where only @solidjs/testing-library resolves the server build, so if it still happens with sharedViteServer: false, please share the full vitest config.

  2. ashtonjurgens commented on Sep 21, 2026

    @ashtonjurgens
    Author

    Apologies, looks like this is deno only. Here's a repro https://gist.github.com/ashtonjurgens/aef5c128b4a24691493d7f2444c21117 . Of course, take the README's AI generated diagnosis/solution with a grain of salt.

  3. ryansolid commented on Sep 27, 2026

    @ryansolid
    Member

    Thanks for the Deno follow-up and the gist — that pins it down.

    #370 fixes a separate Vitest 5 regression found while investigating this: inline test.projects share the root Vite server by default, which bypasses the plugin's config hook (the node project lost the server posture and the jsdom project lost the jest-dom setup file). It does not change the Deno resolution path, and on Node the testing-library render in your repro already passes on next.

    Mechanism: the externalized @solidjs/testing-library imports @solidjs/web through Deno's own resolver, whose conditions are deno/node — and @solidjs/web's exports map both of those to the server build (dist/server.js). Under Node, Vitest forwards the root config's browser condition to the worker's --conditions, so externals resolve the browser build; Deno never receives that condition.

    Two candidate fixes:

    • Plugin side: have the client test posture inline the runtime-consuming packages (as the server posture already does), so their @solidjs/web imports resolve through Vite's browser condition rather than the host runtime's.
    • Workaround today: Deno's custom-conditions setting — deno run --conditions browser … or DENO_CONDITIONS=browser (per the current Deno CLI docs; the gist's --node-conditions/DENO_NODE_CONDITIONS naming doesn't appear there). browser precedes deno in the exports map, so it should win — unverified here, Deno isn't installed on this machine.

    Keeping this open, scoped to the Deno resolution path.

    — Claude via Cursor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions