Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Apr 23, 2024
1 parent 08eb743 commit 264c315
Show file tree
Hide file tree
Showing 16 changed files with 75 additions and 63 deletions.
4 changes: 2 additions & 2 deletions lib/arguments/fd-options.js
@@ -1,6 +1,6 @@
import {parseFd} from './specific.js';

export const getWritable = (destination, to = 'stdin') => {
export const getToStream = (destination, to = 'stdin') => {
const isWritable = true;
const {options, fileDescriptors} = SUBPROCESS_OPTIONS.get(destination);
const fdNumber = getFdNumber(fileDescriptors, to, isWritable);
Expand All @@ -13,7 +13,7 @@ export const getWritable = (destination, to = 'stdin') => {
return destinationStream;
};

export const getReadable = (source, from = 'stdout') => {
export const getFromStream = (source, from = 'stdout') => {
const isWritable = false;
const {options, fileDescriptors} = SUBPROCESS_OPTIONS.get(source);
const fdNumber = getFdNumber(fileDescriptors, from, isWritable);
Expand Down
2 changes: 1 addition & 1 deletion lib/arguments/options.js
Expand Up @@ -10,7 +10,7 @@ import {normalizeCwd} from './cwd.js';
import {normalizeFileUrl} from './file-url.js';
import {normalizeFdSpecificOptions} from './specific.js';

export const handleOptions = (filePath, rawArguments, rawOptions) => {
export const normalizeOptions = (filePath, rawArguments, rawOptions) => {
rawOptions.cwd = normalizeCwd(rawOptions.cwd);
const [processedFile, processedArguments, processedOptions] = handleNodeOption(filePath, rawArguments, rawOptions);

Expand Down
4 changes: 2 additions & 2 deletions lib/convert/iterable.js
@@ -1,5 +1,5 @@
import {BINARY_ENCODINGS} from '../arguments/encoding-option.js';
import {getReadable} from '../arguments/fd-options.js';
import {getFromStream} from '../arguments/fd-options.js';
import {iterateOnSubprocessStream} from '../io/iterate.js';

export const createIterable = (subprocess, encoding, {
Expand All @@ -8,7 +8,7 @@ export const createIterable = (subprocess, encoding, {
preserveNewlines = false,
} = {}) => {
const binary = binaryOption || BINARY_ENCODINGS.has(encoding);
const subprocessStdout = getReadable(subprocess, from);
const subprocessStdout = getFromStream(subprocess, from);
const onStdoutData = iterateOnSubprocessStream({
subprocessStdout,
subprocess,
Expand Down
4 changes: 2 additions & 2 deletions lib/convert/readable.js
@@ -1,7 +1,7 @@
import {Readable} from 'node:stream';
import {callbackify} from 'node:util';
import {BINARY_ENCODINGS} from '../arguments/encoding-option.js';
import {getReadable} from '../arguments/fd-options.js';
import {getFromStream} from '../arguments/fd-options.js';
import {iterateOnSubprocessStream, DEFAULT_OBJECT_HIGH_WATER_MARK} from '../io/iterate.js';
import {addConcurrentStream, waitForConcurrentStreams} from './concurrent.js';
import {
Expand Down Expand Up @@ -42,7 +42,7 @@ export const createReadable = ({subprocess, concurrentStreams, encoding}, {from,

// Retrieve `stdout` (or other stream depending on `from`)
export const getSubprocessStdout = (subprocess, from, concurrentStreams) => {
const subprocessStdout = getReadable(subprocess, from);
const subprocessStdout = getFromStream(subprocess, from);
const waitReadableDestroy = addConcurrentStream(concurrentStreams, subprocessStdout, 'readableDestroy');
return {subprocessStdout, waitReadableDestroy};
};
Expand Down
4 changes: 2 additions & 2 deletions lib/convert/writable.js
@@ -1,6 +1,6 @@
import {Writable} from 'node:stream';
import {callbackify} from 'node:util';
import {getWritable} from '../arguments/fd-options.js';
import {getToStream} from '../arguments/fd-options.js';
import {addConcurrentStream, waitForConcurrentStreams} from './concurrent.js';
import {
safeWaitForSubprocessStdout,
Expand Down Expand Up @@ -29,7 +29,7 @@ export const createWritable = ({subprocess, concurrentStreams}, {to} = {}) => {

// Retrieve `stdin` (or other stream depending on `to`)
export const getSubprocessStdin = (subprocess, to, concurrentStreams) => {
const subprocessStdin = getWritable(subprocess, to);
const subprocessStdin = getToStream(subprocess, to);
const waitWritableFinal = addConcurrentStream(concurrentStreams, subprocessStdin, 'writableFinal');
const waitWritableDestroy = addConcurrentStream(concurrentStreams, subprocessStdin, 'writableDestroy');
return {subprocessStdin, waitWritableFinal, waitWritableDestroy};
Expand Down
12 changes: 11 additions & 1 deletion lib/io/max-buffer.js
Expand Up @@ -51,5 +51,15 @@ export const isMaxBufferSync = (resultError, output, maxBuffer) => resultError?.
&& output !== null
&& output.some(result => result !== null && result.length > getMaxBufferSync(maxBuffer));

// When `maxBuffer` is hit, ensure the result is truncated
export const truncateMaxBufferSync = (result, isMaxBuffer, maxBuffer) => {
if (!isMaxBuffer) {
return result;
}

const maxBufferValue = getMaxBufferSync(maxBuffer);
return result.length > maxBufferValue ? result.slice(0, maxBufferValue) : result;
};

// `spawnSync()` does not allow differentiating `maxBuffer` per file descriptor, so we always use `stdout`
export const getMaxBufferSync = maxBuffer => maxBuffer[1];
export const getMaxBufferSync = ([, stdoutMaxBuffer]) => stdoutMaxBuffer;
8 changes: 2 additions & 6 deletions lib/io/output-sync.js
Expand Up @@ -4,7 +4,7 @@ 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 {getMaxBufferSync} from './max-buffer.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}) => {
Expand All @@ -30,7 +30,7 @@ const transformOutputResultSync = ({result, fileDescriptors, fdNumber, state, is
return;
}

const truncatedResult = truncateResult(result, isMaxBuffer, getMaxBufferSync(maxBuffer));
const truncatedResult = truncateMaxBufferSync(result, isMaxBuffer, maxBuffer);
const uint8ArrayResult = bufferToUint8Array(truncatedResult);
const {stdioItems, objectMode} = fileDescriptors[fdNumber];
const chunks = runOutputGeneratorsSync([uint8ArrayResult], stdioItems, encoding, state);
Expand Down Expand Up @@ -67,10 +67,6 @@ const transformOutputResultSync = ({result, fileDescriptors, fdNumber, state, is
}
};

const truncateResult = (result, isMaxBuffer, maxBuffer) => isMaxBuffer && result.length > maxBuffer
? result.slice(0, maxBuffer)
: result;

const runOutputGeneratorsSync = (chunks, stdioItems, encoding, state) => {
try {
return runGeneratorsSync(chunks, stdioItems, encoding, false);
Expand Down
23 changes: 23 additions & 0 deletions lib/methods/bind.js
@@ -0,0 +1,23 @@
import isPlainObject from 'is-plain-obj';
import {FD_SPECIFIC_OPTIONS} from '../arguments/specific.js';

// Deep merge specific options like `env`. Shallow merge the other ones.
export const mergeOptions = (boundOptions, options) => {
const newOptions = Object.fromEntries(
Object.entries(options).map(([optionName, optionValue]) => [
optionName,
mergeOption(optionName, boundOptions[optionName], optionValue),
]),
);
return {...boundOptions, ...newOptions};
};

const mergeOption = (optionName, boundOptionValue, optionValue) => {
if (DEEP_OPTIONS.has(optionName) && isPlainObject(boundOptionValue) && isPlainObject(optionValue)) {
return {...boundOptionValue, ...optionValue};
}

return optionValue;
};

const DEEP_OPTIONS = new Set(['env', ...FD_SPECIFIC_OPTIONS]);
23 changes: 1 addition & 22 deletions lib/methods/create.js
@@ -1,9 +1,9 @@
import isPlainObject from 'is-plain-obj';
import {FD_SPECIFIC_OPTIONS} from '../arguments/specific.js';
import {normalizeParameters} from './parameters.js';
import {isTemplateString, parseTemplates} from './template.js';
import {execaCoreSync} from './main-sync.js';
import {execaCoreAsync} from './main-async.js';
import {mergeOptions} from './bind.js';

export const createExeca = (mapArguments, boundOptions, deepOptions, setBoundExeca) => {
const createNested = (mapArguments, boundOptions, setBoundExeca) => createExeca(mapArguments, boundOptions, deepOptions, setBoundExeca);
Expand Down Expand Up @@ -58,24 +58,3 @@ const parseArguments = ({mapArguments, firstArgument, nextArguments, deepOptions
isSync,
};
};

// Deep merge specific options like `env`. Shallow merge the other ones.
const mergeOptions = (boundOptions, options) => {
const newOptions = Object.fromEntries(
Object.entries(options).map(([optionName, optionValue]) => [
optionName,
mergeOption(optionName, boundOptions[optionName], optionValue),
]),
);
return {...boundOptions, ...newOptions};
};

const mergeOption = (optionName, boundOptionValue, optionValue) => {
if (DEEP_OPTIONS.has(optionName) && isPlainObject(boundOptionValue) && isPlainObject(optionValue)) {
return {...boundOptionValue, ...optionValue};
}

return optionValue;
};

const DEEP_OPTIONS = new Set(['env', ...FD_SPECIFIC_OPTIONS]);
4 changes: 2 additions & 2 deletions lib/methods/main-async.js
Expand Up @@ -2,7 +2,7 @@ import {setMaxListeners} from 'node:events';
import {spawn} from 'node:child_process';
import {MaxBufferError} from 'get-stream';
import {handleCommand} from '../arguments/command.js';
import {handleOptions} from '../arguments/options.js';
import {normalizeOptions} from '../arguments/options.js';
import {SUBPROCESS_OPTIONS} from '../arguments/fd-options.js';
import {makeError, makeSuccessResult} from '../return/result.js';
import {handleResult} from '../return/reject.js';
Expand Down Expand Up @@ -46,7 +46,7 @@ const handleAsyncArguments = (rawFile, rawArguments, rawOptions) => {
const {command, escapedCommand, startTime, verboseInfo} = handleCommand(rawFile, rawArguments, rawOptions);

try {
const {file, commandArguments, options: normalizedOptions} = handleOptions(rawFile, rawArguments, rawOptions);
const {file, commandArguments, options: normalizedOptions} = normalizeOptions(rawFile, rawArguments, rawOptions);
const options = handleAsyncOptions(normalizedOptions);
const fileDescriptors = handleStdioAsync(options, verboseInfo);
return {
Expand Down
4 changes: 2 additions & 2 deletions lib/methods/main-sync.js
@@ -1,6 +1,6 @@
import {spawnSync} from 'node:child_process';
import {handleCommand} from '../arguments/command.js';
import {handleOptions} from '../arguments/options.js';
import {normalizeOptions} from '../arguments/options.js';
import {makeError, makeEarlyError, makeSuccessResult} from '../return/result.js';
import {handleResult} from '../return/reject.js';
import {handleStdioSync} from '../stdio/handle-sync.js';
Expand Down Expand Up @@ -32,7 +32,7 @@ const handleSyncArguments = (rawFile, rawArguments, rawOptions) => {

try {
const syncOptions = normalizeSyncOptions(rawOptions);
const {file, commandArguments, options} = handleOptions(rawFile, rawArguments, syncOptions);
const {file, commandArguments, options} = normalizeOptions(rawFile, rawArguments, syncOptions);
validateSyncOptions(options);
const fileDescriptors = handleStdioSync(options, verboseInfo);
return {
Expand Down
34 changes: 17 additions & 17 deletions lib/methods/template.js
@@ -1,4 +1,5 @@
import {ChildProcess} from 'node:child_process';
import isPlainObject from 'is-plain-obj';
import {isUint8Array, uint8ArrayToString} from '../utils/uint-array.js';

export const isTemplateString = templates => Array.isArray(templates) && Array.isArray(templates.raw);
Expand Down Expand Up @@ -116,26 +117,25 @@ const parseExpression = expression => {
return String(expression);
}

if (
typeOfExpression === 'object'
&& expression !== null
&& !isSubprocess(expression)
&& 'stdout' in expression
) {
const typeOfStdout = typeof expression.stdout;
if (isSubprocessResult(expression)) {
return getSubprocessResult(expression);
}

if (typeOfStdout === 'string') {
return expression.stdout;
}
throw new TypeError(`Unexpected "${typeOfExpression}" in template expression`);
};

if (isUint8Array(expression.stdout)) {
return uint8ArrayToString(expression.stdout);
}
const isSubprocessResult = expression => isPlainObject(expression)
&& 'stdout' in expression
&& !(expression instanceof ChildProcess);

throw new TypeError(`Unexpected "${typeOfStdout}" stdout in template expression`);
const getSubprocessResult = ({stdout}) => {
if (typeof stdout === 'string') {
return stdout;
}

throw new TypeError(`Unexpected "${typeOfExpression}" in template expression`);
};
if (isUint8Array(stdout)) {
return uint8ArrayToString(stdout);
}

const isSubprocess = value => value instanceof ChildProcess;
throw new TypeError(`Unexpected "${typeof stdout}" stdout in template expression`);
};
6 changes: 3 additions & 3 deletions lib/pipe/pipe-arguments.js
@@ -1,6 +1,6 @@
import {normalizeParameters} from '../methods/parameters.js';
import {getStartTime} from '../return/duration.js';
import {SUBPROCESS_OPTIONS, getWritable, getReadable} from '../arguments/fd-options.js';
import {SUBPROCESS_OPTIONS, getToStream, getFromStream} from '../arguments/fd-options.js';

export const normalizePipeArguments = ({source, sourcePromise, boundOptions, createNested}, ...pipeArguments) => {
const startTime = getStartTime();
Expand Down Expand Up @@ -33,7 +33,7 @@ const getDestinationStream = (boundOptions, createNested, pipeArguments) => {
destination,
pipeOptions: {from, to, unpipeSignal} = {},
} = getDestination(boundOptions, createNested, ...pipeArguments);
const destinationStream = getWritable(destination, to);
const destinationStream = getToStream(destination, to);
return {
destination,
destinationStream,
Expand Down Expand Up @@ -76,7 +76,7 @@ const mapDestinationArguments = ({options}) => ({options: {...options, stdin: 'p

const getSourceStream = (source, from) => {
try {
const sourceStream = getReadable(source, from);
const sourceStream = getFromStream(source, from);
return {sourceStream};
} catch (error) {
return {sourceError: error};
Expand Down
6 changes: 5 additions & 1 deletion lib/terminate/kill.js
Expand Up @@ -21,7 +21,11 @@ export const normalizeForceKillAfterDelay = forceKillAfterDelay => {
const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5;

// Monkey-patches `subprocess.kill()` to add `forceKillAfterDelay` behavior and `.kill(error)`
export const subprocessKill = ({kill, subprocess, options: {forceKillAfterDelay, killSignal}, controller}, signalOrError, errorArgument) => {
export const subprocessKill = (
{kill, subprocess, options: {forceKillAfterDelay, killSignal}, controller},
signalOrError,
errorArgument,
) => {
const {signal, error} = parseKillArguments(signalOrError, errorArgument, killSignal);
emitKillError(subprocess, error);
const killResult = kill(signal);
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit 264c315

Please sign in to comment.