Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions docs/src/pages/en/(pages)/features/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,18 @@ When set, the development server will not validate your `react-server.config.*`
### --eval
</Link>

Evaluate the server entrypoint from the argument, like `node -e`. You can also use _stdin_ as the entrypoint. This type of entrypoint becomes a virtualized entrypoint and is not written to the file system.
Evaluate the server entrypoint from the argument, like `node -e`. Pass `--eval` without a value to read the entrypoint from _stdin_ instead. Stdin is **never** auto-consumed — it is only read when `--eval` is explicitly passed. This type of entrypoint becomes a virtualized entrypoint and is not written to the file system.

```sh
# Inline code
pnpm exec react-server --eval "export default () => <h1>Hello</h1>;"

# From stdin (note the bare --eval, with no value)
echo 'export default () => <h1>Hello from stdin</h1>;' | pnpm exec react-server --eval

# From a file via shell redirect
pnpm exec react-server --eval < ./entry.jsx
```

<Link name="dev-version">
### --version
Expand Down Expand Up @@ -259,7 +270,18 @@ This is useful when you want to override or provide adapter options without modi
### --eval
</Link>

Evaluate the server entrypoint from the argument, like `node -e`. You can also use _stdin_ as the entrypoint. This type of entrypoint becomes a virtualized entrypoint and is not written to the file system.
Evaluate the server entrypoint from the argument, like `node -e`. Pass `--eval` without a value to read the entrypoint from _stdin_ instead. Stdin is **never** auto-consumed — it is only read when `--eval` is explicitly passed. This type of entrypoint becomes a virtualized entrypoint and is not written to the file system.

```sh
# Inline code
pnpm exec react-server build --eval "export default () => <h1>Hello</h1>;"

# From stdin
echo 'export default () => <h1>Hi</h1>;' | pnpm exec react-server build --eval

# From a file via shell redirect
pnpm exec react-server build --eval < ./entry.jsx
```

<Link name="build-outdir">
### --outDir
Expand Down
26 changes: 24 additions & 2 deletions docs/src/pages/ja/(pages)/features/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,18 @@ export default {
### --eval
</Link>

`node -e`と同じように引数からサーバーのエントリーポイントを評価します。または標準入力も指定することが出来ます。このエントリーポイントは仮想化エントリーポイントとなり、ファイルシステムには書き込まれません。
`node -e`と同じように引数からサーバーのエントリーポイントを評価します。値を指定せずに `--eval` だけを渡すと、エントリーポイントを _標準入力_ から読み込みます。標準入力は **自動的には消費されません** — `--eval` が明示的に指定された場合にのみ読み込まれます。このエントリーポイントは仮想化エントリーポイントとなり、ファイルシステムには書き込まれません。

```sh
# インラインコード
pnpm exec react-server --eval "export default () => <h1>Hello</h1>;"

# 標準入力から(値を指定しない `--eval` に注意)
echo 'export default () => <h1>Hello from stdin</h1>;' | pnpm exec react-server --eval

# シェルのリダイレクトでファイルから
pnpm exec react-server --eval < ./entry.jsx
```

<Link name="dev-version">
### --version
Expand Down Expand Up @@ -259,7 +270,18 @@ pnpm react-server build --deploy '{"project": "my-project"}'
### --eval
</Link>

`node -e`と同じように引数に指定したエントリーポイントを評価します。または標準入力も指定することが出来ます。このエントリーポイントはファイルシステムに書き込んだものではなく仮想的なエントリーポイントです。
`node -e`と同じように引数に指定したエントリーポイントを評価します。値を指定せずに `--eval` だけを渡すと、エントリーポイントを _標準入力_ から読み込みます。標準入力は **自動的には消費されません** — `--eval` が明示的に指定された場合にのみ読み込まれます。このエントリーポイントはファイルシステムに書き込んだものではなく仮想的なエントリーポイントです。

```sh
# インラインコード
pnpm exec react-server build --eval "export default () => <h1>Hello</h1>;"

# 標準入力から
echo 'export default () => <h1>Hi</h1>;' | pnpm exec react-server build --eval

# シェルのリダイレクトでファイルから
pnpm exec react-server build --eval < ./entry.jsx
```

<Link name="build-outdir">
### --outDir
Expand Down
5 changes: 4 additions & 1 deletion packages/react-server/bin/commands/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export default (cli) =>
"[boolean|json] deploy using adapter, optionally pass JSON adapter options",
{ default: false }
)
.option("-e, --eval <code>", "evaluate code", { type: "string" })
.option(
"-e, --eval [code]",
"evaluate code as the server entrypoint; pass without a value to read from stdin"
)
.option("--outDir <dir>", "[string] output directory", {
default: ".react-server",
})
Expand Down
5 changes: 4 additions & 1 deletion packages/react-server/bin/commands/dev.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ export default (cli) =>
.option("--no-color", "disable color output", { default: false })
.option("--no-check", "skip dependency checks", { default: false })
.option("--no-validation", "skip config validation", { default: false })
.option("-e, --eval <code>", "evaluate code", { type: "string" })
.option(
"-e, --eval [code]",
"evaluate code as the server entrypoint; pass without a value to read from stdin"
)
.option("-o, --outDir <dir>", "[string] output directory", {
default: ".react-server",
})
Expand Down
2 changes: 1 addition & 1 deletion packages/react-server/lib/build/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export default async function serverBuild(root, options, clientManifestBus) {
{ paths: [cwd] }
);
const rootModulePath =
!root && (options.eval || (!process.stdin.isTTY && !process.env.CI))
!root && options.eval != null && options.eval !== false
? "virtual:react-server-eval.jsx"
: root?.startsWith("virtual:")
? root
Expand Down
23 changes: 4 additions & 19 deletions packages/react-server/lib/dev/action.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { fstatSync } from "node:fs";
import { isIPv6 } from "node:net";

import open from "open";
Expand Down Expand Up @@ -132,25 +131,11 @@ export default async function dev(root, options) {
await import("../../server/action-crypto.mjs");
await initSecretFromConfig(configRoot);

// Detect whether the user is piping code via stdin.
// Use fstat on fd 0 to distinguish a real pipe/file redirect
// from a non-TTY background process, Docker, or CI runner
// where stdin is /dev/null or closed.
// Only relevant when no explicit root module was provided —
// if the user passed `react-server ./app.jsx`, use that even
// when stdin happens to be piped (e.g. via npm-run-all/run-p).
let isStdinPiped = false;
if (!root) {
try {
const stat = fstatSync(0);
isStdinPiped = stat.isFIFO() || stat.isFile();
} catch {
// fd 0 not available — not piped
}
}

// Stdin is never auto-detected as the entrypoint — the user must
// explicitly opt in by passing `--eval` (with a string value, or
// bare to read the entrypoint from stdin).
server = await createServer(
options.eval || isStdinPiped
options.eval != null && options.eval !== false
? "virtual:react-server-eval.jsx"
: root,
options
Expand Down
24 changes: 6 additions & 18 deletions packages/react-server/lib/plugins/react-server-eval.mjs
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
import { fstatSync } from "node:fs";

// Check whether stdin is an actual pipe or file redirect (i.e. the user
// is piping code into react-server), as opposed to a TTY, /dev/null
// (background process), or a closed fd (spawned subprocess).
// `isFIFO()` catches `echo "code" | react-server`
// `isFile()` catches `react-server < file.jsx`
function isStdinPiped() {
try {
const stat = fstatSync(0);
return stat.isFIFO() || stat.isFile();
} catch {
return false;
}
}

export default function reactServerEval(options) {
return {
name: "react-server:eval",
Expand All @@ -30,9 +14,13 @@ export default function reactServerEval(options) {
id: /^virtual:react-server-eval\.jsx$/,
},
async handler() {
if (options.eval) {
// `--eval <code>` → use the literal code string.
// `--eval` (no value) → read the entrypoint from stdin.
// Stdin is never consulted unless `--eval` was explicitly passed.
if (typeof options.eval === "string") {
return options.eval;
} else if (isStdinPiped()) {
}
if (options.eval === true) {
let code = "";
process.stdin.setEncoding("utf8");
for await (const chunk of process.stdin) {
Expand Down
Loading
Loading