@optique/derived-defaults 1.2.0-dev.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/LICENSE +20 -0
- package/README.md +83 -0
- package/dist/index.cjs +348 -0
- package/dist/index.d.cts +75 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +324 -0
- package/package.json +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2025–2026 Hong Minhee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
@optique/derived-defaults
|
|
2
|
+
=========================
|
|
3
|
+
|
|
4
|
+
Derived default value support for [Optique].
|
|
5
|
+
|
|
6
|
+
This package lets CLI applications compute default values from the first-pass
|
|
7
|
+
parse result while keeping clear priority handling:
|
|
8
|
+
|
|
9
|
+
CLI arguments > derived defaults > static defaults.
|
|
10
|
+
|
|
11
|
+
[Optique]: https://optique.dev/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Installation
|
|
15
|
+
------------
|
|
16
|
+
|
|
17
|
+
~~~~ bash
|
|
18
|
+
deno add jsr:@optique/derived-defaults
|
|
19
|
+
npm add @optique/derived-defaults
|
|
20
|
+
pnpm add @optique/derived-defaults
|
|
21
|
+
yarn add @optique/derived-defaults
|
|
22
|
+
bun add @optique/derived-defaults
|
|
23
|
+
~~~~
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
Quick start
|
|
27
|
+
-----------
|
|
28
|
+
|
|
29
|
+
~~~~ typescript
|
|
30
|
+
import { object } from "@optique/core/constructs";
|
|
31
|
+
import { option } from "@optique/core/primitives";
|
|
32
|
+
import { string } from "@optique/core/valueparser";
|
|
33
|
+
import { runAsync } from "@optique/run";
|
|
34
|
+
import {
|
|
35
|
+
bindDerivedDefault,
|
|
36
|
+
createDerivedDefaults,
|
|
37
|
+
} from "@optique/derived-defaults";
|
|
38
|
+
|
|
39
|
+
const derived = createDerivedDefaults({
|
|
40
|
+
workspaceRoot: (parsed: { readonly serviceRoot?: string }) =>
|
|
41
|
+
parsed.serviceRoot == null
|
|
42
|
+
? undefined
|
|
43
|
+
: `${parsed.serviceRoot}/workspace`,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const parser = object({
|
|
47
|
+
serviceRoot: option("--service-root", string()),
|
|
48
|
+
workspaceRoot: bindDerivedDefault(option("--workspace-root", string()), {
|
|
49
|
+
context: derived.context,
|
|
50
|
+
key: "workspaceRoot",
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const result = await runAsync(parser, {
|
|
55
|
+
args: ["--service-root", "/srv/app"],
|
|
56
|
+
contexts: [derived.context],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
console.log(result);
|
|
60
|
+
~~~~
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
Features
|
|
64
|
+
--------
|
|
65
|
+
|
|
66
|
+
- *Two-pass defaults* derived from already parsed CLI values
|
|
67
|
+
- *Async resolver support* with `runAsync()`/`runWith()`
|
|
68
|
+
- *Fallback validation* through the wrapped Optique parser
|
|
69
|
+
- *Custom help text* for values that are computed at runtime
|
|
70
|
+
- *Composable contexts* with `run()`/`runAsync()`/`runWith()`
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
Documentation
|
|
74
|
+
-------------
|
|
75
|
+
|
|
76
|
+
For full documentation, visit
|
|
77
|
+
<https://optique.dev/concepts/derived-defaults>.
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
License
|
|
81
|
+
-------
|
|
82
|
+
|
|
83
|
+
MIT License. See [LICENSE](../../LICENSE) for details.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
const __optique_core_message = __toESM(require("@optique/core/message"));
|
|
25
|
+
const __optique_core_extension = __toESM(require("@optique/core/extension"));
|
|
26
|
+
const __optique_core_fluent = __toESM(require("@optique/core/fluent"));
|
|
27
|
+
const __optique_core_annotations = __toESM(require("@optique/core/annotations"));
|
|
28
|
+
|
|
29
|
+
//#region src/index.ts
|
|
30
|
+
const phase1DerivedDefaultAnnotationMarker = Symbol("@optique/derived-defaults/phase1Annotation");
|
|
31
|
+
function getTypeName(value) {
|
|
32
|
+
if (value === null) return "null";
|
|
33
|
+
if (Array.isArray(value)) return "array";
|
|
34
|
+
return typeof value;
|
|
35
|
+
}
|
|
36
|
+
function isPromiseLike(value) {
|
|
37
|
+
return value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
|
|
38
|
+
}
|
|
39
|
+
function validateSourceContextRequest(request) {
|
|
40
|
+
if (request === void 0) return;
|
|
41
|
+
if (request === null || typeof request !== "object" || !("phase" in request) || request.phase !== "phase1" && request.phase !== "phase2" || request.phase === "phase2" && !("parsed" in request)) throw new TypeError(`Expected getAnnotations() to receive no request or a SourceContextRequest ({ phase: "phase1" } or { phase: "phase2", parsed }), but got: ${getTypeName(request)}.`);
|
|
42
|
+
}
|
|
43
|
+
function replaceDefaultDescription(fragments, description) {
|
|
44
|
+
if (description == null) return fragments;
|
|
45
|
+
return fragments.map((fragment) => {
|
|
46
|
+
if (fragment.type === "entry") return {
|
|
47
|
+
...fragment,
|
|
48
|
+
default: description
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
...fragment,
|
|
52
|
+
entries: fragment.entries.map((entry) => ({
|
|
53
|
+
...entry,
|
|
54
|
+
default: description
|
|
55
|
+
}))
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function validateFallbackValue(mode, innerParser, value) {
|
|
60
|
+
if (innerParser == null || typeof innerParser.validateValue !== "function") return (0, __optique_core_extension.wrapForMode)(mode, {
|
|
61
|
+
success: true,
|
|
62
|
+
value
|
|
63
|
+
});
|
|
64
|
+
return (0, __optique_core_extension.wrapForMode)(mode, innerParser.validateValue(value));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Creates a derived-default context.
|
|
68
|
+
*
|
|
69
|
+
* @param resolvers Resolver map keyed by derived value name.
|
|
70
|
+
* @returns A derived-default context bundle.
|
|
71
|
+
* @throws {TypeError} If `resolvers` is not an object or contains a
|
|
72
|
+
* non-function resolver.
|
|
73
|
+
* @since 1.2.0
|
|
74
|
+
*/
|
|
75
|
+
function createDerivedDefaults(resolvers) {
|
|
76
|
+
if (resolvers == null || typeof resolvers !== "object") throw new TypeError(`Expected resolvers to be an object, but got: ${getTypeName(resolvers)}.`);
|
|
77
|
+
for (const key of Reflect.ownKeys(resolvers)) {
|
|
78
|
+
const resolver = resolvers[key];
|
|
79
|
+
if (typeof resolver !== "function") throw new TypeError(`Expected resolver ${String(key)} to be a function, but got: ${getTypeName(resolver)}.`);
|
|
80
|
+
}
|
|
81
|
+
const contextId = Symbol(`@optique/derived-defaults:${Math.random()}`);
|
|
82
|
+
const context = {
|
|
83
|
+
id: contextId,
|
|
84
|
+
phase: "two-pass",
|
|
85
|
+
getInternalAnnotations(request, annotations) {
|
|
86
|
+
if (request.phase === "phase1") return { [contextId]: phase1DerivedDefaultAnnotationMarker };
|
|
87
|
+
return contextId in annotations ? void 0 : { [contextId]: void 0 };
|
|
88
|
+
},
|
|
89
|
+
getAnnotations(request) {
|
|
90
|
+
validateSourceContextRequest(request);
|
|
91
|
+
if (request?.phase !== "phase2") return {};
|
|
92
|
+
const values = {};
|
|
93
|
+
const pending = [];
|
|
94
|
+
for (const key of Reflect.ownKeys(resolvers)) {
|
|
95
|
+
const resolver = resolvers[key];
|
|
96
|
+
if (request.parsed == null && resolver.length > 0) continue;
|
|
97
|
+
const resolved = resolver(request.parsed ?? void 0);
|
|
98
|
+
if (isPromiseLike(resolved)) pending.push(Promise.resolve(resolved).then((value) => {
|
|
99
|
+
if (value !== void 0) values[key] = value;
|
|
100
|
+
}));
|
|
101
|
+
else if (resolved !== void 0) values[key] = resolved;
|
|
102
|
+
}
|
|
103
|
+
const buildAnnotations = () => Reflect.ownKeys(values).length === 0 ? {} : { [contextId]: { values } };
|
|
104
|
+
return pending.length === 0 ? buildAnnotations() : Promise.all(pending).then(buildAnnotations);
|
|
105
|
+
},
|
|
106
|
+
[Symbol.dispose]() {}
|
|
107
|
+
};
|
|
108
|
+
return { context };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Binds a parser to a derived default value.
|
|
112
|
+
*
|
|
113
|
+
* @param parser Parser that reads the CLI value.
|
|
114
|
+
* @param options Derived default binding options.
|
|
115
|
+
* @returns A parser with derived fallback behavior.
|
|
116
|
+
* @throws {TypeError} If `options.key` is not a property key.
|
|
117
|
+
* @since 1.2.0
|
|
118
|
+
*/
|
|
119
|
+
function bindDerivedDefault(parser, options) {
|
|
120
|
+
const keyType = typeof options.key;
|
|
121
|
+
if (keyType !== "string" && keyType !== "number" && keyType !== "symbol") throw new TypeError(`Expected key to be a property key, but got: ${getTypeName(options.key)}.`);
|
|
122
|
+
const bindStateKey = Symbol("@optique/derived-defaults/bindState");
|
|
123
|
+
function isBindState(value) {
|
|
124
|
+
return value != null && typeof value === "object" && bindStateKey in value;
|
|
125
|
+
}
|
|
126
|
+
function shouldDeferCompletion(state, exec) {
|
|
127
|
+
const annotations = (0, __optique_core_annotations.getAnnotations)(state);
|
|
128
|
+
if (annotations?.[options.context.id] === phase1DerivedDefaultAnnotationMarker) return true;
|
|
129
|
+
return parser.shouldDeferCompletion?.(getInnerState(state), exec) === true;
|
|
130
|
+
}
|
|
131
|
+
function getInnerState(state) {
|
|
132
|
+
if (!isBindState(state)) return state;
|
|
133
|
+
if (state.cliState !== void 0) return state.cliState;
|
|
134
|
+
const initialState = parser.initialState;
|
|
135
|
+
if (initialState != null && typeof initialState !== "object") return initialState;
|
|
136
|
+
const annotated = (0, __optique_core_extension.inheritAnnotations)(state, initialState);
|
|
137
|
+
if (annotated === initialState && initialState != null && typeof initialState === "object") {
|
|
138
|
+
const annotations = (0, __optique_core_annotations.getAnnotations)(state);
|
|
139
|
+
return annotations == null ? initialState : (0, __optique_core_extension.withAnnotationView)(initialState, annotations);
|
|
140
|
+
}
|
|
141
|
+
return annotated;
|
|
142
|
+
}
|
|
143
|
+
function getDerivedValue(state) {
|
|
144
|
+
const annotations = (0, __optique_core_annotations.getAnnotations)(state);
|
|
145
|
+
const annotationValue = annotations?.[options.context.id];
|
|
146
|
+
if (annotationValue == null || typeof annotationValue !== "object" || !("values" in annotationValue)) return void 0;
|
|
147
|
+
return annotationValue.values[options.key];
|
|
148
|
+
}
|
|
149
|
+
function hasDerivedFallback(state) {
|
|
150
|
+
if (getDerivedValue(state) !== void 0) return true;
|
|
151
|
+
return options.default !== void 0;
|
|
152
|
+
}
|
|
153
|
+
function getDerivedOrDefault(state, mode, exec, innerParser) {
|
|
154
|
+
const annotations = (0, __optique_core_annotations.getAnnotations)(state);
|
|
155
|
+
const contextId = options.context.id;
|
|
156
|
+
const contextAbsent = annotations != null && !(contextId in annotations);
|
|
157
|
+
const derivedValue = getDerivedValue(state);
|
|
158
|
+
if (derivedValue !== void 0) return validateFallbackValue(mode, innerParser, derivedValue);
|
|
159
|
+
if (options.default !== void 0) return validateFallbackValue(mode, innerParser, options.default);
|
|
160
|
+
if (innerParser?.canSkip?.(getInnerState(state), exec) === true) return (0, __optique_core_extension.mapModeValue)(mode, (0, __optique_core_extension.wrapForMode)(mode, innerParser.complete(getInnerState(state), exec)), (result) => {
|
|
161
|
+
if (result.success) return result;
|
|
162
|
+
if (contextAbsent) return {
|
|
163
|
+
success: false,
|
|
164
|
+
error: __optique_core_message.message`Derived default value could not be read: the derived default context was not passed to run()'s contexts option.`
|
|
165
|
+
};
|
|
166
|
+
return result;
|
|
167
|
+
});
|
|
168
|
+
if (contextAbsent) return (0, __optique_core_extension.wrapForMode)(mode, {
|
|
169
|
+
success: false,
|
|
170
|
+
error: __optique_core_message.message`Derived default value could not be read: the derived default context was not passed to run()'s contexts option.`
|
|
171
|
+
});
|
|
172
|
+
return (0, __optique_core_extension.wrapForMode)(mode, {
|
|
173
|
+
success: false,
|
|
174
|
+
error: __optique_core_message.message`Missing required derived default value.`
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function getDerivedSourceValue(state, innerState, mode, extractInnerSourceValue, innerParser) {
|
|
178
|
+
const derivedValue = getDerivedValue(state);
|
|
179
|
+
const validateFallback = (parsed) => {
|
|
180
|
+
if (!parsed.success) return parsed;
|
|
181
|
+
if (innerParser == null || typeof innerParser.validateValue !== "function") return parsed;
|
|
182
|
+
return (0, __optique_core_extension.wrapForMode)(mode, innerParser.validateValue(parsed.value));
|
|
183
|
+
};
|
|
184
|
+
if (derivedValue !== void 0) return (0, __optique_core_extension.wrapForMode)(mode, validateFallback({
|
|
185
|
+
success: true,
|
|
186
|
+
value: derivedValue
|
|
187
|
+
}));
|
|
188
|
+
if (options.default !== void 0) return (0, __optique_core_extension.wrapForMode)(mode, validateFallback({
|
|
189
|
+
success: true,
|
|
190
|
+
value: options.default
|
|
191
|
+
}));
|
|
192
|
+
return (0, __optique_core_extension.wrapForMode)(mode, extractInnerSourceValue(innerState));
|
|
193
|
+
}
|
|
194
|
+
const boundParser = {
|
|
195
|
+
mode: parser.mode,
|
|
196
|
+
$valueType: parser.$valueType,
|
|
197
|
+
$stateType: parser.$stateType,
|
|
198
|
+
priority: parser.priority,
|
|
199
|
+
usage: options.default !== void 0 ? [{
|
|
200
|
+
type: "optional",
|
|
201
|
+
terms: parser.usage
|
|
202
|
+
}] : parser.usage,
|
|
203
|
+
leadingNames: parser.leadingNames,
|
|
204
|
+
acceptingAnyToken: parser.acceptingAnyToken,
|
|
205
|
+
initialState: parser.initialState,
|
|
206
|
+
canSkip(state, exec) {
|
|
207
|
+
if (isBindState(state)) {
|
|
208
|
+
if (state.hasCliValue) return parser.canSkip?.(state.cliState, exec) === true;
|
|
209
|
+
if (hasDerivedFallback(state)) return true;
|
|
210
|
+
return parser.canSkip?.(getInnerState(state), exec) === true;
|
|
211
|
+
}
|
|
212
|
+
if (hasDerivedFallback(state)) return true;
|
|
213
|
+
return parser.canSkip?.(state, exec) === true;
|
|
214
|
+
},
|
|
215
|
+
getSuggestRuntimeNodes(state, path) {
|
|
216
|
+
const innerState = getInnerState(state);
|
|
217
|
+
return (0, __optique_core_extension.delegateSuggestNodes)(parser, boundParser, state, path, innerState);
|
|
218
|
+
},
|
|
219
|
+
parse(context) {
|
|
220
|
+
const annotations = (0, __optique_core_annotations.getAnnotations)(context.state);
|
|
221
|
+
const innerState = isBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
|
|
222
|
+
const innerContext = innerState !== context.state ? {
|
|
223
|
+
...context,
|
|
224
|
+
state: innerState
|
|
225
|
+
} : context;
|
|
226
|
+
const processResult = (result) => {
|
|
227
|
+
if (result.success) {
|
|
228
|
+
const cliConsumed = result.consumed.length > 0;
|
|
229
|
+
const nextState$1 = (0, __optique_core_extension.injectAnnotations)({
|
|
230
|
+
[bindStateKey]: true,
|
|
231
|
+
hasCliValue: cliConsumed,
|
|
232
|
+
cliState: result.next.state
|
|
233
|
+
}, annotations);
|
|
234
|
+
return {
|
|
235
|
+
success: true,
|
|
236
|
+
...result.provisional ? { provisional: true } : {},
|
|
237
|
+
next: {
|
|
238
|
+
...result.next,
|
|
239
|
+
state: nextState$1
|
|
240
|
+
},
|
|
241
|
+
consumed: result.consumed
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (result.consumed > 0) return result;
|
|
245
|
+
const nextState = (0, __optique_core_extension.injectAnnotations)({
|
|
246
|
+
[bindStateKey]: true,
|
|
247
|
+
hasCliValue: false
|
|
248
|
+
}, annotations);
|
|
249
|
+
return {
|
|
250
|
+
success: true,
|
|
251
|
+
next: {
|
|
252
|
+
...innerContext,
|
|
253
|
+
state: nextState
|
|
254
|
+
},
|
|
255
|
+
consumed: []
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
return (0, __optique_core_extension.mapModeValue)(parser.mode, (0, __optique_core_extension.wrapForMode)(parser.mode, parser.parse(innerContext)), processResult);
|
|
259
|
+
},
|
|
260
|
+
complete(state, exec) {
|
|
261
|
+
if (isBindState(state) && state.hasCliValue) return (0, __optique_core_extension.wrapForMode)(parser.mode, parser.complete(state.cliState, exec));
|
|
262
|
+
return getDerivedOrDefault(state, parser.mode, exec, parser);
|
|
263
|
+
},
|
|
264
|
+
suggest(context, prefix) {
|
|
265
|
+
const innerState = getInnerState(context.state);
|
|
266
|
+
const innerContext = innerState !== context.state ? {
|
|
267
|
+
...context,
|
|
268
|
+
state: innerState
|
|
269
|
+
} : context;
|
|
270
|
+
return parser.suggest(innerContext, prefix);
|
|
271
|
+
},
|
|
272
|
+
shouldDeferCompletion,
|
|
273
|
+
getDocFragments(state, upperDefaultValue) {
|
|
274
|
+
const defaultValue = upperDefaultValue ?? options.default;
|
|
275
|
+
const fragments = parser.getDocFragments(state, defaultValue);
|
|
276
|
+
if (upperDefaultValue !== void 0 || options.defaultDescription == null) return fragments;
|
|
277
|
+
return {
|
|
278
|
+
...fragments,
|
|
279
|
+
fragments: replaceDefaultDescription(fragments.fragments, options.defaultDescription)
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
Object.defineProperty(boundParser, __optique_core_extension.extractPhase2SeedKey, {
|
|
284
|
+
value(state, exec) {
|
|
285
|
+
const annotations = (0, __optique_core_annotations.getAnnotations)(state);
|
|
286
|
+
if (annotations?.[options.context.id] !== phase1DerivedDefaultAnnotationMarker) {
|
|
287
|
+
const extractInnerPhase2Seed = parser[__optique_core_extension.extractPhase2SeedKey];
|
|
288
|
+
return extractInnerPhase2Seed == null ? (0, __optique_core_extension.wrapForMode)(parser.mode, null) : (0, __optique_core_extension.wrapForMode)(parser.mode, extractInnerPhase2Seed(getInnerState(state), exec));
|
|
289
|
+
}
|
|
290
|
+
return (0, __optique_core_extension.wrapForMode)(parser.mode, {
|
|
291
|
+
value: void 0,
|
|
292
|
+
deferred: true
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
configurable: true
|
|
296
|
+
});
|
|
297
|
+
(0, __optique_core_extension.defineTraits)(boundParser, {
|
|
298
|
+
inheritsAnnotations: true,
|
|
299
|
+
completesFromSource: true
|
|
300
|
+
});
|
|
301
|
+
if ("placeholder" in parser) Object.defineProperty(boundParser, "placeholder", {
|
|
302
|
+
get() {
|
|
303
|
+
return parser.placeholder;
|
|
304
|
+
},
|
|
305
|
+
configurable: true,
|
|
306
|
+
enumerable: false
|
|
307
|
+
});
|
|
308
|
+
if (typeof parser.normalizeValue === "function") Object.defineProperty(boundParser, "normalizeValue", {
|
|
309
|
+
value: parser.normalizeValue.bind(parser),
|
|
310
|
+
configurable: true,
|
|
311
|
+
enumerable: false
|
|
312
|
+
});
|
|
313
|
+
if (typeof parser.validateValue === "function") Object.defineProperty(boundParser, "validateValue", {
|
|
314
|
+
value: parser.validateValue.bind(parser),
|
|
315
|
+
configurable: true,
|
|
316
|
+
enumerable: false
|
|
317
|
+
});
|
|
318
|
+
const dependencyMetadata = (0, __optique_core_extension.mapSourceMetadata)(parser, (sourceMetadata) => ({
|
|
319
|
+
...sourceMetadata,
|
|
320
|
+
getMissingSourceValue: sourceMetadata.preservesSourceValue !== false && options.default !== void 0 ? () => {
|
|
321
|
+
if (typeof parser.validateValue === "function") return (0, __optique_core_extension.wrapForMode)(parser.mode, parser.validateValue(options.default));
|
|
322
|
+
return (0, __optique_core_extension.wrapForMode)(parser.mode, {
|
|
323
|
+
success: true,
|
|
324
|
+
value: options.default
|
|
325
|
+
});
|
|
326
|
+
} : void 0,
|
|
327
|
+
extractSourceValue: (state) => {
|
|
328
|
+
if (!isBindState(state)) {
|
|
329
|
+
if (sourceMetadata.preservesSourceValue) return getDerivedSourceValue(state, state, parser.mode, sourceMetadata.extractSourceValue, parser);
|
|
330
|
+
return (0, __optique_core_extension.wrapForMode)(parser.mode, sourceMetadata.extractSourceValue(state));
|
|
331
|
+
}
|
|
332
|
+
if (state.hasCliValue) return (0, __optique_core_extension.wrapForMode)(parser.mode, sourceMetadata.extractSourceValue(state.cliState));
|
|
333
|
+
const fallbackState = state.cliState ?? state;
|
|
334
|
+
if (!sourceMetadata.preservesSourceValue) return (0, __optique_core_extension.wrapForMode)(parser.mode, sourceMetadata.extractSourceValue(fallbackState));
|
|
335
|
+
return getDerivedSourceValue(state, fallbackState, parser.mode, sourceMetadata.extractSourceValue, parser);
|
|
336
|
+
}
|
|
337
|
+
}));
|
|
338
|
+
if (dependencyMetadata != null) Object.defineProperty(boundParser, "dependencyMetadata", {
|
|
339
|
+
value: dependencyMetadata,
|
|
340
|
+
configurable: true,
|
|
341
|
+
enumerable: false
|
|
342
|
+
});
|
|
343
|
+
return (0, __optique_core_fluent.fluent)(boundParser);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
//#endregion
|
|
347
|
+
exports.bindDerivedDefault = bindDerivedDefault;
|
|
348
|
+
exports.createDerivedDefaults = createDerivedDefaults;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Message } from "@optique/core/message";
|
|
2
|
+
import { FluentParser } from "@optique/core/fluent";
|
|
3
|
+
import { Mode, Parser } from "@optique/core/parser";
|
|
4
|
+
import { SourceContext } from "@optique/core/context";
|
|
5
|
+
|
|
6
|
+
//#region src/index.d.ts
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolver function for a derived default value.
|
|
10
|
+
*
|
|
11
|
+
* @since 1.2.0
|
|
12
|
+
*/
|
|
13
|
+
type DerivedDefaultResolver<TParsed, TValue> = (parsed: TParsed) => TValue | undefined | Promise<TValue | undefined>;
|
|
14
|
+
/**
|
|
15
|
+
* Values produced by a derived-default resolver map.
|
|
16
|
+
*
|
|
17
|
+
* @since 1.2.0
|
|
18
|
+
*/
|
|
19
|
+
type DerivedDefaultValues<TResolvers> = { readonly [K in keyof TResolvers]: TResolvers[K] extends ((parsed: infer _TParsed) => infer TValue) ? Exclude<Awaited<TValue>, undefined> : never };
|
|
20
|
+
/**
|
|
21
|
+
* Keys whose derived default values are assignable to a parser value type.
|
|
22
|
+
*
|
|
23
|
+
* @since 1.2.0
|
|
24
|
+
*/
|
|
25
|
+
type DerivedDefaultKey<TValues extends object, TValue> = { readonly [K in keyof TValues]: TValues[K] extends TValue ? K : never }[keyof TValues];
|
|
26
|
+
/**
|
|
27
|
+
* Source context for derived defaults.
|
|
28
|
+
*
|
|
29
|
+
* @since 1.2.0
|
|
30
|
+
*/
|
|
31
|
+
interface DerivedDefaultsContext<TValues extends object> extends SourceContext {
|
|
32
|
+
readonly phase: "two-pass";
|
|
33
|
+
readonly $values?: TValues;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Derived-default context bundle.
|
|
37
|
+
*
|
|
38
|
+
* @since 1.2.0
|
|
39
|
+
*/
|
|
40
|
+
interface DerivedDefaults<TValues extends object> {
|
|
41
|
+
readonly context: DerivedDefaultsContext<TValues>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Options for binding a parser to a derived default value.
|
|
45
|
+
*
|
|
46
|
+
* @since 1.2.0
|
|
47
|
+
*/
|
|
48
|
+
interface BindDerivedDefaultOptions<TValues extends object, TValue, TKey extends DerivedDefaultKey<TValues, TValue>> {
|
|
49
|
+
readonly context: DerivedDefaultsContext<TValues>;
|
|
50
|
+
readonly key: TKey;
|
|
51
|
+
readonly default?: TValue;
|
|
52
|
+
readonly defaultDescription?: Message;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates a derived-default context.
|
|
56
|
+
*
|
|
57
|
+
* @param resolvers Resolver map keyed by derived value name.
|
|
58
|
+
* @returns A derived-default context bundle.
|
|
59
|
+
* @throws {TypeError} If `resolvers` is not an object or contains a
|
|
60
|
+
* non-function resolver.
|
|
61
|
+
* @since 1.2.0
|
|
62
|
+
*/
|
|
63
|
+
declare function createDerivedDefaults<const TResolvers extends object>(resolvers: TResolvers): DerivedDefaults<DerivedDefaultValues<TResolvers>>;
|
|
64
|
+
/**
|
|
65
|
+
* Binds a parser to a derived default value.
|
|
66
|
+
*
|
|
67
|
+
* @param parser Parser that reads the CLI value.
|
|
68
|
+
* @param options Derived default binding options.
|
|
69
|
+
* @returns A parser with derived fallback behavior.
|
|
70
|
+
* @throws {TypeError} If `options.key` is not a property key.
|
|
71
|
+
* @since 1.2.0
|
|
72
|
+
*/
|
|
73
|
+
declare function bindDerivedDefault<M extends Mode, TValue, TState, TValues extends object, TKey extends DerivedDefaultKey<TValues, TValue>>(parser: Parser<M, TValue, TState>, options: BindDerivedDefaultOptions<TValues, TValue, TKey>): FluentParser<M, TValue, TState>;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { BindDerivedDefaultOptions, DerivedDefaultKey, DerivedDefaultResolver, DerivedDefaultValues, DerivedDefaults, DerivedDefaultsContext, bindDerivedDefault, createDerivedDefaults };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Message } from "@optique/core/message";
|
|
2
|
+
import { FluentParser } from "@optique/core/fluent";
|
|
3
|
+
import { Mode, Parser } from "@optique/core/parser";
|
|
4
|
+
import { SourceContext } from "@optique/core/context";
|
|
5
|
+
|
|
6
|
+
//#region src/index.d.ts
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolver function for a derived default value.
|
|
10
|
+
*
|
|
11
|
+
* @since 1.2.0
|
|
12
|
+
*/
|
|
13
|
+
type DerivedDefaultResolver<TParsed, TValue> = (parsed: TParsed) => TValue | undefined | Promise<TValue | undefined>;
|
|
14
|
+
/**
|
|
15
|
+
* Values produced by a derived-default resolver map.
|
|
16
|
+
*
|
|
17
|
+
* @since 1.2.0
|
|
18
|
+
*/
|
|
19
|
+
type DerivedDefaultValues<TResolvers> = { readonly [K in keyof TResolvers]: TResolvers[K] extends ((parsed: infer _TParsed) => infer TValue) ? Exclude<Awaited<TValue>, undefined> : never };
|
|
20
|
+
/**
|
|
21
|
+
* Keys whose derived default values are assignable to a parser value type.
|
|
22
|
+
*
|
|
23
|
+
* @since 1.2.0
|
|
24
|
+
*/
|
|
25
|
+
type DerivedDefaultKey<TValues extends object, TValue> = { readonly [K in keyof TValues]: TValues[K] extends TValue ? K : never }[keyof TValues];
|
|
26
|
+
/**
|
|
27
|
+
* Source context for derived defaults.
|
|
28
|
+
*
|
|
29
|
+
* @since 1.2.0
|
|
30
|
+
*/
|
|
31
|
+
interface DerivedDefaultsContext<TValues extends object> extends SourceContext {
|
|
32
|
+
readonly phase: "two-pass";
|
|
33
|
+
readonly $values?: TValues;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Derived-default context bundle.
|
|
37
|
+
*
|
|
38
|
+
* @since 1.2.0
|
|
39
|
+
*/
|
|
40
|
+
interface DerivedDefaults<TValues extends object> {
|
|
41
|
+
readonly context: DerivedDefaultsContext<TValues>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Options for binding a parser to a derived default value.
|
|
45
|
+
*
|
|
46
|
+
* @since 1.2.0
|
|
47
|
+
*/
|
|
48
|
+
interface BindDerivedDefaultOptions<TValues extends object, TValue, TKey extends DerivedDefaultKey<TValues, TValue>> {
|
|
49
|
+
readonly context: DerivedDefaultsContext<TValues>;
|
|
50
|
+
readonly key: TKey;
|
|
51
|
+
readonly default?: TValue;
|
|
52
|
+
readonly defaultDescription?: Message;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates a derived-default context.
|
|
56
|
+
*
|
|
57
|
+
* @param resolvers Resolver map keyed by derived value name.
|
|
58
|
+
* @returns A derived-default context bundle.
|
|
59
|
+
* @throws {TypeError} If `resolvers` is not an object or contains a
|
|
60
|
+
* non-function resolver.
|
|
61
|
+
* @since 1.2.0
|
|
62
|
+
*/
|
|
63
|
+
declare function createDerivedDefaults<const TResolvers extends object>(resolvers: TResolvers): DerivedDefaults<DerivedDefaultValues<TResolvers>>;
|
|
64
|
+
/**
|
|
65
|
+
* Binds a parser to a derived default value.
|
|
66
|
+
*
|
|
67
|
+
* @param parser Parser that reads the CLI value.
|
|
68
|
+
* @param options Derived default binding options.
|
|
69
|
+
* @returns A parser with derived fallback behavior.
|
|
70
|
+
* @throws {TypeError} If `options.key` is not a property key.
|
|
71
|
+
* @since 1.2.0
|
|
72
|
+
*/
|
|
73
|
+
declare function bindDerivedDefault<M extends Mode, TValue, TState, TValues extends object, TKey extends DerivedDefaultKey<TValues, TValue>>(parser: Parser<M, TValue, TState>, options: BindDerivedDefaultOptions<TValues, TValue, TKey>): FluentParser<M, TValue, TState>;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { BindDerivedDefaultOptions, DerivedDefaultKey, DerivedDefaultResolver, DerivedDefaultValues, DerivedDefaults, DerivedDefaultsContext, bindDerivedDefault, createDerivedDefaults };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { message } from "@optique/core/message";
|
|
2
|
+
import { defineTraits, delegateSuggestNodes, extractPhase2SeedKey, inheritAnnotations, injectAnnotations, mapModeValue, mapSourceMetadata, withAnnotationView, wrapForMode } from "@optique/core/extension";
|
|
3
|
+
import { fluent } from "@optique/core/fluent";
|
|
4
|
+
import { getAnnotations } from "@optique/core/annotations";
|
|
5
|
+
|
|
6
|
+
//#region src/index.ts
|
|
7
|
+
const phase1DerivedDefaultAnnotationMarker = Symbol("@optique/derived-defaults/phase1Annotation");
|
|
8
|
+
function getTypeName(value) {
|
|
9
|
+
if (value === null) return "null";
|
|
10
|
+
if (Array.isArray(value)) return "array";
|
|
11
|
+
return typeof value;
|
|
12
|
+
}
|
|
13
|
+
function isPromiseLike(value) {
|
|
14
|
+
return value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
|
|
15
|
+
}
|
|
16
|
+
function validateSourceContextRequest(request) {
|
|
17
|
+
if (request === void 0) return;
|
|
18
|
+
if (request === null || typeof request !== "object" || !("phase" in request) || request.phase !== "phase1" && request.phase !== "phase2" || request.phase === "phase2" && !("parsed" in request)) throw new TypeError(`Expected getAnnotations() to receive no request or a SourceContextRequest ({ phase: "phase1" } or { phase: "phase2", parsed }), but got: ${getTypeName(request)}.`);
|
|
19
|
+
}
|
|
20
|
+
function replaceDefaultDescription(fragments, description) {
|
|
21
|
+
if (description == null) return fragments;
|
|
22
|
+
return fragments.map((fragment) => {
|
|
23
|
+
if (fragment.type === "entry") return {
|
|
24
|
+
...fragment,
|
|
25
|
+
default: description
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
...fragment,
|
|
29
|
+
entries: fragment.entries.map((entry) => ({
|
|
30
|
+
...entry,
|
|
31
|
+
default: description
|
|
32
|
+
}))
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function validateFallbackValue(mode, innerParser, value) {
|
|
37
|
+
if (innerParser == null || typeof innerParser.validateValue !== "function") return wrapForMode(mode, {
|
|
38
|
+
success: true,
|
|
39
|
+
value
|
|
40
|
+
});
|
|
41
|
+
return wrapForMode(mode, innerParser.validateValue(value));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Creates a derived-default context.
|
|
45
|
+
*
|
|
46
|
+
* @param resolvers Resolver map keyed by derived value name.
|
|
47
|
+
* @returns A derived-default context bundle.
|
|
48
|
+
* @throws {TypeError} If `resolvers` is not an object or contains a
|
|
49
|
+
* non-function resolver.
|
|
50
|
+
* @since 1.2.0
|
|
51
|
+
*/
|
|
52
|
+
function createDerivedDefaults(resolvers) {
|
|
53
|
+
if (resolvers == null || typeof resolvers !== "object") throw new TypeError(`Expected resolvers to be an object, but got: ${getTypeName(resolvers)}.`);
|
|
54
|
+
for (const key of Reflect.ownKeys(resolvers)) {
|
|
55
|
+
const resolver = resolvers[key];
|
|
56
|
+
if (typeof resolver !== "function") throw new TypeError(`Expected resolver ${String(key)} to be a function, but got: ${getTypeName(resolver)}.`);
|
|
57
|
+
}
|
|
58
|
+
const contextId = Symbol(`@optique/derived-defaults:${Math.random()}`);
|
|
59
|
+
const context = {
|
|
60
|
+
id: contextId,
|
|
61
|
+
phase: "two-pass",
|
|
62
|
+
getInternalAnnotations(request, annotations) {
|
|
63
|
+
if (request.phase === "phase1") return { [contextId]: phase1DerivedDefaultAnnotationMarker };
|
|
64
|
+
return contextId in annotations ? void 0 : { [contextId]: void 0 };
|
|
65
|
+
},
|
|
66
|
+
getAnnotations(request) {
|
|
67
|
+
validateSourceContextRequest(request);
|
|
68
|
+
if (request?.phase !== "phase2") return {};
|
|
69
|
+
const values = {};
|
|
70
|
+
const pending = [];
|
|
71
|
+
for (const key of Reflect.ownKeys(resolvers)) {
|
|
72
|
+
const resolver = resolvers[key];
|
|
73
|
+
if (request.parsed == null && resolver.length > 0) continue;
|
|
74
|
+
const resolved = resolver(request.parsed ?? void 0);
|
|
75
|
+
if (isPromiseLike(resolved)) pending.push(Promise.resolve(resolved).then((value) => {
|
|
76
|
+
if (value !== void 0) values[key] = value;
|
|
77
|
+
}));
|
|
78
|
+
else if (resolved !== void 0) values[key] = resolved;
|
|
79
|
+
}
|
|
80
|
+
const buildAnnotations = () => Reflect.ownKeys(values).length === 0 ? {} : { [contextId]: { values } };
|
|
81
|
+
return pending.length === 0 ? buildAnnotations() : Promise.all(pending).then(buildAnnotations);
|
|
82
|
+
},
|
|
83
|
+
[Symbol.dispose]() {}
|
|
84
|
+
};
|
|
85
|
+
return { context };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Binds a parser to a derived default value.
|
|
89
|
+
*
|
|
90
|
+
* @param parser Parser that reads the CLI value.
|
|
91
|
+
* @param options Derived default binding options.
|
|
92
|
+
* @returns A parser with derived fallback behavior.
|
|
93
|
+
* @throws {TypeError} If `options.key` is not a property key.
|
|
94
|
+
* @since 1.2.0
|
|
95
|
+
*/
|
|
96
|
+
function bindDerivedDefault(parser, options) {
|
|
97
|
+
const keyType = typeof options.key;
|
|
98
|
+
if (keyType !== "string" && keyType !== "number" && keyType !== "symbol") throw new TypeError(`Expected key to be a property key, but got: ${getTypeName(options.key)}.`);
|
|
99
|
+
const bindStateKey = Symbol("@optique/derived-defaults/bindState");
|
|
100
|
+
function isBindState(value) {
|
|
101
|
+
return value != null && typeof value === "object" && bindStateKey in value;
|
|
102
|
+
}
|
|
103
|
+
function shouldDeferCompletion(state, exec) {
|
|
104
|
+
const annotations = getAnnotations(state);
|
|
105
|
+
if (annotations?.[options.context.id] === phase1DerivedDefaultAnnotationMarker) return true;
|
|
106
|
+
return parser.shouldDeferCompletion?.(getInnerState(state), exec) === true;
|
|
107
|
+
}
|
|
108
|
+
function getInnerState(state) {
|
|
109
|
+
if (!isBindState(state)) return state;
|
|
110
|
+
if (state.cliState !== void 0) return state.cliState;
|
|
111
|
+
const initialState = parser.initialState;
|
|
112
|
+
if (initialState != null && typeof initialState !== "object") return initialState;
|
|
113
|
+
const annotated = inheritAnnotations(state, initialState);
|
|
114
|
+
if (annotated === initialState && initialState != null && typeof initialState === "object") {
|
|
115
|
+
const annotations = getAnnotations(state);
|
|
116
|
+
return annotations == null ? initialState : withAnnotationView(initialState, annotations);
|
|
117
|
+
}
|
|
118
|
+
return annotated;
|
|
119
|
+
}
|
|
120
|
+
function getDerivedValue(state) {
|
|
121
|
+
const annotations = getAnnotations(state);
|
|
122
|
+
const annotationValue = annotations?.[options.context.id];
|
|
123
|
+
if (annotationValue == null || typeof annotationValue !== "object" || !("values" in annotationValue)) return void 0;
|
|
124
|
+
return annotationValue.values[options.key];
|
|
125
|
+
}
|
|
126
|
+
function hasDerivedFallback(state) {
|
|
127
|
+
if (getDerivedValue(state) !== void 0) return true;
|
|
128
|
+
return options.default !== void 0;
|
|
129
|
+
}
|
|
130
|
+
function getDerivedOrDefault(state, mode, exec, innerParser) {
|
|
131
|
+
const annotations = getAnnotations(state);
|
|
132
|
+
const contextId = options.context.id;
|
|
133
|
+
const contextAbsent = annotations != null && !(contextId in annotations);
|
|
134
|
+
const derivedValue = getDerivedValue(state);
|
|
135
|
+
if (derivedValue !== void 0) return validateFallbackValue(mode, innerParser, derivedValue);
|
|
136
|
+
if (options.default !== void 0) return validateFallbackValue(mode, innerParser, options.default);
|
|
137
|
+
if (innerParser?.canSkip?.(getInnerState(state), exec) === true) return mapModeValue(mode, wrapForMode(mode, innerParser.complete(getInnerState(state), exec)), (result) => {
|
|
138
|
+
if (result.success) return result;
|
|
139
|
+
if (contextAbsent) return {
|
|
140
|
+
success: false,
|
|
141
|
+
error: message`Derived default value could not be read: the derived default context was not passed to run()'s contexts option.`
|
|
142
|
+
};
|
|
143
|
+
return result;
|
|
144
|
+
});
|
|
145
|
+
if (contextAbsent) return wrapForMode(mode, {
|
|
146
|
+
success: false,
|
|
147
|
+
error: message`Derived default value could not be read: the derived default context was not passed to run()'s contexts option.`
|
|
148
|
+
});
|
|
149
|
+
return wrapForMode(mode, {
|
|
150
|
+
success: false,
|
|
151
|
+
error: message`Missing required derived default value.`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function getDerivedSourceValue(state, innerState, mode, extractInnerSourceValue, innerParser) {
|
|
155
|
+
const derivedValue = getDerivedValue(state);
|
|
156
|
+
const validateFallback = (parsed) => {
|
|
157
|
+
if (!parsed.success) return parsed;
|
|
158
|
+
if (innerParser == null || typeof innerParser.validateValue !== "function") return parsed;
|
|
159
|
+
return wrapForMode(mode, innerParser.validateValue(parsed.value));
|
|
160
|
+
};
|
|
161
|
+
if (derivedValue !== void 0) return wrapForMode(mode, validateFallback({
|
|
162
|
+
success: true,
|
|
163
|
+
value: derivedValue
|
|
164
|
+
}));
|
|
165
|
+
if (options.default !== void 0) return wrapForMode(mode, validateFallback({
|
|
166
|
+
success: true,
|
|
167
|
+
value: options.default
|
|
168
|
+
}));
|
|
169
|
+
return wrapForMode(mode, extractInnerSourceValue(innerState));
|
|
170
|
+
}
|
|
171
|
+
const boundParser = {
|
|
172
|
+
mode: parser.mode,
|
|
173
|
+
$valueType: parser.$valueType,
|
|
174
|
+
$stateType: parser.$stateType,
|
|
175
|
+
priority: parser.priority,
|
|
176
|
+
usage: options.default !== void 0 ? [{
|
|
177
|
+
type: "optional",
|
|
178
|
+
terms: parser.usage
|
|
179
|
+
}] : parser.usage,
|
|
180
|
+
leadingNames: parser.leadingNames,
|
|
181
|
+
acceptingAnyToken: parser.acceptingAnyToken,
|
|
182
|
+
initialState: parser.initialState,
|
|
183
|
+
canSkip(state, exec) {
|
|
184
|
+
if (isBindState(state)) {
|
|
185
|
+
if (state.hasCliValue) return parser.canSkip?.(state.cliState, exec) === true;
|
|
186
|
+
if (hasDerivedFallback(state)) return true;
|
|
187
|
+
return parser.canSkip?.(getInnerState(state), exec) === true;
|
|
188
|
+
}
|
|
189
|
+
if (hasDerivedFallback(state)) return true;
|
|
190
|
+
return parser.canSkip?.(state, exec) === true;
|
|
191
|
+
},
|
|
192
|
+
getSuggestRuntimeNodes(state, path) {
|
|
193
|
+
const innerState = getInnerState(state);
|
|
194
|
+
return delegateSuggestNodes(parser, boundParser, state, path, innerState);
|
|
195
|
+
},
|
|
196
|
+
parse(context) {
|
|
197
|
+
const annotations = getAnnotations(context.state);
|
|
198
|
+
const innerState = isBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
|
|
199
|
+
const innerContext = innerState !== context.state ? {
|
|
200
|
+
...context,
|
|
201
|
+
state: innerState
|
|
202
|
+
} : context;
|
|
203
|
+
const processResult = (result) => {
|
|
204
|
+
if (result.success) {
|
|
205
|
+
const cliConsumed = result.consumed.length > 0;
|
|
206
|
+
const nextState$1 = injectAnnotations({
|
|
207
|
+
[bindStateKey]: true,
|
|
208
|
+
hasCliValue: cliConsumed,
|
|
209
|
+
cliState: result.next.state
|
|
210
|
+
}, annotations);
|
|
211
|
+
return {
|
|
212
|
+
success: true,
|
|
213
|
+
...result.provisional ? { provisional: true } : {},
|
|
214
|
+
next: {
|
|
215
|
+
...result.next,
|
|
216
|
+
state: nextState$1
|
|
217
|
+
},
|
|
218
|
+
consumed: result.consumed
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (result.consumed > 0) return result;
|
|
222
|
+
const nextState = injectAnnotations({
|
|
223
|
+
[bindStateKey]: true,
|
|
224
|
+
hasCliValue: false
|
|
225
|
+
}, annotations);
|
|
226
|
+
return {
|
|
227
|
+
success: true,
|
|
228
|
+
next: {
|
|
229
|
+
...innerContext,
|
|
230
|
+
state: nextState
|
|
231
|
+
},
|
|
232
|
+
consumed: []
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
return mapModeValue(parser.mode, wrapForMode(parser.mode, parser.parse(innerContext)), processResult);
|
|
236
|
+
},
|
|
237
|
+
complete(state, exec) {
|
|
238
|
+
if (isBindState(state) && state.hasCliValue) return wrapForMode(parser.mode, parser.complete(state.cliState, exec));
|
|
239
|
+
return getDerivedOrDefault(state, parser.mode, exec, parser);
|
|
240
|
+
},
|
|
241
|
+
suggest(context, prefix) {
|
|
242
|
+
const innerState = getInnerState(context.state);
|
|
243
|
+
const innerContext = innerState !== context.state ? {
|
|
244
|
+
...context,
|
|
245
|
+
state: innerState
|
|
246
|
+
} : context;
|
|
247
|
+
return parser.suggest(innerContext, prefix);
|
|
248
|
+
},
|
|
249
|
+
shouldDeferCompletion,
|
|
250
|
+
getDocFragments(state, upperDefaultValue) {
|
|
251
|
+
const defaultValue = upperDefaultValue ?? options.default;
|
|
252
|
+
const fragments = parser.getDocFragments(state, defaultValue);
|
|
253
|
+
if (upperDefaultValue !== void 0 || options.defaultDescription == null) return fragments;
|
|
254
|
+
return {
|
|
255
|
+
...fragments,
|
|
256
|
+
fragments: replaceDefaultDescription(fragments.fragments, options.defaultDescription)
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
Object.defineProperty(boundParser, extractPhase2SeedKey, {
|
|
261
|
+
value(state, exec) {
|
|
262
|
+
const annotations = getAnnotations(state);
|
|
263
|
+
if (annotations?.[options.context.id] !== phase1DerivedDefaultAnnotationMarker) {
|
|
264
|
+
const extractInnerPhase2Seed = parser[extractPhase2SeedKey];
|
|
265
|
+
return extractInnerPhase2Seed == null ? wrapForMode(parser.mode, null) : wrapForMode(parser.mode, extractInnerPhase2Seed(getInnerState(state), exec));
|
|
266
|
+
}
|
|
267
|
+
return wrapForMode(parser.mode, {
|
|
268
|
+
value: void 0,
|
|
269
|
+
deferred: true
|
|
270
|
+
});
|
|
271
|
+
},
|
|
272
|
+
configurable: true
|
|
273
|
+
});
|
|
274
|
+
defineTraits(boundParser, {
|
|
275
|
+
inheritsAnnotations: true,
|
|
276
|
+
completesFromSource: true
|
|
277
|
+
});
|
|
278
|
+
if ("placeholder" in parser) Object.defineProperty(boundParser, "placeholder", {
|
|
279
|
+
get() {
|
|
280
|
+
return parser.placeholder;
|
|
281
|
+
},
|
|
282
|
+
configurable: true,
|
|
283
|
+
enumerable: false
|
|
284
|
+
});
|
|
285
|
+
if (typeof parser.normalizeValue === "function") Object.defineProperty(boundParser, "normalizeValue", {
|
|
286
|
+
value: parser.normalizeValue.bind(parser),
|
|
287
|
+
configurable: true,
|
|
288
|
+
enumerable: false
|
|
289
|
+
});
|
|
290
|
+
if (typeof parser.validateValue === "function") Object.defineProperty(boundParser, "validateValue", {
|
|
291
|
+
value: parser.validateValue.bind(parser),
|
|
292
|
+
configurable: true,
|
|
293
|
+
enumerable: false
|
|
294
|
+
});
|
|
295
|
+
const dependencyMetadata = mapSourceMetadata(parser, (sourceMetadata) => ({
|
|
296
|
+
...sourceMetadata,
|
|
297
|
+
getMissingSourceValue: sourceMetadata.preservesSourceValue !== false && options.default !== void 0 ? () => {
|
|
298
|
+
if (typeof parser.validateValue === "function") return wrapForMode(parser.mode, parser.validateValue(options.default));
|
|
299
|
+
return wrapForMode(parser.mode, {
|
|
300
|
+
success: true,
|
|
301
|
+
value: options.default
|
|
302
|
+
});
|
|
303
|
+
} : void 0,
|
|
304
|
+
extractSourceValue: (state) => {
|
|
305
|
+
if (!isBindState(state)) {
|
|
306
|
+
if (sourceMetadata.preservesSourceValue) return getDerivedSourceValue(state, state, parser.mode, sourceMetadata.extractSourceValue, parser);
|
|
307
|
+
return wrapForMode(parser.mode, sourceMetadata.extractSourceValue(state));
|
|
308
|
+
}
|
|
309
|
+
if (state.hasCliValue) return wrapForMode(parser.mode, sourceMetadata.extractSourceValue(state.cliState));
|
|
310
|
+
const fallbackState = state.cliState ?? state;
|
|
311
|
+
if (!sourceMetadata.preservesSourceValue) return wrapForMode(parser.mode, sourceMetadata.extractSourceValue(fallbackState));
|
|
312
|
+
return getDerivedSourceValue(state, fallbackState, parser.mode, sourceMetadata.extractSourceValue, parser);
|
|
313
|
+
}
|
|
314
|
+
}));
|
|
315
|
+
if (dependencyMetadata != null) Object.defineProperty(boundParser, "dependencyMetadata", {
|
|
316
|
+
value: dependencyMetadata,
|
|
317
|
+
configurable: true,
|
|
318
|
+
enumerable: false
|
|
319
|
+
});
|
|
320
|
+
return fluent(boundParser);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
//#endregion
|
|
324
|
+
export { bindDerivedDefault, createDerivedDefaults };
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@optique/derived-defaults",
|
|
3
|
+
"version": "1.2.0-dev.0",
|
|
4
|
+
"description": "Derived default value support for Optique",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"CLI",
|
|
7
|
+
"command-line",
|
|
8
|
+
"commandline",
|
|
9
|
+
"parser",
|
|
10
|
+
"defaults",
|
|
11
|
+
"source-context"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Hong Minhee",
|
|
16
|
+
"email": "hong@minhee.org",
|
|
17
|
+
"url": "https://hongminhee.org/"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://optique.dev/",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/dahlia/optique.git",
|
|
23
|
+
"directory": "packages/derived-defaults/"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/dahlia/optique/issues"
|
|
27
|
+
},
|
|
28
|
+
"funding": [
|
|
29
|
+
"https://github.com/sponsors/dahlia"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20.0.0",
|
|
33
|
+
"bun": ">=1.2.0",
|
|
34
|
+
"deno": ">=2.3.0"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist/",
|
|
38
|
+
"package.json",
|
|
39
|
+
"README.md"
|
|
40
|
+
],
|
|
41
|
+
"type": "module",
|
|
42
|
+
"module": "./dist/index.js",
|
|
43
|
+
"main": "./dist/index.cjs",
|
|
44
|
+
"types": "./dist/index.d.ts",
|
|
45
|
+
"exports": {
|
|
46
|
+
".": {
|
|
47
|
+
"types": {
|
|
48
|
+
"import": "./dist/index.d.ts",
|
|
49
|
+
"require": "./dist/index.d.cts"
|
|
50
|
+
},
|
|
51
|
+
"import": "./dist/index.js",
|
|
52
|
+
"require": "./dist/index.cjs"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"imports": {
|
|
56
|
+
"#src/*.ts": {
|
|
57
|
+
"node": "./dist/*.js",
|
|
58
|
+
"default": "./src/*.ts"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"sideEffects": false,
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@optique/core": "1.2.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/node": "^24.0.0",
|
|
67
|
+
"fast-check": "^4.7.0",
|
|
68
|
+
"tsdown": "^0.13.0",
|
|
69
|
+
"typescript": "^5.8.3",
|
|
70
|
+
"@optique/config": "1.2.0",
|
|
71
|
+
"@optique/env": "1.2.0",
|
|
72
|
+
"@optique/run": "1.2.0"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "tsdown",
|
|
76
|
+
"prepublish": "tsdown",
|
|
77
|
+
"test": "node --test",
|
|
78
|
+
"test:bun": "bun test",
|
|
79
|
+
"test:deno": "deno test",
|
|
80
|
+
"test-all": "tsdown && node --test && bun test && deno test"
|
|
81
|
+
}
|
|
82
|
+
}
|