Skip to content

Commit 9520fbf

Browse files
committed
fix: don't render home page link if it's the same as the repo
1 parent 5f2316a commit 9520fbf

File tree

3 files changed

+136
-1
lines changed

3 files changed

+136
-1
lines changed

app/pages/[...package].vue

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { JsrPackageInfo } from '#shared/types/jsr'
44
import { assertValidPackageName } from '#shared/utils/npm'
55
import { onKeyStroke } from '@vueuse/core'
66
import { joinURL } from 'ufo'
7+
import { areUrlsEquivalent } from '#shared/utils/url'
78
89
definePageMeta({
910
name: 'package',
@@ -205,7 +206,15 @@ const repoProviderIcon = computed(() => {
205206
})
206207
207208
const homepageUrl = computed(() => {
208-
return displayVersion.value?.homepage ?? null
209+
const homepage = displayVersion.value?.homepage
210+
if (!homepage) return null
211+
212+
// Don't show homepage if it's the same as the repository URL
213+
if (repositoryUrl.value && areUrlsEquivalent(homepage, repositoryUrl.value)) {
214+
return null
215+
}
216+
217+
return homepage
209218
})
210219
211220
function normalizeGitUrl(url: string): string {

shared/utils/url.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { withoutProtocol, withoutTrailingSlash } from 'ufo'
2+
3+
/**
4+
* Normalize a URL for comparison by removing protocol, www prefix,
5+
* trailing slashes, hash fragments, and common git tree paths.
6+
*
7+
* Uses ufo utilities where possible, with additional handling for
8+
* www prefix and git-specific paths that ufo's isEqual doesn't cover.
9+
*/
10+
export function normalizeUrlForComparison(url: string): string {
11+
let normalized = withoutProtocol(url).toLowerCase()
12+
normalized = withoutTrailingSlash(normalized)
13+
normalized = normalized
14+
.replace(/^www\./, '')
15+
.replace(/#.*$/, '')
16+
.replace(/\/tree\/(head|main|master)(\/|$)/i, '/')
17+
return withoutTrailingSlash(normalized)
18+
}
19+
20+
/**
21+
* Check if two URLs point to the same resource.
22+
* Handles differences in protocol (http/https), www prefix,
23+
* trailing slashes, and common git branch paths.
24+
*/
25+
export function areUrlsEquivalent(url1: string, url2: string): boolean {
26+
return normalizeUrlForComparison(url1) === normalizeUrlForComparison(url2)
27+
}

test/unit/url-comparison.spec.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { areUrlsEquivalent, normalizeUrlForComparison } from '#shared/utils/url'
3+
4+
describe('normalizeUrlForComparison', () => {
5+
it('removes http protocol', () => {
6+
expect(normalizeUrlForComparison('http://github.com/foo')).toBe('github.com/foo')
7+
})
8+
9+
it('removes https protocol', () => {
10+
expect(normalizeUrlForComparison('https://github.com/foo')).toBe('github.com/foo')
11+
})
12+
13+
it('removes www prefix', () => {
14+
expect(normalizeUrlForComparison('https://www.example.com/foo')).toBe('example.com/foo')
15+
})
16+
17+
it('removes trailing slashes', () => {
18+
expect(normalizeUrlForComparison('https://github.com/foo/')).toBe('github.com/foo')
19+
})
20+
21+
it('removes hash fragments', () => {
22+
expect(normalizeUrlForComparison('https://github.com/foo#readme')).toBe('github.com/foo')
23+
})
24+
25+
it('removes /tree/head path', () => {
26+
expect(normalizeUrlForComparison('https://github.com/foo/bar/tree/head')).toBe(
27+
'github.com/foo/bar',
28+
)
29+
})
30+
31+
it('removes /tree/main path', () => {
32+
expect(normalizeUrlForComparison('https://github.com/foo/bar/tree/main')).toBe(
33+
'github.com/foo/bar',
34+
)
35+
})
36+
37+
it('removes /tree/master path', () => {
38+
expect(normalizeUrlForComparison('https://github.com/foo/bar/tree/master')).toBe(
39+
'github.com/foo/bar',
40+
)
41+
})
42+
43+
it('removes /tree/HEAD/ with trailing content', () => {
44+
expect(normalizeUrlForComparison('https://github.com/foo/bar/tree/HEAD/packages/core')).toBe(
45+
'github.com/foo/bar/packages/core',
46+
)
47+
})
48+
49+
it('lowercases the URL', () => {
50+
expect(normalizeUrlForComparison('https://GitHub.com/Foo/Bar')).toBe('github.com/foo/bar')
51+
})
52+
})
53+
54+
describe('areUrlsEquivalent', () => {
55+
it('returns true for identical URLs', () => {
56+
expect(areUrlsEquivalent('https://github.com/foo', 'https://github.com/foo')).toBe(true)
57+
})
58+
59+
it('returns true for URLs with different protocols', () => {
60+
expect(areUrlsEquivalent('http://github.com/foo', 'https://github.com/foo')).toBe(true)
61+
})
62+
63+
it('returns true for URLs with/without www', () => {
64+
expect(areUrlsEquivalent('https://www.example.com', 'https://example.com')).toBe(true)
65+
})
66+
67+
it('returns true for URLs with/without trailing slash', () => {
68+
expect(areUrlsEquivalent('https://github.com/foo/', 'https://github.com/foo')).toBe(true)
69+
})
70+
71+
it('returns true for repo URL vs homepage with tree/HEAD', () => {
72+
expect(
73+
areUrlsEquivalent(
74+
'https://github.com/nuxt/nuxt/tree/HEAD/packages/nuxt',
75+
'https://github.com/nuxt/nuxt/packages/nuxt',
76+
),
77+
).toBe(true)
78+
})
79+
80+
it('returns true for repo URL vs homepage with tree/main', () => {
81+
expect(
82+
areUrlsEquivalent('https://github.com/foo/bar', 'https://github.com/foo/bar/tree/main'),
83+
).toBe(true)
84+
})
85+
86+
it('returns false for different repos', () => {
87+
expect(areUrlsEquivalent('https://github.com/foo/bar', 'https://github.com/foo/baz')).toBe(
88+
false,
89+
)
90+
})
91+
92+
it('returns false for different domains', () => {
93+
expect(areUrlsEquivalent('https://github.com/foo', 'https://gitlab.com/foo')).toBe(false)
94+
})
95+
96+
it('returns false for repo URL vs separate homepage', () => {
97+
expect(areUrlsEquivalent('https://github.com/nuxt/nuxt', 'https://nuxt.com')).toBe(false)
98+
})
99+
})

0 commit comments

Comments
 (0)