From 4c8dccbaa1ab38997c1b9127370785c85fbe1f98 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 3 May 2021 14:50:27 +0200 Subject: [PATCH 1/2] fix(cli-repl): handle thrown primitives MONGOSH-739 --- packages/cli-repl/src/mongosh-repl.spec.ts | 18 ++++++++++++++++++ packages/cli-repl/src/mongosh-repl.ts | 11 +++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/cli-repl/src/mongosh-repl.spec.ts b/packages/cli-repl/src/mongosh-repl.spec.ts index b9f5f8fd90..e8121c2895 100644 --- a/packages/cli-repl/src/mongosh-repl.spec.ts +++ b/packages/cli-repl/src/mongosh-repl.spec.ts @@ -531,6 +531,24 @@ describe('MongoshNodeRepl', () => { await tick(); expect(stripAnsi(output)).to.equal('\n> '); }); + + context('thrown non-Errors', () => { + it('allows `throw null`', async() => { + output = ''; + input.write('throw null;\n'); + await waitEval(bus); + // We do verify that both `Error` and `null` are syntax-highlighted here. + expect(output).to.match(/\x1b\[\d+mError\x1b\[\d+m: \x1b\[\d+mnull\x1b\[\d+m/); + }); + + it('allows `throw number`', async() => { + output = ''; + input.write('throw 123;\n'); + await waitEval(bus); + // We do verify that both `Error` and `123` are syntax-highlighted here. + expect(output).to.match(/\x1b\[\d+mError\x1b\[\d+m: \x1b\[\d+m123\x1b\[\d+m/); + }); + }); }); context('with fake TTY', () => { diff --git a/packages/cli-repl/src/mongosh-repl.ts b/packages/cli-repl/src/mongosh-repl.ts index 21599903cf..99be1b498b 100644 --- a/packages/cli-repl/src/mongosh-repl.ts +++ b/packages/cli-repl/src/mongosh-repl.ts @@ -12,7 +12,7 @@ import prettyRepl from 'pretty-repl'; import { ReplOptions, REPLServer } from 'repl'; import type { Readable, Writable } from 'stream'; import type { ReadStream, WriteStream } from 'tty'; -import { callbackify, promisify } from 'util'; +import { callbackify, promisify, types } from 'util'; import * as asyncRepl from './async-repl'; import clr, { StyleDefinition } from './clr'; import { MONGOSH_WIKI, TELEMETRY_GREETING_MESSAGE } from './constants'; @@ -343,6 +343,13 @@ class MongoshNodeRepl implements EvaluationListener { // topology, server version, etc., so for those, we do not autocomplete // at all and instead leave that to the @mongosh/autocomplete package. return shellResult.type !== null ? null : shellResult.rawValue; + } catch (err) { + if (!types.isNativeError(err)) { + throw new Error(this.formatOutput({ + value: err + })); + } + throw err; } finally { if (!this.insideAutoCompleteOrGetPrompt) { repl.setPrompt(await this.getShellPrompt()); @@ -415,7 +422,7 @@ class MongoshNodeRepl implements EvaluationListener { return (await passwordPromise).toString(); } - formatOutput(value: any): string { + formatOutput(value: { value: any, type?: string }): string { return formatOutput(value, this.getFormatOptions()); } From 51984cd5c0a46e15a90c1ae98d3d9b79be18d4eb Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 3 May 2021 17:03:40 +0200 Subject: [PATCH 2/2] fixup! fix(cli-repl): handle thrown primitives MONGOSH-739 --- packages/cli-repl/src/mongosh-repl.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/cli-repl/src/mongosh-repl.ts b/packages/cli-repl/src/mongosh-repl.ts index 99be1b498b..888664cccb 100644 --- a/packages/cli-repl/src/mongosh-repl.ts +++ b/packages/cli-repl/src/mongosh-repl.ts @@ -12,7 +12,7 @@ import prettyRepl from 'pretty-repl'; import { ReplOptions, REPLServer } from 'repl'; import type { Readable, Writable } from 'stream'; import type { ReadStream, WriteStream } from 'tty'; -import { callbackify, promisify, types } from 'util'; +import { callbackify, promisify } from 'util'; import * as asyncRepl from './async-repl'; import clr, { StyleDefinition } from './clr'; import { MONGOSH_WIKI, TELEMETRY_GREETING_MESSAGE } from './constants'; @@ -344,7 +344,7 @@ class MongoshNodeRepl implements EvaluationListener { // at all and instead leave that to the @mongosh/autocomplete package. return shellResult.type !== null ? null : shellResult.rawValue; } catch (err) { - if (!types.isNativeError(err)) { + if (!isErrorLike(err)) { throw new Error(this.formatOutput({ value: err })); @@ -386,10 +386,7 @@ class MongoshNodeRepl implements EvaluationListener { // This checks for error instances. // The writer gets called immediately by the internal `repl.eval` // in case of errors. - if (result && ( - (result.message !== undefined && typeof result.stack === 'string') || - (result.code !== undefined && result.errmsg !== undefined) - )) { + if (isErrorLike(result)) { // eslint-disable-next-line chai-friendly/no-unused-expressions this._runtimeState?.shellEvaluator.revertState(); @@ -526,4 +523,15 @@ class MongoshNodeRepl implements EvaluationListener { } } +function isErrorLike(value: any): boolean { + try { + return value && ( + (value.message !== undefined && typeof value.stack === 'string') || + (value.code !== undefined && value.errmsg !== undefined) + ); + } catch (err) { + throw new MongoshInternalError(err?.message || String(err)); + } +} + export default MongoshNodeRepl;