Skip to content

fix(unplugin): do not let Vite dev polling timer keep the process alive - #1846

Open
HsukqiLee wants to merge 2 commits into
facebook:mainfrom
TsinbeiLabs:fix/unplugin-vite-timer-leak
Open

fix(unplugin): do not let Vite dev polling timer keep the process alive#1846
HsukqiLee wants to merge 2 commits into
facebook:mainfrom
TsinbeiLabs:fix/unplugin-vite-timer-leak

Conversation

@HsukqiLee

Copy link
Copy Markdown

What changed / motivation ?

@stylexjs/unplugin's Vite adapter keeps Node alive after tests finish when used under Vitest. In configureServer, when the shared store is active the plugin starts a 150 ms setInterval to broadcast stylex:css-update, but clears it only via server.httpServer?.once('close', ...). Vitest's Vite server has no httpServer, so the cleanup listener is skipped and the interval is never cleared — every vitest run ends with close timed out after 10000ms and suite wall time jumps from ~4.5 s to ~15 s (#1836).

This change calls interval.unref() so the dev-only polling timer can never hold the event loop open, while keeping the existing httpServer close cleanup for normal dev servers. The interval is also exposed on the server (__stylexSharedPollingInterval) so hosts that tear down a server without an httpServer close event (and tests) can observe/clear it. Behavior is unchanged for real dev servers (the timer still fires and is cleared on close); it only stops blocking process exit when there is no httpServer.

Linked PR/Issues

Fixes #1836

Additional Context

  • Added two regression tests in __tests__/unplugin.test.js:
    • the interval is unref'd ( hasRef() === false ) when the server has no httpServer (the Vitest case);
    • the interval is cleared when the httpServer emits close.
  • This corresponds to the issue's suggested fix of using interval.unref?.() so a leaked timer cannot keep Node alive.

Pre-flight checklist

Copilot AI lite review requested due to automatic review settings September 4, 2026 12:52
@vercel

vercel Bot commented Sep 4, 2026

Copy link
Copy Markdown

@HsukqiLee is attempting to deploy a commit to the Meta Open Source Team on Vercel.

A member of the Team first needs to authorize it.

@meta-cla

meta-cla Bot commented Sep 4, 2026

Copy link
Copy Markdown

Hi @HsukqiLee!

Thank you for your pull request and welcome to our community.

Action Required

In 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.

Process

In 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 CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

One of the new regression tests doesn’t actually assert that the interval is cleared on close, so it could pass even if the cleanup regressed.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Fixes a Vite dev-server timer leak in @stylexjs/unplugin that can keep the Node event loop alive under Vitest (where server.httpServer is absent), causing vitest run to hang on shutdown.

Changes:

  • vite adapter: unref() the shared-store polling setInterval so it can’t block process exit, while keeping existing httpServer.close cleanup.
  • Expose the polling interval on the Vite server instance (__stylexSharedPollingInterval) for test/host cleanup.
  • Add regression tests covering unref() behavior and close cleanup wiring.
File summaries
File Description
packages/@stylexjs/unplugin/src/vite.js Unrefs the shared-store polling interval and exposes it on the server for observability/cleanup.
packages/@stylexjs/unplugin/tests/unplugin.test.js Adds regression tests for the polling interval behavior in Vitest/no-httpServer and close-cleanup scenarios.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +479 to +498
test('is cleared when the httpServer closes', () => {
const plugin = unplugin.vite({ dev: true });
const closeListeners = [];
const httpServer = {
once(event, fn) {
if (event === 'close') closeListeners.push(fn);
},
};
const server = makeServer(httpServer);
plugin.configureServer(server);

const interval = server.__stylexSharedPollingInterval;
expect(interval).toBeDefined();
expect(closeListeners.length).toBe(1);
// Simulate the server closing.
closeListeners[0]();
// After close the timer is cleared: calling clearInterval again is safe,
// and the interval no longer has a ref either way.
expect(() => clearInterval(interval)).not.toThrow();
});

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — rewritten in 91b2507 with fake timers and a ws.send spy: bumps the shared store version, asserts one stylex:css-update send on the next tick, then simulates close and asserts no further sends after another version bump.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 4, 2026
@meta-cla

meta-cla Bot commented Sep 4, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[unplugin] Vite adapter leaks a setInterval under Vitest (no httpServer to clear it), tests hang 10s on exit

2 participants