import {writeFileSync, appendFileSync} from 'node:fs'; import {shouldLogOutput, logLinesSync} from '../verbose/output.js'; import {runGeneratorsSync} from '../transform/generator.js'; import {splitLinesSync} from '../transform/split.js'; import {joinToString, joinToUint8Array, bufferToUint8Array} from '../utils/uint-array.js'; import {FILE_TYPES} from '../stdio/type.js'; import {truncateMaxBufferSync} from './max-buffer.js'; // Apply `stdout`/`stderr` options, after spawning, in sync mode export const transformOutputSync = ({fileDescriptors, syncResult: {output}, options, isMaxBuffer, verboseInfo}) => { if (output === null) { return {output: Array.from({length: 3})}; } const state = {}; const outputFiles = new Set([]); const transformedOutput = output.map((result, fdNumber) => transformOutputResultSync({ result, fileDescriptors, fdNumber, state, outputFiles, isMaxBuffer, verboseInfo, }, options)); return {output: transformedOutput, ...state}; }; const transformOutputResultSync = ( {result, fileDescriptors, fdNumber, state, outputFiles, isMaxBuffer, verboseInfo}, {buffer, encoding, lines, stripFinalNewline, maxBuffer}, ) => { if (result === null) { return; } const truncatedResult = truncateMaxBufferSync(result, isMaxBuffer, maxBuffer); const uint8ArrayResult = bufferToUint8Array(truncatedResult); const {stdioItems, objectMode} = fileDescriptors[fdNumber]; const chunks = runOutputGeneratorsSync([uint8ArrayResult], stdioItems, encoding, state); const {serializedResult, finalResult = serializedResult} = serializeChunks({ chunks, objectMode, encoding, lines, stripFinalNewline, fdNumber, }); logOutputSync({ serializedResult, fdNumber, state, verboseInfo, encoding, stdioItems, objectMode, }); const returnedResult = buffer[fdNumber] ? finalResult : undefined; try { if (state.error === undefined) { writeToFiles(serializedResult, stdioItems, outputFiles); } return returnedResult; } catch (error) { state.error = error; return returnedResult; } }; // Applies transform generators to `stdout`/`stderr` const runOutputGeneratorsSync = (chunks, stdioItems, encoding, state) => { try { return runGeneratorsSync(chunks, stdioItems, encoding, false); } catch (error) { state.error = error; return chunks; } }; // The contents is converted to three stages: // - serializedResult: used when the target is a file path/URL or a file descriptor (including 'inherit') // - finalResult/returnedResult: returned as `result.std*` const serializeChunks = ({chunks, objectMode, encoding, lines, stripFinalNewline, fdNumber}) => { if (objectMode) { return {serializedResult: chunks}; } if (encoding === 'buffer') { return {serializedResult: joinToUint8Array(chunks)}; } const serializedResult = joinToString(chunks, encoding); if (lines[fdNumber]) { return {serializedResult, finalResult: splitLinesSync(serializedResult, !stripFinalNewline[fdNumber], objectMode)}; } return {serializedResult}; }; const logOutputSync = ({serializedResult, fdNumber, state, verboseInfo, encoding, stdioItems, objectMode}) => { if (!shouldLogOutput({ stdioItems, encoding, verboseInfo, fdNumber, })) { return; } const linesArray = splitLinesSync(serializedResult, false, objectMode); try { logLinesSync(linesArray, fdNumber, verboseInfo); } catch (error) { state.error ??= error; } }; // When the `std*` target is a file path/URL or a file descriptor const writeToFiles = (serializedResult, stdioItems, outputFiles) => { for (const {path, append} of stdioItems.filter(({type}) => FILE_TYPES.has(type))) { const pathString = typeof path === 'string' ? path : path.toString(); if (append || outputFiles.has(pathString)) { appendFileSync(path, serializedResult); } else { outputFiles.add(pathString); writeFileSync(path, serializedResult); } } };