fix(node): esbuild breaks and does not cleanup (#30469)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
esbuild breaks on function checks and improperly handles dangling
promises, preventing the program to exit successfully.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
The program should exit properly and no zombie processes kept running.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
This commit is contained in:
Rui Lima 2025-06-09 10:50:57 +01:00 committed by GitHub
parent c9021b0e39
commit b38f966707
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 21 additions and 9 deletions

View File

@ -169,14 +169,21 @@ export async function* esbuildExecutor(
});
await ctx.watch();
return () => ctx.dispose();
return async () => ctx.dispose();
})
);
registerCleanupCallback(() => {
assetsResult?.stop();
packageJsonResult?.stop();
disposeFns.forEach((fn) => fn());
if (typeof assetsResult?.stop === 'function') assetsResult.stop();
if (typeof packageJsonResult?.stop === 'function') {
packageJsonResult.stop();
}
disposeFns.forEach(async (fn) => {
await fn();
});
done(); // return from async iterable
});
}

View File

@ -37,8 +37,6 @@ function debounce<T>(fn: () => Promise<T>, wait: number): () => Promise<T> {
let pendingPromise: Promise<T> | null = null;
return () => {
clearTimeout(timeoutId);
if (!pendingPromise) {
pendingPromise = new Promise<T>((resolve, reject) => {
timeoutId = setTimeout(() => {
@ -50,6 +48,9 @@ function debounce<T>(fn: () => Promise<T>, wait: number): () => Promise<T> {
.catch((error) => {
pendingPromise = null;
reject(error);
})
.finally(() => {
clearTimeout(timeoutId);
});
}, wait);
});
@ -197,7 +198,7 @@ export async function* nodeExecutor(
if (code !== 0) {
error(new Error(`Process exited with code ${code}`));
} else {
done();
resolve(done());
}
}
resolve();
@ -226,8 +227,12 @@ export async function* nodeExecutor(
};
const stopAllTasks = async (signal: NodeJS.Signals = 'SIGTERM') => {
additionalExitHandler?.();
await currentTask?.stop(signal);
if (typeof additionalExitHandler === 'function') {
additionalExitHandler();
}
if (typeof currentTask?.stop === 'function') {
await currentTask.stop(signal);
}
for (const task of tasks) {
await task.stop(signal);
}