-
-
Notifications
You must be signed in to change notification settings - Fork 34.7k
readline: initialize input before history manager #61538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
lib/internal/readline/interface.js
Outdated
| this[kSubstringSearch] = null; | ||
| this.output = output; | ||
| this.input = input; | ||
| this.setupHistoryManager(input); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this reodering only resolve this.input being undefine but still doesn't fix accidental REPL history init where readline.createInterface(input) is not supposed to touch REPL history but setupHistoryManager(input) ends up seeing a truthy input.onHistoryFileLoaded and calls ReplHistory.initialize() anyway
|
@thisalihassan Thanks for the feedback! I’ll address the scoping, test, and history initialization issues and update the PR shortly. |
736d855 to
e3a00ce
Compare
|
@thisalihassan Thanks — I’ve addressed the scoping, history initialization, and test issues you mentioned. |
e3a00ce to
9dc21a9
Compare
|
@thisalihassan I’ve cleaned up the diff, reverted the unnecessary scoping changes, fixed the history initialization logic, and updated the test to use real streams. |
thisalihassan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall changes lg but we can improve tests I believe. I will wait someone from the node team to request CI and review the PR
|
Hi Node.js maintainers |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #61538 +/- ##
==========================================
- Coverage 89.77% 89.43% -0.35%
==========================================
Files 673 673
Lines 203820 203815 -5
Branches 39175 38998 -177
==========================================
- Hits 182987 182275 -712
- Misses 13152 13837 +685
- Partials 7681 7703 +22
🚀 New features to boost your workflow:
|
…dling - Reorder initialization: assign this.input before setupHistoryManager() - Preserve original options object for proper history manager initialization - Fix REPL.setupHistory() callback invocation logic - Fix stream event handling in repl persistent history test Fixes: nodejs#61526
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Fixes a readline regression where history initialization could run before this.input is assigned (triggered by an onHistoryFileLoaded property on the input), and adds tests to prevent reintroducing the crash.
Changes:
- Move
setupHistoryManager()to run afterthis.inputis assigned and add guards to avoid initializing persistent history from the readline constructor path. - Add a regression test covering inputs with an
onHistoryFileLoadedproperty acrosscreateInterface()call patterns. - Adjust the REPL persistent history test to wait for the history fixture copy to complete before running the suite.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| lib/internal/readline/interface.js | Reorders history manager setup and changes initialization conditions for persistent history loading. |
| test/parallel/test-readline-history-init-order.js | Adds regression coverage for inputs that expose onHistoryFileLoaded. |
| test/parallel/test-repl-persistent-history.js | Updates fixture copy sequencing before REPL history tests run. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Only initialize REPL history when called from REPL.setupHistory(), | ||
| // not from the readline constructor. | ||
| // Constructor passes: stream OR { input: stream, output: stream, ... } | ||
| // setupHistory passes: { filePath: ..., size: ..., onHistoryFileLoaded: ... } | ||
| // Detect constructor calls by checking for stream.on() or options.input | ||
| if (options && typeof options === 'object') { | ||
| const isStream = typeof options.on === 'function'; | ||
| const hasInputProperty = options.input !== undefined; | ||
| const isFromConstructor = isStream || hasInputProperty; | ||
|
|
||
| if (!isFromConstructor && typeof options.onHistoryFileLoaded === 'function') { | ||
| this.historyManager.initialize(options.onHistoryFileLoaded); |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are trailing whitespace characters in this comment block/blank line (e.g. after constructor. and on the blank line before the if), which will typically fail the repo's JS linting (no-trailing-spaces). Please remove trailing spaces.
| @@ -0,0 +1,48 @@ | |||
| 'use strict'; | |||
|
|
|||
| const common = require('../common'); | |||
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
common is required but never used in this test file. This will fail linting with no-unused-vars in the test suite. Either remove the import or use common (e.g., common.mustNotCall, common.mustCall, or common.skip(...) if needed).
| const common = require('../common'); |
| this[kSubstringSearch] = null; | ||
| this.output = output; | ||
| this.input = input; | ||
| this.setupHistoryManager(inputOptions || input); |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inputOptions (the original options object) is passed into setupHistoryManager(), but ReplHistory only reads options.size/options.history (not historySize). In the options-object path you currently only copy historySize into input.size, so passing inputOptions here will ignore historySize and can break existing readline.createInterface({ historySize }) behavior/tests. Pass the actual input stream (with .size/.history applied), or also copy size/history/removeHistoryDuplicates onto inputOptions before calling setupHistoryManager().
| this.setupHistoryManager(inputOptions || input); | |
| const historyOptions = inputOptions || input; | |
| if (inputOptions !== undefined && input !== undefined) { | |
| if (input.size !== undefined && historyOptions.size === undefined) | |
| historyOptions.size = input.size; | |
| if (input.history !== undefined && historyOptions.history === undefined) | |
| historyOptions.history = input.history; | |
| if (input.removeHistoryDuplicates !== undefined && | |
| historyOptions.removeHistoryDuplicates === undefined) { | |
| historyOptions.removeHistoryDuplicates = input.removeHistoryDuplicates; | |
| } | |
| } | |
| this.setupHistoryManager(historyOptions); |
Fixes a regression where
setupHistoryManager()could run beforethis.inputwas assigned, causing a crash whenonHistoryFileLoadedis present (for example, Proxy or
jest.mock()inputs).This change ensures
this.inputis initialized before history setupand adds a regression test to cover the scenario.
Fixes: #61526
Testing: