diff --git a/.changeset/devtools-optional-peer-stub.md b/.changeset/devtools-optional-peer-stub.md new file mode 100644 index 0000000..dbb97c7 --- /dev/null +++ b/.changeset/devtools-optional-peer-stub.md @@ -0,0 +1,5 @@ +--- +'@solidjs/vite-plugin': patch +--- + +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". diff --git a/src/ssr/index.ts b/src/ssr/index.ts index bfdcb5d..ed308b5 100644 --- a/src/ssr/index.ts +++ b/src/ssr/index.ts @@ -489,11 +489,18 @@ export function startServe( // from the plugin's own file: in pnpm-isolated apps a copy that is only a // dependency of the plugin is not reachable from the app's importers. The // resolved id is kept so imports from generated modules can use it. - devtoolsResolutions[consumer] ??= (async () => - ( - (await resolve(DEVTOOLS_PACKAGE, importer)) ?? - (await resolve(DEVTOOLS_PACKAGE, fileURLToPath(import.meta.url))) - )?.id ?? null)(); + devtoolsResolutions[consumer] ??= (async () => { + // Resolving from the plugin's own file never yields null when the + // package is absent: it is declared an optional peer dependency, so + // Vite answers with its `__vite-optional-peer-dep:` stub (an empty + // module). Treat that stub as "not installed". + const realId = (resolved: { id: string } | null) => + resolved && !resolved.id.startsWith('__vite-optional-peer-dep:') ? resolved.id : null; + return ( + realId(await resolve(DEVTOOLS_PACKAGE, importer)) ?? + realId(await resolve(DEVTOOLS_PACKAGE, fileURLToPath(import.meta.url))) + ); + })(); const id = await devtoolsResolutions[consumer]; devtoolsIds[consumer] = id; if (!id && options.devtools === true) {