cli-kiss 0.2.7 → 0.2.9

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.
Files changed (42) hide show
  1. package/README.md +8 -3
  2. package/dist/index.d.ts +200 -190
  3. package/dist/index.js +2 -2
  4. package/dist/index.js.map +1 -1
  5. package/docs/.vitepress/config.mts +1 -1
  6. package/docs/.vitepress/theme/Layout.vue +16 -0
  7. package/docs/.vitepress/theme/index.ts +5 -1
  8. package/docs/.vitepress/theme/style.css +5 -1
  9. package/docs/guide/01_getting_started.md +2 -2
  10. package/docs/guide/02_commands.md +3 -3
  11. package/docs/guide/03_options.md +11 -11
  12. package/docs/guide/04_positionals.md +9 -9
  13. package/docs/guide/05_input_types.md +17 -16
  14. package/docs/guide/06_run_as_cli.md +1 -1
  15. package/docs/index.md +2 -2
  16. package/docs/public/favicon.ico +0 -0
  17. package/docs/public/logo.png +0 -0
  18. package/package.json +1 -1
  19. package/src/index.ts +1 -1
  20. package/src/lib/Command.ts +51 -40
  21. package/src/lib/Operation.ts +41 -25
  22. package/src/lib/Option.ts +198 -127
  23. package/src/lib/Positional.ts +51 -25
  24. package/src/lib/Reader.ts +188 -226
  25. package/src/lib/Run.ts +20 -9
  26. package/src/lib/Suggest.ts +78 -0
  27. package/src/lib/Type.ts +178 -154
  28. package/src/lib/Typo.ts +58 -55
  29. package/src/lib/Usage.ts +12 -12
  30. package/tests/unit.Reader.commons.ts +86 -123
  31. package/tests/unit.Reader.parsings.ts +14 -26
  32. package/tests/unit.Reader.shortBig.ts +75 -101
  33. package/tests/unit.command.aliases.ts +88 -0
  34. package/tests/unit.command.execute.ts +6 -6
  35. package/tests/unit.command.usage.ts +19 -13
  36. package/tests/unit.fuzzed.alternatives.ts +35 -26
  37. package/tests/unit.runner.colors.ts +8 -33
  38. package/tests/unit.runner.cycle.ts +141 -156
  39. package/tests/unit.runner.errors.ts +25 -22
  40. package/docs/public/hero.png +0 -0
  41. package/src/lib/Similarity.ts +0 -41
  42. package/tests/unit.Reader.aliases.ts +0 -62
@@ -1,4 +1,4 @@
1
- import { it } from "@jest/globals";
1
+ import { expect, it } from "@jest/globals";
2
2
  import {
3
3
  command,
4
4
  operation,
@@ -13,39 +13,43 @@ import {
13
13
  it("run", async function () {
14
14
  await testCase(
15
15
  ["hello"],
16
- '{{Error:}@darkRed}+ Unexpected argument: {{"hello"}@darkYellow}+',
16
+ '{{Error:}@darkRed}+ Unexpected argument: {{"hello"}@darkYellow}+.', // TODO - should we recommend help here ?
17
17
  );
18
18
  await testCase(
19
19
  ["--nope"],
20
- '{{Error:}@darkRed}+ Unknown option: {{"--nope"}@darkYellow}+: did you mean: {{--help}@darkCyan}+, {{--flag}@darkCyan}+, {{--repeatable}@darkCyan}+ ?',
20
+ '{{Error:}@darkRed}+ Unknown option: {{"--nope"}@darkYellow}+. Did you mean: {{--help}@darkCyan}+, {{--flag}@darkCyan}+, {{--repeatable}@darkCyan}+, {{...}-}* ?',
21
21
  );
22
22
  await testCase(
23
23
  ["--repeat"],
24
- '{{Error:}@darkRed}+ Unknown option: {{"--repeat"}@darkYellow}+: did you mean: {{--repeatable}@darkCyan}+, {{--help}@darkCyan}+, {{--flag}@darkCyan}+ ?',
25
- );
26
- await testCase(
27
- ["--flag", "--flag"],
28
- "{{Error:}@darkRed}+ {{--flag}@darkCyan}+: Must not be set multiple times",
29
- );
30
- await testCase(
31
- ["--flag=invalid"],
32
- '{{Error:}@darkRed}+ {{--flag}@darkCyan}+: {{value}@darkMagenta}+: Not a boolean: {{"invalid"}@darkYellow}+',
24
+ '{{Error:}@darkRed}+ Unknown option: {{"--repeat"}@darkYellow}+. Did you mean: {{--repeatable}@darkCyan}+ ?',
33
25
  );
34
26
  await testCase(
35
27
  ["--single-value=a", "--single-value=b"],
36
- "{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: Must not be set multiple times",
28
+ "{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: Must not be set multiple times.",
37
29
  );
38
30
  await testCase(
39
31
  ["--single-value"],
40
- "{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: Requires a value, but got end of input",
32
+ "{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: Requires a value, but got end of input.",
33
+ );
34
+ await testCase(
35
+ [],
36
+ "{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: {{<location>}@darkBlue}+: Is required, but was not set.",
41
37
  );
42
38
  await testCase(
43
39
  ["--single-value=invalid"],
44
- '{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: {{<location>}@darkBlue}+: Not an URL: {{"invalid"}@darkYellow}+',
40
+ '{{Error:}@darkRed}+ {{--single-value}@darkCyan}+: {{<location>}@darkBlue}+: Not an URL: {{"invalid"}@darkYellow}+.',
45
41
  );
46
42
  await testCase(
47
43
  ["--repeatable=invalid"],
48
- '{{Error:}@darkRed}+ {{--repeatable}@darkCyan}+: {{<index>}@darkBlue}+: Not a number: {{"invalid"}@darkYellow}+',
44
+ '{{Error:}@darkRed}+ {{--repeatable}@darkCyan}+: {{<index>}@darkBlue}+: Not a number: {{"invalid"}@darkYellow}+.',
45
+ );
46
+ await testCase(
47
+ ["--flag", "-f"],
48
+ "{{Error:}@darkRed}+ {{--flag}@darkCyan}+, {{-f}@darkCyan}+: Must not be set multiple times.",
49
+ );
50
+ await testCase(
51
+ ["--flag=invalid"],
52
+ '{{Error:}@darkRed}+ {{--flag}@darkCyan}+: {{value}@darkMagenta}+: Not a boolean: {{"invalid"}@darkYellow}+.',
49
53
  );
50
54
  });
51
55
 
@@ -58,16 +62,15 @@ async function testCase(args: Array<string>, error: string) {
58
62
  operation(
59
63
  {
60
64
  options: {
61
- optionFlag: optionFlag({ long: "flag" }),
62
- optionSingleValue: optionSingleValue({
63
- long: "single-value",
64
- type: typeUrl("location"),
65
- defaultIfNotSpecified: () => undefined,
66
- }),
65
+ optionFlag: optionFlag({ long: "flag", short: "f" }),
67
66
  optionRepeatable: optionRepeatable({
68
67
  long: "repeatable",
69
68
  type: typeNumber("index"),
70
69
  }),
70
+ optionSingleValue: optionSingleValue({
71
+ long: "single-value",
72
+ type: typeUrl("location"),
73
+ }),
71
74
  },
72
75
  positionals: [],
73
76
  },
Binary file
@@ -1,41 +0,0 @@
1
- export function similaritySort<Value>(
2
- reference: string,
3
- candidates: { [key: string]: Value } | Array<{ key: string; value: Value }>,
4
- ): Array<Value> {
5
- let entries = Array.isArray(candidates)
6
- ? candidates.map(({ key, value }) => [key, value] as const)
7
- : Object.entries(candidates);
8
- const ranked = entries.map(([key, value]) => {
9
- const score =
10
- damerauLevenshtein(reference, key) /
11
- Math.max(reference.length, key.length);
12
- return { key, value, score };
13
- });
14
- return ranked.sort((a, b) => a.score - b.score).map((v) => v.value);
15
- }
16
-
17
- function damerauLevenshtein(a: string, b: string): number {
18
- const m = a.length;
19
- const n = b.length;
20
- const dp = Array.from({ length: m + 1 }, () => Array<number>(n + 1).fill(0));
21
- for (let i = 0; i <= m; i++) {
22
- dp[i]![0] = i;
23
- }
24
- for (let j = 0; j <= n; j++) {
25
- dp[0]![j] = j;
26
- }
27
- for (let i = 1; i <= m; i++) {
28
- for (let j = 1; j <= n; j++) {
29
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
30
- dp[i]![j] = Math.min(
31
- dp[i - 1]![j]! + 1,
32
- dp[i]![j - 1]! + 1,
33
- dp[i - 1]![j - 1]! + cost,
34
- );
35
- if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
36
- dp[i]![j] = Math.min(dp[i]![j]!, dp[i - 2]![j - 2]! + cost);
37
- }
38
- }
39
- }
40
- return dp[m]![n]!;
41
- }
@@ -1,62 +0,0 @@
1
- import { expect, it } from "@jest/globals";
2
- import { ReaderArgs, ReaderOptionParsing } from "../src";
3
-
4
- it("run", async function () {
5
- const readerArgs = new ReaderArgs([
6
- "--option=1.1",
7
- "--option-alias1=1.2",
8
- "--option-alias2",
9
- "1.3",
10
- "-pts=1.4",
11
- "-o",
12
- "1.5",
13
- "--flag1-alias",
14
- "-fa2=woops",
15
- "-fa2o=1.6",
16
- ]);
17
-
18
- const kOption = readerArgs.registerOption({
19
- longs: ["option", "option-alias1", "option-alias2"],
20
- shorts: ["pts", "o"],
21
- parsing: optionValueFixedUniqueParsing,
22
- });
23
- const kFlag1 = readerArgs.registerOption({
24
- longs: ["flag1", "flag1-alias"],
25
- shorts: [],
26
- parsing: optionFlagParsing,
27
- });
28
- const kFlag2 = readerArgs.registerOption({
29
- longs: ["flag2"],
30
- shorts: ["fa2"],
31
- parsing: optionFlagParsing,
32
- });
33
-
34
- expect(readerArgs.consumePositional()).toStrictEqual(undefined);
35
-
36
- expect(readerArgs.getOptionValues(kOption)).toStrictEqual([
37
- { inlined: "1.1", separated: [] },
38
- { inlined: "1.2", separated: [] },
39
- { inlined: null, separated: ["1.3"] },
40
- { inlined: "1.4", separated: [] },
41
- { inlined: null, separated: ["1.5"] },
42
- { inlined: "1.6", separated: [] },
43
- ]);
44
- expect(readerArgs.getOptionValues(kFlag1)).toStrictEqual([
45
- { inlined: null, separated: [] },
46
- ]);
47
- expect(readerArgs.getOptionValues(kFlag2)).toStrictEqual([
48
- { inlined: "woops", separated: [] },
49
- { inlined: null, separated: [] },
50
- ]);
51
- });
52
-
53
- const optionFlagParsing: ReaderOptionParsing = {
54
- consumeShortGroup: false,
55
- consumeNextArg: () => false,
56
- };
57
-
58
- const optionValueFixedUniqueParsing: ReaderOptionParsing = {
59
- consumeShortGroup: true,
60
- consumeNextArg: (inlined, separated) =>
61
- inlined === null && separated.length === 0,
62
- };