fix(unplugin): prevent file handle leaks in Vitest (#1533)#1534
fix(unplugin): prevent file handle leaks in Vitest (#1533)#1534lecstor wants to merge 1 commit intofacebook:mainfrom
Conversation
Disable Babel config-file discovery (configFile: false) in runBabelTransform() to stop leaked FILEHANDLE operations during test run, and unref() the setInterval polling timer in configureServer() so it does not block process exit in middleware mode.
| Hi @lecstor! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
| @lecstor is attempting to deploy a commit to the Meta Open Source Team on Vercel. A member of the Team first needs to authorize it. |
| Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
What changed / motivation ?
This PR fixes two resource-leak issues in
@stylexjs/unpluginthat causeVitest's
hanging-processreporter to detect ~581 leakedFILEHANDLEoperations and 1 leaked
Timeout, preventing clean process exit.Linked PR/Issues
Fixes #1533
Additional Context
Root Cause
1. Babel config-file discovery —
FILEHANDLE×581runBabelTransform()passesbabelrc: falsebut does not setconfigFile.When
configFileis not explicitly set tofalse, Babel proceeds with rootconfig file discovery, triggering up to 7 parallel file-system probes for config
file variants (
babel.config.{js,cjs,mjs,json,cts,mts,ts}) on everytransformAsync()invocation. In async mode, these use callback-basedfs.readFile/fs.statoperations whose handles are tracked as async resourcesby Vitest's reporter. In a project with ~80 StyleX modules, this produces ~581
leaked handle operations.
2. Unreferenced polling timer —
Timeout×1The
configureServer()hook starts asetInterval(..., 150)to poll the sharedCSS-rules version counter. It attaches a cleanup listener to
server.httpServer?.once('close', ...), but in Vitest's middleware modehttpServerisnull. The interval therefore runs forever, keeping the Node.jsevent loop alive.
Pre-flight checklist
Contribution Guidelines
babelrc: falsewas already set — addingconfigFile: falseis thesymmetric completion of "don't look for external config"
interval.unref()is the standard Node.js pattern for optional timers(used in Vite core itself)
server.httpServer?.once('close', ...)cleanup listener is preservedas a belt-and-suspenders safeguard for non-middleware environments
vitest run --reporter=hanging-process— zero leakedresources