136 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 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);
 | |
| 		}
 | |
| 	}
 | |
| };
 |