@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.
Files changed (92) hide show
  1. package/Cancelled.js +44 -0
  2. package/CliAudience.js +178 -0
  3. package/CliColor.js +13 -19
  4. package/CliEnv.js +89 -0
  5. package/CliExit.js +1 -1
  6. package/CliFailure.js +302 -0
  7. package/CliInteractive.js +71 -0
  8. package/CliLinks.js +154 -0
  9. package/CliLog.js +346 -0
  10. package/CliLogger.js +34 -33
  11. package/CliMessage.js +80 -0
  12. package/CliPrompt.js +104 -0
  13. package/CliRuntime.js +110 -54
  14. package/CliTest.js +16 -0
  15. package/CliTheme.js +141 -0
  16. package/ConfigIssueRenderer.js +14 -33
  17. package/Doc.js +536 -0
  18. package/Fmt.js +133 -0
  19. package/GithubAnnotation.js +40 -0
  20. package/Glyphs.js +83 -0
  21. package/NotInteractive.js +42 -0
  22. package/README.md +145 -131
  23. package/Render.js +255 -0
  24. package/SchemaIssueRenderer.js +7 -10
  25. package/Status.js +166 -0
  26. package/TestTerminal.js +80 -0
  27. package/Token.js +69 -0
  28. package/index.d.ts +3089 -169
  29. package/index.js +19 -1
  30. package/internal/ansi.js +230 -0
  31. package/internal/autoFormat.js +34 -0
  32. package/internal/canPrompt.js +15 -0
  33. package/internal/counts.js +84 -0
  34. package/internal/diagnostics.js +32 -0
  35. package/internal/displayWidth.js +35 -0
  36. package/internal/failureTarget.js +195 -0
  37. package/internal/fallbackAnswer.js +18 -0
  38. package/internal/fileSink.js +62 -0
  39. package/internal/format.js +62 -7
  40. package/internal/layout.js +250 -0
  41. package/internal/linkScheme.js +30 -0
  42. package/internal/linkTarget.js +50 -0
  43. package/internal/logSafety.js +46 -0
  44. package/internal/renderAnsi.js +52 -0
  45. package/internal/renderDoc.js +320 -0
  46. package/internal/renderGithubLog.js +46 -0
  47. package/internal/renderMarkdown.js +368 -0
  48. package/internal/renderPlain.js +50 -0
  49. package/internal/scanAudience.js +106 -0
  50. package/internal/splitFrame.js +56 -0
  51. package/internal/wizardGate.js +18 -0
  52. package/package.json +40 -5
  53. package/testing.d.ts +88 -2
  54. package/testing.js +2 -1
  55. package/ui/CliUi.js +432 -0
  56. package/ui/CliUiLive.js +446 -0
  57. package/ui/Confirm.js +245 -0
  58. package/ui/DocView.js +74 -0
  59. package/ui/KeyHelp.js +62 -0
  60. package/ui/KeyTable.js +199 -0
  61. package/ui/MultiSelect.js +260 -0
  62. package/ui/Select.js +230 -0
  63. package/ui/Tabs.js +202 -0
  64. package/ui/TextInput.js +290 -0
  65. package/ui/Toggle.js +32 -0
  66. package/ui/UiKey.js +44 -0
  67. package/ui/UiProvider.js +60 -0
  68. package/ui/UiStreams.js +18 -0
  69. package/ui/UiTheme.js +119 -0
  70. package/ui/Viewport.js +204 -0
  71. package/ui/internal/ErrorBoundary.js +30 -0
  72. package/ui/internal/Holder.js +74 -0
  73. package/ui/internal/ScreenContext.js +52 -0
  74. package/ui/internal/UiProviders.js +21 -0
  75. package/ui/internal/ink.js +122 -0
  76. package/ui/internal/inkChalk.js +58 -0
  77. package/ui/internal/inkConsole.js +146 -0
  78. package/ui/internal/lazyView.js +74 -0
  79. package/ui/internal/lineText.js +19 -0
  80. package/ui/internal/mountPermit.js +16 -0
  81. package/ui/internal/perfDrain.js +33 -0
  82. package/ui/internal/processStreams.js +19 -0
  83. package/ui/internal/renderOptions.js +13 -0
  84. package/ui/testing/CliUiTest.js +760 -0
  85. package/ui/testing/fakeStreams.js +79 -0
  86. package/ui/testing/terminalModel.js +59 -0
  87. package/ui-testing-serializer.d.ts +14 -0
  88. package/ui-testing-serializer.js +33 -0
  89. package/ui-testing.d.ts +527 -0
  90. package/ui-testing.js +3 -0
  91. package/ui.d.ts +1790 -0
  92. 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 };