-
-
Notifications
You must be signed in to change notification settings - Fork 203
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix passing the same file or stream to both
stdout
and stderr
- Loading branch information
Showing
12 changed files
with
445 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
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.path === secondValue.path; | ||
} | ||
|
||
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.`); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.