jackspeak 4.0.1 → 4.0.3
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/dist/commonjs/index.d.ts +61 -69
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +212 -300
- package/dist/commonjs/index.js.map +1 -1
- package/dist/esm/index.d.ts +61 -69
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +207 -296
- package/dist/esm/index.js.map +1 -1
- package/package.json +10 -11
- package/dist/commonjs/parse-args-cjs.cjs.map +0 -1
- package/dist/commonjs/parse-args-cjs.d.cts.map +0 -1
- package/dist/commonjs/parse-args.d.ts +0 -4
- package/dist/commonjs/parse-args.js +0 -50
- package/dist/esm/parse-args.d.ts +0 -4
- package/dist/esm/parse-args.d.ts.map +0 -1
- package/dist/esm/parse-args.js +0 -26
- package/dist/esm/parse-args.js.map +0 -1
package/dist/esm/index.js
CHANGED
|
@@ -1,19 +1,54 @@
|
|
|
1
|
-
import { inspect } from 'node:util';
|
|
2
|
-
import { parseArgs } from './parse-args.js';
|
|
1
|
+
import { inspect, parseArgs, } from 'node:util';
|
|
3
2
|
// it's a tiny API, just cast it inline, it's fine
|
|
4
3
|
//@ts-ignore
|
|
5
4
|
import cliui from '@isaacs/cliui';
|
|
6
5
|
import { basename } from 'node:path';
|
|
7
|
-
const
|
|
6
|
+
export const isConfigType = (t) => typeof t === 'string' &&
|
|
7
|
+
(t === 'string' || t === 'number' || t === 'boolean');
|
|
8
|
+
const isValidValue = (v, type, multi) => {
|
|
9
|
+
if (multi) {
|
|
10
|
+
if (!Array.isArray(v))
|
|
11
|
+
return false;
|
|
12
|
+
return !v.some((v) => !isValidValue(v, type, false));
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(v))
|
|
15
|
+
return false;
|
|
16
|
+
return typeof v === type;
|
|
17
|
+
};
|
|
18
|
+
const isValidOption = (v, vo) => !!vo &&
|
|
19
|
+
(Array.isArray(v) ? v.every(x => isValidOption(x, vo)) : vo.includes(v));
|
|
20
|
+
/**
|
|
21
|
+
* Determine whether an unknown object is a {@link ConfigOption} based only
|
|
22
|
+
* on its `type` and `multiple` property
|
|
23
|
+
*/
|
|
24
|
+
export const isConfigOptionOfType = (o, type, multi) => !!o &&
|
|
25
|
+
typeof o === 'object' &&
|
|
26
|
+
isConfigType(o.type) &&
|
|
27
|
+
o.type === type &&
|
|
28
|
+
!!o.multiple === multi;
|
|
29
|
+
/**
|
|
30
|
+
* Determine whether an unknown object is a {@link ConfigOption} based on
|
|
31
|
+
* it having all valid properties
|
|
32
|
+
*/
|
|
33
|
+
export const isConfigOption = (o, type, multi) => isConfigOptionOfType(o, type, multi) &&
|
|
34
|
+
undefOrType(o.short, 'string') &&
|
|
35
|
+
undefOrType(o.description, 'string') &&
|
|
36
|
+
undefOrType(o.hint, 'string') &&
|
|
37
|
+
undefOrType(o.validate, 'function') &&
|
|
38
|
+
(o.type === 'boolean' ?
|
|
39
|
+
o.validOptions === undefined
|
|
40
|
+
: undefOrTypeArray(o.validOptions, o.type)) &&
|
|
41
|
+
(o.default === undefined || isValidValue(o.default, type, multi));
|
|
42
|
+
const isHeading = (r) => r.type === 'heading';
|
|
43
|
+
const isDescription = (r) => r.type === 'description';
|
|
44
|
+
const width = Math.min(process?.stdout?.columns ?? 80, 80);
|
|
8
45
|
// indentation spaces from heading level
|
|
9
46
|
const indent = (n) => (n - 1) * 2;
|
|
10
|
-
const toEnvKey = (pref, key) =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
.replace(/ /g, '_');
|
|
16
|
-
};
|
|
47
|
+
const toEnvKey = (pref, key) => [pref, key.replace(/[^a-zA-Z0-9]+/g, ' ')]
|
|
48
|
+
.join(' ')
|
|
49
|
+
.trim()
|
|
50
|
+
.toUpperCase()
|
|
51
|
+
.replace(/ /g, '_');
|
|
17
52
|
const toEnvVal = (value, delim = '\n') => {
|
|
18
53
|
const str = typeof value === 'string' ? value
|
|
19
54
|
: typeof value === 'boolean' ?
|
|
@@ -24,7 +59,7 @@ const toEnvVal = (value, delim = '\n') => {
|
|
|
24
59
|
value.map((v) => toEnvVal(v)).join(delim)
|
|
25
60
|
: /* c8 ignore start */ undefined;
|
|
26
61
|
if (typeof str !== 'string') {
|
|
27
|
-
throw new Error(`could not serialize value to environment: ${JSON.stringify(value)}
|
|
62
|
+
throw new Error(`could not serialize value to environment: ${JSON.stringify(value)}`, { cause: { code: 'JACKSPEAK' } });
|
|
28
63
|
}
|
|
29
64
|
/* c8 ignore stop */
|
|
30
65
|
return str;
|
|
@@ -35,254 +70,144 @@ const fromEnvVal = (env, type, multiple, delim = '\n') => (multiple ?
|
|
|
35
70
|
: type === 'string' ? env
|
|
36
71
|
: type === 'boolean' ? env === '1'
|
|
37
72
|
: +env.trim());
|
|
38
|
-
export const isConfigType = (t) => typeof t === 'string' &&
|
|
39
|
-
(t === 'string' || t === 'number' || t === 'boolean');
|
|
40
73
|
const undefOrType = (v, t) => v === undefined || typeof v === t;
|
|
41
74
|
const undefOrTypeArray = (v, t) => v === undefined || (Array.isArray(v) && v.every(x => typeof x === t));
|
|
42
|
-
const isValidOption = (v, vo) => Array.isArray(v) ? v.every(x => isValidOption(x, vo)) : vo.includes(v);
|
|
43
75
|
// print the value type, for error message reporting
|
|
44
76
|
const valueType = (v) => typeof v === 'string' ? 'string'
|
|
45
77
|
: typeof v === 'boolean' ? 'boolean'
|
|
46
78
|
: typeof v === 'number' ? 'number'
|
|
47
79
|
: Array.isArray(v) ?
|
|
48
|
-
joinTypes([...new Set(v.map(v => valueType(v)))])
|
|
80
|
+
`${joinTypes([...new Set(v.map(v => valueType(v)))])}[]`
|
|
49
81
|
: `${v.type}${v.multiple ? '[]' : ''}`;
|
|
50
82
|
const joinTypes = (types) => types.length === 1 && typeof types[0] === 'string' ?
|
|
51
83
|
types[0]
|
|
52
84
|
: `(${types.join('|')})`;
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
: undefOrTypeArray(o.validOptions, o.type)) &&
|
|
74
|
-
(o.default === undefined || isValidValue(o.default, type, multi)) &&
|
|
75
|
-
!!o.multiple === multi;
|
|
76
|
-
function num(o = {}) {
|
|
77
|
-
const { default: def, validate: val, validOptions, ...rest } = o;
|
|
78
|
-
if (def !== undefined && !isValidValue(def, 'number', false)) {
|
|
79
|
-
throw new TypeError('invalid default value', {
|
|
80
|
-
cause: {
|
|
81
|
-
found: def,
|
|
82
|
-
wanted: 'number',
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
if (!undefOrTypeArray(validOptions, 'number')) {
|
|
87
|
-
throw new TypeError('invalid validOptions', {
|
|
88
|
-
cause: {
|
|
89
|
-
found: validOptions,
|
|
90
|
-
wanted: 'number[]',
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
const validate = val ?
|
|
95
|
-
val
|
|
96
|
-
: undefined;
|
|
97
|
-
return {
|
|
98
|
-
...rest,
|
|
99
|
-
default: def,
|
|
100
|
-
validate,
|
|
101
|
-
validOptions,
|
|
102
|
-
type: 'number',
|
|
103
|
-
multiple: false,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
function numList(o = {}) {
|
|
107
|
-
const { default: def, validate: val, validOptions, ...rest } = o;
|
|
108
|
-
if (def !== undefined && !isValidValue(def, 'number', true)) {
|
|
109
|
-
throw new TypeError('invalid default value', {
|
|
110
|
-
cause: {
|
|
111
|
-
found: def,
|
|
112
|
-
wanted: 'number[]',
|
|
113
|
-
},
|
|
114
|
-
});
|
|
85
|
+
const validateFieldMeta = (field, fieldMeta) => {
|
|
86
|
+
if (fieldMeta) {
|
|
87
|
+
if (field.type !== undefined && field.type !== fieldMeta.type) {
|
|
88
|
+
throw new TypeError(`invalid type`, {
|
|
89
|
+
cause: {
|
|
90
|
+
found: field.type,
|
|
91
|
+
wanted: [fieldMeta.type, undefined],
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (field.multiple !== undefined &&
|
|
96
|
+
!!field.multiple !== fieldMeta.multiple) {
|
|
97
|
+
throw new TypeError(`invalid multiple`, {
|
|
98
|
+
cause: {
|
|
99
|
+
found: field.multiple,
|
|
100
|
+
wanted: [fieldMeta.multiple, undefined],
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return fieldMeta;
|
|
115
105
|
}
|
|
116
|
-
if (!
|
|
117
|
-
throw new TypeError(
|
|
106
|
+
if (!isConfigType(field.type)) {
|
|
107
|
+
throw new TypeError(`invalid type`, {
|
|
118
108
|
cause: {
|
|
119
|
-
found:
|
|
120
|
-
wanted: 'number
|
|
109
|
+
found: field.type,
|
|
110
|
+
wanted: ['string', 'number', 'boolean'],
|
|
121
111
|
},
|
|
122
112
|
});
|
|
123
113
|
}
|
|
124
|
-
const validate = val ?
|
|
125
|
-
val
|
|
126
|
-
: undefined;
|
|
127
114
|
return {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
validate,
|
|
131
|
-
validOptions,
|
|
132
|
-
type: 'number',
|
|
133
|
-
multiple: true,
|
|
115
|
+
type: field.type,
|
|
116
|
+
multiple: !!field.multiple,
|
|
134
117
|
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
default: def,
|
|
160
|
-
validate,
|
|
161
|
-
validOptions,
|
|
162
|
-
type: 'string',
|
|
163
|
-
multiple: false,
|
|
118
|
+
};
|
|
119
|
+
const validateField = (o, type, multiple) => {
|
|
120
|
+
const validateValidOptions = (def, validOptions) => {
|
|
121
|
+
if (!undefOrTypeArray(validOptions, type)) {
|
|
122
|
+
throw new TypeError('invalid validOptions', {
|
|
123
|
+
cause: {
|
|
124
|
+
found: validOptions,
|
|
125
|
+
wanted: valueType({ type, multiple: true }),
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (def !== undefined && validOptions !== undefined) {
|
|
130
|
+
const valid = Array.isArray(def) ?
|
|
131
|
+
def.every(v => validOptions.includes(v))
|
|
132
|
+
: validOptions.includes(def);
|
|
133
|
+
if (!valid) {
|
|
134
|
+
throw new TypeError('invalid default value not in validOptions', {
|
|
135
|
+
cause: {
|
|
136
|
+
found: def,
|
|
137
|
+
wanted: validOptions,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
164
142
|
};
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const { default: def, validate: val, validOptions, ...rest } = o;
|
|
168
|
-
if (def !== undefined && !isValidValue(def, 'string', true)) {
|
|
143
|
+
if (o.default !== undefined &&
|
|
144
|
+
!isValidValue(o.default, type, multiple)) {
|
|
169
145
|
throw new TypeError('invalid default value', {
|
|
170
146
|
cause: {
|
|
171
|
-
found:
|
|
172
|
-
wanted:
|
|
147
|
+
found: o.default,
|
|
148
|
+
wanted: valueType({ type, multiple }),
|
|
173
149
|
},
|
|
174
150
|
});
|
|
175
151
|
}
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
found: validOptions,
|
|
180
|
-
wanted: 'string[]',
|
|
181
|
-
},
|
|
182
|
-
});
|
|
152
|
+
if (isConfigOptionOfType(o, 'number', false) ||
|
|
153
|
+
isConfigOptionOfType(o, 'number', true)) {
|
|
154
|
+
validateValidOptions(o.default, o.validOptions);
|
|
183
155
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
...rest,
|
|
189
|
-
default: def,
|
|
190
|
-
validate,
|
|
191
|
-
validOptions,
|
|
192
|
-
type: 'string',
|
|
193
|
-
multiple: true,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
function flag(o = {}) {
|
|
197
|
-
const { hint, default: def, validate: val, ...rest } = o;
|
|
198
|
-
delete rest.validOptions;
|
|
199
|
-
if (def !== undefined && !isValidValue(def, 'boolean', false)) {
|
|
200
|
-
throw new TypeError('invalid default value');
|
|
201
|
-
}
|
|
202
|
-
const validate = val ?
|
|
203
|
-
val
|
|
204
|
-
: undefined;
|
|
205
|
-
if (hint !== undefined) {
|
|
206
|
-
throw new TypeError('cannot provide hint for flag');
|
|
156
|
+
else if (isConfigOptionOfType(o, 'string', false) ||
|
|
157
|
+
isConfigOptionOfType(o, 'string', true)) {
|
|
158
|
+
validateValidOptions(o.default, o.validOptions);
|
|
207
159
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
function flagList(o = {}) {
|
|
217
|
-
const { hint, default: def, validate: val, ...rest } = o;
|
|
218
|
-
delete rest.validOptions;
|
|
219
|
-
if (def !== undefined && !isValidValue(def, 'boolean', true)) {
|
|
220
|
-
throw new TypeError('invalid default value');
|
|
221
|
-
}
|
|
222
|
-
const validate = val ?
|
|
223
|
-
val
|
|
224
|
-
: undefined;
|
|
225
|
-
if (hint !== undefined) {
|
|
226
|
-
throw new TypeError('cannot provide hint for flag list');
|
|
160
|
+
else if (isConfigOptionOfType(o, 'boolean', false) ||
|
|
161
|
+
isConfigOptionOfType(o, 'boolean', true)) {
|
|
162
|
+
if (o.hint !== undefined) {
|
|
163
|
+
throw new TypeError('cannot provide hint for flag');
|
|
164
|
+
}
|
|
165
|
+
if (o.validOptions !== undefined) {
|
|
166
|
+
throw new TypeError('cannot provide validOptions for flag');
|
|
167
|
+
}
|
|
227
168
|
}
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
default: def,
|
|
231
|
-
validate,
|
|
232
|
-
type: 'boolean',
|
|
233
|
-
multiple: true,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
169
|
+
return o;
|
|
170
|
+
};
|
|
236
171
|
const toParseArgsOptionsConfig = (options) => {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
: String(config.default),
|
|
259
|
-
};
|
|
172
|
+
return Object.entries(options).reduce((acc, [longOption, o]) => {
|
|
173
|
+
const p = {
|
|
174
|
+
type: 'string',
|
|
175
|
+
multiple: !!o.multiple,
|
|
176
|
+
...(typeof o.short === 'string' ? { short: o.short } : undefined),
|
|
177
|
+
};
|
|
178
|
+
const setNoBool = () => {
|
|
179
|
+
if (!longOption.startsWith('no-') && !options[`no-${longOption}`]) {
|
|
180
|
+
acc[`no-${longOption}`] = {
|
|
181
|
+
type: 'boolean',
|
|
182
|
+
multiple: !!o.multiple,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const setDefault = (def, fn) => {
|
|
187
|
+
if (def !== undefined) {
|
|
188
|
+
p.default = fn(def);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
if (isConfigOption(o, 'number', false)) {
|
|
192
|
+
setDefault(o.default, String);
|
|
260
193
|
}
|
|
261
|
-
else {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
type: 'boolean',
|
|
278
|
-
multiple: config.multiple,
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return c;
|
|
194
|
+
else if (isConfigOption(o, 'number', true)) {
|
|
195
|
+
setDefault(o.default, d => d.map(v => String(v)));
|
|
196
|
+
}
|
|
197
|
+
else if (isConfigOption(o, 'string', false) ||
|
|
198
|
+
isConfigOption(o, 'string', true)) {
|
|
199
|
+
setDefault(o.default, v => v);
|
|
200
|
+
}
|
|
201
|
+
else if (isConfigOption(o, 'boolean', false) ||
|
|
202
|
+
isConfigOption(o, 'boolean', true)) {
|
|
203
|
+
p.type = 'boolean';
|
|
204
|
+
setDefault(o.default, v => v);
|
|
205
|
+
setNoBool();
|
|
206
|
+
}
|
|
207
|
+
acc[longOption] = p;
|
|
208
|
+
return acc;
|
|
209
|
+
}, {});
|
|
283
210
|
};
|
|
284
|
-
const isHeading = (r) => r.type === 'heading';
|
|
285
|
-
const isDescription = (r) => r.type === 'description';
|
|
286
211
|
/**
|
|
287
212
|
* Class returned by the {@link jack} function and all configuration
|
|
288
213
|
* definition methods. This is what gets chained together.
|
|
@@ -320,16 +245,12 @@ export class Jack {
|
|
|
320
245
|
this.validate(values);
|
|
321
246
|
}
|
|
322
247
|
catch (er) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
e.cause = { path: source };
|
|
330
|
-
}
|
|
248
|
+
if (source && er instanceof Error) {
|
|
249
|
+
/* c8 ignore next */
|
|
250
|
+
const cause = typeof er.cause === 'object' ? er.cause : {};
|
|
251
|
+
er.cause = { ...cause, path: source };
|
|
331
252
|
}
|
|
332
|
-
throw
|
|
253
|
+
throw er;
|
|
333
254
|
}
|
|
334
255
|
for (const [field, value] of Object.entries(values)) {
|
|
335
256
|
const my = this.#configSet[field];
|
|
@@ -337,7 +258,10 @@ export class Jack {
|
|
|
337
258
|
/* c8 ignore start */
|
|
338
259
|
if (!my) {
|
|
339
260
|
throw new Error('unexpected field in config set: ' + field, {
|
|
340
|
-
cause: {
|
|
261
|
+
cause: {
|
|
262
|
+
code: 'JACKSPEAK',
|
|
263
|
+
found: field,
|
|
264
|
+
},
|
|
341
265
|
});
|
|
342
266
|
}
|
|
343
267
|
/* c8 ignore stop */
|
|
@@ -392,10 +316,9 @@ export class Jack {
|
|
|
392
316
|
if (args === process.argv) {
|
|
393
317
|
args = args.slice(process._eval !== undefined ? 1 : 2);
|
|
394
318
|
}
|
|
395
|
-
const options = toParseArgsOptionsConfig(this.#configSet);
|
|
396
319
|
const result = parseArgs({
|
|
397
320
|
args,
|
|
398
|
-
options,
|
|
321
|
+
options: toParseArgsOptionsConfig(this.#configSet),
|
|
399
322
|
// always strict, but using our own logic
|
|
400
323
|
strict: false,
|
|
401
324
|
allowPositionals: this.#allowPositionals,
|
|
@@ -435,6 +358,7 @@ export class Jack {
|
|
|
435
358
|
`place it at the end of the command after '--', as in ` +
|
|
436
359
|
`'-- ${token.rawName}'`, {
|
|
437
360
|
cause: {
|
|
361
|
+
code: 'JACKSPEAK',
|
|
438
362
|
found: token.rawName + (token.value ? `=${token.value}` : ''),
|
|
439
363
|
},
|
|
440
364
|
});
|
|
@@ -444,6 +368,7 @@ export class Jack {
|
|
|
444
368
|
if (my.type !== 'boolean') {
|
|
445
369
|
throw new Error(`No value provided for ${token.rawName}, expected ${my.type}`, {
|
|
446
370
|
cause: {
|
|
371
|
+
code: 'JACKSPEAK',
|
|
447
372
|
name: token.rawName,
|
|
448
373
|
wanted: valueType(my),
|
|
449
374
|
},
|
|
@@ -453,7 +378,7 @@ export class Jack {
|
|
|
453
378
|
}
|
|
454
379
|
else {
|
|
455
380
|
if (my.type === 'boolean') {
|
|
456
|
-
throw new Error(`Flag ${token.rawName} does not take a value, received '${token.value}'`, { cause: { found: token } });
|
|
381
|
+
throw new Error(`Flag ${token.rawName} does not take a value, received '${token.value}'`, { cause: { code: 'JACKSPEAK', found: token } });
|
|
457
382
|
}
|
|
458
383
|
if (my.type === 'string') {
|
|
459
384
|
value = token.value;
|
|
@@ -464,6 +389,7 @@ export class Jack {
|
|
|
464
389
|
throw new Error(`Invalid value '${token.value}' provided for ` +
|
|
465
390
|
`'${token.rawName}' option, expected number`, {
|
|
466
391
|
cause: {
|
|
392
|
+
code: 'JACKSPEAK',
|
|
467
393
|
name: token.rawName,
|
|
468
394
|
found: token.value,
|
|
469
395
|
wanted: 'number',
|
|
@@ -488,15 +414,12 @@ export class Jack {
|
|
|
488
414
|
for (const [field, value] of Object.entries(p.values)) {
|
|
489
415
|
const valid = this.#configSet[field]?.validate;
|
|
490
416
|
const validOptions = this.#configSet[field]?.validOptions;
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (valid && !valid(value)) {
|
|
496
|
-
cause ??= { name: field, found: value };
|
|
497
|
-
}
|
|
417
|
+
const cause = validOptions && !isValidOption(value, validOptions) ?
|
|
418
|
+
{ name: field, found: value, validOptions: validOptions }
|
|
419
|
+
: valid && !valid(value) ? { name: field, found: value }
|
|
420
|
+
: undefined;
|
|
498
421
|
if (cause) {
|
|
499
|
-
throw new Error(`Invalid value provided for --${field}: ${JSON.stringify(value)}`, { cause });
|
|
422
|
+
throw new Error(`Invalid value provided for --${field}: ${JSON.stringify(value)}`, { cause: { ...cause, code: 'JACKSPEAK' } });
|
|
500
423
|
}
|
|
501
424
|
}
|
|
502
425
|
return p;
|
|
@@ -512,7 +435,7 @@ export class Jack {
|
|
|
512
435
|
// recurse so we get the core config key we care about.
|
|
513
436
|
this.#noNoFields(yes, val, s);
|
|
514
437
|
if (this.#configSet[yes]?.type === 'boolean') {
|
|
515
|
-
throw new Error(`do not set '${s}', instead set '${yes}' as desired.`, { cause: { found: s, wanted: yes } });
|
|
438
|
+
throw new Error(`do not set '${s}', instead set '${yes}' as desired.`, { cause: { code: 'JACKSPEAK', found: s, wanted: yes } });
|
|
516
439
|
}
|
|
517
440
|
}
|
|
518
441
|
/**
|
|
@@ -522,7 +445,7 @@ export class Jack {
|
|
|
522
445
|
validate(o) {
|
|
523
446
|
if (!o || typeof o !== 'object') {
|
|
524
447
|
throw new Error('Invalid config: not an object', {
|
|
525
|
-
cause: { found: o },
|
|
448
|
+
cause: { code: 'JACKSPEAK', found: o },
|
|
526
449
|
});
|
|
527
450
|
}
|
|
528
451
|
const opts = o;
|
|
@@ -535,33 +458,27 @@ export class Jack {
|
|
|
535
458
|
const config = this.#configSet[field];
|
|
536
459
|
if (!config) {
|
|
537
460
|
throw new Error(`Unknown config option: ${field}`, {
|
|
538
|
-
cause: { found: field },
|
|
461
|
+
cause: { code: 'JACKSPEAK', found: field },
|
|
539
462
|
});
|
|
540
463
|
}
|
|
541
464
|
if (!isValidValue(value, config.type, !!config.multiple)) {
|
|
542
465
|
throw new Error(`Invalid value ${valueType(value)} for ${field}, expected ${valueType(config)}`, {
|
|
543
466
|
cause: {
|
|
467
|
+
code: 'JACKSPEAK',
|
|
544
468
|
name: field,
|
|
545
469
|
found: value,
|
|
546
470
|
wanted: valueType(config),
|
|
547
471
|
},
|
|
548
472
|
});
|
|
549
473
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
!
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
found: value,
|
|
556
|
-
validOptions: config.validOptions,
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
if (config.validate && !config.validate(value)) {
|
|
560
|
-
cause ??= { name: field, found: value };
|
|
561
|
-
}
|
|
474
|
+
const cause = config.validOptions && !isValidOption(value, config.validOptions) ?
|
|
475
|
+
{ name: field, found: value, validOptions: config.validOptions }
|
|
476
|
+
: config.validate && !config.validate(value) ?
|
|
477
|
+
{ name: field, found: value }
|
|
478
|
+
: undefined;
|
|
562
479
|
if (cause) {
|
|
563
480
|
throw new Error(`Invalid config value for ${field}: ${value}`, {
|
|
564
|
-
cause,
|
|
481
|
+
cause: { ...cause, code: 'JACKSPEAK' },
|
|
565
482
|
});
|
|
566
483
|
}
|
|
567
484
|
}
|
|
@@ -595,37 +512,37 @@ export class Jack {
|
|
|
595
512
|
* Add one or more number fields.
|
|
596
513
|
*/
|
|
597
514
|
num(fields) {
|
|
598
|
-
return this.#
|
|
515
|
+
return this.#addFieldsWith(fields, 'number', false);
|
|
599
516
|
}
|
|
600
517
|
/**
|
|
601
518
|
* Add one or more multiple number fields.
|
|
602
519
|
*/
|
|
603
520
|
numList(fields) {
|
|
604
|
-
return this.#
|
|
521
|
+
return this.#addFieldsWith(fields, 'number', true);
|
|
605
522
|
}
|
|
606
523
|
/**
|
|
607
524
|
* Add one or more string option fields.
|
|
608
525
|
*/
|
|
609
526
|
opt(fields) {
|
|
610
|
-
return this.#
|
|
527
|
+
return this.#addFieldsWith(fields, 'string', false);
|
|
611
528
|
}
|
|
612
529
|
/**
|
|
613
530
|
* Add one or more multiple string option fields.
|
|
614
531
|
*/
|
|
615
532
|
optList(fields) {
|
|
616
|
-
return this.#
|
|
533
|
+
return this.#addFieldsWith(fields, 'string', true);
|
|
617
534
|
}
|
|
618
535
|
/**
|
|
619
536
|
* Add one or more flag fields.
|
|
620
537
|
*/
|
|
621
538
|
flag(fields) {
|
|
622
|
-
return this.#
|
|
539
|
+
return this.#addFieldsWith(fields, 'boolean', false);
|
|
623
540
|
}
|
|
624
541
|
/**
|
|
625
542
|
* Add one or more multiple flag fields.
|
|
626
543
|
*/
|
|
627
544
|
flagList(fields) {
|
|
628
|
-
return this.#
|
|
545
|
+
return this.#addFieldsWith(fields, 'boolean', true);
|
|
629
546
|
}
|
|
630
547
|
/**
|
|
631
548
|
* Generic field definition method. Similar to flag/flagList/number/etc,
|
|
@@ -633,29 +550,22 @@ export class Jack {
|
|
|
633
550
|
* fields on each one, or Jack won't know how to define them.
|
|
634
551
|
*/
|
|
635
552
|
addFields(fields) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
Object.assign(next.#configSet, fields);
|
|
646
|
-
return next;
|
|
553
|
+
return this.#addFields(this, fields);
|
|
554
|
+
}
|
|
555
|
+
#addFieldsWith(fields, type, multiple) {
|
|
556
|
+
return this.#addFields(this, fields, {
|
|
557
|
+
type,
|
|
558
|
+
multiple,
|
|
559
|
+
});
|
|
647
560
|
}
|
|
648
|
-
#addFields(fields,
|
|
649
|
-
const next = this;
|
|
561
|
+
#addFields(next, fields, opt) {
|
|
650
562
|
Object.assign(next.#configSet, Object.fromEntries(Object.entries(fields).map(([name, field]) => {
|
|
651
563
|
this.#validateName(name, field);
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
});
|
|
658
|
-
return [name, option];
|
|
564
|
+
const { type, multiple } = validateFieldMeta(field, opt);
|
|
565
|
+
const value = { ...field, type, multiple };
|
|
566
|
+
validateField(value, type, multiple);
|
|
567
|
+
next.#fields.push({ type: 'config', name, value });
|
|
568
|
+
return [name, value];
|
|
659
569
|
})));
|
|
660
570
|
return next;
|
|
661
571
|
}
|
|
@@ -691,6 +601,7 @@ export class Jack {
|
|
|
691
601
|
if (this.#usage)
|
|
692
602
|
return this.#usage;
|
|
693
603
|
let headingLevel = 1;
|
|
604
|
+
//@ts-ignore
|
|
694
605
|
const ui = cliui({ width });
|
|
695
606
|
const first = this.#fields[0];
|
|
696
607
|
let start = first?.type === 'heading' ? 1 : 0;
|
|
@@ -932,6 +843,10 @@ export class Jack {
|
|
|
932
843
|
return `Jack ${inspect(this.toJSON(), options)}`;
|
|
933
844
|
}
|
|
934
845
|
}
|
|
846
|
+
/**
|
|
847
|
+
* Main entry point. Create and return a {@link Jack} object.
|
|
848
|
+
*/
|
|
849
|
+
export const jack = (options = {}) => new Jack(options);
|
|
935
850
|
// Unwrap and un-indent, so we can wrap description
|
|
936
851
|
// strings however makes them look nice in the code.
|
|
937
852
|
const normalize = (s, pre = false) => {
|
|
@@ -993,8 +908,4 @@ const normalizeOneLine = (s, pre = false) => {
|
|
|
993
908
|
.trim();
|
|
994
909
|
return pre ? `\`${n}\`` : n;
|
|
995
910
|
};
|
|
996
|
-
/**
|
|
997
|
-
* Main entry point. Create and return a {@link Jack} object.
|
|
998
|
-
*/
|
|
999
|
-
export const jack = (options = {}) => new Jack(options);
|
|
1000
911
|
//# sourceMappingURL=index.js.map
|