GeezerVison is showing github.com — repl: show reference errors during preview · nodejs/node@d33dcf1
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/repl/utils.js +21-2Lines changed: 21 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -48,6 +48,8 @@ const previewOptions = { | 48 | 48 | showHidden: false | 49 | 49 | }; | 50 | 50 |
| | 51 | +const REPL_MODE_STRICT = Symbol('repl-strict'); | | 52 | + | 51 | 53 | // If the error is that we've unexpectedly ended the input, | 52 | 54 | // then let the user try to recover by adding more input. | 53 | 55 | // Note: `e` (the original exception) is not used by the current implementation, | @@ -136,6 +138,8 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) { | 136 | 138 | let previewCompletionCounter = 0; | 137 | 139 | let completionPreview = null; | 138 | 140 |
| | 141 | + let hasCompletions = false; | | 142 | + | 139 | 143 | let wrapped = false; | 140 | 144 |
| 141 | 145 | let escaped = null; | @@ -229,6 +233,8 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) { | 229 | 233 | return; | 230 | 234 | } | 231 | 235 |
| | 236 | + hasCompletions = true; | | 237 | + | 232 | 238 | // If there is a common prefix to all matches, then apply that portion. | 233 | 239 | const completions = rawCompletions.filter((e) => e); | 234 | 240 | const prefix = commonPrefix(completions); | @@ -265,6 +271,12 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) { | 265 | 271 | }); | 266 | 272 | } | 267 | 273 |
| | 274 | + function isInStrictMode(repl) { | | 275 | + return repl.replMode === REPL_MODE_STRICT || process.execArgv | | 276 | + .map((e) => e.toLowerCase().replace(/_/g, '-')) | | 277 | + .includes('--use-strict'); | | 278 | + } | | 279 | + | 268 | 280 | // This returns a code preview for arbitrary input code. | 269 | 281 | function getInputPreview(input, callback) { | 270 | 282 | // For similar reasons as `defaultEval`, wrap expressions starting with a | @@ -292,8 +304,11 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) { | 292 | 304 | // may be inspected. | 293 | 305 | } else if (preview.exceptionDetails && | 294 | 306 | (result.className === 'EvalError' || | 295 | | - result.className === 'SyntaxError' || | 296 | | - result.className === 'ReferenceError')) { | | 307 | + result.className === 'SyntaxError' || | | 308 | + // Report ReferenceError in case the strict mode is active | | 309 | + // for input that has no completions. | | 310 | + (result.className === 'ReferenceError' && | | 311 | + (hasCompletions || !isInStrictMode(repl))))) { | 297 | 312 | callback(null, null); | 298 | 313 | } else if (result.objectId) { | 299 | 314 | // The writer options might change and have influence on the inspect | @@ -339,6 +354,8 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) { | 339 | 354 | return; | 340 | 355 | } | 341 | 356 |
| | 357 | + hasCompletions = false; | | 358 | + | 342 | 359 | // Add the autocompletion preview. | 343 | 360 | const insertPreview = false; | 344 | 361 | showCompletionPreview(repl.line, insertPreview); | @@ -703,6 +720,8 @@ function setupReverseSearch(repl) { | 703 | 720 | } | 704 | 721 |
| 705 | 722 | module.exports = { | | 723 | + REPL_MODE_SLOPPY: Symbol('repl-sloppy'), | | 724 | + REPL_MODE_STRICT, | 706 | 725 | isRecoverableError, | 707 | 726 | kStandaloneREPL: Symbol('kStandaloneREPL'), | 708 | 727 | setupPreview, | |
Collapse file Copy file name to clipboardExpand all lines: lib/repl.js +5-4Lines changed: 5 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -103,6 +103,8 @@ const experimentalREPLAwait = require('internal/options').getOptionValue( | 103 | 103 | '--experimental-repl-await' | 104 | 104 | ); | 105 | 105 | const { | | 106 | + REPL_MODE_SLOPPY, | | 107 | + REPL_MODE_STRICT, | 106 | 108 | isRecoverableError, | 107 | 109 | kStandaloneREPL, | 108 | 110 | setupPreview, | @@ -363,8 +365,7 @@ function REPLServer(prompt, | 363 | 365 | } | 364 | 366 | while (true) { | 365 | 367 | try { | 366 | | - if (!/^\s*$/.test(code) && | 367 | | - self.replMode === exports.REPL_MODE_STRICT) { | | 368 | + if (self.replMode === exports.REPL_MODE_STRICT && !/^\s*$/.test(code)) { | 368 | 369 | // "void 0" keeps the repl from returning "use strict" as the result | 369 | 370 | // value for statements and declarations that don't return a value. | 370 | 371 | code = `'use strict'; void 0;\n${code}`; | @@ -896,8 +897,8 @@ ObjectSetPrototypeOf(REPLServer, Interface); | 896 | 897 |
| 897 | 898 | exports.REPLServer = REPLServer; | 898 | 899 |
| 899 | | -exports.REPL_MODE_SLOPPY = Symbol('repl-sloppy'); | 900 | | -exports.REPL_MODE_STRICT = Symbol('repl-strict'); | | 900 | +exports.REPL_MODE_SLOPPY = REPL_MODE_SLOPPY; | | 901 | +exports.REPL_MODE_STRICT = REPL_MODE_STRICT; | 901 | 902 |
| 902 | 903 | // Prompt is a string to print on each line for the prompt, | 903 | 904 | // source is a stream to use for I/O, defaulting to stdin/stdout. | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-repl-mode.js +17-3Lines changed: 17 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -7,7 +7,8 @@ const repl = require('repl'); | 7 | 7 | const tests = [ | 8 | 8 | testSloppyMode, | 9 | 9 | testStrictMode, | 10 | | - testAutoMode | | 10 | + testAutoMode, | | 11 | + testStrictModeTerminal, | 11 | 12 | ]; | 12 | 13 |
| 13 | 14 | tests.forEach(function(test) { | @@ -37,6 +38,18 @@ function testStrictMode() { | 37 | 38 | assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> '); | 38 | 39 | } | 39 | 40 |
| | 41 | +function testStrictModeTerminal() { | | 42 | + // Verify that ReferenceErrors are reported in strict mode previews. | | 43 | + const cli = initRepl(repl.REPL_MODE_STRICT, { | | 44 | + terminal: true | | 45 | + }); | | 46 | + | | 47 | + cli.input.emit('data', 'xyz '); | | 48 | + assert.ok( | | 49 | + cli.output.accumulator.includes('\n// ReferenceError: xyz is not defined') | | 50 | + ); | | 51 | +} | | 52 | + | 40 | 53 | function testAutoMode() { | 41 | 54 | const cli = initRepl(repl.REPL_MODE_MAGIC); | 42 | 55 |
| @@ -48,7 +61,7 @@ function testAutoMode() { | 48 | 61 | assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> '); | 49 | 62 | } | 50 | 63 |
| 51 | | -function initRepl(mode) { | | 64 | +function initRepl(mode, options) { | 52 | 65 | const input = new Stream(); | 53 | 66 | input.write = input.pause = input.resume = () => {}; | 54 | 67 | input.readable = true; | @@ -65,6 +78,7 @@ function initRepl(mode) { | 65 | 78 | output: output, | 66 | 79 | useColors: false, | 67 | 80 | terminal: false, | 68 | | - replMode: mode | | 81 | + replMode: mode, | | 82 | + ...options | 69 | 83 | }); | 70 | 84 | } |
Collapse file Copy file name to clipboard +46Lines changed: 46 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +// Previews in strict mode should indicate ReferenceErrors. | | 2 | + | | 3 | +'use strict'; | | 4 | + | | 5 | +const common = require('../common'); | | 6 | + | | 7 | +common.skipIfInspectorDisabled(); | | 8 | + | | 9 | +if (process.argv[2] === 'child') { | | 10 | + const stream = require('stream'); | | 11 | + const repl = require('repl'); | | 12 | + class ActionStream extends stream.Stream { | | 13 | + readable = true; | | 14 | + run(data) { | | 15 | + this.emit('data', `${data}`); | | 16 | + this.emit('keypress', '', { ctrl: true, name: 'd' }); | | 17 | + } | | 18 | + resume() {} | | 19 | + pause() {} | | 20 | + } | | 21 | + | | 22 | + repl.start({ | | 23 | + input: new ActionStream(), | | 24 | + output: new stream.Writable({ | | 25 | + write(chunk, _, next) { | | 26 | + console.log(chunk.toString()); | | 27 | + next(); | | 28 | + } | | 29 | + }), | | 30 | + useColors: false, | | 31 | + terminal: true | | 32 | + }).inputStream.run('xyz'); | | 33 | +} else { | | 34 | + const assert = require('assert'); | | 35 | + const { spawnSync } = require('child_process'); | | 36 | + | | 37 | + const result = spawnSync( | | 38 | + process.execPath, | | 39 | + ['--use-strict', `${__filename}`, 'child'] | | 40 | + ); | | 41 | + | | 42 | + assert.match( | | 43 | + result.stdout.toString(), | | 44 | + /\/\/ ReferenceError: xyz is not defined/ | | 45 | + ); | | 46 | +} |
|