Skip to content

Comments

feat: "I'm feeling lucky" search redirection with bang (!) ending#1552

Open
shuuji3 wants to merge 3 commits intonpmx-dev:mainfrom
shuuji3:feat/im-feeling-lucky-with-bang-ending
Open

feat: "I'm feeling lucky" search redirection with bang (!) ending#1552
shuuji3 wants to merge 3 commits intonpmx-dev:mainfrom
shuuji3:feat/im-feeling-lucky-with-bang-ending

Conversation

@shuuji3
Copy link
Member

@shuuji3 shuuji3 commented Feb 21, 2026

resolve #996

@vercel
Copy link

vercel bot commented Feb 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs.npmx.dev Ready Ready Preview, Comment Feb 22, 2026 8:07am
npmx.dev Ready Ready Preview, Comment Feb 22, 2026 8:07am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
npmx-lunaria Ignored Ignored Feb 22, 2026 8:07am

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 21, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

The PR adds "I'm Feeling Lucky" behaviour to search: queries are trimmed and a trailing exclamation mark is removed for matching. A watcher on displayResults (now immediate) and Enter key handling were updated to detect queries ending with "!" (feeling-lucky) and, when there is an exact first-result match, navigate directly to that package; otherwise normal search results are shown. A pendingEnterQuery flow ensures consistent trimmed/cleaned values for future navigation. An end-to-end test file verifies redirect-on-q=...! and normal search behaviour for q=... without the exclamation.

Suggested reviewers

  • danielroe
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR implements exact-match redirection via '!' suffix but #996 specifies a 'feeling-lucky=1' URL parameter. The implementation achieves the functional goal but diverges from the proposed parameter approach. Clarify whether using the '!' suffix fulfils the requirement or if the 'feeling-lucky=1' parameter implementation is still needed for issue #996 closure.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description clearly explains the feature: search queries ending with '!' redirect to exact matches, while normal queries show search results. This directly relates to the changeset.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the 'I'm feeling lucky' search redirection feature as described in the objectives, with no unrelated modifications present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
test/e2e/search-feeling-lucky.spec.ts (1)

3-13: Add a test case for the bang query with no exact match (e.g., nuxt-no!).

The PR description explicitly lists https://npmx.dev/search?q=nuxt-no! as expected to show normal search results (no redirect). This is the key edge-case that distinguishes the feature from a naive "strip ! and always redirect" implementation, and it's currently untested.

Suggested additional test
   test('normal search query (without "!") should not redirect', async ({ page, goto }) => {
     await goto('/search?q=nuxt', { waitUntil: 'hydration' })
     await expect(page.locator('[data-result-index="0"]').first()).toBeVisible({ timeout: 15000 })
     await expect(page).toHaveURL(/\/search\?q=nuxt/)
   })
+
+  test('query ending with "!" but no exact match should show search results', async ({ page, goto }) => {
+    await goto('/search?q=nuxt-no!', { waitUntil: 'hydration' })
+    await expect(page.locator('[data-result-index="0"]').first()).toBeVisible({ timeout: 15000 })
+    await expect(page).toHaveURL(/\/search\?q=nuxt-no!/)
+  })
 })

Comment on lines +427 to +454
// Watch for results to navigate when Enter was pressed before results arrived,
// or for "I'm feeling lucky" redirection when the query ends with "!" and there is the exact match.
watch(
displayResults,
results => {
const rawQuery = normalizeSearchParam(route.query.q)
const isFeelingLucky = rawQuery.endsWith('!')

// Check if input is still focused (user hasn't started navigating or clicked elsewhere)
if (document.activeElement?.tagName !== 'INPUT') {
pendingEnterQuery.value = null
return
}
if (!pendingEnterQuery.value && !isFeelingLucky) return

// Navigate if first result matches the query that was entered
const firstResult = results[0]
// eslint-disable-next-line no-console
console.log('[search] watcher fired', {
pending: pendingEnterQuery.value,
firstResult: firstResult?.package.name,
})
if (firstResult?.package.name === pendingEnterQuery.value) {
pendingEnterQuery.value = null
navigateToPackage(firstResult.package.name)
}
})
// For manual Enter, check if input is still focused (user hasn't started navigating or clicked elsewhere)
if (pendingEnterQuery.value && document.activeElement?.tagName !== 'INPUT') {
pendingEnterQuery.value = null
return
}

const target = pendingEnterQuery.value || rawQuery.replace(/!$/, '')
if (!target) return

// Navigate if first result matches the target query
const firstResult = results[0]
if (firstResult?.package.name === target) {
pendingEnterQuery.value = null
navigateToPackage(firstResult.package.name)
}
},
{ immediate: true },
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Case-sensitive comparison may prevent redirect for mixed-case input.

Line 448 performs a strict === comparison between firstResult?.package.name and target. However, npm package names are case-insensitive, and the visibleResults reordering logic (line 89) already uses .toLowerCase() when finding exact matches. If a user types Nuxt!, target becomes "Nuxt" while the actual package name is "nuxt", so the redirect silently fails.

The same issue exists in the Enter-key handler at line 467.

Suggested fix
-    const target = pendingEnterQuery.value || rawQuery.replace(/!$/, '')
+    const target = (pendingEnterQuery.value || rawQuery.replace(/!$/, '')).toLowerCase()
     if (!target) return

     // Navigate if first result matches the target query
     const firstResult = results[0]
-    if (firstResult?.package.name === target) {
+    if (firstResult?.package.name.toLowerCase() === target) {

And in the Enter-key handler:

-    const cleanedInputValue = inputValue.replace(/!$/, '')
+    const cleanedInputValue = inputValue.replace(/!$/, '').toLowerCase()

     // Check if first result matches the input value exactly
     const firstResult = displayResults.value[0]
-    if (firstResult?.package.name === cleanedInputValue) {
+    if (firstResult?.package.name.toLowerCase() === cleanedInputValue) {

@codecov
Copy link

codecov bot commented Feb 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automatically redirect from the search page

1 participant