Skip to content

Commit f9b9953

Browse files
authored
feat: add withGuide support to password prompt (#451)
1 parent 76550d6 commit f9b9953

File tree

4 files changed

+122
-10
lines changed

4 files changed

+122
-10
lines changed

‎.changeset/rotten-jokes-read.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clack/prompts": patch
3+
---
4+
5+
Add `withGuide` support to password prompt.

‎packages/prompts/src/password.ts‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PasswordPrompt } from '@clack/core';
1+
import { PasswordPrompt, settings } from '@clack/core';
22
import color from 'picocolors';
33
import { type CommonOptions, S_BAR, S_BAR_END, S_PASSWORD_MASK, symbol } from './common.js';
44

@@ -16,7 +16,8 @@ export const password = (opts: PasswordOptions) => {
1616
input: opts.input,
1717
output: opts.output,
1818
render() {
19-
const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`;
19+
const hasGuide = (opts.withGuide ?? settings.withGuide) !== false;
20+
const title = `${hasGuide ? `${color.gray(S_BAR)}\n` : ''}${symbol(this.state)} ${opts.message}\n`;
2021
const userInput = this.userInputWithCursor;
2122
const masked = this.masked;
2223

@@ -26,22 +27,27 @@ export const password = (opts: PasswordOptions) => {
2627
if (opts.clearOnError) {
2728
this.clear();
2829
}
29-
return `${title.trim()}\n${color.yellow(S_BAR)}${maskedText}\n${color.yellow(
30-
S_BAR_END
31-
)} ${color.yellow(this.error)}\n`;
30+
const errorPrefix = hasGuide ? `${color.yellow(S_BAR)}` : '';
31+
const errorPrefixEnd = hasGuide ? color.yellow(S_BAR_END) : '';
32+
return `${title.trim()}\n${errorPrefix}${maskedText}\n${errorPrefixEnd} ${color.yellow(this.error)}\n`;
3233
}
3334
case 'submit': {
3435
const maskedText = masked ? ` ${color.dim(masked)}` : '';
35-
return `${title}${color.gray(S_BAR)}${maskedText}`;
36+
const submitPrefix = hasGuide ? color.gray(S_BAR) : '';
37+
return `${title}${submitPrefix}${maskedText}`;
3638
}
3739
case 'cancel': {
3840
const maskedText = masked ? ` ${color.strikethrough(color.dim(masked))}` : '';
39-
return `${title}${color.gray(S_BAR)}${maskedText}${
40-
masked ? `\n${color.gray(S_BAR)}` : ''
41+
const cancelPrefix = hasGuide ? color.gray(S_BAR) : '';
42+
return `${title}${cancelPrefix}${maskedText}${
43+
masked && hasGuide ? `\n${color.gray(S_BAR)}` : ''
4144
}`;
4245
}
43-
default:
44-
return `${title}${color.cyan(S_BAR)} ${userInput}\n${color.cyan(S_BAR_END)}\n`;
46+
default: {
47+
const defaultPrefix = hasGuide ? `${color.cyan(S_BAR)} ` : '';
48+
const defaultPrefixEnd = hasGuide ? color.cyan(S_BAR_END) : '';
49+
return `${title}${defaultPrefix}${userInput}\n${defaultPrefixEnd}\n`;
50+
}
4551
}
4652
},
4753
}).prompt() as Promise<string | symbol>;

‎packages/prompts/test/__snapshots__/password.test.ts.snap‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,23 @@ exports[`password (isCI = false) > clears input on error when clearOnError is tr
5757
]
5858
`;
5959
60+
exports[`password (isCI = false) > global withGuide: false removes guide 1`] = `
61+
[
62+
"<cursor.hide>",
63+
"◆ foo
64+
_
65+
66+
",
67+
"<cursor.backward count=999><cursor.up count=3>",
68+
"<erase.down>",
69+
"◇ foo
70+
",
71+
"
72+
",
73+
"<cursor.show>",
74+
]
75+
`;
76+
6077
exports[`password (isCI = false) > renders and clears validation errors 1`] = `
6178
[
6279
"<cursor.hide>",
@@ -197,6 +214,23 @@ exports[`password (isCI = false) > renders message 1`] = `
197214
]
198215
`;
199216
217+
exports[`password (isCI = false) > withGuide: false removes guide 1`] = `
218+
[
219+
"<cursor.hide>",
220+
"◆ foo
221+
_
222+
223+
",
224+
"<cursor.backward count=999><cursor.up count=3>",
225+
"<erase.down>",
226+
"◇ foo
227+
",
228+
"
229+
",
230+
"<cursor.show>",
231+
]
232+
`;
233+
200234
exports[`password (isCI = true) > can be aborted by a signal 1`] = `
201235
[
202236
"<cursor.hide>",
@@ -254,6 +288,23 @@ exports[`password (isCI = true) > clears input on error when clearOnError is tru
254288
]
255289
`;
256290
291+
exports[`password (isCI = true) > global withGuide: false removes guide 1`] = `
292+
[
293+
"<cursor.hide>",
294+
"◆ foo
295+
_
296+
297+
",
298+
"<cursor.backward count=999><cursor.up count=3>",
299+
"<erase.down>",
300+
"◇ foo
301+
",
302+
"
303+
",
304+
"<cursor.show>",
305+
]
306+
`;
307+
257308
exports[`password (isCI = true) > renders and clears validation errors 1`] = `
258309
[
259310
"<cursor.hide>",
@@ -393,3 +444,20 @@ exports[`password (isCI = true) > renders message 1`] = `
393444
"<cursor.show>",
394445
]
395446
`;
447+
448+
exports[`password (isCI = true) > withGuide: false removes guide 1`] = `
449+
[
450+
"<cursor.hide>",
451+
"◆ foo
452+
_
453+
454+
",
455+
"<cursor.backward count=999><cursor.up count=3>",
456+
"<erase.down>",
457+
"◇ foo
458+
",
459+
"
460+
",
461+
"<cursor.show>",
462+
]
463+
`;

‎packages/prompts/test/password.test.ts‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { updateSettings } from '@clack/core';
12
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
23
import * as prompts from '../src/index.js';
34
import { MockReadable, MockWritable } from './test-utils.js';
@@ -23,6 +24,7 @@ describe.each(['true', 'false'])('password (isCI = %s)', (isCI) => {
2324

2425
afterEach(() => {
2526
vi.restoreAllMocks();
27+
updateSettings({ withGuide: true });
2628
});
2729

2830
test('renders message', async () => {
@@ -149,4 +151,35 @@ describe.each(['true', 'false'])('password (isCI = %s)', (isCI) => {
149151
expect(value).toBe('yz');
150152
expect(output.buffer).toMatchSnapshot();
151153
});
154+
155+
test('withGuide: false removes guide', async () => {
156+
const result = prompts.password({
157+
message: 'foo',
158+
withGuide: false,
159+
input,
160+
output,
161+
});
162+
163+
input.emit('keypress', '', { name: 'return' });
164+
165+
await result;
166+
167+
expect(output.buffer).toMatchSnapshot();
168+
});
169+
170+
test('global withGuide: false removes guide', async () => {
171+
updateSettings({ withGuide: false });
172+
173+
const result = prompts.password({
174+
message: 'foo',
175+
input,
176+
output,
177+
});
178+
179+
input.emit('keypress', '', { name: 'return' });
180+
181+
await result;
182+
183+
expect(output.buffer).toMatchSnapshot();
184+
});
152185
});

0 commit comments

Comments
 (0)