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/module-main.ts b/packages/devtools/src/module-main.ts index f40f61cd27..38f9836310 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,27 @@ 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(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 + } + })), ) } 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' diff --git a/packages/devtools/test/optimize-deps.test.ts b/packages/devtools/test/optimize-deps.test.ts new file mode 100644 index 0000000000..f8a45879df --- /dev/null +++ b/packages/devtools/test/optimize-deps.test.ts @@ -0,0 +1,80 @@ +import { isAbsolute } from 'node:path' +import { resolvePath } from 'mlly' +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(), + } +}) + +vi.mock('mlly', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + resolvePath: vi.fn(actual.resolvePath), + } +}) + +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(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$/) + 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', + ]) + } + }) +}) 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": {