@optique/core 1.4.0-dev.2897 → 1.4.0-dev.2928
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/constructs.cjs +18 -5
- package/dist/constructs.js +18 -5
- package/dist/doc-pages.cjs +480 -0
- package/dist/doc-pages.d.cts +98 -0
- package/dist/doc-pages.d.ts +98 -0
- package/dist/doc-pages.js +479 -0
- package/dist/facade.cjs +10 -3
- package/dist/facade.d.cts +11 -0
- package/dist/facade.d.ts +11 -0
- package/dist/facade.js +11 -4
- package/dist/internal/doc-metadata.cjs +121 -0
- package/dist/internal/doc-metadata.d.cts +72 -0
- package/dist/internal/doc-metadata.d.ts +72 -0
- package/dist/internal/doc-metadata.js +118 -0
- package/dist/modifiers.cjs +16 -3
- package/dist/modifiers.js +16 -3
- package/dist/primitives.cjs +33 -4
- package/dist/primitives.js +33 -4
- package/dist/usage-internals.cjs +104 -1
- package/dist/usage-internals.js +104 -2
- package/dist/valueparser.cjs +20 -10
- package/dist/valueparser.js +20 -10
- package/package.json +10 -2
- package/skills/optique/SKILL.md +8 -8
package/dist/constructs.cjs
CHANGED
|
@@ -9,6 +9,7 @@ const require_internal_dependency = require('./internal/dependency.cjs');
|
|
|
9
9
|
const require_dependency_runtime = require('./dependency-runtime.cjs');
|
|
10
10
|
const require_internal_parser = require('./internal/parser.cjs');
|
|
11
11
|
const require_annotation_state = require('./annotation-state.cjs');
|
|
12
|
+
const require_internal_doc_metadata = require('./internal/doc-metadata.cjs');
|
|
12
13
|
const require_empty_input = require('./internal/empty-input.cjs');
|
|
13
14
|
const require_execution_context = require('./execution-context.cjs');
|
|
14
15
|
const require_usage_internals = require('./usage-internals.cjs');
|
|
@@ -2012,7 +2013,10 @@ function or(...args) {
|
|
|
2012
2013
|
defineExclusiveSchedulingNodes(singleResult, parsers);
|
|
2013
2014
|
require_internal_parser.defineInheritedAnnotationParser(singleResult);
|
|
2014
2015
|
require_passthrough.defineReachableChildren(singleResult, parsers, (state) => selectExclusiveChildren(parsers, state, true));
|
|
2015
|
-
return require_modifiers.fluent(require_empty_input.withEmptyInputFacts(require_short_option.scopeParser(singleResult, require_short_option.combinedOptionScope(parsers, parsers.map((_, index) => index))), emptyInputFacts))
|
|
2016
|
+
return require_internal_doc_metadata.captureDocParser(require_modifiers.fluent(require_empty_input.withEmptyInputFacts(require_short_option.scopeParser(singleResult, require_short_option.combinedOptionScope(parsers, parsers.map((_, index) => index))), emptyInputFacts)), {
|
|
2017
|
+
kind: "or",
|
|
2018
|
+
children: parsers
|
|
2019
|
+
});
|
|
2016
2020
|
}
|
|
2017
2021
|
/**
|
|
2018
2022
|
* @since 0.5.0
|
|
@@ -3320,10 +3324,15 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
3320
3324
|
state: require_annotation_state.getWrappedChildParseState(state, getFieldState(field, parser), parser)
|
|
3321
3325
|
}));
|
|
3322
3326
|
});
|
|
3323
|
-
return require_modifiers.fluent(require_empty_input.withEmptyInputFacts(require_short_option.scopeParser(objectParser, require_short_option.combinedOptionScope(parserPairs.map(([, p]) => p), parserPairs.map(([field]) => field)), void 0, true), combinedMode === "sync" ? require_empty_input.objectFacts(parserPairs.map(([, p]) => ({
|
|
3327
|
+
return require_internal_doc_metadata.captureDocParser(require_modifiers.fluent(require_empty_input.withEmptyInputFacts(require_short_option.scopeParser(objectParser, require_short_option.combinedOptionScope(parserPairs.map(([, p]) => p), parserPairs.map(([field]) => field)), void 0, true), combinedMode === "sync" ? require_empty_input.objectFacts(parserPairs.map(([, p]) => ({
|
|
3324
3328
|
facts: require_empty_input.getEmptyInputFacts(p),
|
|
3325
3329
|
matchesTokens: p.leadingNames.size > 0 || p.acceptingAnyToken
|
|
3326
|
-
})), parserPairs.map(([field]) => field)) : {}))
|
|
3330
|
+
})), parserPairs.map(([field]) => field)) : {})), {
|
|
3331
|
+
kind: "object",
|
|
3332
|
+
children: parserPairs.map((pair) => pair[1]),
|
|
3333
|
+
label,
|
|
3334
|
+
hidden: options.hidden
|
|
3335
|
+
});
|
|
3327
3336
|
}
|
|
3328
3337
|
function isParserLike(value) {
|
|
3329
3338
|
return value != null && (typeof value === "object" || typeof value === "function") && "parse" in value && "$valueType" in value && "$stateType" in value;
|
|
@@ -4748,9 +4757,13 @@ function seq(...rawArgs) {
|
|
|
4748
4757
|
enumerable: false
|
|
4749
4758
|
});
|
|
4750
4759
|
require_internal_parser.defineInheritedAnnotationParser(seqParser);
|
|
4751
|
-
return require_modifiers.fluent(require_short_option.scopeParser(seqParser, seqScope.source, (context) => {
|
|
4760
|
+
return require_internal_doc_metadata.captureDocParser(require_modifiers.fluent(require_short_option.scopeParser(seqParser, seqScope.source, (context) => {
|
|
4752
4761
|
seqScope.select(context, reachableSeqScope(context));
|
|
4753
|
-
}, true))
|
|
4762
|
+
}, true)), {
|
|
4763
|
+
kind: "seq",
|
|
4764
|
+
children: parsers,
|
|
4765
|
+
label
|
|
4766
|
+
});
|
|
4754
4767
|
}
|
|
4755
4768
|
function merge(...args) {
|
|
4756
4769
|
const label = typeof args[0] === "string" ? args[0] : void 0;
|
package/dist/constructs.js
CHANGED
|
@@ -9,6 +9,7 @@ import { createDependencySourceState, dependencyId, isDependencySourceState, isP
|
|
|
9
9
|
import { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyRuntimeContext, effectfulSchedulingNodesKey, exclusiveSourceScopeKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, forkRuntimeForCommittedSubtree, getDefaultDependencySnapshot, guaranteedStaticSourceIdsKey, hasInactiveCompletion, isMatchedState, recordSourceScope, releaseUncollectedSourceValues, replaceCachedBarrierFailure, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey } from "./dependency-runtime.js";
|
|
10
10
|
import { defineInheritedAnnotationParser, defineParseLanes, getOwnParseLanes, getParserSuggestRuntimeNodes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
|
|
11
11
|
import { annotationViewTargets, getWrappedChildParseState, getWrappedChildState, reconcileObjectChildState, unwrapAnnotationView } from "./annotation-state.js";
|
|
12
|
+
import { captureDocParser } from "./internal/doc-metadata.js";
|
|
12
13
|
import { acceptsEmptyInput, getEmptyInputFacts, longestMatchFacts, objectFacts, orFacts, tupleFacts, withEmptyInputFacts } from "./internal/empty-input.js";
|
|
13
14
|
import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
|
|
14
15
|
import { collectLeadingCandidates } from "./usage-internals.js";
|
|
@@ -2012,7 +2013,10 @@ function or(...args) {
|
|
|
2012
2013
|
defineExclusiveSchedulingNodes(singleResult, parsers);
|
|
2013
2014
|
defineInheritedAnnotationParser(singleResult);
|
|
2014
2015
|
defineReachableChildren(singleResult, parsers, (state) => selectExclusiveChildren(parsers, state, true));
|
|
2015
|
-
return fluent(withEmptyInputFacts(scopeParser(singleResult, combinedOptionScope(parsers, parsers.map((_, index) => index))), emptyInputFacts))
|
|
2016
|
+
return captureDocParser(fluent(withEmptyInputFacts(scopeParser(singleResult, combinedOptionScope(parsers, parsers.map((_, index) => index))), emptyInputFacts)), {
|
|
2017
|
+
kind: "or",
|
|
2018
|
+
children: parsers
|
|
2019
|
+
});
|
|
2016
2020
|
}
|
|
2017
2021
|
/**
|
|
2018
2022
|
* @since 0.5.0
|
|
@@ -3320,10 +3324,15 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
3320
3324
|
state: getWrappedChildParseState(state, getFieldState(field, parser), parser)
|
|
3321
3325
|
}));
|
|
3322
3326
|
});
|
|
3323
|
-
return fluent(withEmptyInputFacts(scopeParser(objectParser, combinedOptionScope(parserPairs.map(([, p]) => p), parserPairs.map(([field]) => field)), void 0, true), combinedMode === "sync" ? objectFacts(parserPairs.map(([, p]) => ({
|
|
3327
|
+
return captureDocParser(fluent(withEmptyInputFacts(scopeParser(objectParser, combinedOptionScope(parserPairs.map(([, p]) => p), parserPairs.map(([field]) => field)), void 0, true), combinedMode === "sync" ? objectFacts(parserPairs.map(([, p]) => ({
|
|
3324
3328
|
facts: getEmptyInputFacts(p),
|
|
3325
3329
|
matchesTokens: p.leadingNames.size > 0 || p.acceptingAnyToken
|
|
3326
|
-
})), parserPairs.map(([field]) => field)) : {}))
|
|
3330
|
+
})), parserPairs.map(([field]) => field)) : {})), {
|
|
3331
|
+
kind: "object",
|
|
3332
|
+
children: parserPairs.map((pair) => pair[1]),
|
|
3333
|
+
label,
|
|
3334
|
+
hidden: options.hidden
|
|
3335
|
+
});
|
|
3327
3336
|
}
|
|
3328
3337
|
function isParserLike(value) {
|
|
3329
3338
|
return value != null && (typeof value === "object" || typeof value === "function") && "parse" in value && "$valueType" in value && "$stateType" in value;
|
|
@@ -4748,9 +4757,13 @@ function seq(...rawArgs) {
|
|
|
4748
4757
|
enumerable: false
|
|
4749
4758
|
});
|
|
4750
4759
|
defineInheritedAnnotationParser(seqParser);
|
|
4751
|
-
return fluent(scopeParser(seqParser, seqScope.source, (context) => {
|
|
4760
|
+
return captureDocParser(fluent(scopeParser(seqParser, seqScope.source, (context) => {
|
|
4752
4761
|
seqScope.select(context, reachableSeqScope(context));
|
|
4753
|
-
}, true))
|
|
4762
|
+
}, true)), {
|
|
4763
|
+
kind: "seq",
|
|
4764
|
+
children: parsers,
|
|
4765
|
+
label
|
|
4766
|
+
});
|
|
4754
4767
|
}
|
|
4755
4768
|
function merge(...args) {
|
|
4756
4769
|
const label = typeof args[0] === "string" ? args[0] : void 0;
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
const require_validate = require('./validate.cjs');
|
|
2
|
+
const require_usage = require('./usage.cjs');
|
|
3
|
+
const require_message = require('./message.cjs');
|
|
4
|
+
const require_doc = require('./doc.cjs');
|
|
5
|
+
const require_internal_doc_metadata = require('./internal/doc-metadata.cjs');
|
|
6
|
+
|
|
7
|
+
//#region src/doc-pages.ts
|
|
8
|
+
/** Strict collection failed with explicit partial result data. @since 1.4.0 */
|
|
9
|
+
var DocPageCollectionError = class extends TypeError {
|
|
10
|
+
/** Collected pages and every unresolved visible scope. */
|
|
11
|
+
result;
|
|
12
|
+
/**
|
|
13
|
+
* Constructs an error from a partial collection.
|
|
14
|
+
* @param result Collected pages and gaps.
|
|
15
|
+
*/
|
|
16
|
+
constructor(result) {
|
|
17
|
+
super("Documentation collection has unresolved scopes.");
|
|
18
|
+
this.name = "DocPageCollectionError";
|
|
19
|
+
this.result = result;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const emptyProjection = {
|
|
23
|
+
usage: [],
|
|
24
|
+
fragments: []
|
|
25
|
+
};
|
|
26
|
+
function key(path) {
|
|
27
|
+
if (path.length > 0) require_validate.validateCommandNames(path, "Documentation path");
|
|
28
|
+
if (path.some((token) => !token.isWellFormed())) throw new TypeError("Documentation paths must contain well-formed Unicode tokens.");
|
|
29
|
+
return JSON.stringify(path);
|
|
30
|
+
}
|
|
31
|
+
function prefix(parent, child) {
|
|
32
|
+
return parent.length <= child.length && parent.every((token, index) => token === child[index]);
|
|
33
|
+
}
|
|
34
|
+
function hasPath(parser, path) {
|
|
35
|
+
const node = require_internal_doc_metadata.getDocNode(parser);
|
|
36
|
+
if (node == null || path.length === 0 || require_usage.isDocHidden(node.hidden) || ambiguousSequence(node)) return false;
|
|
37
|
+
if (node.kind === "command") return !require_usage.isDocHidden(node.hidden) && node.name === path[0] && (path.length === 1 || hasPath(node.children[0], path.slice(1)));
|
|
38
|
+
return node.children?.some((child) => hasPath(child, path)) ?? false;
|
|
39
|
+
}
|
|
40
|
+
function hasCommands(parser) {
|
|
41
|
+
const node = require_internal_doc_metadata.getDocNode(parser);
|
|
42
|
+
if (node == null || require_usage.isDocHidden(node.hidden)) return false;
|
|
43
|
+
return node.kind === "command" || node.children?.some(hasCommands) === true;
|
|
44
|
+
}
|
|
45
|
+
function ambiguousSequence(node) {
|
|
46
|
+
return node.kind === "seq" && (node.children?.filter(hasCommands).length ?? 0) > 1;
|
|
47
|
+
}
|
|
48
|
+
function hideUsage(usage, pathTerms) {
|
|
49
|
+
return usage.map((term) => {
|
|
50
|
+
if (pathTerms.includes(term)) return term;
|
|
51
|
+
if (term.type === "optional" || term.type === "multiple" || term.type === "sequence") return {
|
|
52
|
+
...term,
|
|
53
|
+
terms: hideUsage(term.terms, pathTerms)
|
|
54
|
+
};
|
|
55
|
+
if (term.type === "exclusive") return {
|
|
56
|
+
...term,
|
|
57
|
+
terms: term.terms.map((terms) => hideUsage(terms, pathTerms))
|
|
58
|
+
};
|
|
59
|
+
if (term.type === "option" || term.type === "argument" || term.type === "command" || term.type === "passthrough") return {
|
|
60
|
+
...term,
|
|
61
|
+
hidden: require_usage.mergeHidden(term.hidden, "usage")
|
|
62
|
+
};
|
|
63
|
+
return term;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function leafTerm(usage) {
|
|
67
|
+
for (const term of usage) if (term.type === "optional" || term.type === "multiple" || term.type === "sequence") {
|
|
68
|
+
const found = leafTerm(term.terms);
|
|
69
|
+
if (found != null) return found;
|
|
70
|
+
} else return term;
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
function staticDefault(parser, value) {
|
|
74
|
+
const node = require_internal_doc_metadata.getDocNode(parser);
|
|
75
|
+
if (node == null) return void 0;
|
|
76
|
+
if (node.kind !== "leaf") {
|
|
77
|
+
const preservesValue = node.kind === "command" || node.kind === "or" || node.kind === "optional" || node.kind === "default";
|
|
78
|
+
return preservesValue && node.children?.length === 1 ? staticDefault(node.children[0], value) : void 0;
|
|
79
|
+
}
|
|
80
|
+
if (node.boolean && typeof value === "boolean") return [{
|
|
81
|
+
type: "text",
|
|
82
|
+
text: String(value)
|
|
83
|
+
}];
|
|
84
|
+
const metadata = node.valueParser == null ? void 0 : require_internal_doc_metadata.getDocValue(node.valueParser);
|
|
85
|
+
if (metadata == null || metadata.canFormatDefault === false || typeof value !== metadata.kind) return void 0;
|
|
86
|
+
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "bigint") return void 0;
|
|
87
|
+
return [{
|
|
88
|
+
type: "text",
|
|
89
|
+
text: metadata.preserveNegativeZero && Object.is(value, -0) ? "-0" : String(value)
|
|
90
|
+
}];
|
|
91
|
+
}
|
|
92
|
+
function fragmentsEntries(fragments) {
|
|
93
|
+
return fragments.flatMap((fragment) => fragment.type === "entry" ? [fragment] : fragment.entries);
|
|
94
|
+
}
|
|
95
|
+
function defaultFragments(fragments, value) {
|
|
96
|
+
return fragments.map((fragment) => fragment.type === "entry" ? {
|
|
97
|
+
...fragment,
|
|
98
|
+
default: value
|
|
99
|
+
} : {
|
|
100
|
+
...fragment,
|
|
101
|
+
entries: fragment.entries.map((entry) => ({
|
|
102
|
+
...entry,
|
|
103
|
+
default: value
|
|
104
|
+
}))
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function sections(fragments) {
|
|
108
|
+
const result = [];
|
|
109
|
+
const named = /* @__PURE__ */ new Map();
|
|
110
|
+
for (const fragment of require_doc.deduplicateDocFragments(fragments)) {
|
|
111
|
+
const title = fragment.type === "entry" ? void 0 : fragment.title;
|
|
112
|
+
let entries = named.get(title);
|
|
113
|
+
if (entries == null) {
|
|
114
|
+
entries = [];
|
|
115
|
+
named.set(title, entries);
|
|
116
|
+
result.push({
|
|
117
|
+
title,
|
|
118
|
+
entries
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
entries.push(...(fragment.type === "entry" ? [fragment] : fragment.entries).map(require_doc.cloneDocEntry));
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
function publicUsage(usage) {
|
|
126
|
+
return usage.map((term) => {
|
|
127
|
+
if (term.type === "command") return {
|
|
128
|
+
type: "command",
|
|
129
|
+
name: term.name,
|
|
130
|
+
...term.hidden != null && { hidden: term.hidden }
|
|
131
|
+
};
|
|
132
|
+
if (term.type === "exclusive") return {
|
|
133
|
+
...term,
|
|
134
|
+
terms: term.terms.map(publicUsage)
|
|
135
|
+
};
|
|
136
|
+
if (term.type === "optional" || term.type === "multiple" || term.type === "sequence") return {
|
|
137
|
+
...term,
|
|
138
|
+
terms: publicUsage(term.terms)
|
|
139
|
+
};
|
|
140
|
+
return require_usage.cloneUsageTerm(term);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function docHidden(entry, entries) {
|
|
144
|
+
for (let length = 1; length <= entry.path.length; length++) if (require_usage.isDocHidden(entries.get(key(entry.path.slice(0, length)))?.hidden)) return true;
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Collects documentation through core-owned constructor metadata, without
|
|
149
|
+
* parsing, defaults, normalization, validators, sources, suggestions, handlers,
|
|
150
|
+
* or custom parser/value-parser documentation/formatting methods.
|
|
151
|
+
*
|
|
152
|
+
* Supported constructors are command/or/object/seq, option/flag/argument,
|
|
153
|
+
* optional/multiple/withDefault; safe value metadata comes from string/integer/
|
|
154
|
+
* choice. Unregistered wrappers are opaque. Custom value parsers retain only
|
|
155
|
+
* enclosing primitive metadata captured at construction. Function defaults are
|
|
156
|
+
* omitted unless withDefault supplies a documentation message. Unsupported
|
|
157
|
+
* static default formatting is reported instead of evaluated.
|
|
158
|
+
*
|
|
159
|
+
* Imports and definition/page construction remain application-owned and are
|
|
160
|
+
* not sandboxed. Supply inert definitions separately from executable startup.
|
|
161
|
+
*
|
|
162
|
+
* @param parser The parser definition to document.
|
|
163
|
+
* @param options Strictness and author-supplied page/subtree replacements.
|
|
164
|
+
* @returns Canonical pages and explicit unresolved scopes for the user parser.
|
|
165
|
+
* @throws {DocPageCollectionError} If strict mode finds unresolved scopes.
|
|
166
|
+
* @throws {TypeError} If supplied paths/aliases are invalid or ambiguous,
|
|
167
|
+
* or supplied canonical paths are duplicated.
|
|
168
|
+
* @since 1.4.0
|
|
169
|
+
*/
|
|
170
|
+
function collectDocPages(parser, options = {}) {
|
|
171
|
+
const discovered = new Map([["[]", { path: [] }]]);
|
|
172
|
+
const reports = /* @__PURE__ */ new Map();
|
|
173
|
+
const report = (path, scope, reason) => {
|
|
174
|
+
const gap = {
|
|
175
|
+
path: [...path],
|
|
176
|
+
scope,
|
|
177
|
+
reason
|
|
178
|
+
};
|
|
179
|
+
reports.set(JSON.stringify(gap), gap);
|
|
180
|
+
};
|
|
181
|
+
function enumerate(input, path) {
|
|
182
|
+
const node = require_internal_doc_metadata.getDocNode(input);
|
|
183
|
+
if (node == null) {
|
|
184
|
+
report(path, "subtree", "unregistered-parser");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (require_usage.isDocHidden(node.hidden)) return;
|
|
188
|
+
if (ambiguousSequence(node)) {
|
|
189
|
+
report(path, "subtree", "ambiguous-command-path");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (node.kind === "command") {
|
|
193
|
+
if (require_usage.isDocHidden(node.hidden)) return;
|
|
194
|
+
const childPath = [...path, node.name];
|
|
195
|
+
const pathKey = key(childPath);
|
|
196
|
+
if (discovered.has(pathKey)) report(childPath, "subtree", "ambiguous-command-path");
|
|
197
|
+
else discovered.set(pathKey, {
|
|
198
|
+
path: childPath,
|
|
199
|
+
node
|
|
200
|
+
});
|
|
201
|
+
enumerate(node.children[0], childPath);
|
|
202
|
+
} else for (const child of node.children ?? []) enumerate(child, path);
|
|
203
|
+
}
|
|
204
|
+
enumerate(parser, []);
|
|
205
|
+
function project(input, target, path, pagePath) {
|
|
206
|
+
const node = require_internal_doc_metadata.getDocNode(input);
|
|
207
|
+
if (node == null) {
|
|
208
|
+
report(path, "subtree", "unregistered-parser");
|
|
209
|
+
if (key(path) !== key(pagePath)) report(pagePath, "page", "unregistered-parser");
|
|
210
|
+
return {
|
|
211
|
+
...emptyProjection,
|
|
212
|
+
unresolvedUsage: true
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (require_usage.isDocHidden(node.hidden)) return {
|
|
216
|
+
...emptyProjection,
|
|
217
|
+
usage: node.usage
|
|
218
|
+
};
|
|
219
|
+
if (ambiguousSequence(node)) {
|
|
220
|
+
report(path, "subtree", "ambiguous-command-path");
|
|
221
|
+
if (key(path) !== key(pagePath)) report(pagePath, "page", "ambiguous-command-path");
|
|
222
|
+
return {
|
|
223
|
+
...emptyProjection,
|
|
224
|
+
unresolvedUsage: true
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (node.kind === "leaf") {
|
|
228
|
+
const term = leafTerm(node.usage);
|
|
229
|
+
if (term == null || "hidden" in term && require_usage.isDocHidden(term.hidden)) return {
|
|
230
|
+
...emptyProjection,
|
|
231
|
+
usage: node.usage
|
|
232
|
+
};
|
|
233
|
+
const metadata = node.valueParser == null ? void 0 : require_internal_doc_metadata.getDocValue(node.valueParser);
|
|
234
|
+
if (node.valueParser != null && metadata == null) report(pagePath, "page", "value-metadata-unavailable");
|
|
235
|
+
const choices = metadata?.choices == null ? void 0 : [{
|
|
236
|
+
type: "text",
|
|
237
|
+
text: metadata.choices.map((value) => metadata.preserveNegativeZero && Object.is(value, -0) ? "-0" : String(value)).join(", ")
|
|
238
|
+
}];
|
|
239
|
+
return {
|
|
240
|
+
usage: node.usage,
|
|
241
|
+
fragments: [{
|
|
242
|
+
type: "entry",
|
|
243
|
+
term,
|
|
244
|
+
description: node.description,
|
|
245
|
+
choices
|
|
246
|
+
}]
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (node.kind === "command") {
|
|
250
|
+
if (require_usage.isDocHidden(node.hidden)) return {
|
|
251
|
+
...emptyProjection,
|
|
252
|
+
usage: node.usage
|
|
253
|
+
};
|
|
254
|
+
if (target.length === 0) return {
|
|
255
|
+
usage: node.usage,
|
|
256
|
+
fragments: [{
|
|
257
|
+
type: "entry",
|
|
258
|
+
term: {
|
|
259
|
+
type: "command",
|
|
260
|
+
name: node.name,
|
|
261
|
+
aliases: node.aliases,
|
|
262
|
+
hidden: node.hidden
|
|
263
|
+
},
|
|
264
|
+
description: node.brief ?? node.description,
|
|
265
|
+
showAliases: node.showAliases
|
|
266
|
+
}]
|
|
267
|
+
};
|
|
268
|
+
if (target[0] !== node.name) return emptyProjection;
|
|
269
|
+
const selected = project(node.children[0], target.slice(1), [...path, node.name], pagePath);
|
|
270
|
+
let usage$1 = selected.usage;
|
|
271
|
+
let unresolvedUsage = selected.unresolvedUsage;
|
|
272
|
+
if (target.length === 1 && node.usageLine != null) if (typeof node.usageLine === "function") {
|
|
273
|
+
report([...path, node.name], "page", "functional-usage");
|
|
274
|
+
unresolvedUsage = true;
|
|
275
|
+
} else {
|
|
276
|
+
usage$1 = node.usageLine;
|
|
277
|
+
unresolvedUsage = false;
|
|
278
|
+
}
|
|
279
|
+
const pathTerm = {
|
|
280
|
+
type: "command",
|
|
281
|
+
name: node.name
|
|
282
|
+
};
|
|
283
|
+
return {
|
|
284
|
+
...selected,
|
|
285
|
+
usage: [pathTerm, ...usage$1],
|
|
286
|
+
pathTerms: [pathTerm, ...selected.pathTerms ?? []],
|
|
287
|
+
unresolvedUsage,
|
|
288
|
+
brief: selected.brief ?? node.brief,
|
|
289
|
+
description: selected.description ?? node.description,
|
|
290
|
+
footer: selected.footer ?? node.footer,
|
|
291
|
+
showUsage: selected.showUsage ?? node.showUsage
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (node.kind === "optional" || node.kind === "multiple" || node.kind === "default") {
|
|
295
|
+
const inner = project(node.children[0], target, path, pagePath);
|
|
296
|
+
if (node.kind === "default") {
|
|
297
|
+
const usage$1 = [{
|
|
298
|
+
type: "optional",
|
|
299
|
+
terms: inner.usage
|
|
300
|
+
}];
|
|
301
|
+
if (node.defaultMessage != null) return {
|
|
302
|
+
...inner,
|
|
303
|
+
usage: usage$1,
|
|
304
|
+
fragments: defaultFragments(inner.fragments, node.defaultMessage)
|
|
305
|
+
};
|
|
306
|
+
if (typeof node.defaultValue !== "function" && fragmentsEntries(inner.fragments).length > 0) {
|
|
307
|
+
const formatted = staticDefault(node.children[0], node.defaultValue);
|
|
308
|
+
if (formatted != null) return {
|
|
309
|
+
...inner,
|
|
310
|
+
usage: usage$1,
|
|
311
|
+
fragments: defaultFragments(inner.fragments, formatted)
|
|
312
|
+
};
|
|
313
|
+
report(pagePath, "page", "static-default-format-unavailable");
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
...inner,
|
|
317
|
+
usage: usage$1
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
...inner,
|
|
322
|
+
usage: [node.kind === "optional" ? {
|
|
323
|
+
type: "optional",
|
|
324
|
+
terms: inner.usage
|
|
325
|
+
} : {
|
|
326
|
+
...node.usage.find((term) => term.type === "multiple"),
|
|
327
|
+
type: "multiple",
|
|
328
|
+
terms: inner.usage,
|
|
329
|
+
min: node.min ?? 0
|
|
330
|
+
}]
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
const children = node.children ?? [];
|
|
334
|
+
if (node.kind === "or") {
|
|
335
|
+
if (target.length > 0) {
|
|
336
|
+
const selected = children.filter((child) => hasPath(child, target));
|
|
337
|
+
if (selected.length !== 1) {
|
|
338
|
+
report(path, "subtree", "ambiguous-command-path");
|
|
339
|
+
if (key(path) !== key(pagePath)) report(pagePath, "page", "ambiguous-command-path");
|
|
340
|
+
return {
|
|
341
|
+
...emptyProjection,
|
|
342
|
+
unresolvedUsage: true
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
return project(selected[0], target, path, pagePath);
|
|
346
|
+
}
|
|
347
|
+
const projected$1 = children.map((child) => project(child, [], path, pagePath));
|
|
348
|
+
return {
|
|
349
|
+
usage: node.usage,
|
|
350
|
+
fragments: projected$1.flatMap((child) => child.fragments),
|
|
351
|
+
unresolvedUsage: projected$1.some((child) => child.unresolvedUsage)
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
const projected = children.map((child) => project(child, hasPath(child, target) ? target : [], path, pagePath));
|
|
355
|
+
const fragments = projected.flatMap((child) => child.fragments);
|
|
356
|
+
const pathTerms = projected.flatMap((child) => child.pathTerms ?? []);
|
|
357
|
+
const usage = node.kind === "seq" ? [{
|
|
358
|
+
type: "sequence",
|
|
359
|
+
terms: projected.flatMap((child) => child.usage)
|
|
360
|
+
}] : projected.flatMap((child) => child.usage);
|
|
361
|
+
return {
|
|
362
|
+
usage: node.hidden === "usage" ? hideUsage(usage, pathTerms) : usage,
|
|
363
|
+
pathTerms,
|
|
364
|
+
fragments: node.label == null ? fragments : [...fragments.filter((fragment) => fragment.type === "section" && fragment.title != null), {
|
|
365
|
+
type: "section",
|
|
366
|
+
title: node.label,
|
|
367
|
+
entries: fragmentsEntries(fragments.filter((fragment) => fragment.type === "entry" || fragment.title == null))
|
|
368
|
+
}],
|
|
369
|
+
unresolvedUsage: projected.some((child) => child.unresolvedUsage),
|
|
370
|
+
brief: projected.find((child) => child.brief != null)?.brief,
|
|
371
|
+
description: projected.find((child) => child.description != null)?.description,
|
|
372
|
+
footer: projected.find((child) => child.footer != null)?.footer,
|
|
373
|
+
showUsage: projected.find((child) => child.showUsage != null)?.showUsage
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
const entries = /* @__PURE__ */ new Map();
|
|
377
|
+
for (const { path, node } of discovered.values()) {
|
|
378
|
+
const page = project(parser, path, [], path);
|
|
379
|
+
entries.set(key(path), {
|
|
380
|
+
path,
|
|
381
|
+
aliases: node?.aliases?.filter((alias) => !node.hiddenAliases?.includes(alias)),
|
|
382
|
+
hiddenAliases: node?.hiddenAliases?.slice(),
|
|
383
|
+
hidden: node?.hidden,
|
|
384
|
+
showAliases: node?.showAliases,
|
|
385
|
+
source: "automatic",
|
|
386
|
+
page: {
|
|
387
|
+
usage: page.unresolvedUsage ? void 0 : publicUsage(page.usage),
|
|
388
|
+
sections: sections(page.fragments),
|
|
389
|
+
brief: page.brief == null ? void 0 : require_message.cloneMessage(page.brief),
|
|
390
|
+
description: page.description == null ? void 0 : require_message.cloneMessage(page.description),
|
|
391
|
+
footer: page.footer == null ? void 0 : require_message.cloneMessage(page.footer),
|
|
392
|
+
showUsage: page.showUsage
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
const supplied = /* @__PURE__ */ new Map();
|
|
397
|
+
for (const entry of options.entries ?? []) {
|
|
398
|
+
const entryKey = key(entry.path);
|
|
399
|
+
if (supplied.has(entryKey)) throw new TypeError(`Duplicate supplied command path: ${entryKey}`);
|
|
400
|
+
if (entry.aliases?.length || entry.hiddenAliases?.length) require_validate.validateCommandNames([...entry.aliases ?? [], ...entry.hiddenAliases ?? []], "Documentation alias");
|
|
401
|
+
if (entry.path.length === 0 && (entry.aliases?.length || entry.hiddenAliases?.length || entry.hidden)) throw new TypeError("The root page cannot have aliases or hidden visibility.");
|
|
402
|
+
if (entry.coverage != null && entry.coverage !== "page" && entry.coverage !== "subtree") throw new TypeError("Supplied coverage must be page or subtree.");
|
|
403
|
+
supplied.set(entryKey, entry);
|
|
404
|
+
entries.set(entryKey, {
|
|
405
|
+
...entries.get(entryKey),
|
|
406
|
+
...entry,
|
|
407
|
+
path: [...entry.path],
|
|
408
|
+
source: "supplied",
|
|
409
|
+
coverage: entry.coverage ?? "page"
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
const visible = [...entries.values()].filter((entry) => !docHidden(entry, entries));
|
|
413
|
+
const unresolved = [...reports.values()].filter((gap) => {
|
|
414
|
+
const enclosing = entries.get(key(gap.path));
|
|
415
|
+
if (enclosing != null && docHidden(enclosing, entries)) return false;
|
|
416
|
+
if (gap.reason === "functional-usage") return supplied.get(key(gap.path))?.page.usage == null;
|
|
417
|
+
if (!supplied.has(key(gap.path))) return true;
|
|
418
|
+
if (gap.scope === "page") return false;
|
|
419
|
+
return ![...supplied.values()].some((entry) => entry.coverage === "subtree" && prefix(entry.path, gap.path));
|
|
420
|
+
});
|
|
421
|
+
const missing = /* @__PURE__ */ new Map();
|
|
422
|
+
const spellings = /* @__PURE__ */ new Map();
|
|
423
|
+
for (const entry of entries.values()) {
|
|
424
|
+
if (entry.path.length === 0) continue;
|
|
425
|
+
for (const spelling of [
|
|
426
|
+
entry.path.at(-1),
|
|
427
|
+
...entry.aliases ?? [],
|
|
428
|
+
...entry.hiddenAliases ?? []
|
|
429
|
+
]) {
|
|
430
|
+
const spellingKey = key([...entry.path.slice(0, -1), spelling]);
|
|
431
|
+
const previous = spellings.get(spellingKey);
|
|
432
|
+
if (previous != null && previous !== key(entry.path)) throw new TypeError(`Ambiguous command spelling: ${spellingKey}`);
|
|
433
|
+
spellings.set(spellingKey, key(entry.path));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
for (const entry of visible) {
|
|
437
|
+
for (let length = 0; length < entry.path.length; length++) {
|
|
438
|
+
const parent = entry.path.slice(0, length);
|
|
439
|
+
if (!entries.has(key(parent))) missing.set(key(parent), parent);
|
|
440
|
+
}
|
|
441
|
+
for (const item of entry.page.sections.flatMap((section) => section.entries)) {
|
|
442
|
+
if (item.term.type !== "command" || require_usage.isDocHidden(item.term.hidden)) continue;
|
|
443
|
+
const child = [...entry.path, item.term.name];
|
|
444
|
+
if (!entries.has(key(child))) missing.set(key(child), child);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
for (const path of missing.values()) unresolved.push({
|
|
448
|
+
path,
|
|
449
|
+
scope: "page",
|
|
450
|
+
reason: "missing-command-page"
|
|
451
|
+
});
|
|
452
|
+
const filtered = visible.map((entry) => {
|
|
453
|
+
const pageSections = entry.page.sections.map((section) => ({
|
|
454
|
+
...section,
|
|
455
|
+
entries: section.entries.filter((item) => {
|
|
456
|
+
if (item.term.type !== "command") return true;
|
|
457
|
+
const target = entries.get(key([...entry.path, item.term.name]));
|
|
458
|
+
return target == null || !docHidden(target, entries);
|
|
459
|
+
})
|
|
460
|
+
}));
|
|
461
|
+
return pageSections.some((section, index) => section.entries.length !== entry.page.sections[index].entries.length) ? {
|
|
462
|
+
...entry,
|
|
463
|
+
page: {
|
|
464
|
+
...entry.page,
|
|
465
|
+
sections: pageSections
|
|
466
|
+
}
|
|
467
|
+
} : entry;
|
|
468
|
+
});
|
|
469
|
+
const result = {
|
|
470
|
+
entries: filtered,
|
|
471
|
+
unresolved,
|
|
472
|
+
scope: "parser"
|
|
473
|
+
};
|
|
474
|
+
if ((options.strict ?? true) && unresolved.length > 0) throw new DocPageCollectionError(result);
|
|
475
|
+
return result;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
//#endregion
|
|
479
|
+
exports.DocPageCollectionError = DocPageCollectionError;
|
|
480
|
+
exports.collectDocPages = collectDocPages;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { HiddenVisibility } from "./usage.cjs";
|
|
2
|
+
import { DocPage } from "./doc.cjs";
|
|
3
|
+
import { Mode, Parser } from "./internal/parser.cjs";
|
|
4
|
+
|
|
5
|
+
//#region src/doc-pages.d.ts
|
|
6
|
+
|
|
7
|
+
/** A command page with an explicit canonical path. @since 1.4.0 */
|
|
8
|
+
interface CommandDocPage {
|
|
9
|
+
/** Canonical tokens, excluding program name and positional values. */
|
|
10
|
+
readonly path: readonly string[];
|
|
11
|
+
/** Constructed documentation, with command tokens already in usage. */
|
|
12
|
+
readonly page: DocPage;
|
|
13
|
+
/** Visible aliases of the final command token. */
|
|
14
|
+
readonly aliases?: readonly string[];
|
|
15
|
+
/** Accepted alternative names reserved for validation, never display. */
|
|
16
|
+
readonly hiddenAliases?: readonly string[];
|
|
17
|
+
/** Documentation visibility; hidden ancestors suppress descendants. */
|
|
18
|
+
readonly hidden?: HiddenVisibility;
|
|
19
|
+
/** Default alias display for this command. */
|
|
20
|
+
readonly showAliases?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** An author-supplied replacement or additional page. @since 1.4.0 */
|
|
23
|
+
interface SuppliedCommandDocPage extends CommandDocPage {
|
|
24
|
+
/**
|
|
25
|
+
* A page assertion covers this page only. A subtree assertion declares that
|
|
26
|
+
* the author supplied every visible descendant too; opaque descendants cannot
|
|
27
|
+
* be verified independently. Defaults to page.
|
|
28
|
+
*/
|
|
29
|
+
readonly coverage?: "page" | "subtree";
|
|
30
|
+
}
|
|
31
|
+
/** A collected page and the origin of its documentation. @since 1.4.0 */
|
|
32
|
+
interface CollectedCommandDocPage extends CommandDocPage {
|
|
33
|
+
/** Whether metadata or the author provided this page. */
|
|
34
|
+
readonly source: "automatic" | "supplied";
|
|
35
|
+
/** Coverage asserted by the author, for supplied pages only. */
|
|
36
|
+
readonly coverage?: "page" | "subtree";
|
|
37
|
+
}
|
|
38
|
+
/** A visible scope whose documentation could not be collected. @since 1.4.0 */
|
|
39
|
+
interface DocPageCollectionGap {
|
|
40
|
+
/** Affected page or visible enclosing opaque scope; never positional values. */
|
|
41
|
+
readonly path: readonly string[];
|
|
42
|
+
/** Whether missing information affects this page or an opaque subtree. */
|
|
43
|
+
readonly scope: "page" | "subtree";
|
|
44
|
+
/** A stable diagnostic for the unsupported operation. */
|
|
45
|
+
readonly reason: "unregistered-parser" | "value-metadata-unavailable" | "static-default-format-unavailable" | "functional-usage" | "ambiguous-command-path" | "missing-command-page";
|
|
46
|
+
}
|
|
47
|
+
/** Result data for the supplied parser, without runner augmentation. @since 1.4.0 */
|
|
48
|
+
interface DocPageCollectionResult {
|
|
49
|
+
/** Root and supported commands in definition order, followed by additions. */
|
|
50
|
+
readonly entries: readonly CollectedCommandDocPage[];
|
|
51
|
+
/** Uncovered visible scopes; an empty list does not inspect author imports. */
|
|
52
|
+
readonly unresolved: readonly DocPageCollectionGap[];
|
|
53
|
+
/** Runner-added help/version/completion are outside this parser scope. */
|
|
54
|
+
readonly scope: "parser";
|
|
55
|
+
}
|
|
56
|
+
/** Options for metadata-only collection. @since 1.4.0 */
|
|
57
|
+
interface DocPageCollectionOptions {
|
|
58
|
+
/** Throw for uncovered scopes. Defaults to true. */
|
|
59
|
+
readonly strict?: boolean;
|
|
60
|
+
/** Replacements win at their canonical paths; duplicate supplied paths fail. */
|
|
61
|
+
readonly entries?: readonly SuppliedCommandDocPage[];
|
|
62
|
+
}
|
|
63
|
+
/** Strict collection failed with explicit partial result data. @since 1.4.0 */
|
|
64
|
+
declare class DocPageCollectionError extends TypeError {
|
|
65
|
+
/** Collected pages and every unresolved visible scope. */
|
|
66
|
+
readonly result: DocPageCollectionResult;
|
|
67
|
+
/**
|
|
68
|
+
* Constructs an error from a partial collection.
|
|
69
|
+
* @param result Collected pages and gaps.
|
|
70
|
+
*/
|
|
71
|
+
constructor(result: DocPageCollectionResult);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Collects documentation through core-owned constructor metadata, without
|
|
75
|
+
* parsing, defaults, normalization, validators, sources, suggestions, handlers,
|
|
76
|
+
* or custom parser/value-parser documentation/formatting methods.
|
|
77
|
+
*
|
|
78
|
+
* Supported constructors are command/or/object/seq, option/flag/argument,
|
|
79
|
+
* optional/multiple/withDefault; safe value metadata comes from string/integer/
|
|
80
|
+
* choice. Unregistered wrappers are opaque. Custom value parsers retain only
|
|
81
|
+
* enclosing primitive metadata captured at construction. Function defaults are
|
|
82
|
+
* omitted unless withDefault supplies a documentation message. Unsupported
|
|
83
|
+
* static default formatting is reported instead of evaluated.
|
|
84
|
+
*
|
|
85
|
+
* Imports and definition/page construction remain application-owned and are
|
|
86
|
+
* not sandboxed. Supply inert definitions separately from executable startup.
|
|
87
|
+
*
|
|
88
|
+
* @param parser The parser definition to document.
|
|
89
|
+
* @param options Strictness and author-supplied page/subtree replacements.
|
|
90
|
+
* @returns Canonical pages and explicit unresolved scopes for the user parser.
|
|
91
|
+
* @throws {DocPageCollectionError} If strict mode finds unresolved scopes.
|
|
92
|
+
* @throws {TypeError} If supplied paths/aliases are invalid or ambiguous,
|
|
93
|
+
* or supplied canonical paths are duplicated.
|
|
94
|
+
* @since 1.4.0
|
|
95
|
+
*/
|
|
96
|
+
declare function collectDocPages<M extends Mode, T, S>(parser: Parser<M, T, S>, options?: DocPageCollectionOptions): DocPageCollectionResult;
|
|
97
|
+
//#endregion
|
|
98
|
+
export { CollectedCommandDocPage, CommandDocPage, DocPageCollectionError, DocPageCollectionGap, DocPageCollectionOptions, DocPageCollectionResult, SuppliedCommandDocPage, collectDocPages };
|