From 6cc30fe5c90832cb3eac6e893df86e7790b57613 Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Thu, 17 Sep 2026 20:17:26 -0500 Subject: [PATCH] fix: resolve @testing-library/jest-dom from the project root The Vitest setup-file check used the plugin's own `require`, so it asked whether vite-plugin-solid could reach `@testing-library/jest-dom/vitest`. Under pnpm's isolated layout that succeeds when another package (e.g. Storybook) depends on jest-dom transitively, but Vitest resolves `setupFiles` from the project root, where the package is not installed, and fails with "Failed to load url .../@testing-library/jest-dom/vitest". Probe from the project root instead so the setup file is only injected when the project itself can resolve it. The bare specifier is still what gets injected: the resolved path would be jest-dom's CommonJS entry, which Vitest refuses to load. Fixes #231 Co-Authored-By: Claude Fable 5.1 --- .../jest-dom-resolve-from-project-root.md | 5 +++ src/index.ts | 41 ++++++++++++------- 2 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 .changeset/jest-dom-resolve-from-project-root.md diff --git a/.changeset/jest-dom-resolve-from-project-root.md b/.changeset/jest-dom-resolve-from-project-root.md new file mode 100644 index 0000000..a067b1b --- /dev/null +++ b/.changeset/jest-dom-resolve-from-project-root.md @@ -0,0 +1,5 @@ +--- +'vite-plugin-solid': patch +--- + +Only inject the `@testing-library/jest-dom` Vitest setup file when the package resolves from the project root. 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`. diff --git a/src/index.ts b/src/index.ts index ead2980..256f7fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import solid from 'babel-preset-solid'; import { readFileSync } from 'fs'; import { mergeAndConcat } from 'merge-anything'; import { createRequire } from 'module'; +import path from 'path'; import solidRefresh from 'solid-refresh/babel'; import type { Alias, AliasOptions, FilterPattern, Plugin } from 'vite'; import { createFilter, version } from 'vite'; @@ -178,19 +179,28 @@ function containsSolidField(fields: Record) { return false; } -function getJestDomExport(setupFiles: string[]) { - return setupFiles?.some((path) => /jest-dom/.test(path)) - ? undefined - : ['@testing-library/jest-dom/vitest', '@testing-library/jest-dom/extend-expect'].find( - (path) => { - try { - require.resolve(path); - return true; - } catch (e) { - return false; - } - }, - ); +function getJestDomExport(setupFiles: string[], root: string) { + if (setupFiles?.some((file) => /jest-dom/.test(file))) return undefined; + + // Resolve from the project root, not from this plugin's own location. With pnpm's + // isolated node_modules layout the plugin can reach a jest-dom that only exists as a + // transitive dependency (e.g. of Storybook), while Vitest resolves `setupFiles` from the + // project root, where it isn't installed, and fails to load it. + // https://github.com/solidjs/solid-vite-plugin/issues/231 + // The bare specifier (not the resolved path) is injected on purpose: `require.resolve` picks + // jest-dom's CommonJS entry, which Vitest refuses to load, while Vitest itself resolves the + // specifier to the ESM entry. + const projectRequire = createRequire(path.join(root, 'package.json')); + return ['@testing-library/jest-dom/vitest', '@testing-library/jest-dom/extend-expect'].find( + (specifier) => { + try { + projectRequire.resolve(specifier); + return true; + } catch (e) { + return false; + } + }, + ); } export default function solidPlugin(options: Partial = {}): Plugin { @@ -252,7 +262,10 @@ export default function solidPlugin(options: Partial = {}): Plugin { if (!userTest.browser?.enabled) { // vitest browser mode already has bundled jest-dom assertions // https://main.vitest.dev/guide/browser/assertion-api.html#assertion-api - const jestDomImport = getJestDomExport(userSetupFiles); + const jestDomImport = getJestDomExport( + userSetupFiles, + path.resolve(projectRoot || process.cwd()), + ); if (jestDomImport) { test.setupFiles = [jestDomImport]; }