import { resolve } from 'node:path' import { describe, expect, it, vi } from 'vitest' import type { ComponentsDir } from 'nuxt/schema' import { resolveComponentName, scanComponents } from '../src/components/scan' const fixtureDir = resolve(__dirname, 'fixture') const rFixture = (...p: string[]) => resolve(fixtureDir, ...p) vi.mock('@nuxt/kit', () => ({ isIgnored: () => false })) const dirs: ComponentsDir[] = [ { path: rFixture('components/islands'), enabled: true, extensions: [ 'vue' ], pattern: '**/*.{vue,}', ignore: [ '**/*.stories.{js,ts,jsx,tsx}', '**/*{M,.m,-m}ixin.{js,ts,jsx,tsx}', '**/*.d.ts' ], transpile: false, island: true }, { path: rFixture('components/global'), enabled: true, extensions: [ 'vue' ], pattern: '**/*.{vue,}', ignore: [ '**/*.stories.{js,ts,jsx,tsx}', '**/*{M,.m,-m}ixin.{js,ts,jsx,tsx}', '**/*.d.ts' ], transpile: false, global: true }, { path: rFixture('components'), enabled: true, extensions: [ 'vue' ], pattern: '**/*.{vue,}', ignore: [ '**/*.stories.{js,ts,jsx,tsx}', '**/*{M,.m,-m}ixin.{js,ts,jsx,tsx}', '**/*.d.ts' ], transpile: false }, { path: rFixture('components'), enabled: true, extensions: [ 'vue' ], pattern: '**/*.{vue,}', ignore: [ '**/*.stories.{js,ts,jsx,tsx}', '**/*{M,.m,-m}ixin.{js,ts,jsx,tsx}', '**/*.d.ts' ], transpile: false }, { path: rFixture('components'), extensions: [ 'vue' ], prefix: 'nuxt', enabled: true, pattern: '**/*.{vue,}', ignore: [ '**/*.stories.{js,ts,jsx,tsx}', '**/*{M,.m,-m}ixin.{js,ts,jsx,tsx}', '**/*.d.ts' ], transpile: false } ] const expectedComponents = [ { chunkName: 'components/isle-server', export: 'default', global: undefined, island: true, kebabName: 'isle', mode: 'server', pascalName: 'Isle', prefetch: false, preload: false, priority: 1, shortPath: 'components/islands/Isle.vue' }, { chunkName: 'components/glob', export: 'default', global: true, island: undefined, kebabName: 'glob', mode: 'all', pascalName: 'Glob', prefetch: false, preload: false, priority: 1, shortPath: 'components/global/Glob.vue' }, { mode: 'all', pascalName: 'HelloWorld', kebabName: 'hello-world', chunkName: 'components/hello-world', shortPath: 'components/HelloWorld.vue', export: 'default', global: undefined, island: undefined, prefetch: false, preload: false, priority: 1 }, { mode: 'client', pascalName: 'Nuxt3', kebabName: 'nuxt3', chunkName: 'components/nuxt3-client', shortPath: 'components/Nuxt3.client.vue', export: 'default', global: undefined, island: undefined, prefetch: false, preload: false, priority: 1 }, { mode: 'server', pascalName: 'Nuxt3', kebabName: 'nuxt3', chunkName: 'components/nuxt3-server', shortPath: 'components/Nuxt3.server.vue', export: 'default', global: undefined, island: undefined, prefetch: false, preload: false, priority: 1 }, { chunkName: 'components/client-component-with-props', export: 'default', global: undefined, island: undefined, kebabName: 'client-component-with-props', mode: 'all', pascalName: 'ClientComponentWithProps', prefetch: false, preload: false, priority: 1, shortPath: 'components/client/ComponentWithProps.vue' }, { chunkName: 'components/client-with-client-only-setup', export: 'default', global: undefined, island: undefined, kebabName: 'client-with-client-only-setup', mode: 'all', pascalName: 'ClientWithClientOnlySetup', prefetch: false, preload: false, priority: 1, shortPath: 'components/client/WithClientOnlySetup.vue' }, { mode: 'server', pascalName: 'ParentFolder', kebabName: 'parent-folder', chunkName: 'components/parent-folder-server', shortPath: 'components/parent-folder/index.server.vue', export: 'default', global: undefined, island: undefined, prefetch: false, preload: false, priority: 1 }, { chunkName: 'components/same-name-same', export: 'default', global: undefined, island: undefined, kebabName: 'same-name-same', mode: 'all', pascalName: 'SameNameSame', prefetch: false, preload: false, priority: 1, shortPath: 'components/same-name/same/Same.vue' }, { chunkName: 'components/some-glob', export: 'default', global: true, island: undefined, kebabName: 'some-glob', mode: 'all', pascalName: 'SomeGlob', prefetch: false, preload: false, priority: 1, shortPath: 'components/some-glob.global.vue' }, { chunkName: 'components/some-server', export: 'default', global: undefined, island: true, kebabName: 'some', mode: 'server', pascalName: 'Some', prefetch: false, preload: false, priority: 1, shortPath: 'components/some.island.vue' } ] const srcDir = rFixture('.') it('components:scanComponents', async () => { const scannedComponents = await scanComponents(dirs, srcDir) for (const c of scannedComponents) { // @ts-expect-error filePath is not optional but we don't want it to be in the snapshot delete c.filePath } expect(scannedComponents).deep.eq(expectedComponents) }) const tests: Array<[string, string[], string]> = [ ['WithClientOnlySetup', ['Client'], 'ClientWithClientOnlySetup'], ['ItemHolderItem', ['Item', 'Holder', 'Item'], 'ItemHolderItem'], ['Item', ['Item'], 'Item'], ['Item', ['Item', 'Item'], 'Item'], ['ItemTest', ['Item', 'Test'], 'ItemTest'], ['ThingItemTest', ['Item', 'Thing'], 'ItemThingItemTest'], ['Item', ['Thing', 'Item'], 'ThingItem'], ['Item', ['Item', 'Holder', 'Item'], 'ItemHolderItem'], ['ItemHolder', ['Item', 'Holder', 'Item'], 'ItemHolderItemHolder'], ['ThingItemTest', ['Item', 'Thing', 'Foo'], 'ItemThingFooThingItemTest'], ['ItemIn', ['Item', 'Holder', 'Item', 'In'], 'ItemHolderItemIn'], ['Item', ['Item', 'Holder', 'Test'], 'ItemHolderTestItem'], ['ItemHolderItem', ['Item', 'Holder', 'Item', 'Holder'], 'ItemHolderItemHolderItem'], ['Icones', ['Icon'], 'IconIcones'], ['Icon', ['Icones'], 'IconesIcon'], ['IconHolder', ['IconHolder'], 'IconHolder'], ['GameList', ['Desktop', 'ShareGame', 'Review', 'Detail'], 'DesktopShareGameReviewDetailGameList'] ] describe('components:resolveComponentName', () => { it.each(tests)('resolves %s with prefix parts %s and filename %s', (fileName, prefixParts: string[], result) => { expect(resolveComponentName(fileName, prefixParts)).toBe(result) }) })