Skip to content

Commit

Permalink
feat: export Ux and add configAggregator to SfCommand (#106)
Browse files Browse the repository at this point in the history
* feat: export Ux and add configAggregator to SfCommand

* chore: code review

* chore: rename env var

* chore: bump oclif for flag aliases

* chore: code review

* chore: update jsdoc

Co-authored-by: mshanemc <shane.mclaughlin@salesforce.com>
  • Loading branch information
mdonnalley and mshanemc committed Oct 20, 2022
1 parent 3b4567a commit 9c03d8d
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 14 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"/messages"
],
"dependencies": {
"@oclif/core": "^1.16.4",
"@oclif/core": "^1.19.0",
"@salesforce/core": "^3.30.9",
"@salesforce/kit": "^1.7.0",
"@salesforce/ts-types": "^1.5.20",
Expand Down Expand Up @@ -71,4 +71,4 @@
"publishConfig": {
"access": "public"
}
}
}
2 changes: 1 addition & 1 deletion src/exported.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Flags as OclifFlags } from '@oclif/core';
export { toHelpSection, parseVarArgs } from './util';
export { Deployable, Deployer, DeployerResult } from './deployer';
export { Deauthorizer } from './deauthorizer';
export { Progress, Prompter, generateTableChoices } from './ux';
export { Progress, Prompter, generateTableChoices, Ux } from './ux';
export { SfHook } from './hooks';
export * from './types';
export { SfCommand, SfCommandInterface, StandardColors } from './sfCommand';
Expand Down
3 changes: 2 additions & 1 deletion src/flags/duration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export type DurationFlagConfig = {
*/
export const durationFlag = Flags.custom<Duration, DurationFlagConfig>({
parse: async (input, _, opts) => validate(input, opts),
default: async (context) => context.options.defaultValue ? toDuration(context.options.defaultValue, context.options.unit) : undefined,
default: async (context) =>
context.options.defaultValue ? toDuration(context.options.defaultValue, context.options.unit) : undefined,
});

const validate = (input: string, config: DurationFlagConfig): Duration => {
Expand Down
42 changes: 40 additions & 2 deletions src/sfCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import {
Mode,
EnvironmentVariable,
SfError,
ConfigAggregator,
SfdxConfigAggregator,
} from '@salesforce/core';
import { env } from '@salesforce/kit';
import { AnyJson } from '@salesforce/ts-types';
import * as chalk from 'chalk';
import { Progress, Prompter, Spinner, Ux } from './ux';
Expand Down Expand Up @@ -158,6 +161,34 @@ export abstract class SfCommand<T> extends Command {
public progress: Progress;
public project!: SfProject;

/**
* ConfigAggregator instance for accessing global and local configuration.
*
* NOTE: If the active executable is sfdx, this will be an instance of SfdxConfigAggregator, which supports
* the deprecated sfdx config vars like defaultusername, defaultdevhubusername, apiversion, etc. Otherwise,
* it will be an instance of ConfigAggregator will only supports the config vars introduce by @salesforce/core@v3.
*
* The executable is determined by `this.config.bin` which is supplied by the base oclif/core Command class. The value
* of `this.config.bin` will be the executable running (e.g. sfdx or sf) or, for local development (e.g. using bin/dev),
* it will be the value of oclif.bin in the plugin's package.json.
*
* If you need to write NUTS for a plugin that needs to work with both sets of config vars you can
* use set the `SF_USE_DEPRECATED_CONFIG_VARS` to `true` to force configAggregator to be an instance of SfdxConfigAggregator or
* `false` to force configAggregator to be an instance of ConfigAggregator.
*
* @example
* ```
* import { execCmd } from '@salesforce/cli-plugins-testkit';
* execCmd('config:set defaultusername=test@example.com', {
* env: {
* ...process.env,
* SF_USE_DEPRECATED_CONFIG_VARS: true,
* }
* })
* ```
*/
public configAggregator!: ConfigAggregator;

private warnings: SfCommand.Warning[] = [];
private ux: Ux;
private prompter: Prompter;
Expand All @@ -166,10 +197,10 @@ export abstract class SfCommand<T> extends Command {
public constructor(argv: string[], config: Config) {
super(argv, config);
const outputEnabled = !this.jsonEnabled();
this.spinner = new Spinner(outputEnabled);
this.progress = new Progress(outputEnabled && envVars.getBoolean(EnvironmentVariable.SF_USE_PROGRESS_BAR, true));
this.ux = new Ux(outputEnabled);
this.prompter = new Prompter();
this.spinner = this.ux.spinner;
this.prompter = this.ux.prompter;
this.lifecycle = Lifecycle.getInstance();
}

Expand Down Expand Up @@ -329,6 +360,13 @@ export abstract class SfCommand<T> extends Command {
}

public async _run<R>(): Promise<R | undefined> {
this.configAggregator =
this.config.bin === 'sfdx' ??
env.getBoolean('SF_USE_DEPRECATED_CONFIG_VARS') ??
env.getBoolean('SFDX_USE_DEPRECATED_CONFIG_VARS')
? await SfdxConfigAggregator.create()
: await ConfigAggregator.create();

if (this.statics.requiresProject) {
this.project = await this.assignProject();
}
Expand Down
2 changes: 1 addition & 1 deletion src/ux/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { AnyFunction } from '@salesforce/ts-types';

export class UxBase {
public constructor(protected outputEnabled: boolean) {}
public constructor(public readonly outputEnabled: boolean) {}

protected maybeNoop(fn: AnyFunction<unknown>): void {
if (this.outputEnabled) fn();
Expand Down
78 changes: 75 additions & 3 deletions src/ux/ux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,100 @@
import { CliUx } from '@oclif/core';
import { AnyJson } from '@salesforce/ts-types';
import { UxBase } from './base';
import { Prompter } from './prompter';
import { Spinner } from './spinner';

/**
* UX methods for plugins. Automatically suppress console output if outputEnabled is set to false.
*
* @example
* ```
* import { SfCommand, Ux } from '@salesforce/sf-plugins-core';
* import { AnyJson } from '@salesforce/ts-types';
*
* class MyCommand extends SfCommand<AnyJson> {
* public async run(): Promise<AnyJson> {
* const ux = new Ux(!this.jsonEnabled());
* }
* }
*
* ```
*/
export class Ux extends UxBase {
public constructor(outputEnabled: boolean) {
public spinner: Spinner;
public prompter: Prompter;

public constructor(public readonly outputEnabled: boolean) {
super(outputEnabled);
this.spinner = new Spinner(outputEnabled);
this.prompter = new Prompter();
}

/**
* Log a message to the console. This will be automatically suppressed if output is disabled.
*
* @param message Message to log. Formatting is supported.
* @param args Args to be used for formatting.
*/
public log(message?: string, ...args: string[]): void {
this.maybeNoop(() => CliUx.ux.log(message, ...args));
}

/**
* Log a warning message to the console. This will be automatically suppressed if output is disabled.
*
* @param message Warning message to log.
*/
public warn(message: string | Error): void {
this.maybeNoop(() => CliUx.ux.warn(message));
}

/**
* Display a table to the console. This will be automatically suppressed if output is disabled.
*
* @param data Data to be displayed
* @param columns Columns to display the data in
* @param options Options for how the table should be displayed
*/
public table<T extends Ux.Table.Data>(data: T[], columns: Ux.Table.Columns<T>, options?: Ux.Table.Options): void {
this.maybeNoop(() => CliUx.ux.table(data, columns, options));
}

/**
* Display a url to the console. This will be automatically suppressed if output is disabled.
*
* @param text text to display
* @param uri URL link
* @param params
*/
public url(text: string, uri: string, params = {}): void {
this.maybeNoop(() => CliUx.ux.url(text, uri, params));
}

/**
* Display stylized JSON to the console. This will be automatically suppressed if output is disabled.
*
* @param obj JSON to display
*/
public styledJSON(obj: AnyJson): void {
this.maybeNoop(() => CliUx.ux.styledJSON(obj));
}

public styledObject(obj: AnyJson): void {
this.maybeNoop(() => CliUx.ux.styledObject(obj));
/**
* Display stylized object to the console. This will be automatically suppressed if output is disabled.
*
* @param obj Object to display
* @param keys Keys of object to display
*/
public styledObject(obj: AnyJson, keys?: string[]): void {
this.maybeNoop(() => CliUx.ux.styledObject(obj, keys));
}

/**
* Display stylized header to the console. This will be automatically suppressed if output is disabled.
*
* @param text header to display
*/
public styledHeader(text: string): void {
this.maybeNoop(() => CliUx.ux.styledHeader(text));
}
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -540,10 +540,10 @@
is-wsl "^2.1.1"
tslib "^2.0.0"

"@oclif/core@^1.16.4", "@oclif/core@^1.18.0":
version "1.18.0"
resolved "https://registry.yarnpkg.com/@oclif/core/-/core-1.18.0.tgz#93c790c663c6b24a609d1ea44362f5438cb19d73"
integrity sha512-12SWbbDbMrhBmEuN9cOQkbN+sFUKlSTw+NlCPVVIg3uEhnmkXgx2wZJtCN4c+IBMJjC27L9lDneeoe70yQuiVg==
"@oclif/core@^1.18.0", "@oclif/core@^1.19.0":
version "1.19.0"
resolved "https://registry.yarnpkg.com/@oclif/core/-/core-1.19.0.tgz#b2e5260c2344e5905312efd7538daacaedb75cbf"
integrity sha512-4R087E3TAG6Q9cvr+4wnlryHbciioXkZmUF/Qmc08dReoFus1BIfwtR5kUa/thNmQBw5oeGnrxnl9yA/TaxAmA==
dependencies:
"@oclif/linewrap" "^1.0.0"
"@oclif/screen" "^3.0.2"
Expand Down

0 comments on commit 9c03d8d

Please sign in to comment.