@oclif/core 4.0.0-beta.8 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cache.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Config } from './config/config';
2
- import { Configuration, Plugin } from './interfaces';
2
+ import { OclifConfiguration, Plugin } from './interfaces';
3
3
  type OclifCoreInfo = {
4
4
  name: string;
5
5
  version: string;
@@ -7,7 +7,7 @@ type OclifCoreInfo = {
7
7
  type CacheContents = {
8
8
  rootPlugin: Plugin;
9
9
  config: Config;
10
- exitCodes: Configuration['exitCodes'];
10
+ exitCodes: OclifConfiguration['exitCodes'];
11
11
  '@oclif/core': OclifCoreInfo;
12
12
  };
13
13
  type ValueOf<T> = T[keyof T];
@@ -21,7 +21,7 @@ export default class Cache extends Map<keyof CacheContents, ValueOf<CacheContent
21
21
  get(key: 'config'): Config | undefined;
22
22
  get(key: '@oclif/core'): OclifCoreInfo;
23
23
  get(key: 'rootPlugin'): Plugin | undefined;
24
- get(key: 'exitCodes'): Configuration['exitCodes'] | undefined;
24
+ get(key: 'exitCodes'): OclifConfiguration['exitCodes'] | undefined;
25
25
  private getOclifCoreMeta;
26
26
  }
27
27
  export {};
package/lib/cache.js CHANGED
@@ -28,7 +28,7 @@ class Cache extends Map {
28
28
  try {
29
29
  return {
30
30
  name: '@oclif/core',
31
- version: JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '..', 'package.json'), 'utf8')),
31
+ version: JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '..', 'package.json'), 'utf8')).version,
32
32
  };
33
33
  }
34
34
  catch {
package/lib/command.d.ts CHANGED
@@ -109,7 +109,7 @@ export declare abstract class Command {
109
109
  log(message?: string, ...args: any[]): void;
110
110
  protected logJson(json: unknown): void;
111
111
  logToStderr(message?: string, ...args: any[]): void;
112
- protected parse<F extends FlagOutput, B extends FlagOutput, A extends ArgOutput>(options?: Input<F, B, A>, argv?: string[]): Promise<ParserOutput<F, B, A>>;
112
+ protected parse<F extends FlagOutput, B extends FlagOutput, A extends ArgOutput>(options?: Input<F, B, A>, argv?: string[]): Promise<ParserOutput<F, A>>;
113
113
  protected toErrorJson(err: unknown): any;
114
114
  protected toSuccessJson(result: unknown): any;
115
115
  warn(input: Error | string): Error | string;
package/lib/command.js CHANGED
@@ -148,6 +148,9 @@ class Command {
148
148
  opts = (0, node_url_1.fileURLToPath)(opts);
149
149
  }
150
150
  const config = await config_1.Config.load(opts || require.main?.filename || __dirname);
151
+ const cache = cache_1.default.getInstance();
152
+ if (!cache.has('config'))
153
+ cache.set('config', config);
151
154
  const cmd = new this(argv, config);
152
155
  if (!cmd.id) {
153
156
  const id = cmd.constructor.name.toLowerCase();
@@ -1,5 +1,5 @@
1
1
  import { Command } from '../command';
2
- import { Configuration, Hook, Hooks, PJSON, S3Templates, Topic, UserPJSON } from '../interfaces';
2
+ import { Hook, Hooks, OclifConfiguration, PJSON, S3Templates, Topic, UserPJSON } from '../interfaces';
3
3
  import { ArchTypes, Config as IConfig, LoadOptions, PlatformTypes, VersionDetails } from '../interfaces/config';
4
4
  import { Plugin as IPlugin, Options } from '../interfaces/plugin';
5
5
  import { Theme } from '../interfaces/theme';
@@ -27,7 +27,7 @@ export declare class Config implements IConfig {
27
27
  shell: string;
28
28
  theme?: Theme | undefined;
29
29
  topicSeparator: ' ' | ':';
30
- updateConfig: NonNullable<Configuration['update']>;
30
+ updateConfig: NonNullable<OclifConfiguration['update']>;
31
31
  userAgent: string;
32
32
  userPJSON?: UserPJSON | undefined;
33
33
  valid: boolean;
@@ -117,26 +117,6 @@ export declare class Config implements IConfig {
117
117
  protected windowsUserprofileHome(): string | undefined;
118
118
  protected _shell(): string;
119
119
  private buildS3Config;
120
- /**
121
- * This method is responsible for locating the correct plugin to use for a named command id
122
- * It searches the {Config} registered commands to match either the raw command id or the command alias
123
- * It is possible that more than one command will be found. This is due the ability of two distinct plugins to
124
- * create the same command or command alias.
125
- *
126
- * In the case of more than one found command, the function will select the command based on the order in which
127
- * the plugin is included in the package.json `oclif.plugins` list. The command that occurs first in the list
128
- * is selected as the command to run.
129
- *
130
- * Commands can also be present from either an install or a link. When a command is one of these and a core plugin
131
- * is present, this function defers to the core plugin.
132
- *
133
- * If there is not a core plugin command present, this function will return the first
134
- * plugin as discovered (will not change the order)
135
- *
136
- * @param commands commands to determine the priority of
137
- * @returns command instance {Command.Loadable} or undefined
138
- */
139
- private determinePriority;
140
120
  private getCmdLookupId;
141
121
  private getTopicLookupId;
142
122
  /**
@@ -39,6 +39,7 @@ const logger_1 = require("../logger");
39
39
  const module_loader_1 = require("../module-loader");
40
40
  const performance_1 = require("../performance");
41
41
  const settings_1 = require("../settings");
42
+ const determine_priority_1 = require("../util/determine-priority");
42
43
  const fs_1 = require("../util/fs");
43
44
  const os_1 = require("../util/os");
44
45
  const util_2 = require("../util/util");
@@ -532,7 +533,7 @@ class Config {
532
533
  options = ext;
533
534
  else if (ext)
534
535
  options.ext = ext;
535
- const template = this.updateConfig.s3.templates?.[options.platform ? 'target' : 'vanilla'][type] ?? '';
536
+ const template = this.updateConfig.s3?.templates?.[options.platform ? 'target' : 'vanilla'][type] ?? '';
536
537
  return ejs.render(template, { ...this, ...options });
537
538
  }
538
539
  s3Url(key) {
@@ -622,58 +623,6 @@ class Config {
622
623
  templates,
623
624
  };
624
625
  }
625
- /**
626
- * This method is responsible for locating the correct plugin to use for a named command id
627
- * It searches the {Config} registered commands to match either the raw command id or the command alias
628
- * It is possible that more than one command will be found. This is due the ability of two distinct plugins to
629
- * create the same command or command alias.
630
- *
631
- * In the case of more than one found command, the function will select the command based on the order in which
632
- * the plugin is included in the package.json `oclif.plugins` list. The command that occurs first in the list
633
- * is selected as the command to run.
634
- *
635
- * Commands can also be present from either an install or a link. When a command is one of these and a core plugin
636
- * is present, this function defers to the core plugin.
637
- *
638
- * If there is not a core plugin command present, this function will return the first
639
- * plugin as discovered (will not change the order)
640
- *
641
- * @param commands commands to determine the priority of
642
- * @returns command instance {Command.Loadable} or undefined
643
- */
644
- determinePriority(commands) {
645
- const oclifPlugins = this.pjson.oclif?.plugins ?? [];
646
- const commandPlugins = commands.sort((a, b) => {
647
- const pluginAliasA = a.pluginAlias ?? 'A-Cannot-Find-This';
648
- const pluginAliasB = b.pluginAlias ?? 'B-Cannot-Find-This';
649
- const aIndex = oclifPlugins.indexOf(pluginAliasA);
650
- const bIndex = oclifPlugins.indexOf(pluginAliasB);
651
- // When both plugin types are 'core' plugins sort based on index
652
- if (a.pluginType === 'core' && b.pluginType === 'core') {
653
- // If b appears first in the pjson.plugins sort it first
654
- return aIndex - bIndex;
655
- }
656
- // if b is a core plugin and a is not sort b first
657
- if (b.pluginType === 'core' && a.pluginType !== 'core') {
658
- return 1;
659
- }
660
- // if a is a core plugin and b is not sort a first
661
- if (a.pluginType === 'core' && b.pluginType !== 'core') {
662
- return -1;
663
- }
664
- // if a is a jit plugin and b is not sort b first
665
- if (a.pluginType === 'jit' && b.pluginType !== 'jit') {
666
- return 1;
667
- }
668
- // if b is a jit plugin and a is not sort a first
669
- if (b.pluginType === 'jit' && a.pluginType !== 'jit') {
670
- return -1;
671
- }
672
- // neither plugin is core, so do not change the order
673
- return 0;
674
- });
675
- return commandPlugins[0];
676
- }
677
626
  getCmdLookupId(id) {
678
627
  if (this._commands.has(id))
679
628
  return id;
@@ -701,7 +650,7 @@ class Config {
701
650
  for (const plugin of plugins) {
702
651
  this.plugins.set(plugin.name, plugin);
703
652
  // Delete all commands from the legacy plugin so that we can re-add them.
704
- // This is necessary because this.determinePriority will pick the initial
653
+ // This is necessary because determinePriority will pick the initial
705
654
  // command that was added, which won't have been converted by PluginLegacy yet.
706
655
  for (const cmd of plugin.commands ?? []) {
707
656
  this._commands.delete(cmd.id);
@@ -722,7 +671,10 @@ class Config {
722
671
  for (const command of plugin.commands) {
723
672
  // set canonical command id
724
673
  if (this._commands.has(command.id)) {
725
- const prioritizedCommand = this.determinePriority([this._commands.get(command.id), command]);
674
+ const prioritizedCommand = (0, determine_priority_1.determinePriority)(this.pjson.oclif.plugins ?? [], [
675
+ this._commands.get(command.id),
676
+ command,
677
+ ]);
726
678
  this._commands.set(prioritizedCommand.id, prioritizedCommand);
727
679
  }
728
680
  else {
@@ -739,7 +691,10 @@ class Config {
739
691
  }
740
692
  const handleAlias = (alias, hidden = false) => {
741
693
  if (this._commands.has(alias)) {
742
- const prioritizedCommand = this.determinePriority([this._commands.get(alias), command]);
694
+ const prioritizedCommand = (0, determine_priority_1.determinePriority)(this.pjson.oclif.plugins ?? [], [
695
+ this._commands.get(alias),
696
+ command,
697
+ ]);
743
698
  this._commands.set(alias, { ...prioritizedCommand, id: alias });
744
699
  }
745
700
  else {
@@ -1,34 +1,11 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  const minimatch_1 = require("minimatch");
27
4
  const node_path_1 = require("node:path");
28
5
  const performance_1 = require("../performance");
29
6
  const fs_1 = require("../util/fs");
30
7
  const util_1 = require("../util/util");
31
- const Plugin = __importStar(require("./plugin"));
8
+ const plugin_1 = require("./plugin");
32
9
  const util_2 = require("./util");
33
10
  const debug = (0, util_2.makeDebug)();
34
11
  function findMatchingDependencies(dependencies, patterns) {
@@ -62,7 +39,7 @@ class PluginLoader {
62
39
  }
63
40
  else {
64
41
  const marker = performance_1.Performance.mark(performance_1.OCLIF_MARKER_OWNER, 'plugin.load#root');
65
- rootPlugin = new Plugin.Plugin({ isRoot: true, pjson, root: this.options.root });
42
+ rootPlugin = new plugin_1.Plugin({ isRoot: true, pjson, root: this.options.root });
66
43
  await rootPlugin.load();
67
44
  marker?.addDetails({
68
45
  commandCount: rootPlugin.commands.length,
@@ -154,7 +131,7 @@ class PluginLoader {
154
131
  if (this.plugins.has(name))
155
132
  return;
156
133
  const pluginMarker = performance_1.Performance.mark(performance_1.OCLIF_MARKER_OWNER, `plugin.load#${name}`);
157
- const instance = new Plugin.Plugin(opts);
134
+ const instance = new plugin_1.Plugin(opts);
158
135
  await instance.load();
159
136
  pluginMarker?.addDetails({
160
137
  commandCount: instance.commands.length,
@@ -78,6 +78,24 @@ async function loadTSConfig(root) {
78
78
  (0, warn_1.memoizedWarn)(`Could not parse tsconfig.json for ${root}. Falling back to compiled source.`);
79
79
  }
80
80
  }
81
+ async function registerTsx(root, moduleType) {
82
+ if (REGISTERED.has(root))
83
+ return;
84
+ try {
85
+ const apiPath = moduleType === 'module' ? 'tsx/esm/api' : 'tsx/cjs/api';
86
+ const tsxPath = require.resolve(apiPath, { paths: [root] });
87
+ if (!tsxPath)
88
+ return;
89
+ debug('registering tsx at', root);
90
+ debug('tsx path:', tsxPath);
91
+ const { register } = await import(tsxPath);
92
+ register();
93
+ REGISTERED.add(root);
94
+ }
95
+ catch {
96
+ debug(`Could not find tsx. Skipping tsx registration for ${root}.`);
97
+ }
98
+ }
81
99
  async function registerTSNode(root, tsconfig) {
82
100
  if (REGISTERED.has(root))
83
101
  return;
@@ -135,18 +153,20 @@ async function registerTSNode(root, tsconfig) {
135
153
  }
136
154
  /**
137
155
  * Skip ts-node registration for ESM plugins in production.
138
- * The node ecosystem is not mature enough to support auto-transpiling ESM modules at this time.
156
+ * The node/ts-node ecosystem is not mature enough to support auto-transpiling ESM modules at this time.
139
157
  * See the following:
140
158
  * - https://github.com/TypeStrong/ts-node/issues/1791#issuecomment-1149754228
141
159
  * - https://github.com/nodejs/node/issues/49432
142
160
  * - https://github.com/nodejs/node/pull/49407
143
161
  * - https://github.com/nodejs/node/issues/34049
144
162
  *
145
- * We still register ts-node for ESM plugins when NODE_ENV is "test" or "development" and root plugin is also ESM
163
+ * We still register tsx/ts-node for ESM plugins when NODE_ENV is "test" or "development" and root plugin is also ESM
146
164
  * since that allows plugins to be auto-transpiled when developing locally using `bin/dev.js`.
147
165
  */
148
166
  function cannotTranspileEsm(rootPlugin, plugin, isProduction) {
149
- return (isProduction || rootPlugin?.moduleType === 'commonjs') && plugin?.moduleType === 'module';
167
+ return ((isProduction || rootPlugin?.moduleType === 'commonjs') &&
168
+ plugin?.moduleType === 'module' &&
169
+ !plugin?.pjson.devDependencies?.tsx);
150
170
  }
151
171
  /**
152
172
  * If the dev script is run with ts-node for an ESM plugin, skip ts-node registration
@@ -166,15 +186,18 @@ function cannotUseTsNode(root, plugin, isProduction) {
166
186
  /**
167
187
  * Determine the path to the source file from the compiled ./lib files
168
188
  */
169
- async function determinePath(root, orig) {
189
+ async function determinePath(root, orig, plugin) {
170
190
  const tsconfig = await loadTSConfig(root);
171
191
  if (!tsconfig)
172
192
  return orig;
173
193
  debug(`Determining path for ${orig}`);
174
- if (RUN_TIME === 'tsx' || RUN_TIME === 'bun') {
194
+ if (RUN_TIME === 'bun') {
175
195
  debug(`Skipping ts-node registration for ${root} because the runtime is: ${RUN_TIME}`);
176
196
  }
177
197
  else {
198
+ // attempt to register tsx first. If it fails to register, we will fall back to ts-node
199
+ await registerTsx(root, plugin?.moduleType);
200
+ // if tsx registration succeeded, then this will exit early since the path will be in REGISTERED already
178
201
  await registerTSNode(root, tsconfig);
179
202
  }
180
203
  const { baseUrl, outDir, rootDir, rootDirs } = tsconfig.compilerOptions;
@@ -251,7 +274,7 @@ async function tsPath(root, orig, plugin) {
251
274
  return orig;
252
275
  }
253
276
  try {
254
- return await determinePath(root, orig);
277
+ return await determinePath(root, orig, plugin);
255
278
  }
256
279
  catch (error) {
257
280
  debug(error);
@@ -153,12 +153,7 @@ class CommandHelp extends formatter_1.HelpFormatter {
153
153
  label = labels.join(flag.char ? (0, theme_1.colorize)(this.config?.theme?.flagSeparator, ', ') : ' ');
154
154
  }
155
155
  if (flag.type === 'option') {
156
- let value = flag.helpValue || (this.opts.showFlagNameInTitle ? flag.name : '<value>');
157
- if (!flag.helpValue && flag.options) {
158
- value = showOptions || this.opts.showFlagOptionsInTitle ? `${flag.options.join('|')}` : '<option>';
159
- }
160
- if (flag.multiple)
161
- value += '...';
156
+ let value = docopts_1.DocOpts.formatUsageType(flag, this.opts.showFlagNameInTitle ?? false, this.opts.showFlagOptionsInTitle ?? showOptions);
162
157
  if (!value.includes('|'))
163
158
  value = ansis_1.default.underline(value);
164
159
  label += `=${value}`;
@@ -304,7 +299,11 @@ class CommandHelp extends formatter_1.HelpFormatter {
304
299
  const dollarSign = (0, theme_1.colorize)(this.config?.theme?.dollarSign, '$');
305
300
  const bin = (0, theme_1.colorize)(this.config?.theme?.bin, this.config.bin);
306
301
  const command = (0, theme_1.colorize)(this.config?.theme?.command, '<%= command.id %>');
307
- const commandDescription = (0, theme_1.colorize)(this.config?.theme?.sectionDescription, u.replace('<%= command.id %>', '').replace(standardId, '').replace(configuredId, '').trim());
302
+ const commandDescription = (0, theme_1.colorize)(this.config?.theme?.sectionDescription, u
303
+ .replace('<%= command.id %>', '')
304
+ .replace(new RegExp(`^${standardId}`), '')
305
+ .replace(new RegExp(`^${configuredId}`), '')
306
+ .trim());
308
307
  const line = `${dollarSign} ${bin} ${command} ${commandDescription}`.trim();
309
308
  if (line.length > allowedSpacing) {
310
309
  const splitIndex = line.slice(0, Math.max(0, allowedSpacing)).lastIndexOf(' ');
@@ -60,6 +60,7 @@ export declare class DocOpts {
60
60
  private flagList;
61
61
  private flagMap;
62
62
  constructor(cmd: Command.Loadable);
63
+ static formatUsageType(flag: Command.Flag.Any, showFlagName: boolean, showOptions: boolean): string;
63
64
  static generate(cmd: Command.Loadable): string;
64
65
  toString(): string;
65
66
  private combineElementsToFlag;
@@ -73,6 +73,27 @@ class DocOpts {
73
73
  return flag;
74
74
  });
75
75
  }
76
+ static formatUsageType(flag, showFlagName, showOptions) {
77
+ if (flag.type !== 'option')
78
+ return '';
79
+ let helpValues;
80
+ if (flag.helpValue) {
81
+ // if there is a given helpValue, use it
82
+ helpValues = typeof flag.helpValue === 'string' ? [flag.helpValue] : flag.helpValue;
83
+ }
84
+ else if (flag.options) {
85
+ // if there are options, show them if wanted
86
+ helpValues = [showOptions ? flag.options.join('|') : '<option>'];
87
+ }
88
+ else if (showFlagName) {
89
+ helpValues = [flag.name];
90
+ }
91
+ else {
92
+ // default to <value>
93
+ helpValues = ['<value>'];
94
+ }
95
+ return helpValues.map((v) => `${v}${flag.multiple ? '...' : ''}`).join(' ');
96
+ }
76
97
  static generate(cmd) {
77
98
  return new DocOpts(cmd).toString();
78
99
  }
@@ -93,7 +114,7 @@ class DocOpts {
93
114
  const name = flag.char ? `-${flag.char}` : `--${flag.name}`;
94
115
  if (flag.type === 'boolean')
95
116
  return name;
96
- return `${name}=<value>`;
117
+ return `${name}=${DocOpts.formatUsageType(flag, false, true)}`;
97
118
  }));
98
119
  }
99
120
  return opts.join(' ');
@@ -123,7 +144,7 @@ class DocOpts {
123
144
  // not all flags have short names
124
145
  const flagName = flag.char ? `-${flag.char}` : `--${flag.name}`;
125
146
  if (flag.type === 'option') {
126
- type = flag.options ? ` ${flag.options.join('|')}` : ' <value>';
147
+ type = ` ${DocOpts.formatUsageType(flag, false, true)}`;
127
148
  }
128
149
  const element = `${flagName}${type}`;
129
150
  elementMap[flag.name] = element;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { Command } from '../command';
3
3
  import { Hook, Hooks } from './hooks';
4
- import { Configuration, PJSON, S3Templates } from './pjson';
4
+ import { OclifConfiguration, PJSON, S3Templates } from './pjson';
5
5
  import { Options, Plugin } from './plugin';
6
6
  import { Theme } from './theme';
7
7
  import { Topic } from './topic';
@@ -114,7 +114,7 @@ export interface Config {
114
114
  readonly theme?: Theme | undefined;
115
115
  topicSeparator: ' ' | ':';
116
116
  readonly topics: Topic[];
117
- readonly updateConfig: NonNullable<Configuration['update']>;
117
+ readonly updateConfig: NonNullable<OclifConfiguration['update']>;
118
118
  /**
119
119
  * user agent to use for http calls
120
120
  *
@@ -3,12 +3,12 @@ import { FlagInput } from './parser';
3
3
  * Infer the flags that are returned by Command.parse. This is useful for when you want to assign the flags as a class property.
4
4
  *
5
5
  * @example
6
- * export type StatusFlags = Interfaces.InferredFlags<typeof Status.flags & typeof Status.baseFlags>
6
+ * export type StatusFlags = Interfaces.InferredFlags<typeof Status.flags && typeof Status.baseFlags>
7
7
  *
8
8
  * export abstract class BaseCommand extends Command {
9
9
  * static enableJsonFlag = true
10
10
  *
11
- * static baseFlags = {
11
+ * static flags = {
12
12
  * config: Flags.string({
13
13
  * description: 'specify config file',
14
14
  * }),
@@ -8,7 +8,7 @@ export type { Hook, Hooks } from './hooks';
8
8
  export type { Logger } from './logger';
9
9
  export type { Manifest } from './manifest';
10
10
  export type { Arg, BooleanFlag, CustomOptions, Deprecation, Flag, FlagDefinition, OptionFlag } from './parser';
11
- export type { Configuration, PJSON, S3, S3Templates, UserPJSON } from './pjson';
11
+ export type { LinkedPlugin, OclifConfiguration, PJSON, S3, S3Templates, UserPJSON, UserPlugin } from './pjson';
12
12
  export type { Options, Plugin, PluginOptions } from './plugin';
13
13
  export type { S3Manifest } from './s3-manifest';
14
14
  export type { Theme } from './theme';
@@ -198,7 +198,7 @@ export type BooleanFlagProps = FlagProps & {
198
198
  };
199
199
  export type OptionFlagProps = FlagProps & {
200
200
  type: 'option';
201
- helpValue?: string;
201
+ helpValue?: string | string[];
202
202
  options?: readonly string[];
203
203
  multiple?: boolean;
204
204
  /**
@@ -102,13 +102,14 @@ export type S3 = {
102
102
  folder?: string | undefined;
103
103
  gz?: boolean | undefined;
104
104
  host?: string | undefined;
105
+ indexVersionLimit?: number | undefined;
105
106
  templates?: {
106
107
  target: S3Templates;
107
108
  vanilla: S3Templates;
108
109
  } | undefined;
109
110
  xz?: boolean | undefined;
110
111
  };
111
- export type Configuration = {
112
+ export type OclifConfiguration = {
112
113
  /**
113
114
  * Flags in addition to --help that should trigger help output.
114
115
  */
@@ -176,7 +177,7 @@ export type Configuration = {
176
177
  * Register hooks to run at various points in the CLI lifecycle.
177
178
  */
178
179
  hooks?: {
179
- [name: string]: string | string[] | HookOptions | HookOptions[];
180
+ [name: string]: string | string[] | HookOptions | HookOptions[] | (string | HookOptions)[];
180
181
  };
181
182
  /**
182
183
  * Plugins that can be installed just-in-time.
@@ -237,7 +238,7 @@ export type Configuration = {
237
238
  [k: string]: {
238
239
  description?: string;
239
240
  hidden?: boolean;
240
- subtopics?: Configuration['topics'];
241
+ subtopics?: OclifConfiguration['topics'];
241
242
  };
242
243
  };
243
244
  update?: {
@@ -246,12 +247,12 @@ export type Configuration = {
246
247
  rollout?: number;
247
248
  };
248
249
  disableNpmLookup?: boolean;
249
- node: {
250
+ node?: {
250
251
  targets?: string[];
251
252
  version?: string;
252
253
  options?: string | string[];
253
254
  };
254
- s3: S3;
255
+ s3?: S3;
255
256
  };
256
257
  'warn-if-update-available'?: {
257
258
  authorization?: string;
@@ -278,12 +279,9 @@ export type LinkedPlugin = {
278
279
  root: string;
279
280
  type: 'link';
280
281
  };
281
- export type PluginTypes = {
282
- root: string;
283
- } | UserPlugin | LinkedPlugin;
284
282
  export type UserPJSON = {
285
283
  oclif: {
286
- plugins?: PluginTypes[];
284
+ plugins?: (UserPlugin | LinkedPlugin)[];
287
285
  };
288
286
  private?: boolean;
289
287
  };
@@ -296,6 +294,6 @@ export type PJSON = {
296
294
  [name: string]: string;
297
295
  };
298
296
  name: string;
299
- oclif: Configuration;
297
+ oclif: OclifConfiguration;
300
298
  version: string;
301
299
  };
@@ -315,9 +315,7 @@ class Parser {
315
315
  .map(async (v) => parseFlagOrThrowError(v, i.inputFlag.flag, this.context, {
316
316
  ...(0, util_1.last)(i.tokens),
317
317
  input: v,
318
- }))))
319
- // eslint-disable-next-line unicorn/no-await-expression-member
320
- .map((v) => validateOptions(i.inputFlag.flag, v)),
318
+ })))).map((v) => validateOptions(i.inputFlag.flag, v)),
321
319
  };
322
320
  }
323
321
  // multiple in the oclif-core style
@@ -0,0 +1,21 @@
1
+ import { Command } from '../command';
2
+ /**
3
+ * This function is responsible for locating the correct plugin to use for a named command id
4
+ * It searches the {Config} registered commands to match either the raw command id or the command alias
5
+ * It is possible that more than one command will be found. This is due the ability of two distinct plugins to
6
+ * create the same command or command alias.
7
+ *
8
+ * In the case of more than one found command, the function will select the command based on the order in which
9
+ * the plugin is included in the package.json `oclif.plugins` list. The command that occurs first in the list
10
+ * is selected as the command to run.
11
+ *
12
+ * Commands can also be present from either an install or a link. When a command is one of these and a core plugin
13
+ * is present, this function defers to the core plugin.
14
+ *
15
+ * If there is not a core plugin command present, this function will return the first
16
+ * plugin as discovered (will not change the order)
17
+ *
18
+ * @param commands commands to determine the priority of
19
+ * @returns command instance {Command.Loadable} or undefined
20
+ */
21
+ export declare function determinePriority(plugins: string[], commands: Command.Loadable[]): Command.Loadable;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.determinePriority = void 0;
4
+ /**
5
+ * This function is responsible for locating the correct plugin to use for a named command id
6
+ * It searches the {Config} registered commands to match either the raw command id or the command alias
7
+ * It is possible that more than one command will be found. This is due the ability of two distinct plugins to
8
+ * create the same command or command alias.
9
+ *
10
+ * In the case of more than one found command, the function will select the command based on the order in which
11
+ * the plugin is included in the package.json `oclif.plugins` list. The command that occurs first in the list
12
+ * is selected as the command to run.
13
+ *
14
+ * Commands can also be present from either an install or a link. When a command is one of these and a core plugin
15
+ * is present, this function defers to the core plugin.
16
+ *
17
+ * If there is not a core plugin command present, this function will return the first
18
+ * plugin as discovered (will not change the order)
19
+ *
20
+ * @param commands commands to determine the priority of
21
+ * @returns command instance {Command.Loadable} or undefined
22
+ */
23
+ function determinePriority(plugins, commands) {
24
+ const commandPlugins = commands.sort((a, b) => {
25
+ const pluginAliasA = a.pluginAlias ?? 'A-Cannot-Find-This';
26
+ const pluginAliasB = b.pluginAlias ?? 'B-Cannot-Find-This';
27
+ const aIndex = plugins.indexOf(pluginAliasA);
28
+ const bIndex = plugins.indexOf(pluginAliasB);
29
+ // When both plugin types are 'core' plugins sort based on index
30
+ if (a.pluginType === 'core' && b.pluginType === 'core') {
31
+ // If b appears first in the pjson.plugins sort it first
32
+ return aIndex - bIndex;
33
+ }
34
+ // if b is a core plugin and a is not sort b first
35
+ if (b.pluginType === 'core' && a.pluginType !== 'core') {
36
+ return 1;
37
+ }
38
+ // if a is a core plugin and b is not sort a first
39
+ if (a.pluginType === 'core' && b.pluginType !== 'core') {
40
+ return -1;
41
+ }
42
+ // if a is a jit plugin and b is not sort b first
43
+ if (a.pluginType === 'jit' && b.pluginType !== 'jit') {
44
+ return 1;
45
+ }
46
+ // if b is a jit plugin and a is not sort a first
47
+ if (b.pluginType === 'jit' && a.pluginType !== 'jit') {
48
+ return -1;
49
+ }
50
+ // neither plugin is core, so do not change the order
51
+ return 0;
52
+ });
53
+ return commandPlugins[0];
54
+ }
55
+ exports.determinePriority = determinePriority;
package/lib/util/fs.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.existsSync = exports.safeReadJson = exports.readJsonSync = exports.readJson = exports.fileExists = exports.dirExists = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const promises_1 = require("node:fs/promises");
6
+ const util_1 = require("./util");
6
7
  /**
7
8
  * Parser for Args.directory and Flags.directory. Checks that the provided path
8
9
  * exists and is a directory.
@@ -43,7 +44,15 @@ const fileExists = async (input) => {
43
44
  return input;
44
45
  };
45
46
  exports.fileExists = fileExists;
46
- const cache = new Map();
47
+ class ProdOnlyCache extends Map {
48
+ set(key, value) {
49
+ if ((0, util_1.isProd)() ?? false) {
50
+ super.set(key, value);
51
+ }
52
+ return this;
53
+ }
54
+ }
55
+ const cache = new ProdOnlyCache();
47
56
  async function readJson(path) {
48
57
  if (cache.has(path)) {
49
58
  return JSON.parse(cache.get(path));
package/lib/ux/index.d.ts CHANGED
@@ -55,13 +55,13 @@ export declare const ux: {
55
55
  *
56
56
  * See node's util.format() for formatting options.
57
57
  */
58
- stderr: (str: string | string[] | undefined, ...args: string[]) => void;
58
+ stderr: (str?: string | string[] | undefined, ...args: string[]) => void;
59
59
  /**
60
60
  * Log a formatted string to stdout.
61
61
  *
62
62
  * See node's util.format() for formatting options.
63
63
  */
64
- stdout: (str: string | string[] | undefined, ...args: string[]) => void;
64
+ stdout: (str?: string | string[] | undefined, ...args: string[]) => void;
65
65
  /**
66
66
  * Prints a pretty warning message to stderr.
67
67
  */
@@ -0,0 +1 @@
1
+ export declare function supportsColor(): boolean;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.supportsColor = void 0;
4
+ const supports_color_1 = require("supports-color");
5
+ function supportsColor() {
6
+ return Boolean(supports_color_1.stdout) && Boolean(supports_color_1.stderr);
7
+ }
8
+ exports.supportsColor = supportsColor;
package/lib/ux/theme.d.ts CHANGED
@@ -6,4 +6,4 @@ import { StandardAnsi, Theme } from '../interfaces/theme';
6
6
  * @returns colorized string
7
7
  */
8
8
  export declare function colorize(color: string | StandardAnsi | undefined, text: string): string;
9
- export declare function parseTheme(theme: Record<string, string>): Theme;
9
+ export declare function parseTheme(theme: Record<string, string | Record<string, string>>): Theme;
package/lib/ux/theme.js CHANGED
@@ -6,7 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.parseTheme = exports.colorize = void 0;
7
7
  const ansis_1 = __importDefault(require("ansis"));
8
8
  const theme_1 = require("../interfaces/theme");
9
- function isStandardChalk(color) {
9
+ const supports_color_1 = require("./supports-color");
10
+ function isStandardAnsi(color) {
10
11
  return theme_1.STANDARD_ANSI.includes(color);
11
12
  }
12
13
  /**
@@ -18,7 +19,9 @@ function isStandardChalk(color) {
18
19
  function colorize(color, text) {
19
20
  if (!color)
20
21
  return text;
21
- if (isStandardChalk(color))
22
+ if (!(0, supports_color_1.supportsColor)())
23
+ return text;
24
+ if (isStandardAnsi(color))
22
25
  return ansis_1.default[color](text);
23
26
  if (color.startsWith('#'))
24
27
  return ansis_1.default.hex(color)(text);
@@ -39,5 +42,5 @@ function parseTheme(theme) {
39
42
  }
40
43
  exports.parseTheme = parseTheme;
41
44
  function isValid(color) {
42
- return color.startsWith('#') || color.startsWith('rgb') || isStandardChalk(color) ? color : undefined;
45
+ return color.startsWith('#') || color.startsWith('rgb') || isStandardAnsi(color) ? color : undefined;
43
46
  }
package/lib/ux/write.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const stdout: (str: string | string[] | undefined, ...args: string[]) => void;
2
- export declare const stderr: (str: string | string[] | undefined, ...args: string[]) => void;
1
+ export declare const stdout: (str?: string | string[] | undefined, ...args: string[]) => void;
2
+ export declare const stderr: (str?: string | string[] | undefined, ...args: string[]) => void;
package/lib/ux/write.js CHANGED
@@ -3,8 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.stderr = exports.stdout = void 0;
4
4
  const node_util_1 = require("node:util");
5
5
  const stdout = (str, ...args) => {
6
- if (typeof str === 'string' || !str) {
7
- process.stdout.write((0, node_util_1.format)(str, ...args) + '\n');
6
+ if (!str && args) {
7
+ process.stdout.write((0, node_util_1.format)(...args) + '\n');
8
+ }
9
+ else if (!str) {
10
+ process.stdout.write('\n');
11
+ }
12
+ else if (typeof str === 'string') {
13
+ process.stdout.write((str && (0, node_util_1.format)(str, ...args)) + '\n');
8
14
  }
9
15
  else {
10
16
  process.stdout.write((0, node_util_1.format)(...str, ...args) + '\n');
@@ -12,8 +18,14 @@ const stdout = (str, ...args) => {
12
18
  };
13
19
  exports.stdout = stdout;
14
20
  const stderr = (str, ...args) => {
15
- if (typeof str === 'string' || !str) {
16
- process.stderr.write((0, node_util_1.format)(str, ...args) + '\n');
21
+ if (!str && args) {
22
+ process.stderr.write((0, node_util_1.format)(...args) + '\n');
23
+ }
24
+ else if (!str) {
25
+ process.stderr.write('\n');
26
+ }
27
+ else if (typeof str === 'string') {
28
+ process.stderr.write((str && (0, node_util_1.format)(str, ...args)) + '\n');
17
29
  }
18
30
  else {
19
31
  process.stderr.write((0, node_util_1.format)(...str, ...args) + '\n');
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@oclif/core",
3
3
  "description": "base library for oclif CLIs",
4
- "version": "4.0.0-beta.8",
4
+ "version": "4.0.0",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/core/issues",
7
7
  "dependencies": {
8
8
  "ansi-escapes": "^4.3.2",
9
- "ansis": "^3.0.1",
9
+ "ansis": "^3.1.1",
10
10
  "clean-stack": "^3.0.1",
11
11
  "cli-spinners": "^2.9.2",
12
12
  "cosmiconfig": "^9.0.0",
13
- "debug": "^4.3.4",
13
+ "debug": "^4.3.5",
14
14
  "ejs": "^3.1.10",
15
15
  "get-package-type": "^0.1.0",
16
16
  "globby": "^11.1.0",
@@ -18,7 +18,7 @@
18
18
  "is-wsl": "^2.2.0",
19
19
  "minimatch": "^9.0.4",
20
20
  "string-width": "^4.2.3",
21
- "supports-color": "^9.4.0",
21
+ "supports-color": "^8",
22
22
  "widest-line": "^3.1.0",
23
23
  "wordwrap": "^1.0.0",
24
24
  "wrap-ansi": "^7.0.0"
@@ -28,6 +28,7 @@
28
28
  "@oclif/plugin-help": "^6",
29
29
  "@oclif/plugin-plugins": "^5",
30
30
  "@oclif/prettier-config": "^0.2.1",
31
+ "@oclif/test": "^4",
31
32
  "@types/benchmark": "^2.1.5",
32
33
  "@types/chai": "^4.3.11",
33
34
  "@types/chai-as-promised": "^7.1.8",
@@ -39,11 +40,12 @@
39
40
  "@types/node": "^18",
40
41
  "@types/pnpapi": "^0.0.5",
41
42
  "@types/sinon": "^17.0.3",
43
+ "@types/supports-color": "^8.1.3",
42
44
  "@types/wordwrap": "^1.0.3",
43
45
  "@types/wrap-ansi": "^3.0.0",
44
46
  "benchmark": "^2.1.4",
45
47
  "chai": "^4.4.1",
46
- "chai-as-promised": "^7.1.1",
48
+ "chai-as-promised": "^7.1.2",
47
49
  "commitlint": "^19",
48
50
  "cross-env": "^7.0.3",
49
51
  "eslint": "^8.57.0",
@@ -57,7 +59,7 @@
57
59
  "nyc": "^15.1.0",
58
60
  "prettier": "^3.2.5",
59
61
  "shx": "^0.3.4",
60
- "sinon": "^17",
62
+ "sinon": "^18",
61
63
  "ts-node": "^10.9.2",
62
64
  "tsd": "^0.31.0",
63
65
  "typescript": "^5"
@@ -112,20 +114,18 @@
112
114
  },
113
115
  "scripts": {
114
116
  "build": "shx rm -rf lib && tsc",
115
- "commitlint": "commitlint",
116
117
  "compile": "tsc",
117
118
  "format": "prettier --write \"+(src|test)/**/*.+(ts|js|json)\"",
118
119
  "lint": "eslint . --ext .ts",
119
120
  "posttest": "yarn lint && yarn test:circular-deps",
120
121
  "prepack": "yarn run build",
121
122
  "prepare": "husky",
122
- "pretest": "yarn build && tsc -p test --noEmit --skipLibCheck",
123
- "test:circular-deps": "madge lib/ -c",
123
+ "test:circular-deps": "yarn build && madge lib/ -c",
124
124
  "test:debug": "nyc mocha --debug-brk --inspect \"test/**/*.test.ts\"",
125
125
  "test:integration": "mocha --forbid-only \"test/**/*.integration.ts\" --parallel --timeout 1200000",
126
126
  "test:interoperability": "cross-env DEBUG=integration:* ts-node test/integration/interop.ts",
127
127
  "test:perf": "ts-node test/perf/parser.perf.ts",
128
- "test": "nyc mocha --forbid-only \"test/**/*.test.ts\""
128
+ "test": "nyc mocha --forbid-only \"test/**/*.test.ts\" --parallel"
129
129
  },
130
130
  "types": "lib/index.d.ts"
131
131
  }