Conversation
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
react-server-docs | ce8632d | Apr 09 2026, 12:43 PM |
❌ 14 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
⚡ Benchmark Results
Legend🟢 > 1% improvement | 🔴 > 1% regression | ⚪ within noise margin Benchmarks run on GitHub Actions runners (shared infrastructure) — expect ~5% variance between runs. Consistent directional changes across multiple routes are more meaningful than any single number. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Previously,
react-serverandreact-server buildwould silently treat piped stdin as the server entrypoint wheneverfd 0happened to be a pipe, file redirect, or (in production) any non-TTY context outside CI. That made the CLI's behaviordepend on invisible environmental state: the same command could behave differently depending on whether it was invoked from a shell, an editor task runner, a process manager, or a subprocess whose parent happened to close stdin. It also meant
stdin could be read without the user ever asking for it, which is surprising at best and unsafe at worst.
This change makes
--evalthe single, explicit opt-in. Stdin is now only consulted when the user passes--eval, and nothing else changes the CLI's entrypoint resolution.Behavior
--eval "<code>"passes the inline string as the virtualized server entrypoint, the same as before.--evalwith no value reads the full entrypoint from stdin — this is the new, explicit way to pipe code in. With no--evalat all, stdin isnever touched, even if it is a pipe or a file redirect; the CLI falls back to the positional root or the file-router as it always did.
The JS-level signal is that the plugin's
options.evalis now tri-valued:stringfor inline code,truefor "read stdin", and anything else (undefined /false) for "don't use eval at all". The CLI option definition changed from-e, --eval <code>(required value) to-e, --eval [code](optional value) in bothdevandbuildcommands to support the bare form.Why
The previous auto-detection had three concrete problems. It could consume stdin from an unrelated parent process that happened to leave fd 0 open as a pipe, producing confusing "Root module not provided" errors or worse, running
attacker-controlled code. It was non-deterministic across environments: the production path additionally gated on
!process.env.CI, so the same invocation would take different branches in CI vs. local. And it made the--evalflag's contractambiguous, because stdin could be the entrypoint even when
--evalwas absent, so users couldn't reason about the CLI from the flags alone.Making stdin opt-in via
--evalcollapses these three cases into one rule that is trivial to explain and impossible to trigger by accident.Changes
lib/plugins/react-server-eval.mjsdrops thefstatSync-basedisStdinPipedprobe entirely. The load handler now branches cleanly onoptions.eval: string → return it verbatim,true→ read stdin, otherwise → return the "Root module notprovided" stub.
lib/dev/action.mjsno longer runs its ownfstaton fd 0 to pick the virtual entrypoint; it selects the virtual module only whenoptions.eval != null && options.eval !== false.lib/build/server.mjsdrops the!process.stdin.isTTY && !process.env.CIheuristic at the production root-module decision and uses the same explicit check.bin/commands/dev.mjsandbin/commands/build.mjschange the cac option shape to-e, --eval [code]with clarifiedhelp text describing both forms.
Docs
Both the English and Japanese
features/cli.mdxhave been updated for the dev and build--evalsections. The prose explicitly calls out that stdin is never auto-consumed, and each section now carries three runnable examples: inline code,bare
--evalwith a pipe, and bare--evalwith a shell file redirect.Tests
Two new specs guard the contract at complementary levels.
test/__test__/react-server-eval.spec.mjsis a fast unit spec that imports thereact-server:evalplugin directly and exercises its load hook with a poisonedprocess.stdinthatthrows on read. It verifies that the "no
--eval", "eval: false", and "--eval "<inline>"" paths all return the expected result without ever touching stdin, and thateval: truecorrectly reads both single-chunk and multi-chunk stdinpayloads. The poisoned-stdin trick is what lets the test make a positive assertion about the absence of stdin reads, which a black-box HTTP test cannot observe.
test/__test__/cli-eval.spec.mjsis an end-to-end spec that spawns the real CLI binary as a subprocess. In dev mode it covers three cases:--eval "<inline>"serves the inline marker, bare--eval+ piped stdin serves the stdin marker, anda positional root file + piped invalid JavaScript serves the positional marker (the critical regression guard — if auto-eval ever comes back, the bogus stdin would either crash the server or be rendered instead of the positional root). In
production mode it runs
build --eval "<inline>"with bogus stdin piped in and asserts the build exits successfully, covering thebuild/server.mjscode path. The subprocess helper forcesNO_COLOR=1and strips ANSI defensively so readinessdetection isn't broken by colored log output.
Compatibility
This is a behavior change that could in principle affect users who were relying on the undocumented auto-stdin path. In practice, the documented way to pipe code was always
--eval, and the auto path was an implementation detail of theplugin. Users who were piping code without
--evalneed to add the flag. No API surface outside the CLI is affected.