Skip to content

vp build hangs indefinitely when sass-embedded is used — CLI does not call process.exit() after successful build #1550

@ilkobg

Description

@ilkobg

Describe the bug

vp build completes the build successfully but the process never exits when sass-embedded is installed as the Sass compiler. The Dart VM child process spawned by sass-embedded keeps the Node event loop alive indefinitely.

Root cause (two layers):

  1. Upstream Vite bug ([Bug]: sass-embedded ChildProcess outlives server.close() - cssPlugin teardown drops worker.stop() promise vitejs/vite#22274): Vite's scssProcessor.close() is synchronous but calls async stop() which awaits compiler.dispose(). The promise is dropped, so the sass-embedded Dart subprocess is never shut down. Since vite-plus-core bundles Vite's CSS plugin code, this bug is present in Vite+ as well.

  2. Vite+ CLI behavior: vp build does not call process.exit(0) after a successful build. It relies on the Node event loop draining naturally. When any plugin or preprocessor leaves a handle open (like sass-embedded's persistent ChildProcess), the process hangs forever.

This is the same class of issue as #1396 — the CLI is not resilient against lingering event loop handles from third-party tools.

Reproduction

  1. In any Vite+ project that uses SCSS:
vp install sass-embedded
vp uninstall sass
  1. Run vp build
  2. The build output completes successfully (~19s in our case), but the process hangs at 100% and never exits
  3. lsof -p <pid> shows an orphaned Dart child process keeping the event loop alive

Expected behavior

vp build should exit with code 0 after the build completes, regardless of whether preprocessor plugins have properly cleaned up their handles.

Suggested fix

Short-term (Vite+ side): Add an explicit process.exit(0) in the vp build CLI path after successful build completion. This is a defense-in-depth measure that makes the CLI resilient to handle leaks from any plugin or preprocessor — not just sass-embedded.

Long-term (upstream): Once vitejs/vite#22274 is fixed and the updated Vite source is re-bundled into vite-plus-core, the Dart subprocess will be properly disposed and the event loop will drain naturally. The explicit process.exit(0) would still be good practice as a safety net.

Workaround

Use sass (Dart Sass compiled to JavaScript) instead of sass-embedded. Same API, same output, but runs in-process with no child process — so the event loop drains naturally after build.

System Info

vp: v0.1.16
sass-embedded: ^1.99.0
Node: 22.x
OS: macOS arm64

Validations

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions