cli-kiss 0.0.1 → 0.0.3
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/index.d.ts +56 -18
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +4 -1
- package/src/lib/Command.ts +62 -42
- package/src/lib/{Processor.ts → Execution.ts} +15 -17
- package/src/lib/Grid.ts +56 -0
- package/src/lib/Option.ts +1 -1
- package/src/lib/Reader.ts +17 -11
- package/src/lib/Run.ts +26 -9
- package/src/lib/Typo.ts +73 -0
- package/src/lib/Usage.ts +90 -68
- package/tests/unit.Reader.commons.ts +2 -2
- package/tests/unit.command.run.ts +7 -7
- package/tests/unit.command.usage.ts +102 -21
package/src/lib/Command.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ArgumentUsage } from "./Argument";
|
|
2
|
+
import { Execution } from "./Execution";
|
|
2
3
|
import { OptionUsage } from "./Option";
|
|
3
|
-
import { Processor } from "./Processor";
|
|
4
4
|
import { ReaderTokenizer } from "./Reader";
|
|
5
5
|
|
|
6
6
|
export type Command<Context, Result> = {
|
|
7
|
-
|
|
7
|
+
getTitle(): string | undefined;
|
|
8
8
|
prepareRunner(
|
|
9
9
|
readerTokenizer: ReaderTokenizer,
|
|
10
10
|
): CommandRunner<Context, Result>;
|
|
@@ -16,48 +16,55 @@ export type CommandRunner<Context, Result> = {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
export type CommandUsage = {
|
|
19
|
-
breadcrumbs: Array<
|
|
20
|
-
|
|
19
|
+
breadcrumbs: Array<CommandUsageBreadcrumb>;
|
|
20
|
+
title: string;
|
|
21
|
+
description: Array<string> | undefined;
|
|
21
22
|
options: Array<OptionUsage>;
|
|
22
23
|
arguments: Array<ArgumentUsage>;
|
|
23
|
-
subcommands: Array<{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
subcommands: Array<{ name: string; title: string | undefined }>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type CommandUsageBreadcrumb = {
|
|
28
|
+
kind: "command" | "argument";
|
|
29
|
+
value: string;
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
export function command<Context, Result>(
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
metadata: {
|
|
34
|
+
title: string;
|
|
35
|
+
description?: Array<string>;
|
|
36
|
+
},
|
|
37
|
+
execution: Execution<Context, Result>,
|
|
32
38
|
): Command<Context, Result> {
|
|
33
39
|
return {
|
|
34
|
-
|
|
35
|
-
return
|
|
40
|
+
getTitle() {
|
|
41
|
+
return metadata.title;
|
|
36
42
|
},
|
|
37
43
|
prepareRunner(readerTokenizer: ReaderTokenizer) {
|
|
38
44
|
function computeUsage(): CommandUsage {
|
|
39
|
-
const
|
|
45
|
+
const executionUsage = execution.computeUsage();
|
|
40
46
|
return {
|
|
41
|
-
breadcrumbs:
|
|
42
|
-
(argument
|
|
47
|
+
breadcrumbs: executionUsage.arguments.map((argument) =>
|
|
48
|
+
breadcrumbArgument(argument.label),
|
|
43
49
|
),
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
title: metadata.title,
|
|
51
|
+
description: metadata.description,
|
|
52
|
+
options: executionUsage.options,
|
|
53
|
+
arguments: executionUsage.arguments,
|
|
47
54
|
subcommands: [],
|
|
48
55
|
};
|
|
49
56
|
}
|
|
50
57
|
try {
|
|
51
|
-
const
|
|
58
|
+
const executionResolver = execution.prepareResolver(readerTokenizer);
|
|
52
59
|
const lastPositional = readerTokenizer.consumePositional();
|
|
53
60
|
if (lastPositional !== undefined) {
|
|
54
61
|
throw Error(`Unprocessed positional: ${lastPositional}`);
|
|
55
62
|
}
|
|
56
|
-
const
|
|
63
|
+
const executionCallback = executionResolver();
|
|
57
64
|
return {
|
|
58
65
|
computeUsage,
|
|
59
66
|
async execute(context: Context) {
|
|
60
|
-
return await
|
|
67
|
+
return await executionCallback(context);
|
|
61
68
|
},
|
|
62
69
|
};
|
|
63
70
|
} catch (error) {
|
|
@@ -73,17 +80,20 @@ export function command<Context, Result>(
|
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
export function commandWithSubcommands<Context, Payload, Result>(
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
metadata: {
|
|
84
|
+
title: string;
|
|
85
|
+
description?: Array<string>;
|
|
86
|
+
},
|
|
87
|
+
execution: Execution<Context, Payload>,
|
|
78
88
|
subcommands: { [subcommand: Lowercase<string>]: Command<Payload, Result> },
|
|
79
89
|
): Command<Context, Result> {
|
|
80
90
|
return {
|
|
81
|
-
|
|
82
|
-
return
|
|
91
|
+
getTitle() {
|
|
92
|
+
return metadata.title;
|
|
83
93
|
},
|
|
84
94
|
prepareRunner(readerTokenizer: ReaderTokenizer) {
|
|
85
95
|
try {
|
|
86
|
-
const
|
|
96
|
+
const executionResolver = execution.prepareResolver(readerTokenizer);
|
|
87
97
|
const subcommandName = readerTokenizer.consumePositional();
|
|
88
98
|
if (subcommandName === undefined) {
|
|
89
99
|
throw new Error("Expected a subcommand");
|
|
@@ -94,44 +104,46 @@ export function commandWithSubcommands<Context, Payload, Result>(
|
|
|
94
104
|
throw new Error(`Unknown subcommand: ${subcommandName}`);
|
|
95
105
|
}
|
|
96
106
|
const subcommandRunner = subcommandInput.prepareRunner(readerTokenizer);
|
|
97
|
-
const
|
|
107
|
+
const executionCallback = executionResolver();
|
|
98
108
|
return {
|
|
99
109
|
computeUsage() {
|
|
100
|
-
const
|
|
110
|
+
const executionUsage = execution.computeUsage();
|
|
101
111
|
const subcommandUsage = subcommandRunner.computeUsage();
|
|
102
112
|
return {
|
|
103
|
-
breadcrumbs:
|
|
104
|
-
.map((argument) => argument.label)
|
|
105
|
-
.concat([subcommandName])
|
|
113
|
+
breadcrumbs: executionUsage.arguments
|
|
114
|
+
.map((argument) => breadcrumbArgument(argument.label))
|
|
115
|
+
.concat([breadcrumbCommand(subcommandName)])
|
|
106
116
|
.concat(subcommandUsage.breadcrumbs),
|
|
117
|
+
title: subcommandUsage.title,
|
|
107
118
|
description: subcommandUsage.description,
|
|
108
|
-
options:
|
|
109
|
-
arguments:
|
|
119
|
+
options: executionUsage.options.concat(subcommandUsage.options),
|
|
120
|
+
arguments: executionUsage.arguments.concat(
|
|
110
121
|
subcommandUsage.arguments,
|
|
111
122
|
),
|
|
112
123
|
subcommands: subcommandUsage.subcommands,
|
|
113
124
|
};
|
|
114
125
|
},
|
|
115
126
|
async execute(context: Context) {
|
|
116
|
-
const payload = await
|
|
127
|
+
const payload = await executionCallback(context);
|
|
117
128
|
return await subcommandRunner.execute(payload);
|
|
118
129
|
},
|
|
119
130
|
};
|
|
120
131
|
} catch (error) {
|
|
121
132
|
return {
|
|
122
133
|
computeUsage() {
|
|
123
|
-
const
|
|
134
|
+
const executionUsage = execution.computeUsage();
|
|
124
135
|
return {
|
|
125
|
-
breadcrumbs:
|
|
126
|
-
.map((argument) => argument.label)
|
|
127
|
-
.concat(["<SUBCOMMAND>"]),
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
breadcrumbs: executionUsage.arguments
|
|
137
|
+
.map((argument) => breadcrumbArgument(argument.label))
|
|
138
|
+
.concat([breadcrumbCommand("<SUBCOMMAND>")]),
|
|
139
|
+
title: metadata.title,
|
|
140
|
+
description: metadata.description,
|
|
141
|
+
options: executionUsage.options,
|
|
142
|
+
arguments: executionUsage.arguments,
|
|
131
143
|
subcommands: Object.entries(subcommands).map(
|
|
132
144
|
([name, subcommand]) => ({
|
|
133
145
|
name,
|
|
134
|
-
|
|
146
|
+
title: subcommand.getTitle(),
|
|
135
147
|
}),
|
|
136
148
|
),
|
|
137
149
|
};
|
|
@@ -144,3 +156,11 @@ export function commandWithSubcommands<Context, Payload, Result>(
|
|
|
144
156
|
},
|
|
145
157
|
};
|
|
146
158
|
}
|
|
159
|
+
|
|
160
|
+
function breadcrumbArgument(label: string): CommandUsageBreadcrumb {
|
|
161
|
+
return { kind: "argument", value: label };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function breadcrumbCommand(name: string): CommandUsageBreadcrumb {
|
|
165
|
+
return { kind: "command", value: name };
|
|
166
|
+
}
|
|
@@ -2,28 +2,28 @@ import { Argument, ArgumentUsage } from "./Argument";
|
|
|
2
2
|
import { Option, OptionUsage } from "./Option";
|
|
3
3
|
import { ReaderTokenizer } from "./Reader";
|
|
4
4
|
|
|
5
|
-
export type
|
|
6
|
-
computeUsage():
|
|
5
|
+
export type Execution<Context, Result> = {
|
|
6
|
+
computeUsage(): ExecutionUsage;
|
|
7
7
|
prepareResolver(
|
|
8
8
|
readerTokenizer: ReaderTokenizer,
|
|
9
|
-
):
|
|
9
|
+
): ExecutionResolver<Context, Result>;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
export type
|
|
12
|
+
export type ExecutionResolver<Context, Result> = () => ExecutionCallback<
|
|
13
13
|
Context,
|
|
14
14
|
Result
|
|
15
15
|
>;
|
|
16
16
|
|
|
17
|
-
export type
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
export type ExecutionCallback<Context, Result> = (
|
|
18
|
+
context: Context,
|
|
19
|
+
) => Promise<Result>;
|
|
20
20
|
|
|
21
|
-
export type
|
|
21
|
+
export type ExecutionUsage = {
|
|
22
22
|
options: Array<OptionUsage>;
|
|
23
23
|
arguments: Array<ArgumentUsage>;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
export function
|
|
26
|
+
export function execution<
|
|
27
27
|
Context,
|
|
28
28
|
Result,
|
|
29
29
|
Options extends { [option: string]: Option<any> },
|
|
@@ -43,7 +43,7 @@ export function processor<
|
|
|
43
43
|
};
|
|
44
44
|
},
|
|
45
45
|
) => Promise<Result>,
|
|
46
|
-
):
|
|
46
|
+
): Execution<Context, Result> {
|
|
47
47
|
return {
|
|
48
48
|
computeUsage() {
|
|
49
49
|
const optionsUsage = new Array<OptionUsage>();
|
|
@@ -73,13 +73,11 @@ export function processor<
|
|
|
73
73
|
for (const optionKey in optionsConsumers) {
|
|
74
74
|
optionsValues[optionKey] = optionsConsumers[optionKey]!();
|
|
75
75
|
}
|
|
76
|
-
return {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
82
|
-
},
|
|
76
|
+
return async (context: Context) => {
|
|
77
|
+
return await handler(context, {
|
|
78
|
+
options: optionsValues,
|
|
79
|
+
arguments: argumentsValues,
|
|
80
|
+
});
|
|
83
81
|
};
|
|
84
82
|
};
|
|
85
83
|
},
|
package/src/lib/Grid.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { TypoSupport, TypoText, typoPrintableString } from "./Typo";
|
|
2
|
+
|
|
3
|
+
export type Grid = Array<GridRow>;
|
|
4
|
+
export type GridRow = Array<GridCell>;
|
|
5
|
+
export type GridCell = Array<TypoText>;
|
|
6
|
+
|
|
7
|
+
export function gridToPrintableLines(grid: Grid, typoSupport: TypoSupport) {
|
|
8
|
+
const lines = new Array<string>();
|
|
9
|
+
const gridWidths = new Array<number>();
|
|
10
|
+
for (const gridRow of grid) {
|
|
11
|
+
for (
|
|
12
|
+
let gridColumnIndex = 0;
|
|
13
|
+
gridColumnIndex < gridRow.length;
|
|
14
|
+
gridColumnIndex++
|
|
15
|
+
) {
|
|
16
|
+
const gridCell = gridRow[gridColumnIndex]!;
|
|
17
|
+
const length = gridCellLength(gridCell);
|
|
18
|
+
if (
|
|
19
|
+
gridWidths[gridColumnIndex] === undefined ||
|
|
20
|
+
length > gridWidths[gridColumnIndex]!
|
|
21
|
+
) {
|
|
22
|
+
gridWidths[gridColumnIndex] = length;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
for (const gridRow of grid) {
|
|
27
|
+
const lineColumns = new Array<string>();
|
|
28
|
+
for (
|
|
29
|
+
let gridColumnIndex = 0;
|
|
30
|
+
gridColumnIndex < gridRow.length;
|
|
31
|
+
gridColumnIndex++
|
|
32
|
+
) {
|
|
33
|
+
const gridCell = gridRow[gridColumnIndex]!;
|
|
34
|
+
const parts = gridCell.map((text) =>
|
|
35
|
+
typoPrintableString(typoSupport, text),
|
|
36
|
+
);
|
|
37
|
+
if (gridColumnIndex < gridRow.length - 1) {
|
|
38
|
+
const length = gridCellLength(gridCell);
|
|
39
|
+
const padding = " ".repeat(gridWidths[gridColumnIndex]! - length);
|
|
40
|
+
lineColumns.push(parts.join("") + padding);
|
|
41
|
+
} else {
|
|
42
|
+
lineColumns.push(parts.join(""));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
lines.push(lineColumns.join(" "));
|
|
46
|
+
}
|
|
47
|
+
return lines;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function gridCellLength(cell: GridCell): number {
|
|
51
|
+
let length = 0;
|
|
52
|
+
for (const text of cell) {
|
|
53
|
+
length += text.value.length;
|
|
54
|
+
}
|
|
55
|
+
return length;
|
|
56
|
+
}
|
package/src/lib/Option.ts
CHANGED
|
@@ -122,7 +122,7 @@ export function optionSingleValue<Value>(definition: {
|
|
|
122
122
|
}
|
|
123
123
|
readerTokenizer.registerOption({ key, longs, shorts });
|
|
124
124
|
return () => {
|
|
125
|
-
// TODO - error handling
|
|
125
|
+
// TODO - smooth and beautiful error handling
|
|
126
126
|
const values = readerTokenizer.consumeOption(definition.long);
|
|
127
127
|
if (values.length > 1) {
|
|
128
128
|
throw new Error(
|
package/src/lib/Reader.ts
CHANGED
|
@@ -187,11 +187,9 @@ export class ReaderTokenizer {
|
|
|
187
187
|
const flagKey = this.#flagKeyByLong.get(long);
|
|
188
188
|
if (flagKey !== undefined) {
|
|
189
189
|
if (direct !== null) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (direct === "false") {
|
|
194
|
-
return this.#acknowledgeFlag(flagKey, false);
|
|
190
|
+
const value = asBoolean(direct);
|
|
191
|
+
if (value !== undefined) {
|
|
192
|
+
return this.#acknowledgeFlag(flagKey, value);
|
|
195
193
|
}
|
|
196
194
|
throw new Error(
|
|
197
195
|
`Invalid parameter for long flag: ${flagKey}, value: ${direct}`,
|
|
@@ -213,12 +211,9 @@ export class ReaderTokenizer {
|
|
|
213
211
|
const flagKey = this.#flagKeyByShort.get(short);
|
|
214
212
|
if (flagKey !== undefined) {
|
|
215
213
|
if (rest.startsWith("=")) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
if (rest === "=false") {
|
|
221
|
-
this.#acknowledgeFlag(flagKey, false);
|
|
214
|
+
const value = asBoolean(rest.slice(1));
|
|
215
|
+
if (value !== undefined) {
|
|
216
|
+
this.#acknowledgeFlag(flagKey, value);
|
|
222
217
|
return true;
|
|
223
218
|
}
|
|
224
219
|
throw new Error(
|
|
@@ -283,3 +278,14 @@ export class ReaderTokenizer {
|
|
|
283
278
|
}
|
|
284
279
|
}
|
|
285
280
|
}
|
|
281
|
+
|
|
282
|
+
function asBoolean(value: string): boolean | undefined {
|
|
283
|
+
const lower = value.toLowerCase();
|
|
284
|
+
if (lower === "true" || lower === "t" || lower === "y" || lower === "yes") {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
if (lower === "false" || lower === "f" || lower === "n" || lower === "no") {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
package/src/lib/Run.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { Command } from "./Command";
|
|
2
2
|
import { ReaderTokenizer } from "./Reader";
|
|
3
|
-
import {
|
|
3
|
+
import { typoInferSupport } from "./Typo";
|
|
4
|
+
import { usageToPrintableLines } from "./Usage";
|
|
4
5
|
|
|
5
6
|
export async function runWithArgv<Context, Result>(
|
|
6
7
|
argv: string[],
|
|
7
8
|
context: Context,
|
|
8
9
|
command: Command<Context, Result>,
|
|
9
|
-
cliInfo?: {
|
|
10
|
+
cliInfo?: {
|
|
11
|
+
name?: Lowercase<string>;
|
|
12
|
+
version?: string;
|
|
13
|
+
helpOnError?: boolean;
|
|
14
|
+
},
|
|
10
15
|
): Promise<Result> {
|
|
11
16
|
const cliName = cliInfo?.name ?? argv[1]!;
|
|
12
17
|
const readerTokenizer = new ReaderTokenizer(argv.slice(2));
|
|
13
|
-
readerTokenizer.registerFlag({
|
|
14
|
-
key: "help",
|
|
15
|
-
shorts: [],
|
|
16
|
-
longs: ["help"],
|
|
17
|
-
});
|
|
18
18
|
if (cliInfo?.version) {
|
|
19
19
|
readerTokenizer.registerFlag({
|
|
20
20
|
key: "version",
|
|
@@ -22,6 +22,11 @@ export async function runWithArgv<Context, Result>(
|
|
|
22
22
|
longs: ["version"],
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
+
readerTokenizer.registerFlag({
|
|
26
|
+
key: "help",
|
|
27
|
+
shorts: [],
|
|
28
|
+
longs: ["help"],
|
|
29
|
+
});
|
|
25
30
|
/*
|
|
26
31
|
// TODO - handle completions ?
|
|
27
32
|
readerTokenizer.registerFlag({
|
|
@@ -39,14 +44,26 @@ export async function runWithArgv<Context, Result>(
|
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
46
|
if (readerTokenizer.consumeFlag("help")) {
|
|
42
|
-
console.log(
|
|
47
|
+
console.log(
|
|
48
|
+
usageToPrintableLines({
|
|
49
|
+
cliName,
|
|
50
|
+
commandUsage: commandRunner.computeUsage(),
|
|
51
|
+
typoSupport: typoInferSupport(),
|
|
52
|
+
}).join("\n"),
|
|
53
|
+
);
|
|
43
54
|
process.exit(0);
|
|
44
55
|
}
|
|
45
56
|
try {
|
|
46
57
|
return await commandRunner.execute(context);
|
|
47
58
|
} catch (error) {
|
|
48
59
|
if (cliInfo?.helpOnError ?? true) {
|
|
49
|
-
console.log(
|
|
60
|
+
console.log(
|
|
61
|
+
usageToPrintableLines({
|
|
62
|
+
cliName,
|
|
63
|
+
commandUsage: commandRunner.computeUsage(),
|
|
64
|
+
typoSupport: typoInferSupport(),
|
|
65
|
+
}).join("\n"),
|
|
66
|
+
);
|
|
50
67
|
}
|
|
51
68
|
console.error(error);
|
|
52
69
|
process.exit(1);
|
package/src/lib/Typo.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export type TypoSupport = "none" | "tty" | "html" | "mock";
|
|
2
|
+
|
|
3
|
+
export type TypoText = {
|
|
4
|
+
value: string;
|
|
5
|
+
color?: keyof typeof colorCodes;
|
|
6
|
+
bold?: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function typoPrintableString(
|
|
10
|
+
typoSupport: TypoSupport,
|
|
11
|
+
typoText: TypoText,
|
|
12
|
+
): string {
|
|
13
|
+
if (typoSupport === "none") {
|
|
14
|
+
return typoText.value;
|
|
15
|
+
}
|
|
16
|
+
if (typoSupport === "tty") {
|
|
17
|
+
const colorStartCode = typoText.color ? colorCodes[typoText.color] : "";
|
|
18
|
+
const colorBoldCode = typoText.bold ? boldCode : "";
|
|
19
|
+
return `${colorStartCode}${colorBoldCode}${typoText.value}${resetCode}`;
|
|
20
|
+
}
|
|
21
|
+
if (typoSupport === "html") {
|
|
22
|
+
const colorStartTag = typoText.color
|
|
23
|
+
? `<span style="color: ${typoText.color}">`
|
|
24
|
+
: "";
|
|
25
|
+
const colorEndTag = typoText.color ? "</span>" : "";
|
|
26
|
+
const boldStartTag = typoText.bold ? "<b>" : "";
|
|
27
|
+
const boldEndTag = typoText.bold ? "</b>" : "";
|
|
28
|
+
return `${colorStartTag}${boldStartTag}${typoText.value}${boldEndTag}${colorEndTag}`;
|
|
29
|
+
}
|
|
30
|
+
if (typoSupport === "mock") {
|
|
31
|
+
if (typoText.color && typoText.bold) {
|
|
32
|
+
return `{${typoText.value}}@${typoText.color}+`;
|
|
33
|
+
}
|
|
34
|
+
if (typoText.color) {
|
|
35
|
+
return `{${typoText.value}}@${typoText.color}`;
|
|
36
|
+
}
|
|
37
|
+
if (typoText.bold) {
|
|
38
|
+
return `{${typoText.value}}+`;
|
|
39
|
+
}
|
|
40
|
+
return `{${typoText.value}}`;
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`Unknown typo support: ${typoSupport}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function typoInferSupport(): TypoSupport {
|
|
46
|
+
if (process.env) {
|
|
47
|
+
if (process.env["FORCE_COLOR"] === "0") {
|
|
48
|
+
return "none";
|
|
49
|
+
}
|
|
50
|
+
if (process.env["FORCE_COLOR"]) {
|
|
51
|
+
return "tty";
|
|
52
|
+
}
|
|
53
|
+
if ("NO_COLOR" in process.env) {
|
|
54
|
+
return "none";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!process || !process.stdout || !process.stdout.isTTY) {
|
|
58
|
+
return "none";
|
|
59
|
+
}
|
|
60
|
+
return "tty";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const resetCode = "\x1b[0m";
|
|
64
|
+
const boldCode = "\x1b[1m";
|
|
65
|
+
const colorCodes = {
|
|
66
|
+
red: "\x1b[31m",
|
|
67
|
+
green: "\x1b[32m",
|
|
68
|
+
yellow: "\x1b[33m",
|
|
69
|
+
blue: "\x1b[34m",
|
|
70
|
+
magenta: "\x1b[35m",
|
|
71
|
+
cyan: "\x1b[36m",
|
|
72
|
+
grey: "\x1b[90m",
|
|
73
|
+
};
|