Skip to content

Commit ce41348

Browse files
authored
Merge pull request #2 from cp90-pixel/codex/fix-npm-run-output-erasure-issue
fix: preserve npm run output without trailing newline
2 parents 67cfaf3 + 94854bc commit ce41348

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

lib/utils/display.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ class Progress {
441441
#lastUpdate = 0
442442
#interval
443443
#timeout
444+
#rendered = false
444445

445446
// We are rendering is enabled option is set and we are not waiting for the render timeout
446447
get #rendering () {
@@ -527,12 +528,17 @@ class Progress {
527528
}
528529
this.#clearSpinner()
529530
this.#stream.write(this.#spinner.frames[this.#frameIndex])
531+
this.#rendered = true
530532
}
531533

532534
#clearSpinner () {
535+
if (!this.#rendered) {
536+
return
537+
}
533538
// Move to the start of the line and clear the rest of the line
534539
this.#stream.cursorTo(0)
535540
this.#stream.clearLine(1)
541+
this.#rendered = false
536542
}
537543
}
538544

test/lib/utils/display.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const mockDisplay = async (t, { mocks, load } = {}) => {
2929
...procLog,
3030
display,
3131
displayLoad,
32+
streams: logs.streams,
3233
...logs.logs,
3334
}
3435
}
@@ -101,6 +102,50 @@ t.test('can do progress', async (t) => {
101102
t.strictSame(outputs, ['before input', 'during input', 'after input'])
102103
})
103104

105+
t.test('progress resume does not clear output when spinner inactive', async (t) => {
106+
const { input, output, outputs, streams } = await mockDisplay(t, {
107+
load: {
108+
progress: true,
109+
},
110+
})
111+
112+
const origClearLine = streams.stderr.clearLine
113+
const origCursorTo = streams.stderr.cursorTo
114+
let clearCalls = 0
115+
let cursorCalls = 0
116+
streams.stderr.clearLine = (...args) => {
117+
clearCalls++
118+
return origClearLine.call(streams.stderr, ...args)
119+
}
120+
streams.stderr.cursorTo = (...args) => {
121+
cursorCalls++
122+
return origCursorTo.call(streams.stderr, ...args)
123+
}
124+
125+
t.teardown(() => {
126+
streams.stderr.clearLine = origClearLine
127+
streams.stderr.cursorTo = origCursorTo
128+
})
129+
130+
// ensure the spinner has rendered at least once so progress.off clears it
131+
await timers.setTimeout(300)
132+
133+
const endInput = input.start()
134+
await timers.setTimeout(0)
135+
136+
clearCalls = 0
137+
cursorCalls = 0
138+
139+
output.standard('no trailing newline')
140+
141+
endInput()
142+
await timers.setTimeout(0)
143+
144+
t.equal(clearCalls, 0, 'resume does not clear the line when spinner inactive')
145+
t.equal(cursorCalls, 0, 'resume does not reposition cursor when spinner inactive')
146+
t.equal(outputs.at(-1), 'no trailing newline', 'output remains visible')
147+
})
148+
104149
t.test('handles log throwing', async (t) => {
105150
class ThrowInspect {
106151
#crashes = 0;

0 commit comments

Comments
 (0)