/ utils / execFileNoThrowPortable.ts
execFileNoThrowPortable.ts
 1  import { type Options as ExecaOptions, execaSync } from 'execa'
 2  import { getCwd } from '../utils/cwd.js'
 3  import { slowLogging } from './slowOperations.js'
 4  
 5  const MS_IN_SECOND = 1000
 6  const SECONDS_IN_MINUTE = 60
 7  
 8  type ExecSyncOptions = {
 9    abortSignal?: AbortSignal
10    timeout?: number
11    input?: string
12    stdio?: ExecaOptions['stdio']
13  }
14  
15  /**
16   * @deprecated Use `execa` directly with `{ shell: true, reject: false }` for non-blocking execution.
17   * Sync exec calls block the event loop and cause performance issues.
18   */
19  export function execSyncWithDefaults_DEPRECATED(command: string): string | null
20  /**
21   * @deprecated Use `execa` directly with `{ shell: true, reject: false }` for non-blocking execution.
22   * Sync exec calls block the event loop and cause performance issues.
23   */
24  export function execSyncWithDefaults_DEPRECATED(
25    command: string,
26    options: ExecSyncOptions,
27  ): string | null
28  /**
29   * @deprecated Use `execa` directly with `{ shell: true, reject: false }` for non-blocking execution.
30   * Sync exec calls block the event loop and cause performance issues.
31   */
32  export function execSyncWithDefaults_DEPRECATED(
33    command: string,
34    abortSignal: AbortSignal,
35    timeout?: number,
36  ): string | null
37  /**
38   * @deprecated Use `execa` directly with `{ shell: true, reject: false }` for non-blocking execution.
39   * Sync exec calls block the event loop and cause performance issues.
40   */
41  export function execSyncWithDefaults_DEPRECATED(
42    command: string,
43    optionsOrAbortSignal?: ExecSyncOptions | AbortSignal,
44    timeout = 10 * SECONDS_IN_MINUTE * MS_IN_SECOND,
45  ): string | null {
46    let options: ExecSyncOptions
47  
48    if (optionsOrAbortSignal === undefined) {
49      // No second argument - use defaults
50      options = {}
51    } else if (optionsOrAbortSignal instanceof AbortSignal) {
52      // Old signature - second argument is AbortSignal
53      options = {
54        abortSignal: optionsOrAbortSignal,
55        timeout,
56      }
57    } else {
58      // New signature - second argument is options object
59      options = optionsOrAbortSignal
60    }
61  
62    const {
63      abortSignal,
64      timeout: finalTimeout = 10 * SECONDS_IN_MINUTE * MS_IN_SECOND,
65      input,
66      stdio = ['ignore', 'pipe', 'pipe'],
67    } = options
68  
69    abortSignal?.throwIfAborted()
70    using _ = slowLogging`exec: ${command.slice(0, 200)}`
71    try {
72      const result = execaSync(command, {
73        env: process.env,
74        maxBuffer: 1_000_000,
75        timeout: finalTimeout,
76        cwd: getCwd(),
77        stdio,
78        shell: true, // execSync typically runs shell commands
79        reject: false, // Don't throw on non-zero exit codes
80        input,
81      })
82      if (!result.stdout) {
83        return null
84      }
85      return result.stdout.trim() || null
86    } catch {
87      return null
88    }
89  }