diff --git a/src/rollup/plugins/externals.ts b/src/rollup/plugins/externals.ts index dc660706ea..8772732e52 100644 --- a/src/rollup/plugins/externals.ts +++ b/src/rollup/plugins/externals.ts @@ -5,7 +5,7 @@ import { nodeFileTrace, NodeFileTraceOptions } from '@vercel/nft' import type { Plugin } from 'rollup' import { resolvePath, isValidNodeImport, normalizeid } from 'mlly' import semver from 'semver' -import { isDirectory } from '../../utils' +import { isDirectory, retry } from '../../utils' export interface NodeExternalsOptions { inline?: string[] @@ -220,7 +220,7 @@ export function externals (opts: NodeExternalsOptions): Plugin { } } - const writeFile = async (file) => { + const writeFile = async (file: string) => { if (!await isFile(file)) { return } const src = resolve(opts.traceOptions.base, file) const { pkgName, subpath } = parseNodeModulePath(file) @@ -230,7 +230,7 @@ export function externals (opts: NodeExternalsOptions): Plugin { } // Write traced files - await Promise.all(tracedFiles.map(writeFile)) + await Promise.all(tracedFiles.map(file => retry(() => writeFile(file), 3))) // Write an informative package.json await fsp.writeFile(resolve(opts.outDir, 'package.json'), JSON.stringify({ diff --git a/src/utils/index.ts b/src/utils/index.ts index 9e496390d3..7310420e5b 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -146,3 +146,15 @@ export function resolveAliases (_aliases: Record) { } return aliases } + +export async function retry (fn: () => Promise, retries: number) { + let retry = 0 + let error: any + while (retry++ < retries) { + try { return await fn() } catch (err) { + error = err + await new Promise(resolve => setTimeout(resolve, 2)) + } + } + throw error +} diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts new file mode 100644 index 0000000000..9cbbb44931 --- /dev/null +++ b/test/unit/utils.test.ts @@ -0,0 +1,22 @@ +import { describe, it, expect } from 'vitest' +import { retry } from '../../src/utils' + +describe('retry', () => { + it('retries function', async () => { + let counter = 0 + // eslint-disable-next-line require-await + const fn = async () => { + if (!counter++) { + throw new Error('deliberate') + } + } + + await retry(fn, 3) + expect(counter).toEqual(2) + }) + it('throws function', async () => { + // eslint-disable-next-line require-await + const fn = async () => { throw new Error('deliberate') } + expect(await retry(fn, 2).catch(() => 'thrown')).toEqual('thrown') + }) +})