@noresources/command 0.0.0-stage → 0.1.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/README.md +76 -2
- package/package.json +43 -5
- package/src/Builders.mjs +368 -0
- package/src/Command.mjs +171 -0
- package/src/CommandHandler.mjs +14 -0
- package/src/Documentation.mjs +63 -0
- package/src/Input.mjs +42 -0
- package/src/Options.mjs +515 -0
- package/src/Parameters.mjs +350 -0
- package/src/Positionals.mjs +493 -0
- package/src/commander/adapters.mjs +343 -0
- package/src/commander/index.mjs +26 -0
- package/src/commander/package.json +1 -0
- package/src/lib/options/Verbosity.mjs +55 -0
- package/src/lib/options/index.mjs +15 -0
- package/src/utilities.mjs +40 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
assertTypes
|
|
5
|
+
} from '@noresources/core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @class Documentation
|
|
9
|
+
* @description Represents a structured descriptor for documenting software components, . This object holds two distinct fields: a brief, punchy summary
|
|
10
|
+
* (the abstract) and a detailed, contextual explanation.
|
|
11
|
+
* @property {string} abstract - A concise, single-line summary of the component's function. This is typically used in quick listings.
|
|
12
|
+
* @property {string | undefined} details - A comprehensive, multi-line description providing in-depth usage context, requirements, and side effects.
|
|
13
|
+
*/
|
|
14
|
+
export class Documentation {
|
|
15
|
+
constructor () {
|
|
16
|
+
this.abstract = '';
|
|
17
|
+
this.details = undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @description Serializes the structured documentation descriptor into a single, human-readable string.
|
|
22
|
+
* This method concatenates the primary abstract description and the detailed context,
|
|
23
|
+
* ensuring proper formatting for consumption by logging systems or UI rendering.
|
|
24
|
+
* @returns {string} The complete, formatted documentation string, combining abstract and details.
|
|
25
|
+
*/
|
|
26
|
+
toString () {
|
|
27
|
+
let s = this.abstract || '';
|
|
28
|
+
if (typeof this.details === 'string') {
|
|
29
|
+
s += '\n' + this.details;
|
|
30
|
+
}
|
|
31
|
+
return s;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @description Factory function designed to instantiate a Documentation object from various input formats.
|
|
37
|
+
* It provides a crucial mechanism for normalizing data, allowing documentation to be defined
|
|
38
|
+
* either as a single raw string or a pre-structured JavaScript object.
|
|
39
|
+
* @param {string | object} content - The raw content used to build the descriptor. If a string, the first line is assigned to 'abstract'; the rest is 'details'. If an object, the 'abstract' and 'details' properties are used directly.
|
|
40
|
+
* @returns {Documentation} A fully initialized and structured instance of the Documentation class, ready for use.
|
|
41
|
+
* @throws {Error} Throws an assertion error if the provided 'content' parameter is not of type string or object.
|
|
42
|
+
*/
|
|
43
|
+
export function createDocumentation (content) {
|
|
44
|
+
assertTypes (content, ['string', 'object'], 'content');
|
|
45
|
+
const d = new Documentation;
|
|
46
|
+
if (typeof content === 'string') {
|
|
47
|
+
const lines = content.split('\n');
|
|
48
|
+
d.abstract = lines.shift();
|
|
49
|
+
if (lines.length) {
|
|
50
|
+
d.details = lines.join('\n');
|
|
51
|
+
}
|
|
52
|
+
} else if (typeof content == 'object') {
|
|
53
|
+
d.abstract = content.abstract || '';
|
|
54
|
+
d.details = content.details;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return d;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default {
|
|
61
|
+
Documentation,
|
|
62
|
+
createDocumentation
|
|
63
|
+
};
|
package/src/Input.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @class Input
|
|
5
|
+
* @description Command line parsing result.
|
|
6
|
+
*
|
|
7
|
+
* @property {Array<Object>} commands - A read-only list of all recognized subcommands
|
|
8
|
+
* that the current command handler can delegate to. Each
|
|
9
|
+
* element represents a potential command structure.
|
|
10
|
+
* @property {Object<string, any>} options - A mapping object containing all boolean flags
|
|
11
|
+
* and named options parsed from the command line
|
|
12
|
+
* (e.g., options.verbose, options.configPath).
|
|
13
|
+
* @property {Object} arguments - A structured data representation of all positional
|
|
14
|
+
* arguments provided by the user. This structure supports
|
|
15
|
+
* access methods by both numerical index and defined semantic names.
|
|
16
|
+
*
|
|
17
|
+
* @property {number} arguments.length - The total, zero-based count of positional arguments
|
|
18
|
+
* that were successfully parsed and received.
|
|
19
|
+
* @property {Iterable<string>} arguments.keys - An iterable (like a Set) containing the
|
|
20
|
+
* semantic names defined for all positional arguments.
|
|
21
|
+
* @property {Array<{name: string, value: any}>} arguments.entries - An iterator yielding pairs
|
|
22
|
+
* of positional name and the corresponding
|
|
23
|
+
* values.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // To access the value of the third positional argument:
|
|
27
|
+
* const thirdValue = input.arguments[2];
|
|
28
|
+
*
|
|
29
|
+
* // To check if the user provided a 'force' flag:
|
|
30
|
+
* if (input.options.force) { ... }
|
|
31
|
+
*
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
*/
|
|
34
|
+
export class Input {
|
|
35
|
+
constructor () {
|
|
36
|
+
this.commands = [],
|
|
37
|
+
this.options = {},
|
|
38
|
+
this.arguments = [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default Input;
|
package/src/Options.mjs
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
transformParameterProperty
|
|
5
|
+
} from './utilities.mjs';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
merge,
|
|
9
|
+
toCamelCase,
|
|
10
|
+
isTraversable,
|
|
11
|
+
assertType,
|
|
12
|
+
assertTypes
|
|
13
|
+
} from '@noresources/core';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
createDocumentation
|
|
17
|
+
} from './Documentation.mjs';
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
Parameter
|
|
21
|
+
} from './Parameters.mjs';
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object.<string, string>} OptionTypeMap
|
|
26
|
+
*
|
|
27
|
+
* @description Defines all recognized, predefined.
|
|
28
|
+
*
|
|
29
|
+
* types for command line options.
|
|
30
|
+
* @readonly
|
|
31
|
+
*/
|
|
32
|
+
export const OPTION_TYPE = {
|
|
33
|
+
/**
|
|
34
|
+
* @type {string}
|
|
35
|
+
* @description Represents the 'SWITCH' type.
|
|
36
|
+
* Used for boolean flags; true if present,
|
|
37
|
+
* false if absent.
|
|
38
|
+
*/
|
|
39
|
+
'SWITCH': 'switch',
|
|
40
|
+
/**
|
|
41
|
+
* @type {string}
|
|
42
|
+
* @description Represents the 'COUNTER' type.
|
|
43
|
+
* Used for options whose values should be incremented
|
|
44
|
+
* during parsing.
|
|
45
|
+
*/
|
|
46
|
+
'COUNTER': 'counter',
|
|
47
|
+
/**
|
|
48
|
+
* @type {string}
|
|
49
|
+
* @description Represents the 'VALUE' type.
|
|
50
|
+
* Used for options requiring exactly one argument.
|
|
51
|
+
*/
|
|
52
|
+
'VALUE': 'value',
|
|
53
|
+
/**
|
|
54
|
+
* @type {string}
|
|
55
|
+
* @description Represents the 'VARIADIC' type.
|
|
56
|
+
* Used for options accepting zero or more arguments.
|
|
57
|
+
*/
|
|
58
|
+
'VARIADIC': 'variadic'
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @class Option
|
|
64
|
+
*
|
|
65
|
+
* @abstract
|
|
66
|
+
* @description The base class for all command line
|
|
67
|
+
* option types. It provides the common interface and
|
|
68
|
+
* state management (like names, required status)
|
|
69
|
+
* that all specialized option classes must inherit.
|
|
70
|
+
*/
|
|
71
|
+
export class Option {
|
|
72
|
+
/**
|
|
73
|
+
* Creates an instance of Option. Initializes the
|
|
74
|
+
* base properties required for any option type.
|
|
75
|
+
*
|
|
76
|
+
* @param {Object} options - A configuration object.
|
|
77
|
+
* @param {string} options.property - The canonical
|
|
78
|
+
* name property TO USE in A COMMAND LINE PARSING RESULT OBJECT.
|
|
79
|
+
* @PARAM ôARRAY<STRING> [OPTIONS.NAME] - AN ARRAY OF NAMES THAT CAN BE USED IN THE COMMAND LINE TO REPRESENT THE OPTION.
|
|
80
|
+
* @param {boolean} [options.required] - A flag
|
|
81
|
+
* indicating if the option must be present.
|
|
82
|
+
*
|
|
83
|
+
* @throws {Error} If required types within options
|
|
84
|
+
* are invalid (e.g., property is not a string).
|
|
85
|
+
*/
|
|
86
|
+
constructor (options) {
|
|
87
|
+
if ( ! (typeof options === 'object' && options)) {
|
|
88
|
+
options = {};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
assertTypes(options.property, ['string', 'undefined'], 'options.property');
|
|
92
|
+
this.property = options.property;
|
|
93
|
+
|
|
94
|
+
if (typeof options.documentation !== 'undefined') {
|
|
95
|
+
this.documentation = options.documentation;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this._names = [];
|
|
99
|
+
if (isTraversable(options.names)) {
|
|
100
|
+
for (const name of options.names) {
|
|
101
|
+
assertType(name, 'string', 'options.names[]');
|
|
102
|
+
this._names.push (name);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.required = options.required ? true : false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @returns {string} The constant identifier for
|
|
111
|
+
* this option's type.
|
|
112
|
+
* @throws {Error} Always throws, as subclasses
|
|
113
|
+
* are required to implement this getter.
|
|
114
|
+
*/
|
|
115
|
+
get type () {
|
|
116
|
+
throw new Error (this.constructor.name + ' must override type getter.');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @returns {Array<string>} An immutable array
|
|
121
|
+
* containing all possible names (flags).
|
|
122
|
+
*/
|
|
123
|
+
get names () {
|
|
124
|
+
return this._names;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @returns {string} The standardized property name.
|
|
129
|
+
* This name is determined by properties set during
|
|
130
|
+
* construction, falling back to flag names.
|
|
131
|
+
*/
|
|
132
|
+
get property () {
|
|
133
|
+
if (typeof this._property == 'string') {
|
|
134
|
+
return this._property;
|
|
135
|
+
}
|
|
136
|
+
let fallback;
|
|
137
|
+
for (const n of this._names) {
|
|
138
|
+
if (n.length > 1) {
|
|
139
|
+
return toCamelCase(n);
|
|
140
|
+
}
|
|
141
|
+
if (!fallback) {
|
|
142
|
+
fallback = n;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return fallback;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Programmatically sets the property name.
|
|
151
|
+
*
|
|
152
|
+
* @param {string} value - The desired property name.
|
|
153
|
+
* @throws {Error} If the provided value is not a string.
|
|
154
|
+
*/
|
|
155
|
+
set property (value) {
|
|
156
|
+
assertTypes(value, ['string', 'undefined'], 'value');
|
|
157
|
+
this._property = value;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Gets the option {Documentation} object.
|
|
163
|
+
*
|
|
164
|
+
* @returns {Documentation}
|
|
165
|
+
*/
|
|
166
|
+
get documentation () {
|
|
167
|
+
return this._documentation;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Sets the option documentation.
|
|
172
|
+
*
|
|
173
|
+
* The input value is normalized to a {Documentation} object.
|
|
174
|
+
*
|
|
175
|
+
* @param {Documentation|string|undefined value - User-defined documentation.
|
|
176
|
+
* If undefined, the documentation object is cleared.
|
|
177
|
+
*/
|
|
178
|
+
set documentation (value) {
|
|
179
|
+
if (!value) {
|
|
180
|
+
this._documentation = undefined;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this._documentation = createDocumentation(value);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Coerces the raw input value into the correct data
|
|
189
|
+
* type and validates it against the option's rules.
|
|
190
|
+
*
|
|
191
|
+
* @param {mixed} value - The raw value supplied by the
|
|
192
|
+
* command line parser.
|
|
193
|
+
* @returns {mixed} The coerced and validated value.
|
|
194
|
+
*/
|
|
195
|
+
parse (value) {
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @class SwitchOption
|
|
202
|
+
* @extends {Option}
|
|
203
|
+
* @description Represents a simple boolean flag. When
|
|
204
|
+
* this option is encountered, the associated value is
|
|
205
|
+
* strictly a boolean: true if present, false if absent.
|
|
206
|
+
* This is the standard mechanism for flags like
|
|
207
|
+
* `--verbose` or `--dry-run`.
|
|
208
|
+
*/
|
|
209
|
+
export class SwitchOption extends Option {
|
|
210
|
+
/**
|
|
211
|
+
* Creates a SwitchOption instance.
|
|
212
|
+
*
|
|
213
|
+
* @param {Object} options - Configuration options
|
|
214
|
+
* passed to the base Option class.
|
|
215
|
+
* @constructor
|
|
216
|
+
*/
|
|
217
|
+
constructor (options) {
|
|
218
|
+
super (options);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @returns {string} The constant representing the
|
|
223
|
+
* SWITCH type.
|
|
224
|
+
*/
|
|
225
|
+
get type() {
|
|
226
|
+
return OPTION_TYPE.SWITCH;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Executes the parsing logic for a Switch option.
|
|
231
|
+
* It converts the presence of the flag into a boolean.
|
|
232
|
+
*
|
|
233
|
+
* @param {mixed} value - The raw input value (typically
|
|
234
|
+
* a truthy value if present).
|
|
235
|
+
* @returns {boolean} True if the flag was provided,
|
|
236
|
+
* false otherwise.
|
|
237
|
+
*/
|
|
238
|
+
parse (value) {
|
|
239
|
+
return value ? true : false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* @class CounterOption
|
|
245
|
+
* @extends {Option}
|
|
246
|
+
* @description Represents a numeric counter. This
|
|
247
|
+
* option tracks recurrence; the value is incremented
|
|
248
|
+
* each time the option is used. Common usage is
|
|
249
|
+
* for tracking options like `--port` or `--retries`.
|
|
250
|
+
*
|
|
251
|
+
* @param {Object} options - Configuration object.
|
|
252
|
+
* @param {number} [options.initial=0] - The starting
|
|
253
|
+
* integer value before any arguments are processed.
|
|
254
|
+
*
|
|
255
|
+
* @throws {Error} If the `initial` value provided
|
|
256
|
+
* is not a number.
|
|
257
|
+
*/
|
|
258
|
+
export class CounterOption extends Option {
|
|
259
|
+
/**
|
|
260
|
+
* Creates a CounterOption instance.
|
|
261
|
+
*
|
|
262
|
+
* @param {Object} options - Configuration object.
|
|
263
|
+
* @constructor
|
|
264
|
+
*/
|
|
265
|
+
constructor (options) {
|
|
266
|
+
options = merge ({
|
|
267
|
+
'initial': 0
|
|
268
|
+
}, options);
|
|
269
|
+
|
|
270
|
+
assertType(options.initial, 'number', 'options.initial');
|
|
271
|
+
|
|
272
|
+
super (options);
|
|
273
|
+
this.initial = options.initial;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @returns {string} The constant representing the
|
|
278
|
+
* COUNTER type.
|
|
279
|
+
*/
|
|
280
|
+
get type () {
|
|
281
|
+
return OPTION_TYPE.COUNTER;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Parses the input value, ensuring it is a valid
|
|
286
|
+
* integer.
|
|
287
|
+
*
|
|
288
|
+
* @param {mixed} value - The raw string input from
|
|
289
|
+
* the command line argument.
|
|
290
|
+
* @returns {number} The integer value parsed from the input.
|
|
291
|
+
* @throws {Error} If the provided value cannot be
|
|
292
|
+
* successfully parsed into an integer.
|
|
293
|
+
*/
|
|
294
|
+
parse (value) {
|
|
295
|
+
value = parseInt (value);
|
|
296
|
+
if (isNaN(value)) {
|
|
297
|
+
throw new Error ('Could not convert value to integer');
|
|
298
|
+
}
|
|
299
|
+
return value;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @class ValueOption
|
|
305
|
+
* @extends {Option}
|
|
306
|
+
* @description Represents a standard option requiring
|
|
307
|
+
* one mandatory associated value. This option relies on
|
|
308
|
+
* a separate {@link Parameter} descriptor to manage data
|
|
309
|
+
* type, validation rules, and coercion logic for the value.
|
|
310
|
+
*
|
|
311
|
+
* @param {Object} options - Configuration object.
|
|
312
|
+
* @param {Parameter} options.parameter - The required
|
|
313
|
+
* descriptor instance defining the value's structure.
|
|
314
|
+
*
|
|
315
|
+
* @throws {Error} If the `parameter` descriptor is missing
|
|
316
|
+
* or is not a valid Parameter object.
|
|
317
|
+
*/
|
|
318
|
+
export class ValueOption extends Option {
|
|
319
|
+
/**
|
|
320
|
+
* Creates a ValueOption instance.
|
|
321
|
+
*
|
|
322
|
+
* @param {Object} options - The configuration object.
|
|
323
|
+
* @param {mixed} [options.default=undefined] - An optional
|
|
324
|
+
* default value to use if the option is not provided.
|
|
325
|
+
* @param {Parameter} [options.parameters] - The parameter
|
|
326
|
+
* descriptor, used to validate and coerce the input value.
|
|
327
|
+
*
|
|
328
|
+
* @throws {Error} If the `parameter` descriptor is missing.
|
|
329
|
+
*/
|
|
330
|
+
constructor (options) {
|
|
331
|
+
options = merge({
|
|
332
|
+
//'defaul': undefined,
|
|
333
|
+
//'parameter': undefined
|
|
334
|
+
}, options);
|
|
335
|
+
|
|
336
|
+
assertTypes(options.parameter, ['undefined', Parameter], 'options.parameter');
|
|
337
|
+
|
|
338
|
+
super (options);
|
|
339
|
+
this.default = options.default;
|
|
340
|
+
this.parameter = options.parameter;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* @returns {string} The constant representing the VALUE type.
|
|
345
|
+
*/
|
|
346
|
+
get type() {
|
|
347
|
+
return OPTION_TYPE.VALUE;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Executes the parsing logic. The value is coerced
|
|
352
|
+
* and then validated against the associated
|
|
353
|
+
* Parameter descriptor.
|
|
354
|
+
*
|
|
355
|
+
* @param {mixed} value - The raw value provided by
|
|
356
|
+
* the command line.
|
|
357
|
+
* @returns {mixed} The fully coerced and validated value.
|
|
358
|
+
*/
|
|
359
|
+
parse (value) {
|
|
360
|
+
if (!(this.parameter instanceof Parameter)) {
|
|
361
|
+
return value;
|
|
362
|
+
}
|
|
363
|
+
value = this.parameter.coerce (value);
|
|
364
|
+
this.parameter.validate(value);
|
|
365
|
+
return value;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* @class VariadicOption
|
|
371
|
+
* @extends {Option}
|
|
372
|
+
* @description Represents an option capable of accepting
|
|
373
|
+
* a variable number of associated values (a list). This
|
|
374
|
+
* mechanism is designed to collect multiple inputs
|
|
375
|
+
* (like filenames or paths) into a single array.
|
|
376
|
+
*
|
|
377
|
+
* @param {Object} options - Configuration object.
|
|
378
|
+
* @param {number} [options.min=0] - The minimum number
|
|
379
|
+
* of values that must be provided. Must be 0 or greater.
|
|
380
|
+
* @param {Parameter} [options.parameters] - The descriptor
|
|
381
|
+
* defining the type and validation rules for each element.
|
|
382
|
+
*
|
|
383
|
+
* @throws {Error} If the `parameters` descriptor is
|
|
384
|
+
* missing or is not a valid Parameter object.
|
|
385
|
+
*/
|
|
386
|
+
export class VariadicOption extends Option {
|
|
387
|
+
/**
|
|
388
|
+
* Creates a VariadicOption instance.
|
|
389
|
+
*
|
|
390
|
+
* @param {Object} options - Configuration object.
|
|
391
|
+
* @constructor
|
|
392
|
+
*/
|
|
393
|
+
constructor (options) {
|
|
394
|
+
options = merge ({
|
|
395
|
+
'min': 0
|
|
396
|
+
}, options);
|
|
397
|
+
|
|
398
|
+
assertType(options.min, 'number', 'options.min');
|
|
399
|
+
assertTypes (options.parameters, ['undefined', Parameter], 'options.parameters');
|
|
400
|
+
|
|
401
|
+
super (options);
|
|
402
|
+
this.min = options.min;
|
|
403
|
+
this.parameters = options.parameters;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @returns {string} The constant representing the
|
|
408
|
+
* VARIADIC type.
|
|
409
|
+
*/
|
|
410
|
+
get type () {
|
|
411
|
+
return OPTION_TYPE.VARIADIC;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Parses the input values, collecting them and
|
|
416
|
+
* ensuring each value adheres to the rules defined
|
|
417
|
+
* in the Parameter descriptor.
|
|
418
|
+
*
|
|
419
|
+
* @param {Array<mixed>} value - The array of raw values
|
|
420
|
+
* provided by the command line.
|
|
421
|
+
* @returns {Array<mixed>} The array of values after
|
|
422
|
+
* successful coercion and validation.
|
|
423
|
+
*/
|
|
424
|
+
parse (value) {
|
|
425
|
+
if (!(this.parameters instanceof Parameter)) {
|
|
426
|
+
return value;
|
|
427
|
+
}
|
|
428
|
+
value = this.parameters.coerce (value);
|
|
429
|
+
this.parameters.validate(value);
|
|
430
|
+
return value;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function autodetectType (options) {
|
|
435
|
+
const mapping = [
|
|
436
|
+
['parameters', OPTION_TYPE.VARIADIC],
|
|
437
|
+
['parameter', OPTION_TYPE.VALUE],
|
|
438
|
+
['initial', OPTION_TYPE.COUNTER],
|
|
439
|
+
['default', OPTION_TYPE.VALUE],
|
|
440
|
+
['min', OPTION_TYPE.VARIADIC],
|
|
441
|
+
['max', OPTION_TYPE.VARIADIC]
|
|
442
|
+
];
|
|
443
|
+
|
|
444
|
+
let t;
|
|
445
|
+
for (const m of mapping) {
|
|
446
|
+
const [name, type] = m;
|
|
447
|
+
if (typeof options[name] !== 'undefined') {
|
|
448
|
+
if (typeof t === 'string') {
|
|
449
|
+
throw new Error ('Ambiguous option descriptor');
|
|
450
|
+
}
|
|
451
|
+
t = type;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return t || OPTION_TYPE.SWITCH;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* @function createOption
|
|
460
|
+
* @description A factory function that creates and
|
|
461
|
+
* configures the appropriate option instance based on
|
|
462
|
+
* the provided type and configuration. This handles the
|
|
463
|
+
* runtime instantiation of Option subclasses.
|
|
464
|
+
*
|
|
465
|
+
* @param {(string|Object)} type - The type of option
|
|
466
|
+
* to create. This can be a raw string constant
|
|
467
|
+
* (e.g., 'switch') or a configuration object.
|
|
468
|
+
* @param {Object} [options] - Optional configuration
|
|
469
|
+
* object, containing settings for the option's
|
|
470
|
+
* behavior (e.g., names, required status).
|
|
471
|
+
*
|
|
472
|
+
* @returns {SwitchOption|CounterOption|ValueOption|VariadicOption}
|
|
473
|
+
* A fully initialized and configured option instance.
|
|
474
|
+
*
|
|
475
|
+
* @throws {Error} If the provided option type is
|
|
476
|
+
* unrecognized or unsupported by this factory.
|
|
477
|
+
*/
|
|
478
|
+
export function createOption () {
|
|
479
|
+
let type = arguments[0];
|
|
480
|
+
let options = arguments[1] || {};
|
|
481
|
+
|
|
482
|
+
if (typeof type === 'object') {
|
|
483
|
+
options = type;
|
|
484
|
+
type = options.type;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (typeof type !== 'string') {
|
|
488
|
+
type = autodetectType(options || {});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
switch (type) {
|
|
492
|
+
case OPTION_TYPE.SWITCH: return new SwitchOption (options);
|
|
493
|
+
case OPTION_TYPE.COUNTER: return new CounterOption(options);
|
|
494
|
+
case OPTION_TYPE.VALUE: {
|
|
495
|
+
transformParameterProperty.apply (this, [options, 'parameter']);
|
|
496
|
+
return new ValueOption (options);
|
|
497
|
+
}
|
|
498
|
+
case OPTION_TYPE.VARIADIC: {
|
|
499
|
+
transformParameterProperty.apply (this, [options, 'parameters']);
|
|
500
|
+
return new VariadicOption(options);
|
|
501
|
+
}
|
|
502
|
+
default:
|
|
503
|
+
throw new Error ('Invalid option type: ' + type);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export default {
|
|
508
|
+
OPTION_TYPE,
|
|
509
|
+
Option,
|
|
510
|
+
SwitchOption,
|
|
511
|
+
CounterOption,
|
|
512
|
+
ValueOption,
|
|
513
|
+
VariadicOption,
|
|
514
|
+
createOption
|
|
515
|
+
};
|