-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: Implement password prompt as a standalone prompt (#1353)
Co-authored-by: Matteo Sacchetto <56300116+matteosacchetto@users.noreply.github.com>
- Loading branch information
1 parent
7f69477
commit 11bbb4b
Showing
4 changed files
with
92 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,73 @@ | ||
import type { Prompt } from '@inquirer/type'; | ||
import input from '@inquirer/input'; | ||
import { | ||
createPrompt, | ||
useState, | ||
useKeypress, | ||
usePrefix, | ||
isEnterKey, | ||
type PromptConfig, | ||
} from '@inquirer/core'; | ||
import chalk from 'chalk'; | ||
import ansiEscapes from 'ansi-escapes'; | ||
|
||
type InputConfig = Parameters<typeof input>[0]; | ||
type PasswordConfig = Omit<InputConfig, 'transformer' | 'default'> & { | ||
type PasswordConfig = PromptConfig<{ | ||
mask?: boolean | string; | ||
}; | ||
validate?: (value: string) => boolean | string | Promise<string | boolean>; | ||
}>; | ||
|
||
const password: Prompt<string, PasswordConfig> = (config, context) => { | ||
if ('transformer' in config) { | ||
throw new Error( | ||
'Inquirer password prompt do not support custom transformer function. Use the input prompt instead.', | ||
); | ||
export default createPrompt<string, PasswordConfig>((config, done) => { | ||
const { validate = () => true } = config; | ||
const [status, setStatus] = useState<string>('pending'); | ||
const [errorMsg, setError] = useState<string | undefined>(undefined); | ||
const [value, setValue] = useState<string>(''); | ||
|
||
const isLoading = status === 'loading'; | ||
const prefix = usePrefix(isLoading); | ||
|
||
useKeypress(async (key, rl) => { | ||
// Ignore keypress while our prompt is doing other processing. | ||
if (status !== 'pending') { | ||
return; | ||
} | ||
|
||
if (isEnterKey(key)) { | ||
const answer = value; | ||
setStatus('loading'); | ||
const isValid = await validate(answer); | ||
if (isValid === true) { | ||
setValue(answer); | ||
setStatus('done'); | ||
done(answer); | ||
} else { | ||
// Reset the readline line value to the previous value. On line event, the value | ||
// get cleared, forcing the user to re-enter the value instead of fixing it. | ||
rl.write(value); | ||
setError(isValid || 'You must provide a valid value'); | ||
setStatus('pending'); | ||
} | ||
} else { | ||
setValue(rl.line); | ||
setError(undefined); | ||
} | ||
}); | ||
|
||
const message = chalk.bold(config.message); | ||
let formattedValue = ''; | ||
|
||
if (config.mask) { | ||
const maskChar = typeof config.mask === 'string' ? config.mask : '*'; | ||
formattedValue = maskChar.repeat(value.length); | ||
} else if (status !== 'done') { | ||
formattedValue = `${chalk.dim('[input is masked]')}${ansiEscapes.cursorHide}`; | ||
} | ||
|
||
if (status === 'done') { | ||
formattedValue = chalk.cyan(formattedValue); | ||
} | ||
|
||
let error = ''; | ||
if (errorMsg) { | ||
error = chalk.red(`> ${errorMsg}`); | ||
} | ||
|
||
return input( | ||
{ | ||
...config, // Make sure we do not display the default password | ||
default: undefined, | ||
transformer(str: string, { isFinal }: { isFinal: boolean }) { | ||
if (config.mask) { | ||
const maskChar = typeof config.mask === 'string' ? config.mask : '*'; | ||
return maskChar.repeat(str.length); | ||
} | ||
|
||
if (!isFinal) { | ||
return `${chalk.dim('[input is masked]')}${ansiEscapes.cursorHide}`; | ||
} | ||
|
||
return ''; | ||
}, | ||
}, | ||
context, | ||
); | ||
}; | ||
|
||
export default password; | ||
return [`${prefix} ${message} ${formattedValue}`, error]; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters