Skip to content

Commit

Permalink
feat: stop spinner on process exit (#2)
Browse files Browse the repository at this point in the history
Co-authored-by: PondWader <[email protected]>
  • Loading branch information
43081j and PondWader authored Nov 4, 2024
1 parent 501669c commit b1173af
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
30 changes: 30 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class Spinner {

this.tick();
renderer.addComponent(this.component);
this.addListeners();
}

tick() {
Expand All @@ -60,6 +61,34 @@ export class Spinner {
this.refresh();
}

private onProcessExit = (signal?: string | number) => {
if (this.running) {
this.stop();
// SIGTERM is 15, SIGINT is 2
let signalCode;
if (signal === 'SIGTERM') {
signalCode = 15 + 128;
} else if (signal === 'SIGINT') {
signalCode = 2 + 128;
} else {
signalCode = Number(signal);
}
process.exit(signalCode);
}
};

private addListeners() {
process.once('SIGTERM', this.onProcessExit);
process.once('SIGINT', this.onProcessExit);
process.once('exit', this.onProcessExit);
}

private clearListeners() {
process.off('SIGTERM', this.onProcessExit);
process.off('SIGINT', this.onProcessExit);
process.off('exit', this.onProcessExit);
}

refresh() {
let symbol = this.currentSymbol;
if (this.symbolFormatter) symbol = this.symbolFormatter(symbol);
Expand Down Expand Up @@ -113,6 +142,7 @@ export class Spinner {

private end(keepComponent = true) {
clearInterval(this.interval);
this.clearListeners();
if (keepComponent) this.component.finish();
else renderer.removeComponent(this.component);
this.running = false;
Expand Down
48 changes: 48 additions & 0 deletions src/test/spinner-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as assert from 'node:assert/strict';
import {test} from 'node:test';
import {Spinner, Symbols, renderer} from '../index.js';
import {createFinishingRenderedLine, createRenderedLine, createRenderedOutput, interceptStdout, TickMeasuredSpinner} from './utils.js';
import process from 'node:process';
import * as constants from '../constants.js';

async function testEndMethod(method: keyof Symbols, type: 'str' | 'obj', customSymbol?: string) {
Expand Down Expand Up @@ -62,6 +63,53 @@ test('end methods', async (t) => {

assert.equal(stdout, createRenderedLine(constants.DEFAULT_FRAMES[0], '', true) + createFinishingRenderedLine('', ''));
});

await t.test('process exit', async () => {
let exitCode: unknown = 0;
const exitStub = ((code: unknown) => {
exitCode = code;
}) as typeof process.exit;
const originalExit = process.exit;
process.exit = exitStub;

try {
const stdout = await interceptStdout(async () => {
const spinner = new Spinner();
spinner.start();
// TODO (43081j): maybe this'll spook other things? if something is
// listening for SIGTERM
process.emit('SIGTERM', 'SIGTERM');
});

assert.equal(stdout, createRenderedLine(constants.DEFAULT_FRAMES[0], '', true) + constants.CLEAR_LINE + constants.UP_LINE + constants.CLEAR_LINE + constants.SHOW_CURSOR);
assert.equal(exitCode, 128 + 15);
} finally {
process.exit = originalExit;
}
});

await t.test('process exit (via exit event)', async () => {
let exitCode: unknown = 0;
const exitStub = ((code: unknown) => {
exitCode = code;
}) as typeof process.exit;
const originalExit = process.exit;
process.exit = exitStub;

try {
await interceptStdout(async () => {
const spinner = new Spinner();
spinner.start();
// TODO (43081j): maybe this'll spook other things? if something is
// listening for SIGTERM
process.emit('exit', 1);
});

assert.equal(exitCode, 1);
} finally {
process.exit = originalExit;
}
});
});

async function testSpinner(frames?: string[], text?: string, symbolFormatter?: (v: string) => string) {
Expand Down

0 comments on commit b1173af

Please sign in to comment.