@@ -55,6 +55,7 @@ import {
5555 hasProperty ,
5656 hasTrailingDirectorySeparator ,
5757 hostGetCanonicalFileName ,
58+ inferredTypesContainingFile ,
5859 isArray ,
5960 isDeclarationFileName ,
6061 isExternalModuleNameRelative ,
@@ -450,27 +451,19 @@ export function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffecti
450451 }
451452
452453 if ( currentDirectory !== undefined ) {
453- return getDefaultTypeRoots ( currentDirectory , host ) ;
454+ return getDefaultTypeRoots ( currentDirectory ) ;
454455 }
455456}
456457
457458/**
458459 * Returns the path to every node_modules/@types directory from some ancestor directory.
459460 * Returns undefined if there are none.
460461 */
461- function getDefaultTypeRoots ( currentDirectory : string , host : { directoryExists ?: ( directoryName : string ) => boolean } ) : string [ ] | undefined {
462- if ( ! host . directoryExists ) {
463- return [ combinePaths ( currentDirectory , nodeModulesAtTypes ) ] ;
464- // And if it doesn't exist, tough.
465- }
466-
462+ function getDefaultTypeRoots ( currentDirectory : string ) : string [ ] | undefined {
467463 let typeRoots : string [ ] | undefined ;
468464 forEachAncestorDirectory ( normalizePath ( currentDirectory ) , directory => {
469465 const atTypes = combinePaths ( directory , nodeModulesAtTypes ) ;
470- if ( host . directoryExists ! ( atTypes ) ) {
471- ( typeRoots || ( typeRoots = [ ] ) ) . push ( atTypes ) ;
472- }
473- return undefined ;
466+ ( typeRoots ??= [ ] ) . push ( atTypes ) ;
474467 } ) ;
475468 return typeRoots ;
476469}
@@ -627,10 +620,18 @@ export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string
627620 }
628621 return firstDefined ( typeRoots , typeRoot => {
629622 const candidate = combinePaths ( typeRoot , typeReferenceDirectiveName ) ;
630- const candidateDirectory = getDirectoryPath ( candidate ) ;
631- const directoryExists = directoryProbablyExists ( candidateDirectory , host ) ;
623+ const directoryExists = directoryProbablyExists ( typeRoot , host ) ;
632624 if ( ! directoryExists && traceEnabled ) {
633- trace ( host , Diagnostics . Directory_0_does_not_exist_skipping_all_lookups_in_it , candidateDirectory ) ;
625+ trace ( host , Diagnostics . Directory_0_does_not_exist_skipping_all_lookups_in_it , typeRoot ) ;
626+ }
627+ if ( options . typeRoots ) {
628+ // Custom typeRoots resolve as file or directory just like we do modules
629+ const resolvedFromFile = loadModuleFromFile ( Extensions . Declaration , candidate , ! directoryExists , moduleResolutionState ) ;
630+ if ( resolvedFromFile ) {
631+ const packageDirectory = parseNodeModuleFromPath ( resolvedFromFile . path ) ;
632+ const packageInfo = packageDirectory ? getPackageJsonInfo ( packageDirectory , /*onlyRecordFailures*/ false , moduleResolutionState ) : undefined ;
633+ return resolvedTypeScriptOnly ( withPackageId ( packageInfo , resolvedFromFile ) ) ;
634+ }
634635 }
635636 return resolvedTypeScriptOnly (
636637 loadNodeModuleFromDirectory ( Extensions . Declaration , candidate ,
@@ -646,20 +647,24 @@ export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string
646647
647648 function secondaryLookup ( ) : PathAndPackageId | undefined {
648649 const initialLocationForSecondaryLookup = containingFile && getDirectoryPath ( containingFile ) ;
649-
650650 if ( initialLocationForSecondaryLookup !== undefined ) {
651- // check secondary locations
652- if ( traceEnabled ) {
653- trace ( host , Diagnostics . Looking_up_in_node_modules_folder_initial_location_0 , initialLocationForSecondaryLookup ) ;
654- }
655651 let result : Resolved | undefined ;
656- if ( ! isExternalModuleNameRelative ( typeReferenceDirectiveName ) ) {
657- const searchResult = loadModuleFromNearestNodeModulesDirectory ( Extensions . Declaration , typeReferenceDirectiveName , initialLocationForSecondaryLookup , moduleResolutionState , /*cache*/ undefined , /*redirectedReference*/ undefined ) ;
658- result = searchResult && searchResult . value ;
652+ if ( ! options . typeRoots || ! endsWith ( containingFile ! , inferredTypesContainingFile ) ) {
653+ // check secondary locations
654+ if ( traceEnabled ) {
655+ trace ( host , Diagnostics . Looking_up_in_node_modules_folder_initial_location_0 , initialLocationForSecondaryLookup ) ;
656+ }
657+ if ( ! isExternalModuleNameRelative ( typeReferenceDirectiveName ) ) {
658+ const searchResult = loadModuleFromNearestNodeModulesDirectory ( Extensions . Declaration , typeReferenceDirectiveName , initialLocationForSecondaryLookup , moduleResolutionState , /*cache*/ undefined , /*redirectedReference*/ undefined ) ;
659+ result = searchResult && searchResult . value ;
660+ }
661+ else {
662+ const { path : candidate } = normalizePathForCJSResolution ( initialLocationForSecondaryLookup , typeReferenceDirectiveName ) ;
663+ result = nodeLoadModuleByRelativeName ( Extensions . Declaration , candidate , /*onlyRecordFailures*/ false , moduleResolutionState , /*considerPackageJson*/ true ) ;
664+ }
659665 }
660- else {
661- const { path : candidate } = normalizePathForCJSResolution ( initialLocationForSecondaryLookup , typeReferenceDirectiveName ) ;
662- result = nodeLoadModuleByRelativeName ( Extensions . Declaration , candidate , /*onlyRecordFailures*/ false , moduleResolutionState , /*considerPackageJson*/ true ) ;
666+ else if ( traceEnabled ) {
667+ trace ( host , Diagnostics . Resolving_type_reference_directive_for_program_that_specifies_custom_typeRoots_skipping_lookup_in_node_modules_folder ) ;
663668 }
664669 return resolvedTypeScriptOnly ( result ) ;
665670 }
@@ -1777,6 +1782,9 @@ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleNa
17771782 }
17781783 resolved = loadModuleFromNearestNodeModulesDirectory ( extensions , moduleName , containingDirectory , state , cache , redirectedReference ) ;
17791784 }
1785+ if ( extensions & Extensions . Declaration ) {
1786+ resolved ??= resolveFromTypeRoot ( moduleName , state ) ;
1787+ }
17801788 // For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.
17811789 return resolved && { value : resolved . value && { resolved : resolved . value , isExternalLibraryImport : true } } ;
17821790 }
@@ -3058,12 +3066,12 @@ export function classicNameResolver(moduleName: string, containingFile: string,
30583066 const searchName = normalizePath ( combinePaths ( directory , moduleName ) ) ;
30593067 return toSearchResult ( loadModuleFromFileNoPackageId ( extensions , searchName , /*onlyRecordFailures*/ false , state ) ) ;
30603068 } ) ;
3061- if ( resolved ) {
3062- return resolved ;
3063- }
3069+ if ( resolved ) return resolved ;
30643070 if ( extensions & ( Extensions . TypeScript | Extensions . Declaration ) ) {
30653071 // If we didn't find the file normally, look it up in @types.
3066- return loadModuleFromNearestNodeModulesDirectoryTypesScope ( moduleName , containingDirectory , state ) ;
3072+ let resolved = loadModuleFromNearestNodeModulesDirectoryTypesScope ( moduleName , containingDirectory , state ) ;
3073+ if ( extensions & Extensions . Declaration ) resolved ??= resolveFromTypeRoot ( moduleName , state ) ;
3074+ return resolved ;
30673075 }
30683076 }
30693077 else {
@@ -3073,6 +3081,25 @@ export function classicNameResolver(moduleName: string, containingFile: string,
30733081 }
30743082}
30753083
3084+ function resolveFromTypeRoot ( moduleName : string , state : ModuleResolutionState ) {
3085+ if ( ! state . compilerOptions . typeRoots ) return ;
3086+ for ( const typeRoot of state . compilerOptions . typeRoots ) {
3087+ const candidate = combinePaths ( typeRoot , moduleName ) ;
3088+ const directoryExists = directoryProbablyExists ( typeRoot , state . host ) ;
3089+ if ( ! directoryExists && state . traceEnabled ) {
3090+ trace ( state . host , Diagnostics . Directory_0_does_not_exist_skipping_all_lookups_in_it , typeRoot ) ;
3091+ }
3092+ const resolvedFromFile = loadModuleFromFile ( Extensions . Declaration , candidate , ! directoryExists , state ) ;
3093+ if ( resolvedFromFile ) {
3094+ const packageDirectory = parseNodeModuleFromPath ( resolvedFromFile . path ) ;
3095+ const packageInfo = packageDirectory ? getPackageJsonInfo ( packageDirectory , /*onlyRecordFailures*/ false , state ) : undefined ;
3096+ return toSearchResult ( withPackageId ( packageInfo , resolvedFromFile ) ) ;
3097+ }
3098+ const resolved = loadNodeModuleFromDirectory ( Extensions . Declaration , candidate , ! directoryExists , state ) ;
3099+ if ( resolved ) return toSearchResult ( resolved ) ;
3100+ }
3101+ }
3102+
30763103// Program errors validate that `noEmit` or `emitDeclarationOnly` is also set,
30773104// so this function doesn't check them to avoid propagating errors.
30783105/** @internal */
0 commit comments