From 53214f350d0d6e376b2ebfec2eaca73790359dc0 Mon Sep 17 00:00:00 2001 From: SudhansuBandha Date: Sun, 14 Jun 2026 14:57:44 +0530 Subject: [PATCH 1/4] repl: fix dot command handling in multiline mode Update conditional logic to correctly handle dot commands in multiline REPL input Fixes: https://github.com/nodejs/node/issues/63864 Refs: https://github.com/nodejs/node/pull/63889/ Signed-off-by: SudhansuBandha --- lib/repl.js | 16 ++++++-- ...t-repl-multiline-dot-commands-execution.js | 40 +++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 test/parallel/test-repl-multiline-dot-commands-execution.js diff --git a/lib/repl.js b/lib/repl.js index 17aab1c409beca..3836f8fa17f9b2 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -76,6 +76,7 @@ const { StringPrototypeCharAt, StringPrototypeEndsWith, StringPrototypeIncludes, + StringPrototypeIndexOf, StringPrototypeRepeat, StringPrototypeSlice, StringPrototypeStartsWith, @@ -807,10 +808,17 @@ class REPLServer extends Interface { // Check to see if a REPL keyword was used. If it returns true, // display next prompt and return. if (trimmedCmd) { - if (StringPrototypeCharAt(trimmedCmd, 0) === '.' && - StringPrototypeCharAt(trimmedCmd, 1) !== '.' && - NumberIsNaN(NumberParseFloat(trimmedCmd))) { - const matches = RegExpPrototypeExec(/^\.([^\s]+)\s*(.*)$/, trimmedCmd); + // If condition validates for dot commands at the beginning of the line, + // or dot commands after some whitespace. + const isDotCommandAtStart = StringPrototypeCharAt(trimmedCmd, 0) === '.' && + StringPrototypeCharAt(trimmedCmd, 1) !== '.'; + const dotIndex = StringPrototypeIndexOf(trimmedCmd, '.'); + const isDotCommandAfterWhitespace = dotIndex > 0 && + StringPrototypeCharAt(trimmedCmd, dotIndex + 1) !== '.'; + + if ((isDotCommandAtStart || isDotCommandAfterWhitespace) && + NumberIsNaN(NumberParseFloat(trimmedCmd))) { + const matches = RegExpPrototypeExec(/(?:^|\s)\.([^\s]+)\s*(.*)$/, trimmedCmd); const keyword = matches?.[1]; const rest = matches?.[2]; if (FunctionPrototypeCall(_parseREPLKeyword, self, keyword, rest) === true) { diff --git a/test/parallel/test-repl-multiline-dot-commands-execution.js b/test/parallel/test-repl-multiline-dot-commands-execution.js new file mode 100644 index 00000000000000..b1baac3fc787c9 --- /dev/null +++ b/test/parallel/test-repl-multiline-dot-commands-execution.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + + +const dotCommandSyntaxError = + /Uncaught SyntaxError: Unexpected token '\.'/; + + +function runDotCommand(command, validate) { + const { replServer, output } = startNewREPLServer(); + + replServer.on('exit', common.mustCall()); + replServer.write('function a() {\n'); + replServer.write(`${command}\n`); + validate(replServer, output); + replServer.write('arr = [1,\n'); + replServer.write(`${command}\n`); + validate(replServer, output); + replServer.close(); +} + +runDotCommand('.break', common.mustCall((replServer, output) => { + replServer.write('1 + 1\n'); + assert.doesNotMatch(output.accumulator, dotCommandSyntaxError); + assert.match(output.accumulator, /2\n/); +}, 2)); + +runDotCommand('.clear', common.mustCall((replServer, output) => { + replServer.write('1 + 1\n'); + assert.doesNotMatch(output.accumulator, dotCommandSyntaxError); + assert.match(output.accumulator, /2\n/); +}, 2)); + +runDotCommand('.help', common.mustCall((replServer, output) => { + replServer.write('1 + 1\n'); + assert.doesNotMatch(output.accumulator, dotCommandSyntaxError); + assert.match(output.accumulator, /2\n/); +}, 2)); From b5cb07b1b80bf198abb23d2f0642fd43c76ab8fd Mon Sep 17 00:00:00 2001 From: SudhansuBandha Date: Mon, 15 Jun 2026 13:46:36 +0530 Subject: [PATCH 2/4] improved validation logic for dot operator commands --- lib/repl.js | 10 +++++----- .../test-repl-multiline-dot-commands-execution.js | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/repl.js b/lib/repl.js index 3836f8fa17f9b2..953732e57a8a0e 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -815,12 +815,12 @@ class REPLServer extends Interface { const dotIndex = StringPrototypeIndexOf(trimmedCmd, '.'); const isDotCommandAfterWhitespace = dotIndex > 0 && StringPrototypeCharAt(trimmedCmd, dotIndex + 1) !== '.'; - - if ((isDotCommandAtStart || isDotCommandAfterWhitespace) && + const matches = RegExpPrototypeExec(/(?:^|\s)\.([^\s]+)\s*(.*)$/, trimmedCmd); + const keyword = matches?.[1]; + const rest = matches?.[2]; + const isValidKeyword = keyword && ObjectKeys(self.commands).includes(keyword); + if ((isDotCommandAtStart || isDotCommandAfterWhitespace && isValidKeyword) && NumberIsNaN(NumberParseFloat(trimmedCmd))) { - const matches = RegExpPrototypeExec(/(?:^|\s)\.([^\s]+)\s*(.*)$/, trimmedCmd); - const keyword = matches?.[1]; - const rest = matches?.[2]; if (FunctionPrototypeCall(_parseREPLKeyword, self, keyword, rest) === true) { return; } diff --git a/test/parallel/test-repl-multiline-dot-commands-execution.js b/test/parallel/test-repl-multiline-dot-commands-execution.js index b1baac3fc787c9..7f0e6a4e33eb84 100644 --- a/test/parallel/test-repl-multiline-dot-commands-execution.js +++ b/test/parallel/test-repl-multiline-dot-commands-execution.js @@ -13,9 +13,11 @@ function runDotCommand(command, validate) { replServer.on('exit', common.mustCall()); replServer.write('function a() {\n'); + replServer.write('console.log("logging");\n'); replServer.write(`${command}\n`); validate(replServer, output); replServer.write('arr = [1,\n'); + replServer.write('console.log("logging");\n'); replServer.write(`${command}\n`); validate(replServer, output); replServer.close(); From 70f0280daf265211450a813e3af272fa89954b5a Mon Sep 17 00:00:00 2001 From: SudhansuBandha Date: Mon, 15 Jun 2026 17:19:21 +0530 Subject: [PATCH 3/4] test: rerun CI - 1 From 13c45b3107a1cb75a4e4cc60f288f22cf1403514 Mon Sep 17 00:00:00 2001 From: SudhansuBandha Date: Mon, 22 Jun 2026 17:01:03 +0530 Subject: [PATCH 4/4] updated logic for dot command evaluation --- lib/repl.js | 25 ++++++++----------- ...t-repl-multiline-dot-commands-execution.js | 9 +++++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/repl.js b/lib/repl.js index 953732e57a8a0e..524307accad327 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -76,9 +76,9 @@ const { StringPrototypeCharAt, StringPrototypeEndsWith, StringPrototypeIncludes, - StringPrototypeIndexOf, StringPrototypeRepeat, StringPrototypeSlice, + StringPrototypeSplit, StringPrototypeStartsWith, StringPrototypeTrim, Symbol, @@ -803,24 +803,19 @@ class REPLServer extends Interface { } // Check REPL keywords and empty lines against a trimmed line input. - const trimmedCmd = StringPrototypeTrim(cmd); + let trimmedCmd = StringPrototypeTrim(cmd); // Check to see if a REPL keyword was used. If it returns true, // display next prompt and return. if (trimmedCmd) { - // If condition validates for dot commands at the beginning of the line, - // or dot commands after some whitespace. - const isDotCommandAtStart = StringPrototypeCharAt(trimmedCmd, 0) === '.' && - StringPrototypeCharAt(trimmedCmd, 1) !== '.'; - const dotIndex = StringPrototypeIndexOf(trimmedCmd, '.'); - const isDotCommandAfterWhitespace = dotIndex > 0 && - StringPrototypeCharAt(trimmedCmd, dotIndex + 1) !== '.'; - const matches = RegExpPrototypeExec(/(?:^|\s)\.([^\s]+)\s*(.*)$/, trimmedCmd); - const keyword = matches?.[1]; - const rest = matches?.[2]; - const isValidKeyword = keyword && ObjectKeys(self.commands).includes(keyword); - if ((isDotCommandAtStart || isDotCommandAfterWhitespace && isValidKeyword) && - NumberIsNaN(NumberParseFloat(trimmedCmd))) { + const splitString = StringPrototypeSplit(trimmedCmd, '\r'); + trimmedCmd = splitString[splitString.length - 1]; + if (StringPrototypeCharAt(trimmedCmd, 0) === '.' && + StringPrototypeCharAt(trimmedCmd, 1) !== '.' && + NumberIsNaN(NumberParseFloat(trimmedCmd))) { + const matches = RegExpPrototypeExec(/^\.([^\s]+)\s*(.*)$/, trimmedCmd); + const keyword = matches?.[1]; + const rest = matches?.[2]; if (FunctionPrototypeCall(_parseREPLKeyword, self, keyword, rest) === true) { return; } diff --git a/test/parallel/test-repl-multiline-dot-commands-execution.js b/test/parallel/test-repl-multiline-dot-commands-execution.js index 7f0e6a4e33eb84..c333707c7405ad 100644 --- a/test/parallel/test-repl-multiline-dot-commands-execution.js +++ b/test/parallel/test-repl-multiline-dot-commands-execution.js @@ -12,13 +12,18 @@ function runDotCommand(command, validate) { const { replServer, output } = startNewREPLServer(); replServer.on('exit', common.mustCall()); + replServer.write(`${command}\n`); + replServer.write('let testObj = {\n'); + replServer.write(`${command}:"dummy-value"\n`); + replServer.write(`}`); replServer.write('function a() {\n'); replServer.write('console.log("logging");\n'); + replServer.write(`let value = testObj ${command} \n`); + replServer.write('console.log(value) \n'); replServer.write(`${command}\n`); validate(replServer, output); replServer.write('arr = [1,\n'); - replServer.write('console.log("logging");\n'); - replServer.write(`${command}\n`); + replServer.write('consdole.log("logging");\n'); validate(replServer, output); replServer.close(); }