diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 65316c1f6fa8de..582a3c2c6e43ef 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -1535,20 +1535,34 @@ export function partialEncodeURIPath(uri: string): string { return filePath.replaceAll('%', '%25') + postfix } +type SigtermCallback = (signal?: 'SIGTERM', exitCode?: number) => Promise + +// Use a shared callback when attaching sigterm listeners to avoid `MaxListenersExceededWarning` +const sigtermCallbacks = new Set() +const parentSigtermCallback: SigtermCallback = async (signal, exitCode) => { + await Promise.all([...sigtermCallbacks].map((cb) => cb(signal, exitCode))) +} + export const setupSIGTERMListener = ( callback: (signal?: 'SIGTERM', exitCode?: number) => Promise, ): void => { - process.once('SIGTERM', callback) - if (process.env.CI !== 'true') { - process.stdin.on('end', callback) + if (sigtermCallbacks.size === 0) { + process.once('SIGTERM', parentSigtermCallback) + if (process.env.CI !== 'true') { + process.stdin.on('end', parentSigtermCallback) + } } + sigtermCallbacks.add(callback) } export const teardownSIGTERMListener = ( callback: Parameters[0], ): void => { - process.off('SIGTERM', callback) - if (process.env.CI !== 'true') { - process.stdin.off('end', callback) + sigtermCallbacks.delete(callback) + if (sigtermCallbacks.size === 0) { + process.off('SIGTERM', parentSigtermCallback) + if (process.env.CI !== 'true') { + process.stdin.off('end', parentSigtermCallback) + } } }