@@ -806,7 +806,17 @@ namespace ts {
806
806
let projectReferenceRedirects : Map < ResolvedProjectReference | false > | undefined ;
807
807
let mapFromFileToProjectReferenceRedirects : Map < Path > | undefined ;
808
808
let mapFromToProjectReferenceRedirectSource : Map < SourceOfProjectReferenceRedirect > | undefined ;
809
- const useSourceOfProjectReferenceRedirect = ! ! host . useSourceOfProjectReferenceRedirect && host . useSourceOfProjectReferenceRedirect ( ) ;
809
+
810
+ const useSourceOfProjectReferenceRedirect = ! ! host . useSourceOfProjectReferenceRedirect ?.( ) &&
811
+ ! options . disableSourceOfProjectReferenceRedirect ;
812
+ const onProgramCreateComplete = updateHostForUseSourceOfProjectReferenceRedirect ( {
813
+ compilerHost : host ,
814
+ useSourceOfProjectReferenceRedirect,
815
+ toPath,
816
+ getResolvedProjectReferences,
817
+ getSourceOfProjectReferenceRedirect,
818
+ forEachResolvedProjectReference
819
+ } ) ;
810
820
811
821
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles ( oldProgram , options ) ;
812
822
// We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
@@ -821,12 +831,6 @@ namespace ts {
821
831
if ( ! resolvedProjectReferences ) {
822
832
resolvedProjectReferences = projectReferences . map ( parseProjectReferenceConfigFile ) ;
823
833
}
824
- if ( host . setResolvedProjectReferenceCallbacks ) {
825
- host . setResolvedProjectReferenceCallbacks ( {
826
- getSourceOfProjectReferenceRedirect,
827
- forEachResolvedProjectReference
828
- } ) ;
829
- }
830
834
if ( rootNames . length ) {
831
835
for ( const parsedRef of resolvedProjectReferences ) {
832
836
if ( ! parsedRef ) continue ;
@@ -970,6 +974,7 @@ namespace ts {
970
974
useCaseSensitiveFileNames : ( ) => host . useCaseSensitiveFileNames ( ) ,
971
975
} ;
972
976
977
+ onProgramCreateComplete ( ) ;
973
978
verifyCompilerOptions ( ) ;
974
979
performance . mark ( "afterProgram" ) ;
975
980
performance . measure ( "Program" , "beforeProgram" , "afterProgram" ) ;
@@ -1248,12 +1253,6 @@ namespace ts {
1248
1253
}
1249
1254
if ( projectReferences ) {
1250
1255
resolvedProjectReferences = projectReferences . map ( parseProjectReferenceConfigFile ) ;
1251
- if ( host . setResolvedProjectReferenceCallbacks ) {
1252
- host . setResolvedProjectReferenceCallbacks ( {
1253
- getSourceOfProjectReferenceRedirect,
1254
- forEachResolvedProjectReference
1255
- } ) ;
1256
- }
1257
1256
}
1258
1257
1259
1258
// check if program source files has changed in the way that can affect structure of the program
@@ -3460,6 +3459,183 @@ namespace ts {
3460
3459
}
3461
3460
}
3462
3461
3462
+ interface SymlinkedDirectory {
3463
+ real : string ;
3464
+ realPath : Path ;
3465
+ }
3466
+
3467
+ interface HostForUseSourceOfProjectReferenceRedirect {
3468
+ compilerHost : CompilerHost ;
3469
+ useSourceOfProjectReferenceRedirect : boolean ;
3470
+ toPath ( fileName : string ) : Path ;
3471
+ getResolvedProjectReferences ( ) : readonly ( ResolvedProjectReference | undefined ) [ ] | undefined ;
3472
+ getSourceOfProjectReferenceRedirect ( fileName : string ) : SourceOfProjectReferenceRedirect | undefined ;
3473
+ forEachResolvedProjectReference < T > ( cb : ( resolvedProjectReference : ResolvedProjectReference | undefined , resolvedProjectReferencePath : Path ) => T | undefined ) : T | undefined ;
3474
+ }
3475
+
3476
+ function updateHostForUseSourceOfProjectReferenceRedirect ( host : HostForUseSourceOfProjectReferenceRedirect ) {
3477
+ let mapOfDeclarationDirectories : Map < true > | undefined ;
3478
+ let symlinkedDirectories : Map < SymlinkedDirectory | false > | undefined ;
3479
+ let symlinkedFiles : Map < string > | undefined ;
3480
+
3481
+ const originalFileExists = host . compilerHost . fileExists ;
3482
+ const originalDirectoryExists = host . compilerHost . directoryExists ;
3483
+ const originalGetDirectories = host . compilerHost . getDirectories ;
3484
+ const originalRealpath = host . compilerHost . realpath ;
3485
+
3486
+
3487
+ if ( ! host . useSourceOfProjectReferenceRedirect ) return noop ;
3488
+
3489
+ // This implementation of fileExists checks if the file being requested is
3490
+ // .d.ts file for the referenced Project.
3491
+ // If it is it returns true irrespective of whether that file exists on host
3492
+ host . compilerHost . fileExists = ( file ) => {
3493
+ if ( originalFileExists . call ( host . compilerHost , file ) ) return true ;
3494
+ if ( ! host . getResolvedProjectReferences ( ) ) return false ;
3495
+ if ( ! isDeclarationFileName ( file ) ) return false ;
3496
+
3497
+ // Project references go to source file instead of .d.ts file
3498
+ return fileOrDirectoryExistsUsingSource ( file , /*isFile*/ true ) ;
3499
+ } ;
3500
+
3501
+ if ( originalDirectoryExists ) {
3502
+ // This implementation of directoryExists checks if the directory being requested is
3503
+ // directory of .d.ts file for the referenced Project.
3504
+ // If it is it returns true irrespective of whether that directory exists on host
3505
+ host . compilerHost . directoryExists = path => {
3506
+ if ( originalDirectoryExists . call ( host . compilerHost , path ) ) {
3507
+ handleDirectoryCouldBeSymlink ( path ) ;
3508
+ return true ;
3509
+ }
3510
+
3511
+ if ( ! host . getResolvedProjectReferences ( ) ) return false ;
3512
+
3513
+ if ( ! mapOfDeclarationDirectories ) {
3514
+ mapOfDeclarationDirectories = createMap ( ) ;
3515
+ host . forEachResolvedProjectReference ( ref => {
3516
+ if ( ! ref ) return ;
3517
+ const out = ref . commandLine . options . outFile || ref . commandLine . options . out ;
3518
+ if ( out ) {
3519
+ mapOfDeclarationDirectories ! . set ( getDirectoryPath ( host . toPath ( out ) ) , true ) ;
3520
+ }
3521
+ else {
3522
+ // Set declaration's in different locations only, if they are next to source the directory present doesnt change
3523
+ const declarationDir = ref . commandLine . options . declarationDir || ref . commandLine . options . outDir ;
3524
+ if ( declarationDir ) {
3525
+ mapOfDeclarationDirectories ! . set ( host . toPath ( declarationDir ) , true ) ;
3526
+ }
3527
+ }
3528
+ } ) ;
3529
+ }
3530
+
3531
+ return fileOrDirectoryExistsUsingSource ( path , /*isFile*/ false ) ;
3532
+ } ;
3533
+ }
3534
+
3535
+ if ( originalGetDirectories ) {
3536
+ // Call getDirectories only if directory actually present on the host
3537
+ // This is needed to ensure that we arent getting directories that we fake about presence for
3538
+ host . compilerHost . getDirectories = path =>
3539
+ ! host . getResolvedProjectReferences ( ) || ( originalDirectoryExists && originalDirectoryExists . call ( host . compilerHost , path ) ) ?
3540
+ originalGetDirectories . call ( host . compilerHost , path ) :
3541
+ [ ] ;
3542
+ }
3543
+
3544
+ // This is something we keep for life time of the host
3545
+ if ( originalRealpath ) {
3546
+ host . compilerHost . realpath = s =>
3547
+ symlinkedFiles ?. get ( host . toPath ( s ) ) ||
3548
+ originalRealpath . call ( host . compilerHost , s ) ;
3549
+ }
3550
+
3551
+ return onProgramCreateComplete ;
3552
+
3553
+
3554
+ function onProgramCreateComplete ( ) {
3555
+ host . compilerHost . fileExists = originalFileExists ;
3556
+ host . compilerHost . directoryExists = originalDirectoryExists ;
3557
+ host . compilerHost . getDirectories = originalGetDirectories ;
3558
+ // DO not revert realpath as it could be used later
3559
+ }
3560
+
3561
+ function fileExistsIfProjectReferenceDts ( file : string ) {
3562
+ const source = host . getSourceOfProjectReferenceRedirect ( file ) ;
3563
+ return source !== undefined ?
3564
+ isString ( source ) ? originalFileExists . call ( host . compilerHost , source ) : true :
3565
+ undefined ;
3566
+ }
3567
+
3568
+ function directoryExistsIfProjectReferenceDeclDir ( dir : string ) {
3569
+ const dirPath = host . toPath ( dir ) ;
3570
+ const dirPathWithTrailingDirectorySeparator = `${ dirPath } ${ directorySeparator } ` ;
3571
+ return forEachKey (
3572
+ mapOfDeclarationDirectories ! ,
3573
+ declDirPath => dirPath === declDirPath ||
3574
+ // Any parent directory of declaration dir
3575
+ startsWith ( declDirPath , dirPathWithTrailingDirectorySeparator ) ||
3576
+ // Any directory inside declaration dir
3577
+ startsWith ( dirPath , `${ declDirPath } /` )
3578
+ ) ;
3579
+ }
3580
+
3581
+ function handleDirectoryCouldBeSymlink ( directory : string ) {
3582
+ if ( ! host . getResolvedProjectReferences ( ) ) return ;
3583
+
3584
+ // Because we already watch node_modules, handle symlinks in there
3585
+ if ( ! originalRealpath || ! stringContains ( directory , nodeModulesPathPart ) ) return ;
3586
+ if ( ! symlinkedDirectories ) symlinkedDirectories = createMap ( ) ;
3587
+ const directoryPath = ensureTrailingDirectorySeparator ( host . toPath ( directory ) ) ;
3588
+ if ( symlinkedDirectories . has ( directoryPath ) ) return ;
3589
+
3590
+ const real = normalizePath ( originalRealpath . call ( host . compilerHost , directory ) ) ;
3591
+ let realPath : Path ;
3592
+ if ( real === directory ||
3593
+ ( realPath = ensureTrailingDirectorySeparator ( host . toPath ( real ) ) ) === directoryPath ) {
3594
+ // not symlinked
3595
+ symlinkedDirectories . set ( directoryPath , false ) ;
3596
+ return ;
3597
+ }
3598
+
3599
+ symlinkedDirectories . set ( directoryPath , {
3600
+ real : ensureTrailingDirectorySeparator ( real ) ,
3601
+ realPath
3602
+ } ) ;
3603
+ }
3604
+
3605
+ function fileOrDirectoryExistsUsingSource ( fileOrDirectory : string , isFile : boolean ) : boolean {
3606
+ const fileOrDirectoryExistsUsingSource = isFile ?
3607
+ ( file : string ) => fileExistsIfProjectReferenceDts ( file ) :
3608
+ ( dir : string ) => directoryExistsIfProjectReferenceDeclDir ( dir ) ;
3609
+ // Check current directory or file
3610
+ const result = fileOrDirectoryExistsUsingSource ( fileOrDirectory ) ;
3611
+ if ( result !== undefined ) return result ;
3612
+
3613
+ if ( ! symlinkedDirectories ) return false ;
3614
+ const fileOrDirectoryPath = host . toPath ( fileOrDirectory ) ;
3615
+ if ( ! stringContains ( fileOrDirectoryPath , nodeModulesPathPart ) ) return false ;
3616
+ if ( isFile && symlinkedFiles && symlinkedFiles . has ( fileOrDirectoryPath ) ) return true ;
3617
+
3618
+ // If it contains node_modules check if its one of the symlinked path we know of
3619
+ return firstDefinedIterator (
3620
+ symlinkedDirectories . entries ( ) ,
3621
+ ( [ directoryPath , symlinkedDirectory ] ) => {
3622
+ if ( ! symlinkedDirectory || ! startsWith ( fileOrDirectoryPath , directoryPath ) ) return undefined ;
3623
+ const result = fileOrDirectoryExistsUsingSource ( fileOrDirectoryPath . replace ( directoryPath , symlinkedDirectory . realPath ) ) ;
3624
+ if ( isFile && result ) {
3625
+ if ( ! symlinkedFiles ) symlinkedFiles = createMap ( ) ;
3626
+ // Store the real path for the file'
3627
+ const absolutePath = getNormalizedAbsolutePath ( fileOrDirectory , host . compilerHost . getCurrentDirectory ( ) ) ;
3628
+ symlinkedFiles . set (
3629
+ fileOrDirectoryPath ,
3630
+ `${ symlinkedDirectory . real } ${ absolutePath . replace ( new RegExp ( directoryPath , "i" ) , "" ) } `
3631
+ ) ;
3632
+ }
3633
+ return result ;
3634
+ }
3635
+ ) || false ;
3636
+ }
3637
+ }
3638
+
3463
3639
/*@internal */
3464
3640
export function handleNoEmitOptions ( program : ProgramToEmitFilesAndReportErrors , sourceFile : SourceFile | undefined , cancellationToken : CancellationToken | undefined ) : EmitResult | undefined {
3465
3641
const options = program . getCompilerOptions ( ) ;
0 commit comments