Skip to content

Commit

Permalink
Split input/inputFile options to their own file
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Dec 17, 2023
1 parent 248349c commit 554ef15
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 67 deletions.
2 changes: 1 addition & 1 deletion lib/stdio/async.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {createReadStream, createWriteStream} from 'node:fs';
import {Readable, Writable} from 'node:stream';
import {handleInput} from './input.js';
import {handleInput} from './handle.js';

// Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in async mode
export const handleInputAsync = options => handleInput(addPropertiesAsync, options);
Expand Down
55 changes: 55 additions & 0 deletions lib/stdio/handle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {getStdioOptionType, isRegularUrl, isUnknownStdioString} from './type.js';
import {normalizeStdio} from './normalize.js';
import {handleInputOption, handleInputFileOption} from './input.js';

// Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in async/sync mode
export const handleInput = (addProperties, options) => {
const stdio = normalizeStdio(options);
const stdioArray = arrifyStdio(stdio);
const stdioStreams = stdioArray.map((stdioOption, index) => getStdioStream(stdioOption, index, addProperties, options));
options.stdio = transformStdio(stdio, stdioStreams);
return stdioStreams;
};

const arrifyStdio = (stdio = []) => Array.isArray(stdio) ? stdio : [stdio, stdio, stdio];

const getStdioStream = (stdioOption, index, addProperties, {input, inputFile}) => {
let stdioStream = {
type: getStdioOptionType(stdioOption),
value: stdioOption,
optionName: OPTION_NAMES[index],
direction: index === 0 ? 'input' : 'output',
};

stdioStream = handleInputOption(stdioStream, index, input);
stdioStream = handleInputFileOption(stdioStream, index, inputFile, input);

validateFileStdio(stdioStream);

return {
...stdioStream,
...addProperties[stdioStream.direction][stdioStream.type]?.(stdioStream),
};
};

const OPTION_NAMES = ['stdin', 'stdout', 'stderr'];

const validateFileStdio = ({type, value, optionName}) => {
if (isRegularUrl(value)) {
throw new TypeError(`The \`${optionName}: URL\` option must use the \`file:\` scheme.
For example, you can use the \`pathToFileURL()\` method of the \`url\` core module.`);
}

if (isUnknownStdioString(type, value)) {
throw new TypeError(`The \`${optionName}: filePath\` option must either be an absolute file path or start with \`.\`.`);
}
};

// When the `std*: Iterable | WebStream | URL | filePath`, `input` or `inputFile` option is used, we pipe to `spawned.std*`.
// Therefore the `std*` options must be either `pipe` or `overlapped`. Other values do not set `spawned.std*`.
const transformStdio = (stdio, stdioStreams) => Array.isArray(stdio)
? stdio.map((stdioItem, index) => transformStdioItem(stdioItem, index, stdioStreams))
: stdio;

const transformStdioItem = (stdioItem, index, stdioStreams) =>
stdioStreams[index].type !== 'native' && stdioItem !== 'overlapped' ? 'pipe' : stdioItem;
66 changes: 2 additions & 64 deletions lib/stdio/input.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,7 @@
import {isStream as isNodeStream} from 'is-stream';
import {getStdioOptionType, isRegularUrl, isUnknownStdioString} from './type.js';
import {normalizeStdio} from './normalize.js';

// Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in async/sync mode
export const handleInput = (addProperties, options) => {
const stdio = normalizeStdio(options);
const stdioArray = arrifyStdio(stdio);
const stdioStreams = stdioArray.map((stdioOption, index) => getStdioStream(stdioOption, index, addProperties, options));
options.stdio = transformStdio(stdio, stdioStreams);
return stdioStreams;
};

const arrifyStdio = (stdio = []) => Array.isArray(stdio) ? stdio : [stdio, stdio, stdio];

const getStdioStream = (stdioOption, index, addProperties, {input, inputFile}) => {
let stdioStream = {
type: getStdioOptionType(stdioOption),
value: stdioOption,
optionName: OPTION_NAMES[index],
direction: index === 0 ? 'input' : 'output',
};
validateFileStdio(stdioStream);

stdioStream = handleInputOption(stdioStream, index, input);
stdioStream = handleInputFileOption(stdioStream, index, inputFile, input);

return {
...stdioStream,
...addProperties[stdioStream.direction][stdioStream.type]?.(stdioStream),
};
};

const OPTION_NAMES = ['stdin', 'stdout', 'stderr'];

const validateFileStdio = ({type, value, optionName}) => {
if (type !== 'native') {
return;
}

validateRegularUrl(value, optionName);

if (isUnknownStdioString(value)) {
throw new TypeError(`The \`${optionName}: filePath\` option must either be an absolute file path or start with \`.\`.`);
}
};

// Override the `stdin` option with the `input` option
const handleInputOption = (stdioStream, index, input) => {
export const handleInputOption = (stdioStream, index, input) => {
if (input === undefined || index !== 0) {
return stdioStream;
}
Expand All @@ -58,7 +13,7 @@ const handleInputOption = (stdioStream, index, input) => {
};

// Override the `stdin` option with the `inputFile` option
const handleInputFileOption = (stdioStream, index, inputFile, input) => {
export const handleInputFileOption = (stdioStream, index, inputFile, input) => {
if (inputFile === undefined || index !== 0) {
return stdioStream;
}
Expand All @@ -69,7 +24,6 @@ const handleInputFileOption = (stdioStream, index, inputFile, input) => {

const optionName = 'inputFile';
validateInputOption(stdioStream.value, optionName);
validateRegularUrl(inputFile, optionName);
return {...stdioStream, value: inputFile, type: 'filePath', optionName};
};

Expand All @@ -80,19 +34,3 @@ const validateInputOption = (value, optionName) => {
};

const CAN_USE_INPUT = new Set([undefined, null, 'overlapped', 'pipe']);

const validateRegularUrl = (value, optionName) => {
if (isRegularUrl(value)) {
throw new TypeError(`The \`${optionName}: URL\` option must use the \`file:\` scheme.
For example, you can use the \`pathToFileURL()\` method of the \`url\` core module.`);
}
};

// When the `std*: Iterable | WebStream | URL | filePath`, `input` or `inputFile` option is used, we pipe to `spawned.std*`.
// Therefore the `std*` options must be either `pipe` or `overlapped`. Other values do not set `spawned.std*`.
const transformStdio = (stdio, stdioStreams) => Array.isArray(stdio)
? stdio.map((stdioItem, index) => transformStdioItem(stdioItem, index, stdioStreams))
: stdio;

const transformStdioItem = (stdioItem, index, stdioStreams) =>
stdioStreams[index].type !== 'native' && stdioItem !== 'overlapped' ? 'pipe' : stdioItem;
2 changes: 1 addition & 1 deletion lib/stdio/sync.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {readFileSync, writeFileSync} from 'node:fs';
import {handleInput} from './input.js';
import {handleInput} from './handle.js';
import {TYPE_TO_MESSAGE} from './type.js';

// Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in sync mode
Expand Down
3 changes: 2 additions & 1 deletion lib/stdio/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const isRegularUrl = stdioOption => isUrlInstance(stdioOption) && !hasFil

const stringIsFilePath = stdioOption => stdioOption.startsWith('.') || isAbsolute(stdioOption);
const isFilePath = stdioOption => typeof stdioOption === 'string' && stringIsFilePath(stdioOption);
export const isUnknownStdioString = stdioOption => typeof stdioOption === 'string' && !stringIsFilePath(stdioOption) && !KNOWN_STDIO_STRINGS.has(stdioOption);

export const isUnknownStdioString = (type, stdioOption) => type === 'native' && typeof stdioOption === 'string' && !KNOWN_STDIO_STRINGS.has(stdioOption);
const KNOWN_STDIO_STRINGS = new Set(['ipc', 'ignore', 'inherit', 'overlapped', 'pipe']);

const isReadableStream = stdioOption => Object.prototype.toString.call(stdioOption) === '[object ReadableStream]';
Expand Down

0 comments on commit 554ef15

Please sign in to comment.