@@ -174,6 +174,7 @@ function REPLServer(prompt,
174
174
self . _domain . on ( 'error' , function ( e ) {
175
175
debug ( 'domain error' ) ;
176
176
self . outputStream . write ( ( e . stack || e ) + '\n' ) ;
177
+ self . _currentStringLiteral = null ;
177
178
self . bufferedCommand = '' ;
178
179
self . lines . level = [ ] ;
179
180
self . displayPrompt ( ) ;
@@ -200,6 +201,8 @@ function REPLServer(prompt,
200
201
self . outputStream = output ;
201
202
202
203
self . resetContext ( ) ;
204
+ // Initialize the current string literal found, to be null
205
+ self . _currentStringLiteral = null ;
203
206
self . bufferedCommand = '' ;
204
207
self . lines . level = [ ] ;
205
208
@@ -258,21 +261,86 @@ function REPLServer(prompt,
258
261
sawSIGINT = false ;
259
262
}
260
263
264
+ self . _currentStringLiteral = null ;
261
265
self . bufferedCommand = '' ;
262
266
self . lines . level = [ ] ;
263
267
self . displayPrompt ( ) ;
264
268
} ) ;
265
269
270
+ function parseLine ( line , currentStringLiteral ) {
271
+ var previous = null , current = null ;
272
+
273
+ for ( var i = 0 ; i < line . length ; i += 1 ) {
274
+ if ( previous === '\\' ) {
275
+ // if it is a valid escaping, then skip processing
276
+ previous = current ;
277
+ continue ;
278
+ }
279
+
280
+ current = line . charAt ( i ) ;
281
+ if ( current === currentStringLiteral ) {
282
+ currentStringLiteral = null ;
283
+ } else if ( current === '\'' ||
284
+ current === '"' &&
285
+ currentStringLiteral === null ) {
286
+ currentStringLiteral = current ;
287
+ }
288
+ previous = current ;
289
+ }
290
+
291
+ return currentStringLiteral ;
292
+ }
293
+
294
+ function getFinisherFunction ( cmd , defaultFn ) {
295
+ if ( ( self . _currentStringLiteral === null &&
296
+ cmd . charAt ( cmd . length - 1 ) === '\\' ) ||
297
+ ( self . _currentStringLiteral !== null &&
298
+ cmd . charAt ( cmd . length - 1 ) !== '\\' ) ) {
299
+
300
+ // If the line continuation is used outside string literal or if the
301
+ // string continuation happens with out line continuation, then fail hard.
302
+ // Even if the error is recoverable, get the underlying error and use it.
303
+ return function ( e , ret ) {
304
+ var error = e instanceof Recoverable ? e . err : e ;
305
+
306
+ if ( arguments . length === 2 ) {
307
+ // using second argument only if it is actually passed. Otherwise
308
+ // `undefined` will be printed when invalid REPL commands are used.
309
+ return defaultFn ( error , ret ) ;
310
+ }
311
+
312
+ return defaultFn ( error ) ;
313
+ } ;
314
+ }
315
+ return defaultFn ;
316
+ }
317
+
266
318
self . on ( 'line' , function ( cmd ) {
267
319
debug ( 'line %j' , cmd ) ;
268
320
sawSIGINT = false ;
269
321
var skipCatchall = false ;
322
+ var finisherFn = finish ;
270
323
271
324
// leading whitespaces in template literals should not be trimmed.
272
325
if ( self . _inTemplateLiteral ) {
273
326
self . _inTemplateLiteral = false ;
274
327
} else {
275
- cmd = trimWhitespace ( cmd ) ;
328
+ const wasWithinStrLiteral = self . _currentStringLiteral !== null ;
329
+ self . _currentStringLiteral = parseLine ( cmd , self . _currentStringLiteral ) ;
330
+ const isWithinStrLiteral = self . _currentStringLiteral !== null ;
331
+
332
+ if ( ! wasWithinStrLiteral && ! isWithinStrLiteral ) {
333
+ // Current line has nothing to do with String literals, trim both ends
334
+ cmd = cmd . trim ( ) ;
335
+ } else if ( wasWithinStrLiteral && ! isWithinStrLiteral ) {
336
+ // was part of a string literal, but it is over now, trim only the end
337
+ cmd = cmd . trimRight ( ) ;
338
+ } else if ( isWithinStrLiteral && ! wasWithinStrLiteral ) {
339
+ // was not part of a string literal, but it is now, trim only the start
340
+ cmd = cmd . trimLeft ( ) ;
341
+ }
342
+
343
+ finisherFn = getFinisherFunction ( cmd , finish ) ;
276
344
}
277
345
278
346
// Check to see if a REPL keyword was used. If it returns true,
@@ -305,9 +373,9 @@ function REPLServer(prompt,
305
373
}
306
374
307
375
debug ( 'eval %j' , evalCmd ) ;
308
- self . eval ( evalCmd , self . context , 'repl' , finish ) ;
376
+ self . eval ( evalCmd , self . context , 'repl' , finisherFn ) ;
309
377
} else {
310
- finish ( null ) ;
378
+ finisherFn ( null ) ;
311
379
}
312
380
313
381
function finish ( e , ret ) {
@@ -318,6 +386,7 @@ function REPLServer(prompt,
318
386
self . outputStream . write ( 'npm should be run outside of the ' +
319
387
'node repl, in your normal shell.\n' +
320
388
'(Press Control-D to exit.)\n' ) ;
389
+ self . _currentStringLiteral = null ;
321
390
self . bufferedCommand = '' ;
322
391
self . displayPrompt ( ) ;
323
392
return ;
@@ -339,6 +408,7 @@ function REPLServer(prompt,
339
408
}
340
409
341
410
// Clear buffer if no SyntaxErrors
411
+ self . _currentStringLiteral = null ;
342
412
self . bufferedCommand = '' ;
343
413
344
414
// If we got any output - print it (if no error)
@@ -870,6 +940,7 @@ function defineDefaultCommands(repl) {
870
940
repl . defineCommand ( 'break' , {
871
941
help : 'Sometimes you get stuck, this gets you out' ,
872
942
action : function ( ) {
943
+ this . _currentStringLiteral = null ;
873
944
this . bufferedCommand = '' ;
874
945
this . displayPrompt ( ) ;
875
946
}
@@ -884,6 +955,7 @@ function defineDefaultCommands(repl) {
884
955
repl . defineCommand ( 'clear' , {
885
956
help : clearMessage ,
886
957
action : function ( ) {
958
+ this . _currentStringLiteral = null ;
887
959
this . bufferedCommand = '' ;
888
960
if ( ! this . useGlobal ) {
889
961
this . outputStream . write ( 'Clearing context...\n' ) ;
@@ -949,18 +1021,6 @@ function defineDefaultCommands(repl) {
949
1021
} ) ;
950
1022
}
951
1023
952
-
953
- function trimWhitespace ( cmd ) {
954
- const trimmer = / ^ \s * ( .+ ) \s * $ / m;
955
- var matches = trimmer . exec ( cmd ) ;
956
-
957
- if ( matches && matches . length === 2 ) {
958
- return matches [ 1 ] ;
959
- }
960
- return '' ;
961
- }
962
-
963
-
964
1024
function regexpEscape ( s ) {
965
1025
return s . replace ( / [ - [ \] { } ( ) * + ? . , \\ ^ $ | # \s ] / g, '\\$&' ) ;
966
1026
}
0 commit comments