@oclif/core 2.4.0 → 2.5.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/config/config.js +43 -0
- package/lib/config/plugin.d.ts +1 -0
- package/lib/config/plugin.js +17 -2
- package/lib/index.d.ts +2 -1
- package/lib/index.js +3 -1
- package/lib/interfaces/plugin.d.ts +1 -0
- package/lib/main.d.ts +1 -1
- package/lib/main.js +18 -1
- package/lib/performance.d.ts +62 -0
- package/lib/performance.js +165 -0
- package/lib/settings.d.ts +5 -0
- package/package.json +1 -1
package/lib/config/config.js
CHANGED
|
@@ -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
|
package/lib/config/plugin.d.ts
CHANGED
|
@@ -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);
|
package/lib/config/plugin.js
CHANGED
|
@@ -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
|
-
|
|
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() {
|
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<
|
|
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
|
-
|
|
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,62 @@
|
|
|
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
|
+
private startMarker;
|
|
28
|
+
private stopMarker;
|
|
29
|
+
constructor(name: string, details?: Details);
|
|
30
|
+
addDetails(details: Details): void;
|
|
31
|
+
stop(): void;
|
|
32
|
+
measure(): void;
|
|
33
|
+
}
|
|
34
|
+
export declare class Performance {
|
|
35
|
+
private static markers;
|
|
36
|
+
private static _results;
|
|
37
|
+
private static _highlights;
|
|
38
|
+
static get enabled(): boolean;
|
|
39
|
+
static get results(): PerfResult[];
|
|
40
|
+
static getResult(name: string): PerfResult | undefined;
|
|
41
|
+
static get highlights(): PerfHighlights;
|
|
42
|
+
/**
|
|
43
|
+
* Add a new performance marker
|
|
44
|
+
*
|
|
45
|
+
* @param name Name of the marker. Use `module.method#scope` format
|
|
46
|
+
* @param details Arbitrary details to attach to the marker
|
|
47
|
+
* @returns Marker instance
|
|
48
|
+
*/
|
|
49
|
+
static mark(name: string, details?: Details): Marker | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Collect performance results into static Performance.results
|
|
52
|
+
*
|
|
53
|
+
* @returns Promise<void>
|
|
54
|
+
*/
|
|
55
|
+
static collect(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Add debug logs for plugin loading performance
|
|
58
|
+
* @returns void
|
|
59
|
+
*/
|
|
60
|
+
static debug(): void;
|
|
61
|
+
}
|
|
62
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
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.startMarker = `${this.name}-start`;
|
|
11
|
+
this.stopMarker = `${this.name}-stop`;
|
|
12
|
+
const [caller, scope] = name.split('#');
|
|
13
|
+
const [module, method] = caller.split('.');
|
|
14
|
+
this.module = module;
|
|
15
|
+
this.method = method;
|
|
16
|
+
this.scope = scope;
|
|
17
|
+
perf_hooks_1.performance.mark(this.startMarker);
|
|
18
|
+
}
|
|
19
|
+
addDetails(details) {
|
|
20
|
+
this.details = { ...this.details, ...details };
|
|
21
|
+
}
|
|
22
|
+
stop() {
|
|
23
|
+
perf_hooks_1.performance.mark(this.stopMarker);
|
|
24
|
+
}
|
|
25
|
+
measure() {
|
|
26
|
+
perf_hooks_1.performance.measure(this.name, this.startMarker, this.stopMarker);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
class Performance {
|
|
30
|
+
static get enabled() {
|
|
31
|
+
return settings_1.settings.performanceEnabled ?? false;
|
|
32
|
+
}
|
|
33
|
+
static get results() {
|
|
34
|
+
if (Performance._results.length > 0)
|
|
35
|
+
return Performance._results;
|
|
36
|
+
throw new Error('Perf results not available. Did you forget to call await Performance.collect()?');
|
|
37
|
+
}
|
|
38
|
+
static getResult(name) {
|
|
39
|
+
return Performance.results.find(r => r.name === name);
|
|
40
|
+
}
|
|
41
|
+
static get highlights() {
|
|
42
|
+
if (Performance._highlights)
|
|
43
|
+
return Performance._highlights;
|
|
44
|
+
throw new Error('Perf results not available. Did you forget to call await Performance.collect()?');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Add a new performance marker
|
|
48
|
+
*
|
|
49
|
+
* @param name Name of the marker. Use `module.method#scope` format
|
|
50
|
+
* @param details Arbitrary details to attach to the marker
|
|
51
|
+
* @returns Marker instance
|
|
52
|
+
*/
|
|
53
|
+
static mark(name, details = {}) {
|
|
54
|
+
if (!Performance.enabled)
|
|
55
|
+
return;
|
|
56
|
+
const marker = new Marker(name, details);
|
|
57
|
+
Performance.markers[marker.name] = marker;
|
|
58
|
+
return marker;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Collect performance results into static Performance.results
|
|
62
|
+
*
|
|
63
|
+
* @returns Promise<void>
|
|
64
|
+
*/
|
|
65
|
+
static async collect() {
|
|
66
|
+
if (!Performance.enabled)
|
|
67
|
+
return;
|
|
68
|
+
return new Promise(resolve => {
|
|
69
|
+
const perfObserver = new perf_hooks_1.PerformanceObserver(items => {
|
|
70
|
+
for (const entry of items.getEntries()) {
|
|
71
|
+
if (Performance.markers[entry.name]) {
|
|
72
|
+
const marker = Performance.markers[entry.name];
|
|
73
|
+
Performance._results.push({
|
|
74
|
+
name: entry.name,
|
|
75
|
+
module: marker.module,
|
|
76
|
+
method: marker.method,
|
|
77
|
+
scope: marker.scope,
|
|
78
|
+
duration: entry.duration,
|
|
79
|
+
details: marker.details,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const command = Performance.results.find(r => r.name.startsWith('config.runCommand'));
|
|
84
|
+
const commandLoadTime = command ? Performance.getResult(`plugin.findCommand#${command.details.plugin}.${command.details.command}`)?.duration ?? 0 : 0;
|
|
85
|
+
const pluginLoadTimes = Object.fromEntries(Performance.results
|
|
86
|
+
.filter(({ name }) => name.startsWith('plugin.load#'))
|
|
87
|
+
.sort((a, b) => b.duration - a.duration)
|
|
88
|
+
.map(({ scope, duration }) => [scope, duration]));
|
|
89
|
+
const hookRunTimes = Performance.results
|
|
90
|
+
.filter(({ name }) => name.startsWith('config.runHook#'))
|
|
91
|
+
.reduce((acc, perfResult) => {
|
|
92
|
+
const event = perfResult.details.event;
|
|
93
|
+
if (event) {
|
|
94
|
+
if (!acc[event])
|
|
95
|
+
acc[event] = {};
|
|
96
|
+
acc[event][perfResult.scope] = perfResult.duration;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const event = perfResult.scope;
|
|
100
|
+
if (!acc[event])
|
|
101
|
+
acc[event] = {};
|
|
102
|
+
acc[event].total = perfResult.duration;
|
|
103
|
+
}
|
|
104
|
+
return acc;
|
|
105
|
+
}, {});
|
|
106
|
+
const pluginLoadTimeByType = Object.fromEntries(Performance.results
|
|
107
|
+
.filter(({ name }) => name.startsWith('config.loadPlugins#'))
|
|
108
|
+
.sort((a, b) => b.duration - a.duration)
|
|
109
|
+
.map(({ scope, duration }) => [scope, duration]));
|
|
110
|
+
Performance._highlights = {
|
|
111
|
+
configLoadTime: Performance.getResult('config.load')?.duration ?? 0,
|
|
112
|
+
runTime: Performance.getResult('main.run')?.duration ?? 0,
|
|
113
|
+
initTime: Performance.getResult('main.run#init')?.duration ?? 0,
|
|
114
|
+
commandLoadTime,
|
|
115
|
+
pluginLoadTimes,
|
|
116
|
+
hookRunTimes,
|
|
117
|
+
corePluginsLoadTime: pluginLoadTimeByType.core ?? 0,
|
|
118
|
+
userPluginsLoadTime: pluginLoadTimeByType.user ?? 0,
|
|
119
|
+
linkedPluginsLoadTime: pluginLoadTimeByType.link ?? 0,
|
|
120
|
+
};
|
|
121
|
+
resolve();
|
|
122
|
+
});
|
|
123
|
+
perfObserver.observe({ entryTypes: ['measure'], buffered: true });
|
|
124
|
+
for (const marker of Object.values(Performance.markers)) {
|
|
125
|
+
try {
|
|
126
|
+
marker.measure();
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// ignore
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
perf_hooks_1.performance.clearMarks();
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Add debug logs for plugin loading performance
|
|
137
|
+
* @returns void
|
|
138
|
+
*/
|
|
139
|
+
static debug() {
|
|
140
|
+
if (!Performance.enabled)
|
|
141
|
+
return;
|
|
142
|
+
const debug = require('debug')('perf');
|
|
143
|
+
debug('Init Time: %sms', Performance.highlights.initTime.toFixed(4));
|
|
144
|
+
debug('Config Load Time: %sms', Performance.highlights.configLoadTime.toFixed(4));
|
|
145
|
+
debug('Command Load Time: %sms', Performance.highlights.commandLoadTime.toFixed(4));
|
|
146
|
+
debug('Execution Time: %sms', Performance.highlights.runTime.toFixed(4));
|
|
147
|
+
debug('Core Plugin Load Time: %sms', Performance.highlights.corePluginsLoadTime.toFixed(4));
|
|
148
|
+
debug('User Plugin Load Time: %sms', Performance.highlights.userPluginsLoadTime.toFixed(4));
|
|
149
|
+
debug('Linked Plugin Load Time: %sms', Performance.highlights.linkedPluginsLoadTime.toFixed(4));
|
|
150
|
+
debug('Plugin Load Times:');
|
|
151
|
+
for (const [plugin, duration] of Object.entries(Performance.highlights.pluginLoadTimes)) {
|
|
152
|
+
debug(` ${plugin}: ${duration.toFixed(4)}ms`);
|
|
153
|
+
}
|
|
154
|
+
debug('Hook Run Times:');
|
|
155
|
+
for (const [event, runTimes] of Object.entries(Performance.highlights.hookRunTimes)) {
|
|
156
|
+
debug(` ${event}:`);
|
|
157
|
+
for (const [plugin, duration] of Object.entries(runTimes)) {
|
|
158
|
+
debug(` ${plugin}: ${duration.toFixed(4)}ms`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.Performance = Performance;
|
|
164
|
+
Performance.markers = {};
|
|
165
|
+
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;
|