Skip to content

Commit 134a1a1

Browse files
authored
fix(prompts): respect directory option in path prompt (#483)
1 parent 9786226 commit 134a1a1

4 files changed

Lines changed: 102 additions & 21 deletions

File tree

.changeset/big-pants-invite.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+
Fix the `path` prompt so `directory: true` correctly enforces directory-only selection while still allowing directory navigation, and add regression tests for both directory and default file selection behavior.

packages/prompts/src/path.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export const path = (opts: PathOptions) => {
6363
})
6464
.filter(
6565
({ path, isDirectory }) =>
66-
path.startsWith(userInput) && (opts.directory || !isDirectory)
66+
path.startsWith(userInput) && (isDirectory || !opts.directory)
6767
);
6868
return items.map((item) => ({
6969
value: item.path,

packages/prompts/test/__snapshots__/path.test.ts.snap

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ exports[`text (isCI = false) > can cancel 1`] = `
88
[36m│[39m
99
[36m│[39m [2mSearch:[22m /tmp/█
1010
[36m│[39m [32m●[39m /tmp/bar
11+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
12+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
1113
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
1214
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
1315
[36m└[39m",
14-
"<cursor.backward count=999><cursor.up count=7>",
16+
"<cursor.backward count=999><cursor.up count=9>",
1517
"<cursor.down count=1>",
1618
"<erase.down>",
1719
"[31m■[39m foo
@@ -30,10 +32,12 @@ exports[`text (isCI = false) > cannot submit unknown value 1`] = `
3032
[36m│[39m
3133
[36m│[39m [2mSearch:[22m /tmp/█
3234
[36m│[39m [32m●[39m /tmp/bar
35+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
36+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
3337
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
3438
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
3539
[36m└[39m",
36-
"<cursor.backward count=999><cursor.up count=7>",
40+
"<cursor.backward count=999><cursor.up count=9>",
3741
"<cursor.down count=3>",
3842
"<erase.down>",
3943
"[36m│[39m [2mSearch:[22m /tmp/_█
@@ -57,10 +61,12 @@ exports[`text (isCI = false) > cannot submit unknown value 1`] = `
5761
[36m│[39m
5862
[36m│[39m [2mSearch:[22m /tmp/█
5963
[36m│[39m [32m●[39m /tmp/bar
64+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
65+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
6066
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
6167
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
6268
[36m└[39m",
63-
"<cursor.backward count=999><cursor.up count=7>",
69+
"<cursor.backward count=999><cursor.up count=9>",
6470
"<cursor.down count=3>",
6571
"<erase.down>",
6672
"[36m│[39m [2mSearch:[22m /tmp/b█
@@ -107,10 +113,12 @@ exports[`text (isCI = false) > renders cancelled value if one set 1`] = `
107113
[36m│[39m
108114
[36m│[39m [2mSearch:[22m /tmp/█
109115
[36m│[39m [32m●[39m /tmp/bar
116+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
117+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
110118
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
111119
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
112120
[36m└[39m",
113-
"<cursor.backward count=999><cursor.up count=7>",
121+
"<cursor.backward count=999><cursor.up count=9>",
114122
"<cursor.down count=3>",
115123
"<erase.down>",
116124
"[36m│[39m [2mSearch:[22m /tmp/x█
@@ -141,10 +149,12 @@ exports[`text (isCI = false) > renders message 1`] = `
141149
[36m│[39m
142150
[36m│[39m [2mSearch:[22m /tmp/█
143151
[36m│[39m [32m●[39m /tmp/bar
152+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
153+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
144154
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
145155
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
146156
[36m└[39m",
147-
"<cursor.backward count=999><cursor.up count=7>",
157+
"<cursor.backward count=999><cursor.up count=9>",
148158
"<cursor.down count=1>",
149159
"<erase.down>",
150160
"[32m◇[39m foo
@@ -163,10 +173,12 @@ exports[`text (isCI = false) > renders submitted value 1`] = `
163173
[36m│[39m
164174
[36m│[39m [2mSearch:[22m /tmp/█
165175
[36m│[39m [32m●[39m /tmp/bar
176+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
177+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
166178
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
167179
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
168180
[36m└[39m",
169-
"<cursor.backward count=999><cursor.up count=7>",
181+
"<cursor.backward count=999><cursor.up count=9>",
170182
"<cursor.down count=3>",
171183
"<erase.down>",
172184
"[36m│[39m [2mSearch:[22m /tmp/b█
@@ -197,10 +209,12 @@ exports[`text (isCI = false) > validation errors render and clear (using Error)
197209
[36m│[39m
198210
[36m│[39m [2mSearch:[22m /tmp/█
199211
[36m│[39m [32m●[39m /tmp/bar
212+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
213+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
200214
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
201215
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
202216
[36m└[39m",
203-
"<cursor.backward count=999><cursor.up count=7>",
217+
"<cursor.backward count=999><cursor.up count=9>",
204218
"<cursor.down count=3>",
205219
"<erase.down>",
206220
"[36m│[39m [2mSearch:[22m /tmp/r█
@@ -224,10 +238,12 @@ exports[`text (isCI = false) > validation errors render and clear (using Error)
224238
[36m│[39m
225239
[36m│[39m [2mSearch:[22m /tmp/█
226240
[36m│[39m [2m○[22m [2m/tmp/bar[22m
241+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
242+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
227243
[36m│[39m [32m●[39m /tmp/root.zip
228244
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
229245
[36m└[39m",
230-
"<cursor.backward count=999><cursor.up count=7>",
246+
"<cursor.backward count=999><cursor.up count=9>",
231247
"<cursor.down count=3>",
232248
"<erase.down>",
233249
"[36m│[39m [2mSearch:[22m /tmp/b█
@@ -253,10 +269,12 @@ exports[`text (isCI = false) > validation errors render and clear 1`] = `
253269
[36m│[39m
254270
[36m│[39m [2mSearch:[22m /tmp/█
255271
[36m│[39m [32m●[39m /tmp/bar
272+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
273+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
256274
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
257275
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
258276
[36m└[39m",
259-
"<cursor.backward count=999><cursor.up count=7>",
277+
"<cursor.backward count=999><cursor.up count=9>",
260278
"<cursor.down count=3>",
261279
"<erase.down>",
262280
"[36m│[39m [2mSearch:[22m /tmp/r█
@@ -280,10 +298,12 @@ exports[`text (isCI = false) > validation errors render and clear 1`] = `
280298
[36m│[39m
281299
[36m│[39m [2mSearch:[22m /tmp/█
282300
[36m│[39m [2m○[22m [2m/tmp/bar[22m
301+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
302+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
283303
[36m│[39m [32m●[39m /tmp/root.zip
284304
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
285305
[36m└[39m",
286-
"<cursor.backward count=999><cursor.up count=7>",
306+
"<cursor.backward count=999><cursor.up count=9>",
287307
"<cursor.down count=3>",
288308
"<erase.down>",
289309
"[36m│[39m [2mSearch:[22m /tmp/b█
@@ -309,10 +329,12 @@ exports[`text (isCI = true) > can cancel 1`] = `
309329
[36m│[39m
310330
[36m│[39m [2mSearch:[22m /tmp/█
311331
[36m│[39m [32m●[39m /tmp/bar
332+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
333+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
312334
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
313335
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
314336
[36m└[39m",
315-
"<cursor.backward count=999><cursor.up count=7>",
337+
"<cursor.backward count=999><cursor.up count=9>",
316338
"<cursor.down count=1>",
317339
"<erase.down>",
318340
"[31m■[39m foo
@@ -331,10 +353,12 @@ exports[`text (isCI = true) > cannot submit unknown value 1`] = `
331353
[36m│[39m
332354
[36m│[39m [2mSearch:[22m /tmp/█
333355
[36m│[39m [32m●[39m /tmp/bar
356+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
357+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
334358
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
335359
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
336360
[36m└[39m",
337-
"<cursor.backward count=999><cursor.up count=7>",
361+
"<cursor.backward count=999><cursor.up count=9>",
338362
"<cursor.down count=3>",
339363
"<erase.down>",
340364
"[36m│[39m [2mSearch:[22m /tmp/_█
@@ -358,10 +382,12 @@ exports[`text (isCI = true) > cannot submit unknown value 1`] = `
358382
[36m│[39m
359383
[36m│[39m [2mSearch:[22m /tmp/█
360384
[36m│[39m [32m●[39m /tmp/bar
385+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
386+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
361387
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
362388
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
363389
[36m└[39m",
364-
"<cursor.backward count=999><cursor.up count=7>",
390+
"<cursor.backward count=999><cursor.up count=9>",
365391
"<cursor.down count=3>",
366392
"<erase.down>",
367393
"[36m│[39m [2mSearch:[22m /tmp/b█
@@ -408,10 +434,12 @@ exports[`text (isCI = true) > renders cancelled value if one set 1`] = `
408434
[36m│[39m
409435
[36m│[39m [2mSearch:[22m /tmp/█
410436
[36m│[39m [32m●[39m /tmp/bar
437+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
438+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
411439
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
412440
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
413441
[36m└[39m",
414-
"<cursor.backward count=999><cursor.up count=7>",
442+
"<cursor.backward count=999><cursor.up count=9>",
415443
"<cursor.down count=3>",
416444
"<erase.down>",
417445
"[36m│[39m [2mSearch:[22m /tmp/x█
@@ -442,10 +470,12 @@ exports[`text (isCI = true) > renders message 1`] = `
442470
[36m│[39m
443471
[36m│[39m [2mSearch:[22m /tmp/█
444472
[36m│[39m [32m●[39m /tmp/bar
473+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
474+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
445475
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
446476
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
447477
[36m└[39m",
448-
"<cursor.backward count=999><cursor.up count=7>",
478+
"<cursor.backward count=999><cursor.up count=9>",
449479
"<cursor.down count=1>",
450480
"<erase.down>",
451481
"[32m◇[39m foo
@@ -464,10 +494,12 @@ exports[`text (isCI = true) > renders submitted value 1`] = `
464494
[36m│[39m
465495
[36m│[39m [2mSearch:[22m /tmp/█
466496
[36m│[39m [32m●[39m /tmp/bar
497+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
498+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
467499
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
468500
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
469501
[36m└[39m",
470-
"<cursor.backward count=999><cursor.up count=7>",
502+
"<cursor.backward count=999><cursor.up count=9>",
471503
"<cursor.down count=3>",
472504
"<erase.down>",
473505
"[36m│[39m [2mSearch:[22m /tmp/b█
@@ -498,10 +530,12 @@ exports[`text (isCI = true) > validation errors render and clear (using Error) 1
498530
[36m│[39m
499531
[36m│[39m [2mSearch:[22m /tmp/█
500532
[36m│[39m [32m●[39m /tmp/bar
533+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
534+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
501535
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
502536
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
503537
[36m└[39m",
504-
"<cursor.backward count=999><cursor.up count=7>",
538+
"<cursor.backward count=999><cursor.up count=9>",
505539
"<cursor.down count=3>",
506540
"<erase.down>",
507541
"[36m│[39m [2mSearch:[22m /tmp/r█
@@ -525,10 +559,12 @@ exports[`text (isCI = true) > validation errors render and clear (using Error) 1
525559
[36m│[39m
526560
[36m│[39m [2mSearch:[22m /tmp/█
527561
[36m│[39m [2m○[22m [2m/tmp/bar[22m
562+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
563+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
528564
[36m│[39m [32m●[39m /tmp/root.zip
529565
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
530566
[36m└[39m",
531-
"<cursor.backward count=999><cursor.up count=7>",
567+
"<cursor.backward count=999><cursor.up count=9>",
532568
"<cursor.down count=3>",
533569
"<erase.down>",
534570
"[36m│[39m [2mSearch:[22m /tmp/b█
@@ -554,10 +590,12 @@ exports[`text (isCI = true) > validation errors render and clear 1`] = `
554590
[36m│[39m
555591
[36m│[39m [2mSearch:[22m /tmp/█
556592
[36m│[39m [32m●[39m /tmp/bar
593+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
594+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
557595
[36m│[39m [2m○[22m [2m/tmp/root.zip[22m
558596
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
559597
[36m└[39m",
560-
"<cursor.backward count=999><cursor.up count=7>",
598+
"<cursor.backward count=999><cursor.up count=9>",
561599
"<cursor.down count=3>",
562600
"<erase.down>",
563601
"[36m│[39m [2mSearch:[22m /tmp/r█
@@ -581,10 +619,12 @@ exports[`text (isCI = true) > validation errors render and clear 1`] = `
581619
[36m│[39m
582620
[36m│[39m [2mSearch:[22m /tmp/█
583621
[36m│[39m [2m○[22m [2m/tmp/bar[22m
622+
[36m│[39m [2m○[22m [2m/tmp/foo[22m
623+
[36m│[39m [2m○[22m [2m/tmp/hello[22m
584624
[36m│[39m [32m●[39m /tmp/root.zip
585625
[36m│[39m [2m↑/↓[22m to select • [2mEnter:[22m confirm • [2mType:[22m to search
586626
[36m└[39m",
587-
"<cursor.backward count=999><cursor.up count=7>",
627+
"<cursor.backward count=999><cursor.up count=9>",
588628
"<cursor.down count=3>",
589629
"<erase.down>",
590630
"[36m│[39m [2mSearch:[22m /tmp/b█

packages/prompts/test/path.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,42 @@ describe.each(['true', 'false'])('text (isCI = %s)', (isCI) => {
146146
expect(output.buffer).toMatchSnapshot();
147147
});
148148

149+
test('directory mode only allows selecting directories', async () => {
150+
const result = prompts.path({
151+
message: 'foo',
152+
root: '/tmp/',
153+
directory: true,
154+
input,
155+
output,
156+
});
157+
158+
input.emit('keypress', 'f', { name: 'f' });
159+
input.emit('keypress', '', { name: 'return' });
160+
161+
const value = await result;
162+
163+
expect(value).toBe('/tmp/foo');
164+
});
165+
166+
test('default mode allows selecting files', async () => {
167+
const result = prompts.path({
168+
message: 'foo',
169+
root: '/tmp/',
170+
input,
171+
output,
172+
});
173+
174+
input.emit('keypress', 'r', { name: 'r' });
175+
input.emit('keypress', 'o', { name: 'o' });
176+
input.emit('keypress', 'o', { name: 'o' });
177+
input.emit('keypress', 't', { name: 't' });
178+
input.emit('keypress', '', { name: 'return' });
179+
180+
const value = await result;
181+
182+
expect(value).toBe('/tmp/root.zip');
183+
});
184+
149185
test('validation errors render and clear', async () => {
150186
const result = prompts.path({
151187
message: 'foo',

0 commit comments

Comments
 (0)