@oclif/core 3.19.0 → 3.19.2-qa.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.d.ts +1 -0
- package/lib/config/config.js +5 -0
- package/lib/config/plugin.d.ts +5 -0
- package/lib/config/plugin.js +112 -33
- 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 +46 -1
- package/lib/main.js +7 -11
- package/lib/parser/parse.d.ts +14 -0
- package/lib/parser/parse.js +14 -8
- package/lib/symbols.d.ts +1 -0
- package/lib/symbols.js +4 -0
- package/package.json +2 -2
package/lib/config/config.d.ts
CHANGED
package/lib/config/config.js
CHANGED
|
@@ -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({
|
package/lib/config/plugin.d.ts
CHANGED
|
@@ -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
|
@@ -10,6 +10,7 @@ const node_util_1 = require("node:util");
|
|
|
10
10
|
const errors_1 = require("../errors");
|
|
11
11
|
const module_loader_1 = require("../module-loader");
|
|
12
12
|
const performance_1 = require("../performance");
|
|
13
|
+
const symbols_1 = require("../symbols");
|
|
13
14
|
const cache_command_1 = require("../util/cache-command");
|
|
14
15
|
const find_root_1 = require("../util/find-root");
|
|
15
16
|
const fs_1 = require("../util/fs");
|
|
@@ -30,7 +31,7 @@ function topicsToArray(input, base) {
|
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
33
|
const cachedCommandCanBeUsed = (manifest, id) => Boolean(manifest?.commands[id] && 'isESM' in manifest.commands[id] && 'relativePath' in manifest.commands[id]);
|
|
33
|
-
const
|
|
34
|
+
const searchForCommandClass = (cmd) => {
|
|
34
35
|
if (typeof cmd.run === 'function')
|
|
35
36
|
return cmd;
|
|
36
37
|
if (cmd.default && cmd.default.run)
|
|
@@ -47,9 +48,24 @@ function processCommandIds(files) {
|
|
|
47
48
|
const topics = p.dir.split('/');
|
|
48
49
|
const command = p.name !== 'index' && p.name;
|
|
49
50
|
const id = [...topics, command].filter(Boolean).join(':');
|
|
50
|
-
return id === '' ?
|
|
51
|
+
return id === '' ? symbols_1.SINGLE_COMMAND_CLI_SYMBOL : id;
|
|
51
52
|
});
|
|
52
53
|
}
|
|
54
|
+
function determineCommandDiscoveryOptions(commandDiscovery, defaultCmdId) {
|
|
55
|
+
if (!commandDiscovery)
|
|
56
|
+
return;
|
|
57
|
+
if (typeof commandDiscovery === 'string' && defaultCmdId) {
|
|
58
|
+
return { strategy: 'single', target: commandDiscovery };
|
|
59
|
+
}
|
|
60
|
+
if (typeof commandDiscovery === 'string') {
|
|
61
|
+
return { globPatterns: GLOB_PATTERNS, strategy: 'pattern', target: commandDiscovery };
|
|
62
|
+
}
|
|
63
|
+
if (!commandDiscovery.target)
|
|
64
|
+
throw new errors_1.CLIError('`oclif.commandDiscovery.target` is required.');
|
|
65
|
+
if (!commandDiscovery.strategy)
|
|
66
|
+
throw new errors_1.CLIError('`oclif.commandDiscovery.strategy` is required.');
|
|
67
|
+
return commandDiscovery;
|
|
68
|
+
}
|
|
53
69
|
class Plugin {
|
|
54
70
|
options;
|
|
55
71
|
alias;
|
|
@@ -76,6 +92,8 @@ class Plugin {
|
|
|
76
92
|
_base = `${_pjson.name}@${_pjson.version}`;
|
|
77
93
|
// eslint-disable-next-line new-cap
|
|
78
94
|
_debug = (0, util_2.Debug)();
|
|
95
|
+
commandCache;
|
|
96
|
+
commandDiscoveryOpts;
|
|
79
97
|
flexibleTaxonomy;
|
|
80
98
|
constructor(options) {
|
|
81
99
|
this.options = options;
|
|
@@ -89,32 +107,43 @@ class Plugin {
|
|
|
89
107
|
plugin: this.name,
|
|
90
108
|
});
|
|
91
109
|
const fetch = async () => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
if (this.commandDiscoveryOpts?.strategy === 'pattern') {
|
|
111
|
+
const commandsDir = await this.getCommandsDir();
|
|
112
|
+
if (!commandsDir)
|
|
113
|
+
return;
|
|
114
|
+
let module;
|
|
115
|
+
let isESM;
|
|
116
|
+
let filePath;
|
|
117
|
+
try {
|
|
118
|
+
;
|
|
119
|
+
({ filePath, isESM, module } = cachedCommandCanBeUsed(this.manifest, id)
|
|
120
|
+
? await (0, module_loader_1.loadWithDataFromManifest)(this.manifest.commands[id], this.root)
|
|
121
|
+
: await (0, module_loader_1.loadWithData)(this, (0, node_path_1.join)(commandsDir ?? this.pjson.oclif.commands, ...id.split(':'))));
|
|
122
|
+
this._debug(isESM ? '(import)' : '(require)', filePath);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (!opts.must && error.code === 'MODULE_NOT_FOUND')
|
|
126
|
+
return;
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
const cmd = searchForCommandClass(module);
|
|
130
|
+
if (!cmd)
|
|
131
|
+
return;
|
|
132
|
+
cmd.id = id;
|
|
133
|
+
cmd.plugin = this;
|
|
134
|
+
cmd.isESM = isESM;
|
|
135
|
+
cmd.relativePath = (0, node_path_1.relative)(this.root, filePath || '').split(node_path_1.sep);
|
|
136
|
+
return cmd;
|
|
104
137
|
}
|
|
105
|
-
|
|
106
|
-
|
|
138
|
+
if (this.commandDiscoveryOpts?.strategy === 'single' || this.commandDiscoveryOpts?.strategy === 'explicit') {
|
|
139
|
+
const commandCache = await this.loadCommandsFromTarget();
|
|
140
|
+
const cmd = commandCache?.[id];
|
|
141
|
+
if (!cmd)
|
|
107
142
|
return;
|
|
108
|
-
|
|
143
|
+
cmd.id = id;
|
|
144
|
+
cmd.plugin = this;
|
|
145
|
+
return cmd;
|
|
109
146
|
}
|
|
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
147
|
};
|
|
119
148
|
const cmd = await fetch();
|
|
120
149
|
if (!cmd && opts.must)
|
|
@@ -154,6 +183,8 @@ class Plugin {
|
|
|
154
183
|
this.pjson.oclif = this.pjson['cli-engine'] || {};
|
|
155
184
|
}
|
|
156
185
|
this.hooks = Object.fromEntries(Object.entries(this.pjson.oclif.hooks ?? {}).map(([k, v]) => [k, (0, util_1.castArray)(v)]));
|
|
186
|
+
this.commandDiscoveryOpts = determineCommandDiscoveryOptions(this.pjson.oclif?.commands, this.pjson.oclif?.default);
|
|
187
|
+
this._debug('command discovery options', this.commandDiscoveryOpts);
|
|
157
188
|
this.manifest = await this._manifest();
|
|
158
189
|
this.commands = Object.entries(this.manifest.commands)
|
|
159
190
|
.map(([id, c]) => ({
|
|
@@ -205,7 +236,13 @@ class Plugin {
|
|
|
205
236
|
const manifest = {
|
|
206
237
|
commands: (await Promise.all(this.commandIDs.map(async (id) => {
|
|
207
238
|
try {
|
|
208
|
-
const
|
|
239
|
+
const found = await this.findCommand(id, { must: true });
|
|
240
|
+
const cached = await (0, cache_command_1.cacheCommand)(found, this, respectNoCacheDefault);
|
|
241
|
+
// Ensure that id is set to the id being processed
|
|
242
|
+
// This is necessary because the id is set by findCommand but if there
|
|
243
|
+
// are multiple instances of a Command, then the id will be set to the
|
|
244
|
+
// last one found.
|
|
245
|
+
cached.id = id;
|
|
209
246
|
if (this.flexibleTaxonomy) {
|
|
210
247
|
const permutations = (0, util_2.getCommandIdPermutations)(id);
|
|
211
248
|
const aliasPermutations = cached.aliases.flatMap((a) => (0, util_2.getCommandIdPermutations)(a));
|
|
@@ -246,24 +283,66 @@ class Plugin {
|
|
|
246
283
|
return err;
|
|
247
284
|
}
|
|
248
285
|
async getCommandIDs() {
|
|
249
|
-
const commandsDir = await this.getCommandsDir();
|
|
250
|
-
if (!commandsDir)
|
|
251
|
-
return [];
|
|
252
286
|
const marker = performance_1.Performance.mark(performance_1.OCLIF_MARKER_OWNER, `plugin.getCommandIDs#${this.name}`, { plugin: this.name });
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
287
|
+
let ids;
|
|
288
|
+
switch (this.commandDiscoveryOpts?.strategy) {
|
|
289
|
+
case 'explicit': {
|
|
290
|
+
ids = (await this.getCommandIdsFromTarget()) ?? [];
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
case 'pattern': {
|
|
294
|
+
ids = await this.getCommandIdsFromPattern();
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
case 'single': {
|
|
298
|
+
ids = (await this.getCommandIdsFromTarget()) ?? [];
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
default: {
|
|
302
|
+
ids = [];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
256
305
|
this._debug('found commands', ids);
|
|
257
306
|
marker?.addDetails({ count: ids.length });
|
|
258
307
|
marker?.stop();
|
|
259
308
|
return ids;
|
|
260
309
|
}
|
|
310
|
+
async getCommandIdsFromPattern() {
|
|
311
|
+
const commandsDir = await this.getCommandsDir();
|
|
312
|
+
if (!commandsDir)
|
|
313
|
+
return [];
|
|
314
|
+
this._debug(`loading IDs from ${commandsDir}`);
|
|
315
|
+
const files = await (0, globby_1.default)(this.commandDiscoveryOpts?.globPatterns ?? GLOB_PATTERNS, { cwd: commandsDir });
|
|
316
|
+
return processCommandIds(files);
|
|
317
|
+
}
|
|
318
|
+
async getCommandIdsFromTarget() {
|
|
319
|
+
const commandsFromExport = await this.loadCommandsFromTarget();
|
|
320
|
+
if (commandsFromExport) {
|
|
321
|
+
return Object.keys(commandsFromExport);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
261
324
|
async getCommandsDir() {
|
|
262
325
|
if (this.commandsDir)
|
|
263
326
|
return this.commandsDir;
|
|
264
|
-
this.commandsDir = await (0, ts_node_1.tsPath)(this.root, this.
|
|
327
|
+
this.commandsDir = await (0, ts_node_1.tsPath)(this.root, this.commandDiscoveryOpts?.target, this);
|
|
265
328
|
return this.commandsDir;
|
|
266
329
|
}
|
|
330
|
+
async loadCommandsFromTarget() {
|
|
331
|
+
if (this.commandCache)
|
|
332
|
+
return this.commandCache;
|
|
333
|
+
if (this.commandDiscoveryOpts?.strategy === 'explicit' && this.commandDiscoveryOpts.target) {
|
|
334
|
+
const filePath = await (0, ts_node_1.tsPath)(this.root, this.commandDiscoveryOpts.target, this);
|
|
335
|
+
const module = await (0, module_loader_1.load)(this, filePath);
|
|
336
|
+
this.commandCache = module.default ?? {};
|
|
337
|
+
return this.commandCache;
|
|
338
|
+
}
|
|
339
|
+
if (this.commandDiscoveryOpts?.strategy === 'single' && this.commandDiscoveryOpts.target) {
|
|
340
|
+
const filePath = await (0, ts_node_1.tsPath)(this.root, this.commandDiscoveryOpts?.target ?? this.root, this);
|
|
341
|
+
const module = await (0, module_loader_1.load)(this, filePath);
|
|
342
|
+
this.commandCache = { [symbols_1.SINGLE_COMMAND_CLI_SYMBOL]: searchForCommandClass(module) };
|
|
343
|
+
return this.commandCache;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
267
346
|
warn(err, scope) {
|
|
268
347
|
if (this.warned)
|
|
269
348
|
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,45 @@ 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 `directory`.
|
|
24
|
+
* - `explicit` will use `import` (or `require` for CJS) to load the commands from the
|
|
25
|
+
* specified `file`.
|
|
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 default exports the commands.
|
|
36
|
+
* - This export must be the default export and an object with keys that are the command names
|
|
37
|
+
* and values that are the command classes.
|
|
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
|
+
};
|
|
19
58
|
export declare namespace PJSON {
|
|
20
59
|
interface Plugin extends PJSON {
|
|
21
60
|
name: string;
|
|
@@ -25,7 +64,13 @@ export declare namespace PJSON {
|
|
|
25
64
|
aliases?: {
|
|
26
65
|
[name: string]: null | string;
|
|
27
66
|
};
|
|
28
|
-
commands?: string;
|
|
67
|
+
commands?: string | CommandDiscovery;
|
|
68
|
+
/**
|
|
69
|
+
* Default command id when no command is found. This is used to support single command CLIs.
|
|
70
|
+
* Only supported value is "."
|
|
71
|
+
*
|
|
72
|
+
* @deprecated Use `commands.strategy: 'single'` instead.
|
|
73
|
+
*/
|
|
29
74
|
default?: string;
|
|
30
75
|
description?: string;
|
|
31
76
|
devPlugins?: string[];
|
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/parser/parse.d.ts
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import { OutputArgs, OutputFlags, ParserInput, ParserOutput } from '../interfaces/parser';
|
|
2
|
+
declare global {
|
|
3
|
+
/**
|
|
4
|
+
* Cache the stdin so that it can be read multiple times.
|
|
5
|
+
*
|
|
6
|
+
* This fixes a bug where the stdin would be read multiple times (because Parser.parse() was called more than once)
|
|
7
|
+
* but only the first read would be successful - all other reads would return null.
|
|
8
|
+
*
|
|
9
|
+
* Storing in global is necessary because we want the cache to be shared across all versions of @oclif/core in
|
|
10
|
+
* in the dependency tree. Storing in a variable would only share the cache within the same version of @oclif/core.
|
|
11
|
+
*/
|
|
12
|
+
var oclif: {
|
|
13
|
+
stdinCache?: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
2
16
|
export declare const readStdin: () => Promise<null | string>;
|
|
3
17
|
export declare class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']>, BFlags extends OutputFlags<T['flags']>, TArgs extends OutputArgs<T['args']>> {
|
|
4
18
|
private readonly input;
|
package/lib/parser/parse.js
CHANGED
|
@@ -32,11 +32,15 @@ const readStdin = async () => {
|
|
|
32
32
|
// Because of this, we have to set a timeout to prevent the process from hanging.
|
|
33
33
|
if (stdin.isTTY)
|
|
34
34
|
return null;
|
|
35
|
+
if (global.oclif?.stdinCache) {
|
|
36
|
+
debug('resolved stdin from global cache', global.oclif.stdinCache);
|
|
37
|
+
return global.oclif.stdinCache;
|
|
38
|
+
}
|
|
35
39
|
return new Promise((resolve) => {
|
|
36
40
|
let result = '';
|
|
37
41
|
const ac = new AbortController();
|
|
38
42
|
const { signal } = ac;
|
|
39
|
-
const timeout = setTimeout(() => ac.abort(),
|
|
43
|
+
const timeout = setTimeout(() => ac.abort(), 10);
|
|
40
44
|
const rl = (0, node_readline_1.createInterface)({
|
|
41
45
|
input: stdin,
|
|
42
46
|
output: stdout,
|
|
@@ -48,6 +52,7 @@ const readStdin = async () => {
|
|
|
48
52
|
rl.once('close', () => {
|
|
49
53
|
clearTimeout(timeout);
|
|
50
54
|
debug('resolved from stdin', result);
|
|
55
|
+
global.oclif = { ...global.oclif, stdinCache: result };
|
|
51
56
|
resolve(result);
|
|
52
57
|
});
|
|
53
58
|
signal.addEventListener('abort', () => {
|
|
@@ -85,6 +90,7 @@ class Parser {
|
|
|
85
90
|
}
|
|
86
91
|
async parse() {
|
|
87
92
|
this._debugInput();
|
|
93
|
+
// eslint-disable-next-line complexity
|
|
88
94
|
const parseFlag = async (arg) => {
|
|
89
95
|
const { isLong, name } = this.findFlag(arg);
|
|
90
96
|
if (!name) {
|
|
@@ -107,19 +113,19 @@ class Parser {
|
|
|
107
113
|
}
|
|
108
114
|
this.currentFlag = flag;
|
|
109
115
|
let input = isLong || arg.length < 3 ? this.argv.shift() : arg.slice(arg[2] === '=' ? 3 : 2);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
throw new errors_1.CLIError(`Flag --${name} expects a value`);
|
|
113
|
-
}
|
|
114
|
-
if (flag.allowStdin === 'only' && input !== '-') {
|
|
115
|
-
throw new errors_1.CLIError(`Flag --${name} can only be read from stdin. The value must be "-".`);
|
|
116
|
+
if (flag.allowStdin === 'only' && input !== '-' && input !== undefined) {
|
|
117
|
+
throw new errors_1.CLIError(`Flag --${name} can only be read from stdin. The value must be "-" or not provided at all.`);
|
|
116
118
|
}
|
|
117
|
-
if (flag.allowStdin && input === '-') {
|
|
119
|
+
if ((flag.allowStdin && input === '-') || flag.allowStdin === 'only') {
|
|
118
120
|
const stdin = await (0, exports.readStdin)();
|
|
119
121
|
if (stdin) {
|
|
120
122
|
input = stdin.trim();
|
|
121
123
|
}
|
|
122
124
|
}
|
|
125
|
+
// if the value ends up being one of the command's flags, the user didn't provide an input
|
|
126
|
+
if (typeof input !== 'string' || this.findFlag(input).name) {
|
|
127
|
+
throw new errors_1.CLIError(`Flag --${name} expects a value`);
|
|
128
|
+
}
|
|
123
129
|
this.raw.push({ flag: flag.name, input, type: 'flag' });
|
|
124
130
|
}
|
|
125
131
|
else {
|
package/lib/symbols.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SINGLE_COMMAND_CLI_SYMBOL: string;
|
package/lib/symbols.js
ADDED
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.0",
|
|
4
|
+
"version": "3.19.2-qa.0",
|
|
5
5
|
"author": "Salesforce",
|
|
6
6
|
"bugs": "https://github.com/oclif/core/issues",
|
|
7
7
|
"dependencies": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"madge": "^6.1.0",
|
|
75
75
|
"mocha": "^10.2.0",
|
|
76
76
|
"nyc": "^15.1.0",
|
|
77
|
-
"prettier": "^3.
|
|
77
|
+
"prettier": "^3.2.4",
|
|
78
78
|
"shx": "^0.3.4",
|
|
79
79
|
"sinon": "^16.1.3",
|
|
80
80
|
"ts-node": "^10.9.2",
|