@optique/discover 1.1.0 → 1.2.0-dev.2167
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/README.md +4 -0
- package/dist/{command-DZA06E08.cjs → command-B0lV6NBO.cjs} +2 -2
- package/dist/{command-y_A3hG0g.js → command-DO5zgkvS.js} +2 -2
- package/dist/{command-2HtR3-TV.d.cts → command-DSHBTa5c.d.cts} +5 -2
- package/dist/{command-BAVhIzfI.d.ts → command-DyiVIMUh.d.ts} +5 -2
- package/dist/command.cjs +1 -1
- package/dist/command.d.cts +1 -1
- package/dist/command.d.ts +1 -1
- package/dist/command.js +1 -1
- package/dist/index.cjs +366 -45
- package/dist/index.d.cts +27 -4
- package/dist/index.d.ts +27 -4
- package/dist/index.js +367 -46
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-
|
|
1
|
+
import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-DSHBTa5c.cjs";
|
|
2
2
|
import { Mode, Parser } from "@optique/core/parser";
|
|
3
3
|
import { Message } from "@optique/core/message";
|
|
4
4
|
import { ProgramMetadata } from "@optique/core/program";
|
|
@@ -64,6 +64,16 @@ interface DiscoverCommandsOptions {
|
|
|
64
64
|
* @default Runtime-aware extension defaults from {@link getDefaultExtensions}
|
|
65
65
|
*/
|
|
66
66
|
readonly extensions?: readonly string[];
|
|
67
|
+
/**
|
|
68
|
+
* File name that maps to the containing command path after extension
|
|
69
|
+
* stripping. For example, `stash/index.ts` maps to `stash`, and root
|
|
70
|
+
* `index.ts` maps to the root command. Pass `false` to treat matching files
|
|
71
|
+
* as ordinary command names.
|
|
72
|
+
*
|
|
73
|
+
* @default `"index"`
|
|
74
|
+
* @since 1.2.0
|
|
75
|
+
*/
|
|
76
|
+
readonly entryFileName?: string | false;
|
|
67
77
|
}
|
|
68
78
|
/**
|
|
69
79
|
* Runtime hint for {@link getDefaultExtensions}.
|
|
@@ -134,6 +144,14 @@ interface RunProgramDiscoveryOptions extends RunProgramBaseOptions {
|
|
|
134
144
|
* File suffixes to include during discovery.
|
|
135
145
|
*/
|
|
136
146
|
readonly extensions?: readonly string[];
|
|
147
|
+
/**
|
|
148
|
+
* File name that maps to the containing command path after extension
|
|
149
|
+
* stripping.
|
|
150
|
+
*
|
|
151
|
+
* @default `"index"`
|
|
152
|
+
* @since 1.2.0
|
|
153
|
+
*/
|
|
154
|
+
readonly entryFileName?: string | false;
|
|
137
155
|
/**
|
|
138
156
|
* Static commands cannot be used together with `dir`.
|
|
139
157
|
*/
|
|
@@ -157,6 +175,10 @@ interface RunProgramStaticOptions extends RunProgramBaseOptions {
|
|
|
157
175
|
* File suffixes are only used with file-system discovery.
|
|
158
176
|
*/
|
|
159
177
|
readonly extensions?: never;
|
|
178
|
+
/**
|
|
179
|
+
* Entry file names are only used with file-system discovery.
|
|
180
|
+
*/
|
|
181
|
+
readonly entryFileName?: never;
|
|
160
182
|
}
|
|
161
183
|
/**
|
|
162
184
|
* Options for {@link runProgram}.
|
|
@@ -178,8 +200,8 @@ declare function getDefaultExtensions(options?: RuntimeExtensionOptions): readon
|
|
|
178
200
|
* @param options Discovery options.
|
|
179
201
|
* @returns Discovered commands sorted by command path.
|
|
180
202
|
* @throws {TypeError} If options are invalid, discovery finds no commands,
|
|
181
|
-
* command paths
|
|
182
|
-
* command created with `defineCommand()`.
|
|
203
|
+
* command paths are duplicated, or a module does not default-export
|
|
204
|
+
* a command created with `defineCommand()`.
|
|
183
205
|
* @since 1.1.0
|
|
184
206
|
*/
|
|
185
207
|
declare function discoverCommands(options: DiscoverCommandsOptions): Promise<readonly DiscoveredCommand[]>;
|
|
@@ -189,7 +211,8 @@ declare function discoverCommands(options: DiscoverCommandsOptions): Promise<rea
|
|
|
189
211
|
* @param commands Commands to compose.
|
|
190
212
|
* @param metadata Optional root documentation metadata.
|
|
191
213
|
* @returns A parser that resolves to an internal command invocation.
|
|
192
|
-
* @throws {TypeError} If no commands are provided or command paths
|
|
214
|
+
* @throws {TypeError} If no commands are provided or command paths are
|
|
215
|
+
* duplicated.
|
|
193
216
|
* @since 1.1.0
|
|
194
217
|
*/
|
|
195
218
|
declare function createProgramParser(commands: readonly Pick<DiscoveredCommand, "path" | "command">[], metadata?: ProgramHelpMetadata): Parser<Mode, ProgramInvocation, unknown>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-
|
|
1
|
+
import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-DyiVIMUh.js";
|
|
2
2
|
import { RunOptions } from "@optique/run";
|
|
3
3
|
import { Mode, Parser } from "@optique/core/parser";
|
|
4
4
|
import { Message } from "@optique/core/message";
|
|
@@ -64,6 +64,16 @@ interface DiscoverCommandsOptions {
|
|
|
64
64
|
* @default Runtime-aware extension defaults from {@link getDefaultExtensions}
|
|
65
65
|
*/
|
|
66
66
|
readonly extensions?: readonly string[];
|
|
67
|
+
/**
|
|
68
|
+
* File name that maps to the containing command path after extension
|
|
69
|
+
* stripping. For example, `stash/index.ts` maps to `stash`, and root
|
|
70
|
+
* `index.ts` maps to the root command. Pass `false` to treat matching files
|
|
71
|
+
* as ordinary command names.
|
|
72
|
+
*
|
|
73
|
+
* @default `"index"`
|
|
74
|
+
* @since 1.2.0
|
|
75
|
+
*/
|
|
76
|
+
readonly entryFileName?: string | false;
|
|
67
77
|
}
|
|
68
78
|
/**
|
|
69
79
|
* Runtime hint for {@link getDefaultExtensions}.
|
|
@@ -134,6 +144,14 @@ interface RunProgramDiscoveryOptions extends RunProgramBaseOptions {
|
|
|
134
144
|
* File suffixes to include during discovery.
|
|
135
145
|
*/
|
|
136
146
|
readonly extensions?: readonly string[];
|
|
147
|
+
/**
|
|
148
|
+
* File name that maps to the containing command path after extension
|
|
149
|
+
* stripping.
|
|
150
|
+
*
|
|
151
|
+
* @default `"index"`
|
|
152
|
+
* @since 1.2.0
|
|
153
|
+
*/
|
|
154
|
+
readonly entryFileName?: string | false;
|
|
137
155
|
/**
|
|
138
156
|
* Static commands cannot be used together with `dir`.
|
|
139
157
|
*/
|
|
@@ -157,6 +175,10 @@ interface RunProgramStaticOptions extends RunProgramBaseOptions {
|
|
|
157
175
|
* File suffixes are only used with file-system discovery.
|
|
158
176
|
*/
|
|
159
177
|
readonly extensions?: never;
|
|
178
|
+
/**
|
|
179
|
+
* Entry file names are only used with file-system discovery.
|
|
180
|
+
*/
|
|
181
|
+
readonly entryFileName?: never;
|
|
160
182
|
}
|
|
161
183
|
/**
|
|
162
184
|
* Options for {@link runProgram}.
|
|
@@ -178,8 +200,8 @@ declare function getDefaultExtensions(options?: RuntimeExtensionOptions): readon
|
|
|
178
200
|
* @param options Discovery options.
|
|
179
201
|
* @returns Discovered commands sorted by command path.
|
|
180
202
|
* @throws {TypeError} If options are invalid, discovery finds no commands,
|
|
181
|
-
* command paths
|
|
182
|
-
* command created with `defineCommand()`.
|
|
203
|
+
* command paths are duplicated, or a module does not default-export
|
|
204
|
+
* a command created with `defineCommand()`.
|
|
183
205
|
* @since 1.1.0
|
|
184
206
|
*/
|
|
185
207
|
declare function discoverCommands(options: DiscoverCommandsOptions): Promise<readonly DiscoveredCommand[]>;
|
|
@@ -189,7 +211,8 @@ declare function discoverCommands(options: DiscoverCommandsOptions): Promise<rea
|
|
|
189
211
|
* @param commands Commands to compose.
|
|
190
212
|
* @param metadata Optional root documentation metadata.
|
|
191
213
|
* @returns A parser that resolves to an internal command invocation.
|
|
192
|
-
* @throws {TypeError} If no commands are provided or command paths
|
|
214
|
+
* @throws {TypeError} If no commands are provided or command paths are
|
|
215
|
+
* duplicated.
|
|
193
216
|
* @since 1.1.0
|
|
194
217
|
*/
|
|
195
218
|
declare function createProgramParser(commands: readonly Pick<DiscoveredCommand, "path" | "command">[], metadata?: ProgramHelpMetadata): Parser<Mode, ProgramInvocation, unknown>;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { defineCommand, isCommand } from "./command-
|
|
2
|
-
import { or } from "@optique/core/constructs";
|
|
1
|
+
import { defineCommand, isCommand } from "./command-DO5zgkvS.js";
|
|
2
|
+
import { longestMatch, or } from "@optique/core/constructs";
|
|
3
|
+
import { dispatchByMode, inheritAnnotations, mapModeValue, wrapForMode } from "@optique/core/extension";
|
|
3
4
|
import { map } from "@optique/core/modifiers";
|
|
4
5
|
import { command } from "@optique/core/primitives";
|
|
6
|
+
import { mergeHidden } from "@optique/core/usage";
|
|
5
7
|
import { runAsync } from "@optique/run";
|
|
6
8
|
import { readdir, realpath, stat } from "node:fs/promises";
|
|
7
9
|
import { relative, resolve, sep } from "node:path";
|
|
@@ -38,37 +40,37 @@ function getDefaultExtensions(options = {}) {
|
|
|
38
40
|
* @param options Discovery options.
|
|
39
41
|
* @returns Discovered commands sorted by command path.
|
|
40
42
|
* @throws {TypeError} If options are invalid, discovery finds no commands,
|
|
41
|
-
* command paths
|
|
42
|
-
* command created with `defineCommand()`.
|
|
43
|
+
* command paths are duplicated, or a module does not default-export
|
|
44
|
+
* a command created with `defineCommand()`.
|
|
43
45
|
* @since 1.1.0
|
|
44
46
|
*/
|
|
45
47
|
async function discoverCommands(options) {
|
|
46
48
|
const dir = pathFromDir(options.dir);
|
|
47
49
|
const extensions = normalizeExtensions(options.extensions ?? getDefaultExtensions());
|
|
50
|
+
const entryFileName = normalizeEntryFileName(options.entryFileName);
|
|
48
51
|
const files = await collectCommandFiles(dir, extensions);
|
|
49
52
|
if (files.length < 1) throw new TypeError(`No command modules found in ${dir}.`);
|
|
50
53
|
const seen = /* @__PURE__ */ new Map();
|
|
51
54
|
const discovered = [];
|
|
52
55
|
for (const filePath of files) {
|
|
53
|
-
const path = commandPathFromFile(dir, filePath, extensions);
|
|
56
|
+
const path = commandPathFromFile(dir, filePath, extensions, entryFileName);
|
|
54
57
|
const key = commandPathKey(path);
|
|
55
58
|
const previous = seen.get(key);
|
|
56
59
|
if (previous != null) {
|
|
57
|
-
const displayPath = path
|
|
60
|
+
const displayPath = displayCommandPath(path);
|
|
58
61
|
throw new TypeError(`Duplicate command path "${displayPath}" from ${previous} and ${filePath}.`);
|
|
59
62
|
}
|
|
60
63
|
seen.set(key, filePath);
|
|
61
64
|
const mod = await import(pathToFileURL(filePath).href);
|
|
62
65
|
const commandDefinition = unwrapCommandExport(mod.default);
|
|
63
66
|
if (commandDefinition == null) throw new TypeError(`Module ${filePath} default export must be created with defineCommand().`);
|
|
64
|
-
if (commandDefinition.path != null && commandPathKey(commandDefinition.path) !== commandPathKey(path)) throw new TypeError(`Module ${filePath} declares command path "${commandDefinition.path
|
|
67
|
+
if (commandDefinition.path != null && commandPathKey(commandDefinition.path) !== commandPathKey(path)) throw new TypeError(`Module ${filePath} declares command path "${displayCommandPath(commandDefinition.path)}" but file path defines "${displayCommandPath(path)}".`);
|
|
65
68
|
discovered.push({
|
|
66
69
|
path,
|
|
67
70
|
filePath,
|
|
68
71
|
command: commandDefinition
|
|
69
72
|
});
|
|
70
73
|
}
|
|
71
|
-
rejectPathConflicts(discovered);
|
|
72
74
|
return sortCommands(discovered);
|
|
73
75
|
}
|
|
74
76
|
/**
|
|
@@ -77,7 +79,8 @@ async function discoverCommands(options) {
|
|
|
77
79
|
* @param commands Commands to compose.
|
|
78
80
|
* @param metadata Optional root documentation metadata.
|
|
79
81
|
* @returns A parser that resolves to an internal command invocation.
|
|
80
|
-
* @throws {TypeError} If no commands are provided or command paths
|
|
82
|
+
* @throws {TypeError} If no commands are provided or command paths are
|
|
83
|
+
* duplicated.
|
|
81
84
|
* @since 1.1.0
|
|
82
85
|
*/
|
|
83
86
|
function createProgramParser(commands, metadata = {}) {
|
|
@@ -85,11 +88,7 @@ function createProgramParser(commands, metadata = {}) {
|
|
|
85
88
|
const sortedCommands = sortCommands(commands);
|
|
86
89
|
rejectDuplicatePaths(sortedCommands.map((entry) => ({
|
|
87
90
|
path: entry.path,
|
|
88
|
-
filePath: entry.path
|
|
89
|
-
})));
|
|
90
|
-
rejectPathConflicts(sortedCommands.map((entry) => ({
|
|
91
|
-
path: entry.path,
|
|
92
|
-
filePath: entry.path.join("/")
|
|
91
|
+
filePath: displayCommandPath(entry.path)
|
|
93
92
|
})));
|
|
94
93
|
const rootNode = buildCommandTree(sortedCommands);
|
|
95
94
|
const parser = buildNodeParser(rootNode);
|
|
@@ -109,7 +108,8 @@ async function runProgram(options) {
|
|
|
109
108
|
if (isStaticRunProgramOptions(options)) commands = staticCommandsToEntries(options.commands);
|
|
110
109
|
else commands = await discoverCommands({
|
|
111
110
|
dir: options.dir,
|
|
112
|
-
extensions: options.extensions
|
|
111
|
+
extensions: options.extensions,
|
|
112
|
+
entryFileName: options.entryFileName
|
|
113
113
|
});
|
|
114
114
|
const parser = createProgramParser(commands, options.metadata);
|
|
115
115
|
const invocation = await runAsync(parser, buildRunOptions(options));
|
|
@@ -140,6 +140,14 @@ function normalizeExtensions(extensions) {
|
|
|
140
140
|
}
|
|
141
141
|
return normalized.toSorted((a, b) => b.length - a.length || a.localeCompare(b));
|
|
142
142
|
}
|
|
143
|
+
function normalizeEntryFileName(entryFileName) {
|
|
144
|
+
if (entryFileName === void 0) return "index";
|
|
145
|
+
if (entryFileName === false) return false;
|
|
146
|
+
if (typeof entryFileName !== "string") throw new TypeError(`Command entry file name must be a non-empty file name: ${entryFileName}`);
|
|
147
|
+
const normalized = entryFileName;
|
|
148
|
+
if (normalized.length < 1 || normalized.includes("/") || normalized.includes("\\")) throw new TypeError(`Command entry file name must be a non-empty file name: ${normalized}`);
|
|
149
|
+
return normalized;
|
|
150
|
+
}
|
|
143
151
|
async function collectCommandFiles(dir, extensions, activeDirs = /* @__PURE__ */ new Set()) {
|
|
144
152
|
const canonicalDir = await realpath(dir);
|
|
145
153
|
if (activeDirs.has(canonicalDir)) return [];
|
|
@@ -171,29 +179,24 @@ async function getCommandFileEntryType(path, entry) {
|
|
|
171
179
|
function isDeclarationFile(fileName) {
|
|
172
180
|
return /\.d\.[cm]?ts$/.test(fileName);
|
|
173
181
|
}
|
|
174
|
-
function commandPathFromFile(rootDir, filePath, extensions) {
|
|
182
|
+
function commandPathFromFile(rootDir, filePath, extensions, entryFileName) {
|
|
175
183
|
const matchedExtension = extensions.find((ext) => filePath.endsWith(ext));
|
|
176
184
|
if (matchedExtension == null) throw new TypeError(`No configured extension matches ${filePath}.`);
|
|
177
185
|
const withoutExtension = filePath.slice(0, -matchedExtension.length);
|
|
178
186
|
const relativePath = relative(rootDir, withoutExtension);
|
|
179
187
|
const path = relativePath.split(sep).filter((segment) => segment.length > 0);
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
return
|
|
188
|
+
if (path.length < 1) throw new TypeError(`Command file ${filePath} does not define a path.`);
|
|
189
|
+
if (entryFileName !== false && path[path.length - 1] === entryFileName) return path.slice(0, -1);
|
|
190
|
+
return path;
|
|
183
191
|
}
|
|
184
192
|
function commandPathKey(path) {
|
|
185
193
|
return path.join("\0");
|
|
186
194
|
}
|
|
195
|
+
function displayCommandPath(path) {
|
|
196
|
+
return path.length < 1 ? "<root>" : path.join(" ");
|
|
197
|
+
}
|
|
187
198
|
function isCommandPath(path) {
|
|
188
|
-
return Array.isArray(path) && path.
|
|
189
|
-
}
|
|
190
|
-
function rejectPathConflicts(commands) {
|
|
191
|
-
const paths = new Map(commands.map((entry) => [commandPathKey(entry.path), entry]));
|
|
192
|
-
for (const entry of commands) for (let i = 1; i < entry.path.length; i++) {
|
|
193
|
-
const parent = entry.path.slice(0, i);
|
|
194
|
-
const parentEntry = paths.get(commandPathKey(parent));
|
|
195
|
-
if (parentEntry != null) throw new TypeError(`Command path "${parent.join(" ")}" conflicts with nested command "${entry.path.join(" ")}".`);
|
|
196
|
-
}
|
|
199
|
+
return Array.isArray(path) && path.every((segment) => typeof segment === "string" && segment.length > 0);
|
|
197
200
|
}
|
|
198
201
|
function rejectDuplicatePaths(commands) {
|
|
199
202
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -201,7 +204,7 @@ function rejectDuplicatePaths(commands) {
|
|
|
201
204
|
const key = commandPathKey(entry.path);
|
|
202
205
|
const previous = seen.get(key);
|
|
203
206
|
if (previous != null) {
|
|
204
|
-
const displayPath = entry.path
|
|
207
|
+
const displayPath = displayCommandPath(entry.path);
|
|
205
208
|
throw new TypeError(`Duplicate command path "${displayPath}" from ${previous} and ${entry.filePath}.`);
|
|
206
209
|
}
|
|
207
210
|
seen.set(key, entry.filePath);
|
|
@@ -219,7 +222,7 @@ function isStaticRunProgramOptions(options) {
|
|
|
219
222
|
function staticCommandsToEntries(commands) {
|
|
220
223
|
return commands.map((command$1) => {
|
|
221
224
|
if (!isCommand(command$1)) throw new TypeError("Static command entries must be created with defineCommand().");
|
|
222
|
-
if (!isCommandPath(command$1.path)) throw new TypeError("Static command entries must declare a
|
|
225
|
+
if (!isCommandPath(command$1.path)) throw new TypeError("Static command entries must declare a path.");
|
|
223
226
|
return {
|
|
224
227
|
path: command$1.path,
|
|
225
228
|
command: command$1
|
|
@@ -250,41 +253,354 @@ function buildCommandTree(commands) {
|
|
|
250
253
|
}
|
|
251
254
|
return root;
|
|
252
255
|
}
|
|
253
|
-
function buildNodeParser(node) {
|
|
256
|
+
function buildNodeParser(node, inheritedHidden) {
|
|
257
|
+
const childParser = buildChildrenParser(node, inheritedHidden);
|
|
258
|
+
if (childParser != null && node.command != null) return createExecutableNodeParser(childParser, node.command);
|
|
259
|
+
if (childParser != null) return childParser;
|
|
260
|
+
if (node.command != null) return createLeafParser(node.command);
|
|
261
|
+
throw new TypeError("Command tree node must contain a command.");
|
|
262
|
+
}
|
|
263
|
+
function createExecutableNodeParser(childParser, commandDefinition) {
|
|
264
|
+
const leafParser = createLeafParser(commandDefinition, true);
|
|
265
|
+
const branchParsers = [childParser, leafParser];
|
|
266
|
+
const parser = longestMatch(childParser, leafParser);
|
|
267
|
+
const phase2SeedHook = findPhase2SeedHook(parser);
|
|
268
|
+
const executableParser = {
|
|
269
|
+
...parser,
|
|
270
|
+
$valueType: [],
|
|
271
|
+
$stateType: [],
|
|
272
|
+
initialState: void 0,
|
|
273
|
+
parse(context) {
|
|
274
|
+
const activeState = normalizeExecutableNodeState(context.state);
|
|
275
|
+
if (activeState?.committed === true && activeState.result.success) {
|
|
276
|
+
const branchParser = branchParsers[activeState.branch];
|
|
277
|
+
const result$1 = branchParser.parse(withExecutableNodeChildContext(context, activeState.branch, inheritAnnotations(context.state, activeState.result.next.state), branchParser));
|
|
278
|
+
return mapModeValue(parser.mode, wrapForMode(parser.mode, result$1), (resolved) => wrapBranchParseResult(context, activeState, resolved));
|
|
279
|
+
}
|
|
280
|
+
const result = parser.parse({
|
|
281
|
+
...context,
|
|
282
|
+
state: toExclusiveState(activeState, context.state)
|
|
283
|
+
});
|
|
284
|
+
return mapModeValue(parser.mode, wrapForMode(parser.mode, result), (resolved) => wrapInitialParseResult(context, resolved));
|
|
285
|
+
},
|
|
286
|
+
complete(state, exec) {
|
|
287
|
+
const activeState = normalizeExecutableNodeState(state);
|
|
288
|
+
if (activeState?.result.success === true) return wrapForMode(parser.mode, branchParsers[activeState.branch].complete(inheritAnnotations(state, activeState.result.next.state), withExecutableNodeChildExecPath(exec, activeState.branch)));
|
|
289
|
+
if (activeState == null) return wrapForMode(parser.mode, completeExecutableNodeLeaf(state, exec, leafParser));
|
|
290
|
+
return wrapForMode(parser.mode, parser.complete(toExclusiveState(activeState, state), exec));
|
|
291
|
+
},
|
|
292
|
+
suggest(context, prefix) {
|
|
293
|
+
const activeState = normalizeExecutableNodeState(context.state);
|
|
294
|
+
if (activeState?.committed === true && activeState.result.success) {
|
|
295
|
+
const branchParser = branchParsers[activeState.branch];
|
|
296
|
+
return branchParser.suggest(withExecutableNodeChildContext(context, activeState.branch, inheritAnnotations(context.state, activeState.result.next.state), branchParser), prefix);
|
|
297
|
+
}
|
|
298
|
+
return parser.suggest({
|
|
299
|
+
...context,
|
|
300
|
+
state: toExclusiveState(activeState, context.state)
|
|
301
|
+
}, prefix);
|
|
302
|
+
},
|
|
303
|
+
getSuggestRuntimeNodes(state, path) {
|
|
304
|
+
const activeState = normalizeExecutableNodeState(state);
|
|
305
|
+
if (activeState == null) {
|
|
306
|
+
const branchPath$1 = [...path, 1];
|
|
307
|
+
const branchState$1 = inheritAnnotations(state, leafParser.initialState);
|
|
308
|
+
return getExecutableNodeBranchSuggestRuntimeNodes(leafParser, branchState$1, branchPath$1);
|
|
309
|
+
}
|
|
310
|
+
if (activeState?.result.success !== true) return parser.getSuggestRuntimeNodes?.(toExclusiveState(activeState, state), path) ?? [];
|
|
311
|
+
const branchParser = branchParsers[activeState.branch];
|
|
312
|
+
const branchPath = [...path, activeState.branch];
|
|
313
|
+
const branchState = inheritAnnotations(state, activeState.result.next.state);
|
|
314
|
+
return getExecutableNodeBranchSuggestRuntimeNodes(branchParser, branchState, branchPath);
|
|
315
|
+
},
|
|
316
|
+
getDocFragments(state, defaultValue) {
|
|
317
|
+
const activeState = state.kind === "available" ? normalizeExecutableNodeState(state.state) : void 0;
|
|
318
|
+
const fragments = parser.getDocFragments(state.kind === "available" ? {
|
|
319
|
+
kind: "available",
|
|
320
|
+
state: toExclusiveState(activeState, state.state)
|
|
321
|
+
} : state, defaultValue);
|
|
322
|
+
if (activeState == null) return withCommandDocMetadata(fragments, commandDefinition.metadata);
|
|
323
|
+
return fragments;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
if (phase2SeedHook != null) Object.defineProperty(executableParser, phase2SeedHook.key, {
|
|
327
|
+
value(state, exec) {
|
|
328
|
+
return extractExecutableNodePhase2Seed(state, exec, leafParser, phase2SeedHook);
|
|
329
|
+
},
|
|
330
|
+
configurable: true,
|
|
331
|
+
enumerable: true
|
|
332
|
+
});
|
|
333
|
+
return executableParser;
|
|
334
|
+
}
|
|
335
|
+
function getExecutableNodeBranchSuggestRuntimeNodes(parser, state, path) {
|
|
336
|
+
return parser.getSuggestRuntimeNodes?.(state, path) ?? (parser.dependencyMetadata?.source != null ? [{
|
|
337
|
+
path,
|
|
338
|
+
parser,
|
|
339
|
+
state
|
|
340
|
+
}] : []);
|
|
341
|
+
}
|
|
342
|
+
const phase2SeedSymbolDescription = "@optique/core/extractPhase2Seed";
|
|
343
|
+
function findPhase2SeedHook(parser) {
|
|
344
|
+
for (const key of Object.getOwnPropertySymbols(parser)) {
|
|
345
|
+
if (key.description !== phase2SeedSymbolDescription) continue;
|
|
346
|
+
const value = Reflect.get(parser, key);
|
|
347
|
+
if (typeof value !== "function") continue;
|
|
348
|
+
return {
|
|
349
|
+
key,
|
|
350
|
+
extract(state, exec) {
|
|
351
|
+
const seed = Reflect.apply(value, parser, [state, exec]);
|
|
352
|
+
return seed;
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return void 0;
|
|
357
|
+
}
|
|
358
|
+
function completeExecutableNodeLeaf(state, exec, leafParser) {
|
|
359
|
+
const result = parseExecutableNodeLeaf(state, exec, leafParser);
|
|
360
|
+
return dispatchByMode(leafParser.mode, () => wrapForMode("sync", completeParsedExecutableNodeLeaf(state, exec, leafParser, wrapForMode("sync", result))), () => Promise.resolve(wrapForMode("async", result)).then((resolved) => wrapForMode("async", completeParsedExecutableNodeLeaf(state, exec, leafParser, resolved))));
|
|
361
|
+
}
|
|
362
|
+
function completeParsedExecutableNodeLeaf(state, exec, leafParser, result) {
|
|
363
|
+
const childExec = withExecutableNodeChildExecPath(exec, 1);
|
|
364
|
+
const nextExec = result.success ? mergeExecutableNodeChildExec(childExec, result.next.exec) : childExec;
|
|
365
|
+
const nextState = result.success ? inheritAnnotations(state, result.next.state) : inheritAnnotations(state, leafParser.initialState);
|
|
366
|
+
return leafParser.complete(nextState, nextExec);
|
|
367
|
+
}
|
|
368
|
+
function extractExecutableNodePhase2Seed(state, exec, leafParser, phase2SeedHook) {
|
|
369
|
+
const activeState = normalizeExecutableNodeState(state);
|
|
370
|
+
if (activeState != null) return phase2SeedHook.extract(toExclusiveState(activeState, state), exec);
|
|
371
|
+
const result = parseExecutableNodeLeaf(state, exec, leafParser);
|
|
372
|
+
return dispatchByMode(leafParser.mode, () => extractParsedExecutableNodePhase2Seed(state, exec, wrapForMode("sync", result), phase2SeedHook), () => Promise.resolve(wrapForMode("async", result)).then((resolved) => extractParsedExecutableNodePhase2Seed(state, exec, resolved, phase2SeedHook)));
|
|
373
|
+
}
|
|
374
|
+
function extractParsedExecutableNodePhase2Seed(state, exec, result, phase2SeedHook) {
|
|
375
|
+
if (!result.success) return phase2SeedHook.extract(toExclusiveState(void 0, state), exec);
|
|
376
|
+
const executableState = inheritAnnotations(state, {
|
|
377
|
+
branch: 1,
|
|
378
|
+
result,
|
|
379
|
+
committed: false
|
|
380
|
+
});
|
|
381
|
+
return phase2SeedHook.extract(toExclusiveState(executableState, state), exec);
|
|
382
|
+
}
|
|
383
|
+
function parseExecutableNodeLeaf(state, exec, leafParser) {
|
|
384
|
+
const childExec = withExecutableNodeChildExecPath(exec, 1);
|
|
385
|
+
const childContext = {
|
|
386
|
+
buffer: [],
|
|
387
|
+
optionsTerminated: false,
|
|
388
|
+
usage: leafParser.usage,
|
|
389
|
+
state: inheritAnnotations(state, leafParser.initialState),
|
|
390
|
+
...childExec != null ? {
|
|
391
|
+
exec: childExec,
|
|
392
|
+
dependencyRegistry: childExec.dependencyRegistry
|
|
393
|
+
} : {}
|
|
394
|
+
};
|
|
395
|
+
return leafParser.parse(childContext);
|
|
396
|
+
}
|
|
397
|
+
function normalizeExecutableNodeState(state) {
|
|
398
|
+
if (state == null || typeof state !== "object" || !("branch" in state) || !("result" in state)) return void 0;
|
|
399
|
+
const branch = state.branch;
|
|
400
|
+
if (branch !== 0 && branch !== 1) return void 0;
|
|
401
|
+
const result = state.result;
|
|
402
|
+
if (result == null || typeof result !== "object" || typeof result.success !== "boolean") return void 0;
|
|
403
|
+
return inheritAnnotations(state, {
|
|
404
|
+
branch,
|
|
405
|
+
result,
|
|
406
|
+
committed: state.committed === true
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
function toExclusiveState(state, sourceState = state) {
|
|
410
|
+
const exclusiveState = state == null ? void 0 : [state.branch, state.result];
|
|
411
|
+
return inheritAnnotations(sourceState, exclusiveState);
|
|
412
|
+
}
|
|
413
|
+
function fromExclusiveState(state) {
|
|
414
|
+
if (!Array.isArray(state) || state.length !== 2 || state[0] !== 0 && state[0] !== 1) return void 0;
|
|
415
|
+
return inheritAnnotations(state, {
|
|
416
|
+
branch: state[0],
|
|
417
|
+
result: state[1],
|
|
418
|
+
committed: isCommittedResult(state[1])
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
function isCommittedResult(result) {
|
|
422
|
+
return result != null && typeof result === "object" && result.success === true && Array.isArray(result.consumed) && result.consumed.length > 0;
|
|
423
|
+
}
|
|
424
|
+
function wrapInitialParseResult(_context, result) {
|
|
425
|
+
if (!result.success) return result;
|
|
426
|
+
return {
|
|
427
|
+
success: true,
|
|
428
|
+
consumed: result.consumed,
|
|
429
|
+
provisional: result.provisional,
|
|
430
|
+
next: {
|
|
431
|
+
...result.next,
|
|
432
|
+
state: fromExclusiveState(result.next.state)
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function wrapBranchParseResult(context, activeState, result) {
|
|
437
|
+
if (!result.success) return result;
|
|
438
|
+
const mergedExec = mergeExecutableNodeChildExec(context.exec, result.next.exec);
|
|
439
|
+
const dependencyRegistry = mergedExec?.dependencyRegistry ?? result.next.dependencyRegistry ?? context.dependencyRegistry;
|
|
440
|
+
const nextState = inheritAnnotations(result.next.state, {
|
|
441
|
+
branch: activeState.branch,
|
|
442
|
+
result,
|
|
443
|
+
committed: activeState.committed || result.consumed.length > 0
|
|
444
|
+
});
|
|
445
|
+
return {
|
|
446
|
+
success: true,
|
|
447
|
+
consumed: result.consumed,
|
|
448
|
+
provisional: result.provisional,
|
|
449
|
+
next: {
|
|
450
|
+
...context,
|
|
451
|
+
buffer: result.next.buffer,
|
|
452
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
453
|
+
state: inheritAnnotations(context.state, nextState),
|
|
454
|
+
...mergedExec != null ? {
|
|
455
|
+
exec: mergedExec,
|
|
456
|
+
trace: mergedExec.trace
|
|
457
|
+
} : {},
|
|
458
|
+
...dependencyRegistry != null ? { dependencyRegistry } : {}
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function withExecutableNodeChildContext(context, branch, state, parser) {
|
|
463
|
+
const exec = withExecutableNodeChildExecPath(context.exec, branch);
|
|
464
|
+
const dependencyRegistry = context.dependencyRegistry ?? exec?.dependencyRegistry;
|
|
465
|
+
return {
|
|
466
|
+
...context,
|
|
467
|
+
state,
|
|
468
|
+
usage: parser.usage,
|
|
469
|
+
...exec != null ? {
|
|
470
|
+
exec: dependencyRegistry === exec.dependencyRegistry ? exec : {
|
|
471
|
+
...exec,
|
|
472
|
+
dependencyRegistry
|
|
473
|
+
},
|
|
474
|
+
dependencyRegistry
|
|
475
|
+
} : {}
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function withExecutableNodeChildExecPath(exec, branch) {
|
|
479
|
+
if (exec == null) return void 0;
|
|
480
|
+
return {
|
|
481
|
+
...exec,
|
|
482
|
+
path: [...exec.path ?? [], branch]
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
function mergeExecutableNodeChildExec(parent, child) {
|
|
486
|
+
if (parent == null) return child;
|
|
487
|
+
if (child == null) return parent;
|
|
488
|
+
return {
|
|
489
|
+
...parent,
|
|
490
|
+
trace: child.trace ?? parent.trace,
|
|
491
|
+
dependencyRuntime: child.dependencyRuntime ?? parent.dependencyRuntime,
|
|
492
|
+
dependencyRegistry: child.dependencyRegistry ?? parent.dependencyRegistry,
|
|
493
|
+
commandPath: child.commandPath ?? parent.commandPath,
|
|
494
|
+
preCompletedByParser: child.preCompletedByParser ?? parent.preCompletedByParser,
|
|
495
|
+
excludedSourceFields: child.excludedSourceFields ?? parent.excludedSourceFields
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function withCommandDocMetadata(fragments, metadata) {
|
|
499
|
+
if (metadata == null) return fragments;
|
|
500
|
+
return {
|
|
501
|
+
...fragments,
|
|
502
|
+
brief: fragments.brief ?? metadata.brief,
|
|
503
|
+
description: fragments.description ?? metadata.description,
|
|
504
|
+
footer: fragments.footer ?? metadata.footer
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function buildChildrenParser(node, inheritedHidden) {
|
|
254
508
|
const parsers = [];
|
|
255
509
|
for (const [name, child] of node.children) {
|
|
256
|
-
const
|
|
257
|
-
const
|
|
258
|
-
parsers.push(
|
|
510
|
+
const childHidden = mergeHidden(inheritedHidden, child.command?.metadata?.hidden);
|
|
511
|
+
const childParser = buildNodeParser(child, childHidden);
|
|
512
|
+
if (child.children.size > 0) parsers.push(createNamespaceCommandParser(name, childParser, child.command, inheritedHidden));
|
|
513
|
+
else parsers.push(command(name, childParser, commandMetadataWithInheritedHidden(child.command?.metadata, inheritedHidden)));
|
|
259
514
|
}
|
|
515
|
+
if (parsers.length < 1) return void 0;
|
|
260
516
|
if (parsers.length === 1) return parsers[0];
|
|
261
517
|
return or(...parsers);
|
|
262
518
|
}
|
|
263
|
-
function
|
|
264
|
-
|
|
519
|
+
function createNamespaceCommandParser(name, childParser, commandDefinition, inheritedHidden) {
|
|
520
|
+
const metadata = commandDefinition?.metadata;
|
|
521
|
+
const parser = command(name, childParser, namespaceCommandMetadata(metadata, inheritedHidden));
|
|
522
|
+
const description = metadata?.brief ?? metadata?.description;
|
|
523
|
+
if (description == null) return parser;
|
|
524
|
+
return {
|
|
525
|
+
...parser,
|
|
526
|
+
getDocFragments(state, defaultValue) {
|
|
527
|
+
const fragments = parser.getDocFragments(state, defaultValue);
|
|
528
|
+
if (state.kind !== "unavailable" && (state.kind !== "available" || !Object.is(state.state, parser.initialState))) return fragments;
|
|
529
|
+
return withNamespaceListDocDescription(fragments, name, description);
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function withNamespaceListDocDescription(fragments, name, description) {
|
|
534
|
+
return {
|
|
535
|
+
...fragments,
|
|
536
|
+
fragments: fragments.fragments.map((fragment) => {
|
|
537
|
+
if (fragment.type !== "entry" || fragment.term.type !== "command" || fragment.term.name !== name) return fragment;
|
|
538
|
+
return {
|
|
539
|
+
...fragment,
|
|
540
|
+
description: fragment.description ?? description
|
|
541
|
+
};
|
|
542
|
+
})
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function namespaceCommandMetadata(metadata, inheritedHidden) {
|
|
546
|
+
const hidden = mergeHidden(inheritedHidden, metadata?.hidden);
|
|
547
|
+
if (metadata?.aliases == null && metadata?.errors == null && hidden == null && metadata?.usageLine == null) return void 0;
|
|
548
|
+
return {
|
|
549
|
+
...metadata?.aliases != null && { aliases: metadata.aliases },
|
|
550
|
+
...metadata?.errors != null && { errors: metadata.errors },
|
|
551
|
+
...hidden != null && { hidden },
|
|
552
|
+
...metadata?.usageLine != null && { usageLine: metadata.usageLine }
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function commandMetadataWithInheritedHidden(metadata, inheritedHidden) {
|
|
556
|
+
const hidden = mergeHidden(inheritedHidden, metadata?.hidden);
|
|
557
|
+
if (metadata == null) return hidden == null ? void 0 : { hidden };
|
|
558
|
+
if (hidden === metadata.hidden) return metadata;
|
|
559
|
+
return {
|
|
560
|
+
...metadata,
|
|
561
|
+
...hidden != null && { hidden }
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
function createLeafParser(commandDefinition, includeMetadata = false) {
|
|
565
|
+
const parser = map(commandDefinition.parser, (value) => ({
|
|
265
566
|
command: commandDefinition,
|
|
266
567
|
value,
|
|
267
568
|
handler: commandDefinition.handler
|
|
268
569
|
}));
|
|
570
|
+
if (!includeMetadata) return parser;
|
|
571
|
+
return {
|
|
572
|
+
...parser,
|
|
573
|
+
getDocFragments(state, defaultValue) {
|
|
574
|
+
const fragments = parser.getDocFragments(state, defaultValue);
|
|
575
|
+
return withCommandDocMetadata(fragments, commandDefinition.metadata);
|
|
576
|
+
}
|
|
577
|
+
};
|
|
269
578
|
}
|
|
270
579
|
function withRootDocs(parser, commands, metadata) {
|
|
271
580
|
const rootState = parser.initialState;
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
fragments: [
|
|
581
|
+
const rootCommand = commands.find((entry) => entry.path.length < 1);
|
|
582
|
+
const listedCommands = commands.filter((entry) => entry.path.length > 0);
|
|
583
|
+
const commandsByPath = new Map(commands.map((entry) => [commandPathKey(entry.path), entry.command]));
|
|
584
|
+
const rootDocs = () => {
|
|
585
|
+
const fragments = [...rootCommand?.command.parser.getDocFragments({ kind: "unavailable" }).fragments ?? []];
|
|
586
|
+
if (listedCommands.length > 0) fragments.push({
|
|
277
587
|
type: "section",
|
|
278
|
-
entries:
|
|
588
|
+
entries: listedCommands.map((entry) => ({
|
|
279
589
|
term: {
|
|
280
590
|
type: "command",
|
|
281
591
|
name: entry.path.join(" "),
|
|
282
|
-
hidden: entry.
|
|
592
|
+
hidden: commandPathHidden(entry.path, commandsByPath)
|
|
283
593
|
},
|
|
284
594
|
description: entry.command.metadata?.brief ?? entry.command.metadata?.description
|
|
285
595
|
}))
|
|
286
|
-
}
|
|
287
|
-
|
|
596
|
+
});
|
|
597
|
+
return {
|
|
598
|
+
brief: metadata.brief,
|
|
599
|
+
description: metadata.description,
|
|
600
|
+
footer: metadata.footer,
|
|
601
|
+
fragments
|
|
602
|
+
};
|
|
603
|
+
};
|
|
288
604
|
return {
|
|
289
605
|
...parser,
|
|
290
606
|
getDocFragments(state, defaultValue) {
|
|
@@ -293,9 +609,14 @@ function withRootDocs(parser, commands, metadata) {
|
|
|
293
609
|
}
|
|
294
610
|
};
|
|
295
611
|
}
|
|
612
|
+
function commandPathHidden(path, commandsByPath) {
|
|
613
|
+
let hidden;
|
|
614
|
+
for (let length = 1; length <= path.length; length++) hidden = mergeHidden(hidden, commandsByPath.get(commandPathKey(path.slice(0, length)))?.metadata?.hidden);
|
|
615
|
+
return hidden;
|
|
616
|
+
}
|
|
296
617
|
function buildRunOptions(options) {
|
|
297
618
|
const metadata = options.metadata;
|
|
298
|
-
const { dir: _dir, commands: _commands, extensions: _extensions, metadata: _metadata, help, version, completion,...rest } = options;
|
|
619
|
+
const { dir: _dir, commands: _commands, extensions: _extensions, entryFileName: _entryFileName, metadata: _metadata, help, version, completion,...rest } = options;
|
|
299
620
|
const runOptions = {
|
|
300
621
|
...rest,
|
|
301
622
|
contexts: unwrapProgramContexts(rest.contexts),
|