From 3263b5e875e625b9cc6056c43c6a697018c548c4 Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Sat, 3 Oct 2026 12:47:41 +0900 Subject: [PATCH 01/11] fix(module): unignore test file patterns only for vitest setup --- .../__tests__/SomeComponent.nuxt.test.ts | 8 ++ .../pages/about/team.nuxt.spec.ts | 9 ++ .../tests/resolved-files.spec.ts | 2 +- src/config.ts | 3 + src/module.ts | 9 +- src/module/ignore.ts | 17 +++ src/module/mock.ts | 18 --- .../app/components/Message.nuxt.spec.ts | 9 ++ .../nuxt-ignore/app/components/Message.vue | 3 + .../app/composables/useMessage.nuxt.spec.ts | 8 ++ .../nuxt-ignore/app/composables/useMessage.ts | 3 + .../app/middleware/logger.global.nuxt.spec.ts | 6 + .../app/middleware/logger.global.ts | 3 + .../app/pages/__tests__/index.nuxt.spec.ts | 9 ++ .../nuxt-ignore/app/pages/index.nuxt.spec.ts | 9 ++ test/fixtures/nuxt-ignore/app/pages/index.vue | 3 + .../app/plugins/message.nuxt.test.ts | 12 ++ .../nuxt-ignore/app/plugins/message.ts | 9 ++ test/fixtures/nuxt-ignore/nuxt.config.ts | 3 + .../nuxt-ignore/server/api/hello.get.spec.ts | 10 ++ .../nuxt-ignore/server/api/hello.get.ts | 6 + test/fixtures/nuxt-ignore/tsconfig.json | 3 + test/fixtures/nuxt-ignore/vitest.config.ts | 3 + test/unit/nuxt-ignore.spec.ts | 134 ++++++++++++++++++ 24 files changed, 279 insertions(+), 20 deletions(-) create mode 100644 examples/app-vitest-full/components/__tests__/SomeComponent.nuxt.test.ts create mode 100644 examples/app-vitest-full/pages/about/team.nuxt.spec.ts create mode 100644 src/module/ignore.ts create mode 100644 test/fixtures/nuxt-ignore/app/components/Message.nuxt.spec.ts create mode 100644 test/fixtures/nuxt-ignore/app/components/Message.vue create mode 100644 test/fixtures/nuxt-ignore/app/composables/useMessage.nuxt.spec.ts create mode 100644 test/fixtures/nuxt-ignore/app/composables/useMessage.ts create mode 100644 test/fixtures/nuxt-ignore/app/middleware/logger.global.nuxt.spec.ts create mode 100644 test/fixtures/nuxt-ignore/app/middleware/logger.global.ts create mode 100644 test/fixtures/nuxt-ignore/app/pages/__tests__/index.nuxt.spec.ts create mode 100644 test/fixtures/nuxt-ignore/app/pages/index.nuxt.spec.ts create mode 100644 test/fixtures/nuxt-ignore/app/pages/index.vue create mode 100644 test/fixtures/nuxt-ignore/app/plugins/message.nuxt.test.ts create mode 100644 test/fixtures/nuxt-ignore/app/plugins/message.ts create mode 100644 test/fixtures/nuxt-ignore/nuxt.config.ts create mode 100644 test/fixtures/nuxt-ignore/server/api/hello.get.spec.ts create mode 100644 test/fixtures/nuxt-ignore/server/api/hello.get.ts create mode 100644 test/fixtures/nuxt-ignore/tsconfig.json create mode 100644 test/fixtures/nuxt-ignore/vitest.config.ts create mode 100644 test/unit/nuxt-ignore.spec.ts diff --git a/examples/app-vitest-full/components/__tests__/SomeComponent.nuxt.test.ts b/examples/app-vitest-full/components/__tests__/SomeComponent.nuxt.test.ts new file mode 100644 index 000000000..c61d89b7b --- /dev/null +++ b/examples/app-vitest-full/components/__tests__/SomeComponent.nuxt.test.ts @@ -0,0 +1,8 @@ +import { it, expect } from 'vitest' +import { mountSuspended } from '@nuxt/test-utils/runtime' +import { SomeComponent } from '#components' + +it('mount SomeComponent', async () => { + const wrapper = await mountSuspended(SomeComponent) + expect(wrapper.text()).toContain('This is an auto-imported component') +}) diff --git a/examples/app-vitest-full/pages/about/team.nuxt.spec.ts b/examples/app-vitest-full/pages/about/team.nuxt.spec.ts new file mode 100644 index 000000000..cd61405bf --- /dev/null +++ b/examples/app-vitest-full/pages/about/team.nuxt.spec.ts @@ -0,0 +1,9 @@ +import { it, expect } from 'vitest' +import { mountSuspended } from '@nuxt/test-utils/runtime' + +import Page from './team.vue' + +it('mount team page', async () => { + const wrapper = await mountSuspended(Page) + expect(wrapper.text()).toContain('About team page') +}) diff --git a/examples/app-vitest-full/tests/resolved-files.spec.ts b/examples/app-vitest-full/tests/resolved-files.spec.ts index fda2e17a9..cd1e0a8c0 100644 --- a/examples/app-vitest-full/tests/resolved-files.spec.ts +++ b/examples/app-vitest-full/tests/resolved-files.spec.ts @@ -19,6 +19,6 @@ test('it should include nuxt spec files', { timeout: 30000 }, async ({ onTestFin const nuxtSpecFiles = testFiles.filter(file => file.project.name === 'nuxt') const regularSpecFiles = testFiles.filter(file => file.project.name === 'node') - expect(nuxtSpecFiles.length).toEqual(34) + expect(nuxtSpecFiles.length).toEqual(36) expect(regularSpecFiles.length).toEqual(3) }) diff --git a/src/config.ts b/src/config.ts index 24b9fa0e4..32d3effb8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -58,6 +58,9 @@ async function startNuxtAndGetViteConfig(rootDir = process.cwd(), options: LoadN ssr: false, test: true, modules: ['@nuxt/test-utils/module'], + testUtils: { + _fromVitestConfig: true, + }, }, options.overrides, // vitest always creates a dev server from this config, but the nitro vite environment diff --git a/src/module.ts b/src/module.ts index c99fff4ff..663f22a17 100644 --- a/src/module.ts +++ b/src/module.ts @@ -4,6 +4,7 @@ import type { TestUserConfig as VitestConfig } from 'vitest/config' import { join, relative } from 'pathe' import { isCI } from 'std-env' +import { setupNuxtIgnore } from './module/ignore.ts' import { setupImportMocking } from './module/mock.ts' import { NuxtRootStubPlugin } from './module/plugins/entry.ts' import { runInstallWizard } from './module/install-wizard.ts' @@ -17,6 +18,8 @@ export interface NuxtVitestOptions { startOnBoot?: boolean logToConsole?: boolean vitestConfig?: VitestConfig + /** @internal */ + _fromVitestConfig?: boolean } export default defineNuxtModule({ @@ -33,12 +36,16 @@ export default defineNuxtModule({ await runInstallWizard(nuxt) }, async setup(options, nuxt) { + if (options._fromVitestConfig) { + setupNuxtIgnore(nuxt) + } + if (nuxt.options.test || nuxt.options.dev) { await setupImportMocking(nuxt) } - // inline runtime config the way dev builds do if (nuxt.options.test && !nuxt.options.dev) { + // inline runtime config the way dev builds do nuxt.hook('app:templates', (app) => { const template = app.templates.find(t => t.filename === 'paths.mjs') if (!template?.getContents) { diff --git a/src/module/ignore.ts b/src/module/ignore.ts new file mode 100644 index 000000000..bc57c46f5 --- /dev/null +++ b/src/module/ignore.ts @@ -0,0 +1,17 @@ +import type { Nuxt } from '@nuxt/schema' + +const testIgnorePatterns = [ + /\/__tests__\//, + /\.\{(spec|test)(-d)?(,(spec|test)(-d)?)*\}\.\{([cm]?[tj]sx?)(,([cm]?[tj]sx?))*\}$/, + /\.\{(spec|test)(-d)?(,(spec|test)(-d)?)*\}\.([cm]?[tj]sx?)$/, + /\.(spec|test)(-d)?\.\{([cm]?[tj]sx?)(,([cm]?[tj]sx?))*\}$/, + /\.(spec|test)(-d)?\.([cm]?[tj]sx?)$/, +] as const + +export async function setupNuxtIgnore(nuxt: Nuxt) { + // We want to run Nuxt plugins on test files + const testIgnores = new Set(nuxt.options.ignore.filter(i => !i.startsWith('!') && testIgnorePatterns.some(p => p.test(i)))) + if (testIgnores.size) { + nuxt.options.ignore = nuxt.options.ignore.filter(i => !testIgnores.has(i)) + } +} diff --git a/src/module/mock.ts b/src/module/mock.ts index 47074b978..fe2142474 100644 --- a/src/module/mock.ts +++ b/src/module/mock.ts @@ -1,14 +1,9 @@ import type { Nuxt, NuxtHooks } from '@nuxt/schema' -import { resolveIgnorePatterns } from '@nuxt/kit' import { createMockPlugin } from './plugins/mock.ts' import type { MockPluginContext } from './plugins/mock.ts' import { loadKit } from '../utils.ts' -function isTestPluginFile(src: string) { - return (src.includes('.spec.') || src.includes('.test.')) -} - /** * This module is a macro that transforms `mockNuxtImport()` to `vi.mock()`, * which make it possible to mock Nuxt imports. @@ -40,18 +35,5 @@ export async function setupImportMocking(nuxt: Nuxt) { } }) - // We want to run Nuxt plugins on test files - nuxt.options.ignore = nuxt.options.ignore.filter(i => i !== '**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}') - if (nuxt._ignore) { - for (const pattern of resolveIgnorePatterns('**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}')) { - nuxt._ignore.add(`!${pattern}`) - } - } - - // But do not register test files inside plugins/ as real Nuxt plugins - nuxt.hook('app:resolve', (app) => { - app.plugins = app.plugins.filter(plugin => !isTestPluginFile(plugin.src)) - }) - addVitePlugin(createMockPlugin(ctx).vite()) } diff --git a/test/fixtures/nuxt-ignore/app/components/Message.nuxt.spec.ts b/test/fixtures/nuxt-ignore/app/components/Message.nuxt.spec.ts new file mode 100644 index 000000000..76401b081 --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/components/Message.nuxt.spec.ts @@ -0,0 +1,9 @@ +import { it, expect } from 'vitest' +import { mountSuspended } from '@nuxt/test-utils/runtime' + +import Message from './Message.vue' + +it('should mount', async () => { + const wrapper = await mountSuspended(Message) + expect(wrapper.text()).toContain('Hello Nuxt') +}) diff --git a/test/fixtures/nuxt-ignore/app/components/Message.vue b/test/fixtures/nuxt-ignore/app/components/Message.vue new file mode 100644 index 000000000..5dee486de --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/components/Message.vue @@ -0,0 +1,3 @@ + diff --git a/test/fixtures/nuxt-ignore/app/composables/useMessage.nuxt.spec.ts b/test/fixtures/nuxt-ignore/app/composables/useMessage.nuxt.spec.ts new file mode 100644 index 000000000..75284b653 --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/composables/useMessage.nuxt.spec.ts @@ -0,0 +1,8 @@ +import { it, expect } from 'vitest' +import { useMessage } from './useMessage' + +export function useMessageSpec() {} + +it('useMessage', () => { + expect(useMessage()).toBe('Hello Nuxt') +}) diff --git a/test/fixtures/nuxt-ignore/app/composables/useMessage.ts b/test/fixtures/nuxt-ignore/app/composables/useMessage.ts new file mode 100644 index 000000000..ba6119b5d --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/composables/useMessage.ts @@ -0,0 +1,3 @@ +export function useMessage() { + return 'Hello Nuxt' +} diff --git a/test/fixtures/nuxt-ignore/app/middleware/logger.global.nuxt.spec.ts b/test/fixtures/nuxt-ignore/app/middleware/logger.global.nuxt.spec.ts new file mode 100644 index 000000000..0376165e4 --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/middleware/logger.global.nuxt.spec.ts @@ -0,0 +1,6 @@ +import { it, expect } from 'vitest' +import middleware from './logger.global' + +it('middleware', () => { + expect(middleware(useRoute(), useRoute())).toBeUndefined() +}) diff --git a/test/fixtures/nuxt-ignore/app/middleware/logger.global.ts b/test/fixtures/nuxt-ignore/app/middleware/logger.global.ts new file mode 100644 index 000000000..d23a33f10 --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/middleware/logger.global.ts @@ -0,0 +1,3 @@ +export default defineNuxtRouteMiddleware((to, from) => { + console.log(to.path, from.path) +}) diff --git a/test/fixtures/nuxt-ignore/app/pages/__tests__/index.nuxt.spec.ts b/test/fixtures/nuxt-ignore/app/pages/__tests__/index.nuxt.spec.ts new file mode 100644 index 000000000..78f74f027 --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/pages/__tests__/index.nuxt.spec.ts @@ -0,0 +1,9 @@ +import { it, expect } from 'vitest' +import { mountSuspended } from '@nuxt/test-utils/runtime' + +import Page from '../index.vue' + +it('should mount', async () => { + const wrapper = await mountSuspended(Page) + expect(wrapper.text()).toContain('Index Page') +}) diff --git a/test/fixtures/nuxt-ignore/app/pages/index.nuxt.spec.ts b/test/fixtures/nuxt-ignore/app/pages/index.nuxt.spec.ts new file mode 100644 index 000000000..8bd85e40e --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/pages/index.nuxt.spec.ts @@ -0,0 +1,9 @@ +import { it, expect } from 'vitest' +import { mountSuspended } from '@nuxt/test-utils/runtime' + +import Page from './index.vue' + +it('should mount', async () => { + const wrapper = await mountSuspended(Page) + expect(wrapper.text()).toContain('Index Page') +}) diff --git a/test/fixtures/nuxt-ignore/app/pages/index.vue b/test/fixtures/nuxt-ignore/app/pages/index.vue new file mode 100644 index 000000000..77b1b7339 --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/pages/index.vue @@ -0,0 +1,3 @@ + diff --git a/test/fixtures/nuxt-ignore/app/plugins/message.nuxt.test.ts b/test/fixtures/nuxt-ignore/app/plugins/message.nuxt.test.ts new file mode 100644 index 000000000..3a446638b --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/plugins/message.nuxt.test.ts @@ -0,0 +1,12 @@ +import { it, expect } from 'vitest' +import plugin from './message' + +it('plugin', () => { + expect(plugin(useNuxtApp())).toEqual({ + provide: { + message: 'Hello Nuxt (/)', + }, + }) +}) + +export default defineNuxtPlugin({}) diff --git a/test/fixtures/nuxt-ignore/app/plugins/message.ts b/test/fixtures/nuxt-ignore/app/plugins/message.ts new file mode 100644 index 000000000..bb81cdadc --- /dev/null +++ b/test/fixtures/nuxt-ignore/app/plugins/message.ts @@ -0,0 +1,9 @@ +export default defineNuxtPlugin((nuxt) => { + return { + provide: { + get message() { + return `Hello Nuxt (${nuxt.$router.currentRoute.value.path})` + }, + }, + } +}) diff --git a/test/fixtures/nuxt-ignore/nuxt.config.ts b/test/fixtures/nuxt-ignore/nuxt.config.ts new file mode 100644 index 000000000..dd41d0482 --- /dev/null +++ b/test/fixtures/nuxt-ignore/nuxt.config.ts @@ -0,0 +1,3 @@ +export default defineNuxtConfig({ + modules: ['@nuxt/test-utils/module'], +}) diff --git a/test/fixtures/nuxt-ignore/server/api/hello.get.spec.ts b/test/fixtures/nuxt-ignore/server/api/hello.get.spec.ts new file mode 100644 index 000000000..4793f7bdf --- /dev/null +++ b/test/fixtures/nuxt-ignore/server/api/hello.get.spec.ts @@ -0,0 +1,10 @@ +import { it, expect } from 'vitest' +import { createApp, toWebHandler } from 'h3' +import handler from './hello.get' + +it('/api/hello', async () => { + const api = toWebHandler(createApp().use('/', handler)) + const req = new Request(new URL('http://localhost/?name=Nuxt')) + const res = await api(req).then(r => r.json()) + expect(res).toEqual({ message: 'Hello Nuxt!' }) +}) diff --git a/test/fixtures/nuxt-ignore/server/api/hello.get.ts b/test/fixtures/nuxt-ignore/server/api/hello.get.ts new file mode 100644 index 000000000..613a53671 --- /dev/null +++ b/test/fixtures/nuxt-ignore/server/api/hello.get.ts @@ -0,0 +1,6 @@ +import { defineEventHandler, getQuery } from 'h3' + +export default defineEventHandler((event) => { + const name = getQuery(event).name + return { message: `Hello ${name}!` } +}) diff --git a/test/fixtures/nuxt-ignore/tsconfig.json b/test/fixtures/nuxt-ignore/tsconfig.json new file mode 100644 index 000000000..3921d7e1b --- /dev/null +++ b/test/fixtures/nuxt-ignore/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../.nuxt/tsconfig.json" +} diff --git a/test/fixtures/nuxt-ignore/vitest.config.ts b/test/fixtures/nuxt-ignore/vitest.config.ts new file mode 100644 index 000000000..735b44d88 --- /dev/null +++ b/test/fixtures/nuxt-ignore/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineVitestConfig } from '@nuxt/test-utils/config' + +export default defineVitestConfig({}) diff --git a/test/unit/nuxt-ignore.spec.ts b/test/unit/nuxt-ignore.spec.ts new file mode 100644 index 000000000..faace0b7b --- /dev/null +++ b/test/unit/nuxt-ignore.spec.ts @@ -0,0 +1,134 @@ +import { relative, join } from 'pathe' +import { fileURLToPath } from 'node:url' +import { rm } from 'node:fs/promises' +import { afterAll, describe, expect, it } from 'vitest' +import { loadNuxt, buildNuxt, logger } from '@nuxt/kit' +import { setupNuxtIgnore } from '../../src/module/ignore' + +const TEST_TIMEOUT = process.env.CI ? 60_000 : 30_000 + +describe('nuxtignore', () => { + const fixtureDir = fileURLToPath(new URL('../fixtures/nuxt-ignore', import.meta.url)) + + afterAll(async () => { + await rm(join(fixtureDir, '.nuxt'), { recursive: true, force: true }) + }) + + it('should remove test file pattern from ignore', async () => { + const nuxt = await loadNuxt({ + cwd: fixtureDir, + }) + + nuxt.options.ignore = [ + '**/*.stories.{js,cts,mts,ts,jsx,tsx}', + '**/*.d.{cts,mts,ts}', + '!**/pages/hide/__tests__/**', + '!**/pages/**/this-is-page.{spec,test}.{js,cts,mts,ts,jsx,tsx}', + '!**/components/**/this-is-components.spec.ts', + '**/*.latest.ts', + '**/*.testing.ts', + '**/*.special.ts', + '**/*.inspect.ts', + '**/*.spec.tsxp', + '**/*.{latest,other}.ts', + '**/__tests__/**/*', + '**/*.spec.js', + '**/*.spec.mjs', + '**/*.spec.cjs', + '**/*.spec.jsx', + '**/*.spec.ts', + '**/*.spec.mts', + '**/*.spec.cts', + '**/*.spec.tsx', + '**/*.test.js', + '**/*.test.mjs', + '**/*.test.cjs', + '**/*.test.jsx', + '**/*.test.ts', + '**/*.test.mts', + '**/*.test.cts', + '**/*.test.tsx', + '**/*.spec-d.ts', + '**/*.test-d.ts', + '**/*.{spec,test}.ts', + '**/*.{test-d,spec-d}.ts', + '**/*.spec.{js,cts,mts,ts,jsx,tsx}', + '**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}', + '**/*.{spec,test,spec-d,test-d}.{js,cts,mts,ts,jsx,tsx}', + ] + setupNuxtIgnore(nuxt) + expect(nuxt.options.ignore).toEqual([ + '**/*.stories.{js,cts,mts,ts,jsx,tsx}', + '**/*.d.{cts,mts,ts}', + '!**/pages/hide/__tests__/**', + '!**/pages/**/this-is-page.{spec,test}.{js,cts,mts,ts,jsx,tsx}', + '!**/components/**/this-is-components.spec.ts', + '**/*.latest.ts', + '**/*.testing.ts', + '**/*.special.ts', + '**/*.inspect.ts', + '**/*.spec.tsxp', + '**/*.{latest,other}.ts', + ]) + }) + + it('should ignore test files in runtime dirs', async () => { + const nuxt = await loadNuxt({ cwd: fixtureDir }) + + const normalize = (paths: string[]) => paths + .map(p => relative(fixtureDir, p)) + .filter(p => !p.includes('node_modules/') && !p.includes('../') && !p.includes('.nuxt/')) + .toSorted() + + const scans = { + pages: [] as string[], + imports: [] as string[], + components: [] as string[], + plugins: [] as string[], + middlewares: [] as string[], + serverHandlers: [] as string[], + } + + nuxt.addHooks({ + 'pages:extend'(pages) { + scans.pages = normalize(pages.flatMap(p => p.file!)) + }, + 'imports:extend'(imports) { + scans.imports = normalize(imports.map(p => p.from)) + }, + 'components:extend'(components) { + scans.components = normalize(components.map(p => p.filePath)) + }, + 'app:resolve': (app) => { + scans.plugins = normalize(app.plugins.map(p => p.src)) + scans.middlewares = normalize(app.middleware.map(p => p.path)) + }, + 'nitro:build:before'(nitro) { + scans.serverHandlers = normalize(nitro.scannedHandlers.map(p => p.handler)) + }, + }) + + await buildNuxt(nuxt).finally(() => logger.restoreAll()) + + expect(scans).toEqual({ + pages: [ + 'app/pages/index.vue', + ], + imports: [ + 'app/composables/useMessage.ts', + ], + components: [ + 'app/components/Message.vue', + ], + plugins: [ + 'app/plugins/message.ts', + ], + middlewares: [ + 'app/middleware/logger.global.ts', + ], + serverHandlers: [ + 'server/api/hello.get.ts', + ], + } satisfies typeof scans) + }, TEST_TIMEOUT) +}) From 7cafc1972d0f9b8e7d5c0630e35573e04a146d3b Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Sun, 4 Oct 2026 09:22:29 +0900 Subject: [PATCH 02/11] move other vitest-only configurations into the same conditional block --- src/module.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/module.ts b/src/module.ts index 663f22a17..77730bba5 100644 --- a/src/module.ts +++ b/src/module.ts @@ -36,15 +36,15 @@ export default defineNuxtModule({ await runInstallWizard(nuxt) }, async setup(options, nuxt) { + const { addTypeTemplate, addVitePlugin } = await loadKit(nuxt.options.rootDir) + + const resolver = createResolver(import.meta.url) + + // Setup for Vitest if (options._fromVitestConfig) { setupNuxtIgnore(nuxt) - } - - if (nuxt.options.test || nuxt.options.dev) { await setupImportMocking(nuxt) - } - if (nuxt.options.test && !nuxt.options.dev) { // inline runtime config the way dev builds do nuxt.hook('app:templates', (app) => { const template = app.templates.find(t => t.filename === 'paths.mjs') @@ -60,12 +60,7 @@ export default defineNuxtModule({ .replace(/const getAppConfig = \(\) => useRuntimeConfig\(\)\.app/, () => `const getAppConfig = () => (${inlineAppConfig})`) } }) - } - const { addTypeTemplate, addVitePlugin } = await loadKit(nuxt.options.rootDir) - - const resolver = createResolver(import.meta.url) - if (nuxt.options.test || nuxt.options.dev) { addVitePlugin(NuxtRootStubPlugin({ entry: await resolvePath('#app/entry', { alias: nuxt.options.alias }), rootStubPath: await resolvePath(resolver.resolve('./runtime/nuxt-root')), From 7eb81a355d838d675abfa3eca27182dee8897655 Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Sun, 4 Oct 2026 13:32:24 +0900 Subject: [PATCH 03/11] restore ignore plugins --- src/module/ignore.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/module/ignore.ts b/src/module/ignore.ts index bc57c46f5..545caf25e 100644 --- a/src/module/ignore.ts +++ b/src/module/ignore.ts @@ -2,16 +2,29 @@ import type { Nuxt } from '@nuxt/schema' const testIgnorePatterns = [ /\/__tests__\//, - /\.\{(spec|test)(-d)?(,(spec|test)(-d)?)*\}\.\{([cm]?[tj]sx?)(,([cm]?[tj]sx?))*\}$/, - /\.\{(spec|test)(-d)?(,(spec|test)(-d)?)*\}\.([cm]?[tj]sx?)$/, - /\.(spec|test)(-d)?\.\{([cm]?[tj]sx?)(,([cm]?[tj]sx?))*\}$/, - /\.(spec|test)(-d)?\.([cm]?[tj]sx?)$/, + /\.\{(?:spec|test)(?:-d)?(?:,(spec|test)(?:-d)?)*\}\.\{[cm]?[tj]sx?(?:,[cm]?[tj]sx?)*\}$/, + /\.\{(?:spec|test)(?:-d)?(?:,(spec|test)(?:-d)?)*\}\.[cm]?[tj]sx?$/, + /\.(?:spec|test)(?:-d)?\.\{[cm]?[tj]sx?(?:,[cm]?[tj]sx?)*\}$/, + /\.(?:spec|test)(?:-d)?\.[cm]?[tj]sx?$/, ] as const +function isTestFileIgnorePattern(src: string) { + return !src.startsWith('!') && testIgnorePatterns.some(p => p.test(src)) +} + +function isTestPluginFile(src: string) { + return /\.(?:spec|test)(?:-d)?\.[cm]?[tj]sx?$/.test(src) +} + export async function setupNuxtIgnore(nuxt: Nuxt) { // We want to run Nuxt plugins on test files - const testIgnores = new Set(nuxt.options.ignore.filter(i => !i.startsWith('!') && testIgnorePatterns.some(p => p.test(i)))) + const testIgnores = new Set(nuxt.options.ignore.filter(isTestFileIgnorePattern)) if (testIgnores.size) { nuxt.options.ignore = nuxt.options.ignore.filter(i => !testIgnores.has(i)) } + + // But do not register test files inside plugins/ as real Nuxt plugins + nuxt.hook('app:resolve', (app) => { + app.plugins = app.plugins.filter(plugin => !isTestPluginFile(plugin.src)) + }) } From d797aaf2b7b0470b71d7cbd80f236d92aa357979 Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Sun, 4 Oct 2026 13:33:37 +0900 Subject: [PATCH 04/11] refactor tests --- .../app-vitest-co-location/app/app.config.ts | 3 + .../app/components/Message.nuxt.spec.ts | 12 ++ .../app/components/Message.vue | 7 + .../__tests__/Message.browser.spec.ts | 10 ++ .../__tests__}/Message.nuxt.spec.ts | 2 +- .../app/composables/useMessage.nuxt.spec.ts | 0 .../app/composables/useMessage.ts | 0 .../app/middleware/logger.global.nuxt.spec.ts | 0 .../app/middleware/logger.global.ts | 0 .../app/pages/__tests__/index.nuxt.spec.ts | 0 .../app/pages/index.e2e.spec.ts | 20 +++ .../app/pages/index.nuxt.spec.ts | 1 + .../app/pages/index.vue | 10 ++ .../app/plugins/message.nuxt.test.ts | 2 - .../app/plugins/message.ts | 0 .../app-vitest-co-location/nuxt.config.ts | 11 ++ examples/app-vitest-co-location/package.json | 26 +++ .../server/api/hello.get.e2e.spec.ts | 12 ++ .../server/api/hello.get.ts | 0 .../server/api/hello.get.unit.spec.ts | 0 .../test/nuxt-ignore.unit.test.ts | 70 ++++++++ examples/app-vitest-co-location/tsconfig.json | 17 ++ .../app-vitest-co-location/vitest.config.ts | 49 ++++++ pnpm-lock.yaml | 155 +++++++++++------- .../nuxt-ignore/app/components/Message.vue | 3 - test/fixtures/nuxt-ignore/app/pages/index.vue | 3 - test/fixtures/nuxt-ignore/nuxt.config.ts | 3 - test/fixtures/nuxt-ignore/tsconfig.json | 3 - test/fixtures/nuxt-ignore/vitest.config.ts | 3 - test/unit/nuxt-ignore.spec.ts | 110 +++++-------- 30 files changed, 379 insertions(+), 153 deletions(-) create mode 100644 examples/app-vitest-co-location/app/app.config.ts create mode 100644 examples/app-vitest-co-location/app/components/Message.nuxt.spec.ts create mode 100644 examples/app-vitest-co-location/app/components/Message.vue create mode 100644 examples/app-vitest-co-location/app/components/__tests__/Message.browser.spec.ts rename {test/fixtures/nuxt-ignore/app/components => examples/app-vitest-co-location/app/components/__tests__}/Message.nuxt.spec.ts (85%) rename {test/fixtures/nuxt-ignore => examples/app-vitest-co-location}/app/composables/useMessage.nuxt.spec.ts (100%) rename {test/fixtures/nuxt-ignore => examples/app-vitest-co-location}/app/composables/useMessage.ts (100%) rename {test/fixtures/nuxt-ignore => examples/app-vitest-co-location}/app/middleware/logger.global.nuxt.spec.ts (100%) rename {test/fixtures/nuxt-ignore => examples/app-vitest-co-location}/app/middleware/logger.global.ts (100%) rename {test/fixtures/nuxt-ignore => examples/app-vitest-co-location}/app/pages/__tests__/index.nuxt.spec.ts (100%) create mode 100644 examples/app-vitest-co-location/app/pages/index.e2e.spec.ts rename {test/fixtures/nuxt-ignore => examples/app-vitest-co-location}/app/pages/index.nuxt.spec.ts (84%) create mode 100644 examples/app-vitest-co-location/app/pages/index.vue rename {test/fixtures/nuxt-ignore => examples/app-vitest-co-location}/app/plugins/message.nuxt.test.ts (83%) rename {test/fixtures/nuxt-ignore => examples/app-vitest-co-location}/app/plugins/message.ts (100%) create mode 100644 examples/app-vitest-co-location/nuxt.config.ts create mode 100644 examples/app-vitest-co-location/package.json create mode 100644 examples/app-vitest-co-location/server/api/hello.get.e2e.spec.ts rename {test/fixtures/nuxt-ignore => examples/app-vitest-co-location}/server/api/hello.get.ts (100%) rename test/fixtures/nuxt-ignore/server/api/hello.get.spec.ts => examples/app-vitest-co-location/server/api/hello.get.unit.spec.ts (100%) create mode 100644 examples/app-vitest-co-location/test/nuxt-ignore.unit.test.ts create mode 100644 examples/app-vitest-co-location/tsconfig.json create mode 100644 examples/app-vitest-co-location/vitest.config.ts delete mode 100644 test/fixtures/nuxt-ignore/app/components/Message.vue delete mode 100644 test/fixtures/nuxt-ignore/app/pages/index.vue delete mode 100644 test/fixtures/nuxt-ignore/nuxt.config.ts delete mode 100644 test/fixtures/nuxt-ignore/tsconfig.json delete mode 100644 test/fixtures/nuxt-ignore/vitest.config.ts diff --git a/examples/app-vitest-co-location/app/app.config.ts b/examples/app-vitest-co-location/app/app.config.ts new file mode 100644 index 000000000..b4313b057 --- /dev/null +++ b/examples/app-vitest-co-location/app/app.config.ts @@ -0,0 +1,3 @@ +export default defineAppConfig({ + foo: 'bar', +}) diff --git a/examples/app-vitest-co-location/app/components/Message.nuxt.spec.ts b/examples/app-vitest-co-location/app/components/Message.nuxt.spec.ts new file mode 100644 index 000000000..14a120f32 --- /dev/null +++ b/examples/app-vitest-co-location/app/components/Message.nuxt.spec.ts @@ -0,0 +1,12 @@ +import { it, expect, vi } from 'vitest' +import { mountSuspended, mockNuxtImport } from '@nuxt/test-utils/runtime' + +import Message from './Message.vue' + +mockNuxtImport(useMessage, vi.fn) + +it('should mount', async () => { + vi.mocked(useMessage).mockImplementationOnce(() => 'Mocked!') + const wrapper = await mountSuspended(Message) + expect(wrapper.text()).toContain('Mocked!') +}) diff --git a/examples/app-vitest-co-location/app/components/Message.vue b/examples/app-vitest-co-location/app/components/Message.vue new file mode 100644 index 000000000..bef97565a --- /dev/null +++ b/examples/app-vitest-co-location/app/components/Message.vue @@ -0,0 +1,7 @@ + + + diff --git a/examples/app-vitest-co-location/app/components/__tests__/Message.browser.spec.ts b/examples/app-vitest-co-location/app/components/__tests__/Message.browser.spec.ts new file mode 100644 index 000000000..d794a3836 --- /dev/null +++ b/examples/app-vitest-co-location/app/components/__tests__/Message.browser.spec.ts @@ -0,0 +1,10 @@ +import { it, expect } from 'vitest' +import { render } from '@nuxt/test-utils/browser' + +import { Message } from '#components' + +it('should mount', async () => { + const screen = await render(Message) + const message = screen.getByRole('heading') + await expect.element(message).toMatchTextContent(/Hello Nuxt/) +}) diff --git a/test/fixtures/nuxt-ignore/app/components/Message.nuxt.spec.ts b/examples/app-vitest-co-location/app/components/__tests__/Message.nuxt.spec.ts similarity index 85% rename from test/fixtures/nuxt-ignore/app/components/Message.nuxt.spec.ts rename to examples/app-vitest-co-location/app/components/__tests__/Message.nuxt.spec.ts index 76401b081..86d6f8d4a 100644 --- a/test/fixtures/nuxt-ignore/app/components/Message.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/components/__tests__/Message.nuxt.spec.ts @@ -1,7 +1,7 @@ import { it, expect } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' -import Message from './Message.vue' +import { Message } from '#components' it('should mount', async () => { const wrapper = await mountSuspended(Message) diff --git a/test/fixtures/nuxt-ignore/app/composables/useMessage.nuxt.spec.ts b/examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts similarity index 100% rename from test/fixtures/nuxt-ignore/app/composables/useMessage.nuxt.spec.ts rename to examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts diff --git a/test/fixtures/nuxt-ignore/app/composables/useMessage.ts b/examples/app-vitest-co-location/app/composables/useMessage.ts similarity index 100% rename from test/fixtures/nuxt-ignore/app/composables/useMessage.ts rename to examples/app-vitest-co-location/app/composables/useMessage.ts diff --git a/test/fixtures/nuxt-ignore/app/middleware/logger.global.nuxt.spec.ts b/examples/app-vitest-co-location/app/middleware/logger.global.nuxt.spec.ts similarity index 100% rename from test/fixtures/nuxt-ignore/app/middleware/logger.global.nuxt.spec.ts rename to examples/app-vitest-co-location/app/middleware/logger.global.nuxt.spec.ts diff --git a/test/fixtures/nuxt-ignore/app/middleware/logger.global.ts b/examples/app-vitest-co-location/app/middleware/logger.global.ts similarity index 100% rename from test/fixtures/nuxt-ignore/app/middleware/logger.global.ts rename to examples/app-vitest-co-location/app/middleware/logger.global.ts diff --git a/test/fixtures/nuxt-ignore/app/pages/__tests__/index.nuxt.spec.ts b/examples/app-vitest-co-location/app/pages/__tests__/index.nuxt.spec.ts similarity index 100% rename from test/fixtures/nuxt-ignore/app/pages/__tests__/index.nuxt.spec.ts rename to examples/app-vitest-co-location/app/pages/__tests__/index.nuxt.spec.ts diff --git a/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts b/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts new file mode 100644 index 000000000..91ecfaade --- /dev/null +++ b/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts @@ -0,0 +1,20 @@ +import { fileURLToPath } from 'node:url' +import { it, expect } from 'vitest' +import { createPage, setup } from '@nuxt/test-utils/e2e' + +await setup({ + rootDir: fileURLToPath(new URL('../../', import.meta.url)), + browser: true, +}) + +it('runs e2e browser test', { timeout: 20000 }, async () => { + const page = await createPage('/') + + const title = page.getByRole('heading', { level: 1 }) + await expect(title.textContent()).resolves.toContain('Index Page') + + const paragraph = page.getByRole('paragraph') + await expect(paragraph.textContent()).resolves.toContain('foo: bar') + + await page.close() +}) diff --git a/test/fixtures/nuxt-ignore/app/pages/index.nuxt.spec.ts b/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts similarity index 84% rename from test/fixtures/nuxt-ignore/app/pages/index.nuxt.spec.ts rename to examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts index 8bd85e40e..7de0e3037 100644 --- a/test/fixtures/nuxt-ignore/app/pages/index.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts @@ -6,4 +6,5 @@ import Page from './index.vue' it('should mount', async () => { const wrapper = await mountSuspended(Page) expect(wrapper.text()).toContain('Index Page') + expect(wrapper.text()).toContain('foo: bar') }) diff --git a/examples/app-vitest-co-location/app/pages/index.vue b/examples/app-vitest-co-location/app/pages/index.vue new file mode 100644 index 000000000..b2a45c21c --- /dev/null +++ b/examples/app-vitest-co-location/app/pages/index.vue @@ -0,0 +1,10 @@ + + + diff --git a/test/fixtures/nuxt-ignore/app/plugins/message.nuxt.test.ts b/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts similarity index 83% rename from test/fixtures/nuxt-ignore/app/plugins/message.nuxt.test.ts rename to examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts index 3a446638b..290cd3c00 100644 --- a/test/fixtures/nuxt-ignore/app/plugins/message.nuxt.test.ts +++ b/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts @@ -8,5 +8,3 @@ it('plugin', () => { }, }) }) - -export default defineNuxtPlugin({}) diff --git a/test/fixtures/nuxt-ignore/app/plugins/message.ts b/examples/app-vitest-co-location/app/plugins/message.ts similarity index 100% rename from test/fixtures/nuxt-ignore/app/plugins/message.ts rename to examples/app-vitest-co-location/app/plugins/message.ts diff --git a/examples/app-vitest-co-location/nuxt.config.ts b/examples/app-vitest-co-location/nuxt.config.ts new file mode 100644 index 000000000..423d0bd0a --- /dev/null +++ b/examples/app-vitest-co-location/nuxt.config.ts @@ -0,0 +1,11 @@ +// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + modules: ['@nuxt/test-utils/module'], + devtools: { enabled: true }, + compatibilityDate: '2024-04-03', + typescript: { + tsConfig: { + include: ['../test/nuxt-ignore.unit.test.ts'], + }, + }, +}) diff --git a/examples/app-vitest-co-location/package.json b/examples/app-vitest-co-location/package.json new file mode 100644 index 000000000..fc75fefa9 --- /dev/null +++ b/examples/app-vitest-co-location/package.json @@ -0,0 +1,26 @@ +{ + "name": "example-app-vitest-co-location", + "private": true, + "type": "module", + "scripts": { + "build": "nuxt build", + "dev": "nuxt dev", + "generate": "nuxt generate", + "preview": "nuxt preview", + "postinstall": "nuxt prepare", + "test": "vitest run" + }, + "dependencies": { + "nuxt": "^4.5.2" + }, + "devDependencies": { + "@nuxt/test-utils": "latest", + "@vitest/browser-playwright": "5.0.2", + "@vue/test-utils": "2.5.1", + "happy-dom": "20.14.5", + "playwright-core": "1.63.0", + "tinyglobby": "0.2.17", + "typescript": "6.0.3", + "vitest": "5.0.2" + } +} diff --git a/examples/app-vitest-co-location/server/api/hello.get.e2e.spec.ts b/examples/app-vitest-co-location/server/api/hello.get.e2e.spec.ts new file mode 100644 index 000000000..8e6af8676 --- /dev/null +++ b/examples/app-vitest-co-location/server/api/hello.get.e2e.spec.ts @@ -0,0 +1,12 @@ +import { fileURLToPath } from 'node:url' +import { it, expect } from 'vitest' +import { setup, $fetch } from '@nuxt/test-utils/e2e' + +await setup({ + rootDir: fileURLToPath(new URL('../../', import.meta.url)), +}) + +it('runs e2e api test', { timeout: 20000 }, async () => { + const res = await $fetch('/api/hello', { query: { name: 'Nuxt' } }) + expect(res).toMatchObject({ message: 'Hello Nuxt!' }) +}) diff --git a/test/fixtures/nuxt-ignore/server/api/hello.get.ts b/examples/app-vitest-co-location/server/api/hello.get.ts similarity index 100% rename from test/fixtures/nuxt-ignore/server/api/hello.get.ts rename to examples/app-vitest-co-location/server/api/hello.get.ts diff --git a/test/fixtures/nuxt-ignore/server/api/hello.get.spec.ts b/examples/app-vitest-co-location/server/api/hello.get.unit.spec.ts similarity index 100% rename from test/fixtures/nuxt-ignore/server/api/hello.get.spec.ts rename to examples/app-vitest-co-location/server/api/hello.get.unit.spec.ts diff --git a/examples/app-vitest-co-location/test/nuxt-ignore.unit.test.ts b/examples/app-vitest-co-location/test/nuxt-ignore.unit.test.ts new file mode 100644 index 000000000..8f6bf54f3 --- /dev/null +++ b/examples/app-vitest-co-location/test/nuxt-ignore.unit.test.ts @@ -0,0 +1,70 @@ +import { relative } from 'pathe' +import { fileURLToPath } from 'node:url' +import { describe, expect, it } from 'vitest' +import { loadNuxt, buildNuxt, logger } from '@nuxt/kit' + +const TEST_TIMEOUT = process.env.CI ? 60_000 : 30_000 + +describe('nuxtignore', () => { + const fixtureDir = fileURLToPath(new URL('../', import.meta.url)) + + it('should ignore test files in runtime dirs', async () => { + const nuxt = await loadNuxt({ cwd: fixtureDir }) + + const normalize = (paths: string[]) => paths + .map(p => relative(fixtureDir, p)) + .filter(p => !p.includes('node_modules/') && !p.includes('../') && !p.includes('.nuxt/')) + .toSorted() + + const scans = { + pages: [] as string[], + imports: [] as string[], + components: [] as string[], + plugins: [] as string[], + middlewares: [] as string[], + serverHandlers: [] as string[], + } + + nuxt.addHooks({ + 'pages:extend'(pages) { + scans.pages = normalize(pages.flatMap(p => p.file!)) + }, + 'imports:extend'(imports) { + scans.imports = normalize(imports.map(p => p.from)) + }, + 'components:extend'(components) { + scans.components = normalize(components.map(p => p.filePath)) + }, + 'app:resolve': (app) => { + scans.plugins = normalize(app.plugins.map(p => p.src)) + scans.middlewares = normalize(app.middleware.map(p => p.path)) + }, + 'nitro:build:before'(nitro) { + scans.serverHandlers = normalize(nitro.scannedHandlers.map(p => p.handler)) + }, + }) + + await buildNuxt(nuxt).finally(() => logger.restoreAll()) + + expect(scans).toEqual({ + pages: [ + 'app/pages/index.vue', + ], + imports: [ + 'app/composables/useMessage.ts', + ], + components: [ + 'app/components/Message.vue', + ], + plugins: [ + 'app/plugins/message.ts', + ], + middlewares: [ + 'app/middleware/logger.global.ts', + ], + serverHandlers: [ + 'server/api/hello.get.ts', + ], + } satisfies typeof scans) + }, TEST_TIMEOUT) +}) diff --git a/examples/app-vitest-co-location/tsconfig.json b/examples/app-vitest-co-location/tsconfig.json new file mode 100644 index 000000000..6ae5970c7 --- /dev/null +++ b/examples/app-vitest-co-location/tsconfig.json @@ -0,0 +1,17 @@ +{ + "files": [], + "references": [ + { + "path": "./.nuxt/tsconfig.app.json" + }, + { + "path": "./.nuxt/tsconfig.server.json" + }, + { + "path": "./.nuxt/tsconfig.shared.json" + }, + { + "path": "./.nuxt/tsconfig.node.json" + } + ] +} diff --git a/examples/app-vitest-co-location/vitest.config.ts b/examples/app-vitest-co-location/vitest.config.ts new file mode 100644 index 000000000..41a4acbe2 --- /dev/null +++ b/examples/app-vitest-co-location/vitest.config.ts @@ -0,0 +1,49 @@ +import { defineConfig } from 'vitest/config' +import { defineVitestProject } from '@nuxt/test-utils/config' +import { playwright } from '@vitest/browser-playwright' + +export default defineConfig({ + test: { + projects: [ + { + test: { + name: 'unit', + environment: 'node', + include: ['**/*.unit.{test,spec}.ts'], + }, + }, + { + test: { + name: 'e2e', + environment: 'node', + include: ['**/*.e2e.{test,spec}.ts'], + }, + }, + await defineVitestProject({ + test: { + name: 'nuxt', + environment: 'nuxt', + include: ['**/*.nuxt.{test,spec}.ts'], + }, + }), + await defineVitestProject({ + test: { + name: 'browser', + environment: 'nuxt', + browser: { + enabled: true, + provider: playwright(), + instances: [{ browser: 'chromium' }], + }, + include: ['**/*.browser.{test,spec}.ts'], + setupFiles: ['@nuxt/test-utils/browser'], + }, + }), + ], + onConsoleLog(log) { + if (log.includes(' is an experimental feature')) { + return false + } + }, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 227c18c1e..a49e5b8a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -422,6 +422,37 @@ importers: specifier: 3.1.0 version: 3.1.0(@vue/compiler-dom@3.5.43)(@vue/server-renderer@3.5.43)(vitest@5.0.2)(vue@3.5.43(typescript@6.0.3)) + examples/app-vitest-co-location: + dependencies: + nuxt: + specifier: ^4.5.2 + version: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@oxc-project/types@0.152.0)(@parcel/watcher@2.6.0)(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(@vue/compiler-sfc@3.5.43)(better-sqlite3@13.0.3)(cac@7.0.0)(commander@15.0.0)(db0@0.4.1)(esbuild@0.28.2)(eslint@10.11.0(jiti@2.7.0)(supports-color@11.0.0))(idb-keyval@6.3.0)(ioredis@5.11.1(supports-color@11.0.0))(lightningcss@1.33.0)(magicast@0.5.5)(optionator@0.9.4)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5))(rollup@4.63.5)(srvx@1.0.5)(supports-color@11.0.0)(terser@5.51.2)(typescript@6.0.3)(vite@8.3.1)(vue-tsc@3.3.11(typescript@6.0.3))(yaml@2.9.1) + devDependencies: + '@nuxt/test-utils': + specifier: workspace:* + version: link:../.. + '@vitest/browser-playwright': + specifier: 5.0.2 + version: 5.0.2(playwright@1.63.0)(vite@8.3.1)(vitest@5.0.2) + '@vue/test-utils': + specifier: 2.5.1 + version: 2.5.1(@vue/compiler-dom@3.5.43)(@vue/server-renderer@3.5.43)(vue@3.5.43(typescript@6.0.3)) + happy-dom: + specifier: 20.14.5 + version: 20.14.5 + playwright-core: + specifier: 1.63.0 + version: 1.63.0 + tinyglobby: + specifier: 0.2.17 + version: 0.2.17 + typescript: + specifier: 6.0.3 + version: 6.0.3 + vitest: + specifier: 5.0.2 + version: 5.0.2(@types/node@24.19.0)(@vitest/browser-playwright@5.0.2)(@vitest/ui@5.0.2)(happy-dom@20.14.5)(jsdom@30.1.1)(vite@8.3.1) + examples/app-vitest-full: dependencies: idb-keyval: @@ -577,7 +608,7 @@ importers: version: 3.0.260903-beta nuxt: specifier: npm:nuxt-nightly@5x - version: nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1) + version: nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c) devDependencies: '@nuxt/test-utils': specifier: workspace:* @@ -602,7 +633,7 @@ importers: dependencies: nuxt: specifier: npm:nuxt-nightly@5x - version: nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2) + version: nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd) devDependencies: '@nuxt/test-utils': specifier: workspace:* @@ -2108,9 +2139,9 @@ packages: resolution: {integrity: sha512-llZkSzeTsimFx64U+ThT2xQM2uEce8GIQUYvxgbB6ZFvBhV2LP9LeJJb3HT+syG0uCFLsTCHjV9SfC0WNU1vtA==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} - '@nuxt/cli-nightly@4.0.0-20260929-142719-eabcb01': - resolution: {integrity: sha512-u0/i/uq7k1OBPeg1lOBdmvq0SFNvT/gbjOKe/NvklETFRpN0SmAWBuFIOxhr2eSzinIpw8OyUPjiv4UPL05kqw==} - engines: {node: ^22.19.0 || ^24.11.0 || >=26.0.0} + '@nuxt/cli-nightly@4.0.0-20261003-160317-f140924f': + resolution: {integrity: sha512-kYNtlyg+wE0x8klSGcFZMFARpdUbReRy5TTXWysYJI1Cfz/rXLP0zdSVu8MMtShR1MBuRAlGhrxwUdrbiPPudA==} + engines: {node: ^22.21.0 || ^24.11.0 || >=26.0.0} hasBin: true peerDependencies: '@nuxt/docs': ^4.0.0 @@ -2265,11 +2296,11 @@ packages: '@nuxt/icon@2.5.1': resolution: {integrity: sha512-zBP72Po7BS+tXzoeDRA/Y9TTY77OIcNCyzfgXsOmep7zZShTEoe4p1WZBXce/9oJDK3YXmbYC3ILQ7XvqM4/XA==} - '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62': - resolution: {integrity: sha512-1yc6/lMTISyAoKvpZvcLenBxTX5eTPMSMvUZxQB3TYLycPxta0QxpuIUXYaVR09zjv65+Ixl0dHLrE9j80fjhg==} + '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4': + resolution: {integrity: sha512-jJHmCeXv3qGX2eb0poiuou7iQLgiiwNrCeIH29Zst3oOAh/4Ff1guCO0uiGyxbLm5yepmIktxr30UZG1Inciqw==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - '@nuxt/schema': npm:@nuxt/schema-nightly@5.0.0-29847385.3fde4d62 + '@nuxt/schema': npm:@nuxt/schema-nightly@5.0.0-29850714.87a4b2c4 confbox: '>=0.1.0' dotenv: '*' giget: '>=3.1.0' @@ -2307,14 +2338,14 @@ packages: '@nuxt/cli': ^3.37.0 typescript: ^5.9.3 - '@nuxt/nitro-server-nightly@5.0.0-29847385.3fde4d62': - resolution: {integrity: sha512-3qQv3NoxhQydYrzj2CMyUPBZlEQtEXilsycDXZHS352upwM4mOmPiMU0Fl/RgHrVmvGT1xguO0e4ecLOxYyxaw==} + '@nuxt/nitro-server-nightly@5.0.0-29850714.87a4b2c4': + resolution: {integrity: sha512-TIO1tPGLZtN2biYVWxpbvk9sTf41xb8Gaged3lkjpQSWKuGDScq1GDV6K+Eu1FVTtsa/4p9/w9+EW76x8jIj+g==} engines: {node: ^22.19.0 || ^24.11.0 || >=26.0.0} peerDependencies: '@babel/plugin-proposal-decorators': ^7.25.0 || ^8.0.0 '@babel/plugin-syntax-typescript': ^7.25.0 || ^8.0.0 '@rollup/plugin-babel': ^6.0.0 || ^7.0.0 - nuxt: npm:nuxt-nightly@5.0.0-29847385.3fde4d62 + nuxt: npm:nuxt-nightly@5.0.0-29850714.87a4b2c4 peerDependenciesMeta: '@babel/plugin-proposal-decorators': optional: true @@ -2412,8 +2443,8 @@ packages: zod: optional: true - '@nuxt/vite-builder-nightly@5.0.0-29847385.3fde4d62': - resolution: {integrity: sha512-DXnDGrud1h0aG7ySHcRwfFY48FAFPdInslio5J3wseSZJSeCYoBsCW9TLfUSyVAD0Jh10Ilrjilq3HXJDA2pHA==} + '@nuxt/vite-builder-nightly@5.0.0-29850714.87a4b2c4': + resolution: {integrity: sha512-7k23RCCwUBOGpZ9UiFSEERZXSToHPG13H4rA/p6Zgo3dv4/DrJHieXFqbXHHhU3lf1ukyqhtTR/+UXvI++bKdw==} engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} peerDependencies: '@babel/plugin-proposal-decorators': ^7.25.0 || ^8.0.0 @@ -2421,7 +2452,7 @@ packages: '@vitejs/plugin-vue-jsx': ^5.1.5 autoprefixer: ^10.4.27 cssnano: ^7.1.3 || ^8.0.0 || ^9.0.0 - nuxt: npm:nuxt-nightly@5.0.0-29847385.3fde4d62 + nuxt: npm:nuxt-nightly@5.0.0-29850714.87a4b2c4 rollup-plugin-visualizer: ^6.0.0 || ^7.0.1 vue: ^3.5.43 peerDependenciesMeta: @@ -2458,11 +2489,11 @@ packages: rollup-plugin-visualizer: optional: true - '@nuxt/vite-server-nightly@5.0.0-29847385.3fde4d62': - resolution: {integrity: sha512-dKPR0zVsjJgicCxZ+vzFPtcSxXgdpPUDMNOTvCKojG5btzPZZDUPjynEq0RXven/e4d5eIbUI+gYLXiZ9wS5cg==} + '@nuxt/vite-server-nightly@5.0.0-29850714.87a4b2c4': + resolution: {integrity: sha512-arJGfHPwqdMVWu9hGHmBg75hmLUXSRfZZk2ZGrlz9Z92W86lO027davqZEbtJLM3C3WCFvTULu096GByz+11RA==} engines: {node: ^22.19.0 || ^24.11.0 || >=26.0.0} peerDependencies: - nuxt: npm:nuxt-nightly@5.0.0-29847385.3fde4d62 + nuxt: npm:nuxt-nightly@5.0.0-29850714.87a4b2c4 vite: 8.3.1 '@nuxtjs/color-mode@4.0.1': @@ -7872,8 +7903,8 @@ packages: nuxt-define@1.0.0: resolution: {integrity: sha512-CYZ2WjU+KCyCDVzjYUM4eEpMF0rkPmkpiFrybTqqQCRpUbPt2h3snswWIpFPXTi+osRCY6Og0W/XLAQgDL4FfQ==} - nuxt-nightly@5.0.0-29847385.3fde4d62: - resolution: {integrity: sha512-o0cs3nlRSaUgl9+dRJ2WF1cT8MYs1Rdmw+D4TM+3m5NrNqJBABFGdg4sk/z1TISlz2d8N94PO4maaPr5bvEc3Q==} + nuxt-nightly@5.0.0-29850714.87a4b2c4: + resolution: {integrity: sha512-9DvFDMPH1IYnIgkUzjOO8+h5zZ0tAA+ptdLTbW41oj2//J2qElxvjIJuOWavtpIBzsBQGlAx3QBeosLOVbZaFQ==} engines: {node: ^22.19.0 || ^24.11.0 || >=26.0.0} hasBin: true peerDependencies: @@ -12022,7 +12053,7 @@ snapshots: dependencies: which: 7.0.0 - '@nuxt/cli-nightly@4.0.0-20260929-142719-eabcb01(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(jiti@2.7.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(vite@8.3.1)': + '@nuxt/cli-nightly@4.0.0-20261003-160317-f140924f(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(jiti@2.7.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(vite@8.3.1)': dependencies: '@clack/prompts': 1.8.1 args-tokenizer: 0.3.0 @@ -12275,9 +12306,9 @@ snapshots: - rolldown - unplugin - '@nuxt/devtools-kit@4.0.0-beta.2(@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)))(nitro@3.0.260903-beta)(nitropack@2.13.4(@parcel/watcher@2.6.0)(better-sqlite3@13.0.3)(idb-keyval@6.3.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(srvx@1.0.5)(supports-color@11.0.0)(vite@8.3.1))(vite@8.3.1)': + '@nuxt/devtools-kit@4.0.0-beta.2(@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)))(nitro@3.0.260903-beta)(nitropack@2.13.4(@parcel/watcher@2.6.0)(better-sqlite3@13.0.3)(idb-keyval@6.3.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(srvx@1.0.5)(supports-color@11.0.0)(vite@8.3.1))(vite@8.3.1)': dependencies: - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' nostics: 1.3.0 tinyexec: 1.3.1 vite: 8.3.1(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) @@ -12564,13 +12595,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@4.0.0-beta.2(ac1d089532273e39dbba45edceffc411)': + '@nuxt/devtools@4.0.0-beta.2(7e87715d95891ababf0e5c93f1e8e195)': dependencies: '@devframes/hub': 1.1.0(@devframes/agentic@1.1.0)(crossws@0.4.12(srvx@1.0.5))(devframe@1.1.0)(ocache@0.3.0) '@devframes/plugin-code-server': 1.1.0(@devframes/hub-ui@1.1.0(@devframes/hub@1.1.0(@devframes/agentic@1.1.0)(crossws@0.4.12(srvx@1.0.5))(devframe@1.1.0)(ocache@0.3.0))(@devframes/json-render@1.1.0(@devframes/hub@1.1.0(@devframes/agentic@1.1.0)(crossws@0.4.12(srvx@1.0.5))(devframe@1.1.0)(ocache@0.3.0))(devframe@1.1.0))(devframe@1.1.0))(@devframes/hub@1.1.0(@devframes/agentic@1.1.0)(crossws@0.4.12(srvx@1.0.5))(devframe@1.1.0)(ocache@0.3.0))(devframe@1.1.0)(vite@8.3.1) '@devframes/plugin-data-inspector': 1.1.0(devframe@1.1.0)(vite@8.3.1) - '@nuxt/devtools-kit': 4.0.0-beta.2(@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)))(nitro@3.0.260903-beta)(nitropack@2.13.4(@parcel/watcher@2.6.0)(better-sqlite3@13.0.3)(idb-keyval@6.3.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(srvx@1.0.5)(supports-color@11.0.0)(vite@8.3.1))(vite@8.3.1) - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/devtools-kit': 4.0.0-beta.2(@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)))(nitro@3.0.260903-beta)(nitropack@2.13.4(@parcel/watcher@2.6.0)(better-sqlite3@13.0.3)(idb-keyval@6.3.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(srvx@1.0.5)(supports-color@11.0.0)(vite@8.3.1))(vite@8.3.1) + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' '@vitejs/devtools': 0.7.6(@devframes/json-render@1.1.0(@devframes/hub@1.1.0(@devframes/agentic@1.1.0)(crossws@0.4.12(srvx@1.0.5))(devframe@1.1.0)(ocache@0.3.0))(devframe@1.1.0))(crossws@0.4.12(srvx@1.0.5))(ocache@0.3.0)(srvx@1.0.5)(vite@8.3.1) '@vitejs/devtools-kit': 0.7.6(@devframes/agentic@1.1.0)(cac@7.0.0)(crossws@0.4.12(srvx@1.0.5))(ocache@0.3.0)(srvx@1.0.5)(vite@8.3.1) consola: 3.4.2 @@ -12748,7 +12779,7 @@ snapshots: - vite - vue - '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))': + '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))': dependencies: consola: 3.4.2 defu: 6.1.7 @@ -12966,10 +12997,10 @@ snapshots: - vue-tsc - webpack - '@nuxt/nitro-server-nightly@5.0.0-29847385.3fde4d62(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@oxc-project/types@0.152.0)(acorn@8.18.0)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(lightningcss@1.33.0)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1))(oxc-parser@0.152.0)(rollup@4.63.5)(typescript@6.0.3)(vite@8.3.1)': + '@nuxt/nitro-server-nightly@5.0.0-29850714.87a4b2c4(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@oxc-project/types@0.152.0)(acorn@8.18.0)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(lightningcss@1.33.0)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c))(oxc-parser@0.152.0)(rollup@4.63.5)(typescript@6.0.3)(vite@8.3.1)': dependencies: '@ampproject/remapping': 2.3.0 - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' '@unhead/vue': 3.4.1(@oxc-project/types@0.152.0)(esbuild@0.28.2)(lightningcss@1.33.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)) consola: 3.4.2 defu: 6.1.7 @@ -12986,7 +13017,7 @@ snapshots: my-bad: 0.2.9(vite@8.3.1) nitro: 3.0.260903-beta nostics: 1.3.0 - nuxt: nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1) + nuxt: nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c) ofetch: 2.0.0-alpha.3 ohash: 2.0.12 pathe: 2.0.3 @@ -13029,10 +13060,10 @@ snapshots: - vite - webpack - '@nuxt/nitro-server-nightly@5.0.0-29847385.3fde4d62(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@oxc-project/types@0.152.0)(acorn@8.18.0)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(lightningcss@1.33.0)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2))(oxc-parser@0.152.0)(rollup@4.63.5)(typescript@6.0.3)(vite@8.3.1)': + '@nuxt/nitro-server-nightly@5.0.0-29850714.87a4b2c4(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@oxc-project/types@0.152.0)(acorn@8.18.0)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(lightningcss@1.33.0)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd))(oxc-parser@0.152.0)(rollup@4.63.5)(typescript@6.0.3)(vite@8.3.1)': dependencies: '@ampproject/remapping': 2.3.0 - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' '@unhead/vue': 3.4.1(@oxc-project/types@0.152.0)(esbuild@0.28.2)(lightningcss@1.33.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)) consola: 3.4.2 defu: 6.1.7 @@ -13049,7 +13080,7 @@ snapshots: my-bad: 0.2.9(vite@8.3.1) nitro: 3.0.260903-beta nostics: 1.3.0 - nuxt: nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2) + nuxt: nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd) ofetch: 2.0.0-alpha.3 ohash: 2.0.12 pathe: 2.0.3 @@ -13545,9 +13576,9 @@ snapshots: pkg-types: 2.3.3 std-env: 4.2.0 - '@nuxt/telemetry@2.9.1(@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)))': + '@nuxt/telemetry@2.9.1(@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)))': dependencies: - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' citty: 0.2.2 consola: 3.4.2 rc9: 3.1.0 @@ -13686,9 +13717,9 @@ snapshots: - vue - webpack - '@nuxt/vite-builder-nightly@5.0.0-29847385.3fde4d62(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(@vitejs/plugin-vue-jsx@5.1.6(supports-color@11.0.0)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)))(autoprefixer@10.6.1(postcss@8.5.28))(confbox@0.3.1)(cssnano@8.0.10(postcss@8.5.28))(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.11.0(jiti@2.7.0)(supports-color@11.0.0))(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1))(optionator@0.9.4)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5))(terser@5.51.2)(typescript@6.0.3)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vue-tsc@3.3.11(typescript@6.0.3))(vue@3.5.43(typescript@6.0.3))(yaml@2.9.1)': + '@nuxt/vite-builder-nightly@5.0.0-29850714.87a4b2c4(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(@vitejs/plugin-vue-jsx@5.1.6(supports-color@11.0.0)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)))(autoprefixer@10.6.1(postcss@8.5.28))(confbox@0.3.1)(cssnano@8.0.10(postcss@8.5.28))(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.11.0(jiti@2.7.0)(supports-color@11.0.0))(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c))(optionator@0.9.4)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5))(terser@5.51.2)(typescript@6.0.3)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vue-tsc@3.3.11(typescript@6.0.3))(vue@3.5.43(typescript@6.0.3))(yaml@2.9.1)': dependencies: - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' '@vitejs/plugin-vue': 6.0.9(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)) clickable-path: 0.1.1 consola: 3.4.2 @@ -13701,7 +13732,7 @@ snapshots: knitwork: 1.3.0 mocked-exports: 0.1.1 my-bad: 0.2.9(vite@8.3.1) - nuxt: nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1) + nuxt: nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c) pathe: 2.0.3 pkg-types: 2.3.3 rolldown-string: 0.3.1(rolldown@1.2.11) @@ -13751,9 +13782,9 @@ snapshots: - vue-tsc - yaml - '@nuxt/vite-builder-nightly@5.0.0-29847385.3fde4d62(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(@vitejs/plugin-vue-jsx@5.1.6(supports-color@11.0.0)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)))(autoprefixer@10.6.1(postcss@8.5.28))(confbox@0.3.1)(cssnano@8.0.10(postcss@8.5.28))(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.11.0(jiti@2.7.0)(supports-color@11.0.0))(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2))(optionator@0.9.4)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5))(terser@5.51.2)(typescript@6.0.3)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vue-tsc@3.3.11(typescript@6.0.3))(vue@3.5.43(typescript@6.0.3))(yaml@2.9.1)': + '@nuxt/vite-builder-nightly@5.0.0-29850714.87a4b2c4(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(@vitejs/plugin-vue-jsx@5.1.6(supports-color@11.0.0)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)))(autoprefixer@10.6.1(postcss@8.5.28))(confbox@0.3.1)(cssnano@8.0.10(postcss@8.5.28))(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.11.0(jiti@2.7.0)(supports-color@11.0.0))(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd))(optionator@0.9.4)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5))(terser@5.51.2)(typescript@6.0.3)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vue-tsc@3.3.11(typescript@6.0.3))(vue@3.5.43(typescript@6.0.3))(yaml@2.9.1)': dependencies: - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' '@vitejs/plugin-vue': 6.0.9(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)) clickable-path: 0.1.1 consola: 3.4.2 @@ -13766,7 +13797,7 @@ snapshots: knitwork: 1.3.0 mocked-exports: 0.1.1 my-bad: 0.2.9(vite@8.3.1) - nuxt: nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2) + nuxt: nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd) pathe: 2.0.3 pkg-types: 2.3.3 rolldown-string: 0.3.1(rolldown@1.2.11) @@ -14166,16 +14197,16 @@ snapshots: - webpack - yaml - '@nuxt/vite-server-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1))(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vite@8.3.1)': + '@nuxt/vite-server-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c))(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vite@8.3.1)': dependencies: - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' clickable-path: 0.1.1 defu: 6.1.7 exsolve: 1.1.1 hookable: 6.1.2 mocked-exports: 0.1.1 my-bad: 0.2.9(vite@8.3.1) - nuxt: nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1) + nuxt: nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c) pathe: 2.0.3 rou3: 0.9.2 srvx: 1.0.5 @@ -14194,16 +14225,16 @@ snapshots: - rolldown - unplugin - '@nuxt/vite-server-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2))(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vite@8.3.1)': + '@nuxt/vite-server-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd))(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vite@8.3.1)': dependencies: - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' clickable-path: 0.1.1 defu: 6.1.7 exsolve: 1.1.1 hookable: 6.1.2 mocked-exports: 0.1.1 my-bad: 0.2.9(vite@8.3.1) - nuxt: nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2) + nuxt: nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd) pathe: 2.0.3 rou3: 0.9.2 srvx: 1.0.5 @@ -20521,18 +20552,18 @@ snapshots: nuxt-define@1.0.0: {} - nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1): + nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c): dependencies: '@dxup/nuxt': 0.5.10(esbuild@0.28.2)(magicast@0.5.5)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1) '@jridgewell/trace-mapping': 0.3.31 - '@nuxt/cli': '@nuxt/cli-nightly@4.0.0-20260929-142719-eabcb01(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(jiti@2.7.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(vite@8.3.1)' - '@nuxt/devtools': 4.0.0-beta.2(ac1d089532273e39dbba45edceffc411) - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' - '@nuxt/nitro-server': '@nuxt/nitro-server-nightly@5.0.0-29847385.3fde4d62(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@oxc-project/types@0.152.0)(acorn@8.18.0)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(lightningcss@1.33.0)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1))(oxc-parser@0.152.0)(rollup@4.63.5)(typescript@6.0.3)(vite@8.3.1)' + '@nuxt/cli': '@nuxt/cli-nightly@4.0.0-20261003-160317-f140924f(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(jiti@2.7.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(vite@8.3.1)' + '@nuxt/devtools': 4.0.0-beta.2(7e87715d95891ababf0e5c93f1e8e195) + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/nitro-server': '@nuxt/nitro-server-nightly@5.0.0-29850714.87a4b2c4(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@oxc-project/types@0.152.0)(acorn@8.18.0)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(lightningcss@1.33.0)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c))(oxc-parser@0.152.0)(rollup@4.63.5)(typescript@6.0.3)(vite@8.3.1)' '@nuxt/schema': '@nuxt/schema-nightly@5.0.0-29846154.7802b2c2' - '@nuxt/telemetry': 2.9.1(@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))) - '@nuxt/vite-builder': '@nuxt/vite-builder-nightly@5.0.0-29847385.3fde4d62(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(@vitejs/plugin-vue-jsx@5.1.6(supports-color@11.0.0)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)))(autoprefixer@10.6.1(postcss@8.5.28))(confbox@0.3.1)(cssnano@8.0.10(postcss@8.5.28))(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.11.0(jiti@2.7.0)(supports-color@11.0.0))(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1))(optionator@0.9.4)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5))(terser@5.51.2)(typescript@6.0.3)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vue-tsc@3.3.11(typescript@6.0.3))(vue@3.5.43(typescript@6.0.3))(yaml@2.9.1)' - '@nuxt/vite-server': '@nuxt/vite-server-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(66c9ffb21ea764c292855c24fd0b46f1))(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vite@8.3.1)' + '@nuxt/telemetry': 2.9.1(@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))) + '@nuxt/vite-builder': '@nuxt/vite-builder-nightly@5.0.0-29850714.87a4b2c4(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(@vitejs/plugin-vue-jsx@5.1.6(supports-color@11.0.0)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)))(autoprefixer@10.6.1(postcss@8.5.28))(confbox@0.3.1)(cssnano@8.0.10(postcss@8.5.28))(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.11.0(jiti@2.7.0)(supports-color@11.0.0))(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c))(optionator@0.9.4)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5))(terser@5.51.2)(typescript@6.0.3)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vue-tsc@3.3.11(typescript@6.0.3))(vue@3.5.43(typescript@6.0.3))(yaml@2.9.1)' + '@nuxt/vite-server': '@nuxt/vite-server-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(61fa37ce826fd8907fc6ec7f4d34891c))(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vite@8.3.1)' '@unhead/vue': 3.4.1(@oxc-project/types@0.152.0)(esbuild@0.28.2)(lightningcss@1.33.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)) '@vue/shared': 3.5.43 chokidar: 5.0.0 @@ -20683,18 +20714,18 @@ snapshots: - webpack - yaml - nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2): + nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd): dependencies: '@dxup/nuxt': 0.5.10(esbuild@0.28.2)(magicast@0.5.5)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1) '@jridgewell/trace-mapping': 0.3.31 - '@nuxt/cli': '@nuxt/cli-nightly@4.0.0-20260929-142719-eabcb01(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(jiti@2.7.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(vite@8.3.1)' - '@nuxt/devtools': 4.0.0-beta.2(ac1d089532273e39dbba45edceffc411) - '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' - '@nuxt/nitro-server': '@nuxt/nitro-server-nightly@5.0.0-29847385.3fde4d62(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@oxc-project/types@0.152.0)(acorn@8.18.0)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(lightningcss@1.33.0)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2))(oxc-parser@0.152.0)(rollup@4.63.5)(typescript@6.0.3)(vite@8.3.1)' + '@nuxt/cli': '@nuxt/cli-nightly@4.0.0-20261003-160317-f140924f(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(jiti@2.7.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(vite@8.3.1)' + '@nuxt/devtools': 4.0.0-beta.2(7e87715d95891ababf0e5c93f1e8e195) + '@nuxt/kit': '@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))' + '@nuxt/nitro-server': '@nuxt/nitro-server-nightly@5.0.0-29850714.87a4b2c4(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@oxc-project/types@0.152.0)(acorn@8.18.0)(confbox@0.3.1)(dotenv@17.4.2)(esbuild@0.28.2)(giget@3.3.1)(jiti@2.7.0)(lightningcss@1.33.0)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd))(oxc-parser@0.152.0)(rollup@4.63.5)(typescript@6.0.3)(vite@8.3.1)' '@nuxt/schema': '@nuxt/schema-nightly@5.0.0-29846154.7802b2c2' - '@nuxt/telemetry': 2.9.1(@nuxt/kit-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))) - '@nuxt/vite-builder': '@nuxt/vite-builder-nightly@5.0.0-29847385.3fde4d62(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(@vitejs/plugin-vue-jsx@5.1.6(supports-color@11.0.0)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)))(autoprefixer@10.6.1(postcss@8.5.28))(confbox@0.3.1)(cssnano@8.0.10(postcss@8.5.28))(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.11.0(jiti@2.7.0)(supports-color@11.0.0))(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2))(optionator@0.9.4)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5))(terser@5.51.2)(typescript@6.0.3)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vue-tsc@3.3.11(typescript@6.0.3))(vue@3.5.43(typescript@6.0.3))(yaml@2.9.1)' - '@nuxt/vite-server': '@nuxt/vite-server-nightly@5.0.0-29847385.3fde4d62(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29847385.3fde4d62(8e654c43e1283e8b291669fe3ae7c8f2))(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vite@8.3.1)' + '@nuxt/telemetry': 2.9.1(@nuxt/kit-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))) + '@nuxt/vite-builder': '@nuxt/vite-builder-nightly@5.0.0-29850714.87a4b2c4(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@11.0.0)))(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(@types/node@24.19.0)(@vitejs/devtools@0.7.6)(@vitejs/plugin-vue-jsx@5.1.6(supports-color@11.0.0)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)))(autoprefixer@10.6.1(postcss@8.5.28))(confbox@0.3.1)(cssnano@8.0.10(postcss@8.5.28))(dotenv@17.4.2)(esbuild@0.28.2)(eslint@10.11.0(jiti@2.7.0)(supports-color@11.0.0))(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd))(optionator@0.9.4)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup-plugin-visualizer@7.1.1(rolldown@1.2.11)(rollup@4.63.5))(terser@5.51.2)(typescript@6.0.3)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vue-tsc@3.3.11(typescript@6.0.3))(vue@3.5.43(typescript@6.0.3))(yaml@2.9.1)' + '@nuxt/vite-server': '@nuxt/vite-server-nightly@5.0.0-29850714.87a4b2c4(@nuxt/schema-nightly@5.0.0-29846154.7802b2c2)(confbox@0.3.1)(dotenv@17.4.2)(giget@3.3.1)(jiti@2.7.0)(magic-string@1.4.2)(mlly@1.8.2)(nuxt-nightly@5.0.0-29850714.87a4b2c4(de96468cfd5673a7c7dab0b10b0243dd))(oxc-parser@0.152.0)(rolldown@1.2.11)(unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1))(vite@8.3.1)' '@unhead/vue': 3.4.1(@oxc-project/types@0.152.0)(esbuild@0.28.2)(lightningcss@1.33.0)(oxc-parser@0.152.0)(rolldown@1.2.11)(rollup@4.63.5)(vite@8.3.1)(vue@3.5.43(typescript@6.0.3)) '@vue/shared': 3.5.43 chokidar: 5.0.0 diff --git a/test/fixtures/nuxt-ignore/app/components/Message.vue b/test/fixtures/nuxt-ignore/app/components/Message.vue deleted file mode 100644 index 5dee486de..000000000 --- a/test/fixtures/nuxt-ignore/app/components/Message.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/test/fixtures/nuxt-ignore/app/pages/index.vue b/test/fixtures/nuxt-ignore/app/pages/index.vue deleted file mode 100644 index 77b1b7339..000000000 --- a/test/fixtures/nuxt-ignore/app/pages/index.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/test/fixtures/nuxt-ignore/nuxt.config.ts b/test/fixtures/nuxt-ignore/nuxt.config.ts deleted file mode 100644 index dd41d0482..000000000 --- a/test/fixtures/nuxt-ignore/nuxt.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default defineNuxtConfig({ - modules: ['@nuxt/test-utils/module'], -}) diff --git a/test/fixtures/nuxt-ignore/tsconfig.json b/test/fixtures/nuxt-ignore/tsconfig.json deleted file mode 100644 index 3921d7e1b..000000000 --- a/test/fixtures/nuxt-ignore/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../../.nuxt/tsconfig.json" -} diff --git a/test/fixtures/nuxt-ignore/vitest.config.ts b/test/fixtures/nuxt-ignore/vitest.config.ts deleted file mode 100644 index 735b44d88..000000000 --- a/test/fixtures/nuxt-ignore/vitest.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { defineVitestConfig } from '@nuxt/test-utils/config' - -export default defineVitestConfig({}) diff --git a/test/unit/nuxt-ignore.spec.ts b/test/unit/nuxt-ignore.spec.ts index faace0b7b..2aee5ab54 100644 --- a/test/unit/nuxt-ignore.spec.ts +++ b/test/unit/nuxt-ignore.spec.ts @@ -1,23 +1,25 @@ -import { relative, join } from 'pathe' -import { fileURLToPath } from 'node:url' -import { rm } from 'node:fs/promises' -import { afterAll, describe, expect, it } from 'vitest' -import { loadNuxt, buildNuxt, logger } from '@nuxt/kit' +import { describe, expect, it } from 'vitest' +import type { Nuxt } from 'nuxt/schema' import { setupNuxtIgnore } from '../../src/module/ignore' -const TEST_TIMEOUT = process.env.CI ? 60_000 : 30_000 +type Hook = (...args: unknown[]) => unknown +let hooks: Record -describe('nuxtignore', () => { - const fixtureDir = fileURLToPath(new URL('../fixtures/nuxt-ignore', import.meta.url)) - - afterAll(async () => { - await rm(join(fixtureDir, '.nuxt'), { recursive: true, force: true }) - }) +function createNuxt() { + hooks = {} + const nuxt = { + hook: (name: string, fn: Hook) => { + (hooks[name] ||= []).push(fn) + }, + options: {}, + } as Nuxt + nuxt.options.ignore = [] + return nuxt +} +describe('nuxtignore', () => { it('should remove test file pattern from ignore', async () => { - const nuxt = await loadNuxt({ - cwd: fixtureDir, - }) + const nuxt = createNuxt() nuxt.options.ignore = [ '**/*.stories.{js,cts,mts,ts,jsx,tsx}', @@ -72,63 +74,25 @@ describe('nuxtignore', () => { ]) }) - it('should ignore test files in runtime dirs', async () => { - const nuxt = await loadNuxt({ cwd: fixtureDir }) - - const normalize = (paths: string[]) => paths - .map(p => relative(fixtureDir, p)) - .filter(p => !p.includes('node_modules/') && !p.includes('../') && !p.includes('.nuxt/')) - .toSorted() - - const scans = { - pages: [] as string[], - imports: [] as string[], - components: [] as string[], - plugins: [] as string[], - middlewares: [] as string[], - serverHandlers: [] as string[], - } - - nuxt.addHooks({ - 'pages:extend'(pages) { - scans.pages = normalize(pages.flatMap(p => p.file!)) - }, - 'imports:extend'(imports) { - scans.imports = normalize(imports.map(p => p.from)) - }, - 'components:extend'(components) { - scans.components = normalize(components.map(p => p.filePath)) - }, - 'app:resolve': (app) => { - scans.plugins = normalize(app.plugins.map(p => p.src)) - scans.middlewares = normalize(app.middleware.map(p => p.path)) - }, - 'nitro:build:before'(nitro) { - scans.serverHandlers = normalize(nitro.scannedHandlers.map(p => p.handler)) - }, - }) - - await buildNuxt(nuxt).finally(() => logger.restoreAll()) - - expect(scans).toEqual({ - pages: [ - 'app/pages/index.vue', - ], - imports: [ - 'app/composables/useMessage.ts', - ], - components: [ - 'app/components/Message.vue', - ], + it('should remove test files from plugins', async () => { + const nuxt = createNuxt() + setupNuxtIgnore(nuxt) + const app = { plugins: [ - 'app/plugins/message.ts', - ], - middlewares: [ - 'app/middleware/logger.global.ts', - ], - serverHandlers: [ - 'server/api/hello.get.ts', - ], - } satisfies typeof scans) - }, TEST_TIMEOUT) + 'app/plugins/plugin.ts', + 'app/plugins/plugin.spec.ts', + 'app/plugins/plugin.test.ts', + 'app/plugins/plugin.spec-d.ts', + 'app/plugins/plugin.test-d.ts', + 'app/plugins/plugin.special.ts', + 'app/plugins/plugin.testable.ts', + ].map(src => ({ src })), + } + hooks['app:resolve']?.at(-1)?.(app) + expect(app.plugins.map(({ src }) => src)).toEqual([ + 'app/plugins/plugin.ts', + 'app/plugins/plugin.special.ts', + 'app/plugins/plugin.testable.ts', + ]) + }) }) From 4ca6ebd16d0e3801e9420dd10c5d39e1e175a0d7 Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Sun, 4 Oct 2026 14:05:30 +0900 Subject: [PATCH 05/11] cleanup tests --- .../app/components/__tests__/Message.browser.spec.ts | 2 +- .../app/components/__tests__/Message.nuxt.spec.ts | 2 +- .../app/composables/useMessage.nuxt.spec.ts | 2 -- .../app-vitest-co-location/app/pages/index.e2e.spec.ts | 5 ++--- .../app-vitest-co-location/app/pages/index.nuxt.spec.ts | 2 +- .../app/plugins/message.nuxt.test.ts | 7 +------ examples/app-vitest-co-location/app/plugins/message.ts | 2 +- examples/app-vitest-co-location/nuxt.config.ts | 5 ----- examples/app-vitest-co-location/vitest.config.ts | 1 - 9 files changed, 7 insertions(+), 21 deletions(-) diff --git a/examples/app-vitest-co-location/app/components/__tests__/Message.browser.spec.ts b/examples/app-vitest-co-location/app/components/__tests__/Message.browser.spec.ts index d794a3836..281a28535 100644 --- a/examples/app-vitest-co-location/app/components/__tests__/Message.browser.spec.ts +++ b/examples/app-vitest-co-location/app/components/__tests__/Message.browser.spec.ts @@ -3,7 +3,7 @@ import { render } from '@nuxt/test-utils/browser' import { Message } from '#components' -it('should mount', async () => { +it('should render', async () => { const screen = await render(Message) const message = screen.getByRole('heading') await expect.element(message).toMatchTextContent(/Hello Nuxt/) diff --git a/examples/app-vitest-co-location/app/components/__tests__/Message.nuxt.spec.ts b/examples/app-vitest-co-location/app/components/__tests__/Message.nuxt.spec.ts index 86d6f8d4a..7ecd70ac4 100644 --- a/examples/app-vitest-co-location/app/components/__tests__/Message.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/components/__tests__/Message.nuxt.spec.ts @@ -3,7 +3,7 @@ import { mountSuspended } from '@nuxt/test-utils/runtime' import { Message } from '#components' -it('should mount', async () => { +it('should mount with import from alias', async () => { const wrapper = await mountSuspended(Message) expect(wrapper.text()).toContain('Hello Nuxt') }) diff --git a/examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts b/examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts index 75284b653..fa31c994e 100644 --- a/examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts @@ -1,8 +1,6 @@ import { it, expect } from 'vitest' import { useMessage } from './useMessage' -export function useMessageSpec() {} - it('useMessage', () => { expect(useMessage()).toBe('Hello Nuxt') }) diff --git a/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts b/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts index 91ecfaade..2b6b7ee85 100644 --- a/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts +++ b/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts @@ -7,14 +7,13 @@ await setup({ browser: true, }) -it('runs e2e browser test', { timeout: 20000 }, async () => { +it('runs e2e browser test', { timeout: 20000 }, async ({ onTestFinished }) => { const page = await createPage('/') + onTestFinished(() => page.close()) const title = page.getByRole('heading', { level: 1 }) await expect(title.textContent()).resolves.toContain('Index Page') const paragraph = page.getByRole('paragraph') await expect(paragraph.textContent()).resolves.toContain('foo: bar') - - await page.close() }) diff --git a/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts b/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts index 7de0e3037..a76bbb538 100644 --- a/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts @@ -3,7 +3,7 @@ import { mountSuspended } from '@nuxt/test-utils/runtime' import Page from './index.vue' -it('should mount', async () => { +it('should mount page', async () => { const wrapper = await mountSuspended(Page) expect(wrapper.text()).toContain('Index Page') expect(wrapper.text()).toContain('foo: bar') diff --git a/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts b/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts index 290cd3c00..29d0a013c 100644 --- a/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts +++ b/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts @@ -1,10 +1,5 @@ import { it, expect } from 'vitest' -import plugin from './message' it('plugin', () => { - expect(plugin(useNuxtApp())).toEqual({ - provide: { - message: 'Hello Nuxt (/)', - }, - }) + expect(useNuxtApp().$message()).toEqual('Hello Nuxt (/)') }) diff --git a/examples/app-vitest-co-location/app/plugins/message.ts b/examples/app-vitest-co-location/app/plugins/message.ts index bb81cdadc..e44849388 100644 --- a/examples/app-vitest-co-location/app/plugins/message.ts +++ b/examples/app-vitest-co-location/app/plugins/message.ts @@ -1,7 +1,7 @@ export default defineNuxtPlugin((nuxt) => { return { provide: { - get message() { + message() { return `Hello Nuxt (${nuxt.$router.currentRoute.value.path})` }, }, diff --git a/examples/app-vitest-co-location/nuxt.config.ts b/examples/app-vitest-co-location/nuxt.config.ts index 423d0bd0a..3c0edadf7 100644 --- a/examples/app-vitest-co-location/nuxt.config.ts +++ b/examples/app-vitest-co-location/nuxt.config.ts @@ -3,9 +3,4 @@ export default defineNuxtConfig({ modules: ['@nuxt/test-utils/module'], devtools: { enabled: true }, compatibilityDate: '2024-04-03', - typescript: { - tsConfig: { - include: ['../test/nuxt-ignore.unit.test.ts'], - }, - }, }) diff --git a/examples/app-vitest-co-location/vitest.config.ts b/examples/app-vitest-co-location/vitest.config.ts index 41a4acbe2..213614cff 100644 --- a/examples/app-vitest-co-location/vitest.config.ts +++ b/examples/app-vitest-co-location/vitest.config.ts @@ -36,7 +36,6 @@ export default defineConfig({ instances: [{ browser: 'chromium' }], }, include: ['**/*.browser.{test,spec}.ts'], - setupFiles: ['@nuxt/test-utils/browser'], }, }), ], From 0688d63c26776c1d831872083fd972a09bd70961 Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Sun, 4 Oct 2026 16:07:12 +0900 Subject: [PATCH 06/11] restore add negative patterns to `nuxt._ignore` --- examples/app-vitest-co-location/.nuxtignore | 1 + src/module/ignore.ts | 14 ++-- test/unit/nuxt-ignore.spec.ts | 73 ++++++++++++++++++--- 3 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 examples/app-vitest-co-location/.nuxtignore diff --git a/examples/app-vitest-co-location/.nuxtignore b/examples/app-vitest-co-location/.nuxtignore new file mode 100644 index 000000000..7affc8e76 --- /dev/null +++ b/examples/app-vitest-co-location/.nuxtignore @@ -0,0 +1 @@ +**/__tests__/**/* \ No newline at end of file diff --git a/src/module/ignore.ts b/src/module/ignore.ts index 545caf25e..929786582 100644 --- a/src/module/ignore.ts +++ b/src/module/ignore.ts @@ -1,3 +1,4 @@ +import { resolveIgnorePatterns } from '@nuxt/kit' import type { Nuxt } from '@nuxt/schema' const testIgnorePatterns = [ @@ -18,10 +19,15 @@ function isTestPluginFile(src: string) { export async function setupNuxtIgnore(nuxt: Nuxt) { // We want to run Nuxt plugins on test files - const testIgnores = new Set(nuxt.options.ignore.filter(isTestFileIgnorePattern)) - if (testIgnores.size) { - nuxt.options.ignore = nuxt.options.ignore.filter(i => !testIgnores.has(i)) - } + nuxt.options.ignore = nuxt.options.ignore.filter(i => !isTestFileIgnorePattern(i)) + nuxt.hook('modules:done', () => { + if (!nuxt._ignore) return + for (const pattern of resolveIgnorePatterns()) { + if (isTestFileIgnorePattern(pattern)) { + nuxt._ignore.add(`!${pattern}`) + } + } + }) // But do not register test files inside plugins/ as real Nuxt plugins nuxt.hook('app:resolve', (app) => { diff --git a/test/unit/nuxt-ignore.spec.ts b/test/unit/nuxt-ignore.spec.ts index 2aee5ab54..1d9c51597 100644 --- a/test/unit/nuxt-ignore.spec.ts +++ b/test/unit/nuxt-ignore.spec.ts @@ -1,14 +1,26 @@ -import { describe, expect, it } from 'vitest' -import type { Nuxt } from 'nuxt/schema' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import type { Nuxt, NuxtHookName } from 'nuxt/schema' import { setupNuxtIgnore } from '../../src/module/ignore' +const { resolveIgnorePatterns } = vi.hoisted(() => ({ + resolveIgnorePatterns: vi.fn(() => [] as string[]), +})) + +vi.mock(import('@nuxt/kit'), async (importOriginal) => { + const original = await importOriginal() + return { + ...original, + resolveIgnorePatterns, + } +}) + type Hook = (...args: unknown[]) => unknown -let hooks: Record +let hooks: Record function createNuxt() { - hooks = {} + hooks = {} as typeof hooks const nuxt = { - hook: (name: string, fn: Hook) => { + hook: (name: NuxtHookName, fn: Hook) => { (hooks[name] ||= []).push(fn) }, options: {}, @@ -18,7 +30,12 @@ function createNuxt() { } describe('nuxtignore', () => { - it('should remove test file pattern from ignore', async () => { + beforeEach(() => { + vi.clearAllMocks() + resolveIgnorePatterns.mockImplementation(() => []) + }) + + it('should remove test file patterns from `nuxt.options.ignore`', () => { const nuxt = createNuxt() nuxt.options.ignore = [ @@ -58,6 +75,7 @@ describe('nuxtignore', () => { '**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}', '**/*.{spec,test,spec-d,test-d}.{js,cts,mts,ts,jsx,tsx}', ] + setupNuxtIgnore(nuxt) expect(nuxt.options.ignore).toEqual([ '**/*.stories.{js,cts,mts,ts,jsx,tsx}', @@ -74,9 +92,45 @@ describe('nuxtignore', () => { ]) }) - it('should remove test files from plugins', async () => { + it('should add negative test file patterns to `nuxt._ignore`', async () => { + resolveIgnorePatterns.mockImplementationOnce(() => [ + '**/*.stories.{js,cts,mts,ts,jsx,tsx}', + '**/*.d.{cts,mts,ts}', + '**/__tests__/**', + '**/*.{spec,test}.ts', + '**/*.{test-d,spec-d}.ts', + '**/*.spec.{js,cts,mts,ts,jsx,tsx}', + '**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}', + '**/*.{spec,test,spec-d,test-d}.{js,cts,mts,ts,jsx,tsx}', + '!**/pages/__tests__/**', + '!**/pages/**/this-is-page.{spec,test}.{js,cts,mts,ts,jsx,tsx}', + '!**/components/**/this-is-components.spec.ts', + ]) + + const addedNegativeIgnores = [] as string[] + const nuxt = createNuxt() + nuxt._ignore = { + add: (p: string) => { + addedNegativeIgnores.push(p) + }, + } as typeof nuxt._ignore + setupNuxtIgnore(nuxt) + hooks['modules:done'].at(-1)?.() + + expect(addedNegativeIgnores).toEqual([ + '!**/__tests__/**', + '!**/*.{spec,test}.ts', + '!**/*.{test-d,spec-d}.ts', + '!**/*.spec.{js,cts,mts,ts,jsx,tsx}', + '!**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}', + '!**/*.{spec,test,spec-d,test-d}.{js,cts,mts,ts,jsx,tsx}', + ]) + }) + + it('should remove test files from plugins', () => { + const nuxt = createNuxt() const app = { plugins: [ 'app/plugins/plugin.ts', @@ -88,7 +142,10 @@ describe('nuxtignore', () => { 'app/plugins/plugin.testable.ts', ].map(src => ({ src })), } - hooks['app:resolve']?.at(-1)?.(app) + + setupNuxtIgnore(nuxt) + hooks['app:resolve'].at(-1)?.(app) + expect(app.plugins.map(({ src }) => src)).toEqual([ 'app/plugins/plugin.ts', 'app/plugins/plugin.special.ts', From 3863aad4d78ebd2796d91fe67b5fe792ec271068 Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Sun, 4 Oct 2026 23:31:18 +0900 Subject: [PATCH 07/11] do not register test files inside `middleware/` --- .../app/composables/useMessage.nuxt.spec.ts | 2 + .../app/middleware/logger.global.nuxt.spec.ts | 7 +- .../app/pages/index.e2e.spec.ts | 2 +- .../app/pages/index.nuxt.spec.ts | 19 ++++- .../app/pages/index.vue | 9 ++- .../app/plugins/message.nuxt.test.ts | 10 ++- .../app/plugins/message.ts | 2 +- .../modules/collect-scans.ts | 61 ++++++++++++++++ .../server/api/collect-scans.ts | 6 ++ .../test/nuxt-ignore.unit.test.ts | 70 ------------------- .../test/nuxt/nuxt-ignore-keep.e2e.test.ts | 32 +++++++++ src/module/ignore.ts | 4 +- test/unit/nuxt-ignore.spec.ts | 37 +++++++--- 13 files changed, 172 insertions(+), 89 deletions(-) create mode 100644 examples/app-vitest-co-location/modules/collect-scans.ts create mode 100644 examples/app-vitest-co-location/server/api/collect-scans.ts delete mode 100644 examples/app-vitest-co-location/test/nuxt-ignore.unit.test.ts create mode 100644 examples/app-vitest-co-location/test/nuxt/nuxt-ignore-keep.e2e.test.ts diff --git a/examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts b/examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts index fa31c994e..3acc553a2 100644 --- a/examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/composables/useMessage.nuxt.spec.ts @@ -4,3 +4,5 @@ import { useMessage } from './useMessage' it('useMessage', () => { expect(useMessage()).toBe('Hello Nuxt') }) + +export function useMessageSpec() {} diff --git a/examples/app-vitest-co-location/app/middleware/logger.global.nuxt.spec.ts b/examples/app-vitest-co-location/app/middleware/logger.global.nuxt.spec.ts index 0376165e4..70da24bee 100644 --- a/examples/app-vitest-co-location/app/middleware/logger.global.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/middleware/logger.global.nuxt.spec.ts @@ -1,6 +1,11 @@ -import { it, expect } from 'vitest' +import { it, expect, vi } from 'vitest' import middleware from './logger.global' +import { mockNuxtImport } from '@nuxt/test-utils/runtime' + +mockNuxtImport(useRoute, vi.fn) it('middleware', () => { + const spy = vi.spyOn(console, 'log') expect(middleware(useRoute(), useRoute())).toBeUndefined() + expect(spy).toHaveBeenCalledExactlyOnceWith('/', '/') }) diff --git a/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts b/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts index 2b6b7ee85..f644efc1e 100644 --- a/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts +++ b/examples/app-vitest-co-location/app/pages/index.e2e.spec.ts @@ -14,6 +14,6 @@ it('runs e2e browser test', { timeout: 20000 }, async ({ onTestFinished }) => { const title = page.getByRole('heading', { level: 1 }) await expect(title.textContent()).resolves.toContain('Index Page') - const paragraph = page.getByRole('paragraph') + const paragraph = page.locator('#app-config') await expect(paragraph.textContent()).resolves.toContain('foo: bar') }) diff --git a/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts b/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts index a76bbb538..63691dab7 100644 --- a/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts @@ -1,10 +1,25 @@ import { it, expect } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' +import type { TestCollectScans } from '~~/modules/collect-scans.ts' import Page from './index.vue' it('should mount page', async () => { const wrapper = await mountSuspended(Page) - expect(wrapper.text()).toContain('Index Page') - expect(wrapper.text()).toContain('foo: bar') + const title = wrapper.find('h1') + expect(title.text()).toContain('Index Page') + + const appConfig = wrapper.find('#app-config') + expect(appConfig.text()).toContain('foo: bar') +}) + +it('should ignore plugins and middlewares', async () => { + const wrapper = await mountSuspended(Page) + const scans: TestCollectScans = JSON.parse(wrapper.find('#scans').text()) + + expect(scans.plugins).toContain('app/plugins/message.ts') + expect(scans.plugins).not.toContain('app/plugins/message.nuxt.test.ts') + + expect(scans.middlewares).toContain('app/middleware/logger.global.ts') + expect(scans.middlewares).not.toContain('app/middleware/logger.global.nuxt.spec.ts') }) diff --git a/examples/app-vitest-co-location/app/pages/index.vue b/examples/app-vitest-co-location/app/pages/index.vue index b2a45c21c..fd38a0e75 100644 --- a/examples/app-vitest-co-location/app/pages/index.vue +++ b/examples/app-vitest-co-location/app/pages/index.vue @@ -1,10 +1,17 @@ diff --git a/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts b/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts index 29d0a013c..26a81e1ca 100644 --- a/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts +++ b/examples/app-vitest-co-location/app/plugins/message.nuxt.test.ts @@ -1,5 +1,13 @@ -import { it, expect } from 'vitest' +import { mockNuxtImport } from '@nuxt/test-utils/runtime' +import { it, expect, vi } from 'vitest' + +mockNuxtImport(useMessage, vi.fn) it('plugin', () => { expect(useNuxtApp().$message()).toEqual('Hello Nuxt (/)') }) + +it('plugin mock', () => { + vi.mocked(useMessage).mockImplementationOnce(() => 'Mocked!') + expect(useNuxtApp().$message()).toEqual('Mocked! (/)') +}) diff --git a/examples/app-vitest-co-location/app/plugins/message.ts b/examples/app-vitest-co-location/app/plugins/message.ts index e44849388..32f56e68f 100644 --- a/examples/app-vitest-co-location/app/plugins/message.ts +++ b/examples/app-vitest-co-location/app/plugins/message.ts @@ -2,7 +2,7 @@ export default defineNuxtPlugin((nuxt) => { return { provide: { message() { - return `Hello Nuxt (${nuxt.$router.currentRoute.value.path})` + return `${useMessage()} (${nuxt.$router.currentRoute.value.path})` }, }, } diff --git a/examples/app-vitest-co-location/modules/collect-scans.ts b/examples/app-vitest-co-location/modules/collect-scans.ts new file mode 100644 index 000000000..0999689c3 --- /dev/null +++ b/examples/app-vitest-co-location/modules/collect-scans.ts @@ -0,0 +1,61 @@ +import { relative } from 'pathe' +import { defineNuxtModule, addTemplate, addServerTemplate } from '@nuxt/kit' + +export type TestCollectScans = ReturnType + +function createScans() { + return { + pages: [] as string[], + imports: [] as string[], + components: [] as string[], + plugins: [] as string[], + middlewares: [] as string[], + serverHandlers: [] as string[], + } +} + +export default defineNuxtModule({ + meta: { + name: 'test-collect-scans', + }, + setup(_, nuxt) { + const rootDir = nuxt.options.rootDir + + const normalize = (paths: (string | undefined)[]) => paths + .filter(p => p !== undefined) + .map(p => relative(rootDir, p)) + .filter(p => !p.includes('node_modules/') && !p.includes('../') && !p.includes('.nuxt/')) + .toSorted() + + const scans = createScans() + + addTemplate({ + filename: 'test-collect-scans.mjs', + getContents: () => `export default ${JSON.stringify(scans)};`, + }) + + addServerTemplate({ + filename: '#test-collect-scans/scans.mjs', + getContents: () => `export default ${JSON.stringify(scans)};`, + }) + + nuxt.addHooks({ + 'pages:extend'(pages) { + scans.pages = normalize(pages.map(p => p.file)) + }, + 'imports:extend'(imports) { + scans.imports = normalize(imports.map(p => p.from)) + }, + 'components:extend'(components) { + scans.components = normalize(components.map(p => p.filePath)) + }, + 'app:resolve': (app) => { + scans.plugins = normalize(app.plugins.map(p => p.src)) + scans.middlewares = normalize(app.middleware.map(p => p.path)) + }, + 'nitro:build:before'(nitro) { + scans.serverHandlers = normalize(nitro.scannedHandlers.map(p => p.handler)) + }, + }) + }, +}) diff --git a/examples/app-vitest-co-location/server/api/collect-scans.ts b/examples/app-vitest-co-location/server/api/collect-scans.ts new file mode 100644 index 000000000..8543cc5d7 --- /dev/null +++ b/examples/app-vitest-co-location/server/api/collect-scans.ts @@ -0,0 +1,6 @@ +import { defineEventHandler } from 'h3' + +// @ts-expect-error vertual file +import scans from '#test-collect-scans/scans.mjs' + +export default defineEventHandler(() => scans) diff --git a/examples/app-vitest-co-location/test/nuxt-ignore.unit.test.ts b/examples/app-vitest-co-location/test/nuxt-ignore.unit.test.ts deleted file mode 100644 index 8f6bf54f3..000000000 --- a/examples/app-vitest-co-location/test/nuxt-ignore.unit.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { relative } from 'pathe' -import { fileURLToPath } from 'node:url' -import { describe, expect, it } from 'vitest' -import { loadNuxt, buildNuxt, logger } from '@nuxt/kit' - -const TEST_TIMEOUT = process.env.CI ? 60_000 : 30_000 - -describe('nuxtignore', () => { - const fixtureDir = fileURLToPath(new URL('../', import.meta.url)) - - it('should ignore test files in runtime dirs', async () => { - const nuxt = await loadNuxt({ cwd: fixtureDir }) - - const normalize = (paths: string[]) => paths - .map(p => relative(fixtureDir, p)) - .filter(p => !p.includes('node_modules/') && !p.includes('../') && !p.includes('.nuxt/')) - .toSorted() - - const scans = { - pages: [] as string[], - imports: [] as string[], - components: [] as string[], - plugins: [] as string[], - middlewares: [] as string[], - serverHandlers: [] as string[], - } - - nuxt.addHooks({ - 'pages:extend'(pages) { - scans.pages = normalize(pages.flatMap(p => p.file!)) - }, - 'imports:extend'(imports) { - scans.imports = normalize(imports.map(p => p.from)) - }, - 'components:extend'(components) { - scans.components = normalize(components.map(p => p.filePath)) - }, - 'app:resolve': (app) => { - scans.plugins = normalize(app.plugins.map(p => p.src)) - scans.middlewares = normalize(app.middleware.map(p => p.path)) - }, - 'nitro:build:before'(nitro) { - scans.serverHandlers = normalize(nitro.scannedHandlers.map(p => p.handler)) - }, - }) - - await buildNuxt(nuxt).finally(() => logger.restoreAll()) - - expect(scans).toEqual({ - pages: [ - 'app/pages/index.vue', - ], - imports: [ - 'app/composables/useMessage.ts', - ], - components: [ - 'app/components/Message.vue', - ], - plugins: [ - 'app/plugins/message.ts', - ], - middlewares: [ - 'app/middleware/logger.global.ts', - ], - serverHandlers: [ - 'server/api/hello.get.ts', - ], - } satisfies typeof scans) - }, TEST_TIMEOUT) -}) diff --git a/examples/app-vitest-co-location/test/nuxt/nuxt-ignore-keep.e2e.test.ts b/examples/app-vitest-co-location/test/nuxt/nuxt-ignore-keep.e2e.test.ts new file mode 100644 index 000000000..6f27c1a58 --- /dev/null +++ b/examples/app-vitest-co-location/test/nuxt/nuxt-ignore-keep.e2e.test.ts @@ -0,0 +1,32 @@ +import { fileURLToPath } from 'node:url' +import { expect, it } from 'vitest' +import { setup, $fetch } from '@nuxt/test-utils/e2e' +import type { TestCollectScans } from '~~/modules/collect-scans' + +await setup({ + rootDir: fileURLToPath(new URL('../..', import.meta.url)), +}) + +it('should keep ignore test files in runtime dirs', async () => { + expect(await $fetch('/api/collect-scans')).toEqual({ + pages: [ + 'app/pages/index.vue', + ], + imports: [ + 'app/composables/useMessage.ts', + ], + components: [ + 'app/components/Message.vue', + ], + plugins: [ + 'app/plugins/message.ts', + ], + middlewares: [ + 'app/middleware/logger.global.ts', + ], + serverHandlers: [ + 'server/api/collect-scans.ts', + 'server/api/hello.get.ts', + ], + } satisfies TestCollectScans) +}) diff --git a/src/module/ignore.ts b/src/module/ignore.ts index 929786582..c9a25d3bb 100644 --- a/src/module/ignore.ts +++ b/src/module/ignore.ts @@ -22,6 +22,7 @@ export async function setupNuxtIgnore(nuxt: Nuxt) { nuxt.options.ignore = nuxt.options.ignore.filter(i => !isTestFileIgnorePattern(i)) nuxt.hook('modules:done', () => { if (!nuxt._ignore) return + // And add negative patterns to nuxt._ignore for test files (e.g. from .nuxtignore) for (const pattern of resolveIgnorePatterns()) { if (isTestFileIgnorePattern(pattern)) { nuxt._ignore.add(`!${pattern}`) @@ -29,8 +30,9 @@ export async function setupNuxtIgnore(nuxt: Nuxt) { } }) - // But do not register test files inside plugins/ as real Nuxt plugins nuxt.hook('app:resolve', (app) => { + // But do not register test files inside plugins/ and middleware/ as real Nuxt plugins or middleware app.plugins = app.plugins.filter(plugin => !isTestPluginFile(plugin.src)) + app.middleware = app.middleware.filter(middleware => !isTestPluginFile(middleware.path)) }) } diff --git a/test/unit/nuxt-ignore.spec.ts b/test/unit/nuxt-ignore.spec.ts index 1d9c51597..a9d528f9d 100644 --- a/test/unit/nuxt-ignore.spec.ts +++ b/test/unit/nuxt-ignore.spec.ts @@ -129,27 +129,42 @@ describe('nuxtignore', () => { ]) }) - it('should remove test files from plugins', () => { + it('should remove test files from plugins and middleware', () => { const nuxt = createNuxt() const app = { plugins: [ - 'app/plugins/plugin.ts', - 'app/plugins/plugin.spec.ts', - 'app/plugins/plugin.test.ts', - 'app/plugins/plugin.spec-d.ts', - 'app/plugins/plugin.test-d.ts', - 'app/plugins/plugin.special.ts', - 'app/plugins/plugin.testable.ts', + 'app/plugins/example.ts', + 'app/plugins/example.spec.ts', + 'app/plugins/example.test.ts', + 'app/plugins/example.spec-d.ts', + 'app/plugins/example.test-d.ts', + 'app/plugins/example.special.ts', + 'app/plugins/example.testable.ts', ].map(src => ({ src })), + middleware: [ + 'app/middleware/example.ts', + 'app/middleware/example.spec.ts', + 'app/middleware/example.test.ts', + 'app/middleware/example.spec-d.ts', + 'app/middleware/example.test-d.ts', + 'app/middleware/example.special.ts', + 'app/middleware/example.testable.ts', + ].map(path => ({ path })), } setupNuxtIgnore(nuxt) hooks['app:resolve'].at(-1)?.(app) expect(app.plugins.map(({ src }) => src)).toEqual([ - 'app/plugins/plugin.ts', - 'app/plugins/plugin.special.ts', - 'app/plugins/plugin.testable.ts', + 'app/plugins/example.ts', + 'app/plugins/example.special.ts', + 'app/plugins/example.testable.ts', + ]) + + expect(app.middleware.map(({ path }) => path)).toEqual([ + 'app/middleware/example.ts', + 'app/middleware/example.special.ts', + 'app/middleware/example.testable.ts', ]) }) }) From 18ca517c392e42fb94203b3b3241b79922ee67d9 Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Mon, 5 Oct 2026 19:59:21 +0900 Subject: [PATCH 08/11] add negated common test file patterns to `nuxt._ignore` --- .../app/pages/__tests__/index.nuxt.spec.ts | 6 +- .../app-vitest-co-location/test/mount.spec.ts | 3 + .../test/nuxt/mount.nuxt.spec.ts | 12 ++ .../__tests__/SomeComponent.nuxt.test.ts | 8 - .../pages/about/team.nuxt.spec.ts | 9 - .../tests/resolved-files.spec.ts | 2 +- src/module/ignore.ts | 39 ++-- test/unit/nuxt-ignore.spec.ts | 170 ------------------ 8 files changed, 39 insertions(+), 210 deletions(-) create mode 100644 examples/app-vitest-co-location/test/mount.spec.ts create mode 100644 examples/app-vitest-co-location/test/nuxt/mount.nuxt.spec.ts delete mode 100644 examples/app-vitest-full/components/__tests__/SomeComponent.nuxt.test.ts delete mode 100644 examples/app-vitest-full/pages/about/team.nuxt.spec.ts delete mode 100644 test/unit/nuxt-ignore.spec.ts diff --git a/examples/app-vitest-co-location/app/pages/__tests__/index.nuxt.spec.ts b/examples/app-vitest-co-location/app/pages/__tests__/index.nuxt.spec.ts index 78f74f027..63f889e09 100644 --- a/examples/app-vitest-co-location/app/pages/__tests__/index.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/pages/__tests__/index.nuxt.spec.ts @@ -1,8 +1,10 @@ -import { it, expect } from 'vitest' -import { mountSuspended } from '@nuxt/test-utils/runtime' +import { it, expect, vi } from 'vitest' +import { mountSuspended, mockNuxtImport } from '@nuxt/test-utils/runtime' import Page from '../index.vue' +mockNuxtImport(useAppConfig, vi.fn) + it('should mount', async () => { const wrapper = await mountSuspended(Page) expect(wrapper.text()).toContain('Index Page') diff --git a/examples/app-vitest-co-location/test/mount.spec.ts b/examples/app-vitest-co-location/test/mount.spec.ts new file mode 100644 index 000000000..50834c1cc --- /dev/null +++ b/examples/app-vitest-co-location/test/mount.spec.ts @@ -0,0 +1,3 @@ +import {} from '@nuxt/test-utils/runtime' + +import {} from '#components' diff --git a/examples/app-vitest-co-location/test/nuxt/mount.nuxt.spec.ts b/examples/app-vitest-co-location/test/nuxt/mount.nuxt.spec.ts new file mode 100644 index 000000000..a89a6e558 --- /dev/null +++ b/examples/app-vitest-co-location/test/nuxt/mount.nuxt.spec.ts @@ -0,0 +1,12 @@ +import { it, expect, vi } from 'vitest' +import { mockNuxtImport, mountSuspended } from '@nuxt/test-utils/runtime' + +import { Message } from '#components' + +mockNuxtImport(useMessage, vi.fn) + +it('should mount', async () => { + vi.mocked(useMessage).mockImplementationOnce(() => 'Mocked!') + const wrapper = await mountSuspended(Message) + expect(wrapper.text()).toContain('Mocked!') +}) diff --git a/examples/app-vitest-full/components/__tests__/SomeComponent.nuxt.test.ts b/examples/app-vitest-full/components/__tests__/SomeComponent.nuxt.test.ts deleted file mode 100644 index c61d89b7b..000000000 --- a/examples/app-vitest-full/components/__tests__/SomeComponent.nuxt.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { it, expect } from 'vitest' -import { mountSuspended } from '@nuxt/test-utils/runtime' -import { SomeComponent } from '#components' - -it('mount SomeComponent', async () => { - const wrapper = await mountSuspended(SomeComponent) - expect(wrapper.text()).toContain('This is an auto-imported component') -}) diff --git a/examples/app-vitest-full/pages/about/team.nuxt.spec.ts b/examples/app-vitest-full/pages/about/team.nuxt.spec.ts deleted file mode 100644 index cd61405bf..000000000 --- a/examples/app-vitest-full/pages/about/team.nuxt.spec.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { it, expect } from 'vitest' -import { mountSuspended } from '@nuxt/test-utils/runtime' - -import Page from './team.vue' - -it('mount team page', async () => { - const wrapper = await mountSuspended(Page) - expect(wrapper.text()).toContain('About team page') -}) diff --git a/examples/app-vitest-full/tests/resolved-files.spec.ts b/examples/app-vitest-full/tests/resolved-files.spec.ts index cd1e0a8c0..fda2e17a9 100644 --- a/examples/app-vitest-full/tests/resolved-files.spec.ts +++ b/examples/app-vitest-full/tests/resolved-files.spec.ts @@ -19,6 +19,6 @@ test('it should include nuxt spec files', { timeout: 30000 }, async ({ onTestFin const nuxtSpecFiles = testFiles.filter(file => file.project.name === 'nuxt') const regularSpecFiles = testFiles.filter(file => file.project.name === 'node') - expect(nuxtSpecFiles.length).toEqual(36) + expect(nuxtSpecFiles.length).toEqual(34) expect(regularSpecFiles.length).toEqual(3) }) diff --git a/src/module/ignore.ts b/src/module/ignore.ts index c9a25d3bb..83b7114f3 100644 --- a/src/module/ignore.ts +++ b/src/module/ignore.ts @@ -1,38 +1,37 @@ -import { resolveIgnorePatterns } from '@nuxt/kit' import type { Nuxt } from '@nuxt/schema' -const testIgnorePatterns = [ - /\/__tests__\//, - /\.\{(?:spec|test)(?:-d)?(?:,(spec|test)(?:-d)?)*\}\.\{[cm]?[tj]sx?(?:,[cm]?[tj]sx?)*\}$/, - /\.\{(?:spec|test)(?:-d)?(?:,(spec|test)(?:-d)?)*\}\.[cm]?[tj]sx?$/, - /\.(?:spec|test)(?:-d)?\.\{[cm]?[tj]sx?(?:,[cm]?[tj]sx?)*\}$/, - /\.(?:spec|test)(?:-d)?\.[cm]?[tj]sx?$/, -] as const +const TEST_FILE_PATTERN = { + modifiers: ['test', 'test-d', 'spec', 'spec-d'], + extensions: [ + 'ts', 'cts', 'mts', 'tsx', 'ctsx', 'mtsx', + 'js', 'cjs', 'mjs', 'jsx', 'cjsx', 'mjsx', + ], +} as const -function isTestFileIgnorePattern(src: string) { - return !src.startsWith('!') && testIgnorePatterns.some(p => p.test(src)) -} +const IS_TEST_FILE_RE = /\.(?:test|spec)(?:-d)?\.[cm]?[tj]sx?$/ -function isTestPluginFile(src: string) { - return /\.(?:spec|test)(?:-d)?\.[cm]?[tj]sx?$/.test(src) +function isTestFile(src: string) { + return IS_TEST_FILE_RE.test(src) } export async function setupNuxtIgnore(nuxt: Nuxt) { // We want to run Nuxt plugins on test files - nuxt.options.ignore = nuxt.options.ignore.filter(i => !isTestFileIgnorePattern(i)) + // so remove Nuxt's default ignore pattern + nuxt.options.ignore = nuxt.options.ignore.filter(i => i !== '**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}') nuxt.hook('modules:done', () => { if (!nuxt._ignore) return - // And add negative patterns to nuxt._ignore for test files (e.g. from .nuxtignore) - for (const pattern of resolveIgnorePatterns()) { - if (isTestFileIgnorePattern(pattern)) { - nuxt._ignore.add(`!${pattern}`) + // And add negated common test file patterns to `nuxt._ignore` + // so that custom ignore patterns are covered + for (const modifier of TEST_FILE_PATTERN.modifiers) { + for (const extension of TEST_FILE_PATTERN.extensions) { + nuxt._ignore.add(`!**/*.${modifier}.${extension}`) } } }) nuxt.hook('app:resolve', (app) => { // But do not register test files inside plugins/ and middleware/ as real Nuxt plugins or middleware - app.plugins = app.plugins.filter(plugin => !isTestPluginFile(plugin.src)) - app.middleware = app.middleware.filter(middleware => !isTestPluginFile(middleware.path)) + app.plugins = app.plugins.filter(plugin => !isTestFile(plugin.src)) + app.middleware = app.middleware.filter(middleware => !isTestFile(middleware.path)) }) } diff --git a/test/unit/nuxt-ignore.spec.ts b/test/unit/nuxt-ignore.spec.ts deleted file mode 100644 index a9d528f9d..000000000 --- a/test/unit/nuxt-ignore.spec.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { Nuxt, NuxtHookName } from 'nuxt/schema' -import { setupNuxtIgnore } from '../../src/module/ignore' - -const { resolveIgnorePatterns } = vi.hoisted(() => ({ - resolveIgnorePatterns: vi.fn(() => [] as string[]), -})) - -vi.mock(import('@nuxt/kit'), async (importOriginal) => { - const original = await importOriginal() - return { - ...original, - resolveIgnorePatterns, - } -}) - -type Hook = (...args: unknown[]) => unknown -let hooks: Record - -function createNuxt() { - hooks = {} as typeof hooks - const nuxt = { - hook: (name: NuxtHookName, fn: Hook) => { - (hooks[name] ||= []).push(fn) - }, - options: {}, - } as Nuxt - nuxt.options.ignore = [] - return nuxt -} - -describe('nuxtignore', () => { - beforeEach(() => { - vi.clearAllMocks() - resolveIgnorePatterns.mockImplementation(() => []) - }) - - it('should remove test file patterns from `nuxt.options.ignore`', () => { - const nuxt = createNuxt() - - nuxt.options.ignore = [ - '**/*.stories.{js,cts,mts,ts,jsx,tsx}', - '**/*.d.{cts,mts,ts}', - '!**/pages/hide/__tests__/**', - '!**/pages/**/this-is-page.{spec,test}.{js,cts,mts,ts,jsx,tsx}', - '!**/components/**/this-is-components.spec.ts', - '**/*.latest.ts', - '**/*.testing.ts', - '**/*.special.ts', - '**/*.inspect.ts', - '**/*.spec.tsxp', - '**/*.{latest,other}.ts', - '**/__tests__/**/*', - '**/*.spec.js', - '**/*.spec.mjs', - '**/*.spec.cjs', - '**/*.spec.jsx', - '**/*.spec.ts', - '**/*.spec.mts', - '**/*.spec.cts', - '**/*.spec.tsx', - '**/*.test.js', - '**/*.test.mjs', - '**/*.test.cjs', - '**/*.test.jsx', - '**/*.test.ts', - '**/*.test.mts', - '**/*.test.cts', - '**/*.test.tsx', - '**/*.spec-d.ts', - '**/*.test-d.ts', - '**/*.{spec,test}.ts', - '**/*.{test-d,spec-d}.ts', - '**/*.spec.{js,cts,mts,ts,jsx,tsx}', - '**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}', - '**/*.{spec,test,spec-d,test-d}.{js,cts,mts,ts,jsx,tsx}', - ] - - setupNuxtIgnore(nuxt) - expect(nuxt.options.ignore).toEqual([ - '**/*.stories.{js,cts,mts,ts,jsx,tsx}', - '**/*.d.{cts,mts,ts}', - '!**/pages/hide/__tests__/**', - '!**/pages/**/this-is-page.{spec,test}.{js,cts,mts,ts,jsx,tsx}', - '!**/components/**/this-is-components.spec.ts', - '**/*.latest.ts', - '**/*.testing.ts', - '**/*.special.ts', - '**/*.inspect.ts', - '**/*.spec.tsxp', - '**/*.{latest,other}.ts', - ]) - }) - - it('should add negative test file patterns to `nuxt._ignore`', async () => { - resolveIgnorePatterns.mockImplementationOnce(() => [ - '**/*.stories.{js,cts,mts,ts,jsx,tsx}', - '**/*.d.{cts,mts,ts}', - '**/__tests__/**', - '**/*.{spec,test}.ts', - '**/*.{test-d,spec-d}.ts', - '**/*.spec.{js,cts,mts,ts,jsx,tsx}', - '**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}', - '**/*.{spec,test,spec-d,test-d}.{js,cts,mts,ts,jsx,tsx}', - '!**/pages/__tests__/**', - '!**/pages/**/this-is-page.{spec,test}.{js,cts,mts,ts,jsx,tsx}', - '!**/components/**/this-is-components.spec.ts', - ]) - - const addedNegativeIgnores = [] as string[] - - const nuxt = createNuxt() - nuxt._ignore = { - add: (p: string) => { - addedNegativeIgnores.push(p) - }, - } as typeof nuxt._ignore - - setupNuxtIgnore(nuxt) - hooks['modules:done'].at(-1)?.() - - expect(addedNegativeIgnores).toEqual([ - '!**/__tests__/**', - '!**/*.{spec,test}.ts', - '!**/*.{test-d,spec-d}.ts', - '!**/*.spec.{js,cts,mts,ts,jsx,tsx}', - '!**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}', - '!**/*.{spec,test,spec-d,test-d}.{js,cts,mts,ts,jsx,tsx}', - ]) - }) - - it('should remove test files from plugins and middleware', () => { - const nuxt = createNuxt() - const app = { - plugins: [ - 'app/plugins/example.ts', - 'app/plugins/example.spec.ts', - 'app/plugins/example.test.ts', - 'app/plugins/example.spec-d.ts', - 'app/plugins/example.test-d.ts', - 'app/plugins/example.special.ts', - 'app/plugins/example.testable.ts', - ].map(src => ({ src })), - middleware: [ - 'app/middleware/example.ts', - 'app/middleware/example.spec.ts', - 'app/middleware/example.test.ts', - 'app/middleware/example.spec-d.ts', - 'app/middleware/example.test-d.ts', - 'app/middleware/example.special.ts', - 'app/middleware/example.testable.ts', - ].map(path => ({ path })), - } - - setupNuxtIgnore(nuxt) - hooks['app:resolve'].at(-1)?.(app) - - expect(app.plugins.map(({ src }) => src)).toEqual([ - 'app/plugins/example.ts', - 'app/plugins/example.special.ts', - 'app/plugins/example.testable.ts', - ]) - - expect(app.middleware.map(({ path }) => path)).toEqual([ - 'app/middleware/example.ts', - 'app/middleware/example.special.ts', - 'app/middleware/example.testable.ts', - ]) - }) -}) From 0690c993b951fcd5a83b5f6d3cec80fe45007dc6 Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Mon, 5 Oct 2026 21:06:39 +0900 Subject: [PATCH 09/11] remove unused test file --- examples/app-vitest-co-location/test/mount.spec.ts | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 examples/app-vitest-co-location/test/mount.spec.ts diff --git a/examples/app-vitest-co-location/test/mount.spec.ts b/examples/app-vitest-co-location/test/mount.spec.ts deleted file mode 100644 index 50834c1cc..000000000 --- a/examples/app-vitest-co-location/test/mount.spec.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {} from '@nuxt/test-utils/runtime' - -import {} from '#components' From 2c7d3fc34ede580f7a09f662d5a41a9396a87354 Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Tue, 6 Oct 2026 00:04:10 +0900 Subject: [PATCH 10/11] also do not register test files in pages, components, and imports --- .../app/pages/about.vue | 6 ++++ .../app/pages/about/index.nuxt.spec.ts | 11 +++++++ .../app/pages/about/index.vue | 5 +++ .../app/pages/index.nuxt.spec.ts | 26 ++++++++++++--- .../modules/collect-scans.ts | 14 ++++++-- .../test/nuxt/nuxt-ignore-keep.e2e.test.ts | 2 ++ src/module/ignore.ts | 33 ++++++++++++++++--- 7 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 examples/app-vitest-co-location/app/pages/about.vue create mode 100644 examples/app-vitest-co-location/app/pages/about/index.nuxt.spec.ts create mode 100644 examples/app-vitest-co-location/app/pages/about/index.vue diff --git a/examples/app-vitest-co-location/app/pages/about.vue b/examples/app-vitest-co-location/app/pages/about.vue new file mode 100644 index 000000000..4ff3cd106 --- /dev/null +++ b/examples/app-vitest-co-location/app/pages/about.vue @@ -0,0 +1,6 @@ + diff --git a/examples/app-vitest-co-location/app/pages/about/index.nuxt.spec.ts b/examples/app-vitest-co-location/app/pages/about/index.nuxt.spec.ts new file mode 100644 index 000000000..9516bcea6 --- /dev/null +++ b/examples/app-vitest-co-location/app/pages/about/index.nuxt.spec.ts @@ -0,0 +1,11 @@ +import { it, expect } from 'vitest' +import { mountSuspended } from '@nuxt/test-utils/runtime' +import { NuxtPage } from '#components' + +it('should mount child page', async () => { + const wrapper = await mountSuspended(NuxtPage, { + route: '/about', + }) + expect(wrapper.text()).toContain('About Page') + expect(wrapper.text()).toContain('About Index') +}) diff --git a/examples/app-vitest-co-location/app/pages/about/index.vue b/examples/app-vitest-co-location/app/pages/about/index.vue new file mode 100644 index 000000000..49d568736 --- /dev/null +++ b/examples/app-vitest-co-location/app/pages/about/index.vue @@ -0,0 +1,5 @@ + diff --git a/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts b/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts index 63691dab7..28852ce05 100644 --- a/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts +++ b/examples/app-vitest-co-location/app/pages/index.nuxt.spec.ts @@ -17,9 +17,25 @@ it('should ignore plugins and middlewares', async () => { const wrapper = await mountSuspended(Page) const scans: TestCollectScans = JSON.parse(wrapper.find('#scans').text()) - expect(scans.plugins).toContain('app/plugins/message.ts') - expect(scans.plugins).not.toContain('app/plugins/message.nuxt.test.ts') - - expect(scans.middlewares).toContain('app/middleware/logger.global.ts') - expect(scans.middlewares).not.toContain('app/middleware/logger.global.nuxt.spec.ts') + expect(scans).toEqual({ + pages: [ + 'app/pages/about.vue', + 'app/pages/about/index.vue', + 'app/pages/index.vue', + ], + imports: [ + 'app/composables/useMessage.ts', + ], + components: [ + 'app/components/Message.vue', + ], + plugins: [ + 'app/plugins/message.ts', + ], + middlewares: [ + 'app/middleware/logger.global.ts', + ], + // empty because client environment + serverHandlers: [], + }) }) diff --git a/examples/app-vitest-co-location/modules/collect-scans.ts b/examples/app-vitest-co-location/modules/collect-scans.ts index 0999689c3..4dba7d9bc 100644 --- a/examples/app-vitest-co-location/modules/collect-scans.ts +++ b/examples/app-vitest-co-location/modules/collect-scans.ts @@ -40,8 +40,18 @@ export default defineNuxtModule({ }) nuxt.addHooks({ - 'pages:extend'(pages) { - scans.pages = normalize(pages.map(p => p.file)) + 'pages:extend'(_pages) { + const getPaths = (pages: typeof _pages, paths: string[] = []) => { + for (const p of pages) { + if (p.file) { + paths.push(p.file) + } + getPaths(p.children ?? [], paths) + } + return paths + } + + scans.pages = normalize(getPaths(_pages)) }, 'imports:extend'(imports) { scans.imports = normalize(imports.map(p => p.from)) diff --git a/examples/app-vitest-co-location/test/nuxt/nuxt-ignore-keep.e2e.test.ts b/examples/app-vitest-co-location/test/nuxt/nuxt-ignore-keep.e2e.test.ts index 6f27c1a58..026efd0c5 100644 --- a/examples/app-vitest-co-location/test/nuxt/nuxt-ignore-keep.e2e.test.ts +++ b/examples/app-vitest-co-location/test/nuxt/nuxt-ignore-keep.e2e.test.ts @@ -10,6 +10,8 @@ await setup({ it('should keep ignore test files in runtime dirs', async () => { expect(await $fetch('/api/collect-scans')).toEqual({ pages: [ + 'app/pages/about.vue', + 'app/pages/about/index.vue', 'app/pages/index.vue', ], imports: [ diff --git a/src/module/ignore.ts b/src/module/ignore.ts index 83b7114f3..8e000e0b4 100644 --- a/src/module/ignore.ts +++ b/src/module/ignore.ts @@ -14,6 +14,16 @@ function isTestFile(src: string) { return IS_TEST_FILE_RE.test(src) } +function dropTestFiles(items: T[], toPath: (item: T) => string | undefined) { + const filtered = items.filter((item) => { + const path = toPath(item) + return !path || !isTestFile(path) + }) + if (filtered.length !== items.length) { + items.splice(0, items.length, ...filtered) + } +} + export async function setupNuxtIgnore(nuxt: Nuxt) { // We want to run Nuxt plugins on test files // so remove Nuxt's default ignore pattern @@ -29,9 +39,24 @@ export async function setupNuxtIgnore(nuxt: Nuxt) { } }) - nuxt.hook('app:resolve', (app) => { - // But do not register test files inside plugins/ and middleware/ as real Nuxt plugins or middleware - app.plugins = app.plugins.filter(plugin => !isTestFile(plugin.src)) - app.middleware = app.middleware.filter(middleware => !isTestFile(middleware.path)) + // But do not register test files into the Nuxt app + nuxt.addHooks({ + 'app:resolve'(app) { + dropTestFiles(app.plugins, v => v.src) + dropTestFiles(app.middleware, v => v.path) + }, + 'components:extend'(components) { + dropTestFiles(components, v => v.filePath) + }, + 'imports:extend'(imports) { + dropTestFiles(imports, v => v.from) + }, + 'pages:extend'(_pages) { + const dropTestPages = (pages: typeof _pages) => { + dropTestFiles(pages, p => p.file) + pages.forEach(p => dropTestPages(p.children ?? [])) + } + dropTestPages(_pages) + }, }) } From adcd4082040f5d02e88bb9c47e87c5fe8f9e387b Mon Sep 17 00:00:00 2001 From: yamachi4416 Date: Tue, 6 Oct 2026 19:53:15 +0900 Subject: [PATCH 11/11] restore un-ignore logic, remove relativePath arg, and change timing --- src/module.ts | 2 -- src/module/ignore.ts | 62 -------------------------------------------- src/module/mock.ts | 17 ++++++++++++ 3 files changed, 17 insertions(+), 64 deletions(-) delete mode 100644 src/module/ignore.ts diff --git a/src/module.ts b/src/module.ts index 77730bba5..f1160a7b8 100644 --- a/src/module.ts +++ b/src/module.ts @@ -4,7 +4,6 @@ import type { TestUserConfig as VitestConfig } from 'vitest/config' import { join, relative } from 'pathe' import { isCI } from 'std-env' -import { setupNuxtIgnore } from './module/ignore.ts' import { setupImportMocking } from './module/mock.ts' import { NuxtRootStubPlugin } from './module/plugins/entry.ts' import { runInstallWizard } from './module/install-wizard.ts' @@ -42,7 +41,6 @@ export default defineNuxtModule({ // Setup for Vitest if (options._fromVitestConfig) { - setupNuxtIgnore(nuxt) await setupImportMocking(nuxt) // inline runtime config the way dev builds do diff --git a/src/module/ignore.ts b/src/module/ignore.ts deleted file mode 100644 index 8e000e0b4..000000000 --- a/src/module/ignore.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { Nuxt } from '@nuxt/schema' - -const TEST_FILE_PATTERN = { - modifiers: ['test', 'test-d', 'spec', 'spec-d'], - extensions: [ - 'ts', 'cts', 'mts', 'tsx', 'ctsx', 'mtsx', - 'js', 'cjs', 'mjs', 'jsx', 'cjsx', 'mjsx', - ], -} as const - -const IS_TEST_FILE_RE = /\.(?:test|spec)(?:-d)?\.[cm]?[tj]sx?$/ - -function isTestFile(src: string) { - return IS_TEST_FILE_RE.test(src) -} - -function dropTestFiles(items: T[], toPath: (item: T) => string | undefined) { - const filtered = items.filter((item) => { - const path = toPath(item) - return !path || !isTestFile(path) - }) - if (filtered.length !== items.length) { - items.splice(0, items.length, ...filtered) - } -} - -export async function setupNuxtIgnore(nuxt: Nuxt) { - // We want to run Nuxt plugins on test files - // so remove Nuxt's default ignore pattern - nuxt.options.ignore = nuxt.options.ignore.filter(i => i !== '**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}') - nuxt.hook('modules:done', () => { - if (!nuxt._ignore) return - // And add negated common test file patterns to `nuxt._ignore` - // so that custom ignore patterns are covered - for (const modifier of TEST_FILE_PATTERN.modifiers) { - for (const extension of TEST_FILE_PATTERN.extensions) { - nuxt._ignore.add(`!**/*.${modifier}.${extension}`) - } - } - }) - - // But do not register test files into the Nuxt app - nuxt.addHooks({ - 'app:resolve'(app) { - dropTestFiles(app.plugins, v => v.src) - dropTestFiles(app.middleware, v => v.path) - }, - 'components:extend'(components) { - dropTestFiles(components, v => v.filePath) - }, - 'imports:extend'(imports) { - dropTestFiles(imports, v => v.from) - }, - 'pages:extend'(_pages) { - const dropTestPages = (pages: typeof _pages) => { - dropTestFiles(pages, p => p.file) - pages.forEach(p => dropTestPages(p.children ?? [])) - } - dropTestPages(_pages) - }, - }) -} diff --git a/src/module/mock.ts b/src/module/mock.ts index fe2142474..10f25f050 100644 --- a/src/module/mock.ts +++ b/src/module/mock.ts @@ -1,4 +1,5 @@ import type { Nuxt, NuxtHooks } from '@nuxt/schema' +import { resolveIgnorePatterns } from '@nuxt/kit' import { createMockPlugin } from './plugins/mock.ts' import type { MockPluginContext } from './plugins/mock.ts' @@ -35,5 +36,21 @@ export async function setupImportMocking(nuxt: Nuxt) { } }) + const ignorePatterns: string[] = [] + nuxt.hook('vite:configResolved', (_, { isClient }) => { + if (!isClient) return + ignorePatterns.push(...resolveIgnorePatterns()) + }) + addVitePlugin({ + name: 'nuxt:vitest:un-ignore-for-testing', + configureVitest() { + // We want to run Nuxt plugins on test files, so un-ignore all patterns + for (const pattern of new Set(ignorePatterns)) { + if (pattern.startsWith('!')) continue + nuxt._ignore?.add(`!${pattern}`) + } + }, + }) + addVitePlugin(createMockPlugin(ctx).vite()) }