@oclif/core 0.5.32 → 0.5.36

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/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.5.36](https://github.com/oclif/core/compare/v0.5.35...v0.5.36) (2021-09-14)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * move ctor for command help class to its own function ([#244](https://github.com/oclif/core/issues/244)) ([26f2445](https://github.com/oclif/core/commit/26f24457c71276c38f86821c2b1498ecb8e4e2a4))
11
+
12
+ ### [0.5.35](https://github.com/oclif/core/compare/v0.5.34...v0.5.35) (2021-09-08)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * clear hook timeout ([#243](https://github.com/oclif/core/issues/243)) ([0c32c65](https://github.com/oclif/core/commit/0c32c65c5c30b02bc3ea6e36b0598adfc5b23ec1))
18
+
19
+ ### [0.5.34](https://github.com/oclif/core/compare/v0.5.33...v0.5.34) (2021-08-30)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * add support all properties for a command class in manifest ([deb0765](https://github.com/oclif/core/commit/deb0765f81dbea54c831beba0b608b1a8cd0ecdb))
25
+
26
+ ### [0.5.33](https://github.com/oclif/core/compare/v0.5.32...v0.5.33) (2021-08-30)
27
+
28
+
29
+ ### Bug Fixes
30
+
31
+ * improve Hooks interface ([#234](https://github.com/oclif/core/issues/234)) ([32d0d62](https://github.com/oclif/core/commit/32d0d62ed30c65cdbca7c6da630b5542b38ab3b1))
32
+
5
33
  ### [0.5.32](https://github.com/oclif/core/compare/v0.5.31...v0.5.32) (2021-08-23)
6
34
 
7
35
 
@@ -1,6 +1,6 @@
1
1
  import { Options, Plugin as IPlugin } from '../interfaces/plugin';
2
2
  import { Config as IConfig, ArchTypes, PlatformTypes, LoadOptions } from '../interfaces/config';
3
- import { Command, PJSON, Topic } from '../interfaces';
3
+ import { Command, Hook, Hooks, PJSON, Topic } from '../interfaces';
4
4
  import * as Plugin from './plugin';
5
5
  export declare class Config implements IConfig {
6
6
  options: Options;
@@ -39,7 +39,7 @@ export declare class Config implements IConfig {
39
39
  loadCorePlugins(): Promise<void>;
40
40
  loadDevPlugins(): Promise<void>;
41
41
  loadUserPlugins(): Promise<void>;
42
- runHook<T>(event: string, opts: T): Promise<any>;
42
+ runHook<T extends keyof Hooks>(event: T, opts: Hooks[T]['options'], timeout?: number): Promise<Hook.Result<Hooks[T]['return']>>;
43
43
  runCommand<T = unknown>(id: string, argv?: string[], cachedCommand?: Command.Plugin): Promise<T>;
44
44
  scopedEnvVar(k: string): string | undefined;
45
45
  scopedEnvVarTrue(k: string): boolean;
@@ -127,7 +127,7 @@ class Config {
127
127
  }
128
128
  }
129
129
  }
130
- async runHook(event, opts) {
130
+ async runHook(event, opts, timeout) {
131
131
  debug('start %s hook', event);
132
132
  const search = (m) => {
133
133
  if (typeof m === 'function')
@@ -136,7 +136,20 @@ class Config {
136
136
  return m.default;
137
137
  return Object.values(m).find((m) => typeof m === 'function');
138
138
  };
139
- const results = [];
139
+ const withTimeout = async (ms, promise) => {
140
+ let id;
141
+ const timeout = new Promise((_, reject) => {
142
+ id = setTimeout(() => {
143
+ reject(new Error(`Timed out after ${ms} ms.`));
144
+ }, ms);
145
+ });
146
+ return Promise.race([promise, timeout]).then(result => {
147
+ clearTimeout(id);
148
+ return result;
149
+ });
150
+ };
151
+ const successes = [];
152
+ const failures = [];
140
153
  for (const p of this.plugins) {
141
154
  const debug = require('debug')([this.bin, p.name, 'hooks', event].join(':'));
142
155
  const context = {
@@ -161,11 +174,14 @@ class Config {
161
174
  /* eslint-disable no-await-in-loop */
162
175
  const { isESM, module, filePath } = await module_loader_1.default.loadWithData(p, hook);
163
176
  debug('start', isESM ? '(import)' : '(require)', filePath);
164
- const result = await search(module).call(context, Object.assign(Object.assign({}, opts), { config: this }));
165
- results.push(result);
177
+ const result = timeout ?
178
+ await withTimeout(timeout, search(module).call(context, Object.assign(Object.assign({}, opts), { config: this }))) :
179
+ await search(module).call(context, Object.assign(Object.assign({}, opts), { config: this }));
180
+ successes.push({ plugin: p, result });
166
181
  debug('done');
167
182
  }
168
183
  catch (error) {
184
+ failures.push({ plugin: p, error: error });
169
185
  if (error && error.oclif && error.oclif.exit !== undefined)
170
186
  throw error;
171
187
  this.warn(error, `runHook ${event}`);
@@ -173,7 +189,7 @@ class Config {
173
189
  }
174
190
  }
175
191
  debug('%s hook done', event);
176
- return results;
192
+ return { successes, failures };
177
193
  }
178
194
  async runCommand(id, argv = [], cachedCommand) {
179
195
  debug('runCommand %s %o', id, argv);
@@ -485,7 +501,7 @@ async function toCached(c, plugin) {
485
501
  hidden: a.hidden,
486
502
  }));
487
503
  const args = await Promise.all(argsPromise);
488
- return {
504
+ const stdProperties = {
489
505
  id: c.id,
490
506
  summary: c.summary,
491
507
  description: c.description,
@@ -501,5 +517,14 @@ async function toCached(c, plugin) {
501
517
  flags,
502
518
  args,
503
519
  };
520
+ // do not include these properties in manifest
521
+ const ignoreCommandProperties = ['plugin', '_flags'];
522
+ const stdKeys = Object.keys(stdProperties);
523
+ const keysToAdd = Object.keys(c).filter(property => ![...stdKeys, ...ignoreCommandProperties].includes(property));
524
+ const additionalProperties = {};
525
+ keysToAdd.forEach(key => {
526
+ additionalProperties[key] = c[key];
527
+ });
528
+ return Object.assign(Object.assign({}, stdProperties), additionalProperties);
504
529
  }
505
530
  exports.toCached = toCached;
@@ -1,15 +1,6 @@
1
1
  import * as Interfaces from '../interfaces';
2
2
  import { Example } from '../interfaces/command';
3
- import { HelpFormatter } from './formatter';
4
- export declare type HelpSection = {
5
- header: string;
6
- body: string | [string, string | undefined][] | undefined;
7
- } | undefined;
8
- export declare type HelpSectionRenderer = (data: {
9
- cmd: Interfaces.Command;
10
- flags: Interfaces.Command.Flag[];
11
- args: Interfaces.Command.Arg[];
12
- }, header: string) => HelpSection[] | string | undefined;
3
+ import { HelpFormatter, HelpSectionRenderer } from './formatter';
13
4
  export declare class CommandHelp extends HelpFormatter {
14
5
  command: Interfaces.Command;
15
6
  config: Interfaces.Config;
@@ -1,4 +1,17 @@
1
1
  import * as Interfaces from '../interfaces';
2
+ export declare type HelpSectionKeyValueTable = {
3
+ name: string;
4
+ description: string;
5
+ }[];
6
+ export declare type HelpSection = {
7
+ header: string;
8
+ body: string | HelpSectionKeyValueTable | [string, string | undefined][] | undefined;
9
+ } | undefined;
10
+ export declare type HelpSectionRenderer = (data: {
11
+ cmd: Interfaces.Command;
12
+ flags: Interfaces.Command.Flag[];
13
+ args: Interfaces.Command.Arg[];
14
+ }, header: string) => HelpSection | HelpSection[] | string | undefined;
2
15
  export declare class HelpFormatter {
3
16
  indentSpacing: number;
4
17
  /**
@@ -78,5 +91,5 @@ export declare class HelpFormatter {
78
91
  stripAnsi?: boolean;
79
92
  spacer?: string;
80
93
  }): string;
81
- section(header: string, body: string | [string, string | undefined][]): string;
94
+ section(header: string, body: string | HelpSection | HelpSectionKeyValueTable | [string, string | undefined][]): string;
82
95
  }
@@ -148,10 +148,31 @@ class HelpFormatter {
148
148
  }
149
149
  section(header, body) {
150
150
  // Always render template strings with the provided render function before wrapping and indenting
151
- body = Array.isArray(body) ? body.map(([left, right]) => ([this.render(left), right && this.render(right)])) : this.render(body);
151
+ let newBody;
152
+ if (typeof body === 'string') {
153
+ newBody = this.render(body);
154
+ }
155
+ else if (Array.isArray(body)) {
156
+ newBody = body.map(entry => {
157
+ if ('name' in entry) {
158
+ const tableEntry = entry;
159
+ return ([this.render(tableEntry.name), this.render(tableEntry.description)]);
160
+ }
161
+ const [left, right] = entry;
162
+ return ([this.render(left), right && this.render(right)]);
163
+ });
164
+ }
165
+ else if ('header' in body) {
166
+ return this.section(body.header, body.body);
167
+ }
168
+ else {
169
+ newBody = body
170
+ .map((entry) => ([entry.name, entry.description]))
171
+ .map(([left, right]) => ([this.render(left), right && this.render(right)]));
172
+ }
152
173
  const output = [
153
174
  bold(header),
154
- this.indent(Array.isArray(body) ? this.renderList(body, { stripAnsi: this.opts.stripAnsi, indentation: 2 }) : body),
175
+ this.indent(Array.isArray(newBody) ? this.renderList(newBody, { stripAnsi: this.opts.stripAnsi, indentation: 2 }) : newBody),
155
176
  ].join('\n');
156
177
  return this.opts.stripAnsi ? stripAnsi(output) : output;
157
178
  }
@@ -30,6 +30,7 @@ export declare class Help extends HelpBase {
30
30
  protected showTopicHelp(topic: Interfaces.Topic): Promise<void>;
31
31
  protected formatRoot(): string;
32
32
  protected formatCommand(command: Interfaces.Command): string;
33
+ protected getCommandHelpClass(command: Interfaces.Command): CommandHelp;
33
34
  protected formatCommands(commands: Interfaces.Command[]): string;
34
35
  protected summary(c: Interfaces.Command): string | undefined;
35
36
  protected description(c: Interfaces.Command): string;
package/lib/help/index.js CHANGED
@@ -73,6 +73,7 @@ class Help extends HelpBase {
73
73
  return topics;
74
74
  }
75
75
  async showHelp(argv) {
76
+ argv = argv.filter(arg => !getHelpFlagAdditions(this.config).includes(arg));
76
77
  if (this.config.topicSeparator !== ':')
77
78
  argv = util_2.standardizeIDFromArgv(argv, this.config);
78
79
  const subject = getHelpSubject(argv, this.config);
@@ -172,9 +173,12 @@ class Help extends HelpBase {
172
173
  command.id = command.id.replace(/:/g, this.config.topicSeparator);
173
174
  command.aliases = command.aliases && command.aliases.map(a => a.replace(/:/g, this.config.topicSeparator));
174
175
  }
175
- const help = new this.CommandHelpClass(command, this.config, this.opts);
176
+ const help = this.getCommandHelpClass(command);
176
177
  return help.generate();
177
178
  }
179
+ getCommandHelpClass(command) {
180
+ return new this.CommandHelpClass(command, this.config, this.opts);
181
+ }
178
182
  formatCommands(commands) {
179
183
  if (commands.length === 0)
180
184
  return '';
package/lib/index.d.ts CHANGED
@@ -4,10 +4,11 @@ import { Config, Plugin, tsPath, toCached } from './config';
4
4
  import * as Interfaces from './interfaces';
5
5
  import * as Errors from './errors';
6
6
  import * as Flags from './flags';
7
- import { HelpBase, Help, loadHelpClass } from './help';
7
+ import { CommandHelp, HelpBase, Help, loadHelpClass } from './help';
8
8
  import { toStandardizedId, toConfiguredId } from './help/util';
9
9
  import * as Parser from './parser';
10
10
  import { Hook } from './interfaces/hooks';
11
11
  import { settings, Settings } from './settings';
12
+ import { HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable } from './help/formatter';
12
13
  declare const flush: any;
13
- export { Command, Config, Errors, Flags, loadHelpClass, Help, HelpBase, Hook, Interfaces, Parser, Plugin, run, toCached, tsPath, toStandardizedId, toConfiguredId, settings, Settings, flush, };
14
+ export { Command, CommandHelp, Config, Errors, Flags, loadHelpClass, Help, HelpBase, HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable, Hook, Interfaces, Parser, Plugin, run, toCached, tsPath, toStandardizedId, toConfiguredId, settings, Settings, flush, };
package/lib/index.js CHANGED
@@ -18,6 +18,7 @@ exports.Errors = Errors;
18
18
  const Flags = require("./flags");
19
19
  exports.Flags = Flags;
20
20
  const help_1 = require("./help");
21
+ exports.CommandHelp = help_1.CommandHelp;
21
22
  exports.HelpBase = help_1.HelpBase;
22
23
  exports.Help = help_1.Help;
23
24
  exports.loadHelpClass = help_1.loadHelpClass;
@@ -1,5 +1,5 @@
1
1
  import { PJSON } from './pjson';
2
- import { Hooks } from './hooks';
2
+ import { Hooks, Hook } from './hooks';
3
3
  import { Command } from './command';
4
4
  import { Plugin, Options } from './plugin';
5
5
  import { Topic } from './topic';
@@ -92,7 +92,7 @@ export interface Config {
92
92
  readonly commandIDs: string[];
93
93
  runCommand<T = unknown>(id: string, argv?: string[]): Promise<T>;
94
94
  runCommand<T = unknown>(id: string, argv?: string[], cachedCommand?: Command.Plugin): Promise<T>;
95
- runHook<T extends Hooks, K extends Extract<keyof T, string>>(event: K, opts: T[K]): Promise<any>;
95
+ runHook<T extends keyof Hooks>(event: T, opts: Hooks[T]['options'], timeout?: number): Promise<Hook.Result<Hooks[T]['return']>>;
96
96
  findCommand(id: string, opts: {
97
97
  must: true;
98
98
  }): Command.Plugin;
@@ -1,53 +1,78 @@
1
1
  import { Command } from './command';
2
2
  import { Config } from './config';
3
+ import { Plugin } from './plugin';
4
+ interface HookMeta {
5
+ options: Record<string, unknown>;
6
+ return: any;
7
+ }
3
8
  export interface Hooks {
4
- [event: string]: object;
9
+ [event: string]: HookMeta;
5
10
  init: {
6
- id: string | undefined;
7
- argv: string[];
11
+ options: {
12
+ id: string | undefined;
13
+ argv: string[];
14
+ };
15
+ return: void;
8
16
  };
9
17
  prerun: {
10
- Command: Command.Class;
11
- argv: string[];
18
+ options: {
19
+ Command: Command.Class;
20
+ argv: string[];
21
+ };
22
+ return: void;
12
23
  };
13
24
  postrun: {
14
- Command: Command.Class;
15
- result?: any;
16
- argv: string[];
25
+ options: {
26
+ Command: Command.Class;
27
+ result?: any;
28
+ argv: string[];
29
+ };
30
+ return: void;
17
31
  };
18
32
  preupdate: {
19
- channel: string;
33
+ options: {
34
+ channel: string;
35
+ };
36
+ return: void;
20
37
  };
21
38
  update: {
22
- channel: string;
39
+ options: {
40
+ channel: string;
41
+ };
42
+ return: void;
23
43
  };
24
44
  'command_not_found': {
25
- id: string;
26
- argv?: string[];
45
+ options: {
46
+ id: string;
47
+ argv?: string[];
48
+ };
49
+ return: void;
27
50
  };
28
51
  'plugins:preinstall': {
29
- plugin: {
30
- name: string;
31
- tag: string;
32
- type: 'npm';
33
- } | {
34
- url: string;
35
- type: 'repo';
52
+ options: {
53
+ plugin: {
54
+ name: string;
55
+ tag: string;
56
+ type: 'npm';
57
+ } | {
58
+ url: string;
59
+ type: 'repo';
60
+ };
36
61
  };
62
+ return: void;
37
63
  };
38
64
  }
39
- export declare type HookKeyOrOptions<K> = K extends (keyof Hooks) ? Hooks[K] : K;
40
- export declare type Hook<T> = (this: Hook.Context, options: HookKeyOrOptions<T> & {
65
+ export declare type Hook<T extends keyof P, P extends Hooks = Hooks> = (this: Hook.Context, options: P[T]['options'] & {
41
66
  config: Config;
42
- }) => any;
67
+ }) => Promise<P[T]['return']>;
43
68
  export declare namespace Hook {
44
- type Init = Hook<Hooks['init']>;
45
- type PluginsPreinstall = Hook<Hooks['plugins:preinstall']>;
46
- type Prerun = Hook<Hooks['prerun']>;
47
- type Postrun = Hook<Hooks['postrun']>;
48
- type Preupdate = Hook<Hooks['preupdate']>;
49
- type Update = Hook<Hooks['update']>;
50
- type CommandNotFound = Hook<Hooks['command_not_found']>;
69
+ type Init = Hook<'init'>;
70
+ type PluginsPreinstall = Hook<'plugins:preinstall'>;
71
+ type Prerun = Hook<'prerun'>;
72
+ type Postrun = Hook<'postrun'>;
73
+ type Preupdate = Hook<'preupdate'>;
74
+ type Update = Hook<'update'>;
75
+ type CommandNotFound = Hook<'command_not_found'>;
51
76
  interface Context {
52
77
  config: Config;
53
78
  exit(code?: number): void;
@@ -59,4 +84,15 @@ export declare namespace Hook {
59
84
  log(message?: any, ...args: any[]): void;
60
85
  debug(...args: any[]): void;
61
86
  }
87
+ interface Result<T> {
88
+ successes: Array<{
89
+ result: T;
90
+ plugin: Plugin;
91
+ }>;
92
+ failures: Array<{
93
+ error: Error;
94
+ plugin: Plugin;
95
+ }>;
96
+ }
62
97
  }
98
+ export {};
@@ -3,7 +3,7 @@ export { Config, ArchTypes, PlatformTypes, LoadOptions } from './config';
3
3
  export { Command, Example } from './command';
4
4
  export { OclifError, PrettyPrintableError } from './errors';
5
5
  export { HelpOptions } from './help';
6
- export { Hook, HookKeyOrOptions, Hooks } from './hooks';
6
+ export { Hook, Hooks } from './hooks';
7
7
  export { Manifest } from './manifest';
8
8
  export { ParserArg, Arg, ParseFn, ParserOutput, ParserInput, ArgToken, OptionalArg, FlagOutput, OutputArgs, OutputFlags, FlagUsageOptions, CLIParseErrorOptions, ArgInput, RequiredArg, Metadata, ParsingToken, FlagToken, List, ListItem, BooleanFlag, Flag, FlagBase, OptionFlag, Input, EnumFlagOptions, DefaultContext, Default, Definition, CompletableOptionFlag, Completion, CompletionContext, FlagInput, CompletableFlag, } from './parser';
9
9
  export { PJSON } from './pjson';
package/lib/main.js CHANGED
@@ -49,7 +49,6 @@ async function run(argv = process.argv.slice(2), options) {
49
49
  }
50
50
  // display help version if applicable
51
51
  if (exports.helpAddition(argv, config)) {
52
- argv = argv.filter(arg => !help_1.getHelpFlagAdditions(config).includes(arg));
53
52
  const Help = await help_1.loadHelpClass(config);
54
53
  const help = new Help(config, config.pjson.helpOptions);
55
54
  await help.showHelp(argv);
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": "0.5.32",
4
+ "version": "0.5.36",
5
5
  "author": "Jeff Dickey @jdxcode",
6
6
  "bugs": "https://github.com/oclif/core/issues",
7
7
  "dependencies": {