@optique/core 1.0.0-dev.1400 → 1.0.0-dev.1420
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 +7 -61
- package/dist/facade.js +1 -55
- package/dist/primitives.cjs +3 -0
- package/dist/primitives.d.cts +5 -5
- package/dist/primitives.d.ts +5 -5
- package/dist/primitives.js +3 -0
- package/dist/usage.d.cts +7 -1
- package/dist/usage.d.ts +7 -1
- package/dist/validate.cjs +62 -0
- package/dist/validate.js +60 -0
- package/package.json +1 -1
package/dist/facade.cjs
CHANGED
|
@@ -7,6 +7,7 @@ const require_doc = require('./doc.cjs');
|
|
|
7
7
|
const require_constructs = require('./constructs.cjs');
|
|
8
8
|
const require_context = require('./context.cjs');
|
|
9
9
|
const require_modifiers = require('./modifiers.cjs');
|
|
10
|
+
const require_validate = require('./validate.cjs');
|
|
10
11
|
const require_valueparser = require('./valueparser.cjs');
|
|
11
12
|
const require_primitives = require('./primitives.cjs');
|
|
12
13
|
const require_parser = require('./parser.cjs');
|
|
@@ -741,61 +742,6 @@ function validateVersionValue(value$1) {
|
|
|
741
742
|
if (/[\x00-\x1f\x7f]/.test(value$1)) throw new TypeError("Version value must not contain control characters.");
|
|
742
743
|
return value$1;
|
|
743
744
|
}
|
|
744
|
-
/**
|
|
745
|
-
* Escapes control characters in a string for display in error messages.
|
|
746
|
-
*
|
|
747
|
-
* @param value The string to escape.
|
|
748
|
-
* @returns The escaped string with control characters replaced by escape
|
|
749
|
-
* sequences.
|
|
750
|
-
*/
|
|
751
|
-
function escapeControlChars(value$1) {
|
|
752
|
-
return value$1.replace(/[\x00-\x1f\x7f]/g, (ch) => {
|
|
753
|
-
const code = ch.charCodeAt(0);
|
|
754
|
-
switch (code) {
|
|
755
|
-
case 9: return "\\t";
|
|
756
|
-
case 10: return "\\n";
|
|
757
|
-
case 13: return "\\r";
|
|
758
|
-
default: return `\\x${code.toString(16).padStart(2, "0")}`;
|
|
759
|
-
}
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
/**
|
|
763
|
-
* Validates meta option names at runtime.
|
|
764
|
-
*
|
|
765
|
-
* @param names The option names to validate.
|
|
766
|
-
* @param label A human-readable label for error messages (e.g.,
|
|
767
|
-
* `"Help option"`).
|
|
768
|
-
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
769
|
-
* lacks a valid prefix, or contains whitespace or control characters.
|
|
770
|
-
*/
|
|
771
|
-
function validateOptionNames(names, label) {
|
|
772
|
-
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
773
|
-
for (const name of names) {
|
|
774
|
-
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
775
|
-
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
776
|
-
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
777
|
-
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
778
|
-
if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* Validates meta command names at runtime.
|
|
783
|
-
*
|
|
784
|
-
* @param names The command names to validate.
|
|
785
|
-
* @param label A human-readable label for error messages (e.g.,
|
|
786
|
-
* `"Help command"`).
|
|
787
|
-
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
788
|
-
* whitespace-only, or contains whitespace or control characters.
|
|
789
|
-
*/
|
|
790
|
-
function validateCommandNames(names, label) {
|
|
791
|
-
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
792
|
-
for (const name of names) {
|
|
793
|
-
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
794
|
-
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
795
|
-
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
796
|
-
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
745
|
function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsParam) {
|
|
800
746
|
const isProgram = typeof programNameOrArgs !== "string";
|
|
801
747
|
let parser;
|
|
@@ -839,12 +785,12 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
839
785
|
const onCompletion = options.completion?.onShow ?? (() => ({}));
|
|
840
786
|
const onCompletionResult = (code) => onCompletion(code);
|
|
841
787
|
const onErrorResult = (code) => onError(code);
|
|
842
|
-
if (helpOptionConfig?.names) validateOptionNames(helpOptionConfig.names, "Help option");
|
|
843
|
-
if (helpCommandConfig?.names) validateCommandNames(helpCommandConfig.names, "Help command");
|
|
844
|
-
if (versionOptionConfig?.names) validateOptionNames(versionOptionConfig.names, "Version option");
|
|
845
|
-
if (versionCommandConfig?.names) validateCommandNames(versionCommandConfig.names, "Version command");
|
|
846
|
-
if (completionOptionConfig?.names) validateOptionNames(completionOptionConfig.names, "Completion option");
|
|
847
|
-
if (completionCommandConfig?.names) validateCommandNames(completionCommandConfig.names, "Completion command");
|
|
788
|
+
if (helpOptionConfig?.names) require_validate.validateOptionNames(helpOptionConfig.names, "Help option");
|
|
789
|
+
if (helpCommandConfig?.names) require_validate.validateCommandNames(helpCommandConfig.names, "Help command");
|
|
790
|
+
if (versionOptionConfig?.names) require_validate.validateOptionNames(versionOptionConfig.names, "Version option");
|
|
791
|
+
if (versionCommandConfig?.names) require_validate.validateCommandNames(versionCommandConfig.names, "Version command");
|
|
792
|
+
if (completionOptionConfig?.names) require_validate.validateOptionNames(completionOptionConfig.names, "Completion option");
|
|
793
|
+
if (completionCommandConfig?.names) require_validate.validateCommandNames(completionCommandConfig.names, "Completion command");
|
|
848
794
|
const helpOptionNames = helpOptionConfig?.names ?? ["--help"];
|
|
849
795
|
const helpCommandNames = helpCommandConfig?.names ?? ["help"];
|
|
850
796
|
const versionOptionNames = versionOptionConfig?.names ?? ["--version"];
|
package/dist/facade.js
CHANGED
|
@@ -7,6 +7,7 @@ import { formatDocPage } from "./doc.js";
|
|
|
7
7
|
import { group, longestMatch, object } from "./constructs.js";
|
|
8
8
|
import { isPlaceholderValue } from "./context.js";
|
|
9
9
|
import { multiple, optional, withDefault } from "./modifiers.js";
|
|
10
|
+
import { validateCommandNames, validateOptionNames } from "./validate.js";
|
|
10
11
|
import { string } from "./valueparser.js";
|
|
11
12
|
import { argument, command, constant, flag, option } from "./primitives.js";
|
|
12
13
|
import { getDocPage, parseAsync, parseSync, suggest, suggestAsync } from "./parser.js";
|
|
@@ -741,61 +742,6 @@ function validateVersionValue(value$1) {
|
|
|
741
742
|
if (/[\x00-\x1f\x7f]/.test(value$1)) throw new TypeError("Version value must not contain control characters.");
|
|
742
743
|
return value$1;
|
|
743
744
|
}
|
|
744
|
-
/**
|
|
745
|
-
* Escapes control characters in a string for display in error messages.
|
|
746
|
-
*
|
|
747
|
-
* @param value The string to escape.
|
|
748
|
-
* @returns The escaped string with control characters replaced by escape
|
|
749
|
-
* sequences.
|
|
750
|
-
*/
|
|
751
|
-
function escapeControlChars(value$1) {
|
|
752
|
-
return value$1.replace(/[\x00-\x1f\x7f]/g, (ch) => {
|
|
753
|
-
const code = ch.charCodeAt(0);
|
|
754
|
-
switch (code) {
|
|
755
|
-
case 9: return "\\t";
|
|
756
|
-
case 10: return "\\n";
|
|
757
|
-
case 13: return "\\r";
|
|
758
|
-
default: return `\\x${code.toString(16).padStart(2, "0")}`;
|
|
759
|
-
}
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
/**
|
|
763
|
-
* Validates meta option names at runtime.
|
|
764
|
-
*
|
|
765
|
-
* @param names The option names to validate.
|
|
766
|
-
* @param label A human-readable label for error messages (e.g.,
|
|
767
|
-
* `"Help option"`).
|
|
768
|
-
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
769
|
-
* lacks a valid prefix, or contains whitespace or control characters.
|
|
770
|
-
*/
|
|
771
|
-
function validateOptionNames(names, label) {
|
|
772
|
-
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
773
|
-
for (const name of names) {
|
|
774
|
-
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
775
|
-
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
776
|
-
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
777
|
-
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
778
|
-
if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* Validates meta command names at runtime.
|
|
783
|
-
*
|
|
784
|
-
* @param names The command names to validate.
|
|
785
|
-
* @param label A human-readable label for error messages (e.g.,
|
|
786
|
-
* `"Help command"`).
|
|
787
|
-
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
788
|
-
* whitespace-only, or contains whitespace or control characters.
|
|
789
|
-
*/
|
|
790
|
-
function validateCommandNames(names, label) {
|
|
791
|
-
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
792
|
-
for (const name of names) {
|
|
793
|
-
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
794
|
-
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
795
|
-
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
796
|
-
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
745
|
function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsParam) {
|
|
800
746
|
const isProgram = typeof programNameOrArgs !== "string";
|
|
801
747
|
let parser;
|
package/dist/primitives.cjs
CHANGED
|
@@ -5,6 +5,7 @@ const require_mode_dispatch = require('./mode-dispatch.cjs');
|
|
|
5
5
|
const require_usage = require('./usage.cjs');
|
|
6
6
|
const require_suggestion = require('./suggestion.cjs');
|
|
7
7
|
const require_usage_internals = require('./usage-internals.cjs');
|
|
8
|
+
const require_validate = require('./validate.cjs');
|
|
8
9
|
const require_valueparser = require('./valueparser.cjs');
|
|
9
10
|
|
|
10
11
|
//#region src/primitives.ts
|
|
@@ -309,6 +310,7 @@ function option(...args) {
|
|
|
309
310
|
optionNames$1 = args;
|
|
310
311
|
valueParser = void 0;
|
|
311
312
|
}
|
|
313
|
+
require_validate.validateOptionNames(optionNames$1, "Option");
|
|
312
314
|
const mode = valueParser?.$mode ?? "sync";
|
|
313
315
|
const isAsync = mode === "async";
|
|
314
316
|
const result = {
|
|
@@ -585,6 +587,7 @@ function flag(...args) {
|
|
|
585
587
|
options = lastArg;
|
|
586
588
|
optionNames$1 = args.slice(0, -1);
|
|
587
589
|
} else optionNames$1 = args;
|
|
590
|
+
require_validate.validateOptionNames(optionNames$1, "Flag");
|
|
588
591
|
return {
|
|
589
592
|
$valueType: [],
|
|
590
593
|
$stateType: [],
|
package/dist/primitives.d.cts
CHANGED
|
@@ -119,7 +119,7 @@ interface OptionErrorOptions {
|
|
|
119
119
|
* @returns A {@link Parser} that can parse the specified options and their
|
|
120
120
|
* values.
|
|
121
121
|
*/
|
|
122
|
-
declare function option<M extends Mode, T>(...args: readonly [...readonly OptionName[], ValueParser<M, T>]): Parser<M, T, ValueParserResult<T> | undefined>;
|
|
122
|
+
declare function option<M extends Mode, T>(...args: readonly [OptionName, ...readonly OptionName[], ValueParser<M, T>]): Parser<M, T, ValueParserResult<T> | undefined>;
|
|
123
123
|
/**
|
|
124
124
|
* Creates a parser for various styles of command-line options that take an
|
|
125
125
|
* argument value, such as `--option=value`, `-option=value`, `-o value`,
|
|
@@ -133,7 +133,7 @@ declare function option<M extends Mode, T>(...args: readonly [...readonly Option
|
|
|
133
133
|
* @returns A {@link Parser} that can parse the specified options and their
|
|
134
134
|
* values.
|
|
135
135
|
*/
|
|
136
|
-
declare function option<M extends Mode, T>(...args: readonly [...readonly OptionName[], ValueParser<M, T>, OptionOptions]): Parser<M, T, ValueParserResult<T> | undefined>;
|
|
136
|
+
declare function option<M extends Mode, T>(...args: readonly [OptionName, ...readonly OptionName[], ValueParser<M, T>, OptionOptions]): Parser<M, T, ValueParserResult<T> | undefined>;
|
|
137
137
|
/**
|
|
138
138
|
* Creates a parser for various styles of command-line options that do not
|
|
139
139
|
* take an argument value, such as `--option`, `-o`, or `/option`.
|
|
@@ -141,7 +141,7 @@ declare function option<M extends Mode, T>(...args: readonly [...readonly Option
|
|
|
141
141
|
* @return A {@link Parser} that can parse the specified options as Boolean
|
|
142
142
|
* flags, producing `true` if the option is present.
|
|
143
143
|
*/
|
|
144
|
-
declare function option(...optionNames: readonly OptionName[]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
|
|
144
|
+
declare function option(...optionNames: readonly [OptionName, ...readonly OptionName[]]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
|
|
145
145
|
/**
|
|
146
146
|
* Creates a parser for various styles of command-line options that take an
|
|
147
147
|
* argument value, such as `--option=value`, `-option=value`, `-o value`,
|
|
@@ -152,7 +152,7 @@ declare function option(...optionNames: readonly OptionName[]): Parser<"sync", b
|
|
|
152
152
|
* @returns A {@link Parser} that can parse the specified options and their
|
|
153
153
|
* values.
|
|
154
154
|
*/
|
|
155
|
-
declare function option(...args: readonly [...readonly OptionName[], OptionOptions]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
|
|
155
|
+
declare function option(...args: readonly [OptionName, ...readonly OptionName[], OptionOptions]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
|
|
156
156
|
/**
|
|
157
157
|
* Options for the {@link flag} parser.
|
|
158
158
|
*/
|
|
@@ -242,7 +242,7 @@ interface FlagErrorOptions {
|
|
|
242
242
|
* });
|
|
243
243
|
* ```
|
|
244
244
|
*/
|
|
245
|
-
declare function flag(...args: readonly [...readonly OptionName[], FlagOptions] | readonly OptionName[]): Parser<"sync", true, ValueParserResult<true> | undefined>;
|
|
245
|
+
declare function flag(...args: readonly [OptionName, ...readonly OptionName[], FlagOptions] | readonly [OptionName, ...readonly OptionName[]]): Parser<"sync", true, ValueParserResult<true> | undefined>;
|
|
246
246
|
/**
|
|
247
247
|
* Options for the {@link argument} parser.
|
|
248
248
|
*/
|
package/dist/primitives.d.ts
CHANGED
|
@@ -119,7 +119,7 @@ interface OptionErrorOptions {
|
|
|
119
119
|
* @returns A {@link Parser} that can parse the specified options and their
|
|
120
120
|
* values.
|
|
121
121
|
*/
|
|
122
|
-
declare function option<M extends Mode, T>(...args: readonly [...readonly OptionName[], ValueParser<M, T>]): Parser<M, T, ValueParserResult<T> | undefined>;
|
|
122
|
+
declare function option<M extends Mode, T>(...args: readonly [OptionName, ...readonly OptionName[], ValueParser<M, T>]): Parser<M, T, ValueParserResult<T> | undefined>;
|
|
123
123
|
/**
|
|
124
124
|
* Creates a parser for various styles of command-line options that take an
|
|
125
125
|
* argument value, such as `--option=value`, `-option=value`, `-o value`,
|
|
@@ -133,7 +133,7 @@ declare function option<M extends Mode, T>(...args: readonly [...readonly Option
|
|
|
133
133
|
* @returns A {@link Parser} that can parse the specified options and their
|
|
134
134
|
* values.
|
|
135
135
|
*/
|
|
136
|
-
declare function option<M extends Mode, T>(...args: readonly [...readonly OptionName[], ValueParser<M, T>, OptionOptions]): Parser<M, T, ValueParserResult<T> | undefined>;
|
|
136
|
+
declare function option<M extends Mode, T>(...args: readonly [OptionName, ...readonly OptionName[], ValueParser<M, T>, OptionOptions]): Parser<M, T, ValueParserResult<T> | undefined>;
|
|
137
137
|
/**
|
|
138
138
|
* Creates a parser for various styles of command-line options that do not
|
|
139
139
|
* take an argument value, such as `--option`, `-o`, or `/option`.
|
|
@@ -141,7 +141,7 @@ declare function option<M extends Mode, T>(...args: readonly [...readonly Option
|
|
|
141
141
|
* @return A {@link Parser} that can parse the specified options as Boolean
|
|
142
142
|
* flags, producing `true` if the option is present.
|
|
143
143
|
*/
|
|
144
|
-
declare function option(...optionNames: readonly OptionName[]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
|
|
144
|
+
declare function option(...optionNames: readonly [OptionName, ...readonly OptionName[]]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
|
|
145
145
|
/**
|
|
146
146
|
* Creates a parser for various styles of command-line options that take an
|
|
147
147
|
* argument value, such as `--option=value`, `-option=value`, `-o value`,
|
|
@@ -152,7 +152,7 @@ declare function option(...optionNames: readonly OptionName[]): Parser<"sync", b
|
|
|
152
152
|
* @returns A {@link Parser} that can parse the specified options and their
|
|
153
153
|
* values.
|
|
154
154
|
*/
|
|
155
|
-
declare function option(...args: readonly [...readonly OptionName[], OptionOptions]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
|
|
155
|
+
declare function option(...args: readonly [OptionName, ...readonly OptionName[], OptionOptions]): Parser<"sync", boolean, ValueParserResult<boolean> | undefined>;
|
|
156
156
|
/**
|
|
157
157
|
* Options for the {@link flag} parser.
|
|
158
158
|
*/
|
|
@@ -242,7 +242,7 @@ interface FlagErrorOptions {
|
|
|
242
242
|
* });
|
|
243
243
|
* ```
|
|
244
244
|
*/
|
|
245
|
-
declare function flag(...args: readonly [...readonly OptionName[], FlagOptions] | readonly OptionName[]): Parser<"sync", true, ValueParserResult<true> | undefined>;
|
|
245
|
+
declare function flag(...args: readonly [OptionName, ...readonly OptionName[], FlagOptions] | readonly [OptionName, ...readonly OptionName[]]): Parser<"sync", true, ValueParserResult<true> | undefined>;
|
|
246
246
|
/**
|
|
247
247
|
* Options for the {@link argument} parser.
|
|
248
248
|
*/
|
package/dist/primitives.js
CHANGED
|
@@ -5,6 +5,7 @@ import { dispatchIterableByMode } from "./mode-dispatch.js";
|
|
|
5
5
|
import { extractOptionNames, isDocHidden, isSuggestionHidden } from "./usage.js";
|
|
6
6
|
import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, findSimilar } from "./suggestion.js";
|
|
7
7
|
import { extractLeadingCommandNames } from "./usage-internals.js";
|
|
8
|
+
import { validateOptionNames } from "./validate.js";
|
|
8
9
|
import { isValueParser } from "./valueparser.js";
|
|
9
10
|
|
|
10
11
|
//#region src/primitives.ts
|
|
@@ -309,6 +310,7 @@ function option(...args) {
|
|
|
309
310
|
optionNames$1 = args;
|
|
310
311
|
valueParser = void 0;
|
|
311
312
|
}
|
|
313
|
+
validateOptionNames(optionNames$1, "Option");
|
|
312
314
|
const mode = valueParser?.$mode ?? "sync";
|
|
313
315
|
const isAsync = mode === "async";
|
|
314
316
|
const result = {
|
|
@@ -585,6 +587,7 @@ function flag(...args) {
|
|
|
585
587
|
options = lastArg;
|
|
586
588
|
optionNames$1 = args.slice(0, -1);
|
|
587
589
|
} else optionNames$1 = args;
|
|
590
|
+
validateOptionNames(optionNames$1, "Flag");
|
|
588
591
|
return {
|
|
589
592
|
$valueType: [],
|
|
590
593
|
$stateType: [],
|
package/dist/usage.d.cts
CHANGED
|
@@ -10,8 +10,14 @@ import { NonEmptyString } from "./nonempty.cjs";
|
|
|
10
10
|
* - POSIX-style short options (`-o`) or Java-style options (`-option`)
|
|
11
11
|
* - MS-DOS-style options (`/o`, `/option`)
|
|
12
12
|
* - Plus-prefixed options (`+o`)
|
|
13
|
+
*
|
|
14
|
+
* Each prefix must be followed by at least one character, so bare prefixes
|
|
15
|
+
* like `"-"`, `"/"`, or `"+"` are rejected at compile time. Due to
|
|
16
|
+
* TypeScript template literal limitations, `"--"` still matches the
|
|
17
|
+
* `-${NonEmptyString}` branch and is only rejected at runtime by the
|
|
18
|
+
* `option()` and `flag()` validators.
|
|
13
19
|
*/
|
|
14
|
-
type OptionName = `--${
|
|
20
|
+
type OptionName = `--${NonEmptyString}` | `-${NonEmptyString}` | `/${NonEmptyString}` | `+${NonEmptyString}`;
|
|
15
21
|
/**
|
|
16
22
|
* Visibility control for parser terms.
|
|
17
23
|
*
|
package/dist/usage.d.ts
CHANGED
|
@@ -10,8 +10,14 @@ import { NonEmptyString } from "./nonempty.js";
|
|
|
10
10
|
* - POSIX-style short options (`-o`) or Java-style options (`-option`)
|
|
11
11
|
* - MS-DOS-style options (`/o`, `/option`)
|
|
12
12
|
* - Plus-prefixed options (`+o`)
|
|
13
|
+
*
|
|
14
|
+
* Each prefix must be followed by at least one character, so bare prefixes
|
|
15
|
+
* like `"-"`, `"/"`, or `"+"` are rejected at compile time. Due to
|
|
16
|
+
* TypeScript template literal limitations, `"--"` still matches the
|
|
17
|
+
* `-${NonEmptyString}` branch and is only rejected at runtime by the
|
|
18
|
+
* `option()` and `flag()` validators.
|
|
13
19
|
*/
|
|
14
|
-
type OptionName = `--${
|
|
20
|
+
type OptionName = `--${NonEmptyString}` | `-${NonEmptyString}` | `/${NonEmptyString}` | `+${NonEmptyString}`;
|
|
15
21
|
/**
|
|
16
22
|
* Visibility control for parser terms.
|
|
17
23
|
*
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/validate.ts
|
|
3
|
+
/**
|
|
4
|
+
* Escapes control characters in a string for readable error messages.
|
|
5
|
+
*
|
|
6
|
+
* @param value The string to escape.
|
|
7
|
+
* @returns The escaped string with control characters replaced by escape
|
|
8
|
+
* sequences.
|
|
9
|
+
*/
|
|
10
|
+
function escapeControlChars(value) {
|
|
11
|
+
return value.replace(/[\x00-\x1f\x7f]/g, (ch) => {
|
|
12
|
+
const code = ch.charCodeAt(0);
|
|
13
|
+
switch (code) {
|
|
14
|
+
case 9: return "\\t";
|
|
15
|
+
case 10: return "\\n";
|
|
16
|
+
case 13: return "\\r";
|
|
17
|
+
default: return `\\x${code.toString(16).padStart(2, "0")}`;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validates option names at runtime.
|
|
23
|
+
*
|
|
24
|
+
* @param names The option names to validate.
|
|
25
|
+
* @param label A human-readable label for error messages (e.g.,
|
|
26
|
+
* `"Option"`, `"Flag"`, `"Help option"`).
|
|
27
|
+
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
28
|
+
* lacks a valid prefix, or contains whitespace or control characters.
|
|
29
|
+
*/
|
|
30
|
+
function validateOptionNames(names, label) {
|
|
31
|
+
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
32
|
+
for (const name of names) {
|
|
33
|
+
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
34
|
+
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
35
|
+
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
36
|
+
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
37
|
+
if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
|
|
38
|
+
if (name === "--") throw new TypeError(`${label} name must not be the options terminator "--".`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validates command names at runtime.
|
|
43
|
+
*
|
|
44
|
+
* @param names The command names to validate.
|
|
45
|
+
* @param label A human-readable label for error messages (e.g.,
|
|
46
|
+
* `"Help command"`).
|
|
47
|
+
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
48
|
+
* whitespace-only, or contains whitespace or control characters.
|
|
49
|
+
*/
|
|
50
|
+
function validateCommandNames(names, label) {
|
|
51
|
+
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
52
|
+
for (const name of names) {
|
|
53
|
+
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
54
|
+
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
55
|
+
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
56
|
+
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
exports.validateCommandNames = validateCommandNames;
|
|
62
|
+
exports.validateOptionNames = validateOptionNames;
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//#region src/validate.ts
|
|
2
|
+
/**
|
|
3
|
+
* Escapes control characters in a string for readable error messages.
|
|
4
|
+
*
|
|
5
|
+
* @param value The string to escape.
|
|
6
|
+
* @returns The escaped string with control characters replaced by escape
|
|
7
|
+
* sequences.
|
|
8
|
+
*/
|
|
9
|
+
function escapeControlChars(value) {
|
|
10
|
+
return value.replace(/[\x00-\x1f\x7f]/g, (ch) => {
|
|
11
|
+
const code = ch.charCodeAt(0);
|
|
12
|
+
switch (code) {
|
|
13
|
+
case 9: return "\\t";
|
|
14
|
+
case 10: return "\\n";
|
|
15
|
+
case 13: return "\\r";
|
|
16
|
+
default: return `\\x${code.toString(16).padStart(2, "0")}`;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Validates option names at runtime.
|
|
22
|
+
*
|
|
23
|
+
* @param names The option names to validate.
|
|
24
|
+
* @param label A human-readable label for error messages (e.g.,
|
|
25
|
+
* `"Option"`, `"Flag"`, `"Help option"`).
|
|
26
|
+
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
27
|
+
* lacks a valid prefix, or contains whitespace or control characters.
|
|
28
|
+
*/
|
|
29
|
+
function validateOptionNames(names, label) {
|
|
30
|
+
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
31
|
+
for (const name of names) {
|
|
32
|
+
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
33
|
+
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
34
|
+
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
35
|
+
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
36
|
+
if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
|
|
37
|
+
if (name === "--") throw new TypeError(`${label} name must not be the options terminator "--".`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Validates command names at runtime.
|
|
42
|
+
*
|
|
43
|
+
* @param names The command names to validate.
|
|
44
|
+
* @param label A human-readable label for error messages (e.g.,
|
|
45
|
+
* `"Help command"`).
|
|
46
|
+
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
47
|
+
* whitespace-only, or contains whitespace or control characters.
|
|
48
|
+
*/
|
|
49
|
+
function validateCommandNames(names, label) {
|
|
50
|
+
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
51
|
+
for (const name of names) {
|
|
52
|
+
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
53
|
+
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
54
|
+
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
55
|
+
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
export { validateCommandNames, validateOptionNames };
|