diff --git a/packages/cli-tools/src/fetch.ts b/packages/cli-tools/src/fetch.ts index 69626611b..657606876 100644 --- a/packages/cli-tools/src/fetch.ts +++ b/packages/cli-tools/src/fetch.ts @@ -2,9 +2,10 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; import * as stream from 'stream'; +import {pipeline} from 'stream/promises'; +import {randomUUID} from 'crypto'; import {CLIError} from './errors'; -import logger from './logger'; async function unwrapFetchResult(response: Response) { const data = await response.text(); @@ -20,36 +21,34 @@ async function unwrapFetchResult(response: Response) { * Downloads the given `url` to the OS's temp folder and * returns the path to it. */ -const fetchToTemp = (url: string): Promise => { - try { - return new Promise((resolve, reject) => { - const fileName = path.basename(url); - const tmpDir = path.join(os.tmpdir(), fileName); - - global.fetch(url).then((result) => { - if (result.status >= 400) { - return reject(`Fetch request failed with status ${result.status}`); - } - - if (result.body === null) { - return reject('Fetch request failed - empty body'); - } - - const dest = fs.createWriteStream(tmpDir); - const body = stream.Readable.fromWeb(result.body); - - body.pipe(dest); - - body.on('end', () => { - resolve(tmpDir); - }); +const fetchToTemp = async (url: string): Promise => { + const result = await global.fetch(url); + if (result.status >= 400) { + throw new CLIError(`Fetch request failed with status ${result.status}`); + } + if (result.body === null) { + throw new CLIError('Fetch request failed - empty body'); + } - body.on('error', reject); - }); - }); - } catch (e) { - logger.error(e as any); - throw e; + const fileName = path.basename(new URL(url).pathname) || 'download'; + const tmpFile = path.join( + os.tmpdir(), + `react-native-cli-${randomUUID()}-${fileName}`, + ); + const body = stream.Readable.fromWeb(result.body); + const dest = fs.createWriteStream(tmpFile, {flags: 'wx'}); + let created = false; + dest.once('open', () => { + created = true; + }); + try { + await pipeline(body, dest); + return tmpFile; + } catch (error) { + if (created) { + await fs.promises.unlink(tmpFile).catch(() => {}); + } + throw error; } };