* Downloads a file from URL with redirect support
( urlString: string, destPath: string, spinner: ora.Ora | null )
| 85 | * Downloads a file from URL with redirect support |
| 86 | */ |
| 87 | function downloadFile( |
| 88 | urlString: string, |
| 89 | destPath: string, |
| 90 | spinner: ora.Ora | null |
| 91 | ): Promise<void> { |
| 92 | return new Promise((resolve, reject) => { |
| 93 | const file = fs.createWriteStream(destPath); |
| 94 | const url = new URL(urlString); |
| 95 | const client = url.protocol === "https:" ? https : http; |
| 96 | |
| 97 | const request = client.get(url, (response) => { |
| 98 | const statusCode = response.statusCode || 0; |
| 99 | |
| 100 | // Handle redirects |
| 101 | if (REDIRECT_CODES.includes(statusCode)) { |
| 102 | cleanup(file, response, request, destPath); |
| 103 | const location = response.headers.location; |
| 104 | if (!location) { |
| 105 | if (spinner) spinner.fail("Redirect location not found"); |
| 106 | return reject(new Error("Redirect location not found")); |
| 107 | } |
| 108 | const redirectUrl = location.startsWith("http") |
| 109 | ? location |
| 110 | : new URL(location, url).toString(); |
| 111 | return downloadFile(redirectUrl, destPath, spinner) |
| 112 | .then(resolve) |
| 113 | .catch(reject); |
| 114 | } |
| 115 | |
| 116 | // Handle errors |
| 117 | if (statusCode === 404) { |
| 118 | cleanup(file, response, request, destPath); |
| 119 | if (spinner) spinner.fail("Branch not found"); |
| 120 | return reject(new Error("Branch not found in repository")); |
| 121 | } |
| 122 | |
| 123 | if (statusCode !== 200) { |
| 124 | cleanup(file, response, request, destPath); |
| 125 | if (spinner) spinner.fail("Failed to download from GitHub"); |
| 126 | return reject( |
| 127 | new Error( |
| 128 | `Failed to download from GitHub: ${statusCode} ${response.statusMessage}` |
| 129 | ) |
| 130 | ); |
| 131 | } |
| 132 | |
| 133 | // Success - pipe to file |
| 134 | response.pipe(file); |
| 135 | file.on("finish", () => { |
| 136 | file.close(); |
| 137 | if (spinner) spinner.text = "Extracting components..."; |
| 138 | resolve(); |
| 139 | }); |
| 140 | file.on("error", (err) => { |
| 141 | cleanup(null, response, request, destPath); |
| 142 | if (spinner) spinner.fail("Download failed"); |
| 143 | reject(err); |
| 144 | }); |
no test coverage detected