Skip to content

Commit 6777ee1

Browse files
committed
feat: add command palette download tarball action
1 parent f6029b4 commit 6777ee1

File tree

8 files changed

+73
-33
lines changed

8 files changed

+73
-33
lines changed

app/components/Package/DownloadButton.vue

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import type { SlimPackumentVersion } from '#shared/types'
3+
import { downloadPackageTarball } from '~/utils/package-download'
34
45
const props = defineProps<{
56
packageName: string
@@ -8,42 +9,15 @@ const props = defineProps<{
89
910
const loading = shallowRef(false)
1011
11-
async function getDownloadUrl(tarballUrl: string) {
12-
try {
13-
const response = await fetch(tarballUrl)
14-
if (!response.ok) {
15-
throw new Error(`Failed to fetch tarball (${response.status})`)
16-
}
17-
const blob = await response.blob()
18-
return URL.createObjectURL(blob)
19-
} catch (error) {
20-
// oxlint-disable-next-line no-console -- error logging
21-
console.error('failed to fetch tarball', { cause: error })
22-
return null
23-
}
24-
}
25-
2612
async function downloadPackage() {
27-
const tarballUrl = props.version.dist.tarball
28-
if (!tarballUrl) return
29-
3013
if (loading.value) return
3114
loading.value = true
3215
33-
const downloadUrl = await getDownloadUrl(tarballUrl)
34-
35-
const link = document.createElement('a')
36-
link.href = downloadUrl ?? tarballUrl
37-
link.download = `${props.packageName.replace(/\//g, '__')}-${props.version.version}.tgz`
38-
document.body.appendChild(link)
39-
link.click()
40-
document.body.removeChild(link)
41-
42-
if (downloadUrl) {
43-
URL.revokeObjectURL(downloadUrl)
16+
try {
17+
await downloadPackageTarball(props.packageName, props.version)
18+
} finally {
19+
loading.value = false
4420
}
45-
46-
loading.value = false
4721
}
4822
</script>
4923

app/composables/useCommandPalettePackageCommands.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
CommandPaletteContextCommandInput,
44
CommandPalettePackageContext,
55
} from '~/types/command-palette'
6+
import { downloadPackageTarball } from '~/utils/package-download'
67

78
function activeLabel(isCurrentRoute: boolean, label: string) {
89
return isCurrentRoute ? label : null
@@ -104,6 +105,28 @@ export function useCommandPalettePackageCommands(
104105
},
105106
]
106107

108+
if (resolvedContext.tarballUrl) {
109+
commands.push({
110+
id: 'package-download',
111+
group: 'package',
112+
label: t('command_palette.package.download'),
113+
keywords: [
114+
resolvedContext.packageName,
115+
t('package.download.button'),
116+
t('package.download.tarball'),
117+
],
118+
iconClass: 'i-lucide:download',
119+
action: () => {
120+
void downloadPackageTarball(resolvedContext.packageName, {
121+
version: resolvedContext.resolvedVersion!,
122+
dist: {
123+
tarball: resolvedContext.tarballUrl!,
124+
},
125+
})
126+
},
127+
})
128+
}
129+
107130
if (
108131
resolvedContext.latestVersion &&
109132
resolvedContext.latestVersion !== resolvedContext.resolvedVersion

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ const commandPalettePackageContext = computed(() => {
212212
resolvedVersion: resolvedVersion.value ?? packageData['dist-tags']?.latest ?? null,
213213
latestVersion: packageData['dist-tags']?.latest ?? null,
214214
versions: commandPaletteVersions.value ?? Object.keys(packageData.versions ?? {}),
215+
tarballUrl: packageData.requestedVersion?.dist.tarball ?? null,
215216
}
216217
})
217218

app/types/command-palette.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,5 @@ export interface CommandPalettePackageContext {
7373
resolvedVersion: string | null
7474
latestVersion: string | null
7575
versions: string[]
76+
tarballUrl?: string | null
7677
}

app/utils/package-download.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { SlimPackumentVersion } from '#shared/types'
2+
3+
async function getDownloadUrl(tarballUrl: string) {
4+
try {
5+
const response = await fetch(tarballUrl)
6+
if (!response.ok) {
7+
throw new Error(`Failed to fetch tarball (${response.status})`)
8+
}
9+
const blob = await response.blob()
10+
return URL.createObjectURL(blob)
11+
} catch (error) {
12+
// oxlint-disable-next-line no-console -- error logging
13+
console.error('failed to fetch tarball', { cause: error })
14+
return null
15+
}
16+
}
17+
18+
export async function downloadPackageTarball(
19+
packageName: string,
20+
version: Pick<SlimPackumentVersion, 'version' | 'dist'>,
21+
) {
22+
const tarballUrl = version.dist.tarball
23+
if (!tarballUrl) return
24+
25+
const downloadUrl = await getDownloadUrl(tarballUrl)
26+
27+
const link = document.createElement('a')
28+
link.href = downloadUrl ?? tarballUrl
29+
link.download = `${packageName.replace(/\//g, '__')}-${version.version}.tgz`
30+
document.body.appendChild(link)
31+
link.click()
32+
document.body.removeChild(link)
33+
34+
if (downloadUrl) {
35+
URL.revokeObjectURL(downloadUrl)
36+
}
37+
}

i18n/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@
146146
"docs": "Docs",
147147
"code": "Code",
148148
"diff": "Diff",
149-
"compare": "Compare this package"
149+
"compare": "Compare this package",
150+
"download": "Download tarball"
150151
},
151152
"package_actions": {
152153
"copy_run": "Copy run command"

i18n/locales/fr-FR.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@
146146
"docs": "Doc",
147147
"code": "Code",
148148
"diff": "Diff",
149-
"compare": "Comparer ce paquet"
149+
"compare": "Comparer ce paquet",
150+
"download": "Télécharger le tarball"
150151
},
151152
"package_actions": {
152153
"copy_run": "Copier la commande d'exécution"

test/nuxt/composables/use-command-palette-commands.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ describe('useCommandPaletteCommands', () => {
150150
resolvedVersion: '3.4.0',
151151
latestVersion: '3.5.0',
152152
versions: ['3.3.0', '3.5.0', '3.4.0'],
153+
tarballUrl: 'https://registry.npmjs.org/vue/-/vue-3.4.0.tgz',
153154
},
154155
})
155156

@@ -163,6 +164,7 @@ describe('useCommandPaletteCommands', () => {
163164
'versions',
164165
])
165166
expect(flatCommands.value.find(command => command.id === 'package-diff')).toBeTruthy()
167+
expect(flatCommands.value.find(command => command.id === 'package-download')).toBeTruthy()
166168
expect(flatCommands.value.find(command => command.id === 'package-main')?.to).toBeTruthy()
167169
expect(groupedCommands.value.at(-1)?.id).toBe('versions')
168170
expect(groupedCommands.value.at(-1)?.items[0]?.id).toBe('version:3.4.0')

0 commit comments

Comments
 (0)