@oclif/core 2.6.4 → 2.7.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/command.js CHANGED
@@ -157,16 +157,21 @@ class Command {
157
157
  }
158
158
  warnIfFlagDeprecated(flags) {
159
159
  for (const flag of Object.keys(flags)) {
160
- const deprecated = this.ctor.flags[flag]?.deprecated;
160
+ const flagDef = this.ctor.flags[flag];
161
+ const deprecated = flagDef?.deprecated;
161
162
  if (deprecated) {
162
163
  this.warn((0, util_2.formatFlagDeprecationWarning)(flag, deprecated));
163
164
  }
164
- const deprecateAliases = this.ctor.flags[flag]?.deprecateAliases;
165
- const aliases = (this.ctor.flags[flag]?.aliases ?? []).map(a => a.length === 1 ? `-${a}` : `--${a}`);
165
+ const deprecateAliases = flagDef?.deprecateAliases;
166
+ const aliases = (flagDef?.aliases ?? []).map(a => a.length === 1 ? `-${a}` : `--${a}`);
166
167
  if (deprecateAliases && aliases.length > 0) {
167
168
  const foundAliases = aliases.filter(alias => this.argv.some(a => a.startsWith(alias)));
168
169
  for (const alias of foundAliases) {
169
- this.warn((0, util_2.formatFlagDeprecationWarning)(alias, { to: this.ctor.flags[flag]?.name }));
170
+ let preferredUsage = `--${flagDef?.name}`;
171
+ if (flagDef?.char) {
172
+ preferredUsage += ` | -${flagDef?.char}`;
173
+ }
174
+ this.warn((0, util_2.formatFlagDeprecationWarning)(alias, { to: preferredUsage }));
170
175
  }
171
176
  }
172
177
  }
@@ -136,4 +136,4 @@ export declare class Config implements IConfig {
136
136
  private determinePriority;
137
137
  private insertLegacyPlugins;
138
138
  }
139
- export declare function toCached(c: Command.Class, plugin?: IPlugin): Promise<Command.Cached>;
139
+ export declare function toCached(c: Command.Class, plugin?: IPlugin | undefined, isWritingManifest?: boolean): Promise<Command.Cached>;
@@ -713,11 +713,11 @@ class Config {
713
713
  }
714
714
  exports.Config = Config;
715
715
  // when no manifest exists, the default is calculated. This may throw, so we need to catch it
716
- const defaultFlagToCached = async (flag) => {
716
+ const defaultFlagToCached = async (flag, isWritingManifest = false) => {
717
717
  // Prefer the helpDefaultValue function (returns a friendly string for complex types)
718
718
  if (typeof flag.defaultHelp === 'function') {
719
719
  try {
720
- return await flag.defaultHelp();
720
+ return await flag.defaultHelp({ options: flag, flags: {} }, isWritingManifest);
721
721
  }
722
722
  catch {
723
723
  return;
@@ -726,7 +726,7 @@ const defaultFlagToCached = async (flag) => {
726
726
  // if not specified, try the default function
727
727
  if (typeof flag.default === 'function') {
728
728
  try {
729
- return await flag.default({ options: {}, flags: {} });
729
+ return await flag.default({ options: {}, flags: {} }, isWritingManifest);
730
730
  }
731
731
  catch { }
732
732
  }
@@ -734,11 +734,11 @@ const defaultFlagToCached = async (flag) => {
734
734
  return flag.default;
735
735
  }
736
736
  };
737
- const defaultArgToCached = async (arg) => {
737
+ const defaultArgToCached = async (arg, isWritingManifest = false) => {
738
738
  // Prefer the helpDefaultValue function (returns a friendly string for complex types)
739
739
  if (typeof arg.defaultHelp === 'function') {
740
740
  try {
741
- return await arg.defaultHelp();
741
+ return await arg.defaultHelp({ options: arg, flags: {} }, isWritingManifest);
742
742
  }
743
743
  catch {
744
744
  return;
@@ -747,7 +747,7 @@ const defaultArgToCached = async (arg) => {
747
747
  // if not specified, try the default function
748
748
  if (typeof arg.default === 'function') {
749
749
  try {
750
- return await arg.default({ options: {}, flags: {} });
750
+ return await arg.default({ options: arg, flags: {} }, isWritingManifest);
751
751
  }
752
752
  catch { }
753
753
  }
@@ -755,7 +755,7 @@ const defaultArgToCached = async (arg) => {
755
755
  return arg.default;
756
756
  }
757
757
  };
758
- async function toCached(c, plugin) {
758
+ async function toCached(c, plugin, isWritingManifest) {
759
759
  const flags = {};
760
760
  for (const [name, flag] of Object.entries(c.flags || {})) {
761
761
  if (flag.type === 'boolean') {
@@ -796,7 +796,7 @@ async function toCached(c, plugin) {
796
796
  dependsOn: flag.dependsOn,
797
797
  relationships: flag.relationships,
798
798
  exclusive: flag.exclusive,
799
- default: await defaultFlagToCached(flag),
799
+ default: await defaultFlagToCached(flag, isWritingManifest),
800
800
  deprecated: flag.deprecated,
801
801
  deprecateAliases: c.deprecateAliases,
802
802
  aliases: flag.aliases,
@@ -815,7 +815,7 @@ async function toCached(c, plugin) {
815
815
  description: arg.description,
816
816
  required: arg.required,
817
817
  options: arg.options,
818
- default: await defaultArgToCached(arg),
818
+ default: await defaultArgToCached(arg, isWritingManifest),
819
819
  hidden: arg.hidden,
820
820
  };
821
821
  }
@@ -28,7 +28,13 @@ export declare class Plugin implements IPlugin {
28
28
  protected _debug: (..._: any) => void;
29
29
  protected warned: boolean;
30
30
  constructor(options: PluginOptions);
31
- load(): Promise<void>;
31
+ /**
32
+ * Loads a plugin
33
+ * @param isWritingManifest - if true, exclude selected data from manifest
34
+ * default is false to maintain backwards compatibility
35
+ * @returns Promise<void>
36
+ */
37
+ load(isWritingManifest?: boolean): Promise<void>;
32
38
  get topics(): Topic[];
33
39
  get commandsDir(): string | undefined;
34
40
  get commandIDs(): string[];
@@ -38,7 +44,7 @@ export declare class Plugin implements IPlugin {
38
44
  findCommand(id: string, opts?: {
39
45
  must: boolean;
40
46
  }): Promise<Command.Class | undefined>;
41
- protected _manifest(ignoreManifest: boolean, errorOnManifestCreate?: boolean): Promise<Manifest>;
47
+ protected _manifest(ignoreManifest: boolean, errorOnManifestCreate?: boolean, isWritingManifest?: boolean): Promise<Manifest>;
42
48
  protected warn(err: string | Error | CLIError, scope?: string): void;
43
49
  private addErrorScope;
44
50
  }
@@ -100,7 +100,13 @@ class Plugin {
100
100
  this._debug = (0, util_2.Debug)();
101
101
  this.warned = false;
102
102
  }
103
- async load() {
103
+ /**
104
+ * Loads a plugin
105
+ * @param isWritingManifest - if true, exclude selected data from manifest
106
+ * default is false to maintain backwards compatibility
107
+ * @returns Promise<void>
108
+ */
109
+ async load(isWritingManifest) {
104
110
  this.type = this.options.type || 'core';
105
111
  this.tag = this.options.tag;
106
112
  const root = await findRoot(this.options.name, this.options.root);
@@ -126,7 +132,7 @@ class Plugin {
126
132
  this.pjson.oclif = this.pjson['cli-engine'] || {};
127
133
  }
128
134
  this.hooks = (0, util_3.mapValues)(this.pjson.oclif.hooks || {}, i => Array.isArray(i) ? i : [i]);
129
- this.manifest = await this._manifest(Boolean(this.options.ignoreManifest), Boolean(this.options.errorOnManifestCreate));
135
+ this.manifest = await this._manifest(Boolean(this.options.ignoreManifest), Boolean(this.options.errorOnManifestCreate), isWritingManifest);
130
136
  this.commands = Object
131
137
  .entries(this.manifest.commands)
132
138
  .map(([id, c]) => ({
@@ -205,7 +211,7 @@ class Plugin {
205
211
  marker?.stop();
206
212
  return cmd;
207
213
  }
208
- async _manifest(ignoreManifest, errorOnManifestCreate = false) {
214
+ async _manifest(ignoreManifest, errorOnManifestCreate = false, isWritingManifest = false) {
209
215
  const readManifest = async (dotfile = false) => {
210
216
  try {
211
217
  const p = path.join(this.root, `${dotfile ? '.' : ''}oclif.manifest.json`);
@@ -242,7 +248,7 @@ class Plugin {
242
248
  version: this.version,
243
249
  commands: (await Promise.all(this.commandIDs.map(async (id) => {
244
250
  try {
245
- return [id, await (0, config_1.toCached)(await this.findCommand(id, { must: true }), this)];
251
+ return [id, await (0, config_1.toCached)(await this.findCommand(id, { must: true }), this, isWritingManifest)];
246
252
  }
247
253
  catch (error) {
248
254
  const scope = 'toCached';
package/lib/help/index.js CHANGED
@@ -91,7 +91,7 @@ class Help extends HelpBase {
91
91
  const command = this.config.findCommand(subject);
92
92
  if (command) {
93
93
  if (command.hasDynamicHelp && command.pluginType !== 'jit') {
94
- const dynamicCommand = await (0, config_1.toCached)(await command.load());
94
+ const dynamicCommand = await (0, config_1.toCached)(await command.load(), undefined, false);
95
95
  await this.showCommandHelp(dynamicCommand);
96
96
  }
97
97
  else {
package/lib/help/util.js CHANGED
@@ -101,7 +101,7 @@ function formatFlagDeprecationWarning(flag, opts) {
101
101
  if (opts.version) {
102
102
  message += ` and will be removed in version ${opts.version}`;
103
103
  }
104
- message += opts.to ? `. Use "--${opts.to}" instead.` : '.';
104
+ message += opts.to ? `. Use "${opts.to}" instead.` : '.';
105
105
  return message;
106
106
  }
107
107
  exports.formatFlagDeprecationWarning = formatFlagDeprecationWarning;
@@ -50,6 +50,7 @@ export type Metadata = {
50
50
  };
51
51
  type MetadataFlag = {
52
52
  setFromDefault?: boolean;
53
+ defaultHelp?: unknown;
53
54
  };
54
55
  export type ListItem = [string, string | undefined];
55
56
  export type List = ListItem[];
@@ -58,10 +59,91 @@ export type DefaultContext<T> = {
58
59
  options: T;
59
60
  flags: Record<string, string>;
60
61
  };
61
- export type FlagDefault<T, P = CustomOptions> = T | ((context: DefaultContext<P & OptionFlag<T, P>>) => Promise<T>);
62
- export type FlagDefaultHelp<T, P = CustomOptions> = T | ((context: DefaultContext<P & OptionFlag<T, P>>) => Promise<string | undefined>);
63
- export type ArgDefault<T, P = CustomOptions> = T | ((context: DefaultContext<Arg<T, P>>) => Promise<T>);
64
- export type ArgDefaultHelp<T, P = CustomOptions> = T | ((context: DefaultContext<Arg<T, P>>) => Promise<string | undefined>);
62
+ /**
63
+ * Type to define a default value for a flag.
64
+ * @param context The context of the flag.
65
+ * @param isWritingManifest Informs the function that a manifest file is being written.
66
+ * The manifest file is used to store the flag definitions, with a default value if present, for a command and is published to npm.
67
+ * When a manifest file is being written, the default value may contain data that should not be included in the manifest.
68
+ * The plugin developer can use isWritingManifest to determine if the default value should be omitted from the manifest.
69
+ * in the function's implementation.
70
+ * @example
71
+ * static flags = {
72
+ * foo: flags.string({
73
+ * defaultHelp: async (context, isWritingManifest) => {
74
+ * if (isWritingManifest) {
75
+ * return undefined
76
+ * }
77
+ * return 'value that is used outside a manifest'
78
+ * },
79
+ * }),
80
+ * }
81
+ */
82
+ export type FlagDefault<T, P = CustomOptions> = T | ((context: DefaultContext<P & OptionFlag<T, P>>, isWritingManifest?: boolean) => Promise<T>);
83
+ /**
84
+ * Type to define a defaultHelp value for a flag.
85
+ * The defaultHelp value is used in the help output for the flag and when writing a manifest.
86
+ * It is also can be used to provide a value for the flag when issuing certain error messages.
87
+ *
88
+ * @param context The context of the flag.
89
+ * @param isWritingManifest Informs the function that a manifest file is being written.
90
+ * The manifest file is used to store the flag definitions, with a default value if present via defaultHelp, for a command and is published to npm.
91
+ * When a manifest file is being written, the default value may contain data that should not be included in the manifest.
92
+ * The plugin developer can use isWritingManifest to determine if the defaultHelp value should be omitted from the manifest.
93
+ * in the function's implementation.
94
+ * @example
95
+ * static flags = {
96
+ * foo: flags.string({
97
+ * defaultHelp: async (context, isWritingManifest) => {
98
+ * if (isWritingManifest) {
99
+ * return undefined
100
+ * }
101
+ * return 'value that is used outside a manifest'
102
+ * },
103
+ * }),
104
+ * }
105
+ */
106
+ export type FlagDefaultHelp<T, P = CustomOptions> = T | ((context: DefaultContext<P & OptionFlag<T, P>>, isWritingManifest?: boolean) => Promise<string | undefined>);
107
+ /**
108
+ * Type to define a default value for an arg.
109
+ * @param context The context of the arg.
110
+ * @param isWritingManifest Informs the function that a manifest file is being written.
111
+ * The manifest file is used to store the arg definitions, with a default value if present, for a command and is published to npm.
112
+ * When a manifest file is being written, the default value may contain data that should not be included in the manifest.
113
+ * The plugin developer can use isWritingManifest to determine if the default value should be omitted from the manifest.
114
+ * in the function's implementation.
115
+ * @example
116
+ * public static readonly args = {
117
+ * one: Args.string({
118
+ * default: async (context, isWritingManifest) => {
119
+ * if (isWritingManifest) {
120
+ * return undefined
121
+ * }
122
+ * return 'value that is used outside a manifest'
123
+ * }),
124
+ * };
125
+ */
126
+ export type ArgDefault<T, P = CustomOptions> = T | ((context: DefaultContext<Arg<T, P>>, isWritingManifest?: boolean) => Promise<T>);
127
+ /**
128
+ * Type to define a defaultHelp value for an arg.
129
+ * @param context The context of the arg.
130
+ * @param isWritingManifest Informs the function that a manifest file is being written.
131
+ * The manifest file is used to store the arg definitions, with a default value if present via defaultHelp, for a command and is published to npm.
132
+ * When a manifest file is being written, the default value may contain data that should not be included in the manifest.
133
+ * The plugin developer can use isWritingManifest to determine if the default value should be omitted from the manifest.
134
+ * in the function's implementation.
135
+ * @example
136
+ * public static readonly args = {
137
+ * one: Args.string({
138
+ * defaultHelp: async (context, isWritingManifest) => {
139
+ * if (isWritingManifest) {
140
+ * return undefined
141
+ * }
142
+ * return 'value that is used outside a manifest'
143
+ * }),
144
+ * };
145
+ */
146
+ export type ArgDefaultHelp<T, P = CustomOptions> = T | ((context: DefaultContext<Arg<T, P>>, isWritingManifest?: boolean) => Promise<string | undefined>);
65
147
  export type FlagRelationship = string | {
66
148
  name: string;
67
149
  when: (flags: Record<string, unknown>) => Promise<boolean>;
@@ -74,5 +74,5 @@ export interface Plugin {
74
74
  findCommand(id: string, opts?: {
75
75
  must: boolean;
76
76
  }): Promise<Command.Class> | undefined;
77
- load(): Promise<void>;
77
+ load(isWritingManifest: boolean): Promise<void>;
78
78
  }
@@ -178,6 +178,12 @@ class Parser {
178
178
  const flag = this.input.flags[token.flag];
179
179
  if (!flag)
180
180
  throw new errors_1.CLIError(`Unexpected flag ${token.flag}`);
181
+ // if flag has defaultHelp, capture its value into metadata
182
+ if (Reflect.has(flag, 'defaultHelp')) {
183
+ const defaultHelpProperty = Reflect.get(flag, 'defaultHelp');
184
+ const defaultHelp = (typeof defaultHelpProperty === 'function' ? await defaultHelpProperty({ options: flag, flags, ...this.context }) : defaultHelpProperty);
185
+ this.metaData.flags[token.flag] = { ...this.metaData.flags[token.flag], defaultHelp };
186
+ }
181
187
  if (flag.type === 'boolean') {
182
188
  if (token.input === `--no-${flag.name}`) {
183
189
  flags[token.flag] = false;
@@ -216,7 +222,7 @@ class Parser {
216
222
  const flag = this.input.flags[k];
217
223
  if (flags[k])
218
224
  continue;
219
- if (flag.env && Object.prototype.hasOwnProperty.call(process.env, flag.env)) {
225
+ if (flag.env && Reflect.has(process.env, flag.env)) {
220
226
  const input = process.env[flag.env];
221
227
  if (flag.type === 'option') {
222
228
  if (input) {
@@ -230,7 +236,7 @@ class Parser {
230
236
  }
231
237
  }
232
238
  if (!(k in flags) && flag.default !== undefined) {
233
- this.metaData.flags[k] = { setFromDefault: true };
239
+ this.metaData.flags[k] = { ...this.metaData.flags[k], setFromDefault: true };
234
240
  const defaultValue = (typeof flag.default === 'function' ? await flag.default({ options: flag, flags, ...this.context }) : flag.default);
235
241
  flags[k] = defaultValue;
236
242
  }
@@ -94,7 +94,8 @@ async function validate(parse) {
94
94
  if (parse.output.metadata.flags && parse.output.metadata.flags[name]?.setFromDefault)
95
95
  continue;
96
96
  if (parse.output.flags[flag] !== undefined) {
97
- return { ...base, status: 'failed', reason: `--${flag}=${parse.output.flags[flag]} cannot also be provided when using --${name}` };
97
+ const flagValue = parse.output.metadata.flags?.[flag]?.defaultHelp ?? parse.output.flags[flag];
98
+ return { ...base, status: 'failed', reason: `--${flag}=${flagValue} cannot also be provided when using --${name}` };
98
99
  }
99
100
  }
100
101
  return { ...base, status: 'success' };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oclif/core",
3
3
  "description": "base library for oclif CLIs",
4
- "version": "2.6.4",
4
+ "version": "2.7.0",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/core/issues",
7
7
  "dependencies": {
@@ -37,9 +37,9 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@commitlint/config-conventional": "^12.1.4",
40
- "@oclif/plugin-help": "^5.2.7",
40
+ "@oclif/plugin-help": "^5.2.8",
41
41
  "@oclif/plugin-plugins": "^2.4.1",
42
- "@oclif/test": "^2.3.9",
42
+ "@oclif/test": "^2.3.11",
43
43
  "@types/ansi-styles": "^3.2.1",
44
44
  "@types/chai": "^4.3.4",
45
45
  "@types/chai-as-promised": "^7.1.5",
@@ -65,7 +65,7 @@
65
65
  "eslint": "^7.32.0",
66
66
  "eslint-config-oclif": "^4.0.0",
67
67
  "eslint-config-oclif-typescript": "^1.0.3",
68
- "fancy-test": "^2.0.12",
68
+ "fancy-test": "^2.0.15",
69
69
  "globby": "^11.1.0",
70
70
  "husky": "6",
71
71
  "mocha": "^8.4.0",