Skip to content
Open
Show file tree
Hide file tree
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
303 changes: 192 additions & 111 deletions app/components/Package/Versions.vue

Large diffs are not rendered by default.

239 changes: 239 additions & 0 deletions app/composables/useRepoMeta.ts

Large diffs are not rendered by default.

37 changes: 36 additions & 1 deletion app/pages/package/[[org]]/[name].vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { detectPublishSecurityDowngradeForVersion } from '~/utils/publish-securi
import { useModal } from '~/composables/useModal'
import { useAtproto } from '~/composables/atproto/useAtproto'
import { togglePackageLike } from '~/utils/atproto/likes'
import { getMatchedRelease } from '~/utils/releases'

defineOgImageComponent('Package', {
name: () => packageName.value,
Expand Down Expand Up @@ -358,7 +359,21 @@ const repositoryUrl = computed(() => {
return url
})

const { meta: repoMeta, repoRef, stars, starsLink, forks, forksLink } = useRepoMeta(repositoryUrl)
const {
meta: repoMeta,
repoRef,
stars,
starsLink,
forks,
forksLink,
releases,
} = useRepoMeta(repositoryUrl)

const currentVersionRelease = computed(() => {
const version = resolvedVersion.value ?? displayVersion.value?.version
if (!version || !releases.value.length) return null
return getMatchedRelease(version, releases.value)
})

const PROVIDER_ICONS: Record<string, string> = {
github: 'i-carbon:logo-github',
Expand Down Expand Up @@ -591,6 +606,16 @@ onKeyStroke(
},
)

onKeyStroke(
e => isKeyWithoutModifiers(e, 'r') && !isEditableElement(e.target),
e => {
if (!currentVersionRelease.value) return
e.preventDefault()
navigateTo(currentVersionRelease.value.url, { external: true })
},
{ dedupe: true },
)

const showSkeleton = shallowRef(false)
</script>

Expand Down Expand Up @@ -734,6 +759,15 @@ const showSkeleton = shallowRef(false)
>
{{ $t('package.links.compare') }}
</LinkBase>
<LinkBase
v-if="currentVersionRelease"
variant="button-secondary"
:to="currentVersionRelease.url"
aria-keyshortcuts="r"
classicon="i-carbon:catalog"
>
{{ $t('package.links.release') }}
</LinkBase>
</ButtonGroup>

<!-- Package metrics -->
Expand Down Expand Up @@ -1334,6 +1368,7 @@ const showSkeleton = shallowRef(false)
:dist-tags="pkg['dist-tags'] ?? {}"
:time="pkg.time"
:selected-version="resolvedVersion ?? pkg['dist-tags']?.['latest']"
:releases="releases"
/>

<!-- Install Scripts Warning -->
Expand Down
38 changes: 38 additions & 0 deletions app/utils/releases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Release } from '~/composables/useRepoMeta'

/**
* Match an npm version string to a repository release.
* Handles various tag formats: "v1.0.0", "1.0.0", "release-1.0.0", etc.
*
* @param version - npm version string (e.g., "1.2.3", "0.9.0-beta.1")
* @param releases - Array of releases from repository
* @returns Matched release or null
*/
export function getMatchedRelease(version: string, releases: Release[]): Release | null {
if (!version || !releases.length) return null

// Normalize version for comparison (remove any existing 'v' prefix)
const normalizedVersion = version.replace(/^v/, '').toLowerCase()

// Try exact matches with common tag formats
const tagVariants = [
`v${normalizedVersion}`, // Most common: v1.0.0
normalizedVersion, // Without prefix: 1.0.0
`release-${normalizedVersion}`, // Some projects: release-1.0.0
`${normalizedVersion}-release`, // Alternative: 1.0.0-release
]

for (const release of releases) {
if (!release.url) continue
const releaseTag = release.tag.toLowerCase()

// Check each variant (case-insensitive)
for (const variant of tagVariants) {
if (releaseTag === variant) {
return release
}
}
}

return null
}
6 changes: 5 additions & 1 deletion i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,11 @@
"code": "code",
"docs": "docs",
"fund": "fund",
"compare": "compare"
"compare": "compare",
"release": "release"
},
"releases": {
"view_release_for_version": "View release for {version}"
},
"likes": {
"like": "Like this package",
Expand Down
12 changes: 12 additions & 0 deletions i18n/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,18 @@
},
"compare": {
"type": "string"
},
"release": {
"type": "string"
}
},
"additionalProperties": false
},
"releases": {
"type": "object",
"properties": {
"view_release_for_version": {
"type": "string"
}
},
"additionalProperties": false
Expand Down
6 changes: 5 additions & 1 deletion lunaria/files/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,11 @@
"code": "code",
"docs": "docs",
"fund": "fund",
"compare": "compare"
"compare": "compare",
"release": "release"
},
"releases": {
"view_release_for_version": "View release for {version}"
},
"likes": {
"like": "Like this package",
Expand Down
6 changes: 5 additions & 1 deletion lunaria/files/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,11 @@
"code": "code",
"docs": "docs",
"fund": "fund",
"compare": "compare"
"compare": "compare",
"release": "release"
},
"releases": {
"view_release_for_version": "View release for {version}"
},
"likes": {
"like": "Like this package",
Expand Down
Loading