Skip to content

Commit 7a6e29e

Browse files
committed
feat(Button, PackageDownloadButton): Add async download functionality
1 parent 91e2933 commit 7a6e29e

3 files changed

Lines changed: 50 additions & 20 deletions

File tree

app/components/Button/Base.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ defineExpose({
4646
'text-xs px-2 py-2': size === 'small' && variant === 'subtle',
4747
'border-border': variant !== 'subtle',
4848
'border-border-subtle': variant === 'subtle',
49-
'bg-transparent text-fg hover:enabled:(bg-fg/10) focus-visible:enabled:(bg-fg/10) aria-pressed:(bg-fg/10 border-fg/20 hover:enabled:(bg-fg/20 text-fg/50))':
49+
'bg-transparent text-fg hover:enabled:(bg-fg/10) aria-pressed:(bg-fg/10 border-fg/20 hover:enabled:(bg-fg/20 text-fg/50))':
5050
variant === 'secondary',
51-
'text-bg bg-fg hover:enabled:(bg-fg/50) focus-visible:enabled:(bg-fg/50) aria-pressed:(bg-fg text-bg border-fg hover:enabled:(text-bg/50))':
51+
'text-bg bg-fg hover:enabled:(bg-fg/50) aria-pressed:(bg-fg text-bg border-fg hover:enabled:(text-bg/50))':
5252
variant === 'primary',
53-
'bg-bg-subtle text-fg-muted hover:enabled:(text-fg border-border-hover) focus-visible:enabled:(text-fg border-border-hover) active:enabled:scale-95':
53+
'bg-bg-subtle text-fg-muted hover:enabled:(text-fg border-border-hover) active:enabled:scale-95':
5454
variant === 'subtle',
5555
}"
5656
:type="props.type"

app/components/Package/DownloadButton.vue

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,31 @@ function handleAction(id: string | undefined) {
102102
close()
103103
}
104104
105-
function downloadPackage() {
105+
async function downloadPackage() {
106106
const tarballUrl = props.version.dist.tarball
107107
if (!tarballUrl) return
108108
109-
const link = document.createElement('a')
110-
link.href = tarballUrl
111-
link.download = `${props.packageName}-${props.version.version}.tgz`
112-
document.body.appendChild(link)
113-
link.click()
114-
document.body.removeChild(link)
109+
try {
110+
const response = await fetch(tarballUrl)
111+
const blob = await response.blob()
112+
const url = URL.createObjectURL(blob)
113+
const link = document.createElement('a')
114+
link.href = url
115+
link.download = `${props.packageName.replace(/\//g, '__')}-${props.version.version}.tgz`
116+
document.body.appendChild(link)
117+
link.click()
118+
document.body.removeChild(link)
119+
URL.revokeObjectURL(url)
120+
} catch (error) {
121+
console.error('Failed to download package:', error)
122+
// Fallback to direct link for non-CORS or other issues, though download attribute may be ignored
123+
const link = document.createElement('a')
124+
link.href = tarballUrl
125+
link.download = `${props.packageName.replace(/\//g, '__')}-${props.version.version}.tgz`
126+
document.body.appendChild(link)
127+
link.click()
128+
document.body.removeChild(link)
129+
}
115130
}
116131
117132
function downloadDependenciesScript() {
@@ -125,18 +140,21 @@ function downloadDependenciesScript() {
125140
]
126141
127142
// Add root package
128-
const rootTarball = (props.version.dist as any).tarball
143+
const rootTarball = props.version.dist.tarball
129144
if (rootTarball) {
130145
lines.push(`# ${props.packageName}@${props.version.version}`)
131146
lines.push(
132-
`curl -L ${rootTarball} -o ${props.packageName.replace(/\//g, '__')}-${props.version.version}.tgz`,
147+
`curl -L "${rootTarball}" -o "${props.packageName.replace(/\//g, '__')}-${props.version.version}.tgz"`,
133148
)
134149
}
135150
136151
// Add dependencies
137-
props.installSize.dependencies.forEach((dep: any) => {
152+
props.installSize.dependencies.forEach(dep => {
153+
if (!dep.tarballUrl) return
138154
lines.push(`# ${dep.name}@${dep.version}`)
139-
lines.push(`curl -L ${dep.tarballUrl} -o ${dep.name.replace(/\//g, '__')}-${dep.version}.tgz`)
155+
lines.push(
156+
`curl -L "${dep.tarballUrl}" -o "${dep.name.replace(/\//g, '__')}-${dep.version}.tgz"`,
157+
)
140158
})
141159
142160
const blob = new Blob([lines.join('\n')], { type: 'text/x-shellscript' })

test/nuxt/a11y.spec.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ import {
179179
PackageListControls,
180180
PackageListToolbar,
181181
PackageMaintainers,
182+
PackageDownloadButton,
182183
PackageManagerSelect,
183184
PackageMetricsBadges,
184185
PackagePlaygrounds,
@@ -2937,6 +2938,23 @@ describe('component accessibility audits', () => {
29372938
})
29382939
})
29392940

2941+
describe('PackageDownloadButton', () => {
2942+
it('should have no accessibility violations', async () => {
2943+
const component = await mountSuspended(PackageDownloadButton, {
2944+
props: {
2945+
packageName: 'vue',
2946+
version: {
2947+
version: '3.5.0',
2948+
dist: { tarball: 'https://registry.npmjs.org/vue/-/vue-3.5.0.tgz' },
2949+
} as any,
2950+
installSize: null,
2951+
},
2952+
})
2953+
const results = await runAxe(component)
2954+
expect(results.violations).toEqual([])
2955+
})
2956+
})
2957+
29402958
// Diff components
29412959
describe('DiffFileTree', () => {
29422960
const mockFiles = [
@@ -3314,12 +3332,6 @@ describe('component accessibility audits', () => {
33143332
hunks: [],
33153333
type: 'modify',
33163334
fileName: 'empty.ts',
3317-
},
3318-
})
3319-
const results = await runAxe(component)
3320-
expect(results.violations).toEqual([])
3321-
})
3322-
})
33233335

33243336
describe('DiffSidebarPanel', () => {
33253337
const mockCompare = {

0 commit comments

Comments
 (0)