@optique/core 1.0.0-dev.1514 → 1.0.0-dev.1519
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/facade.cjs +39 -0
- package/dist/facade.js +41 -2
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/usage.cjs +171 -6
- package/dist/usage.d.cts +75 -5
- package/dist/usage.d.ts +75 -5
- package/dist/usage.js +169 -7
- package/dist/validate.cjs +70 -0
- package/dist/validate.js +70 -1
- package/package.json +1 -1
package/dist/facade.cjs
CHANGED
|
@@ -587,6 +587,45 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
587
587
|
const versionCommandNames = versionCommandConfig?.names ?? ["version"];
|
|
588
588
|
const completionCommandNames = completionCommandConfig?.names ?? ["completion"];
|
|
589
589
|
const completionOptionNames = completionOptionConfig?.names ?? ["--completion"];
|
|
590
|
+
const activeMetaEntries = [];
|
|
591
|
+
if (options.help && helpOptionConfig) activeMetaEntries.push([
|
|
592
|
+
"option",
|
|
593
|
+
"help option",
|
|
594
|
+
helpOptionNames
|
|
595
|
+
]);
|
|
596
|
+
if (options.help && helpCommandConfig) activeMetaEntries.push([
|
|
597
|
+
"command",
|
|
598
|
+
"help command",
|
|
599
|
+
helpCommandNames
|
|
600
|
+
]);
|
|
601
|
+
if (options.version && versionOptionConfig) activeMetaEntries.push([
|
|
602
|
+
"option",
|
|
603
|
+
"version option",
|
|
604
|
+
versionOptionNames
|
|
605
|
+
]);
|
|
606
|
+
if (options.version && versionCommandConfig) activeMetaEntries.push([
|
|
607
|
+
"command",
|
|
608
|
+
"version command",
|
|
609
|
+
versionCommandNames
|
|
610
|
+
]);
|
|
611
|
+
if (options.completion && completionOptionConfig) activeMetaEntries.push([
|
|
612
|
+
"option",
|
|
613
|
+
"completion option",
|
|
614
|
+
completionOptionNames,
|
|
615
|
+
true
|
|
616
|
+
]);
|
|
617
|
+
if (options.completion && completionCommandConfig) activeMetaEntries.push([
|
|
618
|
+
"command",
|
|
619
|
+
"completion command",
|
|
620
|
+
completionCommandNames
|
|
621
|
+
]);
|
|
622
|
+
require_validate.validateMetaNameCollisions({
|
|
623
|
+
leadingOptions: require_usage.extractLeadingOptionNames(parser.usage, true),
|
|
624
|
+
leadingCommands: require_usage.extractLeadingCommandNames(parser.usage, true),
|
|
625
|
+
allOptions: require_usage.extractOptionNames(parser.usage, true),
|
|
626
|
+
allCommands: require_usage.extractCommandNames(parser.usage, true),
|
|
627
|
+
allLiterals: require_usage.extractLiteralValues(parser.usage)
|
|
628
|
+
}, activeMetaEntries);
|
|
590
629
|
const defaultShells = {
|
|
591
630
|
bash: require_completion.bash,
|
|
592
631
|
fish: require_completion.fish,
|
package/dist/facade.js
CHANGED
|
@@ -2,8 +2,8 @@ import { injectAnnotations } from "./annotations.js";
|
|
|
2
2
|
import { commandLine, formatMessage, lineBreak, message, optionName, text, value } from "./message.js";
|
|
3
3
|
import { bash, fish, nu, pwsh, zsh } from "./completion.js";
|
|
4
4
|
import { dispatchByMode } from "./mode-dispatch.js";
|
|
5
|
-
import { validateCommandNames, validateOptionNames } from "./validate.js";
|
|
6
|
-
import { formatUsage } from "./usage.js";
|
|
5
|
+
import { validateCommandNames, validateMetaNameCollisions, validateOptionNames } from "./validate.js";
|
|
6
|
+
import { extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, formatUsage } from "./usage.js";
|
|
7
7
|
import { formatDocPage } from "./doc.js";
|
|
8
8
|
import { group, longestMatch, object } from "./constructs.js";
|
|
9
9
|
import { multiple, optional, withDefault } from "./modifiers.js";
|
|
@@ -587,6 +587,45 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
587
587
|
const versionCommandNames = versionCommandConfig?.names ?? ["version"];
|
|
588
588
|
const completionCommandNames = completionCommandConfig?.names ?? ["completion"];
|
|
589
589
|
const completionOptionNames = completionOptionConfig?.names ?? ["--completion"];
|
|
590
|
+
const activeMetaEntries = [];
|
|
591
|
+
if (options.help && helpOptionConfig) activeMetaEntries.push([
|
|
592
|
+
"option",
|
|
593
|
+
"help option",
|
|
594
|
+
helpOptionNames
|
|
595
|
+
]);
|
|
596
|
+
if (options.help && helpCommandConfig) activeMetaEntries.push([
|
|
597
|
+
"command",
|
|
598
|
+
"help command",
|
|
599
|
+
helpCommandNames
|
|
600
|
+
]);
|
|
601
|
+
if (options.version && versionOptionConfig) activeMetaEntries.push([
|
|
602
|
+
"option",
|
|
603
|
+
"version option",
|
|
604
|
+
versionOptionNames
|
|
605
|
+
]);
|
|
606
|
+
if (options.version && versionCommandConfig) activeMetaEntries.push([
|
|
607
|
+
"command",
|
|
608
|
+
"version command",
|
|
609
|
+
versionCommandNames
|
|
610
|
+
]);
|
|
611
|
+
if (options.completion && completionOptionConfig) activeMetaEntries.push([
|
|
612
|
+
"option",
|
|
613
|
+
"completion option",
|
|
614
|
+
completionOptionNames,
|
|
615
|
+
true
|
|
616
|
+
]);
|
|
617
|
+
if (options.completion && completionCommandConfig) activeMetaEntries.push([
|
|
618
|
+
"command",
|
|
619
|
+
"completion command",
|
|
620
|
+
completionCommandNames
|
|
621
|
+
]);
|
|
622
|
+
validateMetaNameCollisions({
|
|
623
|
+
leadingOptions: extractLeadingOptionNames(parser.usage, true),
|
|
624
|
+
leadingCommands: extractLeadingCommandNames(parser.usage, true),
|
|
625
|
+
allOptions: extractOptionNames(parser.usage, true),
|
|
626
|
+
allCommands: extractCommandNames(parser.usage, true),
|
|
627
|
+
allLiterals: extractLiteralValues(parser.usage)
|
|
628
|
+
}, activeMetaEntries);
|
|
590
629
|
const defaultShells = {
|
|
591
630
|
bash,
|
|
592
631
|
fish,
|
package/dist/index.cjs
CHANGED
|
@@ -53,6 +53,9 @@ exports.ensureNonEmptyString = require_nonempty.ensureNonEmptyString;
|
|
|
53
53
|
exports.envVar = require_message.envVar;
|
|
54
54
|
exports.extractArgumentMetavars = require_usage.extractArgumentMetavars;
|
|
55
55
|
exports.extractCommandNames = require_usage.extractCommandNames;
|
|
56
|
+
exports.extractLeadingCommandNames = require_usage.extractLeadingCommandNames;
|
|
57
|
+
exports.extractLeadingOptionNames = require_usage.extractLeadingOptionNames;
|
|
58
|
+
exports.extractLiteralValues = require_usage.extractLiteralValues;
|
|
56
59
|
exports.extractOptionNames = require_usage.extractOptionNames;
|
|
57
60
|
exports.fail = require_primitives.fail;
|
|
58
61
|
exports.fish = require_completion.fish;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Annotations, ParseOptions, annotationKey, getAnnotations } from "./annotations.cjs";
|
|
2
2
|
import { NonEmptyString, ensureNonEmptyString, isNonEmptyString } from "./nonempty.cjs";
|
|
3
3
|
import { Message, MessageFormatOptions, MessageTerm, ValueSetOptions, commandLine, envVar, formatMessage, lineBreak, link, message, metavar, optionName, optionNames, text, value, valueSet, values } from "./message.cjs";
|
|
4
|
-
import { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage } from "./usage.cjs";
|
|
4
|
+
import { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage } from "./usage.cjs";
|
|
5
5
|
import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, ShowChoicesOptions, ShowDefaultOptions, cloneDocEntry, deduplicateDocEntries, deduplicateDocFragments, formatDocPage, isDocEntryHidden } from "./doc.cjs";
|
|
6
6
|
import { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, DeferredMap, DomainOptions, EmailOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, LocaleOptions, MacAddressOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, SocketAddressOptions, SocketAddressValue, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, checkBooleanOption, checkEnumOption, choice, cidr, domain, email, float, hostname, integer, ip, ipv4, ipv6, isValueParser, locale, macAddress, port, portRange, socketAddress, string, url, uuid } from "./valueparser.cjs";
|
|
7
7
|
import { ConditionalErrorOptions, ConditionalOptions, DuplicateOptionError, GroupOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
|
|
@@ -12,4 +12,4 @@ import { CombineModes, DocState, InferMode, InferValue, Mode, ModeIterable, Mode
|
|
|
12
12
|
import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.cjs";
|
|
13
13
|
import { ParserValuePlaceholder, SourceContext } from "./context.cjs";
|
|
14
14
|
import { CommandSubConfig, ContextOptionsParam, ExtractRequiredOptions, OptionSubConfig, RunOptions, RunParserError, RunWithOptions, SubstituteParserValue, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync } from "./facade.cjs";
|
|
15
|
-
export { type Annotations, AnyDependencySource, ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, CombineMode, CombineModes, CombinedDependencyMode, CommandErrorOptions, CommandOptions, CommandSubConfig, ConditionalErrorOptions, ConditionalOptions, ContextOptionsParam, DeferredMap, DeferredParseState, DependencyError, DependencyMode, DependencyRegistry, DependencySource, DependencySourceState, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DomainOptions, DuplicateOptionError, EmailOptions, ExtractRequiredOptions, FlagErrorOptions, FlagOptions, FloatOptions, GroupOptions, HiddenVisibility, HostnameOptions, InferMode, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MacAddressOptions, MergeOptions, type Message, type MessageFormatOptions, type MessageTerm, Mode, ModeIterable, ModeValue, MultipleErrorOptions, MultipleOptions, NoMatchContext, NonEmptyString, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OptionState, OptionSubConfig, OrErrorOptions, OrOptions, type ParseOptions, Parser, ParserContext, ParserResult, ParserValuePlaceholder, PassThroughFormat, PassThroughOptions, PendingDependencySourceState, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, ResolvedDependency, Result, RunOptions, RunParserError, RunWithOptions, ShellCompletion, ShowChoicesOptions, ShowDefaultOptions, SocketAddressOptions, SocketAddressValue, SourceContext, StringOptions, SubstituteParserValue, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, type ValueSetOptions, WithDefaultError, WithDefaultOptions, annotationKey, argument, bash, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, command, commandLine, concat, conditional, constant, createDeferredParseState, createDependencySourceState, createPendingDependencySourceState, deduplicateDocEntries, deduplicateDocFragments, defaultValues, deferredParseMarker, dependency, dependencyId, dependencyIds, dependencySourceMarker, dependencySourceStateMarker, deriveFrom, deriveFromAsync, deriveFromSync, derivedValueParserMarker, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fail, fish, flag, float, formatDependencyError, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDefaultValuesFunction, getDependencyIds, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredParseState, isDependencySource, isDependencySourceState, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isPendingDependencySourceState, isSuggestionHidden, isUsageHidden, isValueParser, isWrappedDependencySource, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, nonEmpty, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, parseAsync, parseSync, parseWithDependency, passThrough, pendingDependencySourceStateMarker, port, portRange, pwsh, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, socketAddress, string, suggest, suggestAsync, suggestSync, suggestWithDependency, text, transformsDependencyValue, transformsDependencyValueMarker, tuple, url, uuid, value, valueSet, values, withDefault, wrappedDependencySourceMarker, zsh };
|
|
15
|
+
export { type Annotations, AnyDependencySource, ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, CombineMode, CombineModes, CombinedDependencyMode, CommandErrorOptions, CommandOptions, CommandSubConfig, ConditionalErrorOptions, ConditionalOptions, ContextOptionsParam, DeferredMap, DeferredParseState, DependencyError, DependencyMode, DependencyRegistry, DependencySource, DependencySourceState, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DomainOptions, DuplicateOptionError, EmailOptions, ExtractRequiredOptions, FlagErrorOptions, FlagOptions, FloatOptions, GroupOptions, HiddenVisibility, HostnameOptions, InferMode, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MacAddressOptions, MergeOptions, type Message, type MessageFormatOptions, type MessageTerm, Mode, ModeIterable, ModeValue, MultipleErrorOptions, MultipleOptions, NoMatchContext, NonEmptyString, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OptionState, OptionSubConfig, OrErrorOptions, OrOptions, type ParseOptions, Parser, ParserContext, ParserResult, ParserValuePlaceholder, PassThroughFormat, PassThroughOptions, PendingDependencySourceState, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, ResolvedDependency, Result, RunOptions, RunParserError, RunWithOptions, ShellCompletion, ShowChoicesOptions, ShowDefaultOptions, SocketAddressOptions, SocketAddressValue, SourceContext, StringOptions, SubstituteParserValue, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, type ValueSetOptions, WithDefaultError, WithDefaultOptions, annotationKey, argument, bash, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, command, commandLine, concat, conditional, constant, createDeferredParseState, createDependencySourceState, createPendingDependencySourceState, deduplicateDocEntries, deduplicateDocFragments, defaultValues, deferredParseMarker, dependency, dependencyId, dependencyIds, dependencySourceMarker, dependencySourceStateMarker, deriveFrom, deriveFromAsync, deriveFromSync, derivedValueParserMarker, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, fail, fish, flag, float, formatDependencyError, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDefaultValuesFunction, getDependencyIds, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredParseState, isDependencySource, isDependencySourceState, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isPendingDependencySourceState, isSuggestionHidden, isUsageHidden, isValueParser, isWrappedDependencySource, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, nonEmpty, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, parseAsync, parseSync, parseWithDependency, passThrough, pendingDependencySourceStateMarker, port, portRange, pwsh, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, socketAddress, string, suggest, suggestAsync, suggestSync, suggestWithDependency, text, transformsDependencyValue, transformsDependencyValueMarker, tuple, url, uuid, value, valueSet, values, withDefault, wrappedDependencySourceMarker, zsh };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Annotations, ParseOptions, annotationKey, getAnnotations } from "./annotations.js";
|
|
2
2
|
import { NonEmptyString, ensureNonEmptyString, isNonEmptyString } from "./nonempty.js";
|
|
3
3
|
import { Message, MessageFormatOptions, MessageTerm, ValueSetOptions, commandLine, envVar, formatMessage, lineBreak, link, message, metavar, optionName, optionNames, text, value, valueSet, values } from "./message.js";
|
|
4
|
-
import { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage } from "./usage.js";
|
|
4
|
+
import { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage } from "./usage.js";
|
|
5
5
|
import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, ShowChoicesOptions, ShowDefaultOptions, cloneDocEntry, deduplicateDocEntries, deduplicateDocFragments, formatDocPage, isDocEntryHidden } from "./doc.js";
|
|
6
6
|
import { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, DeferredMap, DomainOptions, EmailOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, LocaleOptions, MacAddressOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, SocketAddressOptions, SocketAddressValue, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, checkBooleanOption, checkEnumOption, choice, cidr, domain, email, float, hostname, integer, ip, ipv4, ipv6, isValueParser, locale, macAddress, port, portRange, socketAddress, string, url, uuid } from "./valueparser.js";
|
|
7
7
|
import { ConditionalErrorOptions, ConditionalOptions, DuplicateOptionError, GroupOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
|
|
@@ -12,4 +12,4 @@ import { CombineModes, DocState, InferMode, InferValue, Mode, ModeIterable, Mode
|
|
|
12
12
|
import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.js";
|
|
13
13
|
import { ParserValuePlaceholder, SourceContext } from "./context.js";
|
|
14
14
|
import { CommandSubConfig, ContextOptionsParam, ExtractRequiredOptions, OptionSubConfig, RunOptions, RunParserError, RunWithOptions, SubstituteParserValue, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync } from "./facade.js";
|
|
15
|
-
export { type Annotations, AnyDependencySource, ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, CombineMode, CombineModes, CombinedDependencyMode, CommandErrorOptions, CommandOptions, CommandSubConfig, ConditionalErrorOptions, ConditionalOptions, ContextOptionsParam, DeferredMap, DeferredParseState, DependencyError, DependencyMode, DependencyRegistry, DependencySource, DependencySourceState, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DomainOptions, DuplicateOptionError, EmailOptions, ExtractRequiredOptions, FlagErrorOptions, FlagOptions, FloatOptions, GroupOptions, HiddenVisibility, HostnameOptions, InferMode, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MacAddressOptions, MergeOptions, type Message, type MessageFormatOptions, type MessageTerm, Mode, ModeIterable, ModeValue, MultipleErrorOptions, MultipleOptions, NoMatchContext, NonEmptyString, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OptionState, OptionSubConfig, OrErrorOptions, OrOptions, type ParseOptions, Parser, ParserContext, ParserResult, ParserValuePlaceholder, PassThroughFormat, PassThroughOptions, PendingDependencySourceState, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, ResolvedDependency, Result, RunOptions, RunParserError, RunWithOptions, ShellCompletion, ShowChoicesOptions, ShowDefaultOptions, SocketAddressOptions, SocketAddressValue, SourceContext, StringOptions, SubstituteParserValue, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, type ValueSetOptions, WithDefaultError, WithDefaultOptions, annotationKey, argument, bash, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, command, commandLine, concat, conditional, constant, createDeferredParseState, createDependencySourceState, createPendingDependencySourceState, deduplicateDocEntries, deduplicateDocFragments, defaultValues, deferredParseMarker, dependency, dependencyId, dependencyIds, dependencySourceMarker, dependencySourceStateMarker, deriveFrom, deriveFromAsync, deriveFromSync, derivedValueParserMarker, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fail, fish, flag, float, formatDependencyError, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDefaultValuesFunction, getDependencyIds, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredParseState, isDependencySource, isDependencySourceState, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isPendingDependencySourceState, isSuggestionHidden, isUsageHidden, isValueParser, isWrappedDependencySource, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, nonEmpty, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, parseAsync, parseSync, parseWithDependency, passThrough, pendingDependencySourceStateMarker, port, portRange, pwsh, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, socketAddress, string, suggest, suggestAsync, suggestSync, suggestWithDependency, text, transformsDependencyValue, transformsDependencyValueMarker, tuple, url, uuid, value, valueSet, values, withDefault, wrappedDependencySourceMarker, zsh };
|
|
15
|
+
export { type Annotations, AnyDependencySource, ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, CombineMode, CombineModes, CombinedDependencyMode, CommandErrorOptions, CommandOptions, CommandSubConfig, ConditionalErrorOptions, ConditionalOptions, ContextOptionsParam, DeferredMap, DeferredParseState, DependencyError, DependencyMode, DependencyRegistry, DependencySource, DependencySourceState, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DomainOptions, DuplicateOptionError, EmailOptions, ExtractRequiredOptions, FlagErrorOptions, FlagOptions, FloatOptions, GroupOptions, HiddenVisibility, HostnameOptions, InferMode, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MacAddressOptions, MergeOptions, type Message, type MessageFormatOptions, type MessageTerm, Mode, ModeIterable, ModeValue, MultipleErrorOptions, MultipleOptions, NoMatchContext, NonEmptyString, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OptionState, OptionSubConfig, OrErrorOptions, OrOptions, type ParseOptions, Parser, ParserContext, ParserResult, ParserValuePlaceholder, PassThroughFormat, PassThroughOptions, PendingDependencySourceState, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, ResolvedDependency, Result, RunOptions, RunParserError, RunWithOptions, ShellCompletion, ShowChoicesOptions, ShowDefaultOptions, SocketAddressOptions, SocketAddressValue, SourceContext, StringOptions, SubstituteParserValue, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, type ValueSetOptions, WithDefaultError, WithDefaultOptions, annotationKey, argument, bash, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, command, commandLine, concat, conditional, constant, createDeferredParseState, createDependencySourceState, createPendingDependencySourceState, deduplicateDocEntries, deduplicateDocFragments, defaultValues, deferredParseMarker, dependency, dependencyId, dependencyIds, dependencySourceMarker, dependencySourceStateMarker, deriveFrom, deriveFromAsync, deriveFromSync, derivedValueParserMarker, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, fail, fish, flag, float, formatDependencyError, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDefaultValuesFunction, getDependencyIds, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredParseState, isDependencySource, isDependencySourceState, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isPendingDependencySourceState, isSuggestionHidden, isUsageHidden, isValueParser, isWrappedDependencySource, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, nonEmpty, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, parseAsync, parseSync, parseWithDependency, passThrough, pendingDependencySourceStateMarker, port, portRange, pwsh, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, socketAddress, string, suggest, suggestAsync, suggestSync, suggestWithDependency, text, transformsDependencyValue, transformsDependencyValueMarker, tuple, url, uuid, value, valueSet, values, withDefault, wrappedDependencySourceMarker, zsh };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { annotationKey, getAnnotations } from "./annotations.js";
|
|
|
2
2
|
import { commandLine, envVar, formatMessage, lineBreak, link, message, metavar, optionName, optionNames, text, value, valueSet, values } from "./message.js";
|
|
3
3
|
import { bash, fish, nu, pwsh, zsh } from "./completion.js";
|
|
4
4
|
import { DependencyRegistry, createDeferredParseState, createDependencySourceState, createPendingDependencySourceState, defaultValues, deferredParseMarker, dependency, dependencyId, dependencyIds, dependencySourceMarker, dependencySourceStateMarker, deriveFrom, deriveFromAsync, deriveFromSync, derivedValueParserMarker, formatDependencyError, getDefaultValuesFunction, getDependencyIds, isDeferredParseState, isDependencySource, isDependencySourceState, isDerivedValueParser, isPendingDependencySourceState, isWrappedDependencySource, parseWithDependency, pendingDependencySourceStateMarker, suggestWithDependency, transformsDependencyValue, transformsDependencyValueMarker, wrappedDependencySourceMarker } from "./dependency.js";
|
|
5
|
-
import { cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage } from "./usage.js";
|
|
5
|
+
import { cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage } from "./usage.js";
|
|
6
6
|
import { cloneDocEntry, deduplicateDocEntries, deduplicateDocFragments, formatDocPage, isDocEntryHidden } from "./doc.js";
|
|
7
7
|
import { DuplicateOptionError, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
|
|
8
8
|
import { WithDefaultError, map, multiple, nonEmpty, optional, withDefault } from "./modifiers.js";
|
|
@@ -12,4 +12,4 @@ import { argument, command, constant, fail, flag, option, passThrough } from "./
|
|
|
12
12
|
import { getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync } from "./parser.js";
|
|
13
13
|
import { RunParserError, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync } from "./facade.js";
|
|
14
14
|
|
|
15
|
-
export { DependencyRegistry, DuplicateOptionError, RunParserError, WithDefaultError, annotationKey, argument, bash, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, command, commandLine, concat, conditional, constant, createDeferredParseState, createDependencySourceState, createPendingDependencySourceState, deduplicateDocEntries, deduplicateDocFragments, defaultValues, deferredParseMarker, dependency, dependencyId, dependencyIds, dependencySourceMarker, dependencySourceStateMarker, deriveFrom, deriveFromAsync, deriveFromSync, derivedValueParserMarker, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fail, fish, flag, float, formatDependencyError, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDefaultValuesFunction, getDependencyIds, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredParseState, isDependencySource, isDependencySourceState, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isPendingDependencySourceState, isSuggestionHidden, isUsageHidden, isValueParser, isWrappedDependencySource, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, nonEmpty, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, parseAsync, parseSync, parseWithDependency, passThrough, pendingDependencySourceStateMarker, port, portRange, pwsh, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, socketAddress, string, suggest, suggestAsync, suggestSync, suggestWithDependency, text, transformsDependencyValue, transformsDependencyValueMarker, tuple, url, uuid, value, valueSet, values, withDefault, wrappedDependencySourceMarker, zsh };
|
|
15
|
+
export { DependencyRegistry, DuplicateOptionError, RunParserError, WithDefaultError, annotationKey, argument, bash, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, command, commandLine, concat, conditional, constant, createDeferredParseState, createDependencySourceState, createPendingDependencySourceState, deduplicateDocEntries, deduplicateDocFragments, defaultValues, deferredParseMarker, dependency, dependencyId, dependencyIds, dependencySourceMarker, dependencySourceStateMarker, deriveFrom, deriveFromAsync, deriveFromSync, derivedValueParserMarker, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, fail, fish, flag, float, formatDependencyError, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDefaultValuesFunction, getDependencyIds, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredParseState, isDependencySource, isDependencySourceState, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isPendingDependencySourceState, isSuggestionHidden, isUsageHidden, isValueParser, isWrappedDependencySource, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, nonEmpty, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, parseAsync, parseSync, parseWithDependency, passThrough, pendingDependencySourceStateMarker, port, portRange, pwsh, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, socketAddress, string, suggest, suggestAsync, suggestSync, suggestWithDependency, text, transformsDependencyValue, transformsDependencyValueMarker, tuple, url, uuid, value, valueSet, values, withDefault, wrappedDependencySourceMarker, zsh };
|
package/dist/usage.cjs
CHANGED
|
@@ -41,6 +41,8 @@ function mergeHidden(a, b) {
|
|
|
41
41
|
* multiple, and exclusive terms.
|
|
42
42
|
*
|
|
43
43
|
* @param usage The usage description to extract option names from.
|
|
44
|
+
* @param includeHidden Whether to include fully hidden options (`hidden: true`)
|
|
45
|
+
* in the result. Defaults to `false`.
|
|
44
46
|
* @returns A set containing all option names found in the usage description.
|
|
45
47
|
*
|
|
46
48
|
* @example
|
|
@@ -53,12 +55,12 @@ function mergeHidden(a, b) {
|
|
|
53
55
|
* // names = Set(["--verbose", "-v", "--quiet", "-q"])
|
|
54
56
|
* ```
|
|
55
57
|
*/
|
|
56
|
-
function extractOptionNames(usage) {
|
|
58
|
+
function extractOptionNames(usage, includeHidden) {
|
|
57
59
|
const names = /* @__PURE__ */ new Set();
|
|
58
60
|
function traverseUsage(terms) {
|
|
59
61
|
if (!terms || !Array.isArray(terms)) return;
|
|
60
62
|
for (const term of terms) if (term.type === "option") {
|
|
61
|
-
if (isSuggestionHidden(term.hidden)) continue;
|
|
63
|
+
if (!includeHidden && isSuggestionHidden(term.hidden)) continue;
|
|
62
64
|
for (const name of term.names) names.add(name);
|
|
63
65
|
} else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
|
|
64
66
|
else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
|
|
@@ -72,8 +74,10 @@ function extractOptionNames(usage) {
|
|
|
72
74
|
* This function recursively traverses the usage structure and collects
|
|
73
75
|
* all command names, similar to {@link extractOptionNames}.
|
|
74
76
|
*
|
|
75
|
-
* @param usage The usage structure to extract command names from
|
|
76
|
-
* @
|
|
77
|
+
* @param usage The usage structure to extract command names from.
|
|
78
|
+
* @param includeHidden Whether to include fully hidden commands
|
|
79
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
80
|
+
* @returns A set of all command names found in the usage structure.
|
|
77
81
|
*
|
|
78
82
|
* @example
|
|
79
83
|
* ```typescript
|
|
@@ -86,12 +90,12 @@ function extractOptionNames(usage) {
|
|
|
86
90
|
* ```
|
|
87
91
|
* @since 0.7.0
|
|
88
92
|
*/
|
|
89
|
-
function extractCommandNames(usage) {
|
|
93
|
+
function extractCommandNames(usage, includeHidden) {
|
|
90
94
|
const names = /* @__PURE__ */ new Set();
|
|
91
95
|
function traverseUsage(terms) {
|
|
92
96
|
if (!terms || !Array.isArray(terms)) return;
|
|
93
97
|
for (const term of terms) if (term.type === "command") {
|
|
94
|
-
if (isSuggestionHidden(term.hidden)) continue;
|
|
98
|
+
if (!includeHidden && isSuggestionHidden(term.hidden)) continue;
|
|
95
99
|
names.add(term.name);
|
|
96
100
|
} else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
|
|
97
101
|
else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
|
|
@@ -100,6 +104,164 @@ function extractCommandNames(usage) {
|
|
|
100
104
|
return names;
|
|
101
105
|
}
|
|
102
106
|
/**
|
|
107
|
+
* Extracts option names that are reachable at the leading token position
|
|
108
|
+
* (before any command or argument gate).
|
|
109
|
+
*
|
|
110
|
+
* Unlike {@link extractOptionNames}, which traverses the entire usage tree,
|
|
111
|
+
* this function stops scanning a terms array after encountering a `command`,
|
|
112
|
+
* `argument`, or `literal` term, because subsequent terms are scoped under
|
|
113
|
+
* that positional token. It still recurses into `optional`, `multiple`,
|
|
114
|
+
* and `exclusive` containers, since they represent alternatives or wrappers
|
|
115
|
+
* at the same position.
|
|
116
|
+
*
|
|
117
|
+
* Known limitation: this function infers token positions from the `usage`
|
|
118
|
+
* tree, which is a display-oriented structure. Combinators like `tuple()`
|
|
119
|
+
* and `object()` sort or flatten usage by priority rather than token order,
|
|
120
|
+
* so the results can be inaccurate—both false positives (e.g., options
|
|
121
|
+
* appearing before commands due to priority sorting) and false negatives
|
|
122
|
+
* (e.g., options after commands that are actually parallel peers).
|
|
123
|
+
* The proper fix is to use `Parser.leadingNames` instead of usage-tree
|
|
124
|
+
* analysis. This also cannot detect `conditional(argument(...))`
|
|
125
|
+
* discriminator values, which never appear in the usage tree.
|
|
126
|
+
* See https://github.com/dahlia/optique/issues/734 and
|
|
127
|
+
* https://github.com/dahlia/optique/issues/735
|
|
128
|
+
*
|
|
129
|
+
* @param usage The usage description to extract leading option names from.
|
|
130
|
+
* @param includeHidden Whether to include fully hidden options
|
|
131
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
132
|
+
* @returns A set of option names reachable at the leading token position.
|
|
133
|
+
* @since 1.0.0
|
|
134
|
+
*/
|
|
135
|
+
function extractLeadingOptionNames(usage, includeHidden) {
|
|
136
|
+
const names = /* @__PURE__ */ new Set();
|
|
137
|
+
function collectLeading(terms) {
|
|
138
|
+
if (!terms || !Array.isArray(terms)) return;
|
|
139
|
+
for (const term of terms) switch (term.type) {
|
|
140
|
+
case "option":
|
|
141
|
+
if (!includeHidden && isSuggestionHidden(term.hidden)) break;
|
|
142
|
+
for (const name of term.names) names.add(name);
|
|
143
|
+
break;
|
|
144
|
+
case "command": return;
|
|
145
|
+
case "argument":
|
|
146
|
+
case "literal": return;
|
|
147
|
+
case "optional":
|
|
148
|
+
collectLeading(term.terms);
|
|
149
|
+
break;
|
|
150
|
+
case "multiple":
|
|
151
|
+
collectLeading(term.terms);
|
|
152
|
+
if (term.min > 0 && branchConsumesToken(term.terms)) return;
|
|
153
|
+
break;
|
|
154
|
+
case "exclusive":
|
|
155
|
+
for (const branch of term.terms) collectLeading(branch);
|
|
156
|
+
if (exclusiveConsumesToken(term.terms)) return;
|
|
157
|
+
break;
|
|
158
|
+
default: break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
collectLeading(usage);
|
|
162
|
+
return names;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Extracts command names that could match as the first positional token.
|
|
166
|
+
*
|
|
167
|
+
* Unlike {@link extractCommandNames}, which traverses the entire usage tree,
|
|
168
|
+
* this function stops scanning a terms array after encountering a `command`,
|
|
169
|
+
* `argument`, or `literal` term, because subsequent terms in that array are
|
|
170
|
+
* scoped under that positional token (reachable only after the leading term
|
|
171
|
+
* is consumed). It still recurses into `optional`, `multiple`, and
|
|
172
|
+
* `exclusive` containers, since they represent alternatives or optional
|
|
173
|
+
* wrappers at the same token position.
|
|
174
|
+
*
|
|
175
|
+
* Known limitation: this function has the same usage-tree ordering and
|
|
176
|
+
* `conditional(argument(...))` caveats as {@link extractLeadingOptionNames}.
|
|
177
|
+
* See https://github.com/dahlia/optique/issues/734 and
|
|
178
|
+
* https://github.com/dahlia/optique/issues/735
|
|
179
|
+
*
|
|
180
|
+
* @param usage The usage description to extract leading command names from.
|
|
181
|
+
* @param includeHidden Whether to include fully hidden commands
|
|
182
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
183
|
+
* @returns A set of command names that could match at the first token position.
|
|
184
|
+
* @since 1.0.0
|
|
185
|
+
*/
|
|
186
|
+
function extractLeadingCommandNames(usage, includeHidden) {
|
|
187
|
+
const names = /* @__PURE__ */ new Set();
|
|
188
|
+
function collectLeading(terms) {
|
|
189
|
+
if (!terms || !Array.isArray(terms)) return;
|
|
190
|
+
for (const term of terms) switch (term.type) {
|
|
191
|
+
case "command":
|
|
192
|
+
if (!includeHidden && isSuggestionHidden(term.hidden)) return;
|
|
193
|
+
names.add(term.name);
|
|
194
|
+
return;
|
|
195
|
+
case "argument":
|
|
196
|
+
case "literal": return;
|
|
197
|
+
case "optional":
|
|
198
|
+
collectLeading(term.terms);
|
|
199
|
+
break;
|
|
200
|
+
case "multiple":
|
|
201
|
+
collectLeading(term.terms);
|
|
202
|
+
if (term.min > 0 && branchConsumesToken(term.terms)) return;
|
|
203
|
+
break;
|
|
204
|
+
case "exclusive":
|
|
205
|
+
for (const branch of term.terms) collectLeading(branch);
|
|
206
|
+
if (exclusiveConsumesToken(term.terms)) return;
|
|
207
|
+
break;
|
|
208
|
+
default: break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
collectLeading(usage);
|
|
212
|
+
return names;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Checks whether every branch of an exclusive term must consume a positional
|
|
216
|
+
* token. When true, terms after the exclusive are at position N+1 and should
|
|
217
|
+
* not be considered "leading".
|
|
218
|
+
*/
|
|
219
|
+
function exclusiveConsumesToken(branches) {
|
|
220
|
+
if (branches.length === 0) return false;
|
|
221
|
+
return branches.every((branch) => branchConsumesToken(branch));
|
|
222
|
+
}
|
|
223
|
+
function branchConsumesToken(terms) {
|
|
224
|
+
if (!terms || !Array.isArray(terms)) return false;
|
|
225
|
+
for (const term of terms) switch (term.type) {
|
|
226
|
+
case "command":
|
|
227
|
+
case "argument":
|
|
228
|
+
case "literal": return true;
|
|
229
|
+
case "option": break;
|
|
230
|
+
case "optional": break;
|
|
231
|
+
case "multiple":
|
|
232
|
+
if (term.min > 0 && branchConsumesToken(term.terms)) return true;
|
|
233
|
+
break;
|
|
234
|
+
case "exclusive":
|
|
235
|
+
if (exclusiveConsumesToken(term.terms)) return true;
|
|
236
|
+
break;
|
|
237
|
+
default: break;
|
|
238
|
+
}
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Extracts all literal values from a usage description.
|
|
243
|
+
*
|
|
244
|
+
* This function recursively traverses the usage tree and collects all
|
|
245
|
+
* `literal` term values. Literal values represent fixed strings that
|
|
246
|
+
* the user must type (e.g., conditional discriminator values like
|
|
247
|
+
* `"server"` in `conditional(option("--mode", string()), { server: ... })`).
|
|
248
|
+
*
|
|
249
|
+
* @param usage The usage description to extract literal values from.
|
|
250
|
+
* @returns A set of all literal values found in the usage description.
|
|
251
|
+
* @since 1.0.0
|
|
252
|
+
*/
|
|
253
|
+
function extractLiteralValues(usage) {
|
|
254
|
+
const values = /* @__PURE__ */ new Set();
|
|
255
|
+
function traverseUsage(terms) {
|
|
256
|
+
if (!terms || !Array.isArray(terms)) return;
|
|
257
|
+
for (const term of terms) if (term.type === "literal") values.add(term.value);
|
|
258
|
+
else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
|
|
259
|
+
else if (term.type === "exclusive") for (const branch of term.terms) traverseUsage(branch);
|
|
260
|
+
}
|
|
261
|
+
traverseUsage(usage);
|
|
262
|
+
return values;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
103
265
|
* Extracts all argument metavars from a Usage array.
|
|
104
266
|
*
|
|
105
267
|
* This function recursively traverses the usage structure and collects
|
|
@@ -551,6 +713,9 @@ exports.cloneUsage = cloneUsage;
|
|
|
551
713
|
exports.cloneUsageTerm = cloneUsageTerm;
|
|
552
714
|
exports.extractArgumentMetavars = extractArgumentMetavars;
|
|
553
715
|
exports.extractCommandNames = extractCommandNames;
|
|
716
|
+
exports.extractLeadingCommandNames = extractLeadingCommandNames;
|
|
717
|
+
exports.extractLeadingOptionNames = extractLeadingOptionNames;
|
|
718
|
+
exports.extractLiteralValues = extractLiteralValues;
|
|
554
719
|
exports.extractOptionNames = extractOptionNames;
|
|
555
720
|
exports.formatUsage = formatUsage;
|
|
556
721
|
exports.formatUsageTerm = formatUsageTerm;
|
package/dist/usage.d.cts
CHANGED
|
@@ -220,6 +220,8 @@ type Usage = readonly UsageTerm[];
|
|
|
220
220
|
* multiple, and exclusive terms.
|
|
221
221
|
*
|
|
222
222
|
* @param usage The usage description to extract option names from.
|
|
223
|
+
* @param includeHidden Whether to include fully hidden options (`hidden: true`)
|
|
224
|
+
* in the result. Defaults to `false`.
|
|
223
225
|
* @returns A set containing all option names found in the usage description.
|
|
224
226
|
*
|
|
225
227
|
* @example
|
|
@@ -232,15 +234,17 @@ type Usage = readonly UsageTerm[];
|
|
|
232
234
|
* // names = Set(["--verbose", "-v", "--quiet", "-q"])
|
|
233
235
|
* ```
|
|
234
236
|
*/
|
|
235
|
-
declare function extractOptionNames(usage: Usage): Set<string>;
|
|
237
|
+
declare function extractOptionNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
236
238
|
/**
|
|
237
239
|
* Extracts all command names from a Usage array.
|
|
238
240
|
*
|
|
239
241
|
* This function recursively traverses the usage structure and collects
|
|
240
242
|
* all command names, similar to {@link extractOptionNames}.
|
|
241
243
|
*
|
|
242
|
-
* @param usage The usage structure to extract command names from
|
|
243
|
-
* @
|
|
244
|
+
* @param usage The usage structure to extract command names from.
|
|
245
|
+
* @param includeHidden Whether to include fully hidden commands
|
|
246
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
247
|
+
* @returns A set of all command names found in the usage structure.
|
|
244
248
|
*
|
|
245
249
|
* @example
|
|
246
250
|
* ```typescript
|
|
@@ -253,7 +257,73 @@ declare function extractOptionNames(usage: Usage): Set<string>;
|
|
|
253
257
|
* ```
|
|
254
258
|
* @since 0.7.0
|
|
255
259
|
*/
|
|
256
|
-
declare function extractCommandNames(usage: Usage): Set<string>;
|
|
260
|
+
declare function extractCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
261
|
+
/**
|
|
262
|
+
* Extracts option names that are reachable at the leading token position
|
|
263
|
+
* (before any command or argument gate).
|
|
264
|
+
*
|
|
265
|
+
* Unlike {@link extractOptionNames}, which traverses the entire usage tree,
|
|
266
|
+
* this function stops scanning a terms array after encountering a `command`,
|
|
267
|
+
* `argument`, or `literal` term, because subsequent terms are scoped under
|
|
268
|
+
* that positional token. It still recurses into `optional`, `multiple`,
|
|
269
|
+
* and `exclusive` containers, since they represent alternatives or wrappers
|
|
270
|
+
* at the same position.
|
|
271
|
+
*
|
|
272
|
+
* Known limitation: this function infers token positions from the `usage`
|
|
273
|
+
* tree, which is a display-oriented structure. Combinators like `tuple()`
|
|
274
|
+
* and `object()` sort or flatten usage by priority rather than token order,
|
|
275
|
+
* so the results can be inaccurate—both false positives (e.g., options
|
|
276
|
+
* appearing before commands due to priority sorting) and false negatives
|
|
277
|
+
* (e.g., options after commands that are actually parallel peers).
|
|
278
|
+
* The proper fix is to use `Parser.leadingNames` instead of usage-tree
|
|
279
|
+
* analysis. This also cannot detect `conditional(argument(...))`
|
|
280
|
+
* discriminator values, which never appear in the usage tree.
|
|
281
|
+
* See https://github.com/dahlia/optique/issues/734 and
|
|
282
|
+
* https://github.com/dahlia/optique/issues/735
|
|
283
|
+
*
|
|
284
|
+
* @param usage The usage description to extract leading option names from.
|
|
285
|
+
* @param includeHidden Whether to include fully hidden options
|
|
286
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
287
|
+
* @returns A set of option names reachable at the leading token position.
|
|
288
|
+
* @since 1.0.0
|
|
289
|
+
*/
|
|
290
|
+
declare function extractLeadingOptionNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
291
|
+
/**
|
|
292
|
+
* Extracts command names that could match as the first positional token.
|
|
293
|
+
*
|
|
294
|
+
* Unlike {@link extractCommandNames}, which traverses the entire usage tree,
|
|
295
|
+
* this function stops scanning a terms array after encountering a `command`,
|
|
296
|
+
* `argument`, or `literal` term, because subsequent terms in that array are
|
|
297
|
+
* scoped under that positional token (reachable only after the leading term
|
|
298
|
+
* is consumed). It still recurses into `optional`, `multiple`, and
|
|
299
|
+
* `exclusive` containers, since they represent alternatives or optional
|
|
300
|
+
* wrappers at the same token position.
|
|
301
|
+
*
|
|
302
|
+
* Known limitation: this function has the same usage-tree ordering and
|
|
303
|
+
* `conditional(argument(...))` caveats as {@link extractLeadingOptionNames}.
|
|
304
|
+
* See https://github.com/dahlia/optique/issues/734 and
|
|
305
|
+
* https://github.com/dahlia/optique/issues/735
|
|
306
|
+
*
|
|
307
|
+
* @param usage The usage description to extract leading command names from.
|
|
308
|
+
* @param includeHidden Whether to include fully hidden commands
|
|
309
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
310
|
+
* @returns A set of command names that could match at the first token position.
|
|
311
|
+
* @since 1.0.0
|
|
312
|
+
*/
|
|
313
|
+
declare function extractLeadingCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
314
|
+
/**
|
|
315
|
+
* Extracts all literal values from a usage description.
|
|
316
|
+
*
|
|
317
|
+
* This function recursively traverses the usage tree and collects all
|
|
318
|
+
* `literal` term values. Literal values represent fixed strings that
|
|
319
|
+
* the user must type (e.g., conditional discriminator values like
|
|
320
|
+
* `"server"` in `conditional(option("--mode", string()), { server: ... })`).
|
|
321
|
+
*
|
|
322
|
+
* @param usage The usage description to extract literal values from.
|
|
323
|
+
* @returns A set of all literal values found in the usage description.
|
|
324
|
+
* @since 1.0.0
|
|
325
|
+
*/
|
|
326
|
+
declare function extractLiteralValues(usage: Usage): Set<string>;
|
|
257
327
|
/**
|
|
258
328
|
* Extracts all argument metavars from a Usage array.
|
|
259
329
|
*
|
|
@@ -417,4 +487,4 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
|
|
|
417
487
|
*/
|
|
418
488
|
declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
|
|
419
489
|
//#endregion
|
|
420
|
-
export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
|
|
490
|
+
export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
|
package/dist/usage.d.ts
CHANGED
|
@@ -220,6 +220,8 @@ type Usage = readonly UsageTerm[];
|
|
|
220
220
|
* multiple, and exclusive terms.
|
|
221
221
|
*
|
|
222
222
|
* @param usage The usage description to extract option names from.
|
|
223
|
+
* @param includeHidden Whether to include fully hidden options (`hidden: true`)
|
|
224
|
+
* in the result. Defaults to `false`.
|
|
223
225
|
* @returns A set containing all option names found in the usage description.
|
|
224
226
|
*
|
|
225
227
|
* @example
|
|
@@ -232,15 +234,17 @@ type Usage = readonly UsageTerm[];
|
|
|
232
234
|
* // names = Set(["--verbose", "-v", "--quiet", "-q"])
|
|
233
235
|
* ```
|
|
234
236
|
*/
|
|
235
|
-
declare function extractOptionNames(usage: Usage): Set<string>;
|
|
237
|
+
declare function extractOptionNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
236
238
|
/**
|
|
237
239
|
* Extracts all command names from a Usage array.
|
|
238
240
|
*
|
|
239
241
|
* This function recursively traverses the usage structure and collects
|
|
240
242
|
* all command names, similar to {@link extractOptionNames}.
|
|
241
243
|
*
|
|
242
|
-
* @param usage The usage structure to extract command names from
|
|
243
|
-
* @
|
|
244
|
+
* @param usage The usage structure to extract command names from.
|
|
245
|
+
* @param includeHidden Whether to include fully hidden commands
|
|
246
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
247
|
+
* @returns A set of all command names found in the usage structure.
|
|
244
248
|
*
|
|
245
249
|
* @example
|
|
246
250
|
* ```typescript
|
|
@@ -253,7 +257,73 @@ declare function extractOptionNames(usage: Usage): Set<string>;
|
|
|
253
257
|
* ```
|
|
254
258
|
* @since 0.7.0
|
|
255
259
|
*/
|
|
256
|
-
declare function extractCommandNames(usage: Usage): Set<string>;
|
|
260
|
+
declare function extractCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
261
|
+
/**
|
|
262
|
+
* Extracts option names that are reachable at the leading token position
|
|
263
|
+
* (before any command or argument gate).
|
|
264
|
+
*
|
|
265
|
+
* Unlike {@link extractOptionNames}, which traverses the entire usage tree,
|
|
266
|
+
* this function stops scanning a terms array after encountering a `command`,
|
|
267
|
+
* `argument`, or `literal` term, because subsequent terms are scoped under
|
|
268
|
+
* that positional token. It still recurses into `optional`, `multiple`,
|
|
269
|
+
* and `exclusive` containers, since they represent alternatives or wrappers
|
|
270
|
+
* at the same position.
|
|
271
|
+
*
|
|
272
|
+
* Known limitation: this function infers token positions from the `usage`
|
|
273
|
+
* tree, which is a display-oriented structure. Combinators like `tuple()`
|
|
274
|
+
* and `object()` sort or flatten usage by priority rather than token order,
|
|
275
|
+
* so the results can be inaccurate—both false positives (e.g., options
|
|
276
|
+
* appearing before commands due to priority sorting) and false negatives
|
|
277
|
+
* (e.g., options after commands that are actually parallel peers).
|
|
278
|
+
* The proper fix is to use `Parser.leadingNames` instead of usage-tree
|
|
279
|
+
* analysis. This also cannot detect `conditional(argument(...))`
|
|
280
|
+
* discriminator values, which never appear in the usage tree.
|
|
281
|
+
* See https://github.com/dahlia/optique/issues/734 and
|
|
282
|
+
* https://github.com/dahlia/optique/issues/735
|
|
283
|
+
*
|
|
284
|
+
* @param usage The usage description to extract leading option names from.
|
|
285
|
+
* @param includeHidden Whether to include fully hidden options
|
|
286
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
287
|
+
* @returns A set of option names reachable at the leading token position.
|
|
288
|
+
* @since 1.0.0
|
|
289
|
+
*/
|
|
290
|
+
declare function extractLeadingOptionNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
291
|
+
/**
|
|
292
|
+
* Extracts command names that could match as the first positional token.
|
|
293
|
+
*
|
|
294
|
+
* Unlike {@link extractCommandNames}, which traverses the entire usage tree,
|
|
295
|
+
* this function stops scanning a terms array after encountering a `command`,
|
|
296
|
+
* `argument`, or `literal` term, because subsequent terms in that array are
|
|
297
|
+
* scoped under that positional token (reachable only after the leading term
|
|
298
|
+
* is consumed). It still recurses into `optional`, `multiple`, and
|
|
299
|
+
* `exclusive` containers, since they represent alternatives or optional
|
|
300
|
+
* wrappers at the same token position.
|
|
301
|
+
*
|
|
302
|
+
* Known limitation: this function has the same usage-tree ordering and
|
|
303
|
+
* `conditional(argument(...))` caveats as {@link extractLeadingOptionNames}.
|
|
304
|
+
* See https://github.com/dahlia/optique/issues/734 and
|
|
305
|
+
* https://github.com/dahlia/optique/issues/735
|
|
306
|
+
*
|
|
307
|
+
* @param usage The usage description to extract leading command names from.
|
|
308
|
+
* @param includeHidden Whether to include fully hidden commands
|
|
309
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
310
|
+
* @returns A set of command names that could match at the first token position.
|
|
311
|
+
* @since 1.0.0
|
|
312
|
+
*/
|
|
313
|
+
declare function extractLeadingCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
314
|
+
/**
|
|
315
|
+
* Extracts all literal values from a usage description.
|
|
316
|
+
*
|
|
317
|
+
* This function recursively traverses the usage tree and collects all
|
|
318
|
+
* `literal` term values. Literal values represent fixed strings that
|
|
319
|
+
* the user must type (e.g., conditional discriminator values like
|
|
320
|
+
* `"server"` in `conditional(option("--mode", string()), { server: ... })`).
|
|
321
|
+
*
|
|
322
|
+
* @param usage The usage description to extract literal values from.
|
|
323
|
+
* @returns A set of all literal values found in the usage description.
|
|
324
|
+
* @since 1.0.0
|
|
325
|
+
*/
|
|
326
|
+
declare function extractLiteralValues(usage: Usage): Set<string>;
|
|
257
327
|
/**
|
|
258
328
|
* Extracts all argument metavars from a Usage array.
|
|
259
329
|
*
|
|
@@ -417,4 +487,4 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
|
|
|
417
487
|
*/
|
|
418
488
|
declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
|
|
419
489
|
//#endregion
|
|
420
|
-
export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
|
|
490
|
+
export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
|
package/dist/usage.js
CHANGED
|
@@ -41,6 +41,8 @@ function mergeHidden(a, b) {
|
|
|
41
41
|
* multiple, and exclusive terms.
|
|
42
42
|
*
|
|
43
43
|
* @param usage The usage description to extract option names from.
|
|
44
|
+
* @param includeHidden Whether to include fully hidden options (`hidden: true`)
|
|
45
|
+
* in the result. Defaults to `false`.
|
|
44
46
|
* @returns A set containing all option names found in the usage description.
|
|
45
47
|
*
|
|
46
48
|
* @example
|
|
@@ -53,12 +55,12 @@ function mergeHidden(a, b) {
|
|
|
53
55
|
* // names = Set(["--verbose", "-v", "--quiet", "-q"])
|
|
54
56
|
* ```
|
|
55
57
|
*/
|
|
56
|
-
function extractOptionNames(usage) {
|
|
58
|
+
function extractOptionNames(usage, includeHidden) {
|
|
57
59
|
const names = /* @__PURE__ */ new Set();
|
|
58
60
|
function traverseUsage(terms) {
|
|
59
61
|
if (!terms || !Array.isArray(terms)) return;
|
|
60
62
|
for (const term of terms) if (term.type === "option") {
|
|
61
|
-
if (isSuggestionHidden(term.hidden)) continue;
|
|
63
|
+
if (!includeHidden && isSuggestionHidden(term.hidden)) continue;
|
|
62
64
|
for (const name of term.names) names.add(name);
|
|
63
65
|
} else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
|
|
64
66
|
else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
|
|
@@ -72,8 +74,10 @@ function extractOptionNames(usage) {
|
|
|
72
74
|
* This function recursively traverses the usage structure and collects
|
|
73
75
|
* all command names, similar to {@link extractOptionNames}.
|
|
74
76
|
*
|
|
75
|
-
* @param usage The usage structure to extract command names from
|
|
76
|
-
* @
|
|
77
|
+
* @param usage The usage structure to extract command names from.
|
|
78
|
+
* @param includeHidden Whether to include fully hidden commands
|
|
79
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
80
|
+
* @returns A set of all command names found in the usage structure.
|
|
77
81
|
*
|
|
78
82
|
* @example
|
|
79
83
|
* ```typescript
|
|
@@ -86,12 +90,12 @@ function extractOptionNames(usage) {
|
|
|
86
90
|
* ```
|
|
87
91
|
* @since 0.7.0
|
|
88
92
|
*/
|
|
89
|
-
function extractCommandNames(usage) {
|
|
93
|
+
function extractCommandNames(usage, includeHidden) {
|
|
90
94
|
const names = /* @__PURE__ */ new Set();
|
|
91
95
|
function traverseUsage(terms) {
|
|
92
96
|
if (!terms || !Array.isArray(terms)) return;
|
|
93
97
|
for (const term of terms) if (term.type === "command") {
|
|
94
|
-
if (isSuggestionHidden(term.hidden)) continue;
|
|
98
|
+
if (!includeHidden && isSuggestionHidden(term.hidden)) continue;
|
|
95
99
|
names.add(term.name);
|
|
96
100
|
} else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
|
|
97
101
|
else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
|
|
@@ -100,6 +104,164 @@ function extractCommandNames(usage) {
|
|
|
100
104
|
return names;
|
|
101
105
|
}
|
|
102
106
|
/**
|
|
107
|
+
* Extracts option names that are reachable at the leading token position
|
|
108
|
+
* (before any command or argument gate).
|
|
109
|
+
*
|
|
110
|
+
* Unlike {@link extractOptionNames}, which traverses the entire usage tree,
|
|
111
|
+
* this function stops scanning a terms array after encountering a `command`,
|
|
112
|
+
* `argument`, or `literal` term, because subsequent terms are scoped under
|
|
113
|
+
* that positional token. It still recurses into `optional`, `multiple`,
|
|
114
|
+
* and `exclusive` containers, since they represent alternatives or wrappers
|
|
115
|
+
* at the same position.
|
|
116
|
+
*
|
|
117
|
+
* Known limitation: this function infers token positions from the `usage`
|
|
118
|
+
* tree, which is a display-oriented structure. Combinators like `tuple()`
|
|
119
|
+
* and `object()` sort or flatten usage by priority rather than token order,
|
|
120
|
+
* so the results can be inaccurate—both false positives (e.g., options
|
|
121
|
+
* appearing before commands due to priority sorting) and false negatives
|
|
122
|
+
* (e.g., options after commands that are actually parallel peers).
|
|
123
|
+
* The proper fix is to use `Parser.leadingNames` instead of usage-tree
|
|
124
|
+
* analysis. This also cannot detect `conditional(argument(...))`
|
|
125
|
+
* discriminator values, which never appear in the usage tree.
|
|
126
|
+
* See https://github.com/dahlia/optique/issues/734 and
|
|
127
|
+
* https://github.com/dahlia/optique/issues/735
|
|
128
|
+
*
|
|
129
|
+
* @param usage The usage description to extract leading option names from.
|
|
130
|
+
* @param includeHidden Whether to include fully hidden options
|
|
131
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
132
|
+
* @returns A set of option names reachable at the leading token position.
|
|
133
|
+
* @since 1.0.0
|
|
134
|
+
*/
|
|
135
|
+
function extractLeadingOptionNames(usage, includeHidden) {
|
|
136
|
+
const names = /* @__PURE__ */ new Set();
|
|
137
|
+
function collectLeading(terms) {
|
|
138
|
+
if (!terms || !Array.isArray(terms)) return;
|
|
139
|
+
for (const term of terms) switch (term.type) {
|
|
140
|
+
case "option":
|
|
141
|
+
if (!includeHidden && isSuggestionHidden(term.hidden)) break;
|
|
142
|
+
for (const name of term.names) names.add(name);
|
|
143
|
+
break;
|
|
144
|
+
case "command": return;
|
|
145
|
+
case "argument":
|
|
146
|
+
case "literal": return;
|
|
147
|
+
case "optional":
|
|
148
|
+
collectLeading(term.terms);
|
|
149
|
+
break;
|
|
150
|
+
case "multiple":
|
|
151
|
+
collectLeading(term.terms);
|
|
152
|
+
if (term.min > 0 && branchConsumesToken(term.terms)) return;
|
|
153
|
+
break;
|
|
154
|
+
case "exclusive":
|
|
155
|
+
for (const branch of term.terms) collectLeading(branch);
|
|
156
|
+
if (exclusiveConsumesToken(term.terms)) return;
|
|
157
|
+
break;
|
|
158
|
+
default: break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
collectLeading(usage);
|
|
162
|
+
return names;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Extracts command names that could match as the first positional token.
|
|
166
|
+
*
|
|
167
|
+
* Unlike {@link extractCommandNames}, which traverses the entire usage tree,
|
|
168
|
+
* this function stops scanning a terms array after encountering a `command`,
|
|
169
|
+
* `argument`, or `literal` term, because subsequent terms in that array are
|
|
170
|
+
* scoped under that positional token (reachable only after the leading term
|
|
171
|
+
* is consumed). It still recurses into `optional`, `multiple`, and
|
|
172
|
+
* `exclusive` containers, since they represent alternatives or optional
|
|
173
|
+
* wrappers at the same token position.
|
|
174
|
+
*
|
|
175
|
+
* Known limitation: this function has the same usage-tree ordering and
|
|
176
|
+
* `conditional(argument(...))` caveats as {@link extractLeadingOptionNames}.
|
|
177
|
+
* See https://github.com/dahlia/optique/issues/734 and
|
|
178
|
+
* https://github.com/dahlia/optique/issues/735
|
|
179
|
+
*
|
|
180
|
+
* @param usage The usage description to extract leading command names from.
|
|
181
|
+
* @param includeHidden Whether to include fully hidden commands
|
|
182
|
+
* (`hidden: true`) in the result. Defaults to `false`.
|
|
183
|
+
* @returns A set of command names that could match at the first token position.
|
|
184
|
+
* @since 1.0.0
|
|
185
|
+
*/
|
|
186
|
+
function extractLeadingCommandNames(usage, includeHidden) {
|
|
187
|
+
const names = /* @__PURE__ */ new Set();
|
|
188
|
+
function collectLeading(terms) {
|
|
189
|
+
if (!terms || !Array.isArray(terms)) return;
|
|
190
|
+
for (const term of terms) switch (term.type) {
|
|
191
|
+
case "command":
|
|
192
|
+
if (!includeHidden && isSuggestionHidden(term.hidden)) return;
|
|
193
|
+
names.add(term.name);
|
|
194
|
+
return;
|
|
195
|
+
case "argument":
|
|
196
|
+
case "literal": return;
|
|
197
|
+
case "optional":
|
|
198
|
+
collectLeading(term.terms);
|
|
199
|
+
break;
|
|
200
|
+
case "multiple":
|
|
201
|
+
collectLeading(term.terms);
|
|
202
|
+
if (term.min > 0 && branchConsumesToken(term.terms)) return;
|
|
203
|
+
break;
|
|
204
|
+
case "exclusive":
|
|
205
|
+
for (const branch of term.terms) collectLeading(branch);
|
|
206
|
+
if (exclusiveConsumesToken(term.terms)) return;
|
|
207
|
+
break;
|
|
208
|
+
default: break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
collectLeading(usage);
|
|
212
|
+
return names;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Checks whether every branch of an exclusive term must consume a positional
|
|
216
|
+
* token. When true, terms after the exclusive are at position N+1 and should
|
|
217
|
+
* not be considered "leading".
|
|
218
|
+
*/
|
|
219
|
+
function exclusiveConsumesToken(branches) {
|
|
220
|
+
if (branches.length === 0) return false;
|
|
221
|
+
return branches.every((branch) => branchConsumesToken(branch));
|
|
222
|
+
}
|
|
223
|
+
function branchConsumesToken(terms) {
|
|
224
|
+
if (!terms || !Array.isArray(terms)) return false;
|
|
225
|
+
for (const term of terms) switch (term.type) {
|
|
226
|
+
case "command":
|
|
227
|
+
case "argument":
|
|
228
|
+
case "literal": return true;
|
|
229
|
+
case "option": break;
|
|
230
|
+
case "optional": break;
|
|
231
|
+
case "multiple":
|
|
232
|
+
if (term.min > 0 && branchConsumesToken(term.terms)) return true;
|
|
233
|
+
break;
|
|
234
|
+
case "exclusive":
|
|
235
|
+
if (exclusiveConsumesToken(term.terms)) return true;
|
|
236
|
+
break;
|
|
237
|
+
default: break;
|
|
238
|
+
}
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Extracts all literal values from a usage description.
|
|
243
|
+
*
|
|
244
|
+
* This function recursively traverses the usage tree and collects all
|
|
245
|
+
* `literal` term values. Literal values represent fixed strings that
|
|
246
|
+
* the user must type (e.g., conditional discriminator values like
|
|
247
|
+
* `"server"` in `conditional(option("--mode", string()), { server: ... })`).
|
|
248
|
+
*
|
|
249
|
+
* @param usage The usage description to extract literal values from.
|
|
250
|
+
* @returns A set of all literal values found in the usage description.
|
|
251
|
+
* @since 1.0.0
|
|
252
|
+
*/
|
|
253
|
+
function extractLiteralValues(usage) {
|
|
254
|
+
const values = /* @__PURE__ */ new Set();
|
|
255
|
+
function traverseUsage(terms) {
|
|
256
|
+
if (!terms || !Array.isArray(terms)) return;
|
|
257
|
+
for (const term of terms) if (term.type === "literal") values.add(term.value);
|
|
258
|
+
else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
|
|
259
|
+
else if (term.type === "exclusive") for (const branch of term.terms) traverseUsage(branch);
|
|
260
|
+
}
|
|
261
|
+
traverseUsage(usage);
|
|
262
|
+
return values;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
103
265
|
* Extracts all argument metavars from a Usage array.
|
|
104
266
|
*
|
|
105
267
|
* This function recursively traverses the usage structure and collects
|
|
@@ -547,4 +709,4 @@ function* formatUsageTermInternal(term, options) {
|
|
|
547
709
|
}
|
|
548
710
|
|
|
549
711
|
//#endregion
|
|
550
|
-
export { cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
|
|
712
|
+
export { cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
|
package/dist/validate.cjs
CHANGED
|
@@ -63,6 +63,75 @@ function validateCommandNames(names, label) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
+
* Validates that there are no name collisions among meta features
|
|
67
|
+
* (help, version, completion) and between meta features and user parsers.
|
|
68
|
+
*
|
|
69
|
+
* The collision check is *position-aware*:
|
|
70
|
+
*
|
|
71
|
+
* - Meta **command** entries match at `args[0]` only, so they are checked
|
|
72
|
+
* against *leading* user names (those reachable before any positional gate).
|
|
73
|
+
* - Meta **option** entries use lenient scanners that match anywhere in
|
|
74
|
+
* `argv`, so they are checked against *all* user names at every depth,
|
|
75
|
+
* including literal values from conditional discriminators.
|
|
76
|
+
*
|
|
77
|
+
* Meta-vs-meta collisions are always checked in a unified namespace,
|
|
78
|
+
* because a meta command named `"--help"` and a meta option named
|
|
79
|
+
* `"--help"` both compete for the same token.
|
|
80
|
+
*
|
|
81
|
+
* @param userNames User parser names extracted at different scopes.
|
|
82
|
+
* @param metaEntries Active meta feature entries annotated with their kind.
|
|
83
|
+
* @throws {TypeError} If any collision or duplicate is detected.
|
|
84
|
+
* @since 1.0.0
|
|
85
|
+
*/
|
|
86
|
+
function validateMetaNameCollisions(userNames, metaEntries) {
|
|
87
|
+
for (const [, label, names] of metaEntries) {
|
|
88
|
+
const seen = /* @__PURE__ */ new Set();
|
|
89
|
+
for (const name of names) {
|
|
90
|
+
if (seen.has(name)) throw new TypeError(`${capitalize(label)} has a duplicate name: "${name}"`);
|
|
91
|
+
seen.add(name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const nameToLabel = /* @__PURE__ */ new Map();
|
|
95
|
+
for (const [, label, names] of metaEntries) for (const name of names) {
|
|
96
|
+
const existingLabel = nameToLabel.get(name);
|
|
97
|
+
if (existingLabel != null) throw new TypeError(`Name "${name}" is used by both ${existingLabel} and ${label}.`);
|
|
98
|
+
nameToLabel.set(name, label);
|
|
99
|
+
}
|
|
100
|
+
for (let i = 0; i < metaEntries.length; i++) {
|
|
101
|
+
const [, label, names, prefixMatch] = metaEntries[i];
|
|
102
|
+
if (!prefixMatch) continue;
|
|
103
|
+
for (const name of names) {
|
|
104
|
+
const prefix = name + "=";
|
|
105
|
+
for (let j = 0; j < metaEntries.length; j++) {
|
|
106
|
+
const [, otherLabel, otherNames] = metaEntries[j];
|
|
107
|
+
for (const otherName of otherNames) {
|
|
108
|
+
if (i === j && otherName === name) continue;
|
|
109
|
+
if (!otherName.startsWith(prefix)) continue;
|
|
110
|
+
throw new TypeError("The prefix form of name \"" + name + "\" in " + label + " shadows \"" + otherName + "\" in " + otherLabel + ".");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const [kind, label, names, prefixMatch] of metaEntries) {
|
|
116
|
+
const optionNames = kind === "command" ? userNames.leadingOptions : userNames.allOptions;
|
|
117
|
+
const commandNames = kind === "command" ? userNames.leadingCommands : userNames.allCommands;
|
|
118
|
+
for (const name of names) {
|
|
119
|
+
if (optionNames.has(name)) throw new TypeError(`User-defined option "${name}" conflicts with the built-in ${label}.`);
|
|
120
|
+
if (commandNames.has(name)) throw new TypeError(`User-defined command "${name}" conflicts with the built-in ${label}.`);
|
|
121
|
+
if (kind === "option" && userNames.allLiterals.has(name)) throw new TypeError(`Literal value "${name}" conflicts with the built-in ${label}.`);
|
|
122
|
+
if (prefixMatch) {
|
|
123
|
+
const prefix = name + "=";
|
|
124
|
+
for (const userName of optionNames) if (userName.startsWith(prefix)) throw new TypeError(`User-defined option "${userName}" conflicts with the built-in ${label} (prefix "${prefix}").`);
|
|
125
|
+
for (const userName of commandNames) if (userName.startsWith(prefix)) throw new TypeError(`User-defined command "${userName}" conflicts with the built-in ${label} (prefix "${prefix}").`);
|
|
126
|
+
for (const literal of userNames.allLiterals) if (literal.startsWith(prefix)) throw new TypeError(`Literal value "${literal}" conflicts with the built-in ${label} (prefix "${prefix}").`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function capitalize(s) {
|
|
132
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
66
135
|
* Validates a program name at runtime.
|
|
67
136
|
*
|
|
68
137
|
* Program names may contain spaces (e.g., file paths), but must not be empty,
|
|
@@ -81,5 +150,6 @@ function validateProgramName(programName) {
|
|
|
81
150
|
|
|
82
151
|
//#endregion
|
|
83
152
|
exports.validateCommandNames = validateCommandNames;
|
|
153
|
+
exports.validateMetaNameCollisions = validateMetaNameCollisions;
|
|
84
154
|
exports.validateOptionNames = validateOptionNames;
|
|
85
155
|
exports.validateProgramName = validateProgramName;
|
package/dist/validate.js
CHANGED
|
@@ -62,6 +62,75 @@ function validateCommandNames(names, label) {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
|
+
* Validates that there are no name collisions among meta features
|
|
66
|
+
* (help, version, completion) and between meta features and user parsers.
|
|
67
|
+
*
|
|
68
|
+
* The collision check is *position-aware*:
|
|
69
|
+
*
|
|
70
|
+
* - Meta **command** entries match at `args[0]` only, so they are checked
|
|
71
|
+
* against *leading* user names (those reachable before any positional gate).
|
|
72
|
+
* - Meta **option** entries use lenient scanners that match anywhere in
|
|
73
|
+
* `argv`, so they are checked against *all* user names at every depth,
|
|
74
|
+
* including literal values from conditional discriminators.
|
|
75
|
+
*
|
|
76
|
+
* Meta-vs-meta collisions are always checked in a unified namespace,
|
|
77
|
+
* because a meta command named `"--help"` and a meta option named
|
|
78
|
+
* `"--help"` both compete for the same token.
|
|
79
|
+
*
|
|
80
|
+
* @param userNames User parser names extracted at different scopes.
|
|
81
|
+
* @param metaEntries Active meta feature entries annotated with their kind.
|
|
82
|
+
* @throws {TypeError} If any collision or duplicate is detected.
|
|
83
|
+
* @since 1.0.0
|
|
84
|
+
*/
|
|
85
|
+
function validateMetaNameCollisions(userNames, metaEntries) {
|
|
86
|
+
for (const [, label, names] of metaEntries) {
|
|
87
|
+
const seen = /* @__PURE__ */ new Set();
|
|
88
|
+
for (const name of names) {
|
|
89
|
+
if (seen.has(name)) throw new TypeError(`${capitalize(label)} has a duplicate name: "${name}"`);
|
|
90
|
+
seen.add(name);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const nameToLabel = /* @__PURE__ */ new Map();
|
|
94
|
+
for (const [, label, names] of metaEntries) for (const name of names) {
|
|
95
|
+
const existingLabel = nameToLabel.get(name);
|
|
96
|
+
if (existingLabel != null) throw new TypeError(`Name "${name}" is used by both ${existingLabel} and ${label}.`);
|
|
97
|
+
nameToLabel.set(name, label);
|
|
98
|
+
}
|
|
99
|
+
for (let i = 0; i < metaEntries.length; i++) {
|
|
100
|
+
const [, label, names, prefixMatch] = metaEntries[i];
|
|
101
|
+
if (!prefixMatch) continue;
|
|
102
|
+
for (const name of names) {
|
|
103
|
+
const prefix = name + "=";
|
|
104
|
+
for (let j = 0; j < metaEntries.length; j++) {
|
|
105
|
+
const [, otherLabel, otherNames] = metaEntries[j];
|
|
106
|
+
for (const otherName of otherNames) {
|
|
107
|
+
if (i === j && otherName === name) continue;
|
|
108
|
+
if (!otherName.startsWith(prefix)) continue;
|
|
109
|
+
throw new TypeError("The prefix form of name \"" + name + "\" in " + label + " shadows \"" + otherName + "\" in " + otherLabel + ".");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const [kind, label, names, prefixMatch] of metaEntries) {
|
|
115
|
+
const optionNames = kind === "command" ? userNames.leadingOptions : userNames.allOptions;
|
|
116
|
+
const commandNames = kind === "command" ? userNames.leadingCommands : userNames.allCommands;
|
|
117
|
+
for (const name of names) {
|
|
118
|
+
if (optionNames.has(name)) throw new TypeError(`User-defined option "${name}" conflicts with the built-in ${label}.`);
|
|
119
|
+
if (commandNames.has(name)) throw new TypeError(`User-defined command "${name}" conflicts with the built-in ${label}.`);
|
|
120
|
+
if (kind === "option" && userNames.allLiterals.has(name)) throw new TypeError(`Literal value "${name}" conflicts with the built-in ${label}.`);
|
|
121
|
+
if (prefixMatch) {
|
|
122
|
+
const prefix = name + "=";
|
|
123
|
+
for (const userName of optionNames) if (userName.startsWith(prefix)) throw new TypeError(`User-defined option "${userName}" conflicts with the built-in ${label} (prefix "${prefix}").`);
|
|
124
|
+
for (const userName of commandNames) if (userName.startsWith(prefix)) throw new TypeError(`User-defined command "${userName}" conflicts with the built-in ${label} (prefix "${prefix}").`);
|
|
125
|
+
for (const literal of userNames.allLiterals) if (literal.startsWith(prefix)) throw new TypeError(`Literal value "${literal}" conflicts with the built-in ${label} (prefix "${prefix}").`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function capitalize(s) {
|
|
131
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
65
134
|
* Validates a program name at runtime.
|
|
66
135
|
*
|
|
67
136
|
* Program names may contain spaces (e.g., file paths), but must not be empty,
|
|
@@ -79,4 +148,4 @@ function validateProgramName(programName) {
|
|
|
79
148
|
}
|
|
80
149
|
|
|
81
150
|
//#endregion
|
|
82
|
-
export { validateCommandNames, validateOptionNames, validateProgramName };
|
|
151
|
+
export { validateCommandNames, validateMetaNameCollisions, validateOptionNames, validateProgramName };
|