@@ -793,6 +793,92 @@ namespace ts {
793793 }
794794 } ) ;
795795
796+ it ( "can reuse module resolutions within project references" , ( ) => {
797+ const commonOptions = {
798+ composite : true ,
799+ declaration : true ,
800+ target : ScriptTarget . ES2015 ,
801+ traceResolution : true ,
802+ moduleResolution : ModuleResolutionKind . Classic ,
803+ } ;
804+ const tsconfigA = {
805+ compilerOptions : commonOptions ,
806+ include : [ "src" ]
807+ } ;
808+ const tsconfigB = {
809+ compilerOptions : {
810+ ...commonOptions ,
811+ paths : {
812+ a : [ "/a/src/index.ts" ]
813+ }
814+ } ,
815+ projectReferences : [
816+ { path : "/a" }
817+ ]
818+ } ;
819+
820+ const files = [
821+ { name : "/a/src/x.ts" , text : SourceText . New ( "" , "export const x = 1;" , "" ) } ,
822+ { name : "/a/src/index.ts" , text : SourceText . New ( "" , "export { x } from './x';" , "" ) } ,
823+ { name : "/a/tsconfig.json" , text : SourceText . New ( "" , "" , JSON . stringify ( tsconfigA ) ) } ,
824+ { name : "/b/src/b.ts" , text : SourceText . New ( "" , "import { x } from 'a';" , "" ) } ,
825+ ] ;
826+ const rootNamesA = [ "/a/src/index.ts" , "/a/src/x.ts" ] ;
827+ const rootNamesB = [ "/b/src/b.ts" ] ;
828+
829+ const host = createTestCompilerHost ( files , commonOptions . target ) ;
830+
831+ // Instead of hard-coding file system entries for this test, we could also write a function that more
832+ // generally transforms a list of files into a tree of FileSystemEntries.
833+ function getFileSystemEntries ( path : string ) {
834+ const mapPathToFileSystemEntries : { [ path : string ] : FileSystemEntries | undefined } = {
835+ "/a" : { files : [ ] , directories : [ "src" ] } ,
836+ "/a/src" : { files : [ "index.ts" , "x.ts" ] , directories : [ ] }
837+ } ;
838+
839+ const entries = mapPathToFileSystemEntries [ path ] ;
840+ if ( ! entries ) {
841+ throw new Error ( `Unexpected path "${ path } " requested from readDirectory. Test is broken.` ) ;
842+ }
843+ return entries ;
844+ }
845+
846+ host . readDirectory = ( rootDir , extensions , excludes , includes , depth ) =>
847+ matchFiles (
848+ rootDir ,
849+ extensions ,
850+ excludes ,
851+ includes ,
852+ /*useCaseSensitiveFileNames*/ true ,
853+ /*currentDirectory*/ "/" ,
854+ depth ,
855+ getFileSystemEntries ,
856+ /*realpath*/ path => path ) ;
857+
858+ createProgram ( rootNamesA , tsconfigA . compilerOptions , host ) ;
859+ createProgram ( {
860+ rootNames : rootNamesB ,
861+ options : tsconfigB . compilerOptions ,
862+ host,
863+ projectReferences : tsconfigB . projectReferences
864+ } ) ;
865+
866+ // Resolution should not be performed for "./x" from "/a/src/index.ts" multiple times.
867+ assert . deepEqual ( host . getTrace ( ) , [
868+ "======== Resolving module './x' from '/a/src/index.ts'. ========" ,
869+ "Explicitly specified module resolution kind: 'Classic'." ,
870+ "File '/a/src/x.ts' exist - use it as a name resolution result." ,
871+ "======== Module name './x' was successfully resolved to '/a/src/x.ts'. ========" ,
872+ "======== Resolving module 'a' from '/b/src/b.ts'. ========" ,
873+ "Explicitly specified module resolution kind: 'Classic'." ,
874+ "'paths' option is specified, looking for a pattern to match module name 'a'." ,
875+ "Module name 'a', matched pattern 'a'." ,
876+ "Trying substitution '/a/src/index.ts', candidate module location: '/a/src/index.ts'." ,
877+ "File '/a/src/index.ts' exist - use it as a name resolution result." ,
878+ "======== Module name 'a' was successfully resolved to '/a/src/index.ts'. ========" ,
879+ ] , "should reuse resolution to /a/src/x.ts" ) ;
880+ } ) ;
881+
796882 describe ( "redirects" , ( ) => {
797883 const axIndex = "/node_modules/a/node_modules/x/index.d.ts" ;
798884 const axPackage = "/node_modules/a/node_modules/x/package.json" ;
0 commit comments