(
args: string[],
target: string,
abortSignal: AbortSignal,
callback: (
error: ExecFileException | null,
stdout: string,
stderr: string,
) => void,
singleThread = false,
)
| 106 | } |
| 107 | |
| 108 | function ripGrepRaw( |
| 109 | args: string[], |
| 110 | target: string, |
| 111 | abortSignal: AbortSignal, |
| 112 | callback: ( |
| 113 | error: ExecFileException | null, |
| 114 | stdout: string, |
| 115 | stderr: string, |
| 116 | ) => void, |
| 117 | singleThread = false, |
| 118 | ): ChildProcess { |
| 119 | // NB: When running interactively, ripgrep does not require a path as its last |
| 120 | // argument, but when run non-interactively, it will hang unless a path or file |
| 121 | // pattern is provided |
| 122 | |
| 123 | const { rgPath, rgArgs, argv0 } = ripgrepCommand() |
| 124 | |
| 125 | // Use single-threaded mode only if explicitly requested for this call's retry |
| 126 | const threadArgs = singleThread ? ['-j', '1'] : [] |
| 127 | const fullArgs = [...rgArgs, ...threadArgs, ...args, target] |
| 128 | // Allow timeout to be configured via env var (in seconds), otherwise use platform defaults |
| 129 | // WSL has severe performance penalty for file reads (3-5x slower on WSL2) |
| 130 | const defaultTimeout = getPlatform() === 'wsl' ? 60_000 : 20_000 |
| 131 | const parsedSeconds = |
| 132 | parseInt(process.env.CLAUDE_CODE_GLOB_TIMEOUT_SECONDS || '', 10) || 0 |
| 133 | const timeout = parsedSeconds > 0 ? parsedSeconds * 1000 : defaultTimeout |
| 134 | |
| 135 | // For embedded ripgrep, use spawn with argv0 (execFile doesn't support argv0 properly) |
| 136 | if (argv0) { |
| 137 | const child = spawn(rgPath, fullArgs, { |
| 138 | argv0, |
| 139 | signal: abortSignal, |
| 140 | // Prevent visible console window on Windows (no-op on other platforms) |
| 141 | windowsHide: true, |
| 142 | }) |
| 143 | |
| 144 | let stdout = '' |
| 145 | let stderr = '' |
| 146 | let stdoutTruncated = false |
| 147 | let stderrTruncated = false |
| 148 | |
| 149 | child.stdout?.on('data', (data: Buffer) => { |
| 150 | if (!stdoutTruncated) { |
| 151 | stdout += data.toString() |
| 152 | if (stdout.length > MAX_BUFFER_SIZE) { |
| 153 | stdout = stdout.slice(0, MAX_BUFFER_SIZE) |
| 154 | stdoutTruncated = true |
| 155 | } |
| 156 | } |
| 157 | }) |
| 158 | |
| 159 | child.stderr?.on('data', (data: Buffer) => { |
| 160 | if (!stderrTruncated) { |
| 161 | stderr += data.toString() |
| 162 | if (stderr.length > MAX_BUFFER_SIZE) { |
| 163 | stderr = stderr.slice(0, MAX_BUFFER_SIZE) |
| 164 | stderrTruncated = true |
| 165 | } |
no test coverage detected