123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- import {
- SPECIAL_DUPLICATE_TYPES_SYNC,
- SPECIAL_DUPLICATE_TYPES,
- FORBID_DUPLICATE_TYPES,
- TYPE_TO_MESSAGE,
- } from './type.js';
- // Duplicates in the same file descriptor is most likely an error.
- // However, this can be useful with generators.
- export const filterDuplicates = stdioItems => stdioItems.filter((stdioItemOne, indexOne) =>
- stdioItems.every((stdioItemTwo, indexTwo) => stdioItemOne.value !== stdioItemTwo.value
- || indexOne >= indexTwo
- || stdioItemOne.type === 'generator'
- || stdioItemOne.type === 'asyncGenerator'));
- // Check if two file descriptors are sharing the same target.
- // For example `{stdout: {file: './output.txt'}, stderr: {file: './output.txt'}}`.
- export const getDuplicateStream = ({stdioItem: {type, value, optionName}, direction, fileDescriptors, isSync}) => {
- const otherStdioItems = getOtherStdioItems(fileDescriptors, type);
- if (otherStdioItems.length === 0) {
- return;
- }
- if (isSync) {
- validateDuplicateStreamSync({
- otherStdioItems,
- type,
- value,
- optionName,
- direction,
- });
- return;
- }
- if (SPECIAL_DUPLICATE_TYPES.has(type)) {
- return getDuplicateStreamInstance({
- otherStdioItems,
- type,
- value,
- optionName,
- direction,
- });
- }
- if (FORBID_DUPLICATE_TYPES.has(type)) {
- validateDuplicateTransform({
- otherStdioItems,
- type,
- value,
- optionName,
- });
- }
- };
- // Values shared by multiple file descriptors
- const getOtherStdioItems = (fileDescriptors, type) => fileDescriptors
- .flatMap(({direction, stdioItems}) => stdioItems
- .filter(stdioItem => stdioItem.type === type)
- .map((stdioItem => ({...stdioItem, direction}))));
- // With `execaSync()`, do not allow setting a file path both in input and output
- const validateDuplicateStreamSync = ({otherStdioItems, type, value, optionName, direction}) => {
- if (SPECIAL_DUPLICATE_TYPES_SYNC.has(type)) {
- getDuplicateStreamInstance({
- otherStdioItems,
- type,
- value,
- optionName,
- direction,
- });
- }
- };
- // When two file descriptors share the file or stream, we need to re-use the same underlying stream.
- // Otherwise, the stream would be closed twice when piping ends.
- // This is only an issue with output file descriptors.
- // This is not a problem with generator functions since those create a new instance for each file descriptor.
- // We also forbid input and output file descriptors sharing the same file or stream, since that does not make sense.
- const getDuplicateStreamInstance = ({otherStdioItems, type, value, optionName, direction}) => {
- const duplicateStdioItems = otherStdioItems.filter(stdioItem => hasSameValue(stdioItem, value));
- if (duplicateStdioItems.length === 0) {
- return;
- }
- const differentStdioItem = duplicateStdioItems.find(stdioItem => stdioItem.direction !== direction);
- throwOnDuplicateStream(differentStdioItem, optionName, type);
- return direction === 'output' ? duplicateStdioItems[0].stream : undefined;
- };
- const hasSameValue = ({type, value}, secondValue) => {
- if (type === 'filePath') {
- return value.file === secondValue.file;
- }
- if (type === 'fileUrl') {
- return value.href === secondValue.href;
- }
- return value === secondValue;
- };
- // We do not allow two file descriptors to share the same Duplex or TransformStream.
- // This is because those are set directly to `subprocess.std*`.
- // For example, this could result in `subprocess.stdout` and `subprocess.stderr` being the same value.
- // This means reading from either would get data from both stdout and stderr.
- const validateDuplicateTransform = ({otherStdioItems, type, value, optionName}) => {
- const duplicateStdioItem = otherStdioItems.find(({value: {transform}}) => transform === value.transform);
- throwOnDuplicateStream(duplicateStdioItem, optionName, type);
- };
- const throwOnDuplicateStream = (stdioItem, optionName, type) => {
- if (stdioItem !== undefined) {
- throw new TypeError(`The \`${stdioItem.optionName}\` and \`${optionName}\` options must not target ${TYPE_TO_MESSAGE[type]} that is the same.`);
- }
- };
|