main-sync.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import {spawnSync} from 'node:child_process';
  2. import {handleCommand} from '../arguments/command.js';
  3. import {normalizeOptions} from '../arguments/options.js';
  4. import {concatenateShell} from '../arguments/shell.js';
  5. import {makeError, makeEarlyError, makeSuccessResult} from '../return/result.js';
  6. import {handleResult} from '../return/reject.js';
  7. import {handleStdioSync} from '../stdio/handle-sync.js';
  8. import {stripNewline} from '../io/strip-newline.js';
  9. import {addInputOptionsSync} from '../io/input-sync.js';
  10. import {transformOutputSync} from '../io/output-sync.js';
  11. import {getMaxBufferSync} from '../io/max-buffer.js';
  12. import {getAllSync} from '../resolve/all-sync.js';
  13. import {getExitResultSync} from '../resolve/exit-sync.js';
  14. // Main shared logic for all sync methods: `execaSync()`, `$.sync()`
  15. export const execaCoreSync = (rawFile, rawArguments, rawOptions) => {
  16. const {file, commandArguments, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors} = handleSyncArguments(rawFile, rawArguments, rawOptions);
  17. const result = spawnSubprocessSync({
  18. file,
  19. commandArguments,
  20. options,
  21. command,
  22. escapedCommand,
  23. verboseInfo,
  24. fileDescriptors,
  25. startTime,
  26. });
  27. return handleResult(result, verboseInfo, options);
  28. };
  29. // Compute arguments to pass to `child_process.spawnSync()`
  30. const handleSyncArguments = (rawFile, rawArguments, rawOptions) => {
  31. const {command, escapedCommand, startTime, verboseInfo} = handleCommand(rawFile, rawArguments, rawOptions);
  32. const syncOptions = normalizeSyncOptions(rawOptions);
  33. const {file, commandArguments, options} = normalizeOptions(rawFile, rawArguments, syncOptions);
  34. validateSyncOptions(options);
  35. const fileDescriptors = handleStdioSync(options, verboseInfo);
  36. return {
  37. file,
  38. commandArguments,
  39. command,
  40. escapedCommand,
  41. startTime,
  42. verboseInfo,
  43. options,
  44. fileDescriptors,
  45. };
  46. };
  47. // Options normalization logic specific to sync methods
  48. const normalizeSyncOptions = options => options.node && !options.ipc ? {...options, ipc: false} : options;
  49. // Options validation logic specific to sync methods
  50. const validateSyncOptions = ({ipc, ipcInput, detached, cancelSignal}) => {
  51. if (ipcInput) {
  52. throwInvalidSyncOption('ipcInput');
  53. }
  54. if (ipc) {
  55. throwInvalidSyncOption('ipc: true');
  56. }
  57. if (detached) {
  58. throwInvalidSyncOption('detached: true');
  59. }
  60. if (cancelSignal) {
  61. throwInvalidSyncOption('cancelSignal');
  62. }
  63. };
  64. const throwInvalidSyncOption = value => {
  65. throw new TypeError(`The "${value}" option cannot be used with synchronous methods.`);
  66. };
  67. const spawnSubprocessSync = ({file, commandArguments, options, command, escapedCommand, verboseInfo, fileDescriptors, startTime}) => {
  68. const syncResult = runSubprocessSync({
  69. file,
  70. commandArguments,
  71. options,
  72. command,
  73. escapedCommand,
  74. fileDescriptors,
  75. startTime,
  76. });
  77. if (syncResult.failed) {
  78. return syncResult;
  79. }
  80. const {resultError, exitCode, signal, timedOut, isMaxBuffer} = getExitResultSync(syncResult, options);
  81. const {output, error = resultError} = transformOutputSync({
  82. fileDescriptors,
  83. syncResult,
  84. options,
  85. isMaxBuffer,
  86. verboseInfo,
  87. });
  88. const stdio = output.map((stdioOutput, fdNumber) => stripNewline(stdioOutput, options, fdNumber));
  89. const all = stripNewline(getAllSync(output, options), options, 'all');
  90. return getSyncResult({
  91. error,
  92. exitCode,
  93. signal,
  94. timedOut,
  95. isMaxBuffer,
  96. stdio,
  97. all,
  98. options,
  99. command,
  100. escapedCommand,
  101. startTime,
  102. });
  103. };
  104. const runSubprocessSync = ({file, commandArguments, options, command, escapedCommand, fileDescriptors, startTime}) => {
  105. try {
  106. addInputOptionsSync(fileDescriptors, options);
  107. const normalizedOptions = normalizeSpawnSyncOptions(options);
  108. return spawnSync(...concatenateShell(file, commandArguments, normalizedOptions));
  109. } catch (error) {
  110. return makeEarlyError({
  111. error,
  112. command,
  113. escapedCommand,
  114. fileDescriptors,
  115. options,
  116. startTime,
  117. isSync: true,
  118. });
  119. }
  120. };
  121. // The `encoding` option is handled by Execa, not by `child_process.spawnSync()`
  122. const normalizeSpawnSyncOptions = ({encoding, maxBuffer, ...options}) => ({...options, encoding: 'buffer', maxBuffer: getMaxBufferSync(maxBuffer)});
  123. const getSyncResult = ({error, exitCode, signal, timedOut, isMaxBuffer, stdio, all, options, command, escapedCommand, startTime}) => error === undefined
  124. ? makeSuccessResult({
  125. command,
  126. escapedCommand,
  127. stdio,
  128. all,
  129. ipcOutput: [],
  130. options,
  131. startTime,
  132. })
  133. : makeError({
  134. error,
  135. command,
  136. escapedCommand,
  137. timedOut,
  138. isCanceled: false,
  139. isGracefullyCanceled: false,
  140. isMaxBuffer,
  141. isForcefullyTerminated: false,
  142. exitCode,
  143. signal,
  144. stdio,
  145. all,
  146. ipcOutput: [],
  147. options,
  148. startTime,
  149. isSync: true,
  150. });