Skip to content

Commit

Permalink
feat: add type parameter to execCmd (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
peternhale committed Feb 26, 2021
1 parent 1684979 commit 14639f7
Showing 1 changed file with 25 additions and 17 deletions.
42 changes: 25 additions & 17 deletions src/execCmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { join as pathJoin, resolve as pathResolve } from 'path';
import { inspect } from 'util';
import { fs } from '@salesforce/core';
import { Duration, env, parseJson } from '@salesforce/kit';
import { AnyJson, isNumber } from '@salesforce/ts-types';
import { AnyJson, Dictionary, isNumber } from '@salesforce/ts-types';
import Debug from 'debug';
import * as shelljs from 'shelljs';
import { ExecCallback, ExecOptions, ShellString } from 'shelljs';
Expand All @@ -23,7 +23,12 @@ export interface ExecCmdOptions extends ExecOptions {
ensureExitCode?: number;
}

export interface ExecCmdResult {
type JsonOutput<T> = Dictionary<AnyJson> & {
status: number;
result: T;
};

export interface ExecCmdResult<T = AnyJson> {
/**
* Command output from the shell.
*
Expand All @@ -34,7 +39,7 @@ export interface ExecCmdResult {
/**
* Command output parsed as JSON, if `--json` param present.
*/
jsonOutput?: AnyJson;
jsonOutput?: JsonOutput<T>;

/**
* The JsonParseError if parsing failed.
Expand Down Expand Up @@ -62,10 +67,10 @@ const hrtimeToMillisDuration = (hrTime: [number, number]) =>
Duration.milliseconds(hrTime[0] * Duration.MILLIS_IN_SECONDS + hrTime[1] / 1e6);

// Add JSON output if json flag is set
const addJsonOutput = (cmd: string, result: ExecCmdResult): ExecCmdResult => {
const addJsonOutput = <T>(cmd: string, result: ExecCmdResult<T>): ExecCmdResult<T> => {
if (cmd.includes('--json')) {
try {
result.jsonOutput = parseJson(stripAnsi(result.shellOutput.stdout));
result.jsonOutput = parseJson(stripAnsi(result.shellOutput.stdout)) as JsonOutput<T>;
} catch (parseErr: unknown) {
result.jsonError = parseErr as Error;
}
Expand Down Expand Up @@ -108,7 +113,7 @@ const buildCmd = (cmdArgs: string): string => {
return `${bin} ${cmdArgs}`;
};

const execCmdSync = (cmd: string, options?: ExecCmdOptions): ExecCmdResult => {
const execCmdSync = <T>(cmd: string, options?: ExecCmdOptions): ExecCmdResult<T> => {
const debug = Debug('testkit:execCmd');

// Add on the bin path
Expand All @@ -118,7 +123,7 @@ const execCmdSync = (cmd: string, options?: ExecCmdOptions): ExecCmdResult => {
debug(`Running cmd: ${cmd}`);
debug(`Cmd options: ${inspect(cmdOptions)}`);

const result: ExecCmdResult = {
const result: ExecCmdResult<T> = {
shellOutput: '' as ShellString,
execCmdDuration: Duration.seconds(0),
};
Expand All @@ -133,16 +138,16 @@ const execCmdSync = (cmd: string, options?: ExecCmdOptions): ExecCmdResult => {
throw getExitCodeError(cmd, cmdOptions.ensureExitCode, result.shellOutput);
}

return addJsonOutput(cmd, result);
return addJsonOutput<T>(cmd, result);
};

const execCmdAsync = async (cmd: string, options: ExecCmdOptions): Promise<ExecCmdResult> => {
const execCmdAsync = async <T>(cmd: string, options: ExecCmdOptions): Promise<ExecCmdResult<T>> => {
const debug = Debug('testkit:execCmdAsync');

// Add on the bin path
cmd = buildCmd(cmd);

const resultPromise = new Promise<ExecCmdResult>((resolve, reject) => {
const resultPromise = new Promise<ExecCmdResult<T>>((resolve, reject) => {
const cmdOptions = buildCmdOptions(options);

debug(`Running cmd: ${cmd}`);
Expand All @@ -159,15 +164,15 @@ const execCmdAsync = async (cmd: string, options: ExecCmdOptions): Promise<ExecC
reject(getExitCodeError(cmd, cmdOptions.ensureExitCode, output));
}

const result: ExecCmdResult = {
const result: ExecCmdResult<T> = {
shellOutput: new ShellString(stdout),
execCmdDuration,
};
result.shellOutput.code = code;
result.shellOutput.stdout = stripAnsi(stdout);
result.shellOutput.stderr = stripAnsi(stderr);

resolve(addJsonOutput(cmd, result));
resolve(addJsonOutput<T>(cmd, result));
};

// Execute the command async in a child process
Expand Down Expand Up @@ -196,7 +201,7 @@ const execCmdAsync = async (cmd: string, options: ExecCmdOptions): Promise<ExecC
* @param options The options used to run the command.
* @returns The child process exit code, stdout, stderr, cmd run time, and the parsed JSON if `--json` param present.
*/
export function execCmd(cmd: string, options?: ExecCmdOptions & { async?: false }): ExecCmdResult;
export function execCmd<T = AnyJson>(cmd: string, options?: ExecCmdOptions & { async?: false }): ExecCmdResult<T>;

/**
* Asynchronously execute a command with the provided options in a child process.
Expand All @@ -216,12 +221,15 @@ export function execCmd(cmd: string, options?: ExecCmdOptions & { async?: false
* @param options The options used to run the command.
* @returns The child process exit code, stdout, stderr, cmd run time, and the parsed JSON if `--json` param present.
*/
export function execCmd(cmd: string, options: ExecCmdOptions & { async: true }): Promise<ExecCmdResult>;
export function execCmd<T = AnyJson>(cmd: string, options: ExecCmdOptions & { async: true }): Promise<ExecCmdResult<T>>;

export function execCmd(cmd: string, options?: ExecCmdOptions): ExecCmdResult | Promise<ExecCmdResult> {
export function execCmd<T = AnyJson>(
cmd: string,
options?: ExecCmdOptions
): ExecCmdResult<T> | Promise<ExecCmdResult<T>> {
if (options?.async) {
return execCmdAsync(cmd, options);
return execCmdAsync<T>(cmd, options);
} else {
return execCmdSync(cmd, options);
return execCmdSync<T>(cmd, options);
}
}

0 comments on commit 14639f7

Please sign in to comment.