diff --git a/packages/nuxt-cli/package.json b/packages/nuxt-cli/package.json index 84ff488c..a674bd61 100644 --- a/packages/nuxt-cli/package.json +++ b/packages/nuxt-cli/package.json @@ -67,7 +67,7 @@ "consola": "^3.4.2", "defu": "^6.1.7", "exsolve": "^1.1.1", - "fuzzysort": "^3.1.0", + "fuzzysort": "^4.0.1", "get-port-please": "^3.2.0", "nypm": "^0.6.9", "obug": "^2.1.4", diff --git a/packages/nuxt-cli/src/commands/module/_autocomplete.ts b/packages/nuxt-cli/src/commands/module/_autocomplete.ts index a02d8c8e..066dd0b4 100644 --- a/packages/nuxt-cli/src/commands/module/_autocomplete.ts +++ b/packages/nuxt-cli/src/commands/module/_autocomplete.ts @@ -93,7 +93,7 @@ export async function selectModulesAutocomplete(options: AutocompleteOptions): P return true let results = matches.get(search) if (!results) { - results = new Set(fuzzysort.go(search, targets, { keys: ['npm', 'name', 'category'] }).map(r => r.obj.value)) + results = new Set(fuzzysort.go(search, targets, { keys: ['npm', 'name', 'category'], threshold: 0, limit: 0 }).map(r => r.obj.value)) matches.set(search, results) } return results.has(option.value) diff --git a/packages/nuxt-cli/src/commands/module/search.ts b/packages/nuxt-cli/src/commands/module/search.ts index 8b896259..c3181fb1 100644 --- a/packages/nuxt-cli/src/commands/module/search.ts +++ b/packages/nuxt-cli/src/commands/module/search.ts @@ -88,7 +88,6 @@ async function findModuleByKeywords(query: string, nuxtVersion: string) { const results = fuzzysort.go(query, targets, { keys: ['name', 'npm', 'rest'], - all: !query, threshold: SCORE_THRESHOLD, limit: RESULT_LIMIT, }).map(({ obj: { item } }) => { diff --git a/packages/nuxt-cli/test/unit/commands/module/_autocomplete.spec.ts b/packages/nuxt-cli/test/unit/commands/module/_autocomplete.spec.ts index 37d25739..9079bd4a 100644 --- a/packages/nuxt-cli/test/unit/commands/module/_autocomplete.spec.ts +++ b/packages/nuxt-cli/test/unit/commands/module/_autocomplete.spec.ts @@ -337,6 +337,52 @@ describe('selectModulesAutocomplete', () => { expect(capturedFilter!('tail', { value: '@nuxt/tailwind', label: '@nuxt/tailwind' })).toBe(true) expect(capturedFilter!('tail', { value: '@nuxt/image', label: '@nuxt/image' })).toBe(false) }) + + it('should keep every match, not just the first handful', async () => { + vi.doMock('std-env', () => ({ + hasTTY: true, + })) + + let capturedFilter: ((search: string, option: any) => boolean) | undefined + + mockAutocompleteMultiselect.mockImplementation(async (opts: any) => { + capturedFilter = opts.filter + return [] + }) + + const { selectModulesAutocomplete } = await import('../../../../src/commands/module/_autocomplete') + + const modules = Array.from({ length: 30 }, (_, i) => createMockModule({ + npm: `nuxt-module-${i}`, + name: `module-${i}`, + })) + + await selectModulesAutocomplete({ modules }) + + const matched = modules.filter(m => capturedFilter!('module', { value: m.npm, label: m.npm })) + expect(matched).toHaveLength(30) + }) + + it('should keep low-scoring subsequence matches', async () => { + vi.doMock('std-env', () => ({ + hasTTY: true, + })) + + let capturedFilter: ((search: string, option: any) => boolean) | undefined + + mockAutocompleteMultiselect.mockImplementation(async (opts: any) => { + capturedFilter = opts.filter + return [] + }) + + const { selectModulesAutocomplete } = await import('../../../../src/commands/module/_autocomplete') + + await selectModulesAutocomplete({ + modules: [createMockModule({ npm: '@nuxt/typescript-build', name: 'typescript-build', category: 'Devtools' })], + }) + + expect(capturedFilter!('tsb', { value: '@nuxt/typescript-build', label: '@nuxt/typescript-build' })).toBe(true) + }) }) describe('options structure', () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eaf37d3c..d4e56418 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,8 +200,8 @@ importers: specifier: ^1.1.1 version: 1.1.1 fuzzysort: - specifier: ^3.1.0 - version: 3.1.0 + specifier: ^4.0.1 + version: 4.0.1 get-port-please: specifier: ^3.2.0 version: 3.2.0 @@ -4005,8 +4005,8 @@ packages: resolution: {integrity: sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==} engines: {node: '>=18'} - fuzzysort@3.1.0: - resolution: {integrity: sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==} + fuzzysort@4.0.1: + resolution: {integrity: sha512-aQwFrCgx1HmMSnxK5UQIsRL9KBwj2AffSMo8SmbyvZvd1NcZXvVRYm8/hs9QTn+rDwa3QKg1TZoctf8Y3t1vgg==} generic-names@4.0.0: resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==} @@ -9041,7 +9041,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@types/node@24.13.3)(@vitest/coverage-v8@4.1.10)(vite@7.3.3(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.46.2)(yaml@2.9.0)) + vitest: 4.1.10(@types/node@24.13.3)(@vitest/coverage-v8@4.1.10)(vite@8.2.0(@types/node@24.13.3)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.46.2)(yaml@2.9.0)) '@vitest/eslint-plugin@1.6.23(@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@4.1.10)': dependencies: @@ -10515,7 +10515,7 @@ snapshots: function-timeout@1.0.2: {} - fuzzysort@3.1.0: {} + fuzzysort@4.0.1: {} generic-names@4.0.0: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7a64dbbf..94887c33 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,6 +5,7 @@ minimumReleaseAgeExclude: - nuxt-nightly - vue - '@vue/*' + - fuzzysort@4.0.1 packages: - packages/*