Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions app/pages/search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,29 @@ const updateUrlPage = debounce((page: number) => {
const { model: searchQuery, provider: searchProvider } = useGlobalSearch()
const query = computed(() => searchQuery.value)

// Parses the raw search query to extract package info, such as scope, name, version.
// Uses this info to provide a strippedQuery (the query, but without version info) that is used for searching.
const parsedQuery = computed(() => {
const q = query.value.trim()
// Regex matches a (un)scoped package and optionally extracts versioning info using the following syntax: @scope/specifier@version
// It makes use of 4 capture groups to extract this info.
const match = q.match(
/^(?:@(?<scope>[^/]+)\/)?(?<specifier>[^/@ ]+)(?:@(?<version>[^ ]*))?(?<trailing>.*)/,
)
if (!match) return { scope: null, name: q, version: null, strippedQuery: q }

const { scope, specifier, version, trailing } = match.groups ?? {}
// Reconstruct the query without the version info, essentially stripping the version data:
// anything directly after the @ for the version specifier is stripped.
const name = scope ? `@${scope}/${specifier}` : (specifier ?? '')
const strippedQuery = `${name} ${trailing ?? ''}`.trim()

return { scope: scope ?? null, name: name, version: version || null, strippedQuery }
})

const packageScope = computed(() => parsedQuery.value.scope)
const strippedQuery = computed(() => parsedQuery.value.strippedQuery)

// Track if page just loaded (for hiding "Searching..." during view transition)
const hasInteracted = shallowRef(false)
onMounted(() => {
Expand Down Expand Up @@ -192,7 +215,7 @@ const {
suggestions: validatedSuggestions,
packageAvailability,
} = useSearch(
query,
strippedQuery,
searchProvider,
() => ({
size: requestedSize.value,
Expand Down Expand Up @@ -287,14 +310,6 @@ const isValidPackageName = computed(() => isValidNewPackageName(query.value.trim
// Get connector state
const { isConnected, npmUser, listOrgUsers } = useConnector()

// Check if this is a scoped package and extract scope
const packageScope = computed(() => {
const q = query.value.trim()
if (!q.startsWith('@')) return null
const match = q.match(/^@([^/]+)\//)
return match ? match[1] : null
})

// Track org membership for scoped packages
const orgMembership = ref<Record<string, boolean>>({})

Expand Down Expand Up @@ -667,7 +682,7 @@ defineOgImageComponent('Default', {
<!-- No results found -->
<div v-else-if="status === 'success' || status === 'error'" role="status" class="py-12">
<p class="text-fg-muted font-mono mb-6 text-center">
{{ $t('search.no_results', { query }) }}
{{ $t('search.no_results', { query: strippedQuery }) }}
</p>

<!-- User/Org suggestions when no packages found -->
Expand Down
Loading