Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ onKeyDown(
return
}

router.push('/search')
router.push({ name: 'search' })
},
{ dedupe: true },
)
Expand Down
4 changes: 2 additions & 2 deletions app/components/AppFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ const isHome = computed(() => route.name === 'index')
</div>
<!-- Desktop: Show all links. Mobile: Links are in MobileMenu -->
<div class="hidden sm:flex items-center gap-6">
<NuxtLink to="/about" class="link-subtle font-mono text-xs flex items-center">
<NuxtLink :to="{ name: 'about' }" class="link-subtle font-mono text-xs flex items-center">
{{ $t('footer.about') }}
</NuxtLink>
<NuxtLink
to="/privacy"
:to="{ name: 'privacy' }"
class="link-subtle font-mono text-xs min-h-11 flex items-center gap-1 lowercase"
>
{{ $t('privacy_policy.title') }}
Expand Down
10 changes: 5 additions & 5 deletions app/components/AppHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ onKeyStroke(
e => isKeyWithoutModifiers(e, ',') && !isEditableElement(e.target),
e => {
e.preventDefault()
navigateTo('/settings')
navigateTo({ name: 'settings' })
},
{ dedupe: true },
)
Expand All @@ -78,7 +78,7 @@ onKeyStroke(
!e.defaultPrevented,
e => {
e.preventDefault()
navigateTo('/compare')
navigateTo({ name: 'compare' })
},
{ dedupe: true },
)
Expand Down Expand Up @@ -106,7 +106,7 @@ onKeyStroke(
<!-- Desktop: Logo (navigates home) -->
<div v-if="showLogo" class="hidden sm:flex flex-shrink-0 items-center">
<NuxtLink
to="/"
:to="{ name: 'index' }"
:aria-label="$t('header.home')"
dir="ltr"
class="inline-flex items-center gap-1 header-logo font-mono text-lg font-medium text-fg hover:text-fg/90 transition-colors duration-200 rounded"
Expand Down Expand Up @@ -152,7 +152,7 @@ onKeyStroke(
<div class="flex-shrink-0 flex items-center gap-0.5 sm:gap-2">
<!-- Desktop: Compare link -->
<NuxtLink
to="/compare"
:to="{ name: 'compare' }"
class="hidden sm:inline-flex link-subtle font-mono text-sm items-center gap-2 px-2 py-1.5 hover:bg-bg-subtle focus-visible:outline-accent/70 rounded"
aria-keyshortcuts="c"
>
Expand All @@ -167,7 +167,7 @@ onKeyStroke(

<!-- Desktop: Settings link -->
<NuxtLink
to="/settings"
:to="{ name: 'settings' }"
class="hidden sm:inline-flex link-subtle font-mono text-sm items-center gap-2 px-2 py-1.5 hover:bg-bg-subtle focus-visible:outline-accent/70 rounded"
aria-keyshortcuts=","
>
Expand Down
19 changes: 17 additions & 2 deletions app/components/Code/DirectoryListing.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<script setup lang="ts">
import type { PackageFileTree } from '#shared/types'
import type { RouteLocationRaw } from 'vue-router'
import { getFileIcon } from '~/utils/file-icons'
import { formatBytes } from '~/utils/formatters'

const props = defineProps<{
tree: PackageFileTree[]
currentPath: string
baseUrl: string
/** Base path segments for the code route (e.g., ['nuxt', 'v', '4.2.0']) */
basePath: string[]
}>()

// Get the current directory's contents
Expand Down Expand Up @@ -36,6 +39,18 @@ const parentPath = computed(() => {
if (parts.length <= 1) return ''
return parts.slice(0, -1).join('/')
})

// Build route object for a path
function getCodeRoute(nodePath?: string): RouteLocationRaw {
if (!nodePath) {
return { name: 'code', params: { path: props.basePath as [string, ...string[]] } }
}
const pathSegments = [...props.basePath, ...nodePath.split('/')]
return {
name: 'code',
params: { path: pathSegments as [string, ...string[]] },
}
}
</script>

<template>
Expand All @@ -61,7 +76,7 @@ const parentPath = computed(() => {
>
<td class="py-2 px-4">
<NuxtLink
:to="parentPath ? `${baseUrl}/${parentPath}` : baseUrl"
:to="getCodeRoute(parentPath || undefined)"
class="flex items-center gap-2 font-mono text-sm text-fg-muted hover:text-fg transition-colors"
>
<span class="i-carbon:folder w-4 h-4 text-yellow-600" />
Expand All @@ -79,7 +94,7 @@ const parentPath = computed(() => {
>
<td class="py-2 px-4">
<NuxtLink
:to="`${baseUrl}/${node.path}`"
:to="getCodeRoute(node.path)"
class="flex items-center gap-2 font-mono text-sm hover:text-fg transition-colors"
:class="node.type === 'directory' ? 'text-fg' : 'text-fg-muted'"
>
Expand Down
15 changes: 14 additions & 1 deletion app/components/Code/FileTree.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script setup lang="ts">
import type { PackageFileTree } from '#shared/types'
import type { RouteLocationRaw } from 'vue-router'
import { getFileIcon } from '~/utils/file-icons'

const props = defineProps<{
tree: PackageFileTree[]
currentPath: string
baseUrl: string
/** Base path segments for the code route (e.g., ['nuxt', 'v', '4.2.0']) */
basePath: string[]
depth?: number
}>()

Expand All @@ -18,6 +21,15 @@ function isNodeActive(node: PackageFileTree): boolean {
return false
}

// Build route object for a file path
function getFileRoute(nodePath: string): RouteLocationRaw {
const pathSegments = [...props.basePath, ...nodePath.split('/')]
return {
name: 'code',
params: { path: pathSegments as [string, ...string[]] },
}
}

const { toggleDir, isExpanded, autoExpandAncestors } = useFileTreeState(props.baseUrl)

// Auto-expand directories in the current path
Expand Down Expand Up @@ -63,14 +75,15 @@ watch(
:tree="node.children"
:current-path="currentPath"
:base-url="baseUrl"
:base-path="basePath"
:depth="depth + 1"
/>
</template>

<!-- File -->
<template v-else>
<NuxtLink
:to="`${baseUrl}/${node.path}`"
:to="getFileRoute(node.path)"
class="flex items-center gap-1.5 py-1.5 px-3 font-mono text-sm transition-colors hover:bg-bg-muted"
:class="currentPath === node.path ? 'bg-bg-muted text-fg' : 'text-fg-muted'"
:style="{ paddingLeft: `${depth * 12 + 32}px` }"
Expand Down
9 changes: 8 additions & 1 deletion app/components/Code/MobileTreeDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ defineProps<{
tree: PackageFileTree[]
currentPath: string
baseUrl: string
/** Base path segments for the code route (e.g., ['nuxt', 'v', '4.2.0']) */
basePath: string[]
}>()

const isOpen = shallowRef(false)
Expand Down Expand Up @@ -73,7 +75,12 @@ watch(isOpen, open => (isLocked.value = open))
<span class="i-carbon:close w-5 h-5" />
</button>
</div>
<CodeFileTree :tree="tree" :current-path="currentPath" :base-url="baseUrl" />
<CodeFileTree
:tree="tree"
:current-path="currentPath"
:base-url="baseUrl"
:base-path="basePath"
/>
</aside>
</Transition>
</template>
2 changes: 1 addition & 1 deletion app/components/Compare/ComparisonGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function getReplacementTooltip(col: ComparisonGridColumn): string {
>
<span class="inline-flex items-center gap-1.5 truncate">
<NuxtLink
:to="`/package/${col.header}`"
:to="{ name: 'package', params: { package: [col.header] } }"
class="link-subtle font-mono text-sm font-medium text-fg truncate"
:title="col.header"
>
Expand Down
2 changes: 1 addition & 1 deletion app/components/Compare/PackageSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ function handleBlur() {
</template>
<NuxtLink
v-else
:to="`/package/${pkg}`"
:to="{ name: 'package', params: { package: [pkg] } }"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
class="font-mono text-sm text-fg hover:text-accent transition-colors"
>
{{ pkg }}
Expand Down
12 changes: 6 additions & 6 deletions app/components/Header/MobileMenu.client.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ onUnmounted(deactivate)
<!-- Main navigation -->
<div class="px-2 py-2">
<NuxtLink
to="/about"
:to="{ name: 'about' }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -113,7 +113,7 @@ onUnmounted(deactivate)
</NuxtLink>

<NuxtLink
to="/privacy"
:to="{ name: 'privacy' }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -122,7 +122,7 @@ onUnmounted(deactivate)
</NuxtLink>

<NuxtLink
to="/compare"
:to="{ name: 'compare' }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -131,7 +131,7 @@ onUnmounted(deactivate)
</NuxtLink>

<NuxtLink
to="/settings"
:to="{ name: 'settings' }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -142,7 +142,7 @@ onUnmounted(deactivate)
<!-- Connected user links -->
<template v-if="isConnected && npmUser">
<NuxtLink
:to="`/~${npmUser}`"
:to="{ name: '~username', params: { username: npmUser } }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -151,7 +151,7 @@ onUnmounted(deactivate)
</NuxtLink>

<NuxtLink
:to="`/~${npmUser}/orgs`"
:to="{ name: '~username-orgs', params: { username: npmUser } }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand Down
6 changes: 3 additions & 3 deletions app/components/Header/OrgsDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function handleKeydown(event: KeyboardEvent) {
@keydown="handleKeydown"
>
<NuxtLink
:to="`/~${username}/orgs`"
:to="{ name: '~username-orgs', params: { username } }"
class="link-subtle font-mono text-sm inline-flex items-center gap-1"
>
{{ $t('header.orgs') }}
Expand Down Expand Up @@ -94,7 +94,7 @@ function handleKeydown(event: KeyboardEvent) {
<ul v-else-if="orgs.length > 0" class="py-1 max-h-80 overflow-y-auto">
<li v-for="org in orgs" :key="org">
<NuxtLink
:to="`/@${org}`"
:to="{ name: 'org', params: { org } }"
class="block px-3 py-2 font-mono text-sm text-fg hover:bg-bg-subtle transition-colors"
>
@{{ org }}
Expand All @@ -108,7 +108,7 @@ function handleKeydown(event: KeyboardEvent) {

<div class="px-3 py-2 border-t border-border">
<NuxtLink
:to="`/~${username}/orgs`"
:to="{ name: '~username-orgs', params: { username } }"
class="link-subtle font-mono text-xs inline-flex items-center gap-1"
>
{{ $t('header.orgs_dropdown.view_all') }}
Expand Down
6 changes: 3 additions & 3 deletions app/components/Header/PackagesDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function handleKeydown(event: KeyboardEvent) {
@keydown="handleKeydown"
>
<NuxtLink
:to="`/~${username}`"
:to="{ name: '~username', params: { username } }"
class="link-subtle font-mono text-sm inline-flex items-center gap-1"
>
{{ $t('header.packages') }}
Expand Down Expand Up @@ -94,7 +94,7 @@ function handleKeydown(event: KeyboardEvent) {
<ul v-else-if="packages.length > 0" class="py-1 max-h-80 overflow-y-auto">
<li v-for="pkg in packages" :key="pkg">
<NuxtLink
:to="`/package/${pkg}`"
:to="{ name: 'package', params: { package: [pkg] } }"
class="block px-3 py-2 font-mono text-sm text-fg hover:bg-bg-subtle transition-colors truncate"
>
{{ pkg }}
Expand All @@ -108,7 +108,7 @@ function handleKeydown(event: KeyboardEvent) {

<div class="px-3 py-2 border-t border-border">
<NuxtLink
:to="`/~${username}`"
:to="{ name: '~username', params: { username } }"
class="link-subtle font-mono text-xs inline-flex items-center gap-1"
>
{{ $t('header.packages_dropdown.view_all') }}
Expand Down
4 changes: 2 additions & 2 deletions app/components/Package/ClaimPackageModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ const previewPackageJson = computed(() => {

<div class="flex gap-3">
<NuxtLink
:to="`/package/${packageName}`"
:to="{ name: 'package', params: { package: [packageName] } }"
class="flex-1 px-4 py-2 font-mono text-sm text-center text-bg bg-fg rounded-md transition-colors duration-200 hover:bg-fg/90 focus-visible:outline-accent/70"
@click="close"
>
Expand Down Expand Up @@ -277,7 +277,7 @@ const previewPackageJson = computed(() => {
<span v-else class="w-4 h-4 shrink-0" />
<div class="min-w-0">
<NuxtLink
:to="`/package/${pkg.name}`"
:to="{ name: 'package', params: { package: [pkg.name] } }"
class="font-mono text-sm text-fg hover:underline focus-visible:outline-accent/70 rounded"
target="_blank"
>
Expand Down
5 changes: 4 additions & 1 deletion app/components/Package/TableRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ const allMaintainersText = computed(() => {
:key="maintainer.username || maintainer.email"
>
<NuxtLink
:to="`/~${maintainer.username || maintainer.name}`"
:to="{
name: '~username',
params: { username: maintainer.username || maintainer.name || '' },
}"
Comment on lines +109 to +112
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find and examine the TableRow.vue file to see the full context
fd -t f "TableRow.vue"

Repository: npmx-dev/npmx.dev

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Search for maintainer type definitions in the codebase
rg -t ts -t tsx "type.*[Mm]aintainer|interface.*[Mm]aintainer" -A 5

Repository: npmx-dev/npmx.dev

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Look for npm-registry related type definitions that might define Maintainer
fd -t f -e ts -e tsx | xargs grep -l "maintainer\|Maintainer" | head -20

Repository: npmx-dev/npmx.dev

Length of output: 585


🏁 Script executed:

#!/bin/bash
# Read the npm-registry types file
cat shared/types/npm-registry.ts

Repository: npmx-dev/npmx.dev

Length of output: 9637


🏁 Script executed:

#!/bin/bash
# Check the size and then read TableRow.vue
wc -l app/components/Package/TableRow.vue

Repository: npmx-dev/npmx.dev

Length of output: 100


🏁 Script executed:

#!/bin/bash
# Read TableRow.vue to see the full context around lines 109-112
cat -n app/components/Package/TableRow.vue

Repository: npmx-dev/npmx.dev

Length of output: 7783


Guard against empty maintainer route parameters.

Lines 109–112 can pass params: { username: '' } to the route when both username and name are undefined, resulting in an invalid profile link. Render a plain span displaying the email address when no username or name is available.

🔧 Proposed fix
          <NuxtLink
+           v-if="maintainer.username || maintainer.name"
            :to="{
              name: '~username',
-             params: { username: maintainer.username || maintainer.name || '' },
+             params: { username: maintainer.username || maintainer.name },
            }"
            class="hover:text-accent-fallback transition-colors duration-200"
            `@click.stop`
-           >{{ maintainer.username || maintainer.name || maintainer.email }}</NuxtLink
+           >{{ maintainer.username || maintainer.name }}</NuxtLink
           ><span v-if="idx < Math.min(pkg.maintainers.length, 3) - 1">, </span>
+         <span v-else class="text-fg-subtle">{{ maintainer.email }}</span>

class="hover:text-accent-fallback transition-colors duration-200"
@click.stop
>{{ maintainer.username || maintainer.name || maintainer.email }}</NuxtLink
Expand Down
4 changes: 2 additions & 2 deletions app/components/Terminal/Install.vue
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ const copyCreateCommand = () => copyCreate(getFullCreateCommand())
></code
>
<NuxtLink
:to="`/package/${typesPackageName}`"
:to="{ name: 'package', params: { package: [typesPackageName] } }"
class="text-fg-subtle hover:text-fg-muted text-xs transition-colors focus-visible:outline-accent/70 rounded select-none"
:title="$t('package.get_started.view_types', { package: typesPackageName })"
>
Expand Down Expand Up @@ -202,7 +202,7 @@ const copyCreateCommand = () => copyCreate(getFullCreateCommand())
:text="$t('package.create.view', { packageName: createPackageInfo.packageName })"
>
<NuxtLink
:to="`/package/${createPackageInfo.packageName}`"
:to="{ name: 'package', params: { package: [createPackageInfo.packageName] } }"
class="inline-flex items-center justify-center min-w-6 min-h-6 -m-1 p-1 text-fg-muted hover:text-fg text-xs transition-colors focus-visible:outline-2 focus-visible:outline-accent/70 rounded"
>
<span class="i-carbon:information w-3 h-3" aria-hidden="true" />
Expand Down
2 changes: 1 addition & 1 deletion app/components/VersionSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ watch(
<!-- Link to package page for full version list -->
<div class="border-t border-border mt-1 pt-1 px-3 py-2">
<NuxtLink
:to="`/package/${packageName}`"
:to="{ name: 'package', params: { package: [packageName] } }"
class="text-xs text-fg-subtle hover:text-fg transition-[color] focus-visible:outline-none focus-visible:text-fg"
@click="isOpen = false"
>
Expand Down
2 changes: 1 addition & 1 deletion app/pages/@[org].vue
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ defineOgImageComponent('Default', {
<p class="text-fg-muted mb-4">
{{ error?.message ?? $t('org.page.failed_to_load') }}
</p>
<NuxtLink to="/" class="btn">{{ $t('common.go_back_home') }}</NuxtLink>
<NuxtLink :to="{ name: 'index' }" class="btn">{{ $t('common.go_back_home') }}</NuxtLink>
</div>

<!-- Empty state -->
Expand Down
Loading
Loading