@oclif/core 3.19.4 → 3.19.5-dev.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/cache.d.ts +8 -0
- package/lib/cache.js +23 -0
- package/lib/cli-ux/config.js +3 -2
- package/lib/command.js +2 -2
- package/lib/config/config.d.ts +1 -0
- package/lib/config/config.js +12 -6
- package/lib/config/plugin.d.ts +7 -2
- package/lib/config/plugin.js +128 -35
- package/lib/help/docopts.js +1 -1
- package/lib/help/index.js +9 -2
- package/lib/interfaces/config.d.ts +1 -0
- package/lib/interfaces/pjson.d.ts +86 -2
- package/lib/interfaces/plugin.d.ts +2 -2
- package/lib/main.js +7 -11
- package/lib/symbols.d.ts +1 -0
- package/lib/symbols.js +4 -0
- package/lib/util/fs.d.ts +0 -1
- package/lib/util/fs.js +1 -6
- package/package.json +2 -4
package/lib/cache.d.ts
CHANGED
|
@@ -2,15 +2,23 @@ import { PJSON, Plugin } from './interfaces';
|
|
|
2
2
|
type CacheContents = {
|
|
3
3
|
rootPlugin: Plugin;
|
|
4
4
|
exitCodes: PJSON.Plugin['oclif']['exitCodes'];
|
|
5
|
+
'@oclif/core': OclifCoreInfo;
|
|
5
6
|
};
|
|
6
7
|
type ValueOf<T> = T[keyof T];
|
|
8
|
+
type OclifCoreInfo = {
|
|
9
|
+
name: string;
|
|
10
|
+
version: string;
|
|
11
|
+
};
|
|
7
12
|
/**
|
|
8
13
|
* A simple cache for storing values that need to be accessed globally.
|
|
9
14
|
*/
|
|
10
15
|
export default class Cache extends Map<keyof CacheContents, ValueOf<CacheContents>> {
|
|
11
16
|
static instance: Cache;
|
|
17
|
+
constructor();
|
|
12
18
|
static getInstance(): Cache;
|
|
19
|
+
get(key: '@oclif/core'): OclifCoreInfo;
|
|
13
20
|
get(key: 'rootPlugin'): Plugin | undefined;
|
|
14
21
|
get(key: 'exitCodes'): PJSON.Plugin['oclif']['exitCodes'] | undefined;
|
|
22
|
+
private getOclifCoreMeta;
|
|
15
23
|
}
|
|
16
24
|
export {};
|
package/lib/cache.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_fs_1 = require("node:fs");
|
|
4
|
+
const node_path_1 = require("node:path");
|
|
3
5
|
/**
|
|
4
6
|
* A simple cache for storing values that need to be accessed globally.
|
|
5
7
|
*/
|
|
6
8
|
class Cache extends Map {
|
|
7
9
|
static instance;
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
this.set('@oclif/core', this.getOclifCoreMeta());
|
|
13
|
+
}
|
|
8
14
|
static getInstance() {
|
|
9
15
|
if (!Cache.instance) {
|
|
10
16
|
Cache.instance = new Cache();
|
|
@@ -14,5 +20,22 @@ class Cache extends Map {
|
|
|
14
20
|
get(key) {
|
|
15
21
|
return super.get(key);
|
|
16
22
|
}
|
|
23
|
+
getOclifCoreMeta() {
|
|
24
|
+
try {
|
|
25
|
+
// eslint-disable-next-line node/no-extraneous-require
|
|
26
|
+
return { name: '@oclif/core', version: require('@oclif/core/package.json').version };
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
try {
|
|
30
|
+
return {
|
|
31
|
+
name: '@oclif/core',
|
|
32
|
+
version: JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '..', 'package.json'), 'utf8')),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { name: '@oclif/core', version: 'unknown' };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
17
40
|
}
|
|
18
41
|
exports.default = Cache;
|
package/lib/cli-ux/config.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.config = exports.Config = void 0;
|
|
7
|
-
const
|
|
7
|
+
const cache_1 = __importDefault(require("../cache"));
|
|
8
8
|
const simple_1 = __importDefault(require("./action/simple"));
|
|
9
9
|
const spinner_1 = __importDefault(require("./action/spinner"));
|
|
10
10
|
const g = global;
|
|
@@ -35,7 +35,8 @@ class Config {
|
|
|
35
35
|
}
|
|
36
36
|
exports.Config = Config;
|
|
37
37
|
function fetch() {
|
|
38
|
-
const
|
|
38
|
+
const core = cache_1.default.getInstance().get('@oclif/core');
|
|
39
|
+
const major = core?.version.split('.')[0] || 'unknown';
|
|
39
40
|
if (globals[major])
|
|
40
41
|
return globals[major];
|
|
41
42
|
globals[major] = new Config();
|
package/lib/command.js
CHANGED
|
@@ -30,16 +30,16 @@ exports.Command = void 0;
|
|
|
30
30
|
const chalk_1 = __importDefault(require("chalk"));
|
|
31
31
|
const node_url_1 = require("node:url");
|
|
32
32
|
const node_util_1 = require("node:util");
|
|
33
|
+
const cache_1 = __importDefault(require("./cache"));
|
|
33
34
|
const cli_ux_1 = require("./cli-ux");
|
|
34
35
|
const config_1 = require("./config");
|
|
35
36
|
const Errors = __importStar(require("./errors"));
|
|
36
37
|
const util_1 = require("./help/util");
|
|
37
38
|
const Parser = __importStar(require("./parser"));
|
|
38
39
|
const aggregate_flags_1 = require("./util/aggregate-flags");
|
|
39
|
-
const fs_1 = require("./util/fs");
|
|
40
40
|
const ids_1 = require("./util/ids");
|
|
41
41
|
const util_2 = require("./util/util");
|
|
42
|
-
const pjson = (
|
|
42
|
+
const pjson = cache_1.default.getInstance().get('@oclif/core');
|
|
43
43
|
/**
|
|
44
44
|
* swallows stdout epipe errors
|
|
45
45
|
* this occurs when stdout closes such as when piping to head
|
package/lib/config/config.d.ts
CHANGED
package/lib/config/config.js
CHANGED
|
@@ -48,7 +48,7 @@ const ts_node_1 = require("./ts-node");
|
|
|
48
48
|
const util_3 = require("./util");
|
|
49
49
|
// eslint-disable-next-line new-cap
|
|
50
50
|
const debug = (0, util_3.Debug)();
|
|
51
|
-
const _pjson = (
|
|
51
|
+
const _pjson = cache_1.default.getInstance().get('@oclif/core');
|
|
52
52
|
const BASE = `${_pjson.name}@${_pjson.version}`;
|
|
53
53
|
function channelFromVersion(version) {
|
|
54
54
|
const m = version.match(/[^-]+(?:-([^.]+))?/);
|
|
@@ -98,6 +98,7 @@ class Config {
|
|
|
98
98
|
errlog;
|
|
99
99
|
flexibleTaxonomy;
|
|
100
100
|
home;
|
|
101
|
+
isSingleCommandCLI = false;
|
|
101
102
|
name;
|
|
102
103
|
npmRegistry;
|
|
103
104
|
nsisCustomization;
|
|
@@ -328,6 +329,10 @@ class Config {
|
|
|
328
329
|
...(s3.templates && s3.templates.vanilla),
|
|
329
330
|
},
|
|
330
331
|
};
|
|
332
|
+
this.isSingleCommandCLI = Boolean(this.pjson.oclif.default ||
|
|
333
|
+
(typeof this.pjson.oclif.commands !== 'string' &&
|
|
334
|
+
this.pjson.oclif.commands?.strategy === 'single' &&
|
|
335
|
+
this.pjson.oclif.commands?.target));
|
|
331
336
|
await this.loadPluginsAndCommands();
|
|
332
337
|
debug('config done');
|
|
333
338
|
marker?.addDetails({
|
|
@@ -482,14 +487,15 @@ class Config {
|
|
|
482
487
|
};
|
|
483
488
|
const hooks = p.hooks[event] || [];
|
|
484
489
|
for (const hook of hooks) {
|
|
485
|
-
const marker = performance_1.Performance.mark(performance_1.OCLIF_MARKER_OWNER, `config.runHook#${p.name}(${hook})`);
|
|
490
|
+
const marker = performance_1.Performance.mark(performance_1.OCLIF_MARKER_OWNER, `config.runHook#${p.name}(${hook.target})`);
|
|
486
491
|
try {
|
|
487
492
|
/* eslint-disable no-await-in-loop */
|
|
488
|
-
const { filePath, isESM, module } = await (0, module_loader_1.loadWithData)(p, await (0, ts_node_1.tsPath)(p.root, hook, p));
|
|
493
|
+
const { filePath, isESM, module } = await (0, module_loader_1.loadWithData)(p, await (0, ts_node_1.tsPath)(p.root, hook.target, p));
|
|
489
494
|
debug('start', isESM ? '(import)' : '(require)', filePath);
|
|
495
|
+
const hookFn = module[hook.identifier] ?? search(module);
|
|
490
496
|
const result = timeout
|
|
491
|
-
? await withTimeout(timeout,
|
|
492
|
-
: await
|
|
497
|
+
? await withTimeout(timeout, hookFn.call(context, { ...opts, config: this, context }))
|
|
498
|
+
: await hookFn.call(context, { ...opts, config: this, context });
|
|
493
499
|
final.successes.push({ plugin: p, result });
|
|
494
500
|
if (p.name === '@oclif/plugin-legacy' && event === 'init') {
|
|
495
501
|
this.insertLegacyPlugins(result);
|
|
@@ -511,7 +517,7 @@ class Config {
|
|
|
511
517
|
}
|
|
512
518
|
marker?.addDetails({
|
|
513
519
|
event,
|
|
514
|
-
hook,
|
|
520
|
+
hook: hook.target,
|
|
515
521
|
plugin: p.name,
|
|
516
522
|
});
|
|
517
523
|
marker?.stop();
|
package/lib/config/plugin.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from '../command';
|
|
2
2
|
import { Manifest } from '../interfaces/manifest';
|
|
3
|
-
import { PJSON } from '../interfaces/pjson';
|
|
3
|
+
import { HookOptions, PJSON } from '../interfaces/pjson';
|
|
4
4
|
import { Plugin as IPlugin, PluginOptions } from '../interfaces/plugin';
|
|
5
5
|
import { Topic } from '../interfaces/topic';
|
|
6
6
|
export declare class Plugin implements IPlugin {
|
|
@@ -13,7 +13,7 @@ export declare class Plugin implements IPlugin {
|
|
|
13
13
|
commandsDir: string | undefined;
|
|
14
14
|
hasManifest: boolean;
|
|
15
15
|
hooks: {
|
|
16
|
-
[
|
|
16
|
+
[key: string]: HookOptions[];
|
|
17
17
|
};
|
|
18
18
|
isRoot: boolean;
|
|
19
19
|
manifest: Manifest;
|
|
@@ -29,6 +29,8 @@ export declare class Plugin implements IPlugin {
|
|
|
29
29
|
protected warned: boolean;
|
|
30
30
|
_base: string;
|
|
31
31
|
protected _debug: (..._: any) => void;
|
|
32
|
+
private commandCache;
|
|
33
|
+
private commandDiscoveryOpts;
|
|
32
34
|
private flexibleTaxonomy;
|
|
33
35
|
constructor(options: PluginOptions);
|
|
34
36
|
get topics(): Topic[];
|
|
@@ -42,6 +44,9 @@ export declare class Plugin implements IPlugin {
|
|
|
42
44
|
private _manifest;
|
|
43
45
|
private addErrorScope;
|
|
44
46
|
private getCommandIDs;
|
|
47
|
+
private getCommandIdsFromPattern;
|
|
48
|
+
private getCommandIdsFromTarget;
|
|
45
49
|
private getCommandsDir;
|
|
50
|
+
private loadCommandsFromTarget;
|
|
46
51
|
private warn;
|
|
47
52
|
}
|
package/lib/config/plugin.js
CHANGED
|
@@ -7,16 +7,18 @@ exports.Plugin = void 0;
|
|
|
7
7
|
const globby_1 = __importDefault(require("globby"));
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
9
|
const node_util_1 = require("node:util");
|
|
10
|
+
const cache_1 = __importDefault(require("../cache"));
|
|
10
11
|
const errors_1 = require("../errors");
|
|
11
12
|
const module_loader_1 = require("../module-loader");
|
|
12
13
|
const performance_1 = require("../performance");
|
|
14
|
+
const symbols_1 = require("../symbols");
|
|
13
15
|
const cache_command_1 = require("../util/cache-command");
|
|
14
16
|
const find_root_1 = require("../util/find-root");
|
|
15
17
|
const fs_1 = require("../util/fs");
|
|
16
18
|
const util_1 = require("../util/util");
|
|
17
19
|
const ts_node_1 = require("./ts-node");
|
|
18
20
|
const util_2 = require("./util");
|
|
19
|
-
const _pjson = (
|
|
21
|
+
const _pjson = cache_1.default.getInstance().get('@oclif/core');
|
|
20
22
|
function topicsToArray(input, base) {
|
|
21
23
|
if (!input)
|
|
22
24
|
return [];
|
|
@@ -30,7 +32,7 @@ function topicsToArray(input, base) {
|
|
|
30
32
|
});
|
|
31
33
|
}
|
|
32
34
|
const cachedCommandCanBeUsed = (manifest, id) => Boolean(manifest?.commands[id] && 'isESM' in manifest.commands[id] && 'relativePath' in manifest.commands[id]);
|
|
33
|
-
const
|
|
35
|
+
const searchForCommandClass = (cmd) => {
|
|
34
36
|
if (typeof cmd.run === 'function')
|
|
35
37
|
return cmd;
|
|
36
38
|
if (cmd.default && cmd.default.run)
|
|
@@ -47,9 +49,34 @@ function processCommandIds(files) {
|
|
|
47
49
|
const topics = p.dir.split('/');
|
|
48
50
|
const command = p.name !== 'index' && p.name;
|
|
49
51
|
const id = [...topics, command].filter(Boolean).join(':');
|
|
50
|
-
return id === '' ?
|
|
52
|
+
return id === '' ? symbols_1.SINGLE_COMMAND_CLI_SYMBOL : id;
|
|
51
53
|
});
|
|
52
54
|
}
|
|
55
|
+
function determineCommandDiscoveryOptions(commandDiscovery, defaultCmdId) {
|
|
56
|
+
if (!commandDiscovery)
|
|
57
|
+
return;
|
|
58
|
+
if (typeof commandDiscovery === 'string' && defaultCmdId) {
|
|
59
|
+
return { strategy: 'single', target: commandDiscovery };
|
|
60
|
+
}
|
|
61
|
+
if (typeof commandDiscovery === 'string') {
|
|
62
|
+
return { globPatterns: GLOB_PATTERNS, strategy: 'pattern', target: commandDiscovery };
|
|
63
|
+
}
|
|
64
|
+
if (!commandDiscovery.target)
|
|
65
|
+
throw new errors_1.CLIError('`oclif.commandDiscovery.target` is required.');
|
|
66
|
+
if (!commandDiscovery.strategy)
|
|
67
|
+
throw new errors_1.CLIError('`oclif.commandDiscovery.strategy` is required.');
|
|
68
|
+
if (commandDiscovery.strategy === 'explicit' && !commandDiscovery.identifier) {
|
|
69
|
+
commandDiscovery.identifier = 'default';
|
|
70
|
+
}
|
|
71
|
+
return commandDiscovery;
|
|
72
|
+
}
|
|
73
|
+
function determineHookOptions(hook) {
|
|
74
|
+
if (typeof hook === 'string')
|
|
75
|
+
return { identifier: 'default', target: hook };
|
|
76
|
+
if (!hook.identifier)
|
|
77
|
+
return { ...hook, identifier: 'default' };
|
|
78
|
+
return hook;
|
|
79
|
+
}
|
|
53
80
|
class Plugin {
|
|
54
81
|
options;
|
|
55
82
|
alias;
|
|
@@ -76,6 +103,8 @@ class Plugin {
|
|
|
76
103
|
_base = `${_pjson.name}@${_pjson.version}`;
|
|
77
104
|
// eslint-disable-next-line new-cap
|
|
78
105
|
_debug = (0, util_2.Debug)();
|
|
106
|
+
commandCache;
|
|
107
|
+
commandDiscoveryOpts;
|
|
79
108
|
flexibleTaxonomy;
|
|
80
109
|
constructor(options) {
|
|
81
110
|
this.options = options;
|
|
@@ -89,32 +118,43 @@ class Plugin {
|
|
|
89
118
|
plugin: this.name,
|
|
90
119
|
});
|
|
91
120
|
const fetch = async () => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
if (this.commandDiscoveryOpts?.strategy === 'pattern') {
|
|
122
|
+
const commandsDir = await this.getCommandsDir();
|
|
123
|
+
if (!commandsDir)
|
|
124
|
+
return;
|
|
125
|
+
let module;
|
|
126
|
+
let isESM;
|
|
127
|
+
let filePath;
|
|
128
|
+
try {
|
|
129
|
+
;
|
|
130
|
+
({ filePath, isESM, module } = cachedCommandCanBeUsed(this.manifest, id)
|
|
131
|
+
? await (0, module_loader_1.loadWithDataFromManifest)(this.manifest.commands[id], this.root)
|
|
132
|
+
: await (0, module_loader_1.loadWithData)(this, (0, node_path_1.join)(commandsDir ?? this.pjson.oclif.commands, ...id.split(':'))));
|
|
133
|
+
this._debug(isESM ? '(import)' : '(require)', filePath);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (!opts.must && error.code === 'MODULE_NOT_FOUND')
|
|
137
|
+
return;
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
const cmd = searchForCommandClass(module);
|
|
141
|
+
if (!cmd)
|
|
142
|
+
return;
|
|
143
|
+
cmd.id = id;
|
|
144
|
+
cmd.plugin = this;
|
|
145
|
+
cmd.isESM = isESM;
|
|
146
|
+
cmd.relativePath = (0, node_path_1.relative)(this.root, filePath || '').split(node_path_1.sep);
|
|
147
|
+
return cmd;
|
|
104
148
|
}
|
|
105
|
-
|
|
106
|
-
|
|
149
|
+
if (this.commandDiscoveryOpts?.strategy === 'single' || this.commandDiscoveryOpts?.strategy === 'explicit') {
|
|
150
|
+
const commandCache = await this.loadCommandsFromTarget();
|
|
151
|
+
const cmd = commandCache?.[id];
|
|
152
|
+
if (!cmd)
|
|
107
153
|
return;
|
|
108
|
-
|
|
154
|
+
cmd.id = id;
|
|
155
|
+
cmd.plugin = this;
|
|
156
|
+
return cmd;
|
|
109
157
|
}
|
|
110
|
-
const cmd = search(module);
|
|
111
|
-
if (!cmd)
|
|
112
|
-
return;
|
|
113
|
-
cmd.id = id;
|
|
114
|
-
cmd.plugin = this;
|
|
115
|
-
cmd.isESM = isESM;
|
|
116
|
-
cmd.relativePath = (0, node_path_1.relative)(this.root, filePath || '').split(node_path_1.sep);
|
|
117
|
-
return cmd;
|
|
118
158
|
};
|
|
119
159
|
const cmd = await fetch();
|
|
120
160
|
if (!cmd && opts.must)
|
|
@@ -153,7 +193,12 @@ class Plugin {
|
|
|
153
193
|
else {
|
|
154
194
|
this.pjson.oclif = this.pjson['cli-engine'] || {};
|
|
155
195
|
}
|
|
156
|
-
this.hooks = Object.fromEntries(Object.entries(this.pjson.oclif.hooks ?? {}).map(([k, v]) => [
|
|
196
|
+
this.hooks = Object.fromEntries(Object.entries(this.pjson.oclif.hooks ?? {}).map(([k, v]) => [
|
|
197
|
+
k,
|
|
198
|
+
(0, util_1.castArray)(v).map((v) => determineHookOptions(v)),
|
|
199
|
+
]));
|
|
200
|
+
this.commandDiscoveryOpts = determineCommandDiscoveryOptions(this.pjson.oclif?.commands, this.pjson.oclif?.default);
|
|
201
|
+
this._debug('command discovery options', this.commandDiscoveryOpts);
|
|
157
202
|
this.manifest = await this._manifest();
|
|
158
203
|
this.commands = Object.entries(this.manifest.commands)
|
|
159
204
|
.map(([id, c]) => ({
|
|
@@ -205,7 +250,13 @@ class Plugin {
|
|
|
205
250
|
const manifest = {
|
|
206
251
|
commands: (await Promise.all(this.commandIDs.map(async (id) => {
|
|
207
252
|
try {
|
|
208
|
-
const
|
|
253
|
+
const found = await this.findCommand(id, { must: true });
|
|
254
|
+
const cached = await (0, cache_command_1.cacheCommand)(found, this, respectNoCacheDefault);
|
|
255
|
+
// Ensure that id is set to the id being processed
|
|
256
|
+
// This is necessary because the id is set by findCommand but if there
|
|
257
|
+
// are multiple instances of a Command, then the id will be set to the
|
|
258
|
+
// last one found.
|
|
259
|
+
cached.id = id;
|
|
209
260
|
if (this.flexibleTaxonomy) {
|
|
210
261
|
const permutations = (0, util_2.getCommandIdPermutations)(id);
|
|
211
262
|
const aliasPermutations = cached.aliases.flatMap((a) => (0, util_2.getCommandIdPermutations)(a));
|
|
@@ -246,24 +297,66 @@ class Plugin {
|
|
|
246
297
|
return err;
|
|
247
298
|
}
|
|
248
299
|
async getCommandIDs() {
|
|
249
|
-
const commandsDir = await this.getCommandsDir();
|
|
250
|
-
if (!commandsDir)
|
|
251
|
-
return [];
|
|
252
300
|
const marker = performance_1.Performance.mark(performance_1.OCLIF_MARKER_OWNER, `plugin.getCommandIDs#${this.name}`, { plugin: this.name });
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
301
|
+
let ids;
|
|
302
|
+
switch (this.commandDiscoveryOpts?.strategy) {
|
|
303
|
+
case 'explicit': {
|
|
304
|
+
ids = (await this.getCommandIdsFromTarget()) ?? [];
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
case 'pattern': {
|
|
308
|
+
ids = await this.getCommandIdsFromPattern();
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
case 'single': {
|
|
312
|
+
ids = (await this.getCommandIdsFromTarget()) ?? [];
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
default: {
|
|
316
|
+
ids = [];
|
|
317
|
+
}
|
|
318
|
+
}
|
|
256
319
|
this._debug('found commands', ids);
|
|
257
320
|
marker?.addDetails({ count: ids.length });
|
|
258
321
|
marker?.stop();
|
|
259
322
|
return ids;
|
|
260
323
|
}
|
|
324
|
+
async getCommandIdsFromPattern() {
|
|
325
|
+
const commandsDir = await this.getCommandsDir();
|
|
326
|
+
if (!commandsDir)
|
|
327
|
+
return [];
|
|
328
|
+
this._debug(`loading IDs from ${commandsDir}`);
|
|
329
|
+
const files = await (0, globby_1.default)(this.commandDiscoveryOpts?.globPatterns ?? GLOB_PATTERNS, { cwd: commandsDir });
|
|
330
|
+
return processCommandIds(files);
|
|
331
|
+
}
|
|
332
|
+
async getCommandIdsFromTarget() {
|
|
333
|
+
const commandsFromExport = await this.loadCommandsFromTarget();
|
|
334
|
+
if (commandsFromExport) {
|
|
335
|
+
return Object.keys(commandsFromExport);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
261
338
|
async getCommandsDir() {
|
|
262
339
|
if (this.commandsDir)
|
|
263
340
|
return this.commandsDir;
|
|
264
|
-
this.commandsDir = await (0, ts_node_1.tsPath)(this.root, this.
|
|
341
|
+
this.commandsDir = await (0, ts_node_1.tsPath)(this.root, this.commandDiscoveryOpts?.target, this);
|
|
265
342
|
return this.commandsDir;
|
|
266
343
|
}
|
|
344
|
+
async loadCommandsFromTarget() {
|
|
345
|
+
if (this.commandCache)
|
|
346
|
+
return this.commandCache;
|
|
347
|
+
if (this.commandDiscoveryOpts?.strategy === 'explicit' && this.commandDiscoveryOpts.target) {
|
|
348
|
+
const filePath = await (0, ts_node_1.tsPath)(this.root, this.commandDiscoveryOpts.target, this);
|
|
349
|
+
const module = await (0, module_loader_1.load)(this, filePath);
|
|
350
|
+
this.commandCache = module[this.commandDiscoveryOpts?.identifier ?? 'default'] ?? {};
|
|
351
|
+
return this.commandCache;
|
|
352
|
+
}
|
|
353
|
+
if (this.commandDiscoveryOpts?.strategy === 'single' && this.commandDiscoveryOpts.target) {
|
|
354
|
+
const filePath = await (0, ts_node_1.tsPath)(this.root, this.commandDiscoveryOpts?.target ?? this.root, this);
|
|
355
|
+
const module = await (0, module_loader_1.load)(this, filePath);
|
|
356
|
+
this.commandCache = { [symbols_1.SINGLE_COMMAND_CLI_SYMBOL]: searchForCommandClass(module) };
|
|
357
|
+
return this.commandCache;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
267
360
|
warn(err, scope) {
|
|
268
361
|
if (this.warned)
|
|
269
362
|
return;
|
package/lib/help/docopts.js
CHANGED
|
@@ -77,7 +77,7 @@ class DocOpts {
|
|
|
77
77
|
return new DocOpts(cmd).toString();
|
|
78
78
|
}
|
|
79
79
|
toString() {
|
|
80
|
-
const opts =
|
|
80
|
+
const opts = ['<%= command.id %>'];
|
|
81
81
|
if (this.cmd.args) {
|
|
82
82
|
const a = Object.values((0, ensure_arg_object_1.ensureArgObject)(this.cmd.args)).map((arg) => arg.required ? arg.name.toUpperCase() : `[${arg.name.toUpperCase()}]`) || [];
|
|
83
83
|
opts.push(...a);
|
package/lib/help/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const theme_1 = require("../cli-ux/theme");
|
|
|
10
10
|
const write_1 = __importDefault(require("../cli-ux/write"));
|
|
11
11
|
const errors_1 = require("../errors");
|
|
12
12
|
const module_loader_1 = require("../module-loader");
|
|
13
|
+
const symbols_1 = require("../symbols");
|
|
13
14
|
const cache_default_value_1 = require("../util/cache-default-value");
|
|
14
15
|
const ids_1 = require("../util/ids");
|
|
15
16
|
const util_1 = require("../util/util");
|
|
@@ -189,8 +190,8 @@ class Help extends HelpBase {
|
|
|
189
190
|
argv = (0, util_2.standardizeIDFromArgv)(argv, this.config);
|
|
190
191
|
const subject = getHelpSubject(argv, this.config);
|
|
191
192
|
if (!subject) {
|
|
192
|
-
if (this.config.
|
|
193
|
-
const rootCmd = this.config.findCommand(
|
|
193
|
+
if (this.config.isSingleCommandCLI) {
|
|
194
|
+
const rootCmd = this.config.findCommand(symbols_1.SINGLE_COMMAND_CLI_SYMBOL);
|
|
194
195
|
if (rootCmd) {
|
|
195
196
|
await this.showCommandHelp(rootCmd);
|
|
196
197
|
return;
|
|
@@ -201,6 +202,12 @@ class Help extends HelpBase {
|
|
|
201
202
|
}
|
|
202
203
|
const command = this.config.findCommand(subject);
|
|
203
204
|
if (command) {
|
|
205
|
+
if (command.id === symbols_1.SINGLE_COMMAND_CLI_SYMBOL) {
|
|
206
|
+
// If the command is the root command of a single command CLI,
|
|
207
|
+
// then set the command id to an empty string to prevent the
|
|
208
|
+
// the SINGLE_COMMAND_CLI_SYMBOL from being displayed in the help output.
|
|
209
|
+
command.id = '';
|
|
210
|
+
}
|
|
204
211
|
if (command.hasDynamicHelp && command.pluginType !== 'jit') {
|
|
205
212
|
const loaded = await command.load();
|
|
206
213
|
for (const [name, flag] of Object.entries(loaded.flags ?? {})) {
|
|
@@ -16,6 +16,84 @@ export interface PJSON {
|
|
|
16
16
|
};
|
|
17
17
|
version: string;
|
|
18
18
|
}
|
|
19
|
+
export type CommandDiscovery = {
|
|
20
|
+
/**
|
|
21
|
+
* The strategy to use for loading commands.
|
|
22
|
+
*
|
|
23
|
+
* - `pattern` will use glob patterns to find command files in the specified `target`.
|
|
24
|
+
* - `explicit` will use `import` (or `require` for CJS) to load the commands from the
|
|
25
|
+
* specified `target`.
|
|
26
|
+
* - `single` will use the `target` which should export a command class. This is for CLIs that
|
|
27
|
+
* only have a single command.
|
|
28
|
+
*
|
|
29
|
+
* In both cases, the `oclif.manifest.json` file will be used to find the commands if it exists.
|
|
30
|
+
*/
|
|
31
|
+
strategy: 'pattern' | 'explicit' | 'single';
|
|
32
|
+
/**
|
|
33
|
+
* If the `strategy` is `pattern`, this is the **directory** to use to find command files.
|
|
34
|
+
*
|
|
35
|
+
* If the `strategy` is `explicit`, this is the **file** that exports the commands.
|
|
36
|
+
* - This export must be an object with keys that are the command names and values that are the command classes.
|
|
37
|
+
* - Unless `identifier` is specified, the default export will be used.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // in src/commands.ts
|
|
42
|
+
* import Hello from './commands/hello/index.js'
|
|
43
|
+
* import HelloWorld from './commands/hello/world.js'
|
|
44
|
+
*
|
|
45
|
+
* export default {
|
|
46
|
+
* hello: Hello,
|
|
47
|
+
* 'hello:world': HelloWorld,
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
target: string;
|
|
52
|
+
/**
|
|
53
|
+
* The glob patterns to use to find command files when no `oclif.manifest.json` is present.
|
|
54
|
+
* This is only used when `strategy` is `pattern`.
|
|
55
|
+
*/
|
|
56
|
+
globPatterns?: string[];
|
|
57
|
+
/**
|
|
58
|
+
* The name of the export to used when loading the command object from the `target` file. Only
|
|
59
|
+
* used when `strategy` is `explicit`. Defaults to `default`.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // in src/commands.ts
|
|
64
|
+
* import Hello from './commands/hello/index.js'
|
|
65
|
+
* import HelloWorld from './commands/hello/world.js'
|
|
66
|
+
*
|
|
67
|
+
* export const MY_COMMANDS = {
|
|
68
|
+
* hello: Hello,
|
|
69
|
+
* 'hello:world': HelloWorld,
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* In the package.json:
|
|
74
|
+
* ```json
|
|
75
|
+
* {
|
|
76
|
+
* "oclif": {
|
|
77
|
+
* "commands": {
|
|
78
|
+
* "strategy": "explicit",
|
|
79
|
+
* "target": "./dist/index.js",
|
|
80
|
+
* "identifier": "MY_COMMANDS"
|
|
81
|
+
* }
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
identifier?: string;
|
|
86
|
+
};
|
|
87
|
+
export type HookOptions = {
|
|
88
|
+
/**
|
|
89
|
+
* The file path containing hook.
|
|
90
|
+
*/
|
|
91
|
+
target: string;
|
|
92
|
+
/**
|
|
93
|
+
* The name of the export to use when loading the hook function from the `target` file. Defaults to `default`.
|
|
94
|
+
*/
|
|
95
|
+
identifier: string;
|
|
96
|
+
};
|
|
19
97
|
export declare namespace PJSON {
|
|
20
98
|
interface Plugin extends PJSON {
|
|
21
99
|
name: string;
|
|
@@ -25,7 +103,13 @@ export declare namespace PJSON {
|
|
|
25
103
|
aliases?: {
|
|
26
104
|
[name: string]: null | string;
|
|
27
105
|
};
|
|
28
|
-
commands?: string;
|
|
106
|
+
commands?: string | CommandDiscovery;
|
|
107
|
+
/**
|
|
108
|
+
* Default command id when no command is found. This is used to support single command CLIs.
|
|
109
|
+
* Only supported value is "."
|
|
110
|
+
*
|
|
111
|
+
* @deprecated Use `commands.strategy: 'single'` instead.
|
|
112
|
+
*/
|
|
29
113
|
default?: string;
|
|
30
114
|
description?: string;
|
|
31
115
|
devPlugins?: string[];
|
|
@@ -42,7 +126,7 @@ export declare namespace PJSON {
|
|
|
42
126
|
helpClass?: string;
|
|
43
127
|
helpOptions?: HelpOptions;
|
|
44
128
|
hooks?: {
|
|
45
|
-
[name: string]: string | string[];
|
|
129
|
+
[name: string]: string | string[] | HookOptions | HookOptions[];
|
|
46
130
|
};
|
|
47
131
|
jitPlugins?: Record<string, string>;
|
|
48
132
|
macos?: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from '../command';
|
|
2
|
-
import { PJSON } from './pjson';
|
|
2
|
+
import { HookOptions, PJSON } from './pjson';
|
|
3
3
|
import { Topic } from './topic';
|
|
4
4
|
export interface PluginOptions {
|
|
5
5
|
children?: Plugin[];
|
|
@@ -44,7 +44,7 @@ export interface Plugin {
|
|
|
44
44
|
}): Promise<Command.Class> | undefined;
|
|
45
45
|
readonly hasManifest: boolean;
|
|
46
46
|
hooks: {
|
|
47
|
-
[
|
|
47
|
+
[key: string]: HookOptions[];
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
50
|
* True if the plugin is the root plugin.
|
package/lib/main.js
CHANGED
|
@@ -6,9 +6,10 @@ const cli_ux_1 = require("./cli-ux");
|
|
|
6
6
|
const config_1 = require("./config");
|
|
7
7
|
const help_1 = require("./help");
|
|
8
8
|
const performance_1 = require("./performance");
|
|
9
|
+
const symbols_1 = require("./symbols");
|
|
9
10
|
const debug = require('debug')('oclif:main');
|
|
10
11
|
const helpAddition = (argv, config) => {
|
|
11
|
-
if (argv.length === 0 && !config.
|
|
12
|
+
if (argv.length === 0 && !config.isSingleCommandCLI)
|
|
12
13
|
return true;
|
|
13
14
|
const mergedHelpFlags = (0, help_1.getHelpFlagAdditions)(config);
|
|
14
15
|
for (const arg of argv) {
|
|
@@ -47,7 +48,11 @@ async function run(argv, options) {
|
|
|
47
48
|
options = (0, node_url_1.fileURLToPath)(options);
|
|
48
49
|
}
|
|
49
50
|
const config = await config_1.Config.load(options ?? require.main?.filename ?? __dirname);
|
|
50
|
-
|
|
51
|
+
// If this is a single command CLI, then insert the SINGLE_COMMAND_CLI_SYMBOL into the argv array to serve as the command id.
|
|
52
|
+
if (config.isSingleCommandCLI) {
|
|
53
|
+
argv = [symbols_1.SINGLE_COMMAND_CLI_SYMBOL, ...argv];
|
|
54
|
+
}
|
|
55
|
+
const [id, ...argvSlice] = (0, help_1.normalizeArgv)(config, argv);
|
|
51
56
|
// run init hook
|
|
52
57
|
await config.runHook('init', { argv: argvSlice, id });
|
|
53
58
|
// display version if applicable
|
|
@@ -70,17 +75,8 @@ async function run(argv, options) {
|
|
|
70
75
|
const topic = config.flexibleTaxonomy ? null : config.findTopic(id);
|
|
71
76
|
if (topic)
|
|
72
77
|
return config.runCommand('help', [id]);
|
|
73
|
-
if (config.pjson.oclif.default) {
|
|
74
|
-
id = config.pjson.oclif.default;
|
|
75
|
-
argvSlice = argv;
|
|
76
|
-
}
|
|
77
78
|
}
|
|
78
79
|
initMarker?.stop();
|
|
79
|
-
// If the the default command is '.' (signifying that the CLI is a single command CLI) and '.' is provided
|
|
80
|
-
// as an argument, we need to add back the '.' to argv since it was stripped out earlier as part of the
|
|
81
|
-
// command id.
|
|
82
|
-
if (config.pjson.oclif.default === '.' && id === '.' && argv[0] === '.')
|
|
83
|
-
argvSlice = ['.', ...argvSlice];
|
|
84
80
|
try {
|
|
85
81
|
return await config.runCommand(id, argvSlice, cmd);
|
|
86
82
|
}
|
package/lib/symbols.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SINGLE_COMMAND_CLI_SYMBOL: string;
|
package/lib/symbols.js
ADDED
package/lib/util/fs.d.ts
CHANGED
package/lib/util/fs.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.existsSync = exports.safeReadJson = exports.readJsonSync = exports.readJson = exports.fileExists = exports.dirExists =
|
|
3
|
+
exports.existsSync = exports.safeReadJson = exports.readJsonSync = exports.readJson = exports.fileExists = exports.dirExists = void 0;
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
5
|
const promises_1 = require("node:fs/promises");
|
|
6
|
-
const node_path_1 = require("node:path");
|
|
7
|
-
function requireJson(...pathParts) {
|
|
8
|
-
return JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(...pathParts), 'utf8'));
|
|
9
|
-
}
|
|
10
|
-
exports.requireJson = requireJson;
|
|
11
6
|
/**
|
|
12
7
|
* Parser for Args.directory and Flags.directory. Checks that the provided path
|
|
13
8
|
* exists and is a directory.
|
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": "3.19.
|
|
4
|
+
"version": "3.19.5-dev.0",
|
|
5
5
|
"author": "Salesforce",
|
|
6
6
|
"bugs": "https://github.com/oclif/core/issues",
|
|
7
7
|
"dependencies": {
|
|
@@ -114,7 +114,6 @@
|
|
|
114
114
|
"access": "public"
|
|
115
115
|
},
|
|
116
116
|
"scripts": {
|
|
117
|
-
"build:dev": "shx rm -rf lib && tsc --sourceMap",
|
|
118
117
|
"build": "shx rm -rf lib && tsc",
|
|
119
118
|
"commitlint": "commitlint",
|
|
120
119
|
"compile": "tsc",
|
|
@@ -127,9 +126,8 @@
|
|
|
127
126
|
"test:circular-deps": "madge lib/ -c",
|
|
128
127
|
"test:debug": "nyc mocha --debug-brk --inspect \"test/**/*.test.ts\"",
|
|
129
128
|
"test:integration": "mocha --forbid-only \"test/**/*.integration.ts\" --parallel --timeout 1200000",
|
|
130
|
-
"test:
|
|
129
|
+
"test:interoperability": "cross-env DEBUG=integration:* ts-node test/integration/interop.ts",
|
|
131
130
|
"test:perf": "ts-node test/perf/parser.perf.ts",
|
|
132
|
-
"test:dev": "nyc mocha \"test/**/*.test.ts\"",
|
|
133
131
|
"test": "nyc mocha --forbid-only \"test/**/*.test.ts\""
|
|
134
132
|
},
|
|
135
133
|
"types": "lib/index.d.ts"
|