GeezerVison is showing github.com — repl: add isValidParentheses check before wrap input · nodejs/node@c206f8d
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-1Lines changed: 21 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -301,7 +301,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) { | 301 | 301 | function getInputPreview(input, callback) { | 302 | 302 | // For similar reasons as `defaultEval`, wrap expressions starting with a | 303 | 303 | // curly brace with parenthesis. | 304 | | - if (!wrapped && input[0] === '{' && input[input.length - 1] !== ';') { | | 304 | + if (!wrapped && input[0] === '{' && input[input.length - 1] !== ';' && isValidSyntax(input)) { | 305 | 305 | input = `(${input})`; | 306 | 306 | wrapped = true; | 307 | 307 | } | @@ -754,6 +754,25 @@ function setupReverseSearch(repl) { | 754 | 754 |
| 755 | 755 | const startsWithBraceRegExp = /^\s*{/; | 756 | 756 | const endsWithSemicolonRegExp = /;\s*$/; | | 757 | +function isValidSyntax(input) { | | 758 | + try { | | 759 | + AcornParser.parse(input, { | | 760 | + ecmaVersion: 'latest', | | 761 | + allowAwaitOutsideFunction: true, | | 762 | + }); | | 763 | + return true; | | 764 | + } catch { | | 765 | + try { | | 766 | + AcornParser.parse(`_=${input}`, { | | 767 | + ecmaVersion: 'latest', | | 768 | + allowAwaitOutsideFunction: true, | | 769 | + }); | | 770 | + return true; | | 771 | + } catch { | | 772 | + return false; | | 773 | + } | | 774 | + } | | 775 | +} | 757 | 776 |
| 758 | 777 | /** | 759 | 778 | * Checks if some provided code represents an object literal. | @@ -776,4 +795,5 @@ module.exports = { | 776 | 795 | setupPreview, | 777 | 796 | setupReverseSearch, | 778 | 797 | isObjectLiteral, | | 798 | + isValidSyntax, | 779 | 799 | }; |
Collapse file Copy file name to clipboardExpand all lines: lib/repl.js +3-1Lines changed: 3 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -177,6 +177,7 @@ const { | 177 | 177 | setupPreview, | 178 | 178 | setupReverseSearch, | 179 | 179 | isObjectLiteral, | | 180 | + isValidSyntax, | 180 | 181 | } = require('internal/repl/utils'); | 181 | 182 | const { | 182 | 183 | constants: { | @@ -445,7 +446,7 @@ function REPLServer(prompt, | 445 | 446 | let awaitPromise = false; | 446 | 447 | const input = code; | 447 | 448 |
| 448 | | - if (isObjectLiteral(code)) { | | 449 | + if (isObjectLiteral(code) && isValidSyntax(code)) { | 449 | 450 | // Add parentheses to make sure `code` is parsed as an expression | 450 | 451 | code = `(${StringPrototypeTrim(code)})\n`; | 451 | 452 | wrappedCmd = true; | @@ -2156,6 +2157,7 @@ module.exports = { | 2156 | 2157 | REPL_MODE_SLOPPY, | 2157 | 2158 | REPL_MODE_STRICT, | 2158 | 2159 | Recoverable, | | 2160 | + isValidSyntax, | 2159 | 2161 | }; | 2160 | 2162 |
| 2161 | 2163 | ObjectDefineProperty(module.exports, 'builtinModules', { | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-repl-preview.js +84-2Lines changed: 84 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -157,6 +157,83 @@ async function tests(options) { | 157 | 157 | '\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', | 158 | 158 | '\x1B[33m1\x1B[39m', | 159 | 159 | ] | | 160 | + }, { | | 161 | + input: 'aaaa', | | 162 | + noPreview: 'Uncaught ReferenceError: aaaa is not defined', | | 163 | + preview: [ | | 164 | + 'aaaa\r', | | 165 | + 'Uncaught ReferenceError: aaaa is not defined', | | 166 | + ] | | 167 | + }, { | | 168 | + input: '/0', | | 169 | + noPreview: '/0', | | 170 | + preview: [ | | 171 | + '/0\r', | | 172 | + '/0', | | 173 | + '^', | | 174 | + '', | | 175 | + 'Uncaught SyntaxError: Invalid regular expression: missing /', | | 176 | + ] | | 177 | + }, { | | 178 | + input: '{})', | | 179 | + noPreview: '{})', | | 180 | + preview: [ | | 181 | + '{})\r', | | 182 | + '{})', | | 183 | + ' ^', | | 184 | + '', | | 185 | + "Uncaught SyntaxError: Unexpected token ')'", | | 186 | + ], | | 187 | + }, { | | 188 | + input: "{ a: '{' }", | | 189 | + noPreview: "{ a: \x1B[32m'{'\x1B[39m }", | | 190 | + preview: [ | | 191 | + "{ a: '{' }\r", | | 192 | + "{ a: \x1B[32m'{'\x1B[39m }", | | 193 | + ], | | 194 | + }, { | | 195 | + input: "{'{':0}", | | 196 | + noPreview: "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }", | | 197 | + preview: [ | | 198 | + "{'{':0}", | | 199 | + "\x1B[90m{ '{': 0 }\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r", | | 200 | + "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }", | | 201 | + ], | | 202 | + }, { | | 203 | + input: '{[Symbol.for("{")]: 0 }', | | 204 | + noPreview: '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', | | 205 | + preview: [ | | 206 | + '{[Symbol.for("{")]: 0 }\r', | | 207 | + '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', | | 208 | + ], | | 209 | + }, { | | 210 | + input: '{},{}', | | 211 | + noPreview: '{}', | | 212 | + preview: [ | | 213 | + '{},{}', | | 214 | + '\x1B[90m{}\x1B[39m\x1B[13G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', | | 215 | + '{}', | | 216 | + ], | | 217 | + }, { | | 218 | + input: '{} //', | | 219 | + noPreview: 'repl > ', | | 220 | + preview: [ | | 221 | + '{} //\r', | | 222 | + ], | | 223 | + }, { | | 224 | + input: '{} //;', | | 225 | + noPreview: 'repl > ', | | 226 | + preview: [ | | 227 | + '{} //;\r', | | 228 | + ], | | 229 | + }, { | | 230 | + input: '{throw 0}', | | 231 | + noPreview: 'Uncaught \x1B[33m0\x1B[39m', | | 232 | + preview: [ | | 233 | + '{throw 0}', | | 234 | + '\x1B[90m0\x1B[39m\x1B[17G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', | | 235 | + 'Uncaught \x1B[33m0\x1B[39m', | | 236 | + ], | 160 | 237 | }]; | 161 | 238 |
| 162 | 239 | const hasPreview = repl.terminal && | @@ -177,8 +254,13 @@ async function tests(options) { | 177 | 254 | assert.deepStrictEqual(lines, preview); | 178 | 255 | } else { | 179 | 256 | assert.ok(lines[0].includes(noPreview), lines.map(inspect)); | 180 | | - if (preview.length !== 1 || preview[0] !== `${input}\r`) | 181 | | - assert.strictEqual(lines.length, 2); | | 257 | + if (preview.length !== 1 || preview[0] !== `${input}\r`) { | | 258 | + if (preview[preview.length - 1].includes('Uncaught SyntaxError')) { | | 259 | + assert.strictEqual(lines.length, 5); | | 260 | + } else { | | 261 | + assert.strictEqual(lines.length, 2); | | 262 | + } | | 263 | + } | 182 | 264 | } | 183 | 265 | } | 184 | 266 | } | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-repl.js +13Lines changed: 13 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -328,6 +328,19 @@ const errorTests = [ | 328 | 328 | expect: '[Function (anonymous)]' | 329 | 329 | }, | 330 | 330 | // Multiline object | | 331 | + { | | 332 | + send: '{}),({}', | | 333 | + expect: '| ', | | 334 | + }, | | 335 | + { | | 336 | + send: '}', | | 337 | + expect: [ | | 338 | + '{}),({}', | | 339 | + kArrow, | | 340 | + '', | | 341 | + /^Uncaught SyntaxError: /, | | 342 | + ] | | 343 | + }, | 331 | 344 | { | 332 | 345 | send: '{ a: ', | 333 | 346 | expect: '| ' | |
|