@@ -55,6 +55,7 @@ import {
55
55
hasProperty ,
56
56
hasTrailingDirectorySeparator ,
57
57
hostGetCanonicalFileName ,
58
+ inferredTypesContainingFile ,
58
59
isArray ,
59
60
isDeclarationFileName ,
60
61
isExternalModuleNameRelative ,
@@ -450,27 +451,19 @@ export function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffecti
450
451
}
451
452
452
453
if ( currentDirectory !== undefined ) {
453
- return getDefaultTypeRoots ( currentDirectory , host ) ;
454
+ return getDefaultTypeRoots ( currentDirectory ) ;
454
455
}
455
456
}
456
457
457
458
/**
458
459
* Returns the path to every node_modules/@types directory from some ancestor directory.
459
460
* Returns undefined if there are none.
460
461
*/
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 {
467
463
let typeRoots : string [ ] | undefined ;
468
464
forEachAncestorDirectory ( normalizePath ( currentDirectory ) , directory => {
469
465
const atTypes = combinePaths ( directory , nodeModulesAtTypes ) ;
470
- if ( host . directoryExists ! ( atTypes ) ) {
471
- ( typeRoots || ( typeRoots = [ ] ) ) . push ( atTypes ) ;
472
- }
473
- return undefined ;
466
+ ( typeRoots ??= [ ] ) . push ( atTypes ) ;
474
467
} ) ;
475
468
return typeRoots ;
476
469
}
@@ -627,10 +620,18 @@ export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string
627
620
}
628
621
return firstDefined ( typeRoots , typeRoot => {
629
622
const candidate = combinePaths ( typeRoot , typeReferenceDirectiveName ) ;
630
- const candidateDirectory = getDirectoryPath ( candidate ) ;
631
- const directoryExists = directoryProbablyExists ( candidateDirectory , host ) ;
623
+ const directoryExists = directoryProbablyExists ( typeRoot , host ) ;
632
624
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
+ }
634
635
}
635
636
return resolvedTypeScriptOnly (
636
637
loadNodeModuleFromDirectory ( Extensions . Declaration , candidate ,
@@ -646,20 +647,24 @@ export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string
646
647
647
648
function secondaryLookup ( ) : PathAndPackageId | undefined {
648
649
const initialLocationForSecondaryLookup = containingFile && getDirectoryPath ( containingFile ) ;
649
-
650
650
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
- }
655
651
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
+ }
659
665
}
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 ) ;
663
668
}
664
669
return resolvedTypeScriptOnly ( result ) ;
665
670
}
@@ -1777,6 +1782,9 @@ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleNa
1777
1782
}
1778
1783
resolved = loadModuleFromNearestNodeModulesDirectory ( extensions , moduleName , containingDirectory , state , cache , redirectedReference ) ;
1779
1784
}
1785
+ if ( extensions & Extensions . Declaration ) {
1786
+ resolved ??= resolveFromTypeRoot ( moduleName , state ) ;
1787
+ }
1780
1788
// For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.
1781
1789
return resolved && { value : resolved . value && { resolved : resolved . value , isExternalLibraryImport : true } } ;
1782
1790
}
@@ -3058,12 +3066,12 @@ export function classicNameResolver(moduleName: string, containingFile: string,
3058
3066
const searchName = normalizePath ( combinePaths ( directory , moduleName ) ) ;
3059
3067
return toSearchResult ( loadModuleFromFileNoPackageId ( extensions , searchName , /*onlyRecordFailures*/ false , state ) ) ;
3060
3068
} ) ;
3061
- if ( resolved ) {
3062
- return resolved ;
3063
- }
3069
+ if ( resolved ) return resolved ;
3064
3070
if ( extensions & ( Extensions . TypeScript | Extensions . Declaration ) ) {
3065
3071
// 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 ;
3067
3075
}
3068
3076
}
3069
3077
else {
@@ -3073,6 +3081,25 @@ export function classicNameResolver(moduleName: string, containingFile: string,
3073
3081
}
3074
3082
}
3075
3083
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
+
3076
3103
// Program errors validate that `noEmit` or `emitDeclarationOnly` is also set,
3077
3104
// so this function doesn't check them to avoid propagating errors.
3078
3105
/** @internal */
0 commit comments