fix(babel-register): preserve app-managed graceful shutdown - #18137
Conversation
|
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/61948 |
|
commit: |
|
Thank you. Recently we have added a test framework for |
6edcf71 to
b80ff22
Compare
|
@JLHwung Thanks for the review! I've rebased the branch on top of main and added a new test for the graceful shutdown behavior in packages/babel-register/test/fixtures/preload/--require/graceful-shutdown using the new test framework as requested. However, it looks like the CI is having some infrastructure issues: One of the jobs threw a 403 Forbidden error with actions/download-artifact (likely a fork PR permission issue). |
JLHwung
left a comment
There was a problem hiding this comment.
Thank you. This PR does reveal a question that we never think about before:
Does user expect @babel/register to still work in the shutdown listener?
Currently when @babel/register is preloaded, the babel-register listener is very likely the first registered listener and thus executed earlier than any other user-defined listeners. At the point when user-defined listener runs, @babel/register has already reverted, so any require to other files will not be transpiled.
If @babel/register should be the last listener being removed, we should run the whole client teardown logic only when the listenerCount is 1, though if a user-defined listener does not de-register itself, the @babel/register will never get a chance to be removed.
@nicolo-ribaudo @liuxingbaoyu WDYT?
| // eslint-disable-next-line n/no-process-exit | ||
| process.exit(0); | ||
| if (process.listenerCount(signalOrCode) === 1) { | ||
| process.off(signalOrCode, listener!); |
There was a problem hiding this comment.
We should remove listeners for the other two signals as well, because at this point the client has been closed and re-entry to the listener via a different signal will exit early but we didn't get a chance to remove the listener for that signal.
Thanks, the CI error is probably not related. You can naturally retry the CI after addressing my review comments above. |
|
@JLHwung Thanks for the explanation! I've updated the shutdown logic to cleanly remove the listeners for exit, SIGINT, and SIGTERM as you suggested. |
|
@JLHwung |
No. Per Node.js docs https://nodejs.org/api/process.html#process_event_exit, the
The default
The timing of hook uninstalling does yield observable behaviour differences: The current behaviour is any user-land exit listener will not be run with babel-register, because at the moment when they run, babel-register has been uninstalled. |
|
Oops, I realize we didn't actually uninstall the hook, it just saved the cache. |
|
This PR also fixes another issue: previously, the |
|
Looking at the code I cannot figure out where in the listener we would be uninstalling |
|
In the past, we thought that |
|
Thanks! |
|
Hi @nicolo-ribaudo — thank you for reviewing and merging the PR. Since the core fix and implementation originated from my contribution in this PR, I’d like to be formally credited as a co-author. |
|
You are already credited as the main author of the commit: https://github.com/babel/babel/commit/36057c706ce82a0d0a1c8a5f7721e6fcae4ab1b6.patch |
|
Thanks |
Fixes #18134
Description:
This PR addresses an issue where
@babel/registerforces an earlyprocess.exit(0)when receivingSIGTERMorSIGINTsignals, which inadvertently interrupts any application-managed graceful shutdown routines.Changes:
packages/babel-register/src/index.tsto checkprocess.listenerCount(signal).@babel/registeris the only listener, it removes itself and re-raises the signal usingprocess.kill()to preserve default Node.js termination behavior.process.exit(0), allowing the rest of the application's shutdown handlers to run naturally.