@oclif/core 1.24.2 → 1.25.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.
@@ -39,6 +39,7 @@ export declare class Config implements IConfig {
39
39
  constructor(options: Options);
40
40
  static load(opts?: LoadOptions): Promise<IConfig | Config>;
41
41
  load(): Promise<void>;
42
+ loadPluginsAndCommands(): Promise<void>;
42
43
  loadCorePlugins(): Promise<void>;
43
44
  loadDevPlugins(): Promise<void>;
44
45
  loadUserPlugins(): Promise<void>;
@@ -105,6 +106,7 @@ export declare class Config implements IConfig {
105
106
  detail: string;
106
107
  }, scope?: string): void;
107
108
  protected get isProd(): boolean;
109
+ private isJitPluginCommand;
108
110
  private getCmdLookupId;
109
111
  private getTopicLookupId;
110
112
  private loadCommands;
@@ -135,6 +135,10 @@ class Config {
135
135
  ...s3.templates && s3.templates.vanilla,
136
136
  },
137
137
  };
138
+ await this.loadPluginsAndCommands();
139
+ debug('config done');
140
+ }
141
+ async loadPluginsAndCommands() {
138
142
  await this.loadUserPlugins();
139
143
  await this.loadDevPlugins();
140
144
  await this.loadCorePlugins();
@@ -142,7 +146,6 @@ class Config {
142
146
  this.loadCommands(plugin);
143
147
  this.loadTopics(plugin);
144
148
  }
145
- debug('config done');
146
149
  }
147
150
  async loadCorePlugins() {
148
151
  if (this.pjson.oclif.plugins) {
@@ -252,25 +255,48 @@ class Config {
252
255
  // eslint-disable-next-line default-param-last
253
256
  async runCommand(id, argv = [], cachedCommand) {
254
257
  debug('runCommand %s %o', id, argv);
255
- const c = cachedCommand || this.findCommand(id);
258
+ let c = cachedCommand || this.findCommand(id);
256
259
  if (!c) {
257
260
  const matches = this.flexibleTaxonomy ? this.findMatches(id, argv) : [];
258
261
  const hookResult = this.flexibleTaxonomy && matches.length > 0 ?
259
262
  await this.runHook('command_incomplete', { id, argv, matches }) :
260
263
  await this.runHook('command_not_found', { id, argv });
261
- if (hookResult.successes[0]) {
262
- const cmdResult = hookResult.successes[0].result;
263
- return cmdResult;
264
- }
265
- if (hookResult.failures[0]) {
264
+ if (hookResult.successes[0])
265
+ return hookResult.successes[0].result;
266
+ if (hookResult.failures[0])
266
267
  throw hookResult.failures[0].error;
267
- }
268
268
  throw new errors_1.CLIError(`command ${id} not found`);
269
269
  }
270
+ if (this.isJitPluginCommand(c)) {
271
+ const pluginName = c.pluginName;
272
+ const pluginVersion = this.pjson.oclif.jitPlugins[pluginName];
273
+ const jitResult = await this.runHook('jit_plugin_not_installed', {
274
+ id,
275
+ argv,
276
+ command: c,
277
+ pluginName,
278
+ pluginVersion,
279
+ });
280
+ if (jitResult.failures[0])
281
+ throw jitResult.failures[0].error;
282
+ if (jitResult.successes[0]) {
283
+ await this.loadPluginsAndCommands();
284
+ c = this.findCommand(id) ?? c;
285
+ }
286
+ else {
287
+ // this means that no jit_plugin_not_installed hook exists, so we should run the default behavior
288
+ const result = await this.runHook('command_not_found', { id, argv });
289
+ if (result.successes[0])
290
+ return result.successes[0].result;
291
+ if (result.failures[0])
292
+ throw result.failures[0].error;
293
+ throw new errors_1.CLIError(`command ${id} not found`);
294
+ }
295
+ }
270
296
  const command = await c.load();
271
297
  await this.runHook('prerun', { Command: command, argv });
272
298
  const result = (await command.run(argv, this));
273
- await this.runHook('postrun', { Command: command, result: result, argv });
299
+ await this.runHook('postrun', { Command: command, result, argv });
274
300
  return result;
275
301
  }
276
302
  scopedEnvVar(k) {
@@ -489,6 +515,9 @@ class Config {
489
515
  get isProd() {
490
516
  return (0, util_3.isProd)();
491
517
  }
518
+ isJitPluginCommand(c) {
519
+ return Object.keys(this.pjson.oclif.jitPlugins ?? {}).includes(c.pluginName ?? '') && !this.plugins.find(p => p.name === c?.pluginName);
520
+ }
492
521
  getCmdLookupId(id) {
493
522
  if (this._commands.has(id))
494
523
  return id;
@@ -597,6 +626,14 @@ class Config {
597
626
  if (a.pluginType === 'core' && b.pluginType !== 'core') {
598
627
  return -1;
599
628
  }
629
+ // if a is a jit plugin and b is not sort b first
630
+ if (a.pluginType === 'jit' && b.pluginType !== 'jit') {
631
+ return 1;
632
+ }
633
+ // if b is a jit plugin and a is not sort a first
634
+ if (b.pluginType === 'jit' && a.pluginType !== 'jit') {
635
+ return -1;
636
+ }
600
637
  // neither plugin is core, so do not change the order
601
638
  return 0;
602
639
  });
@@ -677,10 +714,7 @@ async function toCached(c, plugin) {
677
714
  }
678
715
  }
679
716
  }
680
- // v2 commands have args as an object, so we need to normalize it to an array for forwards compatibility
681
- // @ts-ignore
682
- const normalized = Array.isArray(c.args) ? c.args ?? [] : Object.entries(c.args ?? {}).map(([name, arg]) => ({ ...arg, name }));
683
- const argsPromise = normalized.map(async (a) => ({
717
+ const argsPromise = (0, util_3.ensureArgArray)(c.args).map(async (a) => ({
684
718
  name: a.name,
685
719
  description: a.description,
686
720
  required: a.required,
@@ -127,7 +127,12 @@ class Plugin {
127
127
  this.manifest = await this._manifest(Boolean(this.options.ignoreManifest), Boolean(this.options.errorOnManifestCreate));
128
128
  this.commands = Object
129
129
  .entries(this.manifest.commands)
130
- .map(([id, c]) => ({ ...c, pluginAlias: this.alias, pluginType: this.type, load: async () => this.findCommand(id, { must: true }) }))
130
+ .map(([id, c]) => ({
131
+ ...c,
132
+ pluginAlias: this.alias,
133
+ pluginType: c.pluginType === 'jit' ? 'jit' : this.type,
134
+ load: async () => this.findCommand(id, { must: true }),
135
+ }))
131
136
  .sort((a, b) => a.id.localeCompare(b.id));
132
137
  }
133
138
  get topics() {
@@ -31,7 +31,7 @@ class CommandHelp extends formatter_1.HelpFormatter {
31
31
  v.name = k;
32
32
  return v;
33
33
  }), f => [!f.char, f.char, f.name]);
34
- const args = (cmd.args || []).filter(a => !a.hidden);
34
+ const args = (0, util_1.ensureArgArray)(cmd.args).filter(a => !a.hidden);
35
35
  const output = (0, util_1.compact)(this.sections().map(({ header, generate }) => {
36
36
  const body = generate({ cmd, flags, args }, header);
37
37
  // Generate can return a list of sections
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DocOpts = void 0;
4
+ const util_1 = require("../util");
4
5
  /**
5
6
  * DocOpts - See http://docopt.org/.
6
7
  *
@@ -75,7 +76,7 @@ class DocOpts {
75
76
  toString() {
76
77
  const opts = this.cmd.id === '.' || this.cmd.id === '' ? [] : ['<%= command.id %>'];
77
78
  if (this.cmd.args) {
78
- const a = this.cmd.args?.map(arg => `[${arg.name.toUpperCase()}]`) || [];
79
+ const a = (0, util_1.ensureArgArray)(this.cmd.args).map(arg => `[${arg.name.toUpperCase()}]`) || [];
79
80
  opts.push(...a);
80
81
  }
81
82
  try {
@@ -58,6 +58,16 @@ export interface Hooks {
58
58
  };
59
59
  return: unknown;
60
60
  };
61
+ 'jit_plugin_not_installed': {
62
+ options: {
63
+ id: string;
64
+ argv: string[];
65
+ command: Command.Loadable;
66
+ pluginName: string;
67
+ pluginVersion: string;
68
+ };
69
+ return: unknown;
70
+ };
61
71
  'plugins:preinstall': {
62
72
  options: {
63
73
  plugin: {
@@ -84,6 +94,7 @@ export declare namespace Hook {
84
94
  type Update = Hook<'update'>;
85
95
  type CommandNotFound = Hook<'command_not_found'>;
86
96
  type CommandIncomplete = Hook<'command_incomplete'>;
97
+ type JitPluginNotInstalled = Hook<'jit_plugin_not_installed'>;
87
98
  interface Context {
88
99
  config: Config;
89
100
  exit(code?: number): void;
@@ -27,6 +27,7 @@ export declare namespace PJSON {
27
27
  default?: string;
28
28
  plugins?: string[];
29
29
  devPlugins?: string[];
30
+ jitPlugins?: Record<string, string>;
30
31
  helpClass?: string;
31
32
  helpOptions?: HelpOptions;
32
33
  aliases?: {
@@ -83,6 +84,7 @@ export declare namespace PJSON {
83
84
  scope?: string;
84
85
  dirname?: string;
85
86
  flexibleTaxonomy?: boolean;
87
+ jitPlugins?: Record<string, string>;
86
88
  };
87
89
  }
88
90
  interface User extends PJSON {
@@ -13,6 +13,7 @@ export interface PluginOptions {
13
13
  }
14
14
  export interface Options extends PluginOptions {
15
15
  devPlugins?: boolean;
16
+ jitPlugins?: boolean;
16
17
  userPlugins?: boolean;
17
18
  channel?: string;
18
19
  version?: string;
package/lib/util.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ArgInput } from './interfaces';
1
2
  export declare function compact<T>(a: (T | undefined)[]): T[];
2
3
  export declare function uniqBy<T>(arr: T[], fn: (cur: T) => any): T[];
3
4
  type SortTypes = string | number | undefined | boolean;
@@ -7,4 +8,14 @@ export declare function isProd(): boolean;
7
8
  export declare function maxBy<T>(arr: T[], fn: (i: T) => number): T | undefined;
8
9
  export declare function sumBy<T>(arr: T[], fn: (i: T) => number): number;
9
10
  export declare function capitalize(s: string): string;
11
+ /**
12
+ * Ensure that the args are in an array instead of an object. This is required to ensure
13
+ * forwards compatibility with the new arg format in v2.
14
+ *
15
+ * @param args The args to ensure are in an array
16
+ * @returns ArgInput
17
+ */
18
+ export declare function ensureArgArray(args?: ArgInput | {
19
+ [name: string]: any;
20
+ }): ArgInput;
10
21
  export {};
package/lib/util.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.capitalize = exports.sumBy = exports.maxBy = exports.isProd = exports.castArray = exports.sortBy = exports.uniqBy = exports.compact = void 0;
3
+ exports.ensureArgArray = exports.capitalize = exports.sumBy = exports.maxBy = exports.isProd = exports.castArray = exports.sortBy = exports.uniqBy = exports.compact = void 0;
4
4
  function compact(a) {
5
5
  return a.filter((a) => Boolean(a));
6
6
  }
@@ -62,3 +62,14 @@ function capitalize(s) {
62
62
  return s ? s.charAt(0).toUpperCase() + s.slice(1).toLowerCase() : '';
63
63
  }
64
64
  exports.capitalize = capitalize;
65
+ /**
66
+ * Ensure that the args are in an array instead of an object. This is required to ensure
67
+ * forwards compatibility with the new arg format in v2.
68
+ *
69
+ * @param args The args to ensure are in an array
70
+ * @returns ArgInput
71
+ */
72
+ function ensureArgArray(args) {
73
+ return Array.isArray(args) ? args ?? [] : Object.entries(args ?? {}).map(([name, arg]) => ({ ...arg, name }));
74
+ }
75
+ exports.ensureArgArray = ensureArgArray;
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": "1.24.2",
4
+ "version": "1.25.0",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/core/issues",
7
7
  "dependencies": {