forward.js 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. import {EventEmitter} from 'node:events';
  2. import {onMessage, onDisconnect} from './incoming.js';
  3. import {undoAddedReferences} from './reference.js';
  4. // Forward the `message` and `disconnect` events from the process and subprocess to a proxy emitter.
  5. // This prevents the `error` event from stopping IPC.
  6. // This also allows debouncing the `message` event.
  7. export const getIpcEmitter = (anyProcess, channel, isSubprocess) => {
  8. if (IPC_EMITTERS.has(anyProcess)) {
  9. return IPC_EMITTERS.get(anyProcess);
  10. }
  11. // Use an `EventEmitter`, like the `process` that is being proxied
  12. // eslint-disable-next-line unicorn/prefer-event-target
  13. const ipcEmitter = new EventEmitter();
  14. ipcEmitter.connected = true;
  15. IPC_EMITTERS.set(anyProcess, ipcEmitter);
  16. forwardEvents({
  17. ipcEmitter,
  18. anyProcess,
  19. channel,
  20. isSubprocess,
  21. });
  22. return ipcEmitter;
  23. };
  24. const IPC_EMITTERS = new WeakMap();
  25. // The `message` and `disconnect` events are buffered in the subprocess until the first listener is setup.
  26. // However, unbuffering happens after one tick, so this give enough time for the caller to setup the listener on the proxy emitter first.
  27. // See https://github.com/nodejs/node/blob/2aaeaa863c35befa2ebaa98fb7737ec84df4d8e9/lib/internal/child_process.js#L721
  28. const forwardEvents = ({ipcEmitter, anyProcess, channel, isSubprocess}) => {
  29. const boundOnMessage = onMessage.bind(undefined, {
  30. anyProcess,
  31. channel,
  32. isSubprocess,
  33. ipcEmitter,
  34. });
  35. anyProcess.on('message', boundOnMessage);
  36. anyProcess.once('disconnect', onDisconnect.bind(undefined, {
  37. anyProcess,
  38. channel,
  39. isSubprocess,
  40. ipcEmitter,
  41. boundOnMessage,
  42. }));
  43. undoAddedReferences(channel, isSubprocess);
  44. };
  45. // Check whether there might still be some `message` events to receive
  46. export const isConnected = anyProcess => {
  47. const ipcEmitter = IPC_EMITTERS.get(anyProcess);
  48. return ipcEmitter === undefined
  49. ? anyProcess.channel !== null
  50. : ipcEmitter.connected;
  51. };