-
Notifications
You must be signed in to change notification settings - Fork 3.5k
fix: the spinner #8322
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: latest
Are you sure you want to change the base?
fix: the spinner #8322
Changes from all commits
030d216
ef7f0ae
676a21a
8a44dfa
d311426
1110958
9d8c130
2cfa70c
702b932
6a78cfc
fd2ab0a
6514150
7b741fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -169,6 +169,7 @@ class Display { | |
|
||
// progress | ||
#progress | ||
#silentPrompt | ||
|
||
// options | ||
#command | ||
|
@@ -347,6 +348,11 @@ class Display { | |
|
||
case input.KEYS.end: | ||
log.resume() | ||
if (this.#silentPrompt) { | ||
// Add newline to preserve output | ||
output.standard('') | ||
this.#silentPrompt = false | ||
} | ||
output.flush() | ||
this.#progress.resume() | ||
break | ||
|
@@ -355,12 +361,29 @@ class Display { | |
// The convention when calling input.read is to pass in a single fn that returns | ||
// the promise to await. resolve and reject are provided by proc-log | ||
const [res, rej, p] = args | ||
return input.start(() => p() | ||
.then(res) | ||
.catch(rej) | ||
// Any call to procLog.input.read will render a prompt to the user, so we always | ||
// add a single newline of output to stdout to move the cursor to the next line | ||
.finally(() => output.standard(''))) | ||
|
||
// Silent inputs like password prompts require special handling | ||
// to preserve output when users hit enter (see input.KEYS.end). | ||
this.#silentPrompt = meta[META]?.silentPrompt || false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes me nervous. The display class is global, and state should be for the entire npm process, not for a single event. It will likely mean we need to pass this along as we are calling the input events. It seems here we have a more subtle issue in that TLDR: the real problem here is that we need to ensure that |
||
|
||
// Use sequential input management to avoid race condition which causes issues | ||
// with spinner and adding newlines | ||
process.emit('input', 'start') | ||
|
||
return p() | ||
.then((result) => { | ||
// User hits enter, process end event and return input | ||
process.emit('input', 'end') | ||
res(result) | ||
return result | ||
}) | ||
.catch((error) => { | ||
// User hits ctrl+c, add newline to preserve output | ||
output.standard('') | ||
this.#silentPrompt = false | ||
process.emit('input', 'end') | ||
rej(error) | ||
}) | ||
} | ||
} | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,3 +118,35 @@ t.test('email - invalid warns and retries', async (t) => { | |
t.equal(result, '[email protected]', 'received the email') | ||
t.equal(logMsg, 'invalid email') | ||
}) | ||
|
||
t.test('read-user-info integration works', async (t) => { | ||
t.teardown(() => { | ||
readResult = null | ||
readOpts = null | ||
}) | ||
|
||
readResult = 'regular-input' | ||
const username = await readUserInfo.username('Username: ') | ||
t.equal(username, 'regular-input', 'should return username from regular prompt') | ||
t.notOk(readOpts.silent, 'username prompt should not set silent') | ||
|
||
readResult = 'secret-password' | ||
const password = await readUserInfo.password('Password: ') | ||
t.equal(password, 'secret-password', 'should return password from silent prompt') | ||
t.match(readOpts, { silent: true }, 'password prompt should set silent: true') | ||
}) | ||
|
||
t.test('silent metadata is passed correctly by read-user-info', async (t) => { | ||
t.teardown(() => { | ||
readResult = null | ||
readOpts = null | ||
}) | ||
|
||
readResult = 'username' | ||
await readUserInfo.username('Username: ') | ||
t.notOk(readOpts?.silent, 'username prompt should not set silent') | ||
|
||
readResult = 'password' | ||
await readUserInfo.password('Password: ') | ||
t.equal(readOpts?.silent, true, 'password prompt should set silent: true') | ||
}) |
Uh oh!
There was an error while loading. Please reload this page.