execFileNoThrowPortable.ts 2.6 KB

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