@pokit/reporter-clack 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/adapter.d.ts +6 -5
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +55 -48
- package/package.json +4 -3
- package/src/adapter.ts +57 -47
package/README.md
CHANGED
package/dist/adapter.d.ts
CHANGED
|
@@ -12,18 +12,19 @@
|
|
|
12
12
|
* - Process output is never interleaved with spinners
|
|
13
13
|
*
|
|
14
14
|
* Rendering strategy:
|
|
15
|
-
* - group:start -> p.intro() with bold label (or
|
|
15
|
+
* - group:start -> p.intro() with bold label (or line-based output)
|
|
16
16
|
* - group:end -> p.outro() with success indicator
|
|
17
|
-
* - activity:start (sequential) -> spinner.start() (or
|
|
17
|
+
* - activity:start (sequential) -> spinner.start() (or line-based output)
|
|
18
18
|
* - activity:start (parallel) -> track activity, update combined spinner
|
|
19
19
|
* - activity:success -> spinner.stop() with checkmark (code 0) or update combined spinner
|
|
20
20
|
* - activity:failure -> spinner.stop() with X (code 1)
|
|
21
21
|
* - activity:update -> spinner.message()
|
|
22
22
|
* - log -> pause spinner if active, p.log.*, resume spinner
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
* -
|
|
26
|
-
* -
|
|
24
|
+
* Non-interactive output (--no-tty/NO_TTY/CI):
|
|
25
|
+
* - Uses line-based output without spinners or clack decorative UI
|
|
26
|
+
* - Unicode symbols are controlled separately with --no-unicode/NO_UNICODE
|
|
27
|
+
* - Color is controlled separately with --no-color/NO_COLOR
|
|
27
28
|
*/
|
|
28
29
|
import type { ReporterAdapter, OutputConfig } from '@pokit/core';
|
|
29
30
|
/**
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,KAAK,EACV,eAAe,EAQf,YAAY,EACb,MAAM,aAAa,CAAC;AAuNrB;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,eAAe,CA2iBvF"}
|
package/dist/adapter.js
CHANGED
|
@@ -12,22 +12,23 @@
|
|
|
12
12
|
* - Process output is never interleaved with spinners
|
|
13
13
|
*
|
|
14
14
|
* Rendering strategy:
|
|
15
|
-
* - group:start -> p.intro() with bold label (or
|
|
15
|
+
* - group:start -> p.intro() with bold label (or line-based output)
|
|
16
16
|
* - group:end -> p.outro() with success indicator
|
|
17
|
-
* - activity:start (sequential) -> spinner.start() (or
|
|
17
|
+
* - activity:start (sequential) -> spinner.start() (or line-based output)
|
|
18
18
|
* - activity:start (parallel) -> track activity, update combined spinner
|
|
19
19
|
* - activity:success -> spinner.stop() with checkmark (code 0) or update combined spinner
|
|
20
20
|
* - activity:failure -> spinner.stop() with X (code 1)
|
|
21
21
|
* - activity:update -> spinner.message()
|
|
22
22
|
* - log -> pause spinner if active, p.log.*, resume spinner
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
* -
|
|
26
|
-
* -
|
|
24
|
+
* Non-interactive output (--no-tty/NO_TTY/CI):
|
|
25
|
+
* - Uses line-based output without spinners or clack decorative UI
|
|
26
|
+
* - Unicode symbols are controlled separately with --no-unicode/NO_UNICODE
|
|
27
|
+
* - Color is controlled separately with --no-color/NO_COLOR
|
|
27
28
|
*/
|
|
28
29
|
import * as p from '@clack/prompts';
|
|
29
30
|
import pc from 'picocolors';
|
|
30
|
-
import { CommandError } from '@pokit/core';
|
|
31
|
+
import { detectOutputConfig, CommandError } from '@pokit/core';
|
|
31
32
|
/**
|
|
32
33
|
* Extract error message and optional output from an error.
|
|
33
34
|
* If the error is a CommandError with output, includes that in the message.
|
|
@@ -57,6 +58,12 @@ function colorize(text, colorFn, useColor) {
|
|
|
57
58
|
function writeLine(line) {
|
|
58
59
|
process.stdout.write(line + '\n');
|
|
59
60
|
}
|
|
61
|
+
function isPlainOutput(outputConfig) {
|
|
62
|
+
return !outputConfig.unicode || !outputConfig.interactive;
|
|
63
|
+
}
|
|
64
|
+
function canUseInteractiveUI(outputConfig) {
|
|
65
|
+
return outputConfig.unicode && outputConfig.interactive;
|
|
66
|
+
}
|
|
60
67
|
/** Maximum number of logs to buffer per activity to prevent memory issues */
|
|
61
68
|
const MAX_BUFFERED_LOGS_PER_ACTIVITY = 100;
|
|
62
69
|
/**
|
|
@@ -80,7 +87,7 @@ function updateParallelSpinnerMessage(state) {
|
|
|
80
87
|
}
|
|
81
88
|
/**
|
|
82
89
|
* Display a log message.
|
|
83
|
-
* Uses clack's log functions in unicode mode,
|
|
90
|
+
* Uses clack's log functions in interactive unicode mode, line output otherwise.
|
|
84
91
|
*
|
|
85
92
|
* @param level - The log level
|
|
86
93
|
* @param message - The message to display
|
|
@@ -89,8 +96,8 @@ function updateParallelSpinnerMessage(state) {
|
|
|
89
96
|
*/
|
|
90
97
|
function displayLog(level, message, state, indented = false) {
|
|
91
98
|
const { outputConfig, symbols } = state;
|
|
92
|
-
// In plain
|
|
93
|
-
if (
|
|
99
|
+
// In plain or non-interactive mode, use simple console output
|
|
100
|
+
if (isPlainOutput(outputConfig)) {
|
|
94
101
|
const prefix = indented ? `${symbols.groupLine} ` : '';
|
|
95
102
|
const levelPrefix = {
|
|
96
103
|
info: symbols.info,
|
|
@@ -102,7 +109,7 @@ function displayLog(level, message, state, indented = false) {
|
|
|
102
109
|
writeLine(`${prefix}${levelPrefix} ${message}`);
|
|
103
110
|
return;
|
|
104
111
|
}
|
|
105
|
-
// Unicode mode - use clack's decorative output
|
|
112
|
+
// Unicode + interactive mode - use clack's decorative output
|
|
106
113
|
const prefix = indented ? '\u2502 ' : ''; // │ for indented logs
|
|
107
114
|
const formattedMessage = prefix + message;
|
|
108
115
|
switch (level) {
|
|
@@ -147,12 +154,12 @@ function flushLogsForActivity(state, activityId) {
|
|
|
147
154
|
* @param options - Optional configuration for the adapter
|
|
148
155
|
*/
|
|
149
156
|
export function createReporterAdapter(options) {
|
|
150
|
-
// Get output config from options, or
|
|
151
|
-
const outputConfig = options?.output ??
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
157
|
+
// Get output config from options, or detect from args/env
|
|
158
|
+
const outputConfig = options?.output ?? detectOutputConfig(process.argv.slice(2));
|
|
159
|
+
// Backwards compatibility: default interactive when missing
|
|
160
|
+
if (outputConfig.interactive === undefined) {
|
|
161
|
+
outputConfig.interactive = true;
|
|
162
|
+
}
|
|
156
163
|
// Verbose can be set via options.verbose for backwards compatibility
|
|
157
164
|
if (options?.verbose !== undefined) {
|
|
158
165
|
outputConfig.verbose = options.verbose;
|
|
@@ -189,8 +196,8 @@ export function createReporterAdapter(options) {
|
|
|
189
196
|
layout: event.layout,
|
|
190
197
|
hasFailure: false,
|
|
191
198
|
});
|
|
192
|
-
// In plain mode, use simple bracket notation
|
|
193
|
-
if (
|
|
199
|
+
// In plain or non-interactive mode, use simple bracket notation
|
|
200
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
194
201
|
const label = colorize(event.label, pc.bold, state.outputConfig.color);
|
|
195
202
|
writeLine(`${state.symbols.groupStart}${label}${state.symbols.groupEnd}`);
|
|
196
203
|
}
|
|
@@ -258,7 +265,7 @@ export function createReporterAdapter(options) {
|
|
|
258
265
|
if (activity.groupId !== event.id)
|
|
259
266
|
continue;
|
|
260
267
|
if (activity.status === 'success') {
|
|
261
|
-
if (
|
|
268
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
262
269
|
const prefix = colorize(state.symbols.success, pc.green, state.outputConfig.color);
|
|
263
270
|
writeLine(` ${prefix} ${activity.label}`);
|
|
264
271
|
}
|
|
@@ -269,7 +276,7 @@ export function createReporterAdapter(options) {
|
|
|
269
276
|
else if (activity.status === 'failure') {
|
|
270
277
|
hasFailures = true;
|
|
271
278
|
// Show label inside group, defer error message with remediation
|
|
272
|
-
if (
|
|
279
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
273
280
|
const prefix = colorize(state.symbols.error, pc.red, state.outputConfig.color);
|
|
274
281
|
writeLine(` ${prefix} ${activity.label}`);
|
|
275
282
|
}
|
|
@@ -295,8 +302,8 @@ export function createReporterAdapter(options) {
|
|
|
295
302
|
}
|
|
296
303
|
}
|
|
297
304
|
// Show appropriate outro based on whether there were failures
|
|
298
|
-
if (
|
|
299
|
-
// Plain
|
|
305
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
306
|
+
// Plain output - simple bracket notation
|
|
300
307
|
if (hasFailures) {
|
|
301
308
|
const failedText = colorize(state.symbols.failed, pc.red, state.outputConfig.color);
|
|
302
309
|
writeLine(`${state.symbols.groupStart}${failedText}${state.symbols.groupEnd}`);
|
|
@@ -307,7 +314,7 @@ export function createReporterAdapter(options) {
|
|
|
307
314
|
}
|
|
308
315
|
}
|
|
309
316
|
else {
|
|
310
|
-
// Unicode mode - use clack's outro
|
|
317
|
+
// Unicode + interactive mode - use clack's outro
|
|
311
318
|
if (hasFailures) {
|
|
312
319
|
p.outro(colorize(`${state.symbols.failed} Failed`, pc.red, state.outputConfig.color));
|
|
313
320
|
}
|
|
@@ -317,7 +324,7 @@ export function createReporterAdapter(options) {
|
|
|
317
324
|
}
|
|
318
325
|
// Print deferred error messages with remediation after the group closes
|
|
319
326
|
for (const deferred of deferredErrors) {
|
|
320
|
-
if (
|
|
327
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
321
328
|
writeLine(`${state.symbols.error} ${deferred.error}`);
|
|
322
329
|
}
|
|
323
330
|
else {
|
|
@@ -352,8 +359,8 @@ export function createReporterAdapter(options) {
|
|
|
352
359
|
groupId: event.parentId,
|
|
353
360
|
status: 'pending',
|
|
354
361
|
});
|
|
355
|
-
// In plain mode,
|
|
356
|
-
if (
|
|
362
|
+
// In plain or non-interactive mode, track activities - results shown at group:end
|
|
363
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
357
364
|
if (!state.parallelSpinnerGroupId) {
|
|
358
365
|
state.parallelSpinnerGroupId = event.parentId;
|
|
359
366
|
const activities = state.parallelActivities.size;
|
|
@@ -361,7 +368,7 @@ export function createReporterAdapter(options) {
|
|
|
361
368
|
}
|
|
362
369
|
}
|
|
363
370
|
else {
|
|
364
|
-
// Unicode mode: create or update the parallel spinner
|
|
371
|
+
// Unicode + interactive mode: create or update the parallel spinner
|
|
365
372
|
if (!state.parallelSpinner) {
|
|
366
373
|
const spinner = p.spinner();
|
|
367
374
|
state.parallelSpinner = {
|
|
@@ -377,17 +384,17 @@ export function createReporterAdapter(options) {
|
|
|
377
384
|
}
|
|
378
385
|
else {
|
|
379
386
|
// Sequential activity
|
|
380
|
-
if (
|
|
381
|
-
// Plain
|
|
387
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
388
|
+
// Plain output: track activity without spinner - result shown on completion
|
|
382
389
|
state.spinners.set(event.id, {
|
|
383
|
-
spinner: null, // Not used in
|
|
390
|
+
spinner: null, // Not used in non-interactive output
|
|
384
391
|
label: event.label,
|
|
385
392
|
currentMessage: event.label,
|
|
386
393
|
parentGroupId: event.parentId,
|
|
387
394
|
});
|
|
388
395
|
}
|
|
389
396
|
else {
|
|
390
|
-
// Unicode mode: create individual spinner
|
|
397
|
+
// Unicode + interactive mode: create individual spinner
|
|
391
398
|
const spinner = p.spinner();
|
|
392
399
|
state.spinners.set(event.id, {
|
|
393
400
|
spinner,
|
|
@@ -409,7 +416,7 @@ export function createReporterAdapter(options) {
|
|
|
409
416
|
// Update the activity label if message provided
|
|
410
417
|
if (event.payload.message) {
|
|
411
418
|
parallelActivity.label = event.payload.message;
|
|
412
|
-
if (state.outputConfig
|
|
419
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
413
420
|
updateParallelSpinnerMessage(state);
|
|
414
421
|
}
|
|
415
422
|
}
|
|
@@ -423,7 +430,7 @@ export function createReporterAdapter(options) {
|
|
|
423
430
|
if (text) {
|
|
424
431
|
entry.currentMessage = text;
|
|
425
432
|
// Only update spinner in unicode mode
|
|
426
|
-
if (state.outputConfig
|
|
433
|
+
if (canUseInteractiveUI(state.outputConfig) && entry.spinner) {
|
|
427
434
|
entry.spinner.message(text);
|
|
428
435
|
}
|
|
429
436
|
}
|
|
@@ -435,7 +442,7 @@ export function createReporterAdapter(options) {
|
|
|
435
442
|
const parallelActivity = state.parallelActivities.get(event.id);
|
|
436
443
|
if (parallelActivity) {
|
|
437
444
|
parallelActivity.status = 'success';
|
|
438
|
-
if (state.outputConfig
|
|
445
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
439
446
|
updateParallelSpinnerMessage(state);
|
|
440
447
|
}
|
|
441
448
|
// Note: For parallel activities, logs will be flushed at group:end
|
|
@@ -444,13 +451,13 @@ export function createReporterAdapter(options) {
|
|
|
444
451
|
// Sequential activity
|
|
445
452
|
const entry = state.spinners.get(event.id);
|
|
446
453
|
if (entry) {
|
|
447
|
-
if (
|
|
448
|
-
// Plain
|
|
454
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
455
|
+
// Plain output - print success line
|
|
449
456
|
const prefix = colorize(state.symbols.success, pc.green, state.outputConfig.color);
|
|
450
457
|
writeLine(` ${prefix} ${entry.label}`);
|
|
451
458
|
}
|
|
452
459
|
else if (entry.spinner) {
|
|
453
|
-
// Unicode mode - stop spinner
|
|
460
|
+
// Unicode + interactive mode - stop spinner
|
|
454
461
|
entry.spinner.stop(entry.label, 0);
|
|
455
462
|
}
|
|
456
463
|
state.spinners.delete(event.id);
|
|
@@ -461,7 +468,7 @@ export function createReporterAdapter(options) {
|
|
|
461
468
|
// Check if this was a suspended activity
|
|
462
469
|
const suspended = state.suspendedActivities.get(event.id);
|
|
463
470
|
if (suspended && !state.suspended) {
|
|
464
|
-
if (
|
|
471
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
465
472
|
const prefix = colorize(state.symbols.success, pc.green, state.outputConfig.color);
|
|
466
473
|
writeLine(` ${prefix} ${suspended.label}`);
|
|
467
474
|
}
|
|
@@ -487,7 +494,7 @@ export function createReporterAdapter(options) {
|
|
|
487
494
|
if (parentGroup) {
|
|
488
495
|
parentGroup.hasFailure = true;
|
|
489
496
|
}
|
|
490
|
-
if (state.outputConfig
|
|
497
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
491
498
|
updateParallelSpinnerMessage(state);
|
|
492
499
|
}
|
|
493
500
|
break;
|
|
@@ -502,13 +509,13 @@ export function createReporterAdapter(options) {
|
|
|
502
509
|
parentGroup.hasFailure = true;
|
|
503
510
|
}
|
|
504
511
|
}
|
|
505
|
-
if (
|
|
506
|
-
// Plain
|
|
512
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
513
|
+
// Plain output - print error line
|
|
507
514
|
const prefix = colorize(state.symbols.error, pc.red, state.outputConfig.color);
|
|
508
515
|
writeLine(` ${prefix} ${errorMessage}`);
|
|
509
516
|
}
|
|
510
517
|
else if (entry.spinner) {
|
|
511
|
-
// Unicode mode - stop spinner with error
|
|
518
|
+
// Unicode + interactive mode - stop spinner with error
|
|
512
519
|
entry.spinner.stop(errorMessage, 1);
|
|
513
520
|
}
|
|
514
521
|
state.spinners.delete(event.id);
|
|
@@ -533,7 +540,7 @@ export function createReporterAdapter(options) {
|
|
|
533
540
|
// Check if this was a suspended activity
|
|
534
541
|
const suspended = state.suspendedActivities.get(event.id);
|
|
535
542
|
if (suspended && !state.suspended) {
|
|
536
|
-
if (
|
|
543
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
537
544
|
const prefix = colorize(state.symbols.error, pc.red, state.outputConfig.color);
|
|
538
545
|
writeLine(` ${prefix} ${suspended.label}: ${errorMessage}`);
|
|
539
546
|
}
|
|
@@ -559,7 +566,7 @@ export function createReporterAdapter(options) {
|
|
|
559
566
|
if (event.level === 'error' &&
|
|
560
567
|
hasActiveSpinners &&
|
|
561
568
|
event.activityId &&
|
|
562
|
-
state.outputConfig
|
|
569
|
+
canUseInteractiveUI(state.outputConfig)) {
|
|
563
570
|
const spinner = state.spinners.get(event.activityId);
|
|
564
571
|
if (spinner && spinner.spinner) {
|
|
565
572
|
// Temporarily stop spinner, show error, resume
|
|
@@ -596,8 +603,8 @@ export function createReporterAdapter(options) {
|
|
|
596
603
|
// Reporter control events
|
|
597
604
|
case 'reporter:suspend': {
|
|
598
605
|
state.suspended = true;
|
|
599
|
-
// Stop all active spinners and track them for completion messages
|
|
600
|
-
if (state.outputConfig
|
|
606
|
+
// Stop all active spinners and track them for completion messages
|
|
607
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
601
608
|
for (const [id, entry] of state.spinners) {
|
|
602
609
|
try {
|
|
603
610
|
if (entry.spinner) {
|
|
@@ -632,8 +639,8 @@ export function createReporterAdapter(options) {
|
|
|
632
639
|
const unsubscribe = bus.on(handleEvent);
|
|
633
640
|
return {
|
|
634
641
|
stop() {
|
|
635
|
-
// Stop all active spinners (only in
|
|
636
|
-
if (state.outputConfig
|
|
642
|
+
// Stop all active spinners (only in interactive mode where spinners exist)
|
|
643
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
637
644
|
for (const entry of state.spinners.values()) {
|
|
638
645
|
try {
|
|
639
646
|
if (entry.spinner) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pokit/reporter-clack",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Clack-based event reporter for pok CLI applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -50,10 +50,11 @@
|
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/bun": "latest",
|
|
53
|
-
"@pokit/core": "0.0.
|
|
53
|
+
"@pokit/core": "0.0.9",
|
|
54
|
+
"@pokit/test-utils": "0.0.0"
|
|
54
55
|
},
|
|
55
56
|
"peerDependencies": {
|
|
56
|
-
"@pokit/core": "0.0.
|
|
57
|
+
"@pokit/core": "0.0.9"
|
|
57
58
|
},
|
|
58
59
|
"engines": {
|
|
59
60
|
"bun": ">=1.0.0"
|
package/src/adapter.ts
CHANGED
|
@@ -12,18 +12,19 @@
|
|
|
12
12
|
* - Process output is never interleaved with spinners
|
|
13
13
|
*
|
|
14
14
|
* Rendering strategy:
|
|
15
|
-
* - group:start -> p.intro() with bold label (or
|
|
15
|
+
* - group:start -> p.intro() with bold label (or line-based output)
|
|
16
16
|
* - group:end -> p.outro() with success indicator
|
|
17
|
-
* - activity:start (sequential) -> spinner.start() (or
|
|
17
|
+
* - activity:start (sequential) -> spinner.start() (or line-based output)
|
|
18
18
|
* - activity:start (parallel) -> track activity, update combined spinner
|
|
19
19
|
* - activity:success -> spinner.stop() with checkmark (code 0) or update combined spinner
|
|
20
20
|
* - activity:failure -> spinner.stop() with X (code 1)
|
|
21
21
|
* - activity:update -> spinner.message()
|
|
22
22
|
* - log -> pause spinner if active, p.log.*, resume spinner
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
* -
|
|
26
|
-
* -
|
|
24
|
+
* Non-interactive output (--no-tty/NO_TTY/CI):
|
|
25
|
+
* - Uses line-based output without spinners or clack decorative UI
|
|
26
|
+
* - Unicode symbols are controlled separately with --no-unicode/NO_UNICODE
|
|
27
|
+
* - Color is controlled separately with --no-color/NO_COLOR
|
|
27
28
|
*/
|
|
28
29
|
|
|
29
30
|
import * as p from '@clack/prompts';
|
|
@@ -73,6 +74,14 @@ function writeLine(line: string): void {
|
|
|
73
74
|
process.stdout.write(line + '\n');
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
function isPlainOutput(outputConfig: OutputConfig): boolean {
|
|
78
|
+
return !outputConfig.unicode || !outputConfig.interactive;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function canUseInteractiveUI(outputConfig: OutputConfig): boolean {
|
|
82
|
+
return outputConfig.unicode && outputConfig.interactive;
|
|
83
|
+
}
|
|
84
|
+
|
|
76
85
|
type SpinnerInstance = ReturnType<typeof p.spinner>;
|
|
77
86
|
|
|
78
87
|
type SpinnerEntry = {
|
|
@@ -172,7 +181,7 @@ function updateParallelSpinnerMessage(state: AdapterState): void {
|
|
|
172
181
|
|
|
173
182
|
/**
|
|
174
183
|
* Display a log message.
|
|
175
|
-
* Uses clack's log functions in unicode mode,
|
|
184
|
+
* Uses clack's log functions in interactive unicode mode, line output otherwise.
|
|
176
185
|
*
|
|
177
186
|
* @param level - The log level
|
|
178
187
|
* @param message - The message to display
|
|
@@ -187,8 +196,8 @@ function displayLog(
|
|
|
187
196
|
): void {
|
|
188
197
|
const { outputConfig, symbols } = state;
|
|
189
198
|
|
|
190
|
-
// In plain
|
|
191
|
-
if (
|
|
199
|
+
// In plain or non-interactive mode, use simple console output
|
|
200
|
+
if (isPlainOutput(outputConfig)) {
|
|
192
201
|
const prefix = indented ? `${symbols.groupLine} ` : '';
|
|
193
202
|
const levelPrefix = {
|
|
194
203
|
info: symbols.info,
|
|
@@ -201,7 +210,7 @@ function displayLog(
|
|
|
201
210
|
return;
|
|
202
211
|
}
|
|
203
212
|
|
|
204
|
-
// Unicode mode - use clack's decorative output
|
|
213
|
+
// Unicode + interactive mode - use clack's decorative output
|
|
205
214
|
const prefix = indented ? '\u2502 ' : ''; // │ for indented logs
|
|
206
215
|
const formattedMessage = prefix + message;
|
|
207
216
|
|
|
@@ -261,12 +270,13 @@ export type ReporterAdapterOptions = {
|
|
|
261
270
|
* @param options - Optional configuration for the adapter
|
|
262
271
|
*/
|
|
263
272
|
export function createReporterAdapter(options?: ReporterAdapterOptions): ReporterAdapter {
|
|
264
|
-
// Get output config from options, or
|
|
265
|
-
const outputConfig: OutputConfig = options?.output ??
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
273
|
+
// Get output config from options, or detect from args/env
|
|
274
|
+
const outputConfig: OutputConfig = options?.output ?? detectOutputConfig(process.argv.slice(2));
|
|
275
|
+
// Backwards compatibility: default interactive when missing
|
|
276
|
+
if (outputConfig.interactive === undefined) {
|
|
277
|
+
outputConfig.interactive = true;
|
|
278
|
+
}
|
|
279
|
+
|
|
270
280
|
// Verbose can be set via options.verbose for backwards compatibility
|
|
271
281
|
if (options?.verbose !== undefined) {
|
|
272
282
|
outputConfig.verbose = options.verbose;
|
|
@@ -307,8 +317,8 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
307
317
|
hasFailure: false,
|
|
308
318
|
});
|
|
309
319
|
|
|
310
|
-
// In plain mode, use simple bracket notation
|
|
311
|
-
if (
|
|
320
|
+
// In plain or non-interactive mode, use simple bracket notation
|
|
321
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
312
322
|
const label = colorize(event.label, pc.bold, state.outputConfig.color);
|
|
313
323
|
writeLine(`${state.symbols.groupStart}${label}${state.symbols.groupEnd}`);
|
|
314
324
|
} else {
|
|
@@ -384,7 +394,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
384
394
|
for (const [activityId, activity] of state.parallelActivities) {
|
|
385
395
|
if (activity.groupId !== event.id) continue;
|
|
386
396
|
if (activity.status === 'success') {
|
|
387
|
-
if (
|
|
397
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
388
398
|
const prefix = colorize(
|
|
389
399
|
state.symbols.success,
|
|
390
400
|
pc.green,
|
|
@@ -397,7 +407,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
397
407
|
} else if (activity.status === 'failure') {
|
|
398
408
|
hasFailures = true;
|
|
399
409
|
// Show label inside group, defer error message with remediation
|
|
400
|
-
if (
|
|
410
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
401
411
|
const prefix = colorize(state.symbols.error, pc.red, state.outputConfig.color);
|
|
402
412
|
writeLine(` ${prefix} ${activity.label}`);
|
|
403
413
|
} else {
|
|
@@ -424,8 +434,8 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
424
434
|
}
|
|
425
435
|
|
|
426
436
|
// Show appropriate outro based on whether there were failures
|
|
427
|
-
if (
|
|
428
|
-
// Plain
|
|
437
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
438
|
+
// Plain output - simple bracket notation
|
|
429
439
|
if (hasFailures) {
|
|
430
440
|
const failedText = colorize(state.symbols.failed, pc.red, state.outputConfig.color);
|
|
431
441
|
writeLine(`${state.symbols.groupStart}${failedText}${state.symbols.groupEnd}`);
|
|
@@ -434,7 +444,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
434
444
|
writeLine(`${state.symbols.groupStart}${doneText}${state.symbols.groupEnd}`);
|
|
435
445
|
}
|
|
436
446
|
} else {
|
|
437
|
-
// Unicode mode - use clack's outro
|
|
447
|
+
// Unicode + interactive mode - use clack's outro
|
|
438
448
|
if (hasFailures) {
|
|
439
449
|
p.outro(
|
|
440
450
|
colorize(`${state.symbols.failed} Failed`, pc.red, state.outputConfig.color)
|
|
@@ -446,7 +456,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
446
456
|
|
|
447
457
|
// Print deferred error messages with remediation after the group closes
|
|
448
458
|
for (const deferred of deferredErrors) {
|
|
449
|
-
if (
|
|
459
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
450
460
|
writeLine(`${state.symbols.error} ${deferred.error}`);
|
|
451
461
|
} else {
|
|
452
462
|
p.log.error(deferred.error);
|
|
@@ -485,15 +495,15 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
485
495
|
status: 'pending',
|
|
486
496
|
});
|
|
487
497
|
|
|
488
|
-
// In plain mode,
|
|
489
|
-
if (
|
|
498
|
+
// In plain or non-interactive mode, track activities - results shown at group:end
|
|
499
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
490
500
|
if (!state.parallelSpinnerGroupId) {
|
|
491
501
|
state.parallelSpinnerGroupId = event.parentId as GroupId;
|
|
492
502
|
const activities = state.parallelActivities.size;
|
|
493
503
|
writeLine(` Running ${activities} task${activities > 1 ? 's' : ''}...`);
|
|
494
504
|
}
|
|
495
505
|
} else {
|
|
496
|
-
// Unicode mode: create or update the parallel spinner
|
|
506
|
+
// Unicode + interactive mode: create or update the parallel spinner
|
|
497
507
|
if (!state.parallelSpinner) {
|
|
498
508
|
const spinner = p.spinner();
|
|
499
509
|
state.parallelSpinner = {
|
|
@@ -508,16 +518,16 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
508
518
|
}
|
|
509
519
|
} else {
|
|
510
520
|
// Sequential activity
|
|
511
|
-
if (
|
|
512
|
-
// Plain
|
|
521
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
522
|
+
// Plain output: track activity without spinner - result shown on completion
|
|
513
523
|
state.spinners.set(event.id, {
|
|
514
|
-
spinner: null as unknown as SpinnerInstance, // Not used in
|
|
524
|
+
spinner: null as unknown as SpinnerInstance, // Not used in non-interactive output
|
|
515
525
|
label: event.label,
|
|
516
526
|
currentMessage: event.label,
|
|
517
527
|
parentGroupId: event.parentId as GroupId | undefined,
|
|
518
528
|
});
|
|
519
529
|
} else {
|
|
520
|
-
// Unicode mode: create individual spinner
|
|
530
|
+
// Unicode + interactive mode: create individual spinner
|
|
521
531
|
const spinner = p.spinner();
|
|
522
532
|
state.spinners.set(event.id, {
|
|
523
533
|
spinner,
|
|
@@ -540,7 +550,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
540
550
|
// Update the activity label if message provided
|
|
541
551
|
if (event.payload.message) {
|
|
542
552
|
parallelActivity.label = event.payload.message;
|
|
543
|
-
if (state.outputConfig
|
|
553
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
544
554
|
updateParallelSpinnerMessage(state);
|
|
545
555
|
}
|
|
546
556
|
}
|
|
@@ -556,7 +566,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
556
566
|
if (text) {
|
|
557
567
|
entry.currentMessage = text;
|
|
558
568
|
// Only update spinner in unicode mode
|
|
559
|
-
if (state.outputConfig
|
|
569
|
+
if (canUseInteractiveUI(state.outputConfig) && entry.spinner) {
|
|
560
570
|
entry.spinner.message(text);
|
|
561
571
|
}
|
|
562
572
|
}
|
|
@@ -569,7 +579,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
569
579
|
const parallelActivity = state.parallelActivities.get(event.id);
|
|
570
580
|
if (parallelActivity) {
|
|
571
581
|
parallelActivity.status = 'success';
|
|
572
|
-
if (state.outputConfig
|
|
582
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
573
583
|
updateParallelSpinnerMessage(state);
|
|
574
584
|
}
|
|
575
585
|
// Note: For parallel activities, logs will be flushed at group:end
|
|
@@ -579,12 +589,12 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
579
589
|
// Sequential activity
|
|
580
590
|
const entry = state.spinners.get(event.id);
|
|
581
591
|
if (entry) {
|
|
582
|
-
if (
|
|
583
|
-
// Plain
|
|
592
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
593
|
+
// Plain output - print success line
|
|
584
594
|
const prefix = colorize(state.symbols.success, pc.green, state.outputConfig.color);
|
|
585
595
|
writeLine(` ${prefix} ${entry.label}`);
|
|
586
596
|
} else if (entry.spinner) {
|
|
587
|
-
// Unicode mode - stop spinner
|
|
597
|
+
// Unicode + interactive mode - stop spinner
|
|
588
598
|
entry.spinner.stop(entry.label, 0);
|
|
589
599
|
}
|
|
590
600
|
state.spinners.delete(event.id);
|
|
@@ -595,7 +605,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
595
605
|
// Check if this was a suspended activity
|
|
596
606
|
const suspended = state.suspendedActivities.get(event.id);
|
|
597
607
|
if (suspended && !state.suspended) {
|
|
598
|
-
if (
|
|
608
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
599
609
|
const prefix = colorize(
|
|
600
610
|
state.symbols.success,
|
|
601
611
|
pc.green,
|
|
@@ -626,7 +636,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
626
636
|
if (parentGroup) {
|
|
627
637
|
parentGroup.hasFailure = true;
|
|
628
638
|
}
|
|
629
|
-
if (state.outputConfig
|
|
639
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
630
640
|
updateParallelSpinnerMessage(state);
|
|
631
641
|
}
|
|
632
642
|
break;
|
|
@@ -643,12 +653,12 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
643
653
|
}
|
|
644
654
|
}
|
|
645
655
|
|
|
646
|
-
if (
|
|
647
|
-
// Plain
|
|
656
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
657
|
+
// Plain output - print error line
|
|
648
658
|
const prefix = colorize(state.symbols.error, pc.red, state.outputConfig.color);
|
|
649
659
|
writeLine(` ${prefix} ${errorMessage}`);
|
|
650
660
|
} else if (entry.spinner) {
|
|
651
|
-
// Unicode mode - stop spinner with error
|
|
661
|
+
// Unicode + interactive mode - stop spinner with error
|
|
652
662
|
entry.spinner.stop(errorMessage, 1);
|
|
653
663
|
}
|
|
654
664
|
state.spinners.delete(event.id);
|
|
@@ -675,7 +685,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
675
685
|
// Check if this was a suspended activity
|
|
676
686
|
const suspended = state.suspendedActivities.get(event.id);
|
|
677
687
|
if (suspended && !state.suspended) {
|
|
678
|
-
if (
|
|
688
|
+
if (isPlainOutput(state.outputConfig)) {
|
|
679
689
|
const prefix = colorize(state.symbols.error, pc.red, state.outputConfig.color);
|
|
680
690
|
writeLine(` ${prefix} ${suspended.label}: ${errorMessage}`);
|
|
681
691
|
} else {
|
|
@@ -704,7 +714,7 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
704
714
|
event.level === 'error' &&
|
|
705
715
|
hasActiveSpinners &&
|
|
706
716
|
event.activityId &&
|
|
707
|
-
state.outputConfig
|
|
717
|
+
canUseInteractiveUI(state.outputConfig)
|
|
708
718
|
) {
|
|
709
719
|
const spinner = state.spinners.get(event.activityId);
|
|
710
720
|
if (spinner && spinner.spinner) {
|
|
@@ -747,8 +757,8 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
747
757
|
// Reporter control events
|
|
748
758
|
case 'reporter:suspend': {
|
|
749
759
|
state.suspended = true;
|
|
750
|
-
// Stop all active spinners and track them for completion messages
|
|
751
|
-
if (state.outputConfig
|
|
760
|
+
// Stop all active spinners and track them for completion messages
|
|
761
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
752
762
|
for (const [id, entry] of state.spinners) {
|
|
753
763
|
try {
|
|
754
764
|
if (entry.spinner) {
|
|
@@ -785,8 +795,8 @@ export function createReporterAdapter(options?: ReporterAdapterOptions): Reporte
|
|
|
785
795
|
|
|
786
796
|
return {
|
|
787
797
|
stop(): void {
|
|
788
|
-
// Stop all active spinners (only in
|
|
789
|
-
if (state.outputConfig
|
|
798
|
+
// Stop all active spinners (only in interactive mode where spinners exist)
|
|
799
|
+
if (canUseInteractiveUI(state.outputConfig)) {
|
|
790
800
|
for (const entry of state.spinners.values()) {
|
|
791
801
|
try {
|
|
792
802
|
if (entry.spinner) {
|