From 7f8506af518c95aa3e81d7ee1c4b7eeac9af4787 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 9 Nov 2026 21:33:23 +0000 Subject: [PATCH 1/6] fix(devtools): anchor optimizeDeps entries on the @nuxt/devtools module --- packages/devtools/src/module-main.ts | 32 +++++++++--- packages/devtools/test/optimize-deps.test.ts | 51 ++++++++++++++++++++ 2 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 packages/devtools/test/optimize-deps.test.ts diff --git a/packages/devtools/src/module-main.ts b/packages/devtools/src/module-main.ts index f40f61cd27..9a5058512c 100644 --- a/packages/devtools/src/module-main.ts +++ b/packages/devtools/src/module-main.ts @@ -10,6 +10,7 @@ import { deprecate, NUXT_DEVTOOLS_GROUP_ID } from '@nuxt/devtools-kit' import { addImports, addPlugin, addTemplate, addVitePlugin, extendViteConfig, logger } from '@nuxt/kit' import { colors } from 'consola/utils' import { serveStaticNodeMiddleware } from 'devframe/utils/serve-static' +import { resolvePath } from 'mlly' import { join, resolve } from 'pathe' import { isGreaterThanOrEqual } from 'verkit' import { searchForWorkspaceRoot, version as viteVersion } from 'vite' @@ -24,6 +25,17 @@ import { readLocalOptions } from './utils/local-options' const MULTIPLE_SLASHES_RE = /\/+/g +// Vite DevTools 0.5's embedded client is served by the hub as an external +// `embedded.js` script (see `runtime/plugins/vite-devtools.client`), so it +// is no longer a bundler-resolved `@vitejs/devtools/client/inject` import to +// pre-bundle here. +const OPTIMIZE_DEPS = [ + '@vitejs/devtools-kit/client', + 'error-stack-parser-es', + 'vite-plugin-vue-devtools/client', + 'vite-plugin-vue-tracer/client/overlay', +] + /** * The published package holding the built client UI, version-locked to this * package and declared as an optional peer dependency. Served as devframe @@ -116,15 +128,19 @@ export async function enableModule(options: ModuleOptions, nuxt: Nuxt) { if (nuxt.options.dev) { nuxt.options.vite.optimizeDeps ||= {} nuxt.options.vite.optimizeDeps.include ||= [] + // Resolve from this module's own location so the entries work whether + // `@nuxt/devtools` is a direct dependency or nested under `nuxt`. nuxt.options.vite.optimizeDeps.include.push( - // Vite DevTools 0.5's embedded client is served by the hub as an external - // `embedded.js` script (see `runtime/plugins/vite-devtools.client`), so it - // is no longer a bundler-resolved `@vitejs/devtools/client/inject` import to - // pre-bundle here. - 'nuxt > @nuxt/devtools > @vitejs/devtools-kit/client', - 'nuxt > @nuxt/devtools > error-stack-parser-es', - 'nuxt > @nuxt/devtools > vite-plugin-vue-devtools/client', - 'nuxt > @nuxt/devtools > vite-plugin-vue-tracer/client/overlay', + ...await Promise.all(OPTIMIZE_DEPS.map(id => resolvePath(id, { + url: import.meta.url, + conditions: [ + 'module', + 'browser', + 'development', + 'import', + 'default', + ], + }).catch(() => id))), ) } diff --git a/packages/devtools/test/optimize-deps.test.ts b/packages/devtools/test/optimize-deps.test.ts new file mode 100644 index 0000000000..6d3f35ff7e --- /dev/null +++ b/packages/devtools/test/optimize-deps.test.ts @@ -0,0 +1,51 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { fakeNuxt } from './fake-nuxt' + +vi.mock('@vitejs/devtools', () => ({ + DevTools: vi.fn(() => []), +})) + +vi.mock('@nuxt/kit', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + addImports: vi.fn(), + addPlugin: vi.fn(), + addTemplate: vi.fn(), + addVitePlugin: vi.fn(), + extendViteConfig: vi.fn(), + } +}) + +afterEach(() => { + vi.unstubAllEnvs() +}) + +async function enableDevtools(nuxt = fakeNuxt()) { + vi.stubEnv('NODE_ENV', 'development') + vi.stubEnv('TEST', '') + const { enableModule } = await import('../src/module-main') + await enableModule({ + dataInspector: false, + viteInspect: false, + componentInspector: false, + vueDevTools: false, + codeServer: { enabled: false }, + } as any, nuxt) + return nuxt.options.vite.optimizeDeps?.include +} + +describe('optimizeDeps.include', () => { + it('resolves injected client deps from @nuxt/devtools', async () => { + const include = await enableDevtools() + + expect(include).toHaveLength(4) + for (const entry of include!) { + expect(entry).toMatch(/^\//) + } + expect(include![0]).toMatch(/@vitejs\/devtools-kit\/dist\/client\.(?:m|c)?js$/) + expect(include![1]).toMatch(/error-stack-parser-es\/dist\/index\.mjs$/) + expect(include![2]).toMatch(/vite-plugin-vue-devtools\/dist\/client\.mjs$/) + expect(include![3]).toMatch(/vite-plugin-vue-tracer\/dist\/client\/overlay\.mjs$/) + }) +}) From 6e576f6a5610f06657c65a7263599f7166ff6a84 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 9 Nov 2026 21:33:25 +0000 Subject: [PATCH 2/6] fix(devtools): type resolved devtools option enabled as required --- packages/devtools-kit/src/_types/options.ts | 2 +- packages/devtools/src/constant.ts | 2 +- packages/devtools/src/server-rpc/general.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/devtools-kit/src/_types/options.ts b/packages/devtools-kit/src/_types/options.ts index 798fa26c67..e157f38be2 100644 --- a/packages/devtools-kit/src/_types/options.ts +++ b/packages/devtools-kit/src/_types/options.ts @@ -11,7 +11,7 @@ export interface ModuleOptions { * * @default true */ - enabled?: boolean + enabled: boolean /** * Custom tabs diff --git a/packages/devtools/src/constant.ts b/packages/devtools/src/constant.ts index d26aae0ae1..af84103e22 100644 --- a/packages/devtools/src/constant.ts +++ b/packages/devtools/src/constant.ts @@ -4,7 +4,7 @@ import { provider } from 'std-env' const isSandboxed = provider === 'stackblitz' || provider === 'codesandbox' export const defaultOptions: ModuleOptions = { - enabled: undefined, // determine multiple conditions + enabled: true, componentInspector: true, vueDevTools: true, viteInspect: true, diff --git a/packages/devtools/src/server-rpc/general.ts b/packages/devtools/src/server-rpc/general.ts index c5c29230eb..f5e79b9add 100644 --- a/packages/devtools/src/server-rpc/general.ts +++ b/packages/devtools/src/server-rpc/general.ts @@ -1,7 +1,7 @@ -import type { ModuleOptions, NuxtLayout } from '@nuxt/schema' +import type { NuxtLayout } from '@nuxt/schema' import type { Component, NuxtApp, NuxtOptions, NuxtPage } from 'nuxt/schema' import type { Unimport } from 'unimport' -import type { AutoImportsWithMetadata, HookInfo, NuxtDevtoolsServerContext, ServerDebugContext, ServerFunctions } from '../types' +import type { AutoImportsWithMetadata, HookInfo, ModuleOptions, NuxtDevtoolsServerContext, ServerDebugContext, ServerFunctions } from '../types' import { existsSync } from 'node:fs' import fs from 'node:fs/promises' import { logger } from '@nuxt/kit' From 1cffb013cc539d90541bc5a416fb9a61ae440085 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 9 Nov 2026 23:09:23 +0000 Subject: [PATCH 3/6] fix(devtools): log optimizeDeps entry resolution failures --- packages/devtools/src/module-main.ts | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/devtools/src/module-main.ts b/packages/devtools/src/module-main.ts index 9a5058512c..38f9836310 100644 --- a/packages/devtools/src/module-main.ts +++ b/packages/devtools/src/module-main.ts @@ -131,16 +131,24 @@ export async function enableModule(options: ModuleOptions, nuxt: Nuxt) { // Resolve from this module's own location so the entries work whether // `@nuxt/devtools` is a direct dependency or nested under `nuxt`. nuxt.options.vite.optimizeDeps.include.push( - ...await Promise.all(OPTIMIZE_DEPS.map(id => resolvePath(id, { - url: import.meta.url, - conditions: [ - 'module', - 'browser', - 'development', - 'import', - 'default', - ], - }).catch(() => id))), + ...await Promise.all(OPTIMIZE_DEPS.map(async (id) => { + try { + return await resolvePath(id, { + url: import.meta.url, + conditions: [ + 'module', + 'browser', + 'development', + 'import', + 'default', + ], + }) + } + catch (error) { + logger.warn(`[nuxt-devtools] Failed to resolve optimizeDeps entry \`${id}\``, error) + return id + } + })), ) } From fefc8242f1b7d74e3b5232fbb054ac20be77f786 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:29:15 +0000 Subject: [PATCH 4/6] [autofix.ci] apply automated fixes --- skills-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills-lock.json b/skills-lock.json index 8cccaf9bc6..27a1a44c70 100644 --- a/skills-lock.json +++ b/skills-lock.json @@ -25,14 +25,14 @@ "source": "antfu/skills", "sourceType": "github", "skillPath": "skills/antfu-create-pr/SKILL.md", - "computedHash": "19fc3e48bd3d2adcdb374416e7b6dac0102b4579b6ea7eed247d7e7f014c3015", + "computedHash": "f6e5de5f27febe99042715627e8ff67dbe2328e0914527e152544a17f2561d2a", "via": "@antfu/skills" }, "antfu-issue-triage": { "source": "antfu/skills", "sourceType": "github", "skillPath": "skills/antfu-issue-triage/SKILL.md", - "computedHash": "efba543cad37aebdcfe00422cce3db46a232d470bc7f54781cc4b96351ce0cd9", + "computedHash": "34f50f30effac696d1bc4111c7cb21dc46da76aee1732b24207b930899e6acfa", "via": "@antfu/skills" }, "antfu-query": { From 09ee569563c82f9e879e819eb7ba72bd61a2f251 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 9 Nov 2026 23:39:48 +0000 Subject: [PATCH 5/6] test(devtools): use platform-aware absolute path check --- packages/devtools/test/optimize-deps.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/devtools/test/optimize-deps.test.ts b/packages/devtools/test/optimize-deps.test.ts index 6d3f35ff7e..c27381fd18 100644 --- a/packages/devtools/test/optimize-deps.test.ts +++ b/packages/devtools/test/optimize-deps.test.ts @@ -1,3 +1,4 @@ +import { isAbsolute } from 'node:path' import { afterEach, describe, expect, it, vi } from 'vitest' import { fakeNuxt } from './fake-nuxt' @@ -41,7 +42,7 @@ describe('optimizeDeps.include', () => { expect(include).toHaveLength(4) for (const entry of include!) { - expect(entry).toMatch(/^\//) + expect(isAbsolute(entry)).toBe(true) } expect(include![0]).toMatch(/@vitejs\/devtools-kit\/dist\/client\.(?:m|c)?js$/) expect(include![1]).toMatch(/error-stack-parser-es\/dist\/index\.mjs$/) From dca8e032cc26641320327176af284d372c14ec37 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 9 Nov 2026 23:40:31 +0000 Subject: [PATCH 6/6] test(devtools): assert optimizeDeps resolve from the module location --- packages/devtools/test/optimize-deps.test.ts | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/devtools/test/optimize-deps.test.ts b/packages/devtools/test/optimize-deps.test.ts index c27381fd18..f8a45879df 100644 --- a/packages/devtools/test/optimize-deps.test.ts +++ b/packages/devtools/test/optimize-deps.test.ts @@ -1,4 +1,5 @@ import { isAbsolute } from 'node:path' +import { resolvePath } from 'mlly' import { afterEach, describe, expect, it, vi } from 'vitest' import { fakeNuxt } from './fake-nuxt' @@ -18,6 +19,14 @@ vi.mock('@nuxt/kit', async (importOriginal) => { } }) +vi.mock('mlly', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + resolvePath: vi.fn(actual.resolvePath), + } +}) + afterEach(() => { vi.unstubAllEnvs() }) @@ -49,4 +58,23 @@ describe('optimizeDeps.include', () => { expect(include![2]).toMatch(/vite-plugin-vue-devtools\/dist\/client\.mjs$/) expect(include![3]).toMatch(/vite-plugin-vue-tracer\/dist\/client\/overlay\.mjs$/) }) + + it('resolves entries from the module location', async () => { + const resolvePathMock = vi.mocked(resolvePath) + resolvePathMock.mockClear() + + await enableDevtools() + + expect(resolvePathMock).toHaveBeenCalledTimes(4) + for (const [, options] of resolvePathMock.mock.calls) { + expect(options?.url).toEqual(expect.stringMatching(/packages\/devtools\/src\/module-main\.ts$/)) + expect(options?.conditions).toEqual([ + 'module', + 'browser', + 'development', + 'import', + 'default', + ]) + } + }) })