Repository: sindresorhus/execa
Pull request #977 (2024-04-16 16:01:00)

Rename files
This PR renames files. It does not change files contents (except for import file paths).

After hundreds of previous PRs, the codebase's file organization needs some cleanup. This PR does this, by re-organizing files and directories. It also makes filenames more explicit.


Patch:
diff --git a/index.js b/index.js
index 663b364..d9f5690 100644
--- a/index.js
+++ b/index.js
@@ -1,9 +1,9 @@
-import {createExeca} from './lib/arguments/create.js';
-import {mapCommandAsync, mapCommandSync} from './lib/command.js';
-import {mapNode} from './lib/arguments/node.js';
-import {mapScriptAsync, setScriptSync, deepScriptOptions} from './lib/script.js';
+import {createExeca} from './lib/methods/create.js';
+import {mapCommandAsync, mapCommandSync} from './lib/methods/command.js';
+import {mapNode} from './lib/methods/node.js';
+import {mapScriptAsync, setScriptSync, deepScriptOptions} from './lib/methods/script.js';
 
-export {ExecaError, ExecaSyncError} from './lib/return/cause.js';
+export {ExecaError, ExecaSyncError} from './lib/return/final-error.js';
 
 export const execa = createExeca(() => ({}));
 export const execaSync = createExeca(() => ({isSync: true}));
diff --git a/lib/arguments/encoding.js b/lib/arguments/encoding-option.js
similarity index 100%
rename from lib/arguments/encoding.js
rename to lib/arguments/encoding-option.js
diff --git a/lib/convert/fd-options.js b/lib/arguments/fd-options.js
similarity index 98%
rename from lib/convert/fd-options.js
rename to lib/arguments/fd-options.js
index 6590c5e..3876c5b 100644
--- a/lib/convert/fd-options.js
+++ b/lib/arguments/fd-options.js
@@ -1,4 +1,4 @@
-import {parseFd} from '../arguments/specific.js';
+import {parseFd} from './specific.js';
 
 export const getWritable = (destination, to = 'stdin') => {
 	const isWritable = true;
diff --git a/lib/arguments/options.js b/lib/arguments/options.js
index 1299219..790346b 100644
--- a/lib/arguments/options.js
+++ b/lib/arguments/options.js
@@ -2,10 +2,10 @@ import {basename} from 'node:path';
 import process from 'node:process';
 import crossSpawn from 'cross-spawn';
 import {npmRunPathEnv} from 'npm-run-path';
-import {normalizeForceKillAfterDelay} from '../exit/kill.js';
-import {validateTimeout} from '../exit/timeout.js';
-import {validateEncoding, BINARY_ENCODINGS} from './encoding.js';
-import {handleNodeOption} from './node.js';
+import {normalizeForceKillAfterDelay} from '../terminate/kill.js';
+import {validateTimeout} from '../terminate/timeout.js';
+import {handleNodeOption} from '../methods/node.js';
+import {validateEncoding, BINARY_ENCODINGS} from './encoding-option.js';
 import {normalizeCwd} from './cwd.js';
 import {normalizeFileUrl} from './file-url.js';
 import {normalizeFdSpecificOptions} from './specific.js';
diff --git a/lib/arguments/specific.js b/lib/arguments/specific.js
index 4938fe1..c57bbc2 100644
--- a/lib/arguments/specific.js
+++ b/lib/arguments/specific.js
@@ -1,5 +1,5 @@
 import isPlainObject from 'is-plain-obj';
-import {STANDARD_STREAMS_ALIASES} from '../utils.js';
+import {STANDARD_STREAMS_ALIASES} from '../utils/standard-stream.js';
 import {verboseDefault} from '../verbose/info.js';
 
 export const normalizeFdSpecificOptions = options => {
diff --git a/lib/convert/duplex.js b/lib/convert/duplex.js
index e15bb5f..4c3a757 100644
--- a/lib/convert/duplex.js
+++ b/lib/convert/duplex.js
@@ -1,6 +1,6 @@
 import {Duplex} from 'node:stream';
 import {callbackify} from 'node:util';
-import {BINARY_ENCODINGS} from '../arguments/encoding.js';
+import {BINARY_ENCODINGS} from '../arguments/encoding-option.js';
 import {
 	getSubprocessStdout,
 	getReadableOptions,
diff --git a/lib/convert/iterable.js b/lib/convert/iterable.js
index f720596..59dc15b 100644
--- a/lib/convert/iterable.js
+++ b/lib/convert/iterable.js
@@ -1,6 +1,6 @@
-import {BINARY_ENCODINGS} from '../arguments/encoding.js';
-import {getReadable} from './fd-options.js';
-import {iterateOnSubprocessStream} from './loop.js';
+import {BINARY_ENCODINGS} from '../arguments/encoding-option.js';
+import {getReadable} from '../arguments/fd-options.js';
+import {iterateOnSubprocessStream} from '../io/iterate.js';
 
 export const createIterable = (subprocess, encoding, {
 	from,
diff --git a/lib/convert/readable.js b/lib/convert/readable.js
index a165ff9..3a09f03 100644
--- a/lib/convert/readable.js
+++ b/lib/convert/readable.js
@@ -1,7 +1,8 @@
 import {Readable} from 'node:stream';
 import {callbackify} from 'node:util';
-import {BINARY_ENCODINGS} from '../arguments/encoding.js';
-import {getReadable} from './fd-options.js';
+import {BINARY_ENCODINGS} from '../arguments/encoding-option.js';
+import {getReadable} from '../arguments/fd-options.js';
+import {iterateOnSubprocessStream, DEFAULT_OBJECT_HIGH_WATER_MARK} from '../io/iterate.js';
 import {addConcurrentStream, waitForConcurrentStreams} from './concurrent.js';
 import {
 	createDeferred,
@@ -10,7 +11,6 @@ import {
 	waitForSubprocess,
 	destroyOtherStream,
 } from './shared.js';
-import {iterateOnSubprocessStream, DEFAULT_OBJECT_HIGH_WATER_MARK} from './loop.js';
 
 // Create a `Readable` stream that forwards from `stdout` and awaits the subprocess
 export const createReadable = ({subprocess, concurrentStreams, encoding}, {from, binary: binaryOption = true, preserveNewlines = true} = {}) => {
diff --git a/lib/convert/shared.js b/lib/convert/shared.js
index ca45c2d..96d2afc 100644
--- a/lib/convert/shared.js
+++ b/lib/convert/shared.js
@@ -1,5 +1,5 @@
 import {finished} from 'node:stream/promises';
-import {isStreamAbort} from '../stream/wait.js';
+import {isStreamAbort} from '../resolve/wait-stream.js';
 
 export const safeWaitForSubprocessStdin = async subprocessStdin => {
 	if (subprocessStdin === undefined) {
diff --git a/lib/convert/writable.js b/lib/convert/writable.js
index bd0f4f8..0098b71 100644
--- a/lib/convert/writable.js
+++ b/lib/convert/writable.js
@@ -1,6 +1,6 @@
 import {Writable} from 'node:stream';
 import {callbackify} from 'node:util';
-import {getWritable} from './fd-options.js';
+import {getWritable} from '../arguments/fd-options.js';
 import {addConcurrentStream, waitForConcurrentStreams} from './concurrent.js';
 import {
 	safeWaitForSubprocessStdout,
diff --git a/lib/stream/contents.js b/lib/io/contents.js
similarity index 93%
rename from lib/stream/contents.js
rename to lib/io/contents.js
index c94a85f..5bdf504 100644
--- a/lib/stream/contents.js
+++ b/lib/io/contents.js
@@ -1,10 +1,10 @@
 import {setImmediate} from 'node:timers/promises';
 import getStream, {getStreamAsArrayBuffer, getStreamAsArray} from 'get-stream';
-import {isArrayBuffer} from '../stdio/uint-array.js';
-import {iterateForResult} from '../convert/loop.js';
+import {isArrayBuffer} from '../utils/uint-array.js';
 import {shouldLogOutput, logLines} from '../verbose/output.js';
-import {getStripFinalNewline} from '../return/strip-newline.js';
+import {iterateForResult} from './iterate.js';
 import {handleMaxBuffer} from './max-buffer.js';
+import {getStripFinalNewline} from './strip-newline.js';
 
 export const getStreamOutput = async ({stream, onStreamEnd, fdNumber, encoding, buffer, maxBuffer, lines, allMixed, stripFinalNewline, verboseInfo, streamInfo: {fileDescriptors}}) => {
 	if (shouldLogOutput({stdioItems: fileDescriptors[fdNumber]?.stdioItems, encoding, verboseInfo, fdNumber})) {
diff --git a/lib/stdio/input-sync.js b/lib/io/input-sync.js
similarity index 90%
rename from lib/stdio/input-sync.js
rename to lib/io/input-sync.js
index d297f36..4b76757 100644
--- a/lib/stdio/input-sync.js
+++ b/lib/io/input-sync.js
@@ -1,6 +1,6 @@
-import {joinToUint8Array, isUint8Array} from './uint-array.js';
-import {TYPE_TO_MESSAGE} from './type.js';
-import {runGeneratorsSync} from './transform-run.js';
+import {runGeneratorsSync} from '../transform/generator.js';
+import {joinToUint8Array, isUint8Array} from '../utils/uint-array.js';
+import {TYPE_TO_MESSAGE} from '../stdio/type.js';
 
 // Apply `stdin`/`input`/`inputFile` options, before spawning, in sync mode, by converting it to the `input` option
 export const addInputOptionsSync = (fileDescriptors, options) => {
diff --git a/lib/convert/loop.js b/lib/io/iterate.js
similarity index 93%
rename from lib/convert/loop.js
rename to lib/io/iterate.js
index bb46abb..39dc3c8 100644
--- a/lib/convert/loop.js
+++ b/lib/io/iterate.js
@@ -1,7 +1,7 @@
 import {on} from 'node:events';
-import {getEncodingTransformGenerator} from '../stdio/encoding-transform.js';
-import {getSplitLinesGenerator} from '../stdio/split.js';
-import {transformChunkSync, finalChunksSync} from '../stdio/transform-sync.js';
+import {getEncodingTransformGenerator} from '../transform/encoding-transform.js';
+import {getSplitLinesGenerator} from '../transform/split.js';
+import {transformChunkSync, finalChunksSync} from '../transform/run-sync.js';
 
 // Iterate over lines of `subprocess.stdout`, used by `subprocess.readable|duplex|iterable()`
 export const iterateOnSubprocessStream = ({subprocessStdout, subprocess, binary, shouldEncode, encoding, preserveNewlines}) => {
diff --git a/lib/stream/max-buffer.js b/lib/io/max-buffer.js
similarity index 96%
rename from lib/stream/max-buffer.js
rename to lib/io/max-buffer.js
index 94f99b1..1c98f85 100644
--- a/lib/stream/max-buffer.js
+++ b/lib/io/max-buffer.js
@@ -1,5 +1,5 @@
 import {MaxBufferError} from 'get-stream';
-import {getStreamName} from '../utils.js';
+import {getStreamName} from '../utils/standard-stream.js';
 
 export const handleMaxBuffer = ({error, stream, readableObjectMode, lines, encoding, fdNumber}) => {
 	if (!(error instanceof MaxBufferError)) {
diff --git a/lib/stdio/output-async.js b/lib/io/output-async.js
similarity index 93%
rename from lib/stdio/output-async.js
rename to lib/io/output-async.js
index 32adf43..9c9a25c 100644
--- a/lib/stdio/output-async.js
+++ b/lib/io/output-async.js
@@ -1,8 +1,8 @@
 import mergeStreams from '@sindresorhus/merge-streams';
-import {isStandardStream} from '../utils.js';
-import {incrementMaxListeners} from '../max-listener.js';
+import {isStandardStream} from '../utils/standard-stream.js';
+import {incrementMaxListeners} from '../utils/max-listeners.js';
+import {TRANSFORM_TYPES} from '../stdio/type.js';
 import {pipeStreams} from './pipeline.js';
-import {TRANSFORM_TYPES} from './type.js';
 
 // Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, after spawning, in async mode
 // When multiple input streams are used, we merge them to ensure the output stream ends only once each input stream has ended
diff --git a/lib/stdio/output-sync.js b/lib/io/output-sync.js
similarity index 92%
rename from lib/stdio/output-sync.js
rename to lib/io/output-sync.js
index 92a1951..dbeb43e 100644
--- a/lib/stdio/output-sync.js
+++ b/lib/io/output-sync.js
@@ -1,10 +1,10 @@
 import {writeFileSync} from 'node:fs';
 import {shouldLogOutput, logLinesSync} from '../verbose/output.js';
-import {getMaxBufferSync} from '../stream/max-buffer.js';
-import {joinToString, joinToUint8Array, bufferToUint8Array} from './uint-array.js';
-import {runGeneratorsSync} from './transform-run.js';
-import {splitLinesSync} from './split.js';
-import {FILE_TYPES} from './type.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 {getMaxBufferSync} from './max-buffer.js';
 
 // Apply `stdout`/`stderr` options, after spawning, in sync mode
 export const transformOutputSync = ({fileDescriptors, syncResult: {output}, options, isMaxBuffer, verboseInfo}) => {
diff --git a/lib/stdio/pipeline.js b/lib/io/pipeline.js
similarity index 95%
rename from lib/stdio/pipeline.js
rename to lib/io/pipeline.js
index 84110d3..c02a52e 100644
--- a/lib/stdio/pipeline.js
+++ b/lib/io/pipeline.js
@@ -1,5 +1,5 @@
 import {finished} from 'node:stream/promises';
-import {isStandardStream} from '../utils.js';
+import {isStandardStream} from '../utils/standard-stream.js';
 
 // Like `Stream.pipeline(source, destination)`, but does not destroy standard streams.
 export const pipeStreams = (source, destination) => {
diff --git a/lib/return/strip-newline.js b/lib/io/strip-newline.js
similarity index 100%
rename from lib/return/strip-newline.js
rename to lib/io/strip-newline.js
diff --git a/lib/command.js b/lib/methods/command.js
similarity index 100%
rename from lib/command.js
rename to lib/methods/command.js
diff --git a/lib/arguments/create.js b/lib/methods/create.js
similarity index 88%
rename from lib/arguments/create.js
rename to lib/methods/create.js
index 89a970d..6097afb 100644
--- a/lib/arguments/create.js
+++ b/lib/methods/create.js
@@ -1,9 +1,9 @@
 import isPlainObject from 'is-plain-obj';
-import {execaCoreAsync} from '../async.js';
-import {execaCoreSync} from '../sync.js';
-import {normalizeArguments} from './normalize.js';
+import {FD_SPECIFIC_OPTIONS} from '../arguments/specific.js';
+import {normalizeParameters} from './parameters.js';
 import {isTemplateString, parseTemplates} from './template.js';
-import {FD_SPECIFIC_OPTIONS} from './specific.js';
+import {execaCoreSync} from './main-sync.js';
+import {execaCoreAsync} from './main-async.js';
 
 export const createExeca = (mapArguments, boundOptions, deepOptions, setBoundExeca) => {
 	const createNested = (mapArguments, boundOptions, setBoundExeca) => createExeca(mapArguments, boundOptions, deepOptions, setBoundExeca);
@@ -31,7 +31,7 @@ const parseArguments = ({mapArguments, firstArgument, nextArguments, deepOptions
 	const callArguments = isTemplateString(firstArgument)
 		? parseTemplates(firstArgument, nextArguments)
 		: [firstArgument, ...nextArguments];
-	const [rawFile, rawArgs, rawOptions] = normalizeArguments(...callArguments);
+	const [rawFile, rawArgs, rawOptions] = normalizeParameters(...callArguments);
 	const mergedOptions = mergeOptions(mergeOptions(deepOptions, boundOptions), rawOptions);
 	const {
 		file = rawFile,
diff --git a/lib/async.js b/lib/methods/main-async.js
similarity index 77%
rename from lib/async.js
rename to lib/methods/main-async.js
index 4264e7d..b3c825d 100644
--- a/lib/async.js
+++ b/lib/methods/main-async.js
@@ -1,22 +1,22 @@
 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 {makeError, makeSuccessResult} from './return/error.js';
-import {stripNewline} from './return/strip-newline.js';
-import {handleResult} from './return/reject.js';
-import {handleEarlyError} from './return/early-error.js';
-import {handleInputAsync} from './stdio/async.js';
-import {pipeOutputAsync} from './stdio/output-async.js';
-import {subprocessKill} from './exit/kill.js';
-import {cleanupOnExit} from './exit/cleanup.js';
-import {pipeToSubprocess} from './pipe/setup.js';
-import {SUBPROCESS_OPTIONS} from './convert/fd-options.js';
-import {logEarlyResult} from './verbose/complete.js';
-import {makeAllStream} from './stream/all.js';
-import {addConvertedStreams} from './convert/add.js';
-import {getSubprocessResult} from './stream/resolve.js';
+import {handleCommand} from '../arguments/command.js';
+import {handleOptions} 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';
+import {handleEarlyError} from '../return/early-error.js';
+import {handleStdioAsync} from '../stdio/handle-async.js';
+import {stripNewline} from '../io/strip-newline.js';
+import {pipeOutputAsync} from '../io/output-async.js';
+import {subprocessKill} from '../terminate/kill.js';
+import {cleanupOnExit} from '../terminate/cleanup.js';
+import {pipeToSubprocess} from '../pipe/setup.js';
+import {logEarlyResult} from '../verbose/complete.js';
+import {makeAllStream} from '../resolve/all-async.js';
+import {waitForSubprocessResult} from '../resolve/wait-subprocess.js';
+import {addConvertedStreams} from '../convert/add.js';
 import {mergePromise} from './promise.js';
 
 export const execaCoreAsync = (rawFile, rawArgs, rawOptions, createNested) => {
@@ -34,7 +34,7 @@ const handleAsyncArguments = (rawFile, rawArgs, rawOptions) => {
 	try {
 		const {file, args, options: normalizedOptions} = handleOptions(rawFile, rawArgs, rawOptions);
 		const options = handleAsyncOptions(normalizedOptions);
-		const fileDescriptors = handleInputAsync(options, verboseInfo);
+		const fileDescriptors = handleStdioAsync(options, verboseInfo);
 		return {file, args, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors};
 	} catch (error) {
 		logEarlyResult(error, startTime, verboseInfo);
@@ -82,7 +82,7 @@ const handlePromise = async ({subprocess, options, startTime, verboseInfo, fileD
 		[exitCode, signal],
 		stdioResults,
 		allResult,
-	] = await getSubprocessResult({subprocess, options, context, verboseInfo, fileDescriptors, originalStreams, controller});
+	] = await waitForSubprocessResult({subprocess, options, context, verboseInfo, fileDescriptors, originalStreams, controller});
 	controller.abort();
 
 	const stdio = stdioResults.map((stdioResult, fdNumber) => stripNewline(stdioResult, options, fdNumber));
diff --git a/lib/sync.js b/lib/methods/main-sync.js
similarity index 81%
rename from lib/sync.js
rename to lib/methods/main-sync.js
index 1eb75f4..0bf0ad8 100644
--- a/lib/sync.js
+++ b/lib/methods/main-sync.js
@@ -1,16 +1,16 @@
 import {spawnSync} from 'node:child_process';
-import {handleCommand} from './arguments/command.js';
-import {handleOptions} from './arguments/options.js';
-import {makeError, makeEarlyError, makeSuccessResult} from './return/error.js';
-import {stripNewline} from './return/strip-newline.js';
-import {handleResult} from './return/reject.js';
-import {handleInputSync} from './stdio/sync.js';
-import {addInputOptionsSync} from './stdio/input-sync.js';
-import {transformOutputSync} from './stdio/output-sync.js';
-import {getAllSync} from './stdio/all-sync.js';
-import {logEarlyResult} from './verbose/complete.js';
-import {getExitResultSync} from './stream/exit-sync.js';
-import {getMaxBufferSync} from './stream/max-buffer.js';
+import {handleCommand} from '../arguments/command.js';
+import {handleOptions} 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';
+import {stripNewline} from '../io/strip-newline.js';
+import {addInputOptionsSync} from '../io/input-sync.js';
+import {transformOutputSync} from '../io/output-sync.js';
+import {getMaxBufferSync} from '../io/max-buffer.js';
+import {logEarlyResult} from '../verbose/complete.js';
+import {getAllSync} from '../resolve/all-sync.js';
+import {getExitResultSync} from '../resolve/exit-sync.js';
 
 export const execaCoreSync = (rawFile, rawArgs, rawOptions) => {
 	const {file, args, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors} = handleSyncArguments(rawFile, rawArgs, rawOptions);
@@ -25,7 +25,7 @@ const handleSyncArguments = (rawFile, rawArgs, rawOptions) => {
 		const syncOptions = normalizeSyncOptions(rawOptions);
 		const {file, args, options} = handleOptions(rawFile, rawArgs, syncOptions);
 		validateSyncOptions(options);
-		const fileDescriptors = handleInputSync(options, verboseInfo);
+		const fileDescriptors = handleStdioSync(options, verboseInfo);
 		return {file, args, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors};
 	} catch (error) {
 		logEarlyResult(error, startTime, verboseInfo);
diff --git a/lib/arguments/node.js b/lib/methods/node.js
similarity index 95%
rename from lib/arguments/node.js
rename to lib/methods/node.js
index db9e9d0..f7cff0a 100644
--- a/lib/arguments/node.js
+++ b/lib/methods/node.js
@@ -1,6 +1,6 @@
 import {execPath, execArgv} from 'node:process';
 import {basename, resolve} from 'node:path';
-import {safeNormalizeFileUrl} from './file-url.js';
+import {safeNormalizeFileUrl} from '../arguments/file-url.js';
 
 export const mapNode = ({options}) => {
 	if (options.node === false) {
diff --git a/lib/arguments/normalize.js b/lib/methods/parameters.js
similarity index 86%
rename from lib/arguments/normalize.js
rename to lib/methods/parameters.js
index 332042c..edf944e 100644
--- a/lib/arguments/normalize.js
+++ b/lib/methods/parameters.js
@@ -1,7 +1,7 @@
 import isPlainObject from 'is-plain-obj';
-import {safeNormalizeFileUrl} from './file-url.js';
+import {safeNormalizeFileUrl} from '../arguments/file-url.js';
 
-export const normalizeArguments = (rawFile, rawArgs = [], rawOptions = {}) => {
+export const normalizeParameters = (rawFile, rawArgs = [], rawOptions = {}) => {
 	const filePath = safeNormalizeFileUrl(rawFile, 'First argument');
 	const [args, options] = isPlainObject(rawArgs)
 		? [[], rawArgs]
diff --git a/lib/promise.js b/lib/methods/promise.js
similarity index 100%
rename from lib/promise.js
rename to lib/methods/promise.js
diff --git a/lib/script.js b/lib/methods/script.js
similarity index 100%
rename from lib/script.js
rename to lib/methods/script.js
diff --git a/lib/arguments/template.js b/lib/methods/template.js
similarity index 98%
rename from lib/arguments/template.js
rename to lib/methods/template.js
index 3e8a7d6..1ad4401 100644
--- a/lib/arguments/template.js
+++ b/lib/methods/template.js
@@ -1,5 +1,5 @@
 import {ChildProcess} from 'node:child_process';
-import {isUint8Array, uint8ArrayToString} from '../stdio/uint-array.js';
+import {isUint8Array, uint8ArrayToString} from '../utils/uint-array.js';
 
 export const isTemplateString = templates => Array.isArray(templates) && Array.isArray(templates.raw);
 
diff --git a/lib/pipe/validate.js b/lib/pipe/pipe-arguments.js
similarity index 91%
rename from lib/pipe/validate.js
rename to lib/pipe/pipe-arguments.js
index 41a3463..200dee0 100644
--- a/lib/pipe/validate.js
+++ b/lib/pipe/pipe-arguments.js
@@ -1,6 +1,6 @@
-import {normalizeArguments} from '../arguments/normalize.js';
+import {normalizeParameters} from '../methods/parameters.js';
 import {getStartTime} from '../return/duration.js';
-import {SUBPROCESS_OPTIONS, getWritable, getReadable} from '../convert/fd-options.js';
+import {SUBPROCESS_OPTIONS, getWritable, getReadable} from '../arguments/fd-options.js';
 
 export const normalizePipeArguments = ({source, sourcePromise, boundOptions, createNested}, ...args) => {
 	const startTime = getStartTime();
@@ -51,7 +51,7 @@ const getDestination = (boundOptions, createNested, firstArgument, ...args) => {
 			throw new TypeError('Please use .pipe("file", ..., options) or .pipe(execa("file", ..., options)) instead of .pipe(options)("file", ...).');
 		}
 
-		const [rawFile, rawArgs, rawOptions] = normalizeArguments(firstArgument, ...args);
+		const [rawFile, rawArgs, rawOptions] = normalizeParameters(firstArgument, ...args);
 		const destination = createNested(mapDestinationArguments)(rawFile, rawArgs, rawOptions);
 		return {destination, pipeOptions: rawOptions};
 	}
diff --git a/lib/pipe/setup.js b/lib/pipe/setup.js
index 9d78c02..145b215 100644
--- a/lib/pipe/setup.js
+++ b/lib/pipe/setup.js
@@ -1,5 +1,5 @@
 import isPlainObject from 'is-plain-obj';
-import {normalizePipeArguments} from './validate.js';
+import {normalizePipeArguments} from './pipe-arguments.js';
 import {handlePipeArgumentsError} from './throw.js';
 import {waitForBothSubprocesses} from './sequence.js';
 import {pipeSubprocessStream} from './streaming.js';
diff --git a/lib/pipe/streaming.js b/lib/pipe/streaming.js
index 7ee7671..cae0cf2 100644
--- a/lib/pipe/streaming.js
+++ b/lib/pipe/streaming.js
@@ -1,7 +1,7 @@
 import {finished} from 'node:stream/promises';
 import mergeStreams from '@sindresorhus/merge-streams';
-import {incrementMaxListeners} from '../max-listener.js';
-import {pipeStreams} from '../stdio/pipeline.js';
+import {incrementMaxListeners} from '../utils/max-listeners.js';
+import {pipeStreams} from '../io/pipeline.js';
 
 // The piping behavior is like Bash.
 // In particular, when one subprocess exits, the other is not terminated by a signal.
diff --git a/lib/pipe/throw.js b/lib/pipe/throw.js
index 67adbc0..89ee3b4 100644
--- a/lib/pipe/throw.js
+++ b/lib/pipe/throw.js
@@ -1,5 +1,5 @@
-import {makeEarlyError} from '../return/error.js';
-import {abortSourceStream, endDestinationStream} from '../stdio/pipeline.js';
+import {makeEarlyError} from '../return/result.js';
+import {abortSourceStream, endDestinationStream} from '../io/pipeline.js';
 
 export const handlePipeArgumentsError = ({
 	sourceStream,
diff --git a/lib/stream/all.js b/lib/resolve/all-async.js
similarity index 96%
rename from lib/stream/all.js
rename to lib/resolve/all-async.js
index b5d08b6..f0a5abc 100644
--- a/lib/stream/all.js
+++ b/lib/resolve/all-async.js
@@ -1,5 +1,5 @@
 import mergeStreams from '@sindresorhus/merge-streams';
-import {waitForSubprocessStream} from './subprocess.js';
+import {waitForSubprocessStream} from './stdio.js';
 
 // `all` interleaves `stdout` and `stderr`
 export const makeAllStream = ({stdout, stderr}, {all}) => all && (stdout || stderr)
diff --git a/lib/stdio/all-sync.js b/lib/resolve/all-sync.js
similarity index 82%
rename from lib/stdio/all-sync.js
rename to lib/resolve/all-sync.js
index a69d489..065aa6d 100644
--- a/lib/stdio/all-sync.js
+++ b/lib/resolve/all-sync.js
@@ -1,5 +1,5 @@
-import {stripNewline} from '../return/strip-newline.js';
-import {isUint8Array, concatUint8Arrays} from './uint-array.js';
+import {isUint8Array, concatUint8Arrays} from '../utils/uint-array.js';
+import {stripNewline} from '../io/strip-newline.js';
 
 export const getAllSync = ([, stdout, stderr], options) => {
 	if (!options.all) {
diff --git a/lib/stream/exit-async.js b/lib/resolve/exit-async.js
similarity index 96%
rename from lib/stream/exit-async.js
rename to lib/resolve/exit-async.js
index 6672e67..cfd7189 100644
--- a/lib/stream/exit-async.js
+++ b/lib/resolve/exit-async.js
@@ -1,5 +1,5 @@
 import {once} from 'node:events';
-import {DiscardedError} from '../return/cause.js';
+import {DiscardedError} from '../return/final-error.js';
 
 // If `error` is emitted before `spawn`, `exit` will never be emitted.
 // However, `error` might be emitted after `spawn`, e.g. with the `cancelSignal` option.
diff --git a/lib/stream/exit-sync.js b/lib/resolve/exit-sync.js
similarity index 83%
rename from lib/stream/exit-sync.js
rename to lib/resolve/exit-sync.js
index 5cd7c1f..bd37703 100644
--- a/lib/stream/exit-sync.js
+++ b/lib/resolve/exit-sync.js
@@ -1,5 +1,5 @@
-import {DiscardedError} from '../return/cause.js';
-import {isMaxBufferSync} from './max-buffer.js';
+import {DiscardedError} from '../return/final-error.js';
+import {isMaxBufferSync} from '../io/max-buffer.js';
 import {isFailedExit} from './exit-async.js';
 
 export const getExitResultSync = ({error, status: exitCode, signal, output}, {maxBuffer}) => {
diff --git a/lib/stream/subprocess.js b/lib/resolve/stdio.js
similarity index 71%
rename from lib/stream/subprocess.js
rename to lib/resolve/stdio.js
index 14ad3ff..bf51bff 100644
--- a/lib/stream/subprocess.js
+++ b/lib/resolve/stdio.js
@@ -1,8 +1,8 @@
-import {waitForStream, isInputFileDescriptor} from './wait.js';
-import {getStreamOutput} from './contents.js';
+import {getStreamOutput} from '../io/contents.js';
+import {waitForStream, isInputFileDescriptor} from './wait-stream.js';
 
 // Read the contents of `subprocess.std*` and|or wait for its completion
-export const waitForSubprocessStreams = ({subprocess, encoding, buffer, maxBuffer, lines, stripFinalNewline, verboseInfo, streamInfo}) => subprocess.stdio.map((stream, fdNumber) => waitForSubprocessStream({
+export const waitForStdioStreams = ({subprocess, encoding, buffer, maxBuffer, lines, stripFinalNewline, verboseInfo, streamInfo}) => subprocess.stdio.map((stream, fdNumber) => waitForSubprocessStream({
 	stream,
 	fdNumber,
 	encoding,
diff --git a/lib/stream/wait.js b/lib/resolve/wait-stream.js
similarity index 100%
rename from lib/stream/wait.js
rename to lib/resolve/wait-stream.js
diff --git a/lib/stream/resolve.js b/lib/resolve/wait-subprocess.js
similarity index 84%
rename from lib/stream/resolve.js
rename to lib/resolve/wait-subprocess.js
index 7492908..3a12080 100644
--- a/lib/stream/resolve.js
+++ b/lib/resolve/wait-subprocess.js
@@ -1,17 +1,17 @@
 import {once} from 'node:events';
 import {isStream as isNodeStream} from 'is-stream';
-import {errorSignal} from '../exit/kill.js';
-import {throwOnTimeout} from '../exit/timeout.js';
-import {isStandardStream} from '../utils.js';
+import {errorSignal} from '../terminate/kill.js';
+import {throwOnTimeout} from '../terminate/timeout.js';
+import {isStandardStream} from '../utils/standard-stream.js';
 import {TRANSFORM_TYPES} from '../stdio/type.js';
-import {waitForAllStream} from './all.js';
-import {waitForSubprocessStreams} from './subprocess.js';
-import {getBufferedData} from './contents.js';
+import {getBufferedData} from '../io/contents.js';
+import {waitForAllStream} from './all-async.js';
+import {waitForStdioStreams} from './stdio.js';
 import {waitForExit, waitForSuccessfulExit} from './exit-async.js';
-import {waitForStream} from './wait.js';
+import {waitForStream} from './wait-stream.js';
 
 // Retrieve result of subprocess: exit code, signal, error, streams (stdout/stderr/all)
-export const getSubprocessResult = async ({
+export const waitForSubprocessResult = async ({
 	subprocess,
 	options: {encoding, buffer, maxBuffer, lines, timeoutDuration: timeout, stripFinalNewline},
 	context,
@@ -23,7 +23,7 @@ export const getSubprocessResult = async ({
 	const exitPromise = waitForExit(subprocess);
 	const streamInfo = {originalStreams, fileDescriptors, subprocess, exitPromise, propagating: false};
 
-	const stdioPromises = waitForSubprocessStreams({subprocess, encoding, buffer, maxBuffer, lines, stripFinalNewline, verboseInfo, streamInfo});
+	const stdioPromises = waitForStdioStreams({subprocess, encoding, buffer, maxBuffer, lines, stripFinalNewline, verboseInfo, streamInfo});
 	const allPromise = waitForAllStream({subprocess, encoding, buffer, maxBuffer, lines, stripFinalNewline, verboseInfo, streamInfo});
 	const originalPromises = waitForOriginalStreams(originalStreams, subprocess, streamInfo);
 	const customStreamsEndPromises = waitForCustomStreamsEnd(fileDescriptors, streamInfo);
diff --git a/lib/return/early-error.js b/lib/return/early-error.js
index 5dae786..a159f87 100644
--- a/lib/return/early-error.js
+++ b/lib/return/early-error.js
@@ -1,7 +1,7 @@
 import {ChildProcess} from 'node:child_process';
 import {PassThrough, Readable, Writable, Duplex} from 'node:stream';
-import {cleanupCustomStreams} from '../stdio/async.js';
-import {makeEarlyError} from './error.js';
+import {cleanupCustomStreams} from '../stdio/handle-async.js';
+import {makeEarlyError} from './result.js';
 import {handleResult} from './reject.js';
 
 // When the subprocess fails to spawn.
diff --git a/lib/return/cause.js b/lib/return/final-error.js
similarity index 100%
rename from lib/return/cause.js
rename to lib/return/final-error.js
diff --git a/lib/return/message.js b/lib/return/message.js
index 3d6885b..9fdba7f 100644
--- a/lib/return/message.js
+++ b/lib/return/message.js
@@ -1,9 +1,9 @@
 import stripFinalNewline from 'strip-final-newline';
-import {isUint8Array, uint8ArrayToString} from '../stdio/uint-array.js';
+import {isUint8Array, uint8ArrayToString} from '../utils/uint-array.js';
 import {fixCwdError} from '../arguments/cwd.js';
 import {escapeLines} from '../arguments/escape.js';
-import {getMaxBufferMessage} from '../stream/max-buffer.js';
-import {DiscardedError, isExecaError} from './cause.js';
+import {getMaxBufferMessage} from '../io/max-buffer.js';
+import {DiscardedError, isExecaError} from './final-error.js';
 
 export const createMessages = ({
 	stdio,
diff --git a/lib/return/error.js b/lib/return/result.js
similarity index 98%
rename from lib/return/error.js
rename to lib/return/result.js
index b0910d9..d5af10e 100644
--- a/lib/return/error.js
+++ b/lib/return/result.js
@@ -1,6 +1,6 @@
 import {signalsByName} from 'human-signals';
 import {getDurationMs} from './duration.js';
-import {getFinalError} from './cause.js';
+import {getFinalError} from './final-error.js';
 import {createMessages} from './message.js';
 
 export const makeSuccessResult = ({
diff --git a/lib/stdio/async.js b/lib/stdio/handle-async.js
similarity index 89%
rename from lib/stdio/async.js
rename to lib/stdio/handle-async.js
index eb4a155..395c7ee 100644
--- a/lib/stdio/async.js
+++ b/lib/stdio/handle-async.js
@@ -1,13 +1,13 @@
 import {createReadStream, createWriteStream} from 'node:fs';
 import {Buffer} from 'node:buffer';
 import {Readable, Writable, Duplex} from 'node:stream';
-import {isStandardStream} from '../utils.js';
-import {handleInput} from './handle.js';
+import {isStandardStream} from '../utils/standard-stream.js';
+import {generatorToStream} from '../transform/generator.js';
+import {handleStdio} from './handle.js';
 import {TYPE_TO_MESSAGE} from './type.js';
-import {generatorToStream} from './transform-run.js';
 
 // Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in async mode
-export const handleInputAsync = (options, verboseInfo) => handleInput(addPropertiesAsync, options, verboseInfo, false);
+export const handleStdioAsync = (options, verboseInfo) => handleStdio(addPropertiesAsync, options, verboseInfo, false);
 
 const forbiddenIfAsync = ({type, optionName}) => {
 	throw new TypeError(`The \`${optionName}\` option cannot be ${TYPE_TO_MESSAGE[type]}.`);
diff --git a/lib/stdio/sync.js b/lib/stdio/handle-sync.js
similarity index 87%
rename from lib/stdio/sync.js
rename to lib/stdio/handle-sync.js
index 803fdee..43fc5e9 100644
--- a/lib/stdio/sync.js
+++ b/lib/stdio/handle-sync.js
@@ -1,10 +1,10 @@
 import {readFileSync} from 'node:fs';
-import {bufferToUint8Array} from './uint-array.js';
-import {handleInput} from './handle.js';
+import {bufferToUint8Array} from '../utils/uint-array.js';
+import {handleStdio} from './handle.js';
 import {TYPE_TO_MESSAGE} from './type.js';
 
 // Normalize `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in sync mode
-export const handleInputSync = (options, verboseInfo) => handleInput(addPropertiesSync, options, verboseInfo, true);
+export const handleStdioSync = (options, verboseInfo) => handleStdio(addPropertiesSync, options, verboseInfo, true);
 
 const forbiddenIfSync = ({type, optionName}) => {
 	throwInvalidSyncValue(optionName, TYPE_TO_MESSAGE[type]);
diff --git a/lib/stdio/handle.js b/lib/stdio/handle.js
index c8d5cd0..6646e7c 100644
--- a/lib/stdio/handle.js
+++ b/lib/stdio/handle.js
@@ -1,15 +1,15 @@
-import {getStreamName} from '../utils.js';
+import {getStreamName} from '../utils/standard-stream.js';
+import {normalizeTransforms} from '../transform/normalize.js';
+import {getFdObjectMode} from '../transform/object-mode.js';
 import {getStdioItemType, isRegularUrl, isUnknownStdioString, FILE_TYPES} from './type.js';
 import {getStreamDirection} from './direction.js';
-import {normalizeStdio} from './option.js';
+import {normalizeStdioOption} from './stdio-option.js';
 import {handleNativeStream} from './native.js';
-import {handleInputOptions} from './input.js';
-import {normalizeTransforms} from './normalize-transform.js';
-import {getFdObjectMode} from './object-mode.js';
+import {handleInputOptions} from './input-option.js';
 
 // Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in async/sync mode
-export const handleInput = (addProperties, options, verboseInfo, isSync) => {
-	const stdio = normalizeStdio(options, isSync);
+export const handleStdio = (addProperties, options, verboseInfo, isSync) => {
+	const stdio = normalizeStdioOption(options, isSync);
 	const fileDescriptors = stdio.map((stdioOption, fdNumber) =>
 		getFileDescriptor({stdioOption, fdNumber, addProperties, options, isSync}));
 	options.stdio = fileDescriptors.map(({stdioItems}) => forwardStdio(stdioItems));
diff --git a/lib/stdio/input.js b/lib/stdio/input-option.js
similarity index 95%
rename from lib/stdio/input.js
rename to lib/stdio/input-option.js
index c4f9050..361538b 100644
--- a/lib/stdio/input.js
+++ b/lib/stdio/input-option.js
@@ -1,5 +1,5 @@
 import {isReadableStream} from 'is-stream';
-import {isUint8Array} from './uint-array.js';
+import {isUint8Array} from '../utils/uint-array.js';
 import {isUrl, isFilePathString} from './type.js';
 
 // Append the `stdin` option with the `input` and `inputFile` options
diff --git a/lib/stdio/native.js b/lib/stdio/native.js
index 31f1d21..fb7a0c5 100644
--- a/lib/stdio/native.js
+++ b/lib/stdio/native.js
@@ -1,9 +1,9 @@
 import {readFileSync} from 'node:fs';
 import tty from 'node:tty';
 import {isStream as isNodeStream} from 'is-stream';
-import {STANDARD_STREAMS} from '../utils.js';
-import {serializeOptionValue} from '../convert/fd-options.js';
-import {bufferToUint8Array} from './uint-array.js';
+import {STANDARD_STREAMS} from '../utils/standard-stream.js';
+import {bufferToUint8Array} from '../utils/uint-array.js';
+import {serializeOptionValue} from '../arguments/fd-options.js';
 
 // When we use multiple `stdio` values for the same streams, we pass 'pipe' to `child_process.spawn()`.
 // We then emulate the piping done by core Node.js.
diff --git a/lib/stdio/option.js b/lib/stdio/stdio-option.js
similarity index 92%
rename from lib/stdio/option.js
rename to lib/stdio/stdio-option.js
index 2a04da2..3e05989 100644
--- a/lib/stdio/option.js
+++ b/lib/stdio/stdio-option.js
@@ -1,7 +1,7 @@
-import {STANDARD_STREAMS_ALIASES} from '../utils.js';
+import {STANDARD_STREAMS_ALIASES} from '../utils/standard-stream.js';
 
 // Add support for `stdin`/`stdout`/`stderr` as an alias for `stdio`
-export const normalizeStdio = ({stdio, ipc, buffer, verbose, ...options}, isSync) => {
+export const normalizeStdioOption = ({stdio, ipc, buffer, verbose, ...options}, isSync) => {
 	const stdioArray = getStdioArray(stdio, options).map((stdioOption, fdNumber) => addDefaultValue(stdioOption, fdNumber));
 	return isSync ? normalizeStdioSync(stdioArray, buffer, verbose) : normalizeStdioAsync(stdioArray, ipc);
 };
diff --git a/lib/stdio/type.js b/lib/stdio/type.js
index d5bd573..3279a75 100644
--- a/lib/stdio/type.js
+++ b/lib/stdio/type.js
@@ -1,6 +1,6 @@
 import {isStream as isNodeStream, isDuplexStream} from 'is-stream';
 import isPlainObj from 'is-plain-obj';
-import {isUint8Array} from './uint-array.js';
+import {isUint8Array} from '../utils/uint-array.js';
 
 // The `stdin`/`stdout`/`stderr` option can be of many types. This detects it.
 export const getStdioItemType = (value, optionName) => {
diff --git a/lib/exit/cleanup.js b/lib/terminate/cleanup.js
similarity index 100%
rename from lib/exit/cleanup.js
rename to lib/terminate/cleanup.js
diff --git a/lib/exit/kill.js b/lib/terminate/kill.js
similarity index 97%
rename from lib/exit/kill.js
rename to lib/terminate/kill.js
index 5bea226..bdb8162 100644
--- a/lib/exit/kill.js
+++ b/lib/terminate/kill.js
@@ -1,6 +1,6 @@
 import os from 'node:os';
 import {setTimeout} from 'node:timers/promises';
-import {isErrorInstance} from '../return/cause.js';
+import {isErrorInstance} from '../return/final-error.js';
 
 export const normalizeForceKillAfterDelay = forceKillAfterDelay => {
 	if (forceKillAfterDelay === false) {
diff --git a/lib/exit/timeout.js b/lib/terminate/timeout.js
similarity index 92%
rename from lib/exit/timeout.js
rename to lib/terminate/timeout.js
index 3d9d1dd..f61157e 100644
--- a/lib/exit/timeout.js
+++ b/lib/terminate/timeout.js
@@ -1,5 +1,5 @@
 import {setTimeout} from 'node:timers/promises';
-import {DiscardedError} from '../return/cause.js';
+import {DiscardedError} from '../return/final-error.js';
 
 export const validateTimeout = ({timeout}) => {
 	if (timeout !== undefined && (!Number.isFinite(timeout) || timeout < 0)) {
diff --git a/lib/stdio/encoding-transform.js b/lib/transform/encoding-transform.js
similarity index 95%
rename from lib/stdio/encoding-transform.js
rename to lib/transform/encoding-transform.js
index 90ff3e1..d3f9ab0 100644
--- a/lib/stdio/encoding-transform.js
+++ b/lib/transform/encoding-transform.js
@@ -1,6 +1,6 @@
 import {Buffer} from 'node:buffer';
 import {StringDecoder} from 'node:string_decoder';
-import {isUint8Array, bufferToUint8Array} from './uint-array.js';
+import {isUint8Array, bufferToUint8Array} from '../utils/uint-array.js';
 
 /*
 When using generators, add an internal generator that converts chunks from `Buffer` to `string` or `Uint8Array`.
diff --git a/lib/stdio/transform-run.js b/lib/transform/generator.js
similarity index 96%
rename from lib/stdio/transform-run.js
rename to lib/transform/generator.js
index 6b1d9f3..6fe5216 100644
--- a/lib/stdio/transform-run.js
+++ b/lib/transform/generator.js
@@ -1,10 +1,10 @@
 import {Transform, getDefaultHighWaterMark} from 'node:stream';
-import {pushChunks, transformChunk, finalChunks, destroyTransform} from './transform-async.js';
-import {pushChunksSync, transformChunkSync, finalChunksSync, runTransformSync} from './transform-sync.js';
-import {getEncodingTransformGenerator} from './encoding-transform.js';
+import {isAsyncGenerator} from '../stdio/type.js';
 import {getSplitLinesGenerator, getAppendNewlineGenerator} from './split.js';
 import {getValidateTransformInput, getValidateTransformReturn} from './validate.js';
-import {isAsyncGenerator} from './type.js';
+import {getEncodingTransformGenerator} from './encoding-transform.js';
+import {pushChunks, transformChunk, finalChunks, destroyTransform} from './run-async.js';
+import {pushChunksSync, transformChunkSync, finalChunksSync, runTransformSync} from './run-sync.js';
 
 /*
 Generators can be used to transform/filter standard streams.
diff --git a/lib/stdio/normalize-transform.js b/lib/transform/normalize.js
similarity index 96%
rename from lib/stdio/normalize-transform.js
rename to lib/transform/normalize.js
index 8cec17a..7ad17e6 100644
--- a/lib/stdio/normalize-transform.js
+++ b/lib/transform/normalize.js
@@ -1,6 +1,6 @@
 import isPlainObj from 'is-plain-obj';
-import {BINARY_ENCODINGS} from '../arguments/encoding.js';
-import {TRANSFORM_TYPES} from './type.js';
+import {BINARY_ENCODINGS} from '../arguments/encoding-option.js';
+import {TRANSFORM_TYPES} from '../stdio/type.js';
 import {getTransformObjectModes} from './object-mode.js';
 
 // Transforms generators/duplex/TransformStream can have multiple shapes.
diff --git a/lib/stdio/object-mode.js b/lib/transform/object-mode.js
similarity index 97%
rename from lib/stdio/object-mode.js
rename to lib/transform/object-mode.js
index 86f621c..d03f976 100644
--- a/lib/stdio/object-mode.js
+++ b/lib/transform/object-mode.js
@@ -1,4 +1,4 @@
-import {TRANSFORM_TYPES} from './type.js';
+import {TRANSFORM_TYPES} from '../stdio/type.js';
 
 /*
 Retrieve the `objectMode`s of a single transform.
diff --git a/lib/stdio/transform-async.js b/lib/transform/run-async.js
similarity index 100%
rename from lib/stdio/transform-async.js
rename to lib/transform/run-async.js
diff --git a/lib/stdio/transform-sync.js b/lib/transform/run-sync.js
similarity index 100%
rename from lib/stdio/transform-sync.js
rename to lib/transform/run-sync.js
diff --git a/lib/stdio/split.js b/lib/transform/split.js
similarity index 100%
rename from lib/stdio/split.js
rename to lib/transform/split.js
diff --git a/lib/stdio/validate.js b/lib/transform/validate.js
similarity index 96%
rename from lib/stdio/validate.js
rename to lib/transform/validate.js
index 1f789a6..77b58bb 100644
--- a/lib/stdio/validate.js
+++ b/lib/transform/validate.js
@@ -1,5 +1,5 @@
 import {Buffer} from 'node:buffer';
-import {isUint8Array} from './uint-array.js';
+import {isUint8Array} from '../utils/uint-array.js';
 
 export const getValidateTransformInput = (writableObjectMode, optionName) => writableObjectMode
 	? undefined
diff --git a/lib/max-listener.js b/lib/utils/max-listeners.js
similarity index 100%
rename from lib/max-listener.js
rename to lib/utils/max-listeners.js
diff --git a/lib/utils.js b/lib/utils/standard-stream.js
similarity index 100%
rename from lib/utils.js
rename to lib/utils/standard-stream.js
diff --git a/lib/stdio/uint-array.js b/lib/utils/uint-array.js
similarity index 100%
rename from lib/stdio/uint-array.js
rename to lib/utils/uint-array.js
diff --git a/lib/verbose/output.js b/lib/verbose/output.js
index d9567f1..c447b4a 100644
--- a/lib/verbose/output.js
+++ b/lib/verbose/output.js
@@ -1,6 +1,6 @@
 import {inspect} from 'node:util';
 import {escapeLines} from '../arguments/escape.js';
-import {BINARY_ENCODINGS} from '../arguments/encoding.js';
+import {BINARY_ENCODINGS} from '../arguments/encoding-option.js';
 import {TRANSFORM_TYPES} from '../stdio/type.js';
 import {verboseLog} from './log.js';
 


Test patch:
diff --git a/test/arguments/encoding.js b/test/arguments/encoding-option.js
similarity index 100%
rename from test/arguments/encoding.js
rename to test/arguments/encoding-option.js
diff --git a/test/convert/fd-options.js b/test/arguments/fd-options.js
similarity index 100%
rename from test/convert/fd-options.js
rename to test/arguments/fd-options.js
diff --git a/test/stdio/input.js b/test/io/input-option.js
similarity index 100%
rename from test/stdio/input.js
rename to test/io/input-option.js
diff --git a/test/stdio/input-sync.js b/test/io/input-sync.js
similarity index 100%
rename from test/stdio/input-sync.js
rename to test/io/input-sync.js
diff --git a/test/convert/loop.js b/test/io/iterate.js
similarity index 100%
rename from test/convert/loop.js
rename to test/io/iterate.js
diff --git a/test/stream/max-buffer.js b/test/io/max-buffer.js
similarity index 100%
rename from test/stream/max-buffer.js
rename to test/io/max-buffer.js
diff --git a/test/stdio/output-async.js b/test/io/output-async.js
similarity index 100%
rename from test/stdio/output-async.js
rename to test/io/output-async.js
diff --git a/test/stdio/output-sync.js b/test/io/output-sync.js
similarity index 100%
rename from test/stdio/output-sync.js
rename to test/io/output-sync.js
diff --git a/test/stdio/pipeline.js b/test/io/pipeline.js
similarity index 100%
rename from test/stdio/pipeline.js
rename to test/io/pipeline.js
diff --git a/test/return/strip-newline.js b/test/io/strip-newline.js
similarity index 100%
rename from test/return/strip-newline.js
rename to test/io/strip-newline.js
diff --git a/test/command.js b/test/methods/command.js
similarity index 95%
rename from test/command.js
rename to test/methods/command.js
index 3f42342..e21d5c8 100644
--- a/test/command.js
+++ b/test/methods/command.js
@@ -1,8 +1,8 @@
 import {join} from 'node:path';
 import test from 'ava';
-import {execaCommand, execaCommandSync} from '../index.js';
-import {setFixtureDir, FIXTURES_DIR} from './helpers/fixtures-dir.js';
-import {QUOTE} from './helpers/verbose.js';
+import {execaCommand, execaCommandSync} from '../../index.js';
+import {setFixtureDir, FIXTURES_DIR} from '../helpers/fixtures-dir.js';
+import {QUOTE} from '../helpers/verbose.js';
 
 setFixtureDir();
 const STDIN_FIXTURE = join(FIXTURES_DIR, 'stdin.js');
diff --git a/test/arguments/create-bind.js b/test/methods/create-bind.js
similarity index 100%
rename from test/arguments/create-bind.js
rename to test/methods/create-bind.js
diff --git a/test/arguments/create-main.js b/test/methods/create-main.js
similarity index 100%
rename from test/arguments/create-main.js
rename to test/methods/create-main.js
diff --git a/test/async.js b/test/methods/main-async.js
similarity index 85%
rename from test/async.js
rename to test/methods/main-async.js
index 7e4bdb6..ac8c945 100644
--- a/test/async.js
+++ b/test/methods/main-async.js
@@ -1,7 +1,7 @@
 import process from 'node:process';
 import test from 'ava';
-import {execa} from '../index.js';
-import {setFixtureDir} from './helpers/fixtures-dir.js';
+import {execa} from '../../index.js';
+import {setFixtureDir} from '../helpers/fixtures-dir.js';
 
 setFixtureDir();
 
diff --git a/test/arguments/node.js b/test/methods/node.js
similarity index 100%
rename from test/arguments/node.js
rename to test/methods/node.js
diff --git a/test/override-promise.js b/test/methods/override-promise.js
similarity index 59%
rename from test/override-promise.js
rename to test/methods/override-promise.js
index 076b54b..6340bc0 100644
--- a/test/override-promise.js
+++ b/test/methods/override-promise.js
@@ -1,9 +1,8 @@
 import test from 'ava';
 // The helper module overrides Promise on import so has to be imported before `execa`.
-import {restorePromise} from './helpers/override-promise.js';
-// eslint-disable-next-line import/order
-import {execa} from '../index.js';
-import {setFixtureDir} from './helpers/fixtures-dir.js';
+import {restorePromise} from '../helpers/override-promise.js';
+import {execa} from '../../index.js';
+import {setFixtureDir} from '../helpers/fixtures-dir.js';
 
 restorePromise();
 setFixtureDir();
diff --git a/test/arguments/normalize-args.js b/test/methods/parameters-args.js
similarity index 100%
rename from test/arguments/normalize-args.js
rename to test/methods/parameters-args.js
diff --git a/test/arguments/normalize-command.js b/test/methods/parameters-command.js
similarity index 100%
rename from test/arguments/normalize-command.js
rename to test/methods/parameters-command.js
diff --git a/test/arguments/normalize-options.js b/test/methods/parameters-options.js
similarity index 100%
rename from test/arguments/normalize-options.js
rename to test/methods/parameters-options.js
diff --git a/test/promise.js b/test/methods/promise.js
similarity index 94%
rename from test/promise.js
rename to test/methods/promise.js
index b24c2b2..cba0f23 100644
--- a/test/promise.js
+++ b/test/methods/promise.js
@@ -1,6 +1,6 @@
 import test from 'ava';
-import {execa} from '../index.js';
-import {setFixtureDir} from './helpers/fixtures-dir.js';
+import {execa} from '../../index.js';
+import {setFixtureDir} from '../helpers/fixtures-dir.js';
 
 setFixtureDir();
 
diff --git a/test/script.js b/test/methods/script.js
similarity index 93%
rename from test/script.js
rename to test/methods/script.js
index dac1d41..b323767 100644
--- a/test/script.js
+++ b/test/methods/script.js
@@ -1,8 +1,8 @@
 import test from 'ava';
 import {isStream} from 'is-stream';
-import {$} from '../index.js';
-import {setFixtureDir} from './helpers/fixtures-dir.js';
-import {foobarString} from './helpers/input.js';
+import {$} from '../../index.js';
+import {setFixtureDir} from '../helpers/fixtures-dir.js';
+import {foobarString} from '../helpers/input.js';
 
 setFixtureDir();
 
diff --git a/test/arguments/template.js b/test/methods/template.js
similarity index 100%
rename from test/arguments/template.js
rename to test/methods/template.js
diff --git a/test/pipe/validate.js b/test/pipe/pipe-arguments.js
similarity index 100%
rename from test/pipe/validate.js
rename to test/pipe/pipe-arguments.js
diff --git a/test/stream/all.js b/test/resolve/all.js
similarity index 100%
rename from test/stream/all.js
rename to test/resolve/all.js
diff --git a/test/stream/buffer-end.js b/test/resolve/buffer-end.js
similarity index 100%
rename from test/stream/buffer-end.js
rename to test/resolve/buffer-end.js
diff --git a/test/stream/exit.js b/test/resolve/exit.js
similarity index 100%
rename from test/stream/exit.js
rename to test/resolve/exit.js
diff --git a/test/stream/no-buffer.js b/test/resolve/no-buffer.js
similarity index 100%
rename from test/stream/no-buffer.js
rename to test/resolve/no-buffer.js
diff --git a/test/stream/subprocess.js b/test/resolve/stdio.js
similarity index 100%
rename from test/stream/subprocess.js
rename to test/resolve/stdio.js
diff --git a/test/stream/wait-abort.js b/test/resolve/wait-abort.js
similarity index 100%
rename from test/stream/wait-abort.js
rename to test/resolve/wait-abort.js
diff --git a/test/stream/wait-epipe.js b/test/resolve/wait-epipe.js
similarity index 100%
rename from test/stream/wait-epipe.js
rename to test/resolve/wait-epipe.js
diff --git a/test/stream/wait-error.js b/test/resolve/wait-error.js
similarity index 100%
rename from test/stream/wait-error.js
rename to test/resolve/wait-error.js
diff --git a/test/stream/resolve.js b/test/resolve/wait-subprocess.js
similarity index 100%
rename from test/stream/resolve.js
rename to test/resolve/wait-subprocess.js
diff --git a/test/return/cause.js b/test/return/final-error.js
similarity index 100%
rename from test/return/cause.js
rename to test/return/final-error.js
diff --git a/test/return/error.js b/test/return/result.js
similarity index 100%
rename from test/return/error.js
rename to test/return/result.js
diff --git a/test/stdio/option.js b/test/stdio/stdio-option.js
similarity index 94%
rename from test/stdio/option.js
rename to test/stdio/stdio-option.js
index 22b6e1d..8dc97b4 100644
--- a/test/stdio/option.js
+++ b/test/stdio/stdio-option.js
@@ -1,11 +1,11 @@
 import {inspect} from 'node:util';
 import test from 'ava';
-import {normalizeStdio} from '../../lib/stdio/option.js';
+import {normalizeStdioOption} from '../../lib/stdio/stdio-option.js';
 
 const macro = (t, input, expected, func) => {
 	if (expected instanceof Error) {
 		t.throws(() => {
-			normalizeStdio(input);
+			normalizeStdioOption(input);
 		}, {message: expected.message});
 		return;
 	}
@@ -15,7 +15,7 @@ const macro = (t, input, expected, func) => {
 
 const macroTitle = name => (title, input) => `${name} ${(inspect(input))}`;
 
-const stdioMacro = (...args) => macro(...args, normalizeStdio);
+const stdioMacro = (...args) => macro(...args, normalizeStdioOption);
 stdioMacro.title = macroTitle('execa()');
 
 test(stdioMacro, {stdio: 'inherit'}, ['inherit', 'inherit', 'inherit']);