@oclif/core 2.4.0 → 2.5.1

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.
@@ -13,6 +13,8 @@ const util_3 = require("../util");
13
13
  const module_loader_1 = require("../module-loader");
14
14
  const help_1 = require("../help");
15
15
  const stream_1 = require("../cli-ux/stream");
16
+ const performance_1 = require("../performance");
17
+ const settings_1 = require("../settings");
16
18
  // eslint-disable-next-line new-cap
17
19
  const debug = (0, util_2.Debug)();
18
20
  const _pjson = (0, util_3.requireJson)(__dirname, '..', '..', 'package.json');
@@ -81,6 +83,7 @@ class Config {
81
83
  }
82
84
  // eslint-disable-next-line complexity
83
85
  async load() {
86
+ settings_1.settings.performanceEnabled = (settings_1.settings.performanceEnabled === undefined ? this.options.enablePerf : settings_1.settings.performanceEnabled) ?? false;
84
87
  const plugin = new Plugin.Plugin({ root: this.options.root });
85
88
  await plugin.load();
86
89
  this.plugins.push(plugin);
@@ -136,10 +139,19 @@ class Config {
136
139
  ...s3.templates && s3.templates.vanilla,
137
140
  },
138
141
  };
142
+ const marker = performance_1.Performance.mark('config.load');
139
143
  await this.loadPluginsAndCommands();
140
144
  debug('config done');
145
+ marker?.addDetails({
146
+ plugins: this.plugins.length,
147
+ commandPermutations: this.commands.length,
148
+ commands: this.plugins.reduce((acc, p) => acc + p.commands.length, 0),
149
+ topics: this.topics.length,
150
+ });
151
+ marker?.stop();
141
152
  }
142
153
  async loadPluginsAndCommands() {
154
+ const marker = performance_1.Performance.mark('config.loadPluginsAndCommands');
143
155
  await this.loadUserPlugins();
144
156
  await this.loadDevPlugins();
145
157
  await this.loadCorePlugins();
@@ -147,6 +159,7 @@ class Config {
147
159
  this.loadCommands(plugin);
148
160
  this.loadTopics(plugin);
149
161
  }
162
+ marker?.stop();
150
163
  }
151
164
  async loadCorePlugins() {
152
165
  if (this.pjson.oclif.plugins) {
@@ -189,6 +202,7 @@ class Config {
189
202
  }
190
203
  }
191
204
  async runHook(event, opts, timeout, captureErrors) {
205
+ const marker = performance_1.Performance.mark(`config.runHook#${event}`);
192
206
  debug('start %s hook', event);
193
207
  const search = (m) => {
194
208
  if (typeof m === 'function')
@@ -233,6 +247,7 @@ class Config {
233
247
  };
234
248
  const hooks = p.hooks[event] || [];
235
249
  for (const hook of hooks) {
250
+ const marker = performance_1.Performance.mark(`config.runHook#${p.name}(${hook})`);
236
251
  try {
237
252
  /* eslint-disable no-await-in-loop */
238
253
  const { isESM, module, filePath } = await module_loader_1.default.loadWithData(p, hook);
@@ -252,13 +267,21 @@ class Config {
252
267
  if (!captureErrors && error.oclif?.exit !== undefined)
253
268
  throw error;
254
269
  }
270
+ marker?.addDetails({
271
+ plugin: p.name,
272
+ event,
273
+ hook,
274
+ });
275
+ marker?.stop();
255
276
  }
256
277
  });
257
278
  await Promise.all(promises);
258
279
  debug('%s hook done', event);
280
+ marker?.stop();
259
281
  return final;
260
282
  }
261
283
  async runCommand(id, argv = [], cachedCommand = null) {
284
+ const marker = performance_1.Performance.mark(`config.runCommand#${id}`);
262
285
  debug('runCommand %s %o', id, argv);
263
286
  let c = cachedCommand ?? this.findCommand(id);
264
287
  if (!c) {
@@ -302,6 +325,8 @@ class Config {
302
325
  await this.runHook('prerun', { Command: command, argv });
303
326
  const result = (await command.run(argv, this));
304
327
  await this.runHook('postrun', { Command: command, result, argv });
328
+ marker?.addDetails({ command: id, plugin: c.pluginName });
329
+ marker?.stop();
305
330
  return result;
306
331
  }
307
332
  scopedEnvVar(k) {
@@ -453,6 +478,7 @@ class Config {
453
478
  async loadPlugins(root, type, plugins, parent) {
454
479
  if (!plugins || plugins.length === 0)
455
480
  return;
481
+ const mark = performance_1.Performance.mark(`config.loadPlugins#${type}`);
456
482
  debug('loading plugins', plugins);
457
483
  await Promise.all((plugins || []).map(async (plugin) => {
458
484
  try {
@@ -465,8 +491,18 @@ class Config {
465
491
  opts.tag = plugin.tag || opts.tag;
466
492
  opts.root = plugin.root || opts.root;
467
493
  }
494
+ const pluginMarker = performance_1.Performance.mark(`plugin.load#${opts.name}`);
468
495
  const instance = new Plugin.Plugin(opts);
469
496
  await instance.load();
497
+ pluginMarker?.addDetails({
498
+ hasManifest: instance.hasManifest,
499
+ commandCount: instance.commands.length,
500
+ topicCount: instance.topics.length,
501
+ type: instance.type,
502
+ usesMain: Boolean(instance.pjson.main),
503
+ name: instance.name,
504
+ });
505
+ pluginMarker?.stop();
470
506
  if (this.plugins.find(p => p.name === instance.name))
471
507
  return;
472
508
  this.plugins.push(instance);
@@ -482,6 +518,8 @@ class Config {
482
518
  this.warn(error, 'loadPlugins');
483
519
  }
484
520
  }));
521
+ mark?.addDetails({ pluginCount: plugins.length });
522
+ mark?.stop();
485
523
  }
486
524
  warn(err, scope) {
487
525
  if (this.warned)
@@ -538,6 +576,7 @@ class Config {
538
576
  return id;
539
577
  }
540
578
  loadCommands(plugin) {
579
+ const marker = performance_1.Performance.mark(`config.loadCommands#${plugin.name}`, { plugin: plugin.name });
541
580
  for (const command of plugin.commands) {
542
581
  if (this._commands.has(command.id)) {
543
582
  const prioritizedCommand = this.determinePriority([this._commands.get(command.id), command]);
@@ -564,8 +603,11 @@ class Config {
564
603
  }
565
604
  }
566
605
  }
606
+ marker?.addDetails({ commandCount: plugin.commands.length });
607
+ marker?.stop();
567
608
  }
568
609
  loadTopics(plugin) {
610
+ const marker = performance_1.Performance.mark(`config.loadTopics#${plugin.name}`, { plugin: plugin.name });
569
611
  for (const topic of (0, util_2.compact)(plugin.topics)) {
570
612
  const existing = this._topics.get(topic.name);
571
613
  if (existing) {
@@ -591,6 +633,7 @@ class Config {
591
633
  parts.pop();
592
634
  }
593
635
  }
636
+ marker?.stop();
594
637
  }
595
638
  /**
596
639
  * This method is responsible for locating the correct plugin to use for a named command id
@@ -23,6 +23,7 @@ export declare class Plugin implements IPlugin {
23
23
  alreadyLoaded: boolean;
24
24
  parent: Plugin | undefined;
25
25
  children: Plugin[];
26
+ hasManifest: boolean;
26
27
  protected _debug: (..._: any) => void;
27
28
  protected warned: boolean;
28
29
  constructor(options: PluginOptions);
@@ -11,6 +11,7 @@ const ts_node_1 = require("./ts-node");
11
11
  const util_3 = require("./util");
12
12
  const util_4 = require("../util");
13
13
  const module_loader_1 = require("../module-loader");
14
+ const performance_1 = require("../performance");
14
15
  const _pjson = (0, util_4.requireJson)(__dirname, '..', '..', 'package.json');
15
16
  function topicsToArray(input, base) {
16
17
  if (!input)
@@ -94,6 +95,7 @@ class Plugin {
94
95
  this.valid = false;
95
96
  this.alreadyLoaded = false;
96
97
  this.children = [];
98
+ this.hasManifest = false;
97
99
  // eslint-disable-next-line new-cap
98
100
  this._debug = (0, util_2.Debug)();
99
101
  this.warned = false;
@@ -144,6 +146,7 @@ class Plugin {
144
146
  get commandIDs() {
145
147
  if (!this.commandsDir)
146
148
  return [];
149
+ const marker = performance_1.Performance.mark(`plugin.commandIDs#${this.name}`, { plugin: this.name });
147
150
  this._debug(`loading IDs from ${this.commandsDir}`);
148
151
  const patterns = [
149
152
  '**/*.+(js|cjs|mjs|ts|tsx)',
@@ -158,9 +161,12 @@ class Plugin {
158
161
  return id === '' ? '.' : id;
159
162
  });
160
163
  this._debug('found commands', ids);
164
+ marker?.addDetails({ count: ids.length });
165
+ marker?.stop();
161
166
  return ids;
162
167
  }
163
168
  async findCommand(id, opts = {}) {
169
+ const marker = performance_1.Performance.mark(`plugin.findCommand#${this.name}.${id}`, { id, plugin: this.name });
164
170
  const fetch = async () => {
165
171
  if (!this.commandsDir)
166
172
  return;
@@ -193,6 +199,7 @@ class Plugin {
193
199
  const cmd = await fetch();
194
200
  if (!cmd && opts.must)
195
201
  (0, errors_1.error)(`command ${id} not found`);
202
+ marker?.stop();
196
203
  return cmd;
197
204
  }
198
205
  async _manifest(ignoreManifest, errorOnManifestCreate = false) {
@@ -205,6 +212,7 @@ class Plugin {
205
212
  }
206
213
  else {
207
214
  this._debug('using manifest from', p);
215
+ this.hasManifest = true;
208
216
  return manifest;
209
217
  }
210
218
  }
@@ -218,12 +226,16 @@ class Plugin {
218
226
  }
219
227
  }
220
228
  };
229
+ const marker = performance_1.Performance.mark(`plugin.manifest#${this.name}`, { plugin: this.name });
221
230
  if (!ignoreManifest) {
222
231
  const manifest = await readManifest();
223
- if (manifest)
232
+ if (manifest) {
233
+ marker?.addDetails({ fromCache: true, commandCount: Object.keys(manifest.commands).length });
234
+ marker?.stop();
224
235
  return manifest;
236
+ }
225
237
  }
226
- return {
238
+ const manifest = {
227
239
  version: this.version,
228
240
  commands: (await Promise.all(this.commandIDs.map(async (id) => {
229
241
  try {
@@ -243,6 +255,9 @@ class Plugin {
243
255
  return commands;
244
256
  }, {}),
245
257
  };
258
+ marker?.addDetails({ fromCache: false, commandCount: Object.keys(manifest.commands).length });
259
+ marker?.stop();
260
+ return manifest;
246
261
  }
247
262
  warn(err, scope) {
248
263
  if (this.warned)
package/lib/index.d.ts CHANGED
@@ -13,5 +13,6 @@ import { settings, Settings } from './settings';
13
13
  import { HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable } from './help/formatter';
14
14
  import * as ux from './cli-ux';
15
15
  import { stderr, stdout } from './cli-ux/stream';
16
+ import { Performance } from './performance';
16
17
  declare const flush: typeof ux.ux.flush;
17
- export { Args, Command, CommandHelp, Config, Errors, Flags, loadHelpClass, Help, HelpBase, HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable, Hook, Interfaces, Parser, Plugin, run, toCached, tsPath, toStandardizedId, toConfiguredId, settings, Settings, flush, ux, execute, stderr, stdout, };
18
+ export { Args, Command, CommandHelp, Config, Errors, Flags, loadHelpClass, Help, HelpBase, HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable, Hook, Interfaces, Parser, Plugin, Performance, run, toCached, tsPath, toStandardizedId, toConfiguredId, settings, Settings, flush, ux, execute, stderr, stdout, };
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.stdout = exports.stderr = exports.execute = exports.ux = exports.flush = exports.settings = exports.toConfiguredId = exports.toStandardizedId = exports.tsPath = exports.toCached = exports.run = exports.Plugin = exports.Parser = exports.Interfaces = exports.HelpBase = exports.Help = exports.loadHelpClass = exports.Flags = exports.Errors = exports.Config = exports.CommandHelp = exports.Command = exports.Args = void 0;
3
+ exports.stdout = exports.stderr = exports.execute = exports.ux = exports.flush = exports.settings = exports.toConfiguredId = exports.toStandardizedId = exports.tsPath = exports.toCached = exports.run = exports.Performance = exports.Plugin = exports.Parser = exports.Interfaces = exports.HelpBase = exports.Help = exports.loadHelpClass = exports.Flags = exports.Errors = exports.Config = exports.CommandHelp = exports.Command = exports.Args = void 0;
4
4
  const semver = require("semver");
5
5
  const command_1 = require("./command");
6
6
  Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return command_1.Command; } });
@@ -38,6 +38,8 @@ const util_2 = require("./util");
38
38
  const stream_1 = require("./cli-ux/stream");
39
39
  Object.defineProperty(exports, "stderr", { enumerable: true, get: function () { return stream_1.stderr; } });
40
40
  Object.defineProperty(exports, "stdout", { enumerable: true, get: function () { return stream_1.stdout; } });
41
+ const performance_1 = require("./performance");
42
+ Object.defineProperty(exports, "Performance", { enumerable: true, get: function () { return performance_1.Performance; } });
41
43
  const flush = ux.flush;
42
44
  exports.flush = flush;
43
45
  function checkCWD() {
@@ -17,6 +17,7 @@ export interface Options extends PluginOptions {
17
17
  userPlugins?: boolean;
18
18
  channel?: string;
19
19
  version?: string;
20
+ enablePerf?: boolean;
20
21
  }
21
22
  export interface Plugin {
22
23
  /**
package/lib/main.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as Interfaces from './interfaces';
2
2
  import { Config } from './config';
3
3
  export declare const helpAddition: (argv: string[], config: Interfaces.Config) => boolean;
4
4
  export declare const versionAddition: (argv: string[], config?: Interfaces.Config) => boolean;
5
- export declare function run(argv?: string[], options?: Interfaces.LoadOptions): Promise<void>;
5
+ export declare function run(argv?: string[], options?: Interfaces.LoadOptions): Promise<unknown>;
6
6
  /**
7
7
  * Load and run oclif CLI
8
8
  *
package/lib/main.js CHANGED
@@ -10,6 +10,7 @@ const settings_1 = require("./settings");
10
10
  const _1 = require(".");
11
11
  const path_1 = require("path");
12
12
  const stream_1 = require("./cli-ux/stream");
13
+ const performance_1 = require("./performance");
13
14
  const log = (message = '', ...args) => {
14
15
  message = typeof message === 'string' ? message : (0, util_1.inspect)(message);
15
16
  stream_1.stdout.write((0, util_1.format)(message, ...args) + '\n');
@@ -36,6 +37,14 @@ const versionAddition = (argv, config) => {
36
37
  };
37
38
  exports.versionAddition = versionAddition;
38
39
  async function run(argv, options) {
40
+ const marker = performance_1.Performance.mark('main.run');
41
+ const initMarker = performance_1.Performance.mark('main.run#init');
42
+ const collectPerf = async () => {
43
+ marker?.stop();
44
+ initMarker?.stop();
45
+ await performance_1.Performance.collect();
46
+ performance_1.Performance.debug();
47
+ };
39
48
  argv = argv ?? process.argv.slice(2);
40
49
  // Handle the case when a file URL string or URL is passed in such as 'import.meta.url'; covert to file path.
41
50
  if (options && ((typeof options === 'string' && options.startsWith('file://')) || options instanceof url_2.URL)) {
@@ -48,6 +57,7 @@ async function run(argv, options) {
48
57
  // display version if applicable
49
58
  if ((0, exports.versionAddition)(argv, config)) {
50
59
  log(config.userAgent);
60
+ await collectPerf();
51
61
  return;
52
62
  }
53
63
  // display help version if applicable
@@ -55,6 +65,7 @@ async function run(argv, options) {
55
65
  const Help = await (0, help_1.loadHelpClass)(config);
56
66
  const help = new Help(config, config.pjson.helpOptions);
57
67
  await help.showHelp(argv);
68
+ await collectPerf();
58
69
  return;
59
70
  }
60
71
  // find & run command
@@ -68,12 +79,18 @@ async function run(argv, options) {
68
79
  argvSlice = argv;
69
80
  }
70
81
  }
82
+ initMarker?.stop();
71
83
  // If the the default command is '.' (signifying that the CLI is a single command CLI) and '.' is provided
72
84
  // as an argument, we need to add back the '.' to argv since it was stripped out earlier as part of the
73
85
  // command id.
74
86
  if (config.pjson.oclif.default === '.' && id === '.' && argv[0] === '.')
75
87
  argvSlice = ['.', ...argvSlice];
76
- return config.runCommand(id, argvSlice, cmd);
88
+ try {
89
+ return await config.runCommand(id, argvSlice, cmd);
90
+ }
91
+ finally {
92
+ await collectPerf();
93
+ }
77
94
  }
78
95
  exports.run = run;
79
96
  function getTsConfigPath(dir, type) {
@@ -0,0 +1,63 @@
1
+ type Details = Record<string, string | boolean | number | string[]>;
2
+ type PerfResult = {
3
+ name: string;
4
+ duration: number;
5
+ details: Details;
6
+ module: string;
7
+ method: string | undefined;
8
+ scope: string | undefined;
9
+ };
10
+ type PerfHighlights = {
11
+ configLoadTime: number;
12
+ runTime: number;
13
+ initTime: number;
14
+ commandLoadTime: number;
15
+ pluginLoadTimes: Record<string, number>;
16
+ corePluginsLoadTime: number;
17
+ userPluginsLoadTime: number;
18
+ linkedPluginsLoadTime: number;
19
+ hookRunTimes: Record<string, Record<string, number>>;
20
+ };
21
+ declare class Marker {
22
+ name: string;
23
+ details: Details;
24
+ module: string;
25
+ method: string;
26
+ scope: string;
27
+ stopped: boolean;
28
+ private startMarker;
29
+ private stopMarker;
30
+ constructor(name: string, details?: Details);
31
+ addDetails(details: Details): void;
32
+ stop(): void;
33
+ measure(): void;
34
+ }
35
+ export declare class Performance {
36
+ private static markers;
37
+ private static _results;
38
+ private static _highlights;
39
+ static get enabled(): boolean;
40
+ static get results(): PerfResult[];
41
+ static getResult(name: string): PerfResult | undefined;
42
+ static get highlights(): PerfHighlights;
43
+ /**
44
+ * Add a new performance marker
45
+ *
46
+ * @param name Name of the marker. Use `module.method#scope` format
47
+ * @param details Arbitrary details to attach to the marker
48
+ * @returns Marker instance
49
+ */
50
+ static mark(name: string, details?: Details): Marker | undefined;
51
+ /**
52
+ * Collect performance results into static Performance.results
53
+ *
54
+ * @returns Promise<void>
55
+ */
56
+ static collect(): Promise<void>;
57
+ /**
58
+ * Add debug logs for plugin loading performance
59
+ * @returns void
60
+ */
61
+ static debug(): void;
62
+ }
63
+ export {};
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Performance = void 0;
4
+ const perf_hooks_1 = require("perf_hooks");
5
+ const settings_1 = require("./settings");
6
+ class Marker {
7
+ constructor(name, details = {}) {
8
+ this.name = name;
9
+ this.details = details;
10
+ this.stopped = false;
11
+ this.startMarker = `${this.name}-start`;
12
+ this.stopMarker = `${this.name}-stop`;
13
+ const [caller, scope] = name.split('#');
14
+ const [module, method] = caller.split('.');
15
+ this.module = module;
16
+ this.method = method;
17
+ this.scope = scope;
18
+ perf_hooks_1.performance.mark(this.startMarker);
19
+ }
20
+ addDetails(details) {
21
+ this.details = { ...this.details, ...details };
22
+ }
23
+ stop() {
24
+ this.stopped = true;
25
+ perf_hooks_1.performance.mark(this.stopMarker);
26
+ }
27
+ measure() {
28
+ perf_hooks_1.performance.measure(this.name, this.startMarker, this.stopMarker);
29
+ }
30
+ }
31
+ class Performance {
32
+ static get enabled() {
33
+ return settings_1.settings.performanceEnabled ?? false;
34
+ }
35
+ static get results() {
36
+ if (Performance._results.length > 0)
37
+ return Performance._results;
38
+ throw new Error('Perf results not available. Did you forget to call await Performance.collect()?');
39
+ }
40
+ static getResult(name) {
41
+ return Performance.results.find(r => r.name === name);
42
+ }
43
+ static get highlights() {
44
+ if (Performance._highlights)
45
+ return Performance._highlights;
46
+ throw new Error('Perf results not available. Did you forget to call await Performance.collect()?');
47
+ }
48
+ /**
49
+ * Add a new performance marker
50
+ *
51
+ * @param name Name of the marker. Use `module.method#scope` format
52
+ * @param details Arbitrary details to attach to the marker
53
+ * @returns Marker instance
54
+ */
55
+ static mark(name, details = {}) {
56
+ if (!Performance.enabled)
57
+ return;
58
+ const marker = new Marker(name, details);
59
+ Performance.markers[marker.name] = marker;
60
+ return marker;
61
+ }
62
+ /**
63
+ * Collect performance results into static Performance.results
64
+ *
65
+ * @returns Promise<void>
66
+ */
67
+ static async collect() {
68
+ if (!Performance.enabled)
69
+ return;
70
+ if (Performance._results.length > 0)
71
+ return;
72
+ for (const marker of Object.values(Performance.markers).filter(m => !m.stopped)) {
73
+ marker.stop();
74
+ }
75
+ return new Promise(resolve => {
76
+ const perfObserver = new perf_hooks_1.PerformanceObserver(items => {
77
+ for (const entry of items.getEntries()) {
78
+ if (Performance.markers[entry.name]) {
79
+ const marker = Performance.markers[entry.name];
80
+ Performance._results.push({
81
+ name: entry.name,
82
+ module: marker.module,
83
+ method: marker.method,
84
+ scope: marker.scope,
85
+ duration: entry.duration,
86
+ details: marker.details,
87
+ });
88
+ }
89
+ }
90
+ const command = Performance.results.find(r => r.name.startsWith('config.runCommand'));
91
+ const commandLoadTime = command ? Performance.getResult(`plugin.findCommand#${command.details.plugin}.${command.details.command}`)?.duration ?? 0 : 0;
92
+ const pluginLoadTimes = Object.fromEntries(Performance.results
93
+ .filter(({ name }) => name.startsWith('plugin.load#'))
94
+ .sort((a, b) => b.duration - a.duration)
95
+ .map(({ scope, duration }) => [scope, duration]));
96
+ const hookRunTimes = Performance.results
97
+ .filter(({ name }) => name.startsWith('config.runHook#'))
98
+ .reduce((acc, perfResult) => {
99
+ const event = perfResult.details.event;
100
+ if (event) {
101
+ if (!acc[event])
102
+ acc[event] = {};
103
+ acc[event][perfResult.scope] = perfResult.duration;
104
+ }
105
+ else {
106
+ const event = perfResult.scope;
107
+ if (!acc[event])
108
+ acc[event] = {};
109
+ acc[event].total = perfResult.duration;
110
+ }
111
+ return acc;
112
+ }, {});
113
+ const pluginLoadTimeByType = Object.fromEntries(Performance.results
114
+ .filter(({ name }) => name.startsWith('config.loadPlugins#'))
115
+ .sort((a, b) => b.duration - a.duration)
116
+ .map(({ scope, duration }) => [scope, duration]));
117
+ Performance._highlights = {
118
+ configLoadTime: Performance.getResult('config.load')?.duration ?? 0,
119
+ runTime: Performance.getResult('main.run')?.duration ?? 0,
120
+ initTime: Performance.getResult('main.run#init')?.duration ?? 0,
121
+ commandLoadTime,
122
+ pluginLoadTimes,
123
+ hookRunTimes,
124
+ corePluginsLoadTime: pluginLoadTimeByType.core ?? 0,
125
+ userPluginsLoadTime: pluginLoadTimeByType.user ?? 0,
126
+ linkedPluginsLoadTime: pluginLoadTimeByType.link ?? 0,
127
+ };
128
+ resolve();
129
+ });
130
+ perfObserver.observe({ entryTypes: ['measure'], buffered: true });
131
+ for (const marker of Object.values(Performance.markers)) {
132
+ try {
133
+ marker.measure();
134
+ }
135
+ catch {
136
+ // ignore
137
+ }
138
+ }
139
+ perf_hooks_1.performance.clearMarks();
140
+ });
141
+ }
142
+ /**
143
+ * Add debug logs for plugin loading performance
144
+ * @returns void
145
+ */
146
+ static debug() {
147
+ if (!Performance.enabled)
148
+ return;
149
+ const debug = require('debug')('perf');
150
+ debug('Init Time: %sms', Performance.highlights.initTime.toFixed(4));
151
+ debug('Config Load Time: %sms', Performance.highlights.configLoadTime.toFixed(4));
152
+ debug('Command Load Time: %sms', Performance.highlights.commandLoadTime.toFixed(4));
153
+ debug('Execution Time: %sms', Performance.highlights.runTime.toFixed(4));
154
+ debug('Core Plugin Load Time: %sms', Performance.highlights.corePluginsLoadTime.toFixed(4));
155
+ debug('User Plugin Load Time: %sms', Performance.highlights.userPluginsLoadTime.toFixed(4));
156
+ debug('Linked Plugin Load Time: %sms', Performance.highlights.linkedPluginsLoadTime.toFixed(4));
157
+ debug('Plugin Load Times:');
158
+ for (const [plugin, duration] of Object.entries(Performance.highlights.pluginLoadTimes)) {
159
+ debug(` ${plugin}: ${duration.toFixed(4)}ms`);
160
+ }
161
+ debug('Hook Run Times:');
162
+ for (const [event, runTimes] of Object.entries(Performance.highlights.hookRunTimes)) {
163
+ debug(` ${event}:`);
164
+ for (const [plugin, duration] of Object.entries(runTimes)) {
165
+ debug(` ${plugin}: ${duration.toFixed(4)}ms`);
166
+ }
167
+ }
168
+ }
169
+ }
170
+ exports.Performance = Performance;
171
+ Performance.markers = {};
172
+ Performance._results = [];
package/lib/settings.d.ts CHANGED
@@ -30,5 +30,10 @@ export type Settings = {
30
30
  * NODE_ENV=development
31
31
  */
32
32
  tsnodeEnabled?: boolean;
33
+ /**
34
+ * Enable performance tracking. Resulting data is available in the `perf` property of the `Config` class.
35
+ * This will be overridden by the `enablePerf` property passed into Config constructor.
36
+ */
37
+ performanceEnabled?: boolean;
33
38
  };
34
39
  export declare const settings: Settings;
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.4.0",
4
+ "version": "2.5.1",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/core/issues",
7
7
  "dependencies": {
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "devDependencies": {
38
38
  "@commitlint/config-conventional": "^12.1.4",
39
- "@oclif/plugin-help": "^5.2.5",
39
+ "@oclif/plugin-help": "^5.2.7",
40
40
  "@oclif/plugin-plugins": "^2.3.2",
41
41
  "@oclif/test": "^2.3.7",
42
42
  "@types/ansi-styles": "^3.2.1",