mswjs / msw

Industry standard API mocking for JavaScript.
https://mswjs.io
MIT License
15.97k stars 519 forks source link

fix(ws): call BroadcastChannel.unref in Node.js environment #2196

Closed DanielleHuisman closed 4 months ago

DanielleHuisman commented 4 months ago

When using the WebSocket preview, the Node.js process doesn't exit properly. It does not matter if you actually create WebSocket links. This is a minimal reproduction:

import { http, passthrough } from "msw";

http.get("/", () => passthrough());

I added why-is-node-running to the script:

import why from "why-is-node-running";
import { http, passthrough } from "msw";

http.get("/", () => passthrough());

setTimeout(why, 1000);

This shows there is a MessagePort keeping the process running:

# MESSAGEPORT
node:internal/async_hooks:202                                                              
node:internal/worker/io:341                                                                
file://./node_modules/msw/lib/core/ws.mjs:8
node:internal/modules/esm/module_job:262                                                   
node:internal/modules/esm/loader:474                                                       
node:internal/modules/run_main:109 

It traces back to this line:

const wsBroadcastChannel = new BroadcastChannel("msw:ws-client-manager");

This PR fixes the problem by calling .unref() in Node.js environments (docs). The typing is a bit ugly, but unref is only available in the type when importing from node:working_threads and not when using the global variable. The browser version of BroadcastChannel does not have an unref function, so the call has to be conditional.