Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/cli-repl/src/async-repl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { once } from 'events';
import chai, { expect } from 'chai';
import sinon from 'ts-sinon';
import sinonChai from 'sinon-chai';
import { tick } from '../test/repl-helpers';
chai.use(sinonChai);

const delay = promisify(setTimeout);
Expand Down Expand Up @@ -123,6 +124,25 @@ describe('AsyncRepl', () => {
expect(foundUid).to.be.true;
});

it('delays the "exit" event until after asynchronous evaluation is finished', async() => {
const { input, repl } = createDefaultAsyncRepl();
let exited = false;
repl.on('exit', () => { exited = true; });

let resolve;
repl.context.asyncFn = () => new Promise((res) => { resolve = res; });

input.end('asyncFn()\n');
expect(exited).to.be.false;

await tick();
resolve();
expect(exited).to.be.false;

await tick();
expect(exited).to.be.true;
});

describe('allows handling exceptions from e.g. the writer function', () => {
it('for succesful completions', async() => {
const error = new Error('throwme');
Expand Down
20 changes: 20 additions & 0 deletions packages/cli-repl/src/async-repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,13 @@ export function start(opts: AsyncREPLOptions): REPLServer {
repl.emit(evalStart, { input } as EvalStartEvent);

try {
let exitEventPending = false;
const exitListener = () => { exitEventPending = true; };
let previousExitListeners: any[] = [];

let sigintListener: (() => void) | undefined = undefined;
let previousSigintListeners: any[] = [];

try {
result = await new Promise((resolve, reject) => {
if (breakEvalOnSigint) {
Expand All @@ -68,6 +73,13 @@ export function start(opts: AsyncREPLOptions): REPLServer {
repl.once('SIGINT', sigintListener);
}

// The REPL may become over-eager and emit 'exit' events while our
// evaluation is still in progress (because it doesn't expect async
// evaluation). If that happens, defer the event until later.
previousExitListeners = repl.rawListeners('exit');
repl.removeAllListeners('exit');
repl.once('exit', exitListener);

const evalResult = asyncEval(originalEval, input, context, filename);

if (sigintListener !== undefined) {
Expand All @@ -84,6 +96,14 @@ export function start(opts: AsyncREPLOptions): REPLServer {
for (const listener of previousSigintListeners) {
repl.on('SIGINT', listener);
}

repl.removeListener('exit', exitListener);
for (const listener of previousExitListeners) {
repl.on('exit', listener);
}
if (exitEventPending) {
process.nextTick(() => repl.emit('exit'));
}
}
} catch (err) {
try {
Expand Down