@@ -2,6 +2,9 @@ import { CliRepl, parseCliArgs, mapCliToDriver, getStoragePaths, getMongocryptdP
2
2
import { generateUri } from '@mongosh/service-provider-server' ;
3
3
import { redactURICredentials } from '@mongosh/history' ;
4
4
import { runMain } from 'module' ;
5
+ import readline from 'readline' ;
6
+ import askcharacter from 'askcharacter' ;
7
+ import stream from 'stream' ;
5
8
6
9
// eslint-disable-next-line complexity, @typescript-eslint/no-floating-promises
7
10
( async ( ) => {
@@ -15,6 +18,7 @@ import { runMain } from 'module';
15
18
}
16
19
17
20
let repl ;
21
+ let isSingleConsoleProcess = false ;
18
22
try {
19
23
const options = parseCliArgs ( process . argv ) ;
20
24
const { version } = require ( '../package.json' ) ;
@@ -53,6 +57,25 @@ import { runMain } from 'module';
53
57
process . removeAllListeners ( 'SIGINT' ) ;
54
58
}
55
59
60
+ // If we are spawned via Windows doubleclick, ask the user for an URI to
61
+ // connect to. Allow an environment variable to override this for testing.
62
+ isSingleConsoleProcess = ! ! process . env . MONGOSH_FORCE_CONNECTION_STRING_PROMPT ;
63
+ if ( ( ! options . connectionSpecifier &&
64
+ process . platform === 'win32' &&
65
+ process . stdin . isTTY &&
66
+ process . stdout . isTTY ) ||
67
+ isSingleConsoleProcess ) {
68
+ try {
69
+ isSingleConsoleProcess ||= require ( 'get-console-process-list' ) ( ) . length === 1 ;
70
+ } catch { /* ignore */ }
71
+ if ( isSingleConsoleProcess ) {
72
+ const result = await ask ( 'Please enter a MongoDB connection string (Default: mongodb://localhost/): ' ) ;
73
+ if ( result . trim ( ) !== '' ) {
74
+ options . connectionSpecifier = result . trim ( ) ;
75
+ }
76
+ }
77
+ }
78
+
56
79
const driverOptions = mapCliToDriver ( options ) ;
57
80
const driverUri = generateUri ( options ) ;
58
81
@@ -79,6 +102,14 @@ import { runMain } from 'module';
79
102
if ( repl !== undefined ) {
80
103
repl . bus . emit ( 'mongosh:error' , e ) ;
81
104
}
105
+ if ( isSingleConsoleProcess ) {
106
+ // In single-process-console mode, it's confusing for the window to be
107
+ // closed immediately after receiving an error. In that case, ask the
108
+ // user to explicitly close the window.
109
+ process . stdout . write ( 'Press any key to exit: ' ) ;
110
+ await askcharacter ( { input : process . stdin , output : process . stdout } ) ;
111
+ process . stdout . write ( '\n' ) ;
112
+ }
82
113
process . exit ( 1 ) ;
83
114
}
84
115
} ) ( ) ;
@@ -96,3 +127,22 @@ function setTerminalWindowTitle(title: string): void {
96
127
process . stdout . write ( `\u001bk${ title } \u001b\\` ) ;
97
128
}
98
129
}
130
+
131
+ async function ask ( prompt : string ) : Promise < string > {
132
+ const stdinCopy = process . stdin . pipe ( new stream . PassThrough ( ) ) ;
133
+ try {
134
+ const readlineInterface = readline . createInterface ( {
135
+ input : stdinCopy ,
136
+ output : process . stdout ,
137
+ prompt,
138
+ } ) ;
139
+ readlineInterface . prompt ( ) ;
140
+ // for-await automatically reads input lines + closes the readline instance again
141
+ for await ( const line of readlineInterface ) {
142
+ return line ;
143
+ }
144
+ return '' ; // Unreachable
145
+ } finally {
146
+ process . stdin . unpipe ( stdinCopy ) ;
147
+ }
148
+ }
0 commit comments