Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add type parameter to execCmd #34

Merged
merged 1 commit into from
Feb 26, 2021
Merged
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
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 @@ -21,7 +21,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 @@ -32,7 +37,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 @@ -60,10 +65,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(result.shellOutput.stdout);
result.jsonOutput = parseJson(result.shellOutput.stdout) as JsonOutput<T>;
} catch (parseErr: unknown) {
result.jsonError = parseErr as Error;
}
Expand Down Expand Up @@ -106,7 +111,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 @@ -116,7 +121,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 @@ -131,16 +136,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 @@ -157,15 +162,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 = stdout;
result.shellOutput.stderr = stderr;

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

// Execute the command async in a child process
Expand Down Expand Up @@ -194,7 +199,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 @@ -214,12 +219,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);
}
}