Skip to content

Commit

Permalink
Merge pull request #1614 from salesforcecli/mdonnalley/core-v4
Browse files Browse the repository at this point in the history
feat: use oclif/core v4
  • Loading branch information
mshanemc committed Jun 5, 2024
2 parents 3aaf41e + 1351026 commit 9ab0d4d
Show file tree
Hide file tree
Showing 16 changed files with 216 additions and 220 deletions.
8 changes: 5 additions & 3 deletions bin/dev.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env ts-node

async function main() {
const oclif = await import('@oclif/core');
oclif.settings.performanceEnabled = true;
await oclif.execute({ development: true, dir: import.meta.url });
const { settings } = await import('@oclif/core/settings');
const { execute } = await import('@oclif/core/execute');

settings.performanceEnabled = true;
await execute({ development: true, dir: import.meta.url });
}

await main();
33 changes: 8 additions & 25 deletions bin/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,13 @@
// Pre-process/prune flags before creating or running the actual CLI
(await import('../dist/flags.js')).preprocessCliFlags(process);

const oclif = await import('@oclif/core');
const { createRequire } = await import('module');
const pjson = createRequire(import.meta.url)('../package.json');
// Since the CLI is a single process, we can have a larger amount of max listeners since
// the process gets shut down. Don't set it to 0 (no limit) since we should still be aware
// of rogue event listeners
process.setMaxListeners(parseInt(process.env.SF_MAX_EVENT_LISTENERS, 10) || 1000);

const cli = await import('../dist/cli.js');

async function main() {
// Since the CLI is a single process, we can have a larger amount of max listeners since
// the process gets shut down. Don't set it to 0 (no limit) since we should still be aware
// of rouge event listeners
process.setMaxListeners(parseInt(process.env.SF_MAX_EVENT_LISTENERS, 10) || 1000);

// Don't let other plugins override the CLI specified max listener count
process.setMaxListeners = () => {};
// Don't let other plugins override the CLI specified max listener count
process.setMaxListeners = () => {};

cli
.create({ version: pjson.version, bin: pjson.oclif.bin, channel: 'stable' })
.run()
.then(async () => {
await oclif.flush();
})
.catch(async (err) => {
await oclif.handle(err);
});
}

await main();
const cli = await import('../dist/cli.js');
await cli.run();
10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
},
"dependencies": {
"@inquirer/select": "^1.3.1",
"@oclif/core": "3.26.9",
"@oclif/core": "4.0.0",
"@oclif/plugin-autocomplete": "3.1.2",
"@oclif/plugin-commands": "4.0.2",
"@oclif/plugin-help": "6.1.0",
Expand Down Expand Up @@ -169,12 +169,11 @@
"@salesforce/plugin-templates": "56.2.9",
"@salesforce/plugin-trust": "3.7.4",
"@salesforce/plugin-user": "3.5.11",
"@salesforce/sf-plugins-core": "9.1.1",
"chalk": "^5.3.0",
"debug": "^4.3.4",
"strip-ansi": "^7.1.0"
"@salesforce/sf-plugins-core": "10.0.0",
"ansis": "^3.2.0"
},
"pinnedDependencies": [
"@oclif/core",
"@oclif/plugin-autocomplete",
"@oclif/plugin-commands",
"@oclif/plugin-help",
Expand Down Expand Up @@ -253,7 +252,6 @@
"@salesforce/plugin-release-management": "^5.4.7",
"@salesforce/ts-sinon": "^1.4.19",
"@salesforce/ts-types": "^2.0.9",
"@types/debug": "^4.1.12",
"aws-sdk": "^2.1631.0",
"oclif": "^4.12.3",
"ts-node": "^10.9.2",
Expand Down
70 changes: 22 additions & 48 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
import { platform, arch, release } from 'node:os';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Config, Interfaces, run as oclifRun, settings } from '@oclif/core';
import { set } from '@salesforce/kit';
import Debug from 'debug';
import { default as nodeEnv, Env } from './util/env.js';

const debug = Debug('sf');
import { execute } from '@oclif/core/execute';
import { Config } from '@oclif/core/config';
import Interfaces from '@oclif/core/interfaces';
import NodeEnv, { Env } from './util/env.js';
import { sfStartupLogger, logger } from './logger.js';

const envVars = [
...new Set([
Expand Down Expand Up @@ -41,14 +40,6 @@ export const UPDATE_DISABLED_DEMO =
'Manual and automatic CLI updates have been disabled in DEMO mode. ' +
'To check for a new version, unset the environment variable SF_ENV.';

export function configureUpdateSites(config: Interfaces.Config, env = nodeEnv): void {
const npmRegistry = env.getNpmRegistryOverride();
if (npmRegistry) {
// Override config value if set via envar
set(config, 'pjson.oclif.warn-if-update-available.registry', npmRegistry);
}
}

export function configureAutoUpdate(envars: Env): void {
if (envars.isDemoMode()) {
// Disable autoupdates in demo mode
Expand Down Expand Up @@ -76,11 +67,12 @@ export function configureAutoUpdate(envars: Env): void {
}
}

function debugCliInfo(version: string, channel: string, env: Env, config: Interfaces.Config): void {
function debugCliInfo(env: Env, config: Interfaces.Config): void {
function debugSection(section: string, items: Array<[string, string]>): void {
const pad = 25;
debug('%s:', section.padStart(pad));
items.forEach(([name, value]) => debug('%s: %s', name.padStart(pad), value));
const header = `### ${section} ###`;
sfStartupLogger.debug('%s', header.padStart(pad));
items.forEach(([name, value]) => sfStartupLogger.debug('%s: %s', name.padStart(pad), value));
}

debugSection('OS', [
Expand All @@ -93,8 +85,8 @@ function debugCliInfo(version: string, channel: string, env: Env, config: Interf
debugSection('NODE', [['version', process.versions.node]]);

debugSection('CLI', [
['version', version],
['channel', channel],
['version', config.version],
['channel', config.channel],
['bin', config.bin],
['data', config.dataDir],
['cache', config.cacheDir],
Expand All @@ -112,33 +104,15 @@ function debugCliInfo(version: string, channel: string, env: Env, config: Interf
);
}

type CreateOptions = {
version: string;
bin: string | undefined;
channel: string;
run?: typeof oclifRun;
env?: typeof nodeEnv;
};

export function create({ version, bin, channel, run, env }: CreateOptions): { run: () => Promise<unknown> } {
settings.performanceEnabled = true;
const root = resolve(fileURLToPath(import.meta.url), '..');
const args = process.argv.slice(2);
const environment = env ?? nodeEnv;
return {
async run(): Promise<unknown> {
const config = new Config({
name: bin,
root,
version,
channel,
});
await config.load();
configureUpdateSites(config, environment);
configureAutoUpdate(environment);
debugCliInfo(version, channel, environment, config);
// Example of how run is used in a test https://github.com/salesforcecli/cli/pull/171/files#diff-1deee0a575599b2df117c280da319f7938aaf6fdb0c04bcdbde769dbf464be69R46
return run ? run(args, config) : oclifRun(args, config);
},
};
export async function run(): Promise<unknown> {
configureAutoUpdate(NodeEnv);
const config = await Config.load({
root: resolve(fileURLToPath(import.meta.url), '..'),
logger,
enablePerf: true,
});
debugCliInfo(NodeEnv, config);
return execute({
loadOptions: config,
});
}
4 changes: 3 additions & 1 deletion src/help/sfCommandHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { Command, CommandHelp, HelpSection, HelpSectionRenderer, Interfaces } from '@oclif/core';
import { CommandHelp, HelpSection, HelpSectionRenderer } from '@oclif/core/help';
import { Command } from '@oclif/core/command';
import Interfaces from '@oclif/core/interfaces';
type SectionType = { header: string; generate: HelpSectionRenderer };

export class SfCommandHelp extends CommandHelp {
Expand Down
15 changes: 10 additions & 5 deletions src/help/sfHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { Command, CommandHelp, Help, Interfaces, toConfiguredId } from '@oclif/core';
import stripAnsi from 'strip-ansi';
import chalk from 'chalk';

import { Command } from '@oclif/core/command';
import { CommandHelp, Help } from '@oclif/core/help';
import Interfaces from '@oclif/core/interfaces';
import { toConfiguredId } from '@oclif/core/util/ids';
import { Ansis } from 'ansis';
import { SfCommandHelp } from './sfCommandHelp.js';

const ansis = new Ansis();

export default class SfHelp extends Help {
protected CommandHelpClass: typeof CommandHelp = SfCommandHelp;
protected commandHelpClass: SfCommandHelp | undefined;
Expand Down Expand Up @@ -62,9 +67,9 @@ export default class SfHelp extends Help {
protected log(...args: string[]): void {
const formatted = args.map((arg) => {
let formattedArg = arg.slice();
const matches = stripAnsi(formattedArg).match(this.commandIdRegex) ?? [];
const matches = ansis.strip(formattedArg).match(this.commandIdRegex) ?? [];
for (const match of matches) {
formattedArg = formattedArg.replaceAll(match, chalk.dim(match));
formattedArg = formattedArg.replaceAll(match, ansis.dim(match));
}

return formattedArg;
Expand Down
11 changes: 6 additions & 5 deletions src/hooks/display-release-notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { Hook, ux } from '@oclif/core';
import type { Hook } from '@oclif/core/hooks';
import ux from '@oclif/core/ux';

export const hook: Hook<'update'> = async function ({ config }) {
export const hook: Hook.Update = async function ({ config }) {
if (process.env.SF_HIDE_RELEASE_NOTES === 'true') return;

try {
return await config.runCommand('whatsnew', ['--hook']);
} catch (err) {
const error = err as Error;
ux.log('NOTE: This error can be ignored in CI and may be silenced in the future');
ux.log('- Set the SF_HIDE_RELEASE_NOTES env var to "true" to skip this script\n');
ux.log(error.message);
ux.stdout('NOTE: This error can be ignored in CI and may be silenced in the future');
ux.stdout('- Set the SF_HIDE_RELEASE_NOTES env var to "true" to skip this script\n');
ux.stdout(error.message);
}
};

Expand Down
8 changes: 6 additions & 2 deletions src/hooks/incomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import * as os from 'node:os';
import { Command, Hook, toConfiguredId, toStandardizedId, Interfaces, loadHelpClass } from '@oclif/core';
import os from 'node:os';
import { Command } from '@oclif/core/command';
import { type Hook } from '@oclif/core/hooks';
import { toConfiguredId, toStandardizedId } from '@oclif/core/util/ids';
import Interfaces from '@oclif/core/interfaces';

function buildChoices(
matches: Command.Loadable[],
Expand Down Expand Up @@ -51,6 +54,7 @@ const hook: Hook.CommandIncomplete = async function ({ config, matches, argv })
);

if (argv.includes('--help') || argv.includes('-h')) {
const { loadHelpClass } = await import('@oclif/core/help');
const Help = await loadHelpClass(config);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const help = new Help(config, config.pjson.helpOptions);
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/pluginsPreinstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { Hook, Errors } from '@oclif/core';
import { type Hook } from '@oclif/core/hooks';

const hook: Hook.PluginsPreinstall = async function (options) {
const verifySignHookResult = await this.config.runHook('plugins:preinstall:verify:signature', options);
Expand All @@ -14,7 +14,8 @@ const hook: Hook.PluginsPreinstall = async function (options) {
);

if (pluginTrustFailure !== undefined) {
await Errors.handle(pluginTrustFailure.error);
const { handle } = await import('@oclif/core/handle');
await handle(pluginTrustFailure.error);
}
};

Expand Down
4 changes: 2 additions & 2 deletions src/hooks/preparse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import type { Hook } from '@oclif/core';
import type { Hook } from '@oclif/core/hooks';

const hook: Hook<'preparse'> = async function ({ argv, options, context }) {
const hook: Hook.Preparse = async function ({ argv, options, context }) {
// Skip this hook if command does not have a --flags-dir flag or if it is not present in argv
if (!argv.includes('--flags-dir') || !options.flags?.['flags-dir']) return argv;
const flagsDir = argv[argv.indexOf('--flags-dir') + 1];
Expand Down
3 changes: 2 additions & 1 deletion src/hooks/prerun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { Hook, ux } from '@oclif/core';
import { type Hook } from '@oclif/core/hooks';

// eslint-disable-next-line @typescript-eslint/require-await
const hook: Hook.Prerun = async function ({ Command, config }) {
Expand All @@ -21,6 +21,7 @@ const hook: Hook.Prerun = async function ({ Command, config }) {
if (!specifiedVersion) return;

if (plugin.version !== specifiedVersion) {
const { ux } = await import('@oclif/core/ux');
ux.warn(
`Plugin ${plugin.name} (${plugin.version}) differs from the version specified by ${config.bin} (${specifiedVersion})`
);
Expand Down
25 changes: 25 additions & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2023, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { format } from 'node:util';
import Interfaces from '@oclif/core/interfaces';
import { Logger } from '@salesforce/core/logger';

const customLogger = (namespace: string): Interfaces.Logger => {
const sfLogger = new Logger(namespace);
return {
child: (ns: string, delimiter?: string) => customLogger(`${namespace}${delimiter ?? ':'}${ns}`),
debug: (formatter: unknown, ...args: unknown[]) => sfLogger.debug(format(formatter, ...args)),
error: (formatter: unknown, ...args: unknown[]) => sfLogger.error(format(formatter, ...args)),
info: (formatter: unknown, ...args: unknown[]) => sfLogger.info(format(formatter, ...args)),
trace: (formatter: unknown, ...args: unknown[]) => sfLogger.trace(format(formatter, ...args)),
warn: (formatter: unknown, ...args: unknown[]) => sfLogger.warn(format(formatter, ...args)),
namespace,
};
};

export const logger = customLogger('sf:oclif');
export const sfStartupLogger = customLogger('sf-startup');
8 changes: 0 additions & 8 deletions src/util/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,6 @@ export class Env extends EnvVars {
return this.getBoolean(Env.SF_INSTALLER);
}

public getNpmRegistryOverride(): string | undefined {
return this.getString(Env.SF_NPM_REGISTRY);
}

public setNpmRegistryOverride(value: string): void {
return this.setString(Env.SF_NPM_REGISTRY, value);
}

public normalizeAutoupdateDisabled(): void {
// Ensure that the legacy envar always causes the oclif counterpart to be set
// see https://github.com/oclif/plugin-update/blob/3946fb296a0a95544ab6364b36a1f7422c8aeddf/src/hooks/init.ts#L22
Expand Down
Loading

0 comments on commit 9ab0d4d

Please sign in to comment.