Skip to content

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

Open
shuuji3 wants to merge 5 commits into
npmx-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 5 commits into
npmx-dev:mainfrom
shuuji3:feat/im-feeling-lucky-with-bang-ending

Conversation

@shuuji3

Copy link
Copy Markdown
Member

resolve#996

@vercel

vercelBot commented Feb 21, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
docs.npmx.devReadyReadyPreview, CommentMay 2, 2026 1:47pm
npmx.devReadyReadyPreview, CommentMay 2, 2026 1:47pm
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
npmx-lunariaIgnoredIgnoredMay 2, 2026 1:47pm

Request Review

@coderabbitai

coderabbitaiBot commented Feb 21, 2026

Copy link
Copy Markdown
Contributor

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 nameStatusExplanationResolution
Linked Issues check❓ InconclusiveThe 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 nameStatusExplanation
Description check✅ PassedThe 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✅ PassedAll 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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

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 threadapp/pages/search.vue
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
Copy Markdown
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) {

Comment threadtest/e2e/search-feeling-lucky.spec.ts Outdated
@codecov

codecovBot commented Feb 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 16 lines in your changes missing coverage. Please review.

Files with missing linesPatch %Lines
app/pages/search.vue0.00%11 Missing and 5 partials ⚠️

📢 Thoughts on this report? Let us know!

@serhalp

Copy link
Copy Markdown
Member

btw there may possibly be some overlap with the 'hit enter' idea here: #1723

@serhalpserhalp added the needs review This PR is waiting for a review from a maintainer label Mar 15, 2026
@serhalpserhalp added the stale This has become stale and may be closed soon label Apr 10, 2026
@github-actionsgithub-actionsBot removed the stale This has become stale and may be closed soon label Apr 18, 2026
@github-actionsgithub-actionsBot added stale This has become stale and may be closed soon and removed stale This has become stale and may be closed soon labels Jun 2, 2026
@github-actionsgithub-actionsBot added the stale This has become stale and may be closed soon label Jul 4, 2026
@ghostdevvghostdevv added the needs discussion An idea that needs more discussion to understand the scope and impact. label Jul 6, 2026
@github-actionsgithub-actionsBot removed the stale This has become stale and may be closed soon label Jul 6, 2026
@gameroman

Copy link
Copy Markdown
Member

Hi @shuuji3 could you resolve the git conflicts please

@github-actionsgithub-actionsBot added the stale This has become stale and may be closed soon label Sep 4, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs discussionAn idea that needs more discussion to understand the scope and impact.needs reviewThis PR is waiting for a review from a maintainerstaleThis has become stale and may be closed soon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automatically redirect from the search page

4 participants

@shuuji3@serhalp@gameroman@ghostdevv