@pnpm/exe 11.0.0-beta.0 → 11.0.0-beta.1
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/node_modules/@gar/promise-retry/lib/index.js +28 -0
- package/dist/node_modules/@gar/promise-retry/package.json +48 -0
- package/dist/node_modules/@npmcli/agent/package.json +7 -7
- package/dist/node_modules/@npmcli/fs/package.json +4 -4
- package/dist/node_modules/abbrev/package.json +11 -15
- package/dist/node_modules/cacache/package.json +10 -11
- package/dist/node_modules/glob/dist/commonjs/glob.js +2 -1
- package/dist/node_modules/glob/dist/commonjs/index.min.js +4 -0
- package/dist/node_modules/glob/dist/commonjs/pattern.js +4 -0
- package/dist/node_modules/glob/dist/esm/glob.js +2 -1
- package/dist/node_modules/glob/dist/esm/index.min.js +4 -0
- package/dist/node_modules/glob/dist/esm/pattern.js +4 -0
- package/dist/node_modules/glob/package.json +38 -37
- package/dist/node_modules/graceful-fs/graceful-fs.js +1 -1
- package/dist/node_modules/iconv-lite/encodings/dbcs-codec.js +460 -525
- package/dist/node_modules/iconv-lite/encodings/dbcs-data.js +179 -182
- package/dist/node_modules/iconv-lite/encodings/index.js +15 -15
- package/dist/node_modules/iconv-lite/encodings/internal.js +168 -148
- package/dist/node_modules/iconv-lite/encodings/sbcs-codec.js +55 -52
- package/dist/node_modules/iconv-lite/encodings/sbcs-data.js +174 -175
- package/dist/node_modules/iconv-lite/encodings/utf16.js +121 -131
- package/dist/node_modules/iconv-lite/encodings/utf32.js +226 -238
- package/dist/node_modules/iconv-lite/encodings/utf7.js +208 -215
- package/dist/node_modules/iconv-lite/lib/bom-handling.js +30 -34
- package/dist/node_modules/iconv-lite/lib/helpers/merge-exports.js +13 -0
- package/dist/node_modules/iconv-lite/lib/index.js +125 -123
- package/dist/node_modules/iconv-lite/lib/streams.js +92 -96
- package/dist/node_modules/iconv-lite/package.json +34 -8
- package/dist/node_modules/isexe/package.json +65 -18
- package/dist/node_modules/lru-cache/dist/commonjs/index.js +134 -85
- package/dist/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
- package/dist/node_modules/lru-cache/dist/esm/index.js +134 -85
- package/dist/node_modules/lru-cache/dist/esm/index.min.js +1 -1
- package/dist/node_modules/lru-cache/package.json +18 -41
- package/dist/node_modules/make-fetch-happen/lib/remote.js +1 -1
- package/dist/node_modules/make-fetch-happen/package.json +10 -10
- package/dist/node_modules/minipass-fetch/lib/body.js +25 -15
- package/dist/node_modules/minipass-fetch/package.json +7 -7
- package/dist/node_modules/minipass-sized/dist/commonjs/index.js +69 -0
- package/dist/node_modules/minipass-sized/dist/esm/index.js +64 -0
- package/dist/node_modules/minipass-sized/package.json +39 -9
- package/dist/node_modules/node-gyp/.release-please-manifest.json +1 -1
- package/dist/node_modules/node-gyp/bin/node-gyp.js +7 -0
- package/dist/node_modules/node-gyp/gyp/.release-please-manifest.json +1 -1
- package/dist/node_modules/node-gyp/gyp/pylib/gyp/MSVSNew.py +3 -3
- package/dist/node_modules/node-gyp/gyp/pylib/gyp/MSVSVersion.py +26 -1
- package/dist/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py +1 -1
- package/dist/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja.py +3 -4
- package/dist/node_modules/node-gyp/gyp/pylib/gyp/xcodeproj_file.py +1 -1
- package/dist/node_modules/node-gyp/gyp/pyproject.toml +1 -1
- package/dist/node_modules/node-gyp/lib/build.js +2 -2
- package/dist/node_modules/node-gyp/lib/find-python.js +2 -8
- package/dist/node_modules/node-gyp/lib/find-visualstudio.js +11 -5
- package/dist/node_modules/node-gyp/lib/install.js +1 -1
- package/dist/node_modules/node-gyp/lib/process-release.js +3 -3
- package/dist/node_modules/node-gyp/package.json +13 -13
- package/dist/node_modules/nopt/package.json +5 -5
- package/dist/node_modules/proc-log/lib/index.js +10 -5
- package/dist/node_modules/proc-log/package.json +5 -5
- package/dist/node_modules/retry/lib/retry.js +2 -2
- package/dist/node_modules/retry/lib/retry_operation.js +10 -6
- package/dist/node_modules/retry/package.json +6 -2
- package/dist/node_modules/ssri/lib/index.js +33 -63
- package/dist/node_modules/ssri/package.json +17 -17
- package/dist/node_modules/unique-filename/package.json +5 -5
- package/dist/node_modules/unique-slug/package.json +4 -4
- package/dist/node_modules/which/package.json +29 -20
- package/dist/pnpm.mjs +63952 -54674
- package/dist/worker.js +601 -2162
- package/package.json +8 -8
- package/dist/node_modules/@isaacs/cliui/dist/commonjs/ansi-regex/index.js +0 -16
- package/dist/node_modules/@isaacs/cliui/dist/commonjs/ansi-styles/index.js +0 -170
- package/dist/node_modules/@isaacs/cliui/dist/commonjs/eastasianwidth/index.js +0 -307
- package/dist/node_modules/@isaacs/cliui/dist/commonjs/emoji-regex/index.js +0 -7
- package/dist/node_modules/@isaacs/cliui/dist/commonjs/index.js +0 -322
- package/dist/node_modules/@isaacs/cliui/dist/commonjs/index.min.js +0 -12
- package/dist/node_modules/@isaacs/cliui/dist/commonjs/string-width/index.js +0 -49
- package/dist/node_modules/@isaacs/cliui/dist/commonjs/strip-ansi/index.js +0 -8
- package/dist/node_modules/@isaacs/cliui/dist/commonjs/wrap-ansi/index.js +0 -176
- package/dist/node_modules/@isaacs/cliui/dist/esm/ansi-regex/index.js +0 -12
- package/dist/node_modules/@isaacs/cliui/dist/esm/ansi-styles/index.js +0 -167
- package/dist/node_modules/@isaacs/cliui/dist/esm/eastasianwidth/index.js +0 -299
- package/dist/node_modules/@isaacs/cliui/dist/esm/emoji-regex/index.js +0 -3
- package/dist/node_modules/@isaacs/cliui/dist/esm/index.js +0 -317
- package/dist/node_modules/@isaacs/cliui/dist/esm/index.min.js +0 -12
- package/dist/node_modules/@isaacs/cliui/dist/esm/string-width/index.js +0 -46
- package/dist/node_modules/@isaacs/cliui/dist/esm/strip-ansi/index.js +0 -4
- package/dist/node_modules/@isaacs/cliui/dist/esm/wrap-ansi/index.js +0 -172
- package/dist/node_modules/@isaacs/cliui/package.json +0 -163
- package/dist/node_modules/cross-spawn/LICENSE +0 -21
- package/dist/node_modules/cross-spawn/index.js +0 -39
- package/dist/node_modules/cross-spawn/lib/enoent.js +0 -59
- package/dist/node_modules/cross-spawn/lib/parse.js +0 -91
- package/dist/node_modules/cross-spawn/lib/util/escape.js +0 -47
- package/dist/node_modules/cross-spawn/lib/util/readShebang.js +0 -23
- package/dist/node_modules/cross-spawn/lib/util/resolveCommand.js +0 -52
- package/dist/node_modules/cross-spawn/package.json +0 -73
- package/dist/node_modules/encoding/.prettierrc.js +0 -8
- package/dist/node_modules/encoding/LICENSE +0 -16
- package/dist/node_modules/encoding/lib/encoding.js +0 -83
- package/dist/node_modules/encoding/package.json +0 -18
- package/dist/node_modules/err-code/.eslintrc.json +0 -7
- package/dist/node_modules/err-code/bower.json +0 -30
- package/dist/node_modules/err-code/index.js +0 -47
- package/dist/node_modules/err-code/index.umd.js +0 -51
- package/dist/node_modules/err-code/package.json +0 -34
- package/dist/node_modules/foreground-child/LICENSE +0 -15
- package/dist/node_modules/foreground-child/dist/commonjs/all-signals.js +0 -58
- package/dist/node_modules/foreground-child/dist/commonjs/index.js +0 -123
- package/dist/node_modules/foreground-child/dist/commonjs/proxy-signals.js +0 -38
- package/dist/node_modules/foreground-child/dist/commonjs/watchdog.js +0 -50
- package/dist/node_modules/foreground-child/dist/esm/all-signals.js +0 -52
- package/dist/node_modules/foreground-child/dist/esm/index.js +0 -115
- package/dist/node_modules/foreground-child/dist/esm/proxy-signals.js +0 -34
- package/dist/node_modules/foreground-child/dist/esm/watchdog.js +0 -46
- package/dist/node_modules/foreground-child/package.json +0 -106
- package/dist/node_modules/glob/dist/esm/bin.d.mts +0 -3
- package/dist/node_modules/glob/dist/esm/bin.mjs +0 -346
- package/dist/node_modules/iconv-lite/.github/dependabot.yml +0 -11
- package/dist/node_modules/iconv-lite/.idea/codeStyles/Project.xml +0 -47
- package/dist/node_modules/iconv-lite/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/dist/node_modules/iconv-lite/.idea/iconv-lite.iml +0 -12
- package/dist/node_modules/iconv-lite/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/dist/node_modules/iconv-lite/.idea/modules.xml +0 -8
- package/dist/node_modules/iconv-lite/.idea/vcs.xml +0 -6
- package/dist/node_modules/isexe/LICENSE +0 -15
- package/dist/node_modules/isexe/index.js +0 -57
- package/dist/node_modules/isexe/mode.js +0 -41
- package/dist/node_modules/isexe/windows.js +0 -42
- package/dist/node_modules/jackspeak/dist/commonjs/index.js +0 -944
- package/dist/node_modules/jackspeak/dist/commonjs/index.min.js +0 -33
- package/dist/node_modules/jackspeak/dist/commonjs/package.json +0 -3
- package/dist/node_modules/jackspeak/dist/esm/index.js +0 -936
- package/dist/node_modules/jackspeak/dist/esm/index.min.js +0 -33
- package/dist/node_modules/jackspeak/dist/esm/package.json +0 -3
- package/dist/node_modules/jackspeak/package.json +0 -115
- package/dist/node_modules/lru-cache/LICENSE +0 -15
- package/dist/node_modules/minipass-sized/index.js +0 -67
- package/dist/node_modules/minipass-sized/node_modules/minipass/LICENSE +0 -15
- package/dist/node_modules/minipass-sized/node_modules/minipass/index.js +0 -649
- package/dist/node_modules/minipass-sized/node_modules/minipass/package.json +0 -56
- package/dist/node_modules/minipass-sized/package-lock.json +0 -3464
- package/dist/node_modules/node-gyp/node_modules/isexe/dist/commonjs/package.json +0 -3
- package/dist/node_modules/node-gyp/node_modules/isexe/dist/esm/package.json +0 -3
- package/dist/node_modules/node-gyp/node_modules/isexe/package.json +0 -78
- package/dist/node_modules/node-gyp/node_modules/which/LICENSE +0 -15
- package/dist/node_modules/node-gyp/node_modules/which/package.json +0 -52
- package/dist/node_modules/package-json-from-dist/dist/commonjs/index.js +0 -134
- package/dist/node_modules/package-json-from-dist/dist/commonjs/package.json +0 -3
- package/dist/node_modules/package-json-from-dist/dist/esm/index.js +0 -129
- package/dist/node_modules/package-json-from-dist/dist/esm/package.json +0 -3
- package/dist/node_modules/package-json-from-dist/package.json +0 -68
- package/dist/node_modules/path-key/index.js +0 -16
- package/dist/node_modules/path-key/license +0 -9
- package/dist/node_modules/path-key/package.json +0 -39
- package/dist/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +0 -1589
- package/dist/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +0 -2
- package/dist/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/package.json +0 -3
- package/dist/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +0 -1585
- package/dist/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +0 -2
- package/dist/node_modules/path-scurry/node_modules/lru-cache/dist/esm/package.json +0 -3
- package/dist/node_modules/path-scurry/node_modules/lru-cache/package.json +0 -101
- package/dist/node_modules/promise-retry/index.js +0 -52
- package/dist/node_modules/promise-retry/package.json +0 -37
- package/dist/node_modules/retry/equation.gif +0 -0
- package/dist/node_modules/shebang-command/index.js +0 -19
- package/dist/node_modules/shebang-command/license +0 -9
- package/dist/node_modules/shebang-command/package.json +0 -34
- package/dist/node_modules/shebang-regex/index.js +0 -2
- package/dist/node_modules/shebang-regex/license +0 -9
- package/dist/node_modules/shebang-regex/package.json +0 -35
- package/dist/node_modules/signal-exit/LICENSE.txt +0 -16
- package/dist/node_modules/signal-exit/dist/cjs/browser.js +0 -10
- package/dist/node_modules/signal-exit/dist/cjs/index.js +0 -279
- package/dist/node_modules/signal-exit/dist/cjs/package.json +0 -3
- package/dist/node_modules/signal-exit/dist/cjs/signals.js +0 -42
- package/dist/node_modules/signal-exit/dist/mjs/browser.js +0 -4
- package/dist/node_modules/signal-exit/dist/mjs/index.js +0 -275
- package/dist/node_modules/signal-exit/dist/mjs/package.json +0 -3
- package/dist/node_modules/signal-exit/dist/mjs/signals.js +0 -39
- package/dist/node_modules/signal-exit/package.json +0 -106
- package/dist/node_modules/which/bin/node-which +0 -52
- package/dist/node_modules/which/which.js +0 -125
- /package/dist/node_modules/{promise-retry → @gar/promise-retry}/LICENSE +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/commonjs/index.js +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/commonjs/index.min.js +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/commonjs/options.js +0 -0
- /package/dist/node_modules/{@isaacs/cliui → isexe}/dist/commonjs/package.json +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/commonjs/posix.js +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/commonjs/win32.js +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/esm/index.js +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/esm/index.min.js +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/esm/options.js +0 -0
- /package/dist/node_modules/{@isaacs/cliui → isexe}/dist/esm/package.json +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/esm/posix.js +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/isexe → isexe}/dist/esm/win32.js +0 -0
- /package/dist/node_modules/{foreground-child → minipass-sized}/dist/commonjs/package.json +0 -0
- /package/dist/node_modules/{foreground-child → minipass-sized}/dist/esm/package.json +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/which → which}/bin/which.js +0 -0
- /package/dist/node_modules/{node-gyp/node_modules/which → which}/lib/index.js +0 -0
|
@@ -1,936 +0,0 @@
|
|
|
1
|
-
import { inspect, parseArgs, } from 'node:util';
|
|
2
|
-
import { cliui } from '@isaacs/cliui/min';
|
|
3
|
-
import { basename } from 'node:path';
|
|
4
|
-
export const isConfigType = (t) => typeof t === 'string' &&
|
|
5
|
-
(t === 'string' || t === 'number' || t === 'boolean');
|
|
6
|
-
const isValidValue = (v, type, multi) => {
|
|
7
|
-
if (multi) {
|
|
8
|
-
if (!Array.isArray(v))
|
|
9
|
-
return false;
|
|
10
|
-
return !v.some((v) => !isValidValue(v, type, false));
|
|
11
|
-
}
|
|
12
|
-
if (Array.isArray(v))
|
|
13
|
-
return false;
|
|
14
|
-
return typeof v === type;
|
|
15
|
-
};
|
|
16
|
-
const isValidOption = (v, vo) => !!vo &&
|
|
17
|
-
(Array.isArray(v) ? v.every(x => isValidOption(x, vo)) : vo.includes(v));
|
|
18
|
-
/**
|
|
19
|
-
* Determine whether an unknown object is a {@link ConfigOption} based only
|
|
20
|
-
* on its `type` and `multiple` property
|
|
21
|
-
*/
|
|
22
|
-
export const isConfigOptionOfType = (o, type, multi) => !!o &&
|
|
23
|
-
typeof o === 'object' &&
|
|
24
|
-
'type' in o &&
|
|
25
|
-
isConfigType(o.type) &&
|
|
26
|
-
o.type === type &&
|
|
27
|
-
!!o.multiple === multi;
|
|
28
|
-
/**
|
|
29
|
-
* Determine whether an unknown object is a {@link ConfigOption} based on
|
|
30
|
-
* it having all valid properties
|
|
31
|
-
*/
|
|
32
|
-
export const isConfigOption = (o, type, multi) => isConfigOptionOfType(o, type, multi) &&
|
|
33
|
-
undefOrType(o.short, 'string') &&
|
|
34
|
-
undefOrType(o.description, 'string') &&
|
|
35
|
-
undefOrType(o.hint, 'string') &&
|
|
36
|
-
undefOrType(o.validate, 'function') &&
|
|
37
|
-
(o.type === 'boolean' ?
|
|
38
|
-
o.validOptions === undefined
|
|
39
|
-
: undefOrTypeArray(o.validOptions, o.type)) &&
|
|
40
|
-
(o.default === undefined || isValidValue(o.default, type, multi));
|
|
41
|
-
const isHeading = (r) => r.type === 'heading';
|
|
42
|
-
const isDescription = (r) => r.type === 'description';
|
|
43
|
-
const width = Math.min(process?.stdout?.columns ?? 80, 80);
|
|
44
|
-
// indentation spaces from heading level
|
|
45
|
-
const indent = (n) => (n - 1) * 2;
|
|
46
|
-
const toEnvKey = (pref, key) => [pref, key.replace(/[^a-zA-Z0-9]+/g, ' ')]
|
|
47
|
-
.join(' ')
|
|
48
|
-
.trim()
|
|
49
|
-
.toUpperCase()
|
|
50
|
-
.replace(/ /g, '_');
|
|
51
|
-
const toEnvVal = (value, delim = '\n') => {
|
|
52
|
-
const str = typeof value === 'string' ? value
|
|
53
|
-
: typeof value === 'boolean' ?
|
|
54
|
-
value ? '1'
|
|
55
|
-
: '0'
|
|
56
|
-
: typeof value === 'number' ? String(value)
|
|
57
|
-
: Array.isArray(value) ?
|
|
58
|
-
value.map((v) => toEnvVal(v)).join(delim)
|
|
59
|
-
: /* c8 ignore start */ undefined;
|
|
60
|
-
if (typeof str !== 'string') {
|
|
61
|
-
throw new Error(`could not serialize value to environment: ${JSON.stringify(value)}`, { cause: { code: 'JACKSPEAK' } });
|
|
62
|
-
}
|
|
63
|
-
/* c8 ignore stop */
|
|
64
|
-
return str;
|
|
65
|
-
};
|
|
66
|
-
const fromEnvVal = (env, type, multiple, delim = '\n') => (multiple ?
|
|
67
|
-
env ? env.split(delim).map(v => fromEnvVal(v, type, false))
|
|
68
|
-
: []
|
|
69
|
-
: type === 'string' ? env
|
|
70
|
-
: type === 'boolean' ? env === '1'
|
|
71
|
-
: +env.trim());
|
|
72
|
-
const undefOrType = (v, t) => v === undefined || typeof v === t;
|
|
73
|
-
const undefOrTypeArray = (v, t) => v === undefined || (Array.isArray(v) && v.every(x => typeof x === t));
|
|
74
|
-
// print the value type, for error message reporting
|
|
75
|
-
const valueType = (v) => typeof v === 'string' ? 'string'
|
|
76
|
-
: typeof v === 'boolean' ? 'boolean'
|
|
77
|
-
: typeof v === 'number' ? 'number'
|
|
78
|
-
: Array.isArray(v) ?
|
|
79
|
-
`${joinTypes([...new Set(v.map(v => valueType(v)))])}[]`
|
|
80
|
-
: `${v.type}${v.multiple ? '[]' : ''}`;
|
|
81
|
-
const joinTypes = (types) => types.length === 1 && typeof types[0] === 'string' ?
|
|
82
|
-
types[0]
|
|
83
|
-
: `(${types.join('|')})`;
|
|
84
|
-
const validateFieldMeta = (field, fieldMeta) => {
|
|
85
|
-
if (fieldMeta) {
|
|
86
|
-
if (field.type !== undefined && field.type !== fieldMeta.type) {
|
|
87
|
-
throw new TypeError(`invalid type`, {
|
|
88
|
-
cause: {
|
|
89
|
-
found: field.type,
|
|
90
|
-
wanted: [fieldMeta.type, undefined],
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
if (field.multiple !== undefined &&
|
|
95
|
-
!!field.multiple !== fieldMeta.multiple) {
|
|
96
|
-
throw new TypeError(`invalid multiple`, {
|
|
97
|
-
cause: {
|
|
98
|
-
found: field.multiple,
|
|
99
|
-
wanted: [fieldMeta.multiple, undefined],
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
return fieldMeta;
|
|
104
|
-
}
|
|
105
|
-
if (!isConfigType(field.type)) {
|
|
106
|
-
throw new TypeError(`invalid type`, {
|
|
107
|
-
cause: {
|
|
108
|
-
found: field.type,
|
|
109
|
-
wanted: ['string', 'number', 'boolean'],
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
type: field.type,
|
|
115
|
-
multiple: !!field.multiple,
|
|
116
|
-
};
|
|
117
|
-
};
|
|
118
|
-
const validateField = (o, type, multiple) => {
|
|
119
|
-
const validateValidOptions = (def, validOptions) => {
|
|
120
|
-
if (!undefOrTypeArray(validOptions, type)) {
|
|
121
|
-
throw new TypeError('invalid validOptions', {
|
|
122
|
-
cause: {
|
|
123
|
-
found: validOptions,
|
|
124
|
-
wanted: valueType({ type, multiple: true }),
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
if (def !== undefined && validOptions !== undefined) {
|
|
129
|
-
const valid = Array.isArray(def) ?
|
|
130
|
-
def.every(v => validOptions.includes(v))
|
|
131
|
-
: validOptions.includes(def);
|
|
132
|
-
if (!valid) {
|
|
133
|
-
throw new TypeError('invalid default value not in validOptions', {
|
|
134
|
-
cause: {
|
|
135
|
-
found: def,
|
|
136
|
-
wanted: validOptions,
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
if (o.default !== undefined &&
|
|
143
|
-
!isValidValue(o.default, type, multiple)) {
|
|
144
|
-
throw new TypeError('invalid default value', {
|
|
145
|
-
cause: {
|
|
146
|
-
found: o.default,
|
|
147
|
-
wanted: valueType({ type, multiple }),
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
if (isConfigOptionOfType(o, 'number', false) ||
|
|
152
|
-
isConfigOptionOfType(o, 'number', true)) {
|
|
153
|
-
validateValidOptions(o.default, o.validOptions);
|
|
154
|
-
}
|
|
155
|
-
else if (isConfigOptionOfType(o, 'string', false) ||
|
|
156
|
-
isConfigOptionOfType(o, 'string', true)) {
|
|
157
|
-
validateValidOptions(o.default, o.validOptions);
|
|
158
|
-
}
|
|
159
|
-
else if (isConfigOptionOfType(o, 'boolean', false) ||
|
|
160
|
-
isConfigOptionOfType(o, 'boolean', true)) {
|
|
161
|
-
if (o.hint !== undefined) {
|
|
162
|
-
throw new TypeError('cannot provide hint for flag');
|
|
163
|
-
}
|
|
164
|
-
if (o.validOptions !== undefined) {
|
|
165
|
-
throw new TypeError('cannot provide validOptions for flag');
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return o;
|
|
169
|
-
};
|
|
170
|
-
const toParseArgsOptionsConfig = (options) => {
|
|
171
|
-
return Object.entries(options).reduce((acc, [longOption, o]) => {
|
|
172
|
-
const p = {
|
|
173
|
-
type: 'string',
|
|
174
|
-
multiple: !!o.multiple,
|
|
175
|
-
...(typeof o.short === 'string' ? { short: o.short } : undefined),
|
|
176
|
-
};
|
|
177
|
-
const setNoBool = () => {
|
|
178
|
-
if (!longOption.startsWith('no-') && !options[`no-${longOption}`]) {
|
|
179
|
-
acc[`no-${longOption}`] = {
|
|
180
|
-
type: 'boolean',
|
|
181
|
-
multiple: !!o.multiple,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
const setDefault = (def, fn) => {
|
|
186
|
-
if (def !== undefined) {
|
|
187
|
-
p.default = fn(def);
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
if (isConfigOption(o, 'number', false)) {
|
|
191
|
-
setDefault(o.default, String);
|
|
192
|
-
}
|
|
193
|
-
else if (isConfigOption(o, 'number', true)) {
|
|
194
|
-
setDefault(o.default, d => d.map(v => String(v)));
|
|
195
|
-
}
|
|
196
|
-
else if (isConfigOption(o, 'string', false) ||
|
|
197
|
-
isConfigOption(o, 'string', true)) {
|
|
198
|
-
setDefault(o.default, v => v);
|
|
199
|
-
}
|
|
200
|
-
else if (isConfigOption(o, 'boolean', false) ||
|
|
201
|
-
isConfigOption(o, 'boolean', true)) {
|
|
202
|
-
p.type = 'boolean';
|
|
203
|
-
setDefault(o.default, v => v);
|
|
204
|
-
setNoBool();
|
|
205
|
-
}
|
|
206
|
-
acc[longOption] = p;
|
|
207
|
-
return acc;
|
|
208
|
-
}, {});
|
|
209
|
-
};
|
|
210
|
-
/**
|
|
211
|
-
* Class returned by the {@link jack} function and all configuration
|
|
212
|
-
* definition methods. This is what gets chained together.
|
|
213
|
-
*/
|
|
214
|
-
export class Jack {
|
|
215
|
-
#configSet;
|
|
216
|
-
#shorts;
|
|
217
|
-
#options;
|
|
218
|
-
#fields = [];
|
|
219
|
-
#env;
|
|
220
|
-
#envPrefix;
|
|
221
|
-
#allowPositionals;
|
|
222
|
-
#usage;
|
|
223
|
-
#usageMarkdown;
|
|
224
|
-
constructor(options = {}) {
|
|
225
|
-
this.#options = options;
|
|
226
|
-
this.#allowPositionals = options.allowPositionals !== false;
|
|
227
|
-
this.#env =
|
|
228
|
-
this.#options.env === undefined ? process.env : this.#options.env;
|
|
229
|
-
this.#envPrefix = options.envPrefix;
|
|
230
|
-
// We need to fib a little, because it's always the same object, but it
|
|
231
|
-
// starts out as having an empty config set. Then each method that adds
|
|
232
|
-
// fields returns `this as Jack<C & { ...newConfigs }>`
|
|
233
|
-
this.#configSet = Object.create(null);
|
|
234
|
-
this.#shorts = Object.create(null);
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Resulting definitions, suitable to be passed to Node's `util.parseArgs`,
|
|
238
|
-
* but also including `description` and `short` fields, if set.
|
|
239
|
-
*/
|
|
240
|
-
get definitions() {
|
|
241
|
-
return this.#configSet;
|
|
242
|
-
}
|
|
243
|
-
/** map of `{ <short>: <long> }` strings for each short name defined */
|
|
244
|
-
get shorts() {
|
|
245
|
-
return this.#shorts;
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* options passed to the {@link Jack} constructor
|
|
249
|
-
*/
|
|
250
|
-
get jackOptions() {
|
|
251
|
-
return this.#options;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* the data used to generate {@link Jack#usage} and
|
|
255
|
-
* {@link Jack#usageMarkdown} content.
|
|
256
|
-
*/
|
|
257
|
-
get usageFields() {
|
|
258
|
-
return this.#fields;
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Set the default value (which will still be overridden by env or cli)
|
|
262
|
-
* as if from a parsed config file. The optional `source` param, if
|
|
263
|
-
* provided, will be included in error messages if a value is invalid or
|
|
264
|
-
* unknown.
|
|
265
|
-
*/
|
|
266
|
-
setConfigValues(values, source = '') {
|
|
267
|
-
try {
|
|
268
|
-
this.validate(values);
|
|
269
|
-
}
|
|
270
|
-
catch (er) {
|
|
271
|
-
if (source && er instanceof Error) {
|
|
272
|
-
/* c8 ignore next */
|
|
273
|
-
const cause = typeof er.cause === 'object' ? er.cause : {};
|
|
274
|
-
er.cause = { ...cause, path: source };
|
|
275
|
-
Error.captureStackTrace(er, this.setConfigValues);
|
|
276
|
-
}
|
|
277
|
-
throw er;
|
|
278
|
-
}
|
|
279
|
-
for (const [field, value] of Object.entries(values)) {
|
|
280
|
-
const my = this.#configSet[field];
|
|
281
|
-
// already validated, just for TS's benefit
|
|
282
|
-
/* c8 ignore start */
|
|
283
|
-
if (!my) {
|
|
284
|
-
throw new Error('unexpected field in config set: ' + field, {
|
|
285
|
-
cause: {
|
|
286
|
-
code: 'JACKSPEAK',
|
|
287
|
-
found: field,
|
|
288
|
-
},
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
/* c8 ignore stop */
|
|
292
|
-
my.default = value;
|
|
293
|
-
}
|
|
294
|
-
return this;
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Parse a string of arguments, and return the resulting
|
|
298
|
-
* `{ values, positionals }` object.
|
|
299
|
-
*
|
|
300
|
-
* If an {@link JackOptions#envPrefix} is set, then it will read default
|
|
301
|
-
* values from the environment, and write the resulting values back
|
|
302
|
-
* to the environment as well.
|
|
303
|
-
*
|
|
304
|
-
* Environment values always take precedence over any other value, except
|
|
305
|
-
* an explicit CLI setting.
|
|
306
|
-
*/
|
|
307
|
-
parse(args = process.argv) {
|
|
308
|
-
this.loadEnvDefaults();
|
|
309
|
-
const p = this.parseRaw(args);
|
|
310
|
-
this.applyDefaults(p);
|
|
311
|
-
this.writeEnv(p);
|
|
312
|
-
return p;
|
|
313
|
-
}
|
|
314
|
-
loadEnvDefaults() {
|
|
315
|
-
if (this.#envPrefix) {
|
|
316
|
-
for (const [field, my] of Object.entries(this.#configSet)) {
|
|
317
|
-
const ek = toEnvKey(this.#envPrefix, field);
|
|
318
|
-
const env = this.#env[ek];
|
|
319
|
-
if (env !== undefined) {
|
|
320
|
-
my.default = fromEnvVal(env, my.type, !!my.multiple, my.delim);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
applyDefaults(p) {
|
|
326
|
-
for (const [field, c] of Object.entries(this.#configSet)) {
|
|
327
|
-
if (c.default !== undefined && !(field in p.values)) {
|
|
328
|
-
//@ts-ignore
|
|
329
|
-
p.values[field] = c.default;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Only parse the command line arguments passed in.
|
|
335
|
-
* Does not strip off the `node script.js` bits, so it must be just the
|
|
336
|
-
* arguments you wish to have parsed.
|
|
337
|
-
* Does not read from or write to the environment, or set defaults.
|
|
338
|
-
*/
|
|
339
|
-
parseRaw(args) {
|
|
340
|
-
if (args === process.argv) {
|
|
341
|
-
args = args.slice(process._eval !== undefined ? 1 : 2);
|
|
342
|
-
}
|
|
343
|
-
const result = parseArgs({
|
|
344
|
-
args,
|
|
345
|
-
options: toParseArgsOptionsConfig(this.#configSet),
|
|
346
|
-
// always strict, but using our own logic
|
|
347
|
-
strict: false,
|
|
348
|
-
allowPositionals: this.#allowPositionals,
|
|
349
|
-
tokens: true,
|
|
350
|
-
});
|
|
351
|
-
const p = {
|
|
352
|
-
values: {},
|
|
353
|
-
positionals: [],
|
|
354
|
-
};
|
|
355
|
-
for (const token of result.tokens) {
|
|
356
|
-
if (token.kind === 'positional') {
|
|
357
|
-
p.positionals.push(token.value);
|
|
358
|
-
if (this.#options.stopAtPositional ||
|
|
359
|
-
this.#options.stopAtPositionalTest?.(token.value)) {
|
|
360
|
-
p.positionals.push(...args.slice(token.index + 1));
|
|
361
|
-
break;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
else if (token.kind === 'option') {
|
|
365
|
-
let value = undefined;
|
|
366
|
-
if (token.name.startsWith('no-')) {
|
|
367
|
-
const my = this.#configSet[token.name];
|
|
368
|
-
const pname = token.name.substring('no-'.length);
|
|
369
|
-
const pos = this.#configSet[pname];
|
|
370
|
-
if (pos &&
|
|
371
|
-
pos.type === 'boolean' &&
|
|
372
|
-
(!my ||
|
|
373
|
-
(my.type === 'boolean' && !!my.multiple === !!pos.multiple))) {
|
|
374
|
-
value = false;
|
|
375
|
-
token.name = pname;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
const my = this.#configSet[token.name];
|
|
379
|
-
if (!my) {
|
|
380
|
-
throw new Error(`Unknown option '${token.rawName}'. ` +
|
|
381
|
-
`To specify a positional argument starting with a '-', ` +
|
|
382
|
-
`place it at the end of the command after '--', as in ` +
|
|
383
|
-
`'-- ${token.rawName}'`, {
|
|
384
|
-
cause: {
|
|
385
|
-
code: 'JACKSPEAK',
|
|
386
|
-
found: token.rawName + (token.value ? `=${token.value}` : ''),
|
|
387
|
-
},
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
if (value === undefined) {
|
|
391
|
-
if (token.value === undefined) {
|
|
392
|
-
if (my.type !== 'boolean') {
|
|
393
|
-
throw new Error(`No value provided for ${token.rawName}, expected ${my.type}`, {
|
|
394
|
-
cause: {
|
|
395
|
-
code: 'JACKSPEAK',
|
|
396
|
-
name: token.rawName,
|
|
397
|
-
wanted: valueType(my),
|
|
398
|
-
},
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
value = true;
|
|
402
|
-
}
|
|
403
|
-
else {
|
|
404
|
-
if (my.type === 'boolean') {
|
|
405
|
-
throw new Error(`Flag ${token.rawName} does not take a value, received '${token.value}'`, { cause: { code: 'JACKSPEAK', found: token } });
|
|
406
|
-
}
|
|
407
|
-
if (my.type === 'string') {
|
|
408
|
-
value = token.value;
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
value = +token.value;
|
|
412
|
-
if (value !== value) {
|
|
413
|
-
throw new Error(`Invalid value '${token.value}' provided for ` +
|
|
414
|
-
`'${token.rawName}' option, expected number`, {
|
|
415
|
-
cause: {
|
|
416
|
-
code: 'JACKSPEAK',
|
|
417
|
-
name: token.rawName,
|
|
418
|
-
found: token.value,
|
|
419
|
-
wanted: 'number',
|
|
420
|
-
},
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
if (my.multiple) {
|
|
427
|
-
const pv = p.values;
|
|
428
|
-
const tn = pv[token.name] ?? [];
|
|
429
|
-
pv[token.name] = tn;
|
|
430
|
-
tn.push(value);
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
const pv = p.values;
|
|
434
|
-
pv[token.name] = value;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
for (const [field, value] of Object.entries(p.values)) {
|
|
439
|
-
const valid = this.#configSet[field]?.validate;
|
|
440
|
-
const validOptions = this.#configSet[field]?.validOptions;
|
|
441
|
-
const cause = validOptions && !isValidOption(value, validOptions) ?
|
|
442
|
-
{ name: field, found: value, validOptions }
|
|
443
|
-
: valid && !valid(value) ? { name: field, found: value }
|
|
444
|
-
: undefined;
|
|
445
|
-
if (cause) {
|
|
446
|
-
throw new Error(`Invalid value provided for --${field}: ${JSON.stringify(value)}`, { cause: { ...cause, code: 'JACKSPEAK' } });
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return p;
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* do not set fields as 'no-foo' if 'foo' exists and both are bools
|
|
453
|
-
* just set foo.
|
|
454
|
-
*/
|
|
455
|
-
#noNoFields(f, val, s = f) {
|
|
456
|
-
if (!f.startsWith('no-') || typeof val !== 'boolean')
|
|
457
|
-
return;
|
|
458
|
-
const yes = f.substring('no-'.length);
|
|
459
|
-
// recurse so we get the core config key we care about.
|
|
460
|
-
this.#noNoFields(yes, val, s);
|
|
461
|
-
if (this.#configSet[yes]?.type === 'boolean') {
|
|
462
|
-
throw new Error(`do not set '${s}', instead set '${yes}' as desired.`, { cause: { code: 'JACKSPEAK', found: s, wanted: yes } });
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Validate that any arbitrary object is a valid configuration `values`
|
|
467
|
-
* object. Useful when loading config files or other sources.
|
|
468
|
-
*/
|
|
469
|
-
validate(o) {
|
|
470
|
-
if (!o || typeof o !== 'object') {
|
|
471
|
-
throw new Error('Invalid config: not an object', {
|
|
472
|
-
cause: { code: 'JACKSPEAK', found: o },
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
const opts = o;
|
|
476
|
-
for (const field in o) {
|
|
477
|
-
const value = opts[field];
|
|
478
|
-
/* c8 ignore next - for TS */
|
|
479
|
-
if (value === undefined)
|
|
480
|
-
continue;
|
|
481
|
-
this.#noNoFields(field, value);
|
|
482
|
-
const config = this.#configSet[field];
|
|
483
|
-
if (!config) {
|
|
484
|
-
throw new Error(`Unknown config option: ${field}`, {
|
|
485
|
-
cause: { code: 'JACKSPEAK', found: field },
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
if (!isValidValue(value, config.type, !!config.multiple)) {
|
|
489
|
-
throw new Error(`Invalid value ${valueType(value)} for ${field}, expected ${valueType(config)}`, {
|
|
490
|
-
cause: {
|
|
491
|
-
code: 'JACKSPEAK',
|
|
492
|
-
name: field,
|
|
493
|
-
found: value,
|
|
494
|
-
wanted: valueType(config),
|
|
495
|
-
},
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
const cause = config.validOptions && !isValidOption(value, config.validOptions) ?
|
|
499
|
-
{ name: field, found: value, validOptions: config.validOptions }
|
|
500
|
-
: config.validate && !config.validate(value) ?
|
|
501
|
-
{ name: field, found: value }
|
|
502
|
-
: undefined;
|
|
503
|
-
if (cause) {
|
|
504
|
-
throw new Error(`Invalid config value for ${field}: ${JSON.stringify(value)}`, {
|
|
505
|
-
cause: { ...cause, code: 'JACKSPEAK' },
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
writeEnv(p) {
|
|
511
|
-
if (!this.#env || !this.#envPrefix)
|
|
512
|
-
return;
|
|
513
|
-
for (const [field, value] of Object.entries(p.values)) {
|
|
514
|
-
const my = this.#configSet[field];
|
|
515
|
-
this.#env[toEnvKey(this.#envPrefix, field)] = toEnvVal(value, my?.delim);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
/**
|
|
519
|
-
* Add a heading to the usage output banner
|
|
520
|
-
*/
|
|
521
|
-
heading(text, level, { pre = false } = {}) {
|
|
522
|
-
if (level === undefined) {
|
|
523
|
-
level = this.#fields.some(r => isHeading(r)) ? 2 : 1;
|
|
524
|
-
}
|
|
525
|
-
this.#fields.push({ type: 'heading', text, level, pre });
|
|
526
|
-
return this;
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* Add a long-form description to the usage output at this position.
|
|
530
|
-
*/
|
|
531
|
-
description(text, { pre } = {}) {
|
|
532
|
-
this.#fields.push({ type: 'description', text, pre });
|
|
533
|
-
return this;
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Add one or more number fields.
|
|
537
|
-
*/
|
|
538
|
-
num(fields) {
|
|
539
|
-
return this.#addFieldsWith(fields, 'number', false);
|
|
540
|
-
}
|
|
541
|
-
/**
|
|
542
|
-
* Add one or more multiple number fields.
|
|
543
|
-
*/
|
|
544
|
-
numList(fields) {
|
|
545
|
-
return this.#addFieldsWith(fields, 'number', true);
|
|
546
|
-
}
|
|
547
|
-
/**
|
|
548
|
-
* Add one or more string option fields.
|
|
549
|
-
*/
|
|
550
|
-
opt(fields) {
|
|
551
|
-
return this.#addFieldsWith(fields, 'string', false);
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* Add one or more multiple string option fields.
|
|
555
|
-
*/
|
|
556
|
-
optList(fields) {
|
|
557
|
-
return this.#addFieldsWith(fields, 'string', true);
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Add one or more flag fields.
|
|
561
|
-
*/
|
|
562
|
-
flag(fields) {
|
|
563
|
-
return this.#addFieldsWith(fields, 'boolean', false);
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* Add one or more multiple flag fields.
|
|
567
|
-
*/
|
|
568
|
-
flagList(fields) {
|
|
569
|
-
return this.#addFieldsWith(fields, 'boolean', true);
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Generic field definition method. Similar to flag/flagList/number/etc,
|
|
573
|
-
* but you must specify the `type` (and optionally `multiple` and `delim`)
|
|
574
|
-
* fields on each one, or Jack won't know how to define them.
|
|
575
|
-
*/
|
|
576
|
-
addFields(fields) {
|
|
577
|
-
return this.#addFields(this, fields);
|
|
578
|
-
}
|
|
579
|
-
#addFieldsWith(fields, type, multiple) {
|
|
580
|
-
return this.#addFields(this, fields, {
|
|
581
|
-
type,
|
|
582
|
-
multiple,
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
#addFields(next, fields, opt) {
|
|
586
|
-
Object.assign(next.#configSet, Object.fromEntries(Object.entries(fields).map(([name, field]) => {
|
|
587
|
-
this.#validateName(name, field);
|
|
588
|
-
const { type, multiple } = validateFieldMeta(field, opt);
|
|
589
|
-
const value = { ...field, type, multiple };
|
|
590
|
-
validateField(value, type, multiple);
|
|
591
|
-
next.#fields.push({ type: 'config', name, value });
|
|
592
|
-
return [name, value];
|
|
593
|
-
})));
|
|
594
|
-
return next;
|
|
595
|
-
}
|
|
596
|
-
#validateName(name, field) {
|
|
597
|
-
if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(name)) {
|
|
598
|
-
throw new TypeError(`Invalid option name: ${name}, ` +
|
|
599
|
-
`must be '-' delimited ASCII alphanumeric`);
|
|
600
|
-
}
|
|
601
|
-
if (this.#configSet[name]) {
|
|
602
|
-
throw new TypeError(`Cannot redefine option ${name}`);
|
|
603
|
-
}
|
|
604
|
-
if (this.#shorts[name]) {
|
|
605
|
-
throw new TypeError(`Cannot redefine option ${name}, already ` +
|
|
606
|
-
`in use for ${this.#shorts[name]}`);
|
|
607
|
-
}
|
|
608
|
-
if (field.short) {
|
|
609
|
-
if (!/^[a-zA-Z0-9]$/.test(field.short)) {
|
|
610
|
-
throw new TypeError(`Invalid ${name} short option: ${field.short}, ` +
|
|
611
|
-
'must be 1 ASCII alphanumeric character');
|
|
612
|
-
}
|
|
613
|
-
if (this.#shorts[field.short]) {
|
|
614
|
-
throw new TypeError(`Invalid ${name} short option: ${field.short}, ` +
|
|
615
|
-
`already in use for ${this.#shorts[field.short]}`);
|
|
616
|
-
}
|
|
617
|
-
this.#shorts[field.short] = name;
|
|
618
|
-
this.#shorts[name] = name;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
/**
|
|
622
|
-
* Return the usage banner for the given configuration
|
|
623
|
-
*/
|
|
624
|
-
usage() {
|
|
625
|
-
if (this.#usage)
|
|
626
|
-
return this.#usage;
|
|
627
|
-
let headingLevel = 1;
|
|
628
|
-
//@ts-ignore
|
|
629
|
-
const ui = cliui({ width });
|
|
630
|
-
const first = this.#fields[0];
|
|
631
|
-
let start = first?.type === 'heading' ? 1 : 0;
|
|
632
|
-
if (first?.type === 'heading') {
|
|
633
|
-
ui.div({
|
|
634
|
-
padding: [0, 0, 0, 0],
|
|
635
|
-
text: normalize(first.text),
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
ui.div({ padding: [0, 0, 0, 0], text: 'Usage:' });
|
|
639
|
-
if (this.#options.usage) {
|
|
640
|
-
ui.div({
|
|
641
|
-
text: this.#options.usage,
|
|
642
|
-
padding: [0, 0, 0, 2],
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
else {
|
|
646
|
-
const cmd = basename(String(process.argv[1]));
|
|
647
|
-
const shortFlags = [];
|
|
648
|
-
const shorts = [];
|
|
649
|
-
const flags = [];
|
|
650
|
-
const opts = [];
|
|
651
|
-
for (const [field, config] of Object.entries(this.#configSet)) {
|
|
652
|
-
if (config.short) {
|
|
653
|
-
if (config.type === 'boolean')
|
|
654
|
-
shortFlags.push(config.short);
|
|
655
|
-
else
|
|
656
|
-
shorts.push([config.short, config.hint || field]);
|
|
657
|
-
}
|
|
658
|
-
else {
|
|
659
|
-
if (config.type === 'boolean')
|
|
660
|
-
flags.push(field);
|
|
661
|
-
else
|
|
662
|
-
opts.push([field, config.hint || field]);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
const sf = shortFlags.length ? ' -' + shortFlags.join('') : '';
|
|
666
|
-
const so = shorts.map(([k, v]) => ` --${k}=<${v}>`).join('');
|
|
667
|
-
const lf = flags.map(k => ` --${k}`).join('');
|
|
668
|
-
const lo = opts.map(([k, v]) => ` --${k}=<${v}>`).join('');
|
|
669
|
-
const usage = `${cmd}${sf}${so}${lf}${lo}`.trim();
|
|
670
|
-
ui.div({
|
|
671
|
-
text: usage,
|
|
672
|
-
padding: [0, 0, 0, 2],
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
ui.div({ padding: [0, 0, 0, 0], text: '' });
|
|
676
|
-
const maybeDesc = this.#fields[start];
|
|
677
|
-
if (maybeDesc && isDescription(maybeDesc)) {
|
|
678
|
-
const print = normalize(maybeDesc.text, maybeDesc.pre);
|
|
679
|
-
start++;
|
|
680
|
-
ui.div({ padding: [0, 0, 0, 0], text: print });
|
|
681
|
-
ui.div({ padding: [0, 0, 0, 0], text: '' });
|
|
682
|
-
}
|
|
683
|
-
const { rows, maxWidth } = this.#usageRows(start);
|
|
684
|
-
// every heading/description after the first gets indented by 2
|
|
685
|
-
// extra spaces.
|
|
686
|
-
for (const row of rows) {
|
|
687
|
-
if (row.left) {
|
|
688
|
-
// If the row is too long, don't wrap it
|
|
689
|
-
// Bump the right-hand side down a line to make room
|
|
690
|
-
const configIndent = indent(Math.max(headingLevel, 2));
|
|
691
|
-
if (row.left.length > maxWidth - 3) {
|
|
692
|
-
ui.div({ text: row.left, padding: [0, 0, 0, configIndent] });
|
|
693
|
-
ui.div({ text: row.text, padding: [0, 0, 0, maxWidth] });
|
|
694
|
-
}
|
|
695
|
-
else {
|
|
696
|
-
ui.div({
|
|
697
|
-
text: row.left,
|
|
698
|
-
padding: [0, 1, 0, configIndent],
|
|
699
|
-
width: maxWidth,
|
|
700
|
-
}, { padding: [0, 0, 0, 0], text: row.text });
|
|
701
|
-
}
|
|
702
|
-
if (row.skipLine) {
|
|
703
|
-
ui.div({ padding: [0, 0, 0, 0], text: '' });
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
else {
|
|
707
|
-
if (isHeading(row)) {
|
|
708
|
-
const { level } = row;
|
|
709
|
-
headingLevel = level;
|
|
710
|
-
// only h1 and h2 have bottom padding
|
|
711
|
-
// h3-h6 do not
|
|
712
|
-
const b = level <= 2 ? 1 : 0;
|
|
713
|
-
ui.div({ ...row, padding: [0, 0, b, indent(level)] });
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
ui.div({ ...row, padding: [0, 0, 1, indent(headingLevel + 1)] });
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
return (this.#usage = ui.toString());
|
|
721
|
-
}
|
|
722
|
-
/**
|
|
723
|
-
* Return the usage banner markdown for the given configuration
|
|
724
|
-
*/
|
|
725
|
-
usageMarkdown() {
|
|
726
|
-
if (this.#usageMarkdown)
|
|
727
|
-
return this.#usageMarkdown;
|
|
728
|
-
const out = [];
|
|
729
|
-
let headingLevel = 1;
|
|
730
|
-
const first = this.#fields[0];
|
|
731
|
-
let start = first?.type === 'heading' ? 1 : 0;
|
|
732
|
-
if (first?.type === 'heading') {
|
|
733
|
-
out.push(`# ${normalizeOneLine(first.text)}`);
|
|
734
|
-
}
|
|
735
|
-
out.push('Usage:');
|
|
736
|
-
if (this.#options.usage) {
|
|
737
|
-
out.push(normalizeMarkdown(this.#options.usage, true));
|
|
738
|
-
}
|
|
739
|
-
else {
|
|
740
|
-
const cmd = basename(String(process.argv[1]));
|
|
741
|
-
const shortFlags = [];
|
|
742
|
-
const shorts = [];
|
|
743
|
-
const flags = [];
|
|
744
|
-
const opts = [];
|
|
745
|
-
for (const [field, config] of Object.entries(this.#configSet)) {
|
|
746
|
-
if (config.short) {
|
|
747
|
-
if (config.type === 'boolean')
|
|
748
|
-
shortFlags.push(config.short);
|
|
749
|
-
else
|
|
750
|
-
shorts.push([config.short, config.hint || field]);
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
if (config.type === 'boolean')
|
|
754
|
-
flags.push(field);
|
|
755
|
-
else
|
|
756
|
-
opts.push([field, config.hint || field]);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
const sf = shortFlags.length ? ' -' + shortFlags.join('') : '';
|
|
760
|
-
const so = shorts.map(([k, v]) => ` --${k}=<${v}>`).join('');
|
|
761
|
-
const lf = flags.map(k => ` --${k}`).join('');
|
|
762
|
-
const lo = opts.map(([k, v]) => ` --${k}=<${v}>`).join('');
|
|
763
|
-
const usage = `${cmd}${sf}${so}${lf}${lo}`.trim();
|
|
764
|
-
out.push(normalizeMarkdown(usage, true));
|
|
765
|
-
}
|
|
766
|
-
const maybeDesc = this.#fields[start];
|
|
767
|
-
if (maybeDesc && isDescription(maybeDesc)) {
|
|
768
|
-
out.push(normalizeMarkdown(maybeDesc.text, maybeDesc.pre));
|
|
769
|
-
start++;
|
|
770
|
-
}
|
|
771
|
-
const { rows } = this.#usageRows(start);
|
|
772
|
-
// heading level in markdown is number of # ahead of text
|
|
773
|
-
for (const row of rows) {
|
|
774
|
-
if (row.left) {
|
|
775
|
-
out.push('#'.repeat(headingLevel + 1) +
|
|
776
|
-
' ' +
|
|
777
|
-
normalizeOneLine(row.left, true));
|
|
778
|
-
if (row.text)
|
|
779
|
-
out.push(normalizeMarkdown(row.text));
|
|
780
|
-
}
|
|
781
|
-
else if (isHeading(row)) {
|
|
782
|
-
const { level } = row;
|
|
783
|
-
headingLevel = level;
|
|
784
|
-
out.push(`${'#'.repeat(headingLevel)} ${normalizeOneLine(row.text, row.pre)}`);
|
|
785
|
-
}
|
|
786
|
-
else {
|
|
787
|
-
out.push(normalizeMarkdown(row.text, !!row.pre));
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
return (this.#usageMarkdown = out.join('\n\n') + '\n');
|
|
791
|
-
}
|
|
792
|
-
#usageRows(start) {
|
|
793
|
-
// turn each config type into a row, and figure out the width of the
|
|
794
|
-
// left hand indentation for the option descriptions.
|
|
795
|
-
const maxMax = Math.max(12, Math.min(26, Math.floor(width / 3)));
|
|
796
|
-
let maxWidth = 8;
|
|
797
|
-
let prev = undefined;
|
|
798
|
-
const rows = [];
|
|
799
|
-
for (const field of this.#fields.slice(start)) {
|
|
800
|
-
if (field.type !== 'config') {
|
|
801
|
-
if (prev?.type === 'config')
|
|
802
|
-
prev.skipLine = true;
|
|
803
|
-
prev = undefined;
|
|
804
|
-
field.text = normalize(field.text, !!field.pre);
|
|
805
|
-
rows.push(field);
|
|
806
|
-
continue;
|
|
807
|
-
}
|
|
808
|
-
const { value } = field;
|
|
809
|
-
const desc = value.description || '';
|
|
810
|
-
const mult = value.multiple ? 'Can be set multiple times' : '';
|
|
811
|
-
const opts = value.validOptions?.length ?
|
|
812
|
-
'Valid options: ' +
|
|
813
|
-
value.validOptions.map(v => JSON.stringify(v)).join(', ')
|
|
814
|
-
: '';
|
|
815
|
-
const dmDelim = desc.includes('\n') ? '\n\n' : '\n';
|
|
816
|
-
const extra = [opts, mult].join(dmDelim).trim();
|
|
817
|
-
const text = (normalize(desc) + dmDelim + extra).trim();
|
|
818
|
-
const hint = value.hint ||
|
|
819
|
-
(value.type === 'number' ? 'n'
|
|
820
|
-
: value.type === 'string' ? field.name
|
|
821
|
-
: undefined);
|
|
822
|
-
const short = !value.short ? ''
|
|
823
|
-
: value.type === 'boolean' ? `-${value.short} `
|
|
824
|
-
: `-${value.short}<${hint}> `;
|
|
825
|
-
const left = value.type === 'boolean' ?
|
|
826
|
-
`${short}--${field.name}`
|
|
827
|
-
: `${short}--${field.name}=<${hint}>`;
|
|
828
|
-
const row = { text, left, type: 'config' };
|
|
829
|
-
if (text.length > width - maxMax) {
|
|
830
|
-
row.skipLine = true;
|
|
831
|
-
}
|
|
832
|
-
if (prev && left.length > maxMax)
|
|
833
|
-
prev.skipLine = true;
|
|
834
|
-
prev = row;
|
|
835
|
-
const len = left.length + 4;
|
|
836
|
-
if (len > maxWidth && len < maxMax) {
|
|
837
|
-
maxWidth = len;
|
|
838
|
-
}
|
|
839
|
-
rows.push(row);
|
|
840
|
-
}
|
|
841
|
-
return { rows, maxWidth };
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Return the configuration options as a plain object
|
|
845
|
-
*/
|
|
846
|
-
toJSON() {
|
|
847
|
-
return Object.fromEntries(Object.entries(this.#configSet).map(([field, def]) => [
|
|
848
|
-
field,
|
|
849
|
-
{
|
|
850
|
-
type: def.type,
|
|
851
|
-
...(def.multiple ? { multiple: true } : {}),
|
|
852
|
-
...(def.delim ? { delim: def.delim } : {}),
|
|
853
|
-
...(def.short ? { short: def.short } : {}),
|
|
854
|
-
...(def.description ?
|
|
855
|
-
{ description: normalize(def.description) }
|
|
856
|
-
: {}),
|
|
857
|
-
...(def.validate ? { validate: def.validate } : {}),
|
|
858
|
-
...(def.validOptions ? { validOptions: def.validOptions } : {}),
|
|
859
|
-
...(def.default !== undefined ? { default: def.default } : {}),
|
|
860
|
-
...(def.hint ? { hint: def.hint } : {}),
|
|
861
|
-
},
|
|
862
|
-
]));
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* Custom printer for `util.inspect`
|
|
866
|
-
*/
|
|
867
|
-
[inspect.custom](_, options) {
|
|
868
|
-
return `Jack ${inspect(this.toJSON(), options)}`;
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
/**
|
|
872
|
-
* Main entry point. Create and return a {@link Jack} object.
|
|
873
|
-
*/
|
|
874
|
-
export const jack = (options = {}) => new Jack(options);
|
|
875
|
-
// Unwrap and un-indent, so we can wrap description
|
|
876
|
-
// strings however makes them look nice in the code.
|
|
877
|
-
const normalize = (s, pre = false) => {
|
|
878
|
-
if (pre)
|
|
879
|
-
// prepend a ZWSP to each line so cliui doesn't strip it.
|
|
880
|
-
return s
|
|
881
|
-
.split('\n')
|
|
882
|
-
.map(l => `\u200b${l}`)
|
|
883
|
-
.join('\n');
|
|
884
|
-
return s
|
|
885
|
-
.split(/^\s*```\s*$/gm)
|
|
886
|
-
.map((s, i) => {
|
|
887
|
-
if (i % 2 === 1) {
|
|
888
|
-
if (!s.trim()) {
|
|
889
|
-
return `\`\`\`\n\`\`\`\n`;
|
|
890
|
-
}
|
|
891
|
-
// outdent the ``` blocks, but preserve whitespace otherwise.
|
|
892
|
-
const split = s.split('\n');
|
|
893
|
-
// throw out the \n at the start and end
|
|
894
|
-
split.pop();
|
|
895
|
-
split.shift();
|
|
896
|
-
const si = split.reduce((shortest, l) => {
|
|
897
|
-
/* c8 ignore next */
|
|
898
|
-
const ind = l.match(/^\s*/)?.[0] ?? '';
|
|
899
|
-
if (ind.length)
|
|
900
|
-
return Math.min(ind.length, shortest);
|
|
901
|
-
else
|
|
902
|
-
return shortest;
|
|
903
|
-
}, Infinity);
|
|
904
|
-
/* c8 ignore next */
|
|
905
|
-
const i = isFinite(si) ? si : 0;
|
|
906
|
-
return ('\n```\n' +
|
|
907
|
-
split.map(s => `\u200b${s.substring(i)}`).join('\n') +
|
|
908
|
-
'\n```\n');
|
|
909
|
-
}
|
|
910
|
-
return (s
|
|
911
|
-
// remove single line breaks, except for lists
|
|
912
|
-
.replace(/([^\n])\n[ \t]*([^\n])/g, (_, $1, $2) => !/^[-*]/.test($2) ? `${$1} ${$2}` : `${$1}\n${$2}`)
|
|
913
|
-
// normalize mid-line whitespace
|
|
914
|
-
.replace(/([^\n])[ \t]+([^\n])/g, '$1 $2')
|
|
915
|
-
// two line breaks are enough
|
|
916
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
917
|
-
// remove any spaces at the start of a line
|
|
918
|
-
.replace(/\n[ \t]+/g, '\n')
|
|
919
|
-
.trim());
|
|
920
|
-
})
|
|
921
|
-
.join('\n');
|
|
922
|
-
};
|
|
923
|
-
// normalize for markdown printing, remove leading spaces on lines
|
|
924
|
-
const normalizeMarkdown = (s, pre = false) => {
|
|
925
|
-
const n = normalize(s, pre).replace(/\\/g, '\\\\');
|
|
926
|
-
return pre ?
|
|
927
|
-
`\`\`\`\n${n.replace(/\u200b/g, '')}\n\`\`\``
|
|
928
|
-
: n.replace(/\n +/g, '\n').trim();
|
|
929
|
-
};
|
|
930
|
-
const normalizeOneLine = (s, pre = false) => {
|
|
931
|
-
const n = normalize(s, pre)
|
|
932
|
-
.replace(/[\s\u200b]+/g, ' ')
|
|
933
|
-
.trim();
|
|
934
|
-
return pre ? `\`${n}\`` : n;
|
|
935
|
-
};
|
|
936
|
-
//# sourceMappingURL=index.js.map
|