@@ -70,6 +70,7 @@ interface Array<T> {}`
70
70
path : string ;
71
71
content ?: string ;
72
72
fileSize ?: number ;
73
+ symLink ?: string ;
73
74
}
74
75
75
76
interface FSEntry {
@@ -86,6 +87,10 @@ interface Array<T> {}`
86
87
entries : FSEntry [ ] ;
87
88
}
88
89
90
+ interface SymLink extends FSEntry {
91
+ symLink : string ;
92
+ }
93
+
89
94
function isFolder ( s : FSEntry ) : s is Folder {
90
95
return s && isArray ( ( < Folder > s ) . entries ) ;
91
96
}
@@ -94,6 +99,10 @@ interface Array<T> {}`
94
99
return s && isString ( ( < File > s ) . content ) ;
95
100
}
96
101
102
+ function isSymLink ( s : FSEntry ) : s is SymLink {
103
+ return s && isString ( ( < SymLink > s ) . symLink ) ;
104
+ }
105
+
97
106
function invokeWatcherCallbacks < T > ( callbacks : T [ ] , invokeCallback : ( cb : T ) => void ) : void {
98
107
if ( callbacks ) {
99
108
// The array copy is made to ensure that even if one of the callback removes the callbacks,
@@ -316,9 +325,12 @@ interface Array<T> {}`
316
325
}
317
326
}
318
327
else {
319
- // TODO: Changing from file => folder
328
+ // TODO: Changing from file => folder/Symlink
320
329
}
321
330
}
331
+ else if ( isSymLink ( currentEntry ) ) {
332
+ // TODO: update symlinks
333
+ }
322
334
else {
323
335
// Folder
324
336
if ( isString ( fileOrDirectory . content ) ) {
@@ -339,7 +351,7 @@ interface Array<T> {}`
339
351
// If this entry is not from the new file or folder
340
352
if ( ! mapNewLeaves . get ( path ) ) {
341
353
// Leaf entries that arent in new list => remove these
342
- if ( isFile ( fileOrDirectory ) || isFolder ( fileOrDirectory ) && fileOrDirectory . entries . length === 0 ) {
354
+ if ( isFile ( fileOrDirectory ) || isSymLink ( fileOrDirectory ) || isFolder ( fileOrDirectory ) && fileOrDirectory . entries . length === 0 ) {
343
355
this . removeFileOrFolder ( fileOrDirectory , folder => ! mapNewLeaves . get ( folder . path ) ) ;
344
356
}
345
357
}
@@ -387,6 +399,12 @@ interface Array<T> {}`
387
399
const baseFolder = this . ensureFolder ( getDirectoryPath ( file . fullPath ) ) ;
388
400
this . addFileOrFolderInFolder ( baseFolder , file , ignoreWatchInvokedWithTriggerAsFileCreate ) ;
389
401
}
402
+ else if ( isString ( fileOrDirectory . symLink ) ) {
403
+ const symLink = this . toSymLink ( fileOrDirectory ) ;
404
+ Debug . assert ( ! this . fs . get ( symLink . path ) ) ;
405
+ const baseFolder = this . ensureFolder ( getDirectoryPath ( symLink . fullPath ) ) ;
406
+ this . addFileOrFolderInFolder ( baseFolder , symLink , ignoreWatchInvokedWithTriggerAsFileCreate ) ;
407
+ }
390
408
else {
391
409
const fullPath = getNormalizedAbsolutePath ( fileOrDirectory . path , this . currentDirectory ) ;
392
410
this . ensureFolder ( fullPath ) ;
@@ -414,20 +432,20 @@ interface Array<T> {}`
414
432
return folder ;
415
433
}
416
434
417
- private addFileOrFolderInFolder ( folder : Folder , fileOrDirectory : File | Folder , ignoreWatch ?: boolean ) {
435
+ private addFileOrFolderInFolder ( folder : Folder , fileOrDirectory : File | Folder | SymLink , ignoreWatch ?: boolean ) {
418
436
folder . entries . push ( fileOrDirectory ) ;
419
437
this . fs . set ( fileOrDirectory . path , fileOrDirectory ) ;
420
438
421
439
if ( ignoreWatch ) {
422
440
return ;
423
441
}
424
- if ( isFile ( fileOrDirectory ) ) {
442
+ if ( isFile ( fileOrDirectory ) || isSymLink ( fileOrDirectory ) ) {
425
443
this . invokeFileWatcher ( fileOrDirectory . fullPath , FileWatcherEventKind . Created ) ;
426
444
}
427
445
this . invokeDirectoryWatcher ( folder . fullPath , fileOrDirectory . fullPath ) ;
428
446
}
429
447
430
- private removeFileOrFolder ( fileOrDirectory : File | Folder , isRemovableLeafFolder : ( folder : Folder ) => boolean , isRenaming ?: boolean ) {
448
+ private removeFileOrFolder ( fileOrDirectory : File | Folder | SymLink , isRemovableLeafFolder : ( folder : Folder ) => boolean , isRenaming ?: boolean ) {
431
449
const basePath = getDirectoryPath ( fileOrDirectory . path ) ;
432
450
const baseFolder = this . fs . get ( basePath ) as Folder ;
433
451
if ( basePath !== fileOrDirectory . path ) {
@@ -436,7 +454,7 @@ interface Array<T> {}`
436
454
}
437
455
this . fs . delete ( fileOrDirectory . path ) ;
438
456
439
- if ( isFile ( fileOrDirectory ) ) {
457
+ if ( isFile ( fileOrDirectory ) || isSymLink ( fileOrDirectory ) ) {
440
458
this . invokeFileWatcher ( fileOrDirectory . fullPath , FileWatcherEventKind . Deleted ) ;
441
459
}
442
460
else {
@@ -503,6 +521,15 @@ interface Array<T> {}`
503
521
} ;
504
522
}
505
523
524
+ private toSymLink ( fileOrDirectory : FileOrFolder ) : SymLink {
525
+ const fullPath = getNormalizedAbsolutePath ( fileOrDirectory . path , this . currentDirectory ) ;
526
+ return {
527
+ path : this . toPath ( fullPath ) ,
528
+ fullPath,
529
+ symLink : getNormalizedAbsolutePath ( fileOrDirectory . symLink , getDirectoryPath ( fullPath ) )
530
+ } ;
531
+ }
532
+
506
533
private toFolder ( path : string ) : Folder {
507
534
const fullPath = getNormalizedAbsolutePath ( path , this . currentDirectory ) ;
508
535
return {
@@ -512,14 +539,52 @@ interface Array<T> {}`
512
539
} ;
513
540
}
514
541
542
+ private getRealFsEntry < T extends FSEntry > ( isFsEntry : ( fsEntry : FSEntry ) => fsEntry is T , path : Path , fsEntry = this . fs . get ( path ) ) : T | undefined {
543
+ if ( isFsEntry ( fsEntry ) ) {
544
+ return fsEntry ;
545
+ }
546
+
547
+ if ( isSymLink ( fsEntry ) ) {
548
+ return this . getRealFsEntry ( isFsEntry , this . toPath ( fsEntry . symLink ) ) ;
549
+ }
550
+
551
+ if ( fsEntry ) {
552
+ // This fs entry is something else
553
+ return undefined ;
554
+ }
555
+
556
+ const realpath = this . realpath ( path ) ;
557
+ if ( path !== realpath ) {
558
+ return this . getRealFsEntry ( isFsEntry , realpath as Path ) ;
559
+ }
560
+
561
+ return undefined ;
562
+ }
563
+
564
+ private isFile ( fsEntry : FSEntry ) {
565
+ return ! ! this . getRealFile ( fsEntry . path , fsEntry ) ;
566
+ }
567
+
568
+ private getRealFile ( path : Path , fsEntry ?: FSEntry ) : File | undefined {
569
+ return this . getRealFsEntry ( isFile , path , fsEntry ) ;
570
+ }
571
+
572
+ private isFolder ( fsEntry : FSEntry ) {
573
+ return ! ! this . getRealFolder ( fsEntry . path , fsEntry ) ;
574
+ }
575
+
576
+ private getRealFolder ( path : Path , fsEntry = this . fs . get ( path ) ) : Folder | undefined {
577
+ return this . getRealFsEntry ( isFolder , path , fsEntry ) ;
578
+ }
579
+
515
580
fileExists ( s : string ) {
516
581
const path = this . toFullPath ( s ) ;
517
- return isFile ( this . fs . get ( path ) ) ;
582
+ return ! ! this . getRealFile ( path ) ;
518
583
}
519
584
520
- readFile ( s : string ) {
521
- const fsEntry = this . fs . get ( this . toFullPath ( s ) ) ;
522
- return isFile ( fsEntry ) ? fsEntry . content : undefined ;
585
+ readFile ( s : string ) : string {
586
+ const fsEntry = this . getRealFile ( this . toFullPath ( s ) ) ;
587
+ return fsEntry ? fsEntry . content : undefined ;
523
588
}
524
589
525
590
getFileSize ( s : string ) {
@@ -533,14 +598,14 @@ interface Array<T> {}`
533
598
534
599
directoryExists ( s : string ) {
535
600
const path = this . toFullPath ( s ) ;
536
- return isFolder ( this . fs . get ( path ) ) ;
601
+ return ! ! this . getRealFolder ( path ) ;
537
602
}
538
603
539
- getDirectories ( s : string ) {
604
+ getDirectories ( s : string ) : string [ ] {
540
605
const path = this . toFullPath ( s ) ;
541
- const folder = this . fs . get ( path ) ;
542
- if ( isFolder ( folder ) ) {
543
- return mapDefined ( folder . entries , entry => isFolder ( entry ) ? getBaseFileName ( entry . fullPath ) : undefined ) ;
606
+ const folder = this . getRealFolder ( path ) ;
607
+ if ( folder ) {
608
+ return mapDefined ( folder . entries , entry => this . isFolder ( entry ) ? getBaseFileName ( entry . fullPath ) : undefined ) ;
544
609
}
545
610
Debug . fail ( folder ? "getDirectories called on file" : "getDirectories called on missing folder" ) ;
546
611
return [ ] ;
@@ -550,13 +615,13 @@ interface Array<T> {}`
550
615
return ts . matchFiles ( path , extensions , exclude , include , this . useCaseSensitiveFileNames , this . getCurrentDirectory ( ) , depth , ( dir ) => {
551
616
const directories : string [ ] = [ ] ;
552
617
const files : string [ ] = [ ] ;
553
- const dirEntry = this . fs . get ( this . toPath ( dir ) ) ;
554
- if ( isFolder ( dirEntry ) ) {
555
- dirEntry . entries . forEach ( ( entry ) => {
556
- if ( isFolder ( entry ) ) {
618
+ const folder = this . getRealFolder ( this . toPath ( dir ) ) ;
619
+ if ( folder ) {
620
+ folder . entries . forEach ( ( entry ) => {
621
+ if ( this . isFolder ( entry ) ) {
557
622
directories . push ( getBaseFileName ( entry . fullPath ) ) ;
558
623
}
559
- else if ( isFile ( entry ) ) {
624
+ else if ( this . isFile ( entry ) ) {
560
625
files . push ( getBaseFileName ( entry . fullPath ) ) ;
561
626
}
562
627
else {
@@ -682,6 +747,23 @@ interface Array<T> {}`
682
747
clear ( this . output ) ;
683
748
}
684
749
750
+ realpath ( s : string ) : string {
751
+ const fullPath = this . toNormalizedAbsolutePath ( s ) ;
752
+ const path = this . toPath ( fullPath ) ;
753
+ if ( getDirectoryPath ( path ) === path ) {
754
+ // Root
755
+ return s ;
756
+ }
757
+ const dirFullPath = this . realpath ( getDirectoryPath ( fullPath ) ) ;
758
+ const realFullPath = combinePaths ( dirFullPath , getBaseFileName ( fullPath ) ) ;
759
+ const fsEntry = this . fs . get ( this . toPath ( realFullPath ) ) ;
760
+ if ( isSymLink ( fsEntry ) ) {
761
+ return this . realpath ( fsEntry . symLink ) ;
762
+ }
763
+
764
+ return realFullPath ;
765
+ }
766
+
685
767
readonly existMessage = "System Exit" ;
686
768
exitCode : number ;
687
769
readonly resolvePath = ( s : string ) => s ;
0 commit comments