Skip to content

[CLI] Auto restart with --experimental-wasm-jspi support#3281

Merged
adamziel merged 8 commits intotrunkfrom
cli/auto-respawn-jspi
Mar 17, 2026
Merged

[CLI] Auto restart with --experimental-wasm-jspi support#3281
adamziel merged 8 commits intotrunkfrom
cli/auto-respawn-jspi

Conversation

@adamziel
Copy link
Copy Markdown
Collaborator

@adamziel adamziel commented Feb 17, 2026

Summary

Using JSPI with the CLI used to require manually passing --experimental-wasm-jspi or using the unbuilt-jspi NX target. Most users never do this, especially when running Playground CLI via npx @wp-playground/cli, and silently fall back to Asyncify.

The CLI now detects at startup whether JSPI could be enabled and respawns itself with the flag. On Node 24+ (JSPI unflagged), no respawn happens. On Node 22 (flag exists but JSPI non-functional), the child sees the flag in execArgv and stops — no infinite loop. The heavy run-cli module is only loaded via dynamic import in the process that actually runs the server.

Test plan

  • npx nx dev playground-cli server on Node 23 — should auto-respawn and use JSPI
  • npx nx unbuilt-asyncify playground-cli -- server on Node 22 — should try flag, fall back to Asyncify (no infinite loop)
  • node --experimental-wasm-jspi packages/playground/cli/src/cli.ts server — should NOT respawn (flag already present)
  • Start server, press Ctrl+C — should shut down cleanly
  • npx nx lint playground-cli and npx nx typecheck playground-cli pass

cc @JanJakes @wojtekn

Until now, using JSPI with the CLI required manually passing
--experimental-wasm-jspi or using the unbuilt-jspi NX target. This is
easy to forget and means most users fall back to Asyncify without
realizing it.

The CLI now detects at startup whether JSPI could be enabled and
respawns itself with the flag when needed. On Node 24+ where JSPI is
unflagged, no respawn happens. On Node 22 where the flag exists but
JSPI is non-functional, the child process sees the flag in execArgv
and stops — no infinite loop.

The heavy run-cli module is loaded via dynamic import only in the
process that will actually run the server, keeping the parent process
lightweight.
Copilot AI review requested due to automatic review settings February 17, 2026 12:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds automatic JSPI (JavaScript Promise Integration) enablement for the CLI by detecting at startup whether JSPI support can be enabled and respawning the process with the --experimental-wasm-jspi flag when appropriate. This eliminates the need for users to manually pass the flag or use special NX targets, preventing silent fallback to Asyncify.

Changes:

  • Added detection logic to determine when process respawn is needed based on JSPI availability, existing flags, and Node.js version
  • Modified CLI entry point to conditionally respawn with JSPI flag and handle signal forwarding for clean shutdown
  • Changed to dynamic import of run-cli module to avoid loading heavy modules in parent process before respawn

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/playground/cli/src/ensure-jspi.ts New module implementing JSPI availability detection and respawn decision logic
packages/playground/cli/src/cli.ts Updated entry point with respawn logic, signal handling, and dynamic import

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The Vite externals list includes 'child_process' but not
'node:child_process'. The node: prefix caused Rollup to treat
the module as a browser external, breaking the production build.
Node 22 ships V8 12.4 which only has the old JSPI API
(WebAssembly.Suspender, removed in V8 12.8). The new API we need
(WebAssembly.Suspending) arrived in V8 12.6 = Node 23. Respawning
on Node 22 was harmless but wasteful — it would spawn a child,
discover the flag didn't help, and fall back to Asyncify anyway.

Bun reports process.versions.node as v22.x for compatibility but
uses JavaScriptCore, which doesn't support JSPI or the
--experimental-wasm-jspi flag. Deno uses V8 and has JSPI since 2.4,
but handles it through its own mechanisms — the Node flag doesn't
apply. Guard against both so we only respawn on actual Node.js.
If the child process exits within the first second with a non-zero
code, the --experimental-wasm-jspi flag was likely rejected by the
runtime. Instead of propagating the failure, the parent process
continues and runs the CLI without JSPI.

This makes the respawn attempt safe even on runtimes we haven't
explicitly guarded against — the worst case is a brief failed spawn
followed by normal Asyncify execution. Also handles spawn() errors
(e.g. ENOENT) the same way.
@adamziel adamziel requested review from a team and zaerl February 17, 2026 15:30
@adamziel adamziel changed the title [CLI] Auto-respawn with --experimental-wasm-jspi [CLI] Auto restart with --experimental-wasm-jspi support Feb 17, 2026
@adamziel adamziel marked this pull request as draft February 17, 2026 16:44
@adamziel
Copy link
Copy Markdown
Collaborator Author

We need to make sure the asyncify tests still run under asyncify

The unbuilt-asyncify target runs cli.ts directly to test the Asyncify
code path. On Node 24 (which the CI test script upgrades to),
shouldRespawnWithJSPI() would return true and silently switch to JSPI,
defeating the purpose of the test.

PLAYGROUND_FORCE_ASYNCIFY=1 now opts out of the auto-respawn, and the
unbuilt-asyncify target sets it.
@adamziel adamziel force-pushed the cli/auto-respawn-jspi branch from daa4f32 to 87d905b Compare February 17, 2026 19:32
@brandonpayton
Copy link
Copy Markdown
Member

This is such a good idea. It's weird... I feel like I dreamed this this week. 😆 Then I saw the PR.

@adamziel adamziel marked this pull request as ready for review March 17, 2026 20:32
@adamziel
Copy link
Copy Markdown
Collaborator Author

This just came up in #3407. I'll go ahead and merge this one since it covers some more bases (such as non-Node runtimes). I'll just wait for the rebased branch to finish testing in CI.

@wojtekn any concerns on Studio end? I don't expect so since Studio uses JSPI already, but I'm happy to rollback and revisit if needed.

@adamziel adamziel merged commit 5ac54b6 into trunk Mar 17, 2026
46 checks passed
@adamziel adamziel deleted the cli/auto-respawn-jspi branch March 17, 2026 21:14
@wojtekn
Copy link
Copy Markdown
Collaborator

wojtekn commented Mar 18, 2026

@adamziel Studio always passes --experimental-wasm-jspi flag, so the change likely won't affect Studio.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants