-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathutils.js
160 lines (145 loc) · 5.16 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
'use strict';
const {
ArrayPrototypeFind,
ObjectEntries,
StringPrototypeCharAt,
StringPrototypeIncludes,
StringPrototypeSlice,
StringPrototypeStartsWith,
} = require('./primordials');
const {
validateObject
} = require('./validators');
// These are internal utilities to make the parsing logic easier to read, and
// add lots of detail for the curious. They are in a separate file to allow
// unit testing, although that is not essential (this could be rolled into
// main file and just tested implicitly via API).
//
// These routines are for internal use, not for export to client.
/**
* Determines if the argument may be used as an option value.
* NB: We are choosing not to accept option-ish arguments.
* @example
* isOptionValue('V') // returns true
* isOptionValue('-v') // returns false
* isOptionValue('--foo') // returns false
* isOptionValue(undefined) // returns false
*/
function isOptionValue(value) {
if (value == null) return false;
if (value === '-') return true; // e.g. representing stdin/stdout for file
// Open Group Utility Conventions are that an option-argument
// is the argument after the option, and may start with a dash.
// However, we are currently rejecting these and prioritising the
// option-like appearance of the argument. Rejection allows more error
// detection for strict:true, but comes at the cost of rejecting intended
// values starting with a dash, especially negative numbers.
return !StringPrototypeStartsWith(value, '-');
}
/**
* Determines if `arg` is a just a short option.
* @example '-f'
*/
function isLoneShortOption(arg) {
return arg.length === 2 &&
StringPrototypeCharAt(arg, 0) === '-' &&
StringPrototypeCharAt(arg, 1) !== '-';
}
/**
* Determines if `arg` is a lone long option.
* @example
* isLoneLongOption('a') // returns false
* isLoneLongOption('-a') // returns false
* isLoneLongOption('--foo) // returns true
* isLoneLongOption('--foo=bar') // returns false
*/
function isLoneLongOption(arg) {
return arg.length > 2 &&
StringPrototypeStartsWith(arg, '--') &&
!StringPrototypeIncludes(StringPrototypeSlice(arg, 3), '=');
}
/**
* Determines if `arg` is a long option and value in the same argument.
* @example
* isLongOptionAndValue('--foo) // returns false
* isLongOptionAndValue('--foo=bar') // returns true
*/
function isLongOptionAndValue(arg) {
return arg.length > 2 &&
StringPrototypeStartsWith(arg, '--') &&
StringPrototypeIncludes(StringPrototypeSlice(arg, 3), '=');
}
/**
* Determines if `arg` is a short option group.
*
* See Guideline 5 of the [Open Group Utility Conventions](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html).
* One or more options without option-arguments, followed by at most one
* option that takes an option-argument, should be accepted when grouped
* behind one '-' delimiter.
* @example
* isShortOptionGroup('-a', {}) // returns false
* isShortOptionGroup('-ab', {}) // returns true
* // -fb is an option and a value, not a short option group
* isShortOptionGroup('-fb', {
* options: { f: { type: 'string' }}
* }) // returns false
* isShortOptionGroup('-bf', {
* options: { f: { type: 'string' }}
* }) // returns true
* // -bfb is an edge case, return true and caller sorts it out
* isShortOptionGroup('-bfb', {
* options: { f: { type: 'string' }}
* }) // returns true
*/
function isShortOptionGroup(arg, options) {
if (arg.length <= 2) return false;
if (StringPrototypeCharAt(arg, 0) !== '-') return false;
if (StringPrototypeCharAt(arg, 1) === '-') return false;
const firstShort = StringPrototypeCharAt(arg, 1);
const longOption = findLongOptionForShort(firstShort, options);
return options[longOption]?.type !== 'string';
}
/**
* Determine is arg is a short string option followed by its value.
* @example
* isShortOptionAndValue('-a, {}); // returns false
* isShortOptionAndValue('-ab, {}); // returns false
* isShortOptionAndValue('-fFILE', {
* options: { foo: { short: 'f', type: 'string' }}
* }) // returns true
*/
function isShortOptionAndValue(arg, options) {
validateObject(options, 'options');
if (arg.length <= 2) return false;
if (StringPrototypeCharAt(arg, 0) !== '-') return false;
if (StringPrototypeCharAt(arg, 1) === '-') return false;
const shortOption = StringPrototypeCharAt(arg, 1);
const longOption = findLongOptionForShort(shortOption, options);
return options[longOption]?.type === 'string';
}
/**
* Find the long option associated with a short option. Looks for a configured
* `short` and returns the short option itself if a long option is not found.
* @example
* findLongOptionForShort('a', {}) // returns 'a'
* findLongOptionForShort('b', {
* options: { bar: { short: 'b' } }
* }) // returns 'bar'
*/
function findLongOptionForShort(shortOption, options) {
validateObject(options, 'options');
const { 0: longOption } = ArrayPrototypeFind(
ObjectEntries(options),
({ 1: optionConfig }) => optionConfig.short === shortOption
) || [];
return longOption || shortOption;
}
module.exports = {
findLongOptionForShort,
isLoneLongOption,
isLoneShortOption,
isLongOptionAndValue,
isOptionValue,
isShortOptionAndValue,
isShortOptionGroup
};