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
19 changes: 19 additions & 0 deletions packages/cli-repl/src/mongosh-repl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,25 @@ describe('MongoshNodeRepl', () => {
await waitEval(bus);
expect(output).to.include('ISODate("2021-05-04T15:49:33.000Z")');
});

it('handles a long series of errors', async function() {
input.write('-asdf();\n'.repeat(20));
await waitEval(bus);
expect(mongoshRepl.runtimeState().repl.listenerCount('SIGINT')).to.equal(1);
});

it('does not run statements that should not run', async() => {
input.write(`
sleep(0);
throw new Error();
if (false)
print ('!this should not run!');
`);
for (let i = 0; i < 20; i++) {
await tick();
}
expect(output).not.to.include('!this should not run!');
});
});

context('with terminal: true', () => {
Expand Down
39 changes: 37 additions & 2 deletions packages/cli-repl/src/mongosh-repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import askpassword from 'askpassword';
import { Console } from 'console';
import { once } from 'events';
import prettyRepl from 'pretty-repl';
import { ReplOptions, REPLServer } from 'repl';
import type { Readable, Writable } from 'stream';
import { ReplOptions, REPLServer, start as replStart } from 'repl';
import { Readable, Writable, PassThrough } from 'stream';
import type { ReadStream, WriteStream } from 'tty';
import { callbackify, promisify } from 'util';
import * as asyncRepl from './async-repl';
Expand Down Expand Up @@ -56,6 +56,40 @@ type Mutable<T> = {
-readonly[P in keyof T]: T[P]
};

// https://github.com/nodejs/node/pull/38314
function fixupReplForNodeBug38314(repl: REPLServer): void {
{
// Check whether bug is present:
const input = new PassThrough();
const output = new PassThrough();
const evalFn = (code: any, ctx: any, filename: any, cb: any) => cb(new Error('err'));
const prompt = 'prompt#';
replStart({ input, output, eval: evalFn as any, prompt });
input.end('s\n');
if (!String(output.read()).includes('prompt#prompt#')) {
return; // All good, nothing to do here.
}
}

// If it is, fix up the REPL's domain 'error' listener to not call displayPrompt()
const domain = (repl as any)._domain;
const domainErrorListeners = domain.listeners('error');
const origListener = domainErrorListeners.find((fn: any) => fn.name === 'debugDomainError');
if (!origListener) {
throw new Error('Could not find REPL domain error listener');
}
domain.removeListener('error', origListener);
domain.on('error', function(this: any, err: Error) {
const origDisplayPrompt = repl.displayPrompt;
repl.displayPrompt = () => {};
try {
origListener.call(this, err);
} finally {
repl.displayPrompt = origDisplayPrompt;
}
});
}

/**
* An instance of a `mongosh` REPL, without any of the actual I/O.
*/
Expand Down Expand Up @@ -113,6 +147,7 @@ class MongoshNodeRepl implements EvaluationListener {
(err: Error) => Object.assign(new MongoshInternalError(err.message), { stack: err.stack }),
...this.nodeReplOptions
});
fixupReplForNodeBug38314(repl);

const console = new Console({
stdout: this.output,
Expand Down
4 changes: 1 addition & 3 deletions packages/cli-repl/test/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { promises as fs, createReadStream } from 'fs';
import { promisify } from 'util';
import rimraf from 'rimraf';
import path from 'path';
import { readReplLogfile, hasNodeBug38314 } from './repl-helpers';
import { readReplLogfile } from './repl-helpers';

describe('e2e', function() {
const testServer = startTestServer('shared');
Expand Down Expand Up @@ -558,8 +558,6 @@ describe('e2e', function() {
let result;
result = await shell.executeLine('require("a")');
expect(result).to.match(/Error: Cannot find module 'a'/);
// Wait for double prompt because of Node.js REPL bug
if (hasNodeBug38314()) await eventually(() => shell.assertContainsOutput('> > '));
result = await shell.executeLine('require("./a")');
expect(result).to.match(/^A$/m);
result = await shell.executeLine('require("b")');
Expand Down
16 changes: 1 addition & 15 deletions packages/cli-repl/test/repl-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import chai, { expect } from 'chai';
import sinon from 'ts-sinon';
import sinonChai from 'sinon-chai';
import chaiAsPromised from 'chai-as-promised';
import repl from 'repl';
import { PassThrough } from 'stream';
import type { MongoshBus, MongoshBusEventsMap } from '@mongosh/types';

chai.use(sinonChai);
Expand Down Expand Up @@ -78,17 +76,6 @@ async function readReplLogfile(logPath: string) {
.map((line) => JSON.parse(line));
}

// https://github.com/nodejs/node/pull/38314
function hasNodeBug38314() {
const input = new PassThrough();
const output = new PassThrough();
const evalFn = (code, ctx, filename, cb) => cb(new Error('err'));
const prompt = 'prompt#';
repl.start({ input, output, eval: evalFn, prompt });
input.end('s\n');
return String(output.read()).includes('prompt#prompt#');
}

export {
expect,
sinon,
Expand All @@ -98,6 +85,5 @@ export {
waitEval,
waitCompletion,
fakeTTYProps,
readReplLogfile,
hasNodeBug38314
readReplLogfile
};