@@ -259,6 +259,7 @@ export interface ModuleResolutionState {
259
259
requestContainingDirectory : string | undefined ;
260
260
reportDiagnostic : DiagnosticReporter ;
261
261
isConfigLookup : boolean ;
262
+ candidateIsFromPackageJsonField : boolean ;
262
263
}
263
264
264
265
/** Just the fields that we use for module resolution.
@@ -526,6 +527,7 @@ export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string
526
527
requestContainingDirectory : containingDirectory ,
527
528
reportDiagnostic : diag => void diagnostics . push ( diag ) ,
528
529
isConfigLookup : false ,
530
+ candidateIsFromPackageJsonField : false ,
529
531
} ;
530
532
let resolved = primaryLookup ( ) ;
531
533
let primary = true ;
@@ -1652,6 +1654,7 @@ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleNa
1652
1654
requestContainingDirectory : containingDirectory ,
1653
1655
reportDiagnostic : diag => void diagnostics . push ( diag ) ,
1654
1656
isConfigLookup,
1657
+ candidateIsFromPackageJsonField : false ,
1655
1658
} ;
1656
1659
1657
1660
if ( traceEnabled && getEmitModuleResolutionKind ( compilerOptions ) >= ModuleResolutionKind . Node16 && getEmitModuleResolutionKind ( compilerOptions ) <= ModuleResolutionKind . NodeNext ) {
@@ -1948,7 +1951,7 @@ function tryAddingExtensions(candidate: string, extensions: Extensions, original
1948
1951
1949
1952
function tryExtension ( ext : string , resolvedUsingTsExtension ?: boolean ) : PathAndExtension | undefined {
1950
1953
const path = tryFile ( candidate + ext , onlyRecordFailures , state ) ;
1951
- return path === undefined ? undefined : { path, ext, resolvedUsingTsExtension } ;
1954
+ return path === undefined ? undefined : { path, ext, resolvedUsingTsExtension : ! state . candidateIsFromPackageJsonField && resolvedUsingTsExtension } ;
1952
1955
}
1953
1956
}
1954
1957
@@ -2111,6 +2114,7 @@ export function getTemporaryModuleResolutionState(packageJsonInfoCache: PackageJ
2111
2114
requestContainingDirectory : undefined ,
2112
2115
reportDiagnostic : noop ,
2113
2116
isConfigLookup : false ,
2117
+ candidateIsFromPackageJsonField : false ,
2114
2118
} ;
2115
2119
}
2116
2120
@@ -2232,11 +2236,14 @@ function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: st
2232
2236
// (technically it only emits a deprecation warning in esm packages right now, but that's probably
2233
2237
// enough to mean we don't need to support it)
2234
2238
const features = state . features ;
2239
+ const candidateIsFromPackageJsonField = state . candidateIsFromPackageJsonField ;
2240
+ state . candidateIsFromPackageJsonField = true ;
2235
2241
if ( jsonContent ?. type !== "module" ) {
2236
2242
state . features &= ~ NodeResolutionFeatures . EsmMode ;
2237
2243
}
2238
2244
const result = nodeLoadModuleByRelativeName ( expandedExtensions , candidate , onlyRecordFailures , state , /*considerPackageJson*/ false ) ;
2239
2245
state . features = features ;
2246
+ state . candidateIsFromPackageJsonField = candidateIsFromPackageJsonField ;
2240
2247
return result ;
2241
2248
} ;
2242
2249
@@ -2313,7 +2320,18 @@ function loadModuleFromSelfNameReference(extensions: Extensions, moduleName: str
2313
2320
return undefined ;
2314
2321
}
2315
2322
const trailingParts = parts . slice ( nameParts . length ) ;
2316
- return loadModuleFromExports ( scope , extensions , ! length ( trailingParts ) ? "." : `.${ directorySeparator } ${ trailingParts . join ( directorySeparator ) } ` , state , cache , redirectedReference ) ;
2323
+ const subpath = ! length ( trailingParts ) ? "." : `.${ directorySeparator } ${ trailingParts . join ( directorySeparator ) } ` ;
2324
+ // Maybe TODO: splitting extensions into two priorities should be unnecessary, except
2325
+ // https://github.com/microsoft/TypeScript/issues/50762 makes the behavior different.
2326
+ // As long as that bug exists, we need to do two passes here in self-name loading
2327
+ // in order to be consistent with (non-self) library-name loading in
2328
+ // `loadModuleFromNearestNodeModulesDirectoryWorker`, which uses two passes in order
2329
+ // to prioritize `@types` packages higher up the directory tree over untyped
2330
+ // implementation packages.
2331
+ const priorityExtensions = extensions & ( Extensions . TypeScript | Extensions . Declaration ) ;
2332
+ const secondaryExtensions = extensions & ~ ( Extensions . TypeScript | Extensions . Declaration ) ;
2333
+ return loadModuleFromExports ( scope , priorityExtensions , subpath , state , cache , redirectedReference )
2334
+ || loadModuleFromExports ( scope , secondaryExtensions , subpath , state , cache , redirectedReference ) ;
2317
2335
}
2318
2336
2319
2337
function loadModuleFromExports ( scope : PackageJsonInfo , extensions : Extensions , subpath : string , state : ModuleResolutionState , cache : ModuleResolutionCache | undefined , redirectedReference : ResolvedProjectReference | undefined ) : SearchResult < Resolved > {
@@ -2753,27 +2771,31 @@ function loadModuleFromImmediateNodeModulesDirectory(extensions: Extensions, mod
2753
2771
2754
2772
function loadModuleFromSpecificNodeModulesDirectory ( extensions : Extensions , moduleName : string , nodeModulesDirectory : string , nodeModulesDirectoryExists : boolean , state : ModuleResolutionState , cache : ModuleResolutionCache | undefined , redirectedReference : ResolvedProjectReference | undefined ) : Resolved | undefined {
2755
2773
const candidate = normalizePath ( combinePaths ( nodeModulesDirectory , moduleName ) ) ;
2774
+ const { packageName, rest } = parsePackageName ( moduleName ) ;
2775
+ const packageDirectory = combinePaths ( nodeModulesDirectory , packageName ) ;
2756
2776
2777
+ let rootPackageInfo : PackageJsonInfo | undefined ;
2757
2778
// First look for a nested package.json, as in `node_modules/foo/bar/package.json`.
2758
2779
let packageInfo = getPackageJsonInfo ( candidate , ! nodeModulesDirectoryExists , state ) ;
2759
2780
// But only if we're not respecting export maps (if we are, we might redirect around this location)
2760
- if ( ! ( state . features & NodeResolutionFeatures . Exports ) ) {
2761
- if ( packageInfo ) {
2762
- const fromFile = loadModuleFromFile ( extensions , candidate , ! nodeModulesDirectoryExists , state ) ;
2763
- if ( fromFile ) {
2764
- return noPackageId ( fromFile ) ;
2765
- }
2766
-
2767
- const fromDirectory = loadNodeModuleFromDirectoryWorker (
2768
- extensions ,
2769
- candidate ,
2770
- ! nodeModulesDirectoryExists ,
2771
- state ,
2772
- packageInfo . contents . packageJsonContent ,
2773
- getVersionPathsOfPackageJsonInfo ( packageInfo , state ) ,
2774
- ) ;
2775
- return withPackageId ( packageInfo , fromDirectory ) ;
2781
+ if ( rest !== "" && packageInfo && (
2782
+ ! ( state . features & NodeResolutionFeatures . Exports ) ||
2783
+ ! hasProperty ( ( rootPackageInfo = getPackageJsonInfo ( packageDirectory , ! nodeModulesDirectoryExists , state ) ) ?. contents . packageJsonContent ?? emptyArray , "exports" )
2784
+ ) ) {
2785
+ const fromFile = loadModuleFromFile ( extensions , candidate , ! nodeModulesDirectoryExists , state ) ;
2786
+ if ( fromFile ) {
2787
+ return noPackageId ( fromFile ) ;
2776
2788
}
2789
+
2790
+ const fromDirectory = loadNodeModuleFromDirectoryWorker (
2791
+ extensions ,
2792
+ candidate ,
2793
+ ! nodeModulesDirectoryExists ,
2794
+ state ,
2795
+ packageInfo . contents . packageJsonContent ,
2796
+ getVersionPathsOfPackageJsonInfo ( packageInfo , state ) ,
2797
+ ) ;
2798
+ return withPackageId ( packageInfo , fromDirectory ) ;
2777
2799
}
2778
2800
2779
2801
const loader : ResolutionKindSpecificLoader = ( extensions , candidate , onlyRecordFailures , state ) => {
@@ -2800,11 +2822,9 @@ function loadModuleFromSpecificNodeModulesDirectory(extensions: Extensions, modu
2800
2822
return withPackageId ( packageInfo , pathAndExtension ) ;
2801
2823
} ;
2802
2824
2803
- const { packageName, rest } = parsePackageName ( moduleName ) ;
2804
- const packageDirectory = combinePaths ( nodeModulesDirectory , packageName ) ;
2805
2825
if ( rest !== "" ) {
2806
2826
// Previous `packageInfo` may have been from a nested package.json; ensure we have the one from the package root now.
2807
- packageInfo = getPackageJsonInfo ( packageDirectory , ! nodeModulesDirectoryExists , state ) ;
2827
+ packageInfo = rootPackageInfo ?? getPackageJsonInfo ( packageDirectory , ! nodeModulesDirectoryExists , state ) ;
2808
2828
}
2809
2829
// package exports are higher priority than file/directory/typesVersions lookups and (and, if there's exports present, blocks them)
2810
2830
if ( packageInfo && packageInfo . contents . packageJsonContent . exports && state . features & NodeResolutionFeatures . Exports ) {
@@ -2935,6 +2955,7 @@ export function classicNameResolver(moduleName: string, containingFile: string,
2935
2955
requestContainingDirectory : containingDirectory ,
2936
2956
reportDiagnostic : diag => void diagnostics . push ( diag ) ,
2937
2957
isConfigLookup : false ,
2958
+ candidateIsFromPackageJsonField : false ,
2938
2959
} ;
2939
2960
2940
2961
const resolved =
@@ -3015,6 +3036,7 @@ export function loadModuleFromGlobalCache(moduleName: string, projectName: strin
3015
3036
requestContainingDirectory : undefined ,
3016
3037
reportDiagnostic : diag => void diagnostics . push ( diag ) ,
3017
3038
isConfigLookup : false ,
3039
+ candidateIsFromPackageJsonField : false ,
3018
3040
} ;
3019
3041
const resolved = loadModuleFromImmediateNodeModulesDirectory ( Extensions . Declaration , moduleName , globalCache , state , /*typesScopeOnly*/ false , /*cache*/ undefined , /*redirectedReference*/ undefined ) ;
3020
3042
return createResolvedModuleWithFailedLookupLocations (
0 commit comments