Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Feature request: PTY support in `child_process.spawn()`

オープン
#64,019 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
32/100
issue の種類
機能追加
明瞭さ
おおむね明確
活発さ
静か
技術スタック
javascript, node.js
領域
api, backend

調査の方向性

エントリポイントの child_process.spawn() から始めて test/pseudo-tty/pty_helper.py を確認し、その後、参照されている libuv の issue と PR を確認してください。このリクエストは libuv の PTY サポートが前提となるため、そこでブロックされています。完了には、stream、resize、fallback、spawnSync の処理を含む、POSIX と ConPTY のクロスプラットフォーム動作に対応した合意済みの API が必要です。

索引モデルが issue の本文から書いたものです。

説明

feature request
What is the problem this feature will solve?

Please add first-class pseudo-terminal (PTY) support to child_process.spawn()
(and ideally spawnSync()), so child processes see a real TTY on stdout/stderr/stdin
instead of pipes.

Today, the only practical option is third-party native addons such as
node-pty. That works, but it adds
native compilation, Electron/ABI friction, and an extra dependency for a capability
that shells and other runtimes expose natively.

Built-in PTY support would make Node.js a better cross-platform choice for
developer tools, CI runners, test harnesses, and build orchestrators compared
with wrapping PowerShell or Python just to get a TTY.

Problem

When spawn() uses the default stdio: ['pipe', 'pipe', 'pipe'], the child's
stdout/stderr are not TTYs. Many programs behave differently:

  • Block buffering instead of line buffering (bash, make, gcc, pacman, etc.)
  • isatty() / process.stdout.isTTY is false (no ANSI colors, different log format)
  • Interactive prompts break (password prompts, read, pagers)
  • Progress output is delayed until buffers fill or the process exits

Workarounds are incomplete:

  • stdbuf -oL -eL only affects some libc-buffered programs; grandchild processes
    (e.g. make jobs) may still block-buffer.
  • script -q -f allocates a PTY but adds Script started/done noise to logs.
  • Redirecting to a file (>log.txt) inside the shell has the same non-TTY behavior.

Concrete use case

I maintain a cross-platform bootstrap/build runner (Windows + MSYS/Cygwin) written
in TypeScript/Node.js because it is faster and simpler to ship than PowerShell or
Python for this use case.

The runner uses spawn(bash, ['--login', '-c', script]) and tees output to both
the terminal and a log file. Without a PTY:

  1. The terminal shows almost nothing for minutes ("stuck" UX).
  2. The log file updates in large bursts, not line-by-line.
  3. Some toolchain steps change behavior under non-TTY conditions.

We currently wrap commands in stdbuf -oL -eL and manually tee data events.
That helps a little but is fragile and platform-dependent. A PTY would fix the
root cause.

Minimal desired behavior:

import { spawn } from 'node:child_process';
import { createWriteStream } from 'node:fs';

const log = createWriteStream('build.log');
const child = spawn('bash', ['--login', '-c', 'make -j8'], {
  cwd: repoRoot,
  env: process.env,
  stdio: ['pipe', 'pty', 'pty'], // or a dedicated option; see below
});

child.on('data', (chunk) => {
  log.write(chunk);
  process.stdout.write(chunk);
});

The child should believe it has a terminal (isatty(1) === true), use
line-oriented output, and flush promptly.

Why not node-pty?

node-pty is widely used (VS Code, etc.)
and I am grateful it exists, but for application-level build/CI tooling it has
drawbacks:

  • Native addon: requires node-gyp/prebuilds; breaks or needs rebuilds across
    Node/Electron ABI changes.
  • Extra dependency for something that feels like core process/spawn behavior.
  • API divergence from child_process.spawn() (different return type, resize
    events, etc.).

For terminal emulators, node-pty is fine. For "run this shell script and stream
output live to console + log", PTY belongs in core next to spawn.

Proposed API (sketch)

Option A - extend stdio:

spawn(cmd, args, {
  stdio: ['pipe', 'pty', 'pty'],
  pty: {
    cols: process.stdout.columns ?? 80,
    rows: process.stdout.rows ?? 24,
    name: 'xterm-256color',
  },
});

Option B - dedicated flag:

spawn(cmd, args, {
  pty: true,
  stdio: ['pipe', 'pipe', 'pipe'],
});

Requirements:

  • Works on Linux, macOS, Windows 10+ (ConPTY).
  • Document graceful failure on older Windows (throw or fallback to pipes).
  • Optional resize(cols, rows) on the child handle.
  • PTY stream emits data events; stdin remains writable for automation.
  • Document interaction with shell: true, detached, windowsHide.

Platform notes

  • POSIX: forkpty(3) / openpty + session setup (Node already has test helpers in
    test/pseudo-tty/pty_helper.py).
  • Windows: CreatePseudoConsole() (ConPTY). Older Windows may need documented
    unsupported behavior rather than winpty-level emulation in core.

Prior art / related issues

This request is blocked on libuv PTY landing first; once libuv exposes
spawn-with-PTY, please expose it through child_process.

Why this matters for Node.js

Node is already the default for cross-platform CLI tooling (yarn, vite, eslint,
etc.). Live, faithful subprocess output is a basic expectation for:

  • build systems and monorepo orchestrators
  • test runners
  • dev environment bootstrap scripts
  • CI log streaming

Without PTY, authors either accept broken UX, add native deps, or shell out to
PowerShell/Python/bash script hacks. Native PTY would close that gap and reduce
reliance on node-pty for non-terminal-emulator use cases.

What is the feature you are proposing to solve the problem?

Add optional pseudo-terminal (PTY) support to child_process.spawn() (and
spawnSync()) so a spawned child can use a real TTY instead of pipes.

Proposed API (either shape is fine):

spawn(cmd, args, {
stdio: ['pipe', 'pty', 'pty'],
pty: { cols, rows, name: 'xterm-256color' },
});

or:

spawn(cmd, args, { pty: true, stdio: ['pipe', 'pipe', 'pipe'] });

Behavior:

  • Child sees isatty(stdout/stderr) === true (line-buffered output, colors,
    interactive prompts work as in a real terminal).
  • Parent gets a readable/writable PTY stream (emit 'data', write stdin).
  • Optional child.resize(cols, rows) for terminal size changes.
  • Linux/macOS via POSIX PTY; Windows 10+ via ConPTY; document unsupported
    fallback on older Windows.

This should be implemented on top of libuv PTY support (libuv#2640,
libuv PR#4802) and exposed through child_process, similar to how pipe
and inherit stdio modes work today.

What alternatives have you considered?

No response

主要言語
JavaScript
スター
122k
フォーク
37.4k
平均マージ
4日 4時間
マージ済み PR(30日)
276

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

nodejs/node のほかの issue

nodejs/node の issue をすべて見る

似ている issue

JavaScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。