Skip to content
Merged
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
12 changes: 12 additions & 0 deletions app/components/Package/Versions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@ function getTagVersions(tag: string): VersionDisplay[] {
:title="$t('package.versions.title')"
id="versions"
>
<template #actions>
<a
:href="`https://majors.nullvoxpopuli.com/q?packages=${packageName}`"
target="_blank"
rel="noopener noreferrer"
class="text-fg-subtle hover:text-fg transition-colors duration-200 inline-flex items-center justify-center min-w-6 min-h-6 -m-1 p-1"
:title="$t('package.downloads.community_distribution')"
>
<span class="i-carbon:load-balancer-network w-3.5 h-3.5" aria-hidden="true" />
<span class="sr-only">{{ $t('package.downloads.community_distribution') }}</span>
</a>
Comment on lines +309 to +319
Copy link
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

🌐 Web query:

majors.nullvoxpopuli.com package query parameter format scoped packages

💡 Result:

majors.nullvoxpopuli.com reads the package name(s) from the packages query parameter (i.e. ?packages=...). (nullvoxpopuli.com)

For scoped npm packages, use normal URL-encoding for reserved characters—most importantly:

  • @%40
  • /%2F

Example:

https://majors.nullvoxpopuli.com/?packages=%40ember%2Fdata

Citations:


🏁 Script executed:

# Check how packageName is used elsewhere in the codebase
rg "packageName" app/components/Package/Versions.vue -B 2 -A 2 | head -50

Repository: npmx-dev/npmx.dev

Length of output: 1625


🏁 Script executed:

# Look for any existing URL encoding patterns in the codebase
rg "encodeURIComponent|encodeURI" --type vue --type ts --type tsx | head -20

Repository: npmx-dev/npmx.dev

Length of output: 88


🏁 Script executed:

# Check the package name variable definition and source
rg "const packageName|let packageName|packageName:" app/components/Package/Versions.vue -B 3 -A 1

Repository: npmx-dev/npmx.dev

Length of output: 177


Encode packageName in the query string.

Scoped package names (e.g. @scope/package) contain characters like @ and / that must be URL-encoded (%40 and %2F respectively) for the majors.nullvoxpopuli.com service to correctly parse the query parameter.

🔧 Suggested change
-        :href="`https://majors.nullvoxpopuli.com/q?packages=${packageName}`"
+        :href="`https://majors.nullvoxpopuli.com/q?packages=${encodeURIComponent(packageName)}`"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<template #actions>
<a
:href="`https://majors.nullvoxpopuli.com/q?packages=${packageName}`"
target="_blank"
rel="noopener noreferrer"
class="text-fg-subtle hover:text-fg transition-colors duration-200 inline-flex items-center justify-center min-w-6 min-h-6 -m-1 p-1"
:title="$t('package.downloads.community_distribution')"
>
<span class="i-carbon:load-balancer-network w-3.5 h-3.5" aria-hidden="true" />
<span class="sr-only">{{ $t('package.downloads.community_distribution') }}</span>
</a>
<template `#actions`>
<a
:href="`https://majors.nullvoxpopuli.com/q?packages=${encodeURIComponent(packageName)}`"
target="_blank"
rel="noopener noreferrer"
class="text-fg-subtle hover:text-fg transition-colors duration-200 inline-flex items-center justify-center min-w-6 min-h-6 -m-1 p-1"
:title="$t('package.downloads.community_distribution')"
>
<span class="i-carbon:load-balancer-network w-3.5 h-3.5" aria-hidden="true" />
<span class="sr-only">{{ $t('package.downloads.community_distribution') }}</span>
</a>

</template>
<div class="space-y-0.5 min-w-0">
<!-- Dist-tag rows (limited to MAX_VISIBLE_TAGS) -->
<div v-for="row in visibleTagRows" :key="row.id">
Expand Down
1 change: 1 addition & 0 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
"date_range": "{start} to {end}",
"date_range_multiline": "{start}\nto {end}",
"analyze": "Analyze downloads",
"community_distribution": "View community adoption distribution",
"modal_title": "Downloads",
"granularity": "Granularity",
"granularity_daily": "Daily",
Expand Down
1 change: 1 addition & 0 deletions lunaria/files/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
"date_range": "{start} to {end}",
"date_range_multiline": "{start}\nto {end}",
"analyze": "Analyze downloads",
"community_distribution": "View community adoption distribution",
"modal_title": "Downloads",
"granularity": "Granularity",
"granularity_daily": "Daily",
Expand Down
1 change: 1 addition & 0 deletions lunaria/files/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
"date_range": "{start} to {end}",
"date_range_multiline": "{start}\nto {end}",
"analyze": "Analyze downloads",
"community_distribution": "View community adoption distribution",
"modal_title": "Downloads",
"granularity": "Granularity",
"granularity_daily": "Daily",
Expand Down
24 changes: 12 additions & 12 deletions test/nuxt/components/PackageVersions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ describe('PackageVersions', () => {
},
})

// Find version links (exclude anchor links that start with #)
// Find version links (exclude anchor links that start with # and external links)
const versionLinks = component
.findAll('a')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filtering all links like this with a semantic wrapper feels a bit goofy, but I don't know what testing patterns are common/recommended, and this was pretty hands off for me. Lemme know what changes you want

.filter(a => !a.attributes('href')?.startsWith('#'))
.filter(a => !a.attributes('href')?.startsWith('#') && a.attributes('target') !== '_blank')
expect(versionLinks.length).toBeGreaterThan(0)
expect(versionLinks[0]?.text()).toBe('2.0.0')
})
Expand All @@ -93,10 +93,10 @@ describe('PackageVersions', () => {
},
})

// Find version links (exclude anchor links that start with #)
// Find version links (exclude anchor links that start with # and external links)
const versionLinks = component
.findAll('a')
.filter(a => !a.attributes('href')?.startsWith('#'))
.filter(a => !a.attributes('href')?.startsWith('#') && a.attributes('target') !== '_blank')
expect(versionLinks.length).toBeGreaterThan(0)
expect(versionLinks[0]?.text()).toBe('1.0.0')
})
Expand Down Expand Up @@ -187,10 +187,10 @@ describe('PackageVersions', () => {
},
})

// Find version links (exclude anchor links that start with #)
// Find version links (exclude anchor links that start with # and external links)
const versionLinks = component
.findAll('a')
.filter(a => !a.attributes('href')?.startsWith('#'))
.filter(a => !a.attributes('href')?.startsWith('#') && a.attributes('target') !== '_blank')
const versions = versionLinks.map(l => l.text())
// Should be sorted by version descending
expect(versions[0]).toBe('2.0.0')
Expand All @@ -210,10 +210,10 @@ describe('PackageVersions', () => {
},
})

// Find version links (exclude anchor links that start with #)
// Find version links (exclude anchor links that start with # and external links)
const versionLinks = component
.findAll('a')
.filter(a => !a.attributes('href')?.startsWith('#'))
.filter(a => !a.attributes('href')?.startsWith('#') && a.attributes('target') !== '_blank')
expect(versionLinks.length).toBeGreaterThan(0)
expect(versionLinks[0]?.classes()).toContain('text-red-400')
})
Expand All @@ -230,10 +230,10 @@ describe('PackageVersions', () => {
},
})

// Find version links (exclude anchor links that start with #)
// Find version links (exclude anchor links that start with # and external links)
const versionLinks = component
.findAll('a')
.filter(a => !a.attributes('href')?.startsWith('#'))
.filter(a => !a.attributes('href')?.startsWith('#') && a.attributes('target') !== '_blank')
expect(versionLinks.length).toBeGreaterThan(0)
expect(versionLinks[0]?.attributes('title')).toContain('deprecated')
})
Expand Down Expand Up @@ -562,10 +562,10 @@ describe('PackageVersions', () => {
},
})

// Count visible version links (excluding anchor links that start with #)
// Count visible version links (excluding anchor links that start with # and external links)
const visibleLinks = component
.findAll('a')
.filter(a => !a.attributes('href')?.startsWith('#'))
.filter(a => !a.attributes('href')?.startsWith('#') && a.attributes('target') !== '_blank')
// Should have max 10 visible links in the main section
expect(visibleLinks.length).toBeLessThanOrEqual(10)
})
Expand Down