@effected/cli 0.10.0 → 0.12.0
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/Cancelled.js +44 -0
- package/CliAudience.js +178 -0
- package/CliColor.js +13 -19
- package/CliEnv.js +89 -0
- package/CliExit.js +1 -1
- package/CliFailure.js +302 -0
- package/CliInteractive.js +71 -0
- package/CliLinks.js +154 -0
- package/CliLog.js +346 -0
- package/CliLogger.js +34 -33
- package/CliMessage.js +80 -0
- package/CliPrompt.js +104 -0
- package/CliRuntime.js +110 -54
- package/CliTest.js +16 -0
- package/CliTheme.js +141 -0
- package/ConfigIssueRenderer.js +14 -33
- package/Doc.js +536 -0
- package/Fmt.js +133 -0
- package/GithubAnnotation.js +40 -0
- package/Glyphs.js +83 -0
- package/NotInteractive.js +42 -0
- package/README.md +145 -131
- package/Render.js +255 -0
- package/SchemaIssueRenderer.js +7 -10
- package/Status.js +166 -0
- package/TestTerminal.js +80 -0
- package/Token.js +69 -0
- package/index.d.ts +3089 -169
- package/index.js +19 -1
- package/internal/ansi.js +230 -0
- package/internal/autoFormat.js +34 -0
- package/internal/canPrompt.js +15 -0
- package/internal/counts.js +84 -0
- package/internal/diagnostics.js +32 -0
- package/internal/displayWidth.js +35 -0
- package/internal/failureTarget.js +195 -0
- package/internal/fallbackAnswer.js +18 -0
- package/internal/fileSink.js +62 -0
- package/internal/format.js +62 -7
- package/internal/layout.js +250 -0
- package/internal/linkScheme.js +30 -0
- package/internal/linkTarget.js +50 -0
- package/internal/logSafety.js +46 -0
- package/internal/renderAnsi.js +52 -0
- package/internal/renderDoc.js +320 -0
- package/internal/renderGithubLog.js +46 -0
- package/internal/renderMarkdown.js +368 -0
- package/internal/renderPlain.js +50 -0
- package/internal/scanAudience.js +106 -0
- package/internal/splitFrame.js +56 -0
- package/internal/wizardGate.js +18 -0
- package/package.json +40 -5
- package/testing.d.ts +88 -2
- package/testing.js +2 -1
- package/ui/CliUi.js +432 -0
- package/ui/CliUiLive.js +446 -0
- package/ui/Confirm.js +245 -0
- package/ui/DocView.js +74 -0
- package/ui/KeyHelp.js +62 -0
- package/ui/KeyTable.js +199 -0
- package/ui/MultiSelect.js +260 -0
- package/ui/Select.js +230 -0
- package/ui/Tabs.js +202 -0
- package/ui/TextInput.js +290 -0
- package/ui/Toggle.js +32 -0
- package/ui/UiKey.js +44 -0
- package/ui/UiProvider.js +60 -0
- package/ui/UiStreams.js +18 -0
- package/ui/UiTheme.js +119 -0
- package/ui/Viewport.js +204 -0
- package/ui/internal/ErrorBoundary.js +30 -0
- package/ui/internal/Holder.js +74 -0
- package/ui/internal/ScreenContext.js +52 -0
- package/ui/internal/UiProviders.js +21 -0
- package/ui/internal/ink.js +122 -0
- package/ui/internal/inkChalk.js +58 -0
- package/ui/internal/inkConsole.js +146 -0
- package/ui/internal/lazyView.js +74 -0
- package/ui/internal/lineText.js +19 -0
- package/ui/internal/mountPermit.js +16 -0
- package/ui/internal/perfDrain.js +33 -0
- package/ui/internal/processStreams.js +19 -0
- package/ui/internal/renderOptions.js +13 -0
- package/ui/testing/CliUiTest.js +760 -0
- package/ui/testing/fakeStreams.js +79 -0
- package/ui/testing/terminalModel.js +59 -0
- package/ui-testing-serializer.d.ts +14 -0
- package/ui-testing-serializer.js +33 -0
- package/ui-testing.d.ts +527 -0
- package/ui-testing.js +3 -0
- package/ui.d.ts +1790 -0
- package/ui.js +17 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { PassThrough, Writable } from "node:stream";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/testing/fakeStreams.ts
|
|
4
|
+
const capture = (columns, rows, both, onWrite) => {
|
|
5
|
+
const chunks = [];
|
|
6
|
+
const stream = new Writable({ write(chunk, _encoding, callback) {
|
|
7
|
+
const text = chunk.toString();
|
|
8
|
+
chunks.push(text);
|
|
9
|
+
both.push(text);
|
|
10
|
+
onWrite?.(text);
|
|
11
|
+
callback();
|
|
12
|
+
} });
|
|
13
|
+
Object.assign(stream, {
|
|
14
|
+
isTTY: true,
|
|
15
|
+
columns,
|
|
16
|
+
rows
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
stream,
|
|
20
|
+
text: () => chunks.join("")
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Make in-memory stdin, stdout and stderr that satisfy Ink's stream contract: TTYs with a size, a recorded
|
|
25
|
+
* `setRawMode`, `ref` and `unref`, and captured writes.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* The third file licensed to touch Node, testing only: Ink's stream
|
|
29
|
+
* contract is Node's, so the fakes are `node:stream` streams rather than a hand-rolled emitter that would have to
|
|
30
|
+
* reproduce `readable`, `read()`, `setEncoding` and the write-callback barrier Ink waits on at unmount.
|
|
31
|
+
*
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
const makeFakeStreams = (options = {}) => {
|
|
35
|
+
const columns = options.columns ?? 80;
|
|
36
|
+
const rows = options.rows ?? 24;
|
|
37
|
+
const rawModes = [];
|
|
38
|
+
const stdin = new PassThrough();
|
|
39
|
+
Object.assign(stdin, {
|
|
40
|
+
isTTY: true,
|
|
41
|
+
setRawMode: (mode) => {
|
|
42
|
+
rawModes.push(mode);
|
|
43
|
+
return stdin;
|
|
44
|
+
},
|
|
45
|
+
ref: () => stdin,
|
|
46
|
+
unref: () => stdin
|
|
47
|
+
});
|
|
48
|
+
const both = [];
|
|
49
|
+
const stdout = capture(columns, rows, both, options.onStdoutWrite);
|
|
50
|
+
const stderr = capture(columns, rows, both);
|
|
51
|
+
return {
|
|
52
|
+
streams: {
|
|
53
|
+
stdin,
|
|
54
|
+
stdout: stdout.stream,
|
|
55
|
+
stderr: stderr.stream
|
|
56
|
+
},
|
|
57
|
+
rawModes,
|
|
58
|
+
stdout: stdout.text,
|
|
59
|
+
stderr: stderr.text,
|
|
60
|
+
written: () => both.join(""),
|
|
61
|
+
input: (data) => {
|
|
62
|
+
stdin.write(data);
|
|
63
|
+
},
|
|
64
|
+
resize: (nextColumns, nextRows) => {
|
|
65
|
+
Object.assign(stdout.stream, {
|
|
66
|
+
columns: nextColumns,
|
|
67
|
+
rows: nextRows
|
|
68
|
+
});
|
|
69
|
+
Object.assign(stderr.stream, {
|
|
70
|
+
columns: nextColumns,
|
|
71
|
+
rows: nextRows
|
|
72
|
+
});
|
|
73
|
+
stdout.stream.emit("resize");
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
//#endregion
|
|
79
|
+
export { makeFakeStreams };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/ui/testing/terminalModel.ts
|
|
2
|
+
const ESC = String.fromCharCode(27);
|
|
3
|
+
/**
|
|
4
|
+
* A small terminal model for the production render path: what a terminal shows after `written`, as its non-empty
|
|
5
|
+
* lines.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* It applies printable text, line feeds, the erase and cursor moves Ink's log-update writes (erase line, cursor up,
|
|
9
|
+
* cursor to column one), and the clears of Ink's clear-terminal frame: `ESC[2J` blanks the visible screen, `ESC[3J`
|
|
10
|
+
* drops the scrollback above it, `ESC[H` homes the cursor to the screen's top left. The visible screen is the last
|
|
11
|
+
* `rows` lines; with `rows` unknown, the whole buffer counts as the screen, so a clear takes everything. Every other
|
|
12
|
+
* escape is ignored. It does not wrap a line wider than the terminal. Shared by `CliUiTest.live`'s transcript and the
|
|
13
|
+
* kit's own production-path tests.
|
|
14
|
+
*
|
|
15
|
+
* @param written - every byte written to the terminal
|
|
16
|
+
* @param rows - the terminal's height, which decides how much of the buffer a clear takes as the screen
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
const screenAfter = (written, rows) => {
|
|
21
|
+
const lines = [""];
|
|
22
|
+
let row = 0;
|
|
23
|
+
let column = 0;
|
|
24
|
+
const screenTop = () => rows === void 0 ? 0 : Math.max(0, lines.length - rows);
|
|
25
|
+
const sequence = new RegExp(`${ESC}\\[([0-9;?]*)([A-Za-z])|${ESC}\\][^\\u0007]*\\u0007|([\\s\\S])`, "g");
|
|
26
|
+
for (const match of written.matchAll(sequence)) {
|
|
27
|
+
const [, params, command, character] = match;
|
|
28
|
+
if (character !== void 0) {
|
|
29
|
+
if (character === "\n") {
|
|
30
|
+
row++;
|
|
31
|
+
column = 0;
|
|
32
|
+
while (lines.length <= row) lines.push("");
|
|
33
|
+
} else if (character === "\r") column = 0;
|
|
34
|
+
else if (character >= " ") {
|
|
35
|
+
const line = lines[row] ?? "";
|
|
36
|
+
lines[row] = `${line.slice(0, column).padEnd(column)}${character}${line.slice(column + 1)}`;
|
|
37
|
+
column++;
|
|
38
|
+
}
|
|
39
|
+
} else if (command === "K") lines[row] = "";
|
|
40
|
+
else if (command === "A") row = Math.max(0, row - Number(params === "" ? 1 : params));
|
|
41
|
+
else if (command === "G") column = 0;
|
|
42
|
+
else if (command === "J" && params === "2") for (let index = screenTop(); index < lines.length; index++) lines[index] = "";
|
|
43
|
+
else if (command === "J" && params === "3") {
|
|
44
|
+
const top = screenTop();
|
|
45
|
+
lines.splice(0, top);
|
|
46
|
+
row = Math.max(0, row - top);
|
|
47
|
+
if (lines.length === 0) lines.push("");
|
|
48
|
+
} else if (command === "H") {
|
|
49
|
+
const [line, col] = params === "" ? [1, 1] : params.split(";").map((part) => Number(part === "" ? 1 : part));
|
|
50
|
+
row = screenTop() + (line ?? 1) - 1;
|
|
51
|
+
column = (col ?? 1) - 1;
|
|
52
|
+
while (lines.length <= row) lines.push("");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return lines.map((line) => line.trimEnd()).filter((line) => line !== "");
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
//#endregion
|
|
59
|
+
export { screenAfter };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/ui-testing-serializer.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* `CliUiTest.serializer`: it claims a string carrying escapes or token markup and prints it as token markup with each
|
|
4
|
+
* line's trailing spaces trimmed. See `CliUiTest.serializer` for what it claims.
|
|
5
|
+
*
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
declare const serializer: {
|
|
9
|
+
readonly test: (value: unknown) => boolean;
|
|
10
|
+
readonly serialize: (value: unknown) => string;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { serializer as default };
|
|
14
|
+
//# sourceMappingURL=ui-testing-serializer.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { CliUiTest } from "./ui/testing/CliUiTest.js";
|
|
2
|
+
|
|
3
|
+
//#region src/ui-testing-serializer.ts
|
|
4
|
+
/**
|
|
5
|
+
* `CliUiTest.serializer` as a module's default export, for Vitest's `snapshotSerializers` config.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Vitest's `test.snapshotSerializers` takes module paths whose default export is a serializer, so registering this
|
|
9
|
+
* one needs no `expect.addSnapshotSerializer` call and no shim file of your own:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // vitest.config.ts
|
|
13
|
+
* import { defineConfig } from "vitest/config"
|
|
14
|
+
*
|
|
15
|
+
* export default defineConfig({
|
|
16
|
+
* test: { snapshotSerializers: ["@effected/cli/ui/testing/serializer"] },
|
|
17
|
+
* })
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* A separate entrypoint so a program's runtime import graph never loads test code.
|
|
21
|
+
*
|
|
22
|
+
* @packageDocumentation
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* `CliUiTest.serializer`: it claims a string carrying escapes or token markup and prints it as token markup with each
|
|
26
|
+
* line's trailing spaces trimmed. See `CliUiTest.serializer` for what it claims.
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
const serializer = CliUiTest.serializer;
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
export { serializer as default };
|