@rushstack/terminal 0.21.0 → 0.22.1

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 (172) hide show
  1. package/CHANGELOG.json +40 -0
  2. package/CHANGELOG.md +15 -1
  3. package/dist/tsdoc-metadata.json +1 -1
  4. package/lib-esm/AnsiEscape.js +131 -0
  5. package/lib-esm/AnsiEscape.js.map +1 -0
  6. package/lib-esm/CallbackWritable.js +27 -0
  7. package/lib-esm/CallbackWritable.js.map +1 -0
  8. package/lib-esm/Colorize.js +158 -0
  9. package/lib-esm/Colorize.js.map +1 -0
  10. package/lib-esm/ConsoleTerminalProvider.js +58 -0
  11. package/lib-esm/ConsoleTerminalProvider.js.map +1 -0
  12. package/lib-esm/DiscardStdoutTransform.js +90 -0
  13. package/lib-esm/DiscardStdoutTransform.js.map +1 -0
  14. package/lib-esm/IProblemCollector.js +4 -0
  15. package/lib-esm/IProblemCollector.js.map +1 -0
  16. package/lib-esm/ITerminal.js +4 -0
  17. package/lib-esm/ITerminal.js.map +1 -0
  18. package/lib-esm/ITerminalChunk.js +18 -0
  19. package/lib-esm/ITerminalChunk.js.map +1 -0
  20. package/lib-esm/ITerminalProvider.js +29 -0
  21. package/lib-esm/ITerminalProvider.js.map +1 -0
  22. package/lib-esm/MockWritable.js +28 -0
  23. package/lib-esm/MockWritable.js.map +1 -0
  24. package/lib-esm/NoOpTerminalProvider.js +30 -0
  25. package/lib-esm/NoOpTerminalProvider.js.map +1 -0
  26. package/lib-esm/NormalizeNewlinesTextRewriter.js +66 -0
  27. package/lib-esm/NormalizeNewlinesTextRewriter.js.map +1 -0
  28. package/lib-esm/PrefixProxyTerminalProvider.js +58 -0
  29. package/lib-esm/PrefixProxyTerminalProvider.js.map +1 -0
  30. package/lib-esm/PrintUtilities.js +146 -0
  31. package/lib-esm/PrintUtilities.js.map +1 -0
  32. package/lib-esm/ProblemCollector.js +85 -0
  33. package/lib-esm/ProblemCollector.js.map +1 -0
  34. package/lib-esm/RemoveColorsTextRewriter.js +91 -0
  35. package/lib-esm/RemoveColorsTextRewriter.js.map +1 -0
  36. package/lib-esm/SplitterTransform.js +74 -0
  37. package/lib-esm/SplitterTransform.js.map +1 -0
  38. package/lib-esm/StdioLineTransform.js +111 -0
  39. package/lib-esm/StdioLineTransform.js.map +1 -0
  40. package/lib-esm/StdioSummarizer.js +95 -0
  41. package/lib-esm/StdioSummarizer.js.map +1 -0
  42. package/lib-esm/StdioWritable.js +27 -0
  43. package/lib-esm/StdioWritable.js.map +1 -0
  44. package/lib-esm/StringBufferTerminalProvider.js +185 -0
  45. package/lib-esm/StringBufferTerminalProvider.js.map +1 -0
  46. package/lib-esm/Terminal.js +150 -0
  47. package/lib-esm/Terminal.js.map +1 -0
  48. package/lib-esm/TerminalStreamWritable.js +49 -0
  49. package/lib-esm/TerminalStreamWritable.js.map +1 -0
  50. package/lib-esm/TerminalTransform.js +53 -0
  51. package/lib-esm/TerminalTransform.js.map +1 -0
  52. package/lib-esm/TerminalWritable.js +119 -0
  53. package/lib-esm/TerminalWritable.js.map +1 -0
  54. package/lib-esm/TextRewriter.js +35 -0
  55. package/lib-esm/TextRewriter.js.map +1 -0
  56. package/lib-esm/TextRewriterTransform.js +90 -0
  57. package/lib-esm/TextRewriterTransform.js.map +1 -0
  58. package/lib-esm/index.js +38 -0
  59. package/lib-esm/index.js.map +1 -0
  60. package/package.json +30 -6
  61. /package/{lib → lib-commonjs}/AnsiEscape.js +0 -0
  62. /package/{lib → lib-commonjs}/AnsiEscape.js.map +0 -0
  63. /package/{lib → lib-commonjs}/CallbackWritable.js +0 -0
  64. /package/{lib → lib-commonjs}/CallbackWritable.js.map +0 -0
  65. /package/{lib → lib-commonjs}/Colorize.js +0 -0
  66. /package/{lib → lib-commonjs}/Colorize.js.map +0 -0
  67. /package/{lib → lib-commonjs}/ConsoleTerminalProvider.js +0 -0
  68. /package/{lib → lib-commonjs}/ConsoleTerminalProvider.js.map +0 -0
  69. /package/{lib → lib-commonjs}/DiscardStdoutTransform.js +0 -0
  70. /package/{lib → lib-commonjs}/DiscardStdoutTransform.js.map +0 -0
  71. /package/{lib → lib-commonjs}/IProblemCollector.js +0 -0
  72. /package/{lib → lib-commonjs}/IProblemCollector.js.map +0 -0
  73. /package/{lib → lib-commonjs}/ITerminal.js +0 -0
  74. /package/{lib → lib-commonjs}/ITerminal.js.map +0 -0
  75. /package/{lib → lib-commonjs}/ITerminalChunk.js +0 -0
  76. /package/{lib → lib-commonjs}/ITerminalChunk.js.map +0 -0
  77. /package/{lib → lib-commonjs}/ITerminalProvider.js +0 -0
  78. /package/{lib → lib-commonjs}/ITerminalProvider.js.map +0 -0
  79. /package/{lib → lib-commonjs}/MockWritable.js +0 -0
  80. /package/{lib → lib-commonjs}/MockWritable.js.map +0 -0
  81. /package/{lib → lib-commonjs}/NoOpTerminalProvider.js +0 -0
  82. /package/{lib → lib-commonjs}/NoOpTerminalProvider.js.map +0 -0
  83. /package/{lib → lib-commonjs}/NormalizeNewlinesTextRewriter.js +0 -0
  84. /package/{lib → lib-commonjs}/NormalizeNewlinesTextRewriter.js.map +0 -0
  85. /package/{lib → lib-commonjs}/PrefixProxyTerminalProvider.js +0 -0
  86. /package/{lib → lib-commonjs}/PrefixProxyTerminalProvider.js.map +0 -0
  87. /package/{lib → lib-commonjs}/PrintUtilities.js +0 -0
  88. /package/{lib → lib-commonjs}/PrintUtilities.js.map +0 -0
  89. /package/{lib → lib-commonjs}/ProblemCollector.js +0 -0
  90. /package/{lib → lib-commonjs}/ProblemCollector.js.map +0 -0
  91. /package/{lib → lib-commonjs}/RemoveColorsTextRewriter.js +0 -0
  92. /package/{lib → lib-commonjs}/RemoveColorsTextRewriter.js.map +0 -0
  93. /package/{lib → lib-commonjs}/SplitterTransform.js +0 -0
  94. /package/{lib → lib-commonjs}/SplitterTransform.js.map +0 -0
  95. /package/{lib → lib-commonjs}/StdioLineTransform.js +0 -0
  96. /package/{lib → lib-commonjs}/StdioLineTransform.js.map +0 -0
  97. /package/{lib → lib-commonjs}/StdioSummarizer.js +0 -0
  98. /package/{lib → lib-commonjs}/StdioSummarizer.js.map +0 -0
  99. /package/{lib → lib-commonjs}/StdioWritable.js +0 -0
  100. /package/{lib → lib-commonjs}/StdioWritable.js.map +0 -0
  101. /package/{lib → lib-commonjs}/StringBufferTerminalProvider.js +0 -0
  102. /package/{lib → lib-commonjs}/StringBufferTerminalProvider.js.map +0 -0
  103. /package/{lib → lib-commonjs}/Terminal.js +0 -0
  104. /package/{lib → lib-commonjs}/Terminal.js.map +0 -0
  105. /package/{lib → lib-commonjs}/TerminalStreamWritable.js +0 -0
  106. /package/{lib → lib-commonjs}/TerminalStreamWritable.js.map +0 -0
  107. /package/{lib → lib-commonjs}/TerminalTransform.js +0 -0
  108. /package/{lib → lib-commonjs}/TerminalTransform.js.map +0 -0
  109. /package/{lib → lib-commonjs}/TerminalWritable.js +0 -0
  110. /package/{lib → lib-commonjs}/TerminalWritable.js.map +0 -0
  111. /package/{lib → lib-commonjs}/TextRewriter.js +0 -0
  112. /package/{lib → lib-commonjs}/TextRewriter.js.map +0 -0
  113. /package/{lib → lib-commonjs}/TextRewriterTransform.js +0 -0
  114. /package/{lib → lib-commonjs}/TextRewriterTransform.js.map +0 -0
  115. /package/{lib → lib-commonjs}/index.js +0 -0
  116. /package/{lib → lib-commonjs}/index.js.map +0 -0
  117. /package/{lib → lib-dts}/AnsiEscape.d.ts +0 -0
  118. /package/{lib → lib-dts}/AnsiEscape.d.ts.map +0 -0
  119. /package/{lib → lib-dts}/CallbackWritable.d.ts +0 -0
  120. /package/{lib → lib-dts}/CallbackWritable.d.ts.map +0 -0
  121. /package/{lib → lib-dts}/Colorize.d.ts +0 -0
  122. /package/{lib → lib-dts}/Colorize.d.ts.map +0 -0
  123. /package/{lib → lib-dts}/ConsoleTerminalProvider.d.ts +0 -0
  124. /package/{lib → lib-dts}/ConsoleTerminalProvider.d.ts.map +0 -0
  125. /package/{lib → lib-dts}/DiscardStdoutTransform.d.ts +0 -0
  126. /package/{lib → lib-dts}/DiscardStdoutTransform.d.ts.map +0 -0
  127. /package/{lib → lib-dts}/IProblemCollector.d.ts +0 -0
  128. /package/{lib → lib-dts}/IProblemCollector.d.ts.map +0 -0
  129. /package/{lib → lib-dts}/ITerminal.d.ts +0 -0
  130. /package/{lib → lib-dts}/ITerminal.d.ts.map +0 -0
  131. /package/{lib → lib-dts}/ITerminalChunk.d.ts +0 -0
  132. /package/{lib → lib-dts}/ITerminalChunk.d.ts.map +0 -0
  133. /package/{lib → lib-dts}/ITerminalProvider.d.ts +0 -0
  134. /package/{lib → lib-dts}/ITerminalProvider.d.ts.map +0 -0
  135. /package/{lib → lib-dts}/MockWritable.d.ts +0 -0
  136. /package/{lib → lib-dts}/MockWritable.d.ts.map +0 -0
  137. /package/{lib → lib-dts}/NoOpTerminalProvider.d.ts +0 -0
  138. /package/{lib → lib-dts}/NoOpTerminalProvider.d.ts.map +0 -0
  139. /package/{lib → lib-dts}/NormalizeNewlinesTextRewriter.d.ts +0 -0
  140. /package/{lib → lib-dts}/NormalizeNewlinesTextRewriter.d.ts.map +0 -0
  141. /package/{lib → lib-dts}/PrefixProxyTerminalProvider.d.ts +0 -0
  142. /package/{lib → lib-dts}/PrefixProxyTerminalProvider.d.ts.map +0 -0
  143. /package/{lib → lib-dts}/PrintUtilities.d.ts +0 -0
  144. /package/{lib → lib-dts}/PrintUtilities.d.ts.map +0 -0
  145. /package/{lib → lib-dts}/ProblemCollector.d.ts +0 -0
  146. /package/{lib → lib-dts}/ProblemCollector.d.ts.map +0 -0
  147. /package/{lib → lib-dts}/RemoveColorsTextRewriter.d.ts +0 -0
  148. /package/{lib → lib-dts}/RemoveColorsTextRewriter.d.ts.map +0 -0
  149. /package/{lib → lib-dts}/SplitterTransform.d.ts +0 -0
  150. /package/{lib → lib-dts}/SplitterTransform.d.ts.map +0 -0
  151. /package/{lib → lib-dts}/StdioLineTransform.d.ts +0 -0
  152. /package/{lib → lib-dts}/StdioLineTransform.d.ts.map +0 -0
  153. /package/{lib → lib-dts}/StdioSummarizer.d.ts +0 -0
  154. /package/{lib → lib-dts}/StdioSummarizer.d.ts.map +0 -0
  155. /package/{lib → lib-dts}/StdioWritable.d.ts +0 -0
  156. /package/{lib → lib-dts}/StdioWritable.d.ts.map +0 -0
  157. /package/{lib → lib-dts}/StringBufferTerminalProvider.d.ts +0 -0
  158. /package/{lib → lib-dts}/StringBufferTerminalProvider.d.ts.map +0 -0
  159. /package/{lib → lib-dts}/Terminal.d.ts +0 -0
  160. /package/{lib → lib-dts}/Terminal.d.ts.map +0 -0
  161. /package/{lib → lib-dts}/TerminalStreamWritable.d.ts +0 -0
  162. /package/{lib → lib-dts}/TerminalStreamWritable.d.ts.map +0 -0
  163. /package/{lib → lib-dts}/TerminalTransform.d.ts +0 -0
  164. /package/{lib → lib-dts}/TerminalTransform.d.ts.map +0 -0
  165. /package/{lib → lib-dts}/TerminalWritable.d.ts +0 -0
  166. /package/{lib → lib-dts}/TerminalWritable.d.ts.map +0 -0
  167. /package/{lib → lib-dts}/TextRewriter.d.ts +0 -0
  168. /package/{lib → lib-dts}/TextRewriter.d.ts.map +0 -0
  169. /package/{lib → lib-dts}/TextRewriterTransform.d.ts +0 -0
  170. /package/{lib → lib-dts}/TextRewriterTransform.d.ts.map +0 -0
  171. /package/{lib → lib-dts}/index.d.ts +0 -0
  172. /package/{lib → lib-dts}/index.d.ts.map +0 -0
@@ -0,0 +1,91 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import { AnsiEscape } from './AnsiEscape';
4
+ import { TextRewriter } from './TextRewriter';
5
+ var State;
6
+ (function (State) {
7
+ // Buffer is empty, and we're looking for the ESC character
8
+ State[State["Start"] = 0] = "Start";
9
+ // We're looking for the '[' character
10
+ State[State["AwaitingBracket"] = 1] = "AwaitingBracket";
11
+ // We're reading the codes after the '[' character
12
+ State[State["ReadingCodes"] = 2] = "ReadingCodes";
13
+ })(State || (State = {}));
14
+ /**
15
+ * For use with {@link TextRewriterTransform}, this rewriter removes ANSI escape codes
16
+ * including colored text.
17
+ *
18
+ * @remarks
19
+ * The implementation also removes other ANSI escape codes such as cursor positioning.
20
+ * The specific set of affected codes may be adjusted in the future.
21
+ *
22
+ * @public
23
+ */
24
+ export class RemoveColorsTextRewriter extends TextRewriter {
25
+ initialize() {
26
+ return { buffer: '', parseState: State.Start };
27
+ }
28
+ process(unknownState, text) {
29
+ const state = unknownState;
30
+ // We will be matching AnsiEscape._csiRegExp:
31
+ //
32
+ // /\x1b\[([\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e])/gu
33
+ //
34
+ const ESC = '\x1b';
35
+ let result = '';
36
+ let index = 0;
37
+ while (index < text.length) {
38
+ if (state.parseState === State.Start) {
39
+ // The buffer is empty, which means we haven't found anything yet
40
+ const csiIndex = text.indexOf(ESC, index);
41
+ if (csiIndex < 0) {
42
+ // We reached the end of "text" without finding another CSI prefix
43
+ result += text.substring(index);
44
+ break;
45
+ }
46
+ // Append everything up to the CSI prefix
47
+ result += text.substring(index, csiIndex);
48
+ // Save the partial match in the buffer
49
+ state.buffer = ESC;
50
+ index = csiIndex + 1;
51
+ state.parseState = State.AwaitingBracket;
52
+ }
53
+ else {
54
+ // The buffer has characters, which means we started matching a partial sequence
55
+ // Read another character into the buffer
56
+ const c = text[index];
57
+ ++index;
58
+ state.buffer += c;
59
+ if (state.parseState === State.AwaitingBracket) {
60
+ if (c === '[') {
61
+ state.parseState = State.ReadingCodes;
62
+ }
63
+ else {
64
+ // Failed to match, so append the buffer and start over
65
+ result += state.buffer;
66
+ state.buffer = '';
67
+ state.parseState = State.Start;
68
+ }
69
+ }
70
+ else {
71
+ // state.state === State.ReadingCodes
72
+ // Stop when we reach any character that is not [\x30-\x3f] or [\x20-\x2f]
73
+ const code = c.charCodeAt(0);
74
+ if (code < 0x20 || code > 0x3f) {
75
+ result += AnsiEscape.removeCodes(state.buffer);
76
+ state.buffer = '';
77
+ state.parseState = State.Start;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return result;
83
+ }
84
+ close(unknownState) {
85
+ const state = unknownState;
86
+ const result = state.buffer;
87
+ state.buffer = '';
88
+ return result;
89
+ }
90
+ }
91
+ //# sourceMappingURL=RemoveColorsTextRewriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RemoveColorsTextRewriter.js","sourceRoot":"","sources":["../src/RemoveColorsTextRewriter.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { AnsiEscape } from './AnsiEscape';\nimport { TextRewriter, type TextRewriterState } from './TextRewriter';\n\nenum State {\n // Buffer is empty, and we're looking for the ESC character\n Start,\n // We're looking for the '[' character\n AwaitingBracket,\n // We're reading the codes after the '[' character\n ReadingCodes\n}\n\ninterface IRemoveColorsTextRewriterState extends TextRewriterState {\n buffer: string;\n parseState: State;\n}\n\n/**\n * For use with {@link TextRewriterTransform}, this rewriter removes ANSI escape codes\n * including colored text.\n *\n * @remarks\n * The implementation also removes other ANSI escape codes such as cursor positioning.\n * The specific set of affected codes may be adjusted in the future.\n *\n * @public\n */\nexport class RemoveColorsTextRewriter extends TextRewriter {\n public initialize(): TextRewriterState {\n return { buffer: '', parseState: State.Start } as IRemoveColorsTextRewriterState;\n }\n\n public process(unknownState: TextRewriterState, text: string): string {\n const state: IRemoveColorsTextRewriterState = unknownState as IRemoveColorsTextRewriterState;\n\n // We will be matching AnsiEscape._csiRegExp:\n //\n // /\\x1b\\[([\\x30-\\x3f]*[\\x20-\\x2f]*[\\x40-\\x7e])/gu\n //\n const ESC: string = '\\x1b';\n\n let result: string = '';\n let index: number = 0;\n\n while (index < text.length) {\n if (state.parseState === State.Start) {\n // The buffer is empty, which means we haven't found anything yet\n\n const csiIndex: number = text.indexOf(ESC, index);\n if (csiIndex < 0) {\n // We reached the end of \"text\" without finding another CSI prefix\n result += text.substring(index);\n break;\n }\n\n // Append everything up to the CSI prefix\n result += text.substring(index, csiIndex);\n\n // Save the partial match in the buffer\n state.buffer = ESC;\n index = csiIndex + 1;\n state.parseState = State.AwaitingBracket;\n } else {\n // The buffer has characters, which means we started matching a partial sequence\n\n // Read another character into the buffer\n const c: string = text[index];\n ++index;\n state.buffer += c;\n\n if (state.parseState === State.AwaitingBracket) {\n if (c === '[') {\n state.parseState = State.ReadingCodes;\n } else {\n // Failed to match, so append the buffer and start over\n result += state.buffer;\n state.buffer = '';\n state.parseState = State.Start;\n }\n } else {\n // state.state === State.ReadingCodes\n\n // Stop when we reach any character that is not [\\x30-\\x3f] or [\\x20-\\x2f]\n const code: number = c.charCodeAt(0);\n if (code < 0x20 || code > 0x3f) {\n result += AnsiEscape.removeCodes(state.buffer);\n state.buffer = '';\n state.parseState = State.Start;\n }\n }\n }\n }\n\n return result;\n }\n\n public close(unknownState: TextRewriterState): string {\n const state: IRemoveColorsTextRewriterState = unknownState as IRemoveColorsTextRewriterState;\n\n const result: string = state.buffer;\n state.buffer = '';\n return result;\n }\n}\n"]}
@@ -0,0 +1,74 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import { TerminalWritable } from './TerminalWritable';
4
+ /**
5
+ * Use this instead of {@link TerminalTransform} if you need to output `ITerminalChunk`
6
+ * data to more than one destination.
7
+ *
8
+ * @remarks
9
+ *
10
+ * Splitting streams complicates the pipeline topology and can make debugging more difficult.
11
+ * For this reason, it is modeled as an explicit `SplitterTransform` node, rather than
12
+ * as a built-in feature of `TerminalTransform`.
13
+ *
14
+ * @public
15
+ */
16
+ export class SplitterTransform extends TerminalWritable {
17
+ constructor(options) {
18
+ super();
19
+ this._destinations = new Set(options.destinations);
20
+ }
21
+ get destinations() {
22
+ return this._destinations;
23
+ }
24
+ /**
25
+ * Adds a destination to the set of destinations. Duplicates are ignored.
26
+ * Only new chunks received after the destination is added will be sent to it.
27
+ * @param destination - The destination to add.
28
+ */
29
+ addDestination(destination) {
30
+ this._destinations.add(destination);
31
+ }
32
+ /**
33
+ * Removes a destination from the set of destinations. It will no longer receive chunks, and will be closed, unless
34
+ * `destination.preventAutoclose` is set to `true`.
35
+ * @param destination - The destination to remove.
36
+ * @param close - If `true` (default), the destination will be closed when removed, unless `destination.preventAutoclose` is set to `true`.
37
+ * @returns `true` if the destination was removed, `false` if it was not found.
38
+ * @remarks
39
+ * If the destination is not found, it will not be closed.
40
+ */
41
+ removeDestination(destination, close = true) {
42
+ if (this._destinations.delete(destination)) {
43
+ if (close && !destination.preventAutoclose) {
44
+ destination.close();
45
+ }
46
+ return true;
47
+ }
48
+ return false;
49
+ }
50
+ onWriteChunk(chunk) {
51
+ for (const destination of this._destinations) {
52
+ destination.writeChunk(chunk);
53
+ }
54
+ }
55
+ onClose() {
56
+ const errors = [];
57
+ // If an exception is thrown, try to ensure that the other destinations get closed properly
58
+ for (const destination of this._destinations) {
59
+ if (!destination.preventAutoclose) {
60
+ try {
61
+ destination.close();
62
+ }
63
+ catch (error) {
64
+ errors.push(error);
65
+ }
66
+ }
67
+ }
68
+ this._destinations.clear();
69
+ if (errors.length > 0) {
70
+ throw errors[0];
71
+ }
72
+ }
73
+ }
74
+ //# sourceMappingURL=SplitterTransform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SplitterTransform.js","sourceRoot":"","sources":["../src/SplitterTransform.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,2DAA2D;AAE3D,OAAO,EAAE,gBAAgB,EAAiC,MAAM,oBAAoB,CAAC;AAerF;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,iBAAkB,SAAQ,gBAAgB;IAGrD,YAAmB,OAAkC;QACnD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,aAAa,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACrD,CAAC;IAED,IAAW,YAAY;QACrB,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACI,cAAc,CAAC,WAA6B;QACjD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;OAQG;IACI,iBAAiB,CAAC,WAA6B,EAAE,QAAiB,IAAI;QAC3E,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,IAAI,KAAK,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC;gBAC3C,WAAW,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAES,YAAY,CAAC,KAAqB;QAC1C,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YAC7C,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAES,OAAO;QACf,MAAM,MAAM,GAAY,EAAE,CAAC;QAE3B,2FAA2F;QAC3F,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YAC7C,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC;gBAClC,IAAI,CAAC;oBACH,WAAW,CAAC,KAAK,EAAE,CAAC;gBACtB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,CAAC,KAAc,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAE3B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;CACF","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { TerminalWritable, type ITerminalWritableOptions } from './TerminalWritable';\nimport type { ITerminalChunk } from './ITerminalChunk';\n\n/**\n * Constructor options for {@link SplitterTransform}.\n *\n * @public\n */\nexport interface ISplitterTransformOptions extends ITerminalWritableOptions {\n /**\n * Each input chunk will be passed to each destination in the iterable.\n */\n destinations: Iterable<TerminalWritable>;\n}\n\n/**\n * Use this instead of {@link TerminalTransform} if you need to output `ITerminalChunk`\n * data to more than one destination.\n *\n * @remarks\n *\n * Splitting streams complicates the pipeline topology and can make debugging more difficult.\n * For this reason, it is modeled as an explicit `SplitterTransform` node, rather than\n * as a built-in feature of `TerminalTransform`.\n *\n * @public\n */\nexport class SplitterTransform extends TerminalWritable {\n private readonly _destinations: Set<TerminalWritable>;\n\n public constructor(options: ISplitterTransformOptions) {\n super();\n this._destinations = new Set(options.destinations);\n }\n\n public get destinations(): ReadonlySet<TerminalWritable> {\n return this._destinations;\n }\n\n /**\n * Adds a destination to the set of destinations. Duplicates are ignored.\n * Only new chunks received after the destination is added will be sent to it.\n * @param destination - The destination to add.\n */\n public addDestination(destination: TerminalWritable): void {\n this._destinations.add(destination);\n }\n\n /**\n * Removes a destination from the set of destinations. It will no longer receive chunks, and will be closed, unless\n * `destination.preventAutoclose` is set to `true`.\n * @param destination - The destination to remove.\n * @param close - If `true` (default), the destination will be closed when removed, unless `destination.preventAutoclose` is set to `true`.\n * @returns `true` if the destination was removed, `false` if it was not found.\n * @remarks\n * If the destination is not found, it will not be closed.\n */\n public removeDestination(destination: TerminalWritable, close: boolean = true): boolean {\n if (this._destinations.delete(destination)) {\n if (close && !destination.preventAutoclose) {\n destination.close();\n }\n return true;\n }\n return false;\n }\n\n protected onWriteChunk(chunk: ITerminalChunk): void {\n for (const destination of this._destinations) {\n destination.writeChunk(chunk);\n }\n }\n\n protected onClose(): void {\n const errors: Error[] = [];\n\n // If an exception is thrown, try to ensure that the other destinations get closed properly\n for (const destination of this._destinations) {\n if (!destination.preventAutoclose) {\n try {\n destination.close();\n } catch (error) {\n errors.push(error as Error);\n }\n }\n }\n\n this._destinations.clear();\n\n if (errors.length > 0) {\n throw errors[0];\n }\n }\n}\n"]}
@@ -0,0 +1,111 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import { Text, NewlineKind } from '@rushstack/node-core-library';
4
+ import { TerminalChunkKind } from './ITerminalChunk';
5
+ import { TerminalTransform } from './TerminalTransform';
6
+ /**
7
+ * `StderrLineTransform` normalizes lines that mix characters from `stdout` and `stderr`,
8
+ * so that each output line is routed entirely to `stdout` or `stderr`.
9
+ *
10
+ * @remarks
11
+ * IMPORTANT: This transform assumes that its input has been normalized to use `"\n"` newlines.
12
+ *
13
+ * IMPORTANT: This transform does not produce realtime output, because lines are buffered
14
+ * until a newline character is encountered.
15
+ *
16
+ * Suppose that a poorly behaved process produces output like this:
17
+ *
18
+ * ```ts
19
+ * process.stderr.write('An error occurred, cleaning up');
20
+ * process.stdout.write('.'); // (delay)
21
+ * process.stdout.write('.'); // (delay)
22
+ * process.stdout.write('.');
23
+ * process.stdout.write('\n');
24
+ * process.stderr.write('The process completed with errors\n');
25
+ * ```
26
+ *
27
+ * When `stdout` and `stderr` are combined on the console, the mistake in the output would not be noticeable:
28
+ * ```
29
+ * An error occurred, cleaning up...
30
+ * The process completed with errors
31
+ * ```
32
+ *
33
+ * However, if we discard `stdout`, then `stderr` is malformed:
34
+ * ```
35
+ * An error occurred, cleaning upThe process completed with errors
36
+ * ```
37
+ *
38
+ * Tooling scripts can introduce these sorts of problems via edge cases that are difficult to find and fix.
39
+ *
40
+ * `StderrLineTransform` normalizes the output so that if a combined line contains any `stderr` characters,
41
+ * then the entire line is routed to `stderr`. Later, if we discard `stdout`, then the output will
42
+ * preserve the appropriate context:
43
+ *
44
+ * ```
45
+ * An error occurred, cleaning up...
46
+ * The process completed with errors
47
+ * ```
48
+ *
49
+ * @privateRemarks
50
+ * This class is experimental and marked as `@beta`. The algorithm may need some fine-tuning, or there may
51
+ * be better solutions to this problem.
52
+ *
53
+ * @beta
54
+ */
55
+ export class StderrLineTransform extends TerminalTransform {
56
+ constructor(options) {
57
+ super(options);
58
+ this._accumulatedLine = '';
59
+ this._accumulatedStderr = false;
60
+ this.newline = Text.getNewline(options.newlineKind || NewlineKind.Lf);
61
+ }
62
+ onWriteChunk(chunk) {
63
+ if (chunk.text.indexOf('\r') >= 0) {
64
+ throw new Error('StderrLineTransform expects chunks with normalized newlines');
65
+ }
66
+ // After _newlineNormalizerTransform was applied, we can now assume that all newlines
67
+ // use the "\n" string
68
+ const text = chunk.text;
69
+ let startIndex = 0;
70
+ while (startIndex < text.length) {
71
+ if (chunk.kind === TerminalChunkKind.Stderr) {
72
+ this._accumulatedStderr = true;
73
+ }
74
+ const endIndex = text.indexOf('\n', startIndex);
75
+ if (endIndex < 0) {
76
+ // we did not find \n, so simply append
77
+ this._accumulatedLine += text.substring(startIndex);
78
+ break;
79
+ }
80
+ // append everything up to \n
81
+ this._accumulatedLine += text.substring(startIndex, endIndex);
82
+ this._processAccumulatedLine();
83
+ // skip the \n
84
+ startIndex = endIndex + 1;
85
+ }
86
+ }
87
+ onClose() {
88
+ if (this._accumulatedLine.length > 0) {
89
+ this._processAccumulatedLine();
90
+ }
91
+ this.autocloseDestination();
92
+ }
93
+ _processAccumulatedLine() {
94
+ this._accumulatedLine += this.newline;
95
+ if (this._accumulatedStderr) {
96
+ this.destination.writeChunk({
97
+ kind: TerminalChunkKind.Stderr,
98
+ text: this._accumulatedLine
99
+ });
100
+ }
101
+ else {
102
+ this.destination.writeChunk({
103
+ kind: TerminalChunkKind.Stdout,
104
+ text: this._accumulatedLine
105
+ });
106
+ }
107
+ this._accumulatedLine = '';
108
+ this._accumulatedStderr = false;
109
+ }
110
+ }
111
+ //# sourceMappingURL=StdioLineTransform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StdioLineTransform.js","sourceRoot":"","sources":["../src/StdioLineTransform.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,2DAA2D;AAE3D,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAuB,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAkC,MAAM,qBAAqB,CAAC;AAaxF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,OAAO,mBAAoB,SAAQ,iBAAiB;IAMxD,YAAmB,OAAmC;QACpD,KAAK,CAAC,OAAO,CAAC,CAAC;QAEf,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC3B,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAEhC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IAES,YAAY,CAAC,KAAqB;QAC1C,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACjF,CAAC;QAED,qFAAqF;QACrF,sBAAsB;QACtB,MAAM,IAAI,GAAW,KAAK,CAAC,IAAI,CAAC;QAChC,IAAI,UAAU,GAAW,CAAC,CAAC;QAE3B,OAAO,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,MAAM,EAAE,CAAC;gBAC5C,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;YACjC,CAAC;YAED,MAAM,QAAQ,GAAW,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YACxD,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;gBACjB,uCAAuC;gBACvC,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;gBACpD,MAAM;YACR,CAAC;YAED,6BAA6B;YAC7B,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAE9D,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAE/B,cAAc;YACd,UAAU,GAAG,QAAQ,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAES,OAAO;QACf,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,uBAAuB,EAAE,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC9B,CAAC;IAEO,uBAAuB;QAC7B,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,OAAO,CAAC;QAEtC,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;gBAC1B,IAAI,EAAE,iBAAiB,CAAC,MAAM;gBAC9B,IAAI,EAAE,IAAI,CAAC,gBAAgB;aAC5B,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;gBAC1B,IAAI,EAAE,iBAAiB,CAAC,MAAM;gBAC9B,IAAI,EAAE,IAAI,CAAC,gBAAgB;aAC5B,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC3B,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;IAClC,CAAC;CACF","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { Text, NewlineKind } from '@rushstack/node-core-library';\n\nimport { type ITerminalChunk, TerminalChunkKind } from './ITerminalChunk';\nimport { TerminalTransform, type ITerminalTransformOptions } from './TerminalTransform';\n\n/**\n * Constructor options for {@link StderrLineTransform}\n * @beta\n */\nexport interface IStdioLineTransformOptions extends ITerminalTransformOptions {\n /**\n * Specifies the kind of newline for the output.\n */\n newlineKind?: NewlineKind;\n}\n\n/**\n * `StderrLineTransform` normalizes lines that mix characters from `stdout` and `stderr`,\n * so that each output line is routed entirely to `stdout` or `stderr`.\n *\n * @remarks\n * IMPORTANT: This transform assumes that its input has been normalized to use `\"\\n\"` newlines.\n *\n * IMPORTANT: This transform does not produce realtime output, because lines are buffered\n * until a newline character is encountered.\n *\n * Suppose that a poorly behaved process produces output like this:\n *\n * ```ts\n * process.stderr.write('An error occurred, cleaning up');\n * process.stdout.write('.'); // (delay)\n * process.stdout.write('.'); // (delay)\n * process.stdout.write('.');\n * process.stdout.write('\\n');\n * process.stderr.write('The process completed with errors\\n');\n * ```\n *\n * When `stdout` and `stderr` are combined on the console, the mistake in the output would not be noticeable:\n * ```\n * An error occurred, cleaning up...\n * The process completed with errors\n * ```\n *\n * However, if we discard `stdout`, then `stderr` is malformed:\n * ```\n * An error occurred, cleaning upThe process completed with errors\n * ```\n *\n * Tooling scripts can introduce these sorts of problems via edge cases that are difficult to find and fix.\n *\n * `StderrLineTransform` normalizes the output so that if a combined line contains any `stderr` characters,\n * then the entire line is routed to `stderr`. Later, if we discard `stdout`, then the output will\n * preserve the appropriate context:\n *\n * ```\n * An error occurred, cleaning up...\n * The process completed with errors\n * ```\n *\n * @privateRemarks\n * This class is experimental and marked as `@beta`. The algorithm may need some fine-tuning, or there may\n * be better solutions to this problem.\n *\n * @beta\n */\nexport class StderrLineTransform extends TerminalTransform {\n private _accumulatedLine: string;\n private _accumulatedStderr: boolean;\n\n public readonly newline: string;\n\n public constructor(options: IStdioLineTransformOptions) {\n super(options);\n\n this._accumulatedLine = '';\n this._accumulatedStderr = false;\n\n this.newline = Text.getNewline(options.newlineKind || NewlineKind.Lf);\n }\n\n protected onWriteChunk(chunk: ITerminalChunk): void {\n if (chunk.text.indexOf('\\r') >= 0) {\n throw new Error('StderrLineTransform expects chunks with normalized newlines');\n }\n\n // After _newlineNormalizerTransform was applied, we can now assume that all newlines\n // use the \"\\n\" string\n const text: string = chunk.text;\n let startIndex: number = 0;\n\n while (startIndex < text.length) {\n if (chunk.kind === TerminalChunkKind.Stderr) {\n this._accumulatedStderr = true;\n }\n\n const endIndex: number = text.indexOf('\\n', startIndex);\n if (endIndex < 0) {\n // we did not find \\n, so simply append\n this._accumulatedLine += text.substring(startIndex);\n break;\n }\n\n // append everything up to \\n\n this._accumulatedLine += text.substring(startIndex, endIndex);\n\n this._processAccumulatedLine();\n\n // skip the \\n\n startIndex = endIndex + 1;\n }\n }\n\n protected onClose(): void {\n if (this._accumulatedLine.length > 0) {\n this._processAccumulatedLine();\n }\n this.autocloseDestination();\n }\n\n private _processAccumulatedLine(): void {\n this._accumulatedLine += this.newline;\n\n if (this._accumulatedStderr) {\n this.destination.writeChunk({\n kind: TerminalChunkKind.Stderr,\n text: this._accumulatedLine\n });\n } else {\n this.destination.writeChunk({\n kind: TerminalChunkKind.Stdout,\n text: this._accumulatedLine\n });\n }\n\n this._accumulatedLine = '';\n this._accumulatedStderr = false;\n }\n}\n"]}
@@ -0,0 +1,95 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import { TerminalChunkKind } from './ITerminalChunk';
4
+ import { TerminalWritable } from './TerminalWritable';
5
+ /**
6
+ * Summarizes the results of a failed build task by returning a subset of `stderr` output not to exceed
7
+ * a specified maximum number of lines.
8
+ *
9
+ * @remarks
10
+ * IMPORTANT: This transform assumes that its input was prepared by {@link StderrLineTransform}, so that each
11
+ * {@link ITerminalChunk.text} item is a single line terminated by a `"\n"` character.
12
+ *
13
+ * The {@link IStdioSummarizerOptions.leadingLines} and {@link IStdioSummarizerOptions.trailingLines}
14
+ * counts specify the maximum number of lines to be returned. Any additional lines will be omitted.
15
+ * For example, if `leadingLines` and `trailingLines` were set to `3`, then the summary of 16 `stderr` lines might
16
+ * look like this:
17
+ *
18
+ * ```
19
+ * Line 1
20
+ * Line 2
21
+ * Line 3
22
+ * ...10 lines omitted...
23
+ * Line 14
24
+ * Line 15
25
+ * Line 16
26
+ * ```
27
+ *
28
+ * If the `stderr` output is completely empty, then the `stdout` output will be summarized instead.
29
+ *
30
+ * @beta
31
+ */
32
+ export class StdioSummarizer extends TerminalWritable {
33
+ constructor(options) {
34
+ super(options);
35
+ this._abridgedOmittedLines = 0;
36
+ if (!options) {
37
+ options = {};
38
+ }
39
+ this._leadingLines = options.leadingLines !== undefined ? options.leadingLines : 10;
40
+ this._trailingLines = options.trailingLines !== undefined ? options.trailingLines : 10;
41
+ this._abridgedLeading = [];
42
+ this._abridgedTrailing = [];
43
+ this._abridgedStderr = false;
44
+ }
45
+ /**
46
+ * Returns the summary report.
47
+ *
48
+ * @remarks
49
+ * The `close()` method must be called before `getReport()` can be used.
50
+ */
51
+ getReport() {
52
+ if (this.isOpen) {
53
+ throw new Error('The summary cannot be prepared until after close() is called.');
54
+ }
55
+ const report = [...this._abridgedLeading];
56
+ if (this._abridgedOmittedLines === 1) {
57
+ report.push(` ...${this._abridgedOmittedLines} line omitted...\n`);
58
+ }
59
+ if (this._abridgedOmittedLines > 1) {
60
+ report.push(` ...${this._abridgedOmittedLines} lines omitted...\n`);
61
+ }
62
+ report.push(...this._abridgedTrailing);
63
+ return report.join('');
64
+ }
65
+ onWriteChunk(chunk) {
66
+ if (chunk.text.length === 0 || chunk.text[chunk.text.length - 1] !== '\n') {
67
+ throw new Error('StdioSummarizer expects chunks that were separated parsed into lines by StderrLineTransform\n' +
68
+ ' Invalid input: ' +
69
+ JSON.stringify(chunk.text));
70
+ }
71
+ if (chunk.kind === TerminalChunkKind.Stderr && !this._abridgedStderr) {
72
+ // The first time we see stderr, switch to capturing stderr
73
+ this._abridgedStderr = true;
74
+ this._abridgedLeading.length = 0;
75
+ this._abridgedTrailing.length = 0;
76
+ this._abridgedOmittedLines = 0;
77
+ }
78
+ else if (this._abridgedStderr && chunk.kind !== TerminalChunkKind.Stderr) {
79
+ // If we're capturing stderr, then ignore non-stderr input
80
+ return;
81
+ }
82
+ // Did we capture enough leading lines?
83
+ if (this._abridgedLeading.length < this._leadingLines) {
84
+ this._abridgedLeading.push(chunk.text);
85
+ return;
86
+ }
87
+ this._abridgedTrailing.push(chunk.text);
88
+ // If we captured to many trailing lines, omit the extras
89
+ while (this._abridgedTrailing.length > this._trailingLines) {
90
+ this._abridgedTrailing.shift();
91
+ ++this._abridgedOmittedLines;
92
+ }
93
+ }
94
+ }
95
+ //# sourceMappingURL=StdioSummarizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StdioSummarizer.js","sourceRoot":"","sources":["../src/StdioSummarizer.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { type ITerminalChunk, TerminalChunkKind } from './ITerminalChunk';\nimport { type ITerminalWritableOptions, TerminalWritable } from './TerminalWritable';\n\n/**\n * Constructor options for {@link StdioSummarizer}.\n * @beta\n */\nexport interface IStdioSummarizerOptions extends ITerminalWritableOptions {\n /**\n * Specifies the maximum number of leading lines to include in the summary.\n * @defaultValue `10`\n */\n leadingLines?: number;\n\n /**\n * Specifies the maximum number of trailing lines to include in the summary.\n * @defaultValue `10`\n */\n trailingLines?: number;\n}\n\n/**\n * Summarizes the results of a failed build task by returning a subset of `stderr` output not to exceed\n * a specified maximum number of lines.\n *\n * @remarks\n * IMPORTANT: This transform assumes that its input was prepared by {@link StderrLineTransform}, so that each\n * {@link ITerminalChunk.text} item is a single line terminated by a `\"\\n\"` character.\n *\n * The {@link IStdioSummarizerOptions.leadingLines} and {@link IStdioSummarizerOptions.trailingLines}\n * counts specify the maximum number of lines to be returned. Any additional lines will be omitted.\n * For example, if `leadingLines` and `trailingLines` were set to `3`, then the summary of 16 `stderr` lines might\n * look like this:\n *\n * ```\n * Line 1\n * Line 2\n * Line 3\n * ...10 lines omitted...\n * Line 14\n * Line 15\n * Line 16\n * ```\n *\n * If the `stderr` output is completely empty, then the `stdout` output will be summarized instead.\n *\n * @beta\n */\nexport class StdioSummarizer extends TerminalWritable {\n // Capture up to this many leading lines\n private _leadingLines: number;\n\n // Capture up to this many trailing lines\n private _trailingLines: number;\n\n private readonly _abridgedLeading: string[];\n private readonly _abridgedTrailing: string[];\n private _abridgedOmittedLines: number = 0;\n private _abridgedStderr: boolean;\n\n public constructor(options?: IStdioSummarizerOptions) {\n super(options);\n\n if (!options) {\n options = {};\n }\n\n this._leadingLines = options.leadingLines !== undefined ? options.leadingLines : 10;\n this._trailingLines = options.trailingLines !== undefined ? options.trailingLines : 10;\n\n this._abridgedLeading = [];\n this._abridgedTrailing = [];\n this._abridgedStderr = false;\n }\n\n /**\n * Returns the summary report.\n *\n * @remarks\n * The `close()` method must be called before `getReport()` can be used.\n */\n public getReport(): string {\n if (this.isOpen) {\n throw new Error('The summary cannot be prepared until after close() is called.');\n }\n const report: string[] = [...this._abridgedLeading];\n if (this._abridgedOmittedLines === 1) {\n report.push(` ...${this._abridgedOmittedLines} line omitted...\\n`);\n }\n if (this._abridgedOmittedLines > 1) {\n report.push(` ...${this._abridgedOmittedLines} lines omitted...\\n`);\n }\n report.push(...this._abridgedTrailing);\n return report.join('');\n }\n\n public onWriteChunk(chunk: ITerminalChunk): void {\n if (chunk.text.length === 0 || chunk.text[chunk.text.length - 1] !== '\\n') {\n throw new Error(\n 'StdioSummarizer expects chunks that were separated parsed into lines by StderrLineTransform\\n' +\n ' Invalid input: ' +\n JSON.stringify(chunk.text)\n );\n }\n\n if (chunk.kind === TerminalChunkKind.Stderr && !this._abridgedStderr) {\n // The first time we see stderr, switch to capturing stderr\n this._abridgedStderr = true;\n this._abridgedLeading.length = 0;\n this._abridgedTrailing.length = 0;\n this._abridgedOmittedLines = 0;\n } else if (this._abridgedStderr && chunk.kind !== TerminalChunkKind.Stderr) {\n // If we're capturing stderr, then ignore non-stderr input\n return;\n }\n\n // Did we capture enough leading lines?\n if (this._abridgedLeading.length < this._leadingLines) {\n this._abridgedLeading.push(chunk.text);\n return;\n }\n\n this._abridgedTrailing.push(chunk.text);\n\n // If we captured to many trailing lines, omit the extras\n while (this._abridgedTrailing.length > this._trailingLines) {\n this._abridgedTrailing.shift();\n ++this._abridgedOmittedLines;\n }\n }\n}\n"]}
@@ -0,0 +1,27 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import process from 'node:process';
4
+ import { TerminalChunkKind } from './ITerminalChunk';
5
+ import { TerminalWritable } from './TerminalWritable';
6
+ /**
7
+ * A {@link TerminalWritable} subclass that writes its output directly to the process `stdout` and `stderr`
8
+ * streams.
9
+ *
10
+ * @remarks
11
+ * This is the standard output target for a process. You normally do not need to construct
12
+ * this class; the {@link StdioWritable."instance"} singleton can be used instead.
13
+ *
14
+ * @public
15
+ */
16
+ export class StdioWritable extends TerminalWritable {
17
+ onWriteChunk(chunk) {
18
+ if (chunk.kind === TerminalChunkKind.Stdout) {
19
+ process.stdout.write(chunk.text);
20
+ }
21
+ else if (chunk.kind === TerminalChunkKind.Stderr) {
22
+ process.stderr.write(chunk.text);
23
+ }
24
+ }
25
+ }
26
+ StdioWritable.instance = new StdioWritable();
27
+ //# sourceMappingURL=StdioWritable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StdioWritable.js","sourceRoot":"","sources":["../src/StdioWritable.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,2DAA2D;AAE3D,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAuB,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAc,SAAQ,gBAAgB;IAGvC,YAAY,CAAC,KAAqB;QAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,MAAM,EAAE,CAAC;YAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,MAAM,EAAE,CAAC;YACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;;AARa,sBAAQ,GAAkB,IAAI,aAAa,EAAE,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport process from 'node:process';\n\nimport { type ITerminalChunk, TerminalChunkKind } from './ITerminalChunk';\nimport { TerminalWritable } from './TerminalWritable';\n\n/**\n * A {@link TerminalWritable} subclass that writes its output directly to the process `stdout` and `stderr`\n * streams.\n *\n * @remarks\n * This is the standard output target for a process. You normally do not need to construct\n * this class; the {@link StdioWritable.\"instance\"} singleton can be used instead.\n *\n * @public\n */\nexport class StdioWritable extends TerminalWritable {\n public static instance: StdioWritable = new StdioWritable();\n\n protected onWriteChunk(chunk: ITerminalChunk): void {\n if (chunk.kind === TerminalChunkKind.Stdout) {\n process.stdout.write(chunk.text);\n } else if (chunk.kind === TerminalChunkKind.Stderr) {\n process.stderr.write(chunk.text);\n }\n }\n}\n"]}
@@ -0,0 +1,185 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import { StringBuilder, Text } from '@rushstack/node-core-library';
4
+ import { TerminalProviderSeverity } from './ITerminalProvider';
5
+ import { AnsiEscape } from './AnsiEscape';
6
+ function _normalizeOptions(options) {
7
+ return {
8
+ normalizeSpecialCharacters: true,
9
+ ...options
10
+ };
11
+ }
12
+ function _normalizeOutput(s, options) {
13
+ const { normalizeSpecialCharacters } = _normalizeOptions(options !== null && options !== void 0 ? options : {});
14
+ return _normalizeOutputInner(s, normalizeSpecialCharacters);
15
+ }
16
+ function _normalizeOutputInner(s, normalizeSpecialCharacters) {
17
+ s = Text.convertToLf(s);
18
+ if (normalizeSpecialCharacters) {
19
+ return AnsiEscape.formatForTests(s, { encodeNewlines: true });
20
+ }
21
+ else {
22
+ return s;
23
+ }
24
+ }
25
+ const LONGEST_SEVERITY_NAME_LENGTH = Object.keys(TerminalProviderSeverity).reduce((max, k) => Math.max(max, k.length), 0);
26
+ /**
27
+ * Terminal provider that stores written data in buffers separated by severity.
28
+ * This terminal provider is designed to be used when code that prints to a terminal
29
+ * is being unit tested.
30
+ *
31
+ * @beta
32
+ */
33
+ export class StringBufferTerminalProvider {
34
+ constructor(supportsColor = false) {
35
+ this._standardBuffer = new StringBuilder();
36
+ this._verboseBuffer = new StringBuilder();
37
+ this._debugBuffer = new StringBuilder();
38
+ this._warningBuffer = new StringBuilder();
39
+ this._errorBuffer = new StringBuilder();
40
+ this._allOutputChunks = [];
41
+ this.supportsColor = supportsColor;
42
+ }
43
+ /**
44
+ * {@inheritDoc ITerminalProvider.write}
45
+ */
46
+ write(text, severity) {
47
+ const severityName = TerminalProviderSeverity[severity];
48
+ const lastChunk = this._allOutputChunks[this._allOutputChunks.length - 1];
49
+ if (lastChunk && lastChunk.severity === severityName) {
50
+ lastChunk.text += text;
51
+ }
52
+ else {
53
+ this._allOutputChunks.push({
54
+ text,
55
+ severity: severityName
56
+ });
57
+ }
58
+ switch (severity) {
59
+ case TerminalProviderSeverity.warning: {
60
+ this._warningBuffer.append(text);
61
+ break;
62
+ }
63
+ case TerminalProviderSeverity.error: {
64
+ this._errorBuffer.append(text);
65
+ break;
66
+ }
67
+ case TerminalProviderSeverity.verbose: {
68
+ this._verboseBuffer.append(text);
69
+ break;
70
+ }
71
+ case TerminalProviderSeverity.debug: {
72
+ this._debugBuffer.append(text);
73
+ break;
74
+ }
75
+ case TerminalProviderSeverity.log:
76
+ default: {
77
+ this._standardBuffer.append(text);
78
+ break;
79
+ }
80
+ }
81
+ }
82
+ /**
83
+ * {@inheritDoc ITerminalProvider.eolCharacter}
84
+ */
85
+ get eolCharacter() {
86
+ return '\n';
87
+ }
88
+ /**
89
+ * Get everything that has been written at log-level severity.
90
+ */
91
+ getOutput(options) {
92
+ return _normalizeOutput(this._standardBuffer.toString(), options);
93
+ }
94
+ /**
95
+ * @deprecated - use {@link StringBufferTerminalProvider.getVerboseOutput}
96
+ */
97
+ getVerbose(options) {
98
+ return this.getVerboseOutput(options);
99
+ }
100
+ /**
101
+ * Get everything that has been written at verbose-level severity.
102
+ */
103
+ getVerboseOutput(options) {
104
+ return _normalizeOutput(this._verboseBuffer.toString(), options);
105
+ }
106
+ /**
107
+ * Get everything that has been written at debug-level severity.
108
+ */
109
+ getDebugOutput(options) {
110
+ return _normalizeOutput(this._debugBuffer.toString(), options);
111
+ }
112
+ /**
113
+ * Get everything that has been written at error-level severity.
114
+ */
115
+ getErrorOutput(options) {
116
+ return _normalizeOutput(this._errorBuffer.toString(), options);
117
+ }
118
+ /**
119
+ * Get everything that has been written at warning-level severity.
120
+ */
121
+ getWarningOutput(options) {
122
+ return _normalizeOutput(this._warningBuffer.toString(), options);
123
+ }
124
+ getAllOutput(sparse, options) {
125
+ const result = {};
126
+ const log = this.getOutput(options);
127
+ if (!sparse || log) {
128
+ result.log = log;
129
+ }
130
+ const warning = this.getWarningOutput(options);
131
+ if (!sparse || warning) {
132
+ result.warning = warning;
133
+ }
134
+ const error = this.getErrorOutput(options);
135
+ if (!sparse || error) {
136
+ result.error = error;
137
+ }
138
+ const verbose = this.getVerboseOutput(options);
139
+ if (!sparse || verbose) {
140
+ result.verbose = verbose;
141
+ }
142
+ const debug = this.getDebugOutput(options);
143
+ if (!sparse || debug) {
144
+ result.debug = debug;
145
+ }
146
+ return result;
147
+ }
148
+ getAllOutputAsChunks(options = {}) {
149
+ const { asLines, normalizeSpecialCharacters } = _normalizeOptions(options);
150
+ if (asLines) {
151
+ const lines = [];
152
+ for (const { text: rawText, severity: rawSeverity } of this._allOutputChunks) {
153
+ const severity = rawSeverity.padStart(LONGEST_SEVERITY_NAME_LENGTH, ' ');
154
+ const lfText = Text.convertToLf(rawText);
155
+ const rawLines = lfText.split('\n');
156
+ // Emit one entry per logical line.
157
+ for (let i = 0; i < rawLines.length; i++) {
158
+ const isLast = i === rawLines.length - 1;
159
+ const isFinalTrailingEmpty = isLast && rawLines[i] === '';
160
+ if (isFinalTrailingEmpty) {
161
+ continue;
162
+ }
163
+ const hasNewlineAfter = i < rawLines.length - 1;
164
+ // If the original output had a newline after this line, preserve it as the special token
165
+ // (e.g. "[n]") when normalization is enabled.
166
+ const shouldIncludeNewlineToken = normalizeSpecialCharacters && hasNewlineAfter;
167
+ const lineText = shouldIncludeNewlineToken ? `${rawLines[i]}\n` : rawLines[i];
168
+ const text = _normalizeOutputInner(lineText, normalizeSpecialCharacters);
169
+ lines.push(`[${severity}] ${text}`);
170
+ }
171
+ }
172
+ return lines;
173
+ }
174
+ else {
175
+ return this._allOutputChunks.map(({ text: rawText, severity }) => {
176
+ const text = _normalizeOutputInner(rawText, normalizeSpecialCharacters);
177
+ return {
178
+ text,
179
+ severity
180
+ };
181
+ });
182
+ }
183
+ }
184
+ }
185
+ //# sourceMappingURL=StringBufferTerminalProvider.js.map