@@ -15,6 +15,9 @@ const observe = require('inquirer/lib/utils/events');
1515import Base from 'inquirer/lib/prompts/base' ;
1616import { Question , Transformer } from 'inquirer'
1717import Paginator from 'inquirer/lib/utils/paginator' ;
18+ import { Node } from './types' ;
19+ import { isSubPath } from './utils' ;
20+ import { getUpperDirNode } from './upperDir' ;
1821
1922type FileTreeSelectionPromptOptions < T = any > = Pick < Question < T > , 'type' | 'name' | 'message' | 'filter' | 'validate' | 'default' > & {
2023 transformer ?: Transformer < T >
@@ -47,6 +50,10 @@ type FileTreeSelectionPromptOptions<T = any> = Pick<Question<T>, 'type' | 'name'
4750 */
4851 hideRoot ?: boolean
4952 selectedList ?: string [ ]
53+ /**
54+ * show `..` in inside root dir, and you the user can press space on it to go upper directory. Default: false
55+ */
56+ enableGoUpperDirectory ?: boolean
5057}
5158
5259declare module 'inquirer' {
@@ -60,29 +67,36 @@ declare module 'inquirer' {
6067 * onlyShowDir: boolean (default: false)
6168 */
6269class FileTreeSelectionPrompt extends Base < FileTreeSelectionPromptOptions & { states : any } > {
63- fileTree : any
70+ rootNode : Node
6471 firstRender : boolean
6572 shownList : string [ ] | Record < string , any >
6673 selectedList : string [ ] | Record < string , any >
6774 paginator : Paginator
6875 done : ( ...args : any [ ] ) => void
69- active : Record < string , any >
76+ active : Node
77+
78+ get fileTree ( ) {
79+ if ( this . opt . hideRoot ) {
80+ return this . rootNode
81+ }
7082
83+ return {
84+ children : [ this . rootNode ]
85+ }
86+ }
7187
7288 constructor ( questions , rl , answers ) {
7389 super ( questions , rl , answers ) ;
7490
7591 const root = path . resolve ( process . cwd ( ) , this . opt . root || '.' ) ;
76- const rootNode = {
92+ const rootNode : Node = {
7793 path : root ,
7894 type : 'directory' ,
7995 name : '.(root directory)' ,
8096 _rootNode : true
8197 }
8298
83- this . fileTree = {
84- children : [ rootNode ]
85- } ;
99+ this . rootNode = rootNode
86100
87101 this . shownList = [ ]
88102
@@ -162,25 +176,21 @@ class FileTreeSelectionPrompt extends Base<FileTreeSelectionPromptOptions & {sta
162176
163177 cliCursor . hide ( ) ;
164178 if ( this . firstRender ) {
165- const rootNode = this . fileTree . children [ 0 ] ;
179+ const rootNode = this . rootNode ;
166180
167181 await this . prepareChildren ( rootNode ) ;
168182 rootNode . open = true ;
169- if ( this . opt . hideRoot ) {
170- this . fileTree . children = rootNode . children ;
171- this . active = this . active || this . fileTree . children [ 0 ] ;
172- } else {
173- this . active = this . active || rootNode . children [ 0 ] ;
174- }
175- this . render ( ) ;
183+ this . active = this . active || rootNode . children [ 0 ] ;
176184 this . prepareChildren ( this . active ) ;
185+ this . render ( )
177186 }
178187
179188 return this ;
180189 }
181190
182191 renderFileTree ( root = this . fileTree , indent = 2 ) {
183192 const children = root . children || [ ]
193+
184194 let output = ''
185195 const transformer = this . opt . transformer ;
186196 const isFinal = this . status === 'answered' ;
@@ -212,7 +222,10 @@ class FileTreeSelectionPrompt extends Base<FileTreeSelectionPromptOptions & {sta
212222 const safeIndent = ( indent - prefix . length + 2 ) > 0
213223 ? indent - prefix . length + 2
214224 : 0 ;
215- if ( transformer ) {
225+
226+ if ( itemPath . name == '..' ) {
227+ showValue = `${ ' ' . repeat ( safeIndent ) } ${ prefix } ..(Press \`Space\` to go parent directory)\n`
228+ } else if ( transformer ) {
216229 const transformedValue = transformer ( itemPath . path , this . answers , { isFinal } ) ;
217230 showValue = ' ' . repeat ( safeIndent ) + prefix + transformedValue + '\n' ;
218231 } else {
@@ -237,11 +250,11 @@ class FileTreeSelectionPrompt extends Base<FileTreeSelectionPromptOptions & {sta
237250 return output
238251 }
239252
240- async prepareChildren ( node ) {
253+ async prepareChildren ( node : Node ) {
241254 const parentPath = node . path ;
242255
243256 try {
244- if ( ! fs . lstatSync ( parentPath ) . isDirectory ( ) || node . children || node . open === true ) {
257+ if ( node . name == '..' || ! fs . lstatSync ( parentPath ) . isDirectory ( ) || node . children || node . open === true ) {
245258 return ;
246259 }
247260
@@ -303,37 +316,41 @@ class FileTreeSelectionPrompt extends Base<FileTreeSelectionPromptOptions & {sta
303316 await addValidity ( node ) ;
304317 }
305318
319+ if ( this . opt . enableGoUpperDirectory && node === this . rootNode ) {
320+ this . rootNode . children . unshift ( getUpperDirNode ( this . rootNode . path ) )
321+ }
322+
306323 // When it's single selection and has default value, we should expand to the default file.
307324 if ( this . firstRender && this . opt . default && ! this . opt . multiple ) {
308325 const defaultPath = this . opt . default ;
309- const exists = fs . existsSync ( defaultPath ) ;
326+ const founded = node . children . find ( item => {
327+ if ( item . name === '..' ) {
328+ return false
329+ }
310330
311- if ( exists ) {
312- const founded = node . children . find ( item => {
313- if ( item . path === defaultPath ) {
314- return true ;
315- }
331+ if ( item . path === defaultPath ) {
332+ return true ;
333+ }
316334
317- if ( defaultPath . includes ( `${ item . path } ${ path . sep } ` ) ) {
318- return true ;
319- }
320- } ) ;
335+ if ( defaultPath . includes ( `${ item . path } ${ path . sep } ` ) ) {
336+ return true ;
337+ }
338+ } ) ;
321339
322- if ( founded ) {
323- if ( founded . path === defaultPath ) {
324- this . active = founded ;
340+ if ( founded ) {
341+ if ( founded . path === defaultPath ) {
342+ this . active = founded ;
325343
326- let parent = founded . parent ;
344+ let parent = founded . parent ;
327345
328- while ( parent && ! parent . _rootNode ) {
329- parent . open = true ;
330- parent = parent . parent ;
331- }
332- }
333- else {
334- return await this . prepareChildren ( founded ) ;
346+ while ( parent && ! parent . _rootNode ) {
347+ parent . open = true ;
348+ parent = parent . parent ;
335349 }
336350 }
351+ else {
352+ return await this . prepareChildren ( founded ) ;
353+ }
337354 }
338355 }
339356
@@ -413,7 +430,11 @@ class FileTreeSelectionPrompt extends Base<FileTreeSelectionPromptOptions & {sta
413430 }
414431
415432 this . active = this . shownList [ index ]
416- this . prepareChildren ( this . active ) ;
433+
434+ if ( this . active . name !== '..' ) {
435+ this . prepareChildren ( this . active ) ;
436+ }
437+
417438 this . render ( )
418439 }
419440
@@ -441,8 +462,22 @@ class FileTreeSelectionPrompt extends Base<FileTreeSelectionPromptOptions & {sta
441462 this . render ( )
442463 }
443464
444- onSpaceKey ( tirggerByTab = false ) {
445- if ( ! tirggerByTab && this . opt . multiple ) {
465+ async onSpaceKey ( triggerByTab = false ) {
466+ if ( ! triggerByTab && this . active . name == '..' && isSubPath ( this . active . path , this . rootNode . path ) ) {
467+ this . rootNode = {
468+ ...this . active ,
469+ name : path . basename ( this . active . path ) ,
470+ }
471+ await this . prepareChildren ( this . rootNode ) ;
472+ this . active = this . rootNode . children ?. [ 0 ]
473+ this . firstRender = true
474+ this . rootNode . open = true
475+ this . render ( )
476+ this . firstRender = false
477+ return
478+ }
479+
480+ if ( ! triggerByTab && this . opt . multiple ) {
446481 if ( this . active . isValid === false ) {
447482 return
448483 }
0 commit comments