Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"toolPackages": [
{
"packageName": "@microsoft/api-extractor",
"packageVersion": "7.53.0"
"packageVersion": "7.53.1"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/node-core-library",
"comment": "Update the return type of `Executable.waitForExitAsync` to omit `stdout` and `stderr` if an `encoding` parameter isn't passed to the options object.",
"type": "patch"
}
],
"packageName": "@rushstack/node-core-library"
}
12 changes: 8 additions & 4 deletions common/reviews/api/node-core-library.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class Executable {
static tryResolve(filename: string, options?: IExecutableResolveOptions): string | undefined;
static waitForExitAsync(childProcess: child_process.ChildProcess, options: IWaitForExitWithStringOptions): Promise<IWaitForExitResult<string>>;
static waitForExitAsync(childProcess: child_process.ChildProcess, options: IWaitForExitWithBufferOptions): Promise<IWaitForExitResult<Buffer>>;
static waitForExitAsync(childProcess: child_process.ChildProcess, options?: IWaitForExitOptions): Promise<IWaitForExitResult<never>>;
static waitForExitAsync(childProcess: child_process.ChildProcess, options?: IWaitForExitOptions): Promise<IWaitForExitResultWithoutOutput>;
}

// @public
Expand Down Expand Up @@ -667,13 +667,17 @@ export interface IWaitForExitOptions {
}

// @public
export interface IWaitForExitResult<T extends Buffer | string | never = never> {
exitCode: number | null;
signal: string | null;
export interface IWaitForExitResult<T extends Buffer | string = never> extends IWaitForExitResultWithoutOutput {
stderr: T;
stdout: T;
}

// @public
export interface IWaitForExitResultWithoutOutput {
exitCode: number | null;
signal: string | null;
}

// @public
export interface IWaitForExitWithBufferOptions extends IWaitForExitOptions {
encoding: 'buffer';
Expand Down
79 changes: 49 additions & 30 deletions libraries/node-core-library/src/Executable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,21 +160,12 @@ export interface IWaitForExitWithBufferOptions extends IWaitForExitOptions {
}

/**
* The result of running a process to completion using {@link Executable.(waitForExitAsync:3)}.
* The result of running a process to completion using {@link Executable.(waitForExitAsync:3)}. This
* interface does not include stdout or stderr output because an {@link IWaitForExitOptions.encoding} was not specified.
*
* @public
*/
export interface IWaitForExitResult<T extends Buffer | string | never = never> {
/**
* The process stdout output, if encoding was specified.
*/
stdout: T;

/**
* The process stderr output, if encoding was specified.
*/
stderr: T;

export interface IWaitForExitResultWithoutOutput {
/**
* The process exit code. If the process was terminated, this will be null.
*/
Expand All @@ -188,6 +179,25 @@ export interface IWaitForExitResult<T extends Buffer | string | never = never> {
signal: string | null;
}

/**
* The result of running a process to completion using {@link Executable.(waitForExitAsync:1)},
* or {@link Executable.(waitForExitAsync:2)}.
*
* @public
*/
export interface IWaitForExitResult<T extends Buffer | string = never>
extends IWaitForExitResultWithoutOutput {
/**
* The process stdout output, if encoding was specified.
*/
stdout: T;

/**
* The process stderr output, if encoding was specified.
*/
stderr: T;
}

// Common environmental state used by Executable members
interface IExecutableContext {
currentWorkingDirectory: string;
Expand Down Expand Up @@ -554,12 +564,12 @@ export class Executable {
public static async waitForExitAsync(
childProcess: child_process.ChildProcess,
options?: IWaitForExitOptions
): Promise<IWaitForExitResult<never>>;
): Promise<IWaitForExitResultWithoutOutput>;

public static async waitForExitAsync<T extends Buffer | string | never = never>(
public static async waitForExitAsync<T extends Buffer | string>(
childProcess: child_process.ChildProcess,
options: IWaitForExitOptions = {}
): Promise<IWaitForExitResult<T>> {
): Promise<IWaitForExitResult<T> | IWaitForExitResultWithoutOutput> {
const { throwOnNonZeroExitCode, throwOnSignal, encoding } = options;
if (encoding && (!childProcess.stdout || !childProcess.stderr)) {
throw new Error(
Expand Down Expand Up @@ -611,22 +621,31 @@ export class Executable {
}
);

let stdout: T | undefined;
let stderr: T | undefined;
if (encoding === 'buffer') {
stdout = Buffer.concat(collectedStdout as Buffer[]) as T;
stderr = Buffer.concat(collectedStderr as Buffer[]) as T;
} else if (encoding !== undefined) {
stdout = collectedStdout.join('') as T;
stderr = collectedStderr.join('') as T;
}
let result: IWaitForExitResult<T> | IWaitForExitResultWithoutOutput;
if (encoding) {
let stdout: T | undefined;
let stderr: T | undefined;

if (encoding === 'buffer') {
stdout = Buffer.concat(collectedStdout as Buffer[]) as T;
stderr = Buffer.concat(collectedStderr as Buffer[]) as T;
} else if (encoding !== undefined) {
stdout = collectedStdout.join('') as T;
stderr = collectedStderr.join('') as T;
}

const result: IWaitForExitResult<T> = {
stdout: stdout as T,
stderr: stderr as T,
exitCode,
signal
};
result = {
stdout: stdout as T,
stderr: stderr as T,
exitCode,
signal
};
} else {
result = {
exitCode,
signal
};
}

return result;
}
Expand Down
1 change: 1 addition & 0 deletions libraries/node-core-library/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export {
type IWaitForExitWithBufferOptions,
type IWaitForExitWithStringOptions,
type IWaitForExitResult,
type IWaitForExitResultWithoutOutput,
type IProcessInfo,
Executable
} from './Executable';
Expand Down
9 changes: 5 additions & 4 deletions libraries/node-core-library/src/test/Executable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
parseProcessListOutputAsync,
type IProcessInfo,
type IExecutableSpawnSyncOptions,
type IWaitForExitResult
type IWaitForExitResult,
type IWaitForExitResultWithoutOutput
} from '../Executable';
import { FileSystem } from '../FileSystem';
import { PosixModeBits } from '../PosixModeBits';
Expand Down Expand Up @@ -236,11 +237,11 @@ describe('Executable process tests', () => {
environment,
currentWorkingDirectory: executableFolder
});
const result: IWaitForExitResult = await Executable.waitForExitAsync(childProcess);
const result: IWaitForExitResultWithoutOutput = await Executable.waitForExitAsync(childProcess);
expect(result.exitCode).toEqual(0);
expect(result.signal).toBeNull();
expect(result.stderr).toBeUndefined();
expect(result.stderr).toBeUndefined();
expect('stdout' in result).toBe(false);
expect('stderr' in result).toBe(false);
});

test('Executable.runToCompletion(Executable.spawn("npm-binary-wrapper")) with buffer output', async () => {
Expand Down
23 changes: 18 additions & 5 deletions libraries/rush-lib/src/utilities/Utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
SubprocessTerminator,
Executable,
type IWaitForExitResult,
Async
Async,
type IWaitForExitResultWithoutOutput
} from '@rushstack/node-core-library';

import type { RushConfiguration } from '../api/RushConfiguration';
Expand Down Expand Up @@ -161,6 +162,11 @@ export type OptionalToUndefined<T> = Omit<T, OptionalKeys<T>> & {
[K in OptionalKeys<T>]-?: Exclude<T[K], undefined> | undefined;
};

type IExecuteCommandInternalOptions = Omit<IExecuteCommandOptions, 'suppressOutput'> & {
stdio: child_process.SpawnSyncOptions['stdio'];
captureOutput: boolean;
};

export class Utilities {
public static syncNpmrc: typeof syncNpmrc = syncNpmrc;

Expand Down Expand Up @@ -781,6 +787,16 @@ export class Utilities {
* Executes the command with the specified command-line parameters, and waits for it to complete.
* The current directory will be set to the specified workingDirectory.
*/
private static async _executeCommandInternalAsync(
options: IExecuteCommandInternalOptions & { captureOutput: true }
): Promise<IWaitForExitResult<string>>;
/**
* Executes the command with the specified command-line parameters, and waits for it to complete.
* The current directory will be set to the specified workingDirectory. This does not capture output.
*/
private static async _executeCommandInternalAsync(
options: IExecuteCommandInternalOptions & { captureOutput: false | undefined }
): Promise<IWaitForExitResultWithoutOutput>;
private static async _executeCommandInternalAsync({
command,
args,
Expand All @@ -791,10 +807,7 @@ export class Utilities {
onStdoutStreamChunk,
captureOutput,
captureExitCodeAndSignal
}: Omit<IExecuteCommandOptions, 'suppressOutput'> & {
stdio: child_process.SpawnSyncOptions['stdio'];
captureOutput: boolean;
}): Promise<IWaitForExitResult> {
}: IExecuteCommandInternalOptions): Promise<IWaitForExitResult<string> | IWaitForExitResultWithoutOutput> {
const options: child_process.SpawnSyncOptions = {
cwd: workingDirectory,
shell: true,
Expand Down
Loading