@@ -1285,12 +1285,40 @@ namespace ts.server {
1285
1285
}
1286
1286
1287
1287
let definitions = this . mapDefinitionInfoLocations ( unmappedDefinitionAndBoundSpan . definitions , project ) . slice ( ) ;
1288
- const needsJsResolution = every ( definitions . filter ( d => d . isAliasTarget ) , d => ! ! d . isAmbient ) || some ( definitions , d => ! ! d . failedAliasResolution ) ;
1288
+ const needsJsResolution = ! some ( definitions , d => ! ! d . isAliasTarget && ! d . isAmbient ) || some ( definitions , d => ! ! d . failedAliasResolution ) ;
1289
1289
if ( needsJsResolution ) {
1290
1290
project . withAuxiliaryProjectForFiles ( [ file ] , auxiliaryProject => {
1291
- const jsDefinitions = auxiliaryProject . getLanguageService ( ) . getDefinitionAndBoundSpan ( file , position ) ;
1292
- for ( const jsDefinition of jsDefinitions ?. definitions || emptyArray ) {
1293
- pushIfUnique ( definitions , jsDefinition , ( a , b ) => a . fileName === b . fileName && a . textSpan . start === b . textSpan . start ) ;
1291
+ const ls = auxiliaryProject . getLanguageService ( ) ;
1292
+ const jsDefinitions = ls . getDefinitionAndBoundSpan ( file , position , /*aliasesOnly*/ true ) ;
1293
+ if ( some ( jsDefinitions ?. definitions ) ) {
1294
+ for ( const jsDefinition of jsDefinitions ! . definitions ) {
1295
+ pushIfUnique ( definitions , jsDefinition , ( a , b ) => a . fileName === b . fileName && a . textSpan . start === b . textSpan . start ) ;
1296
+ }
1297
+ }
1298
+ else {
1299
+ const ambientCandidates = definitions . filter ( d => d . isAliasTarget && d . isAmbient ) ;
1300
+ for ( const candidate of ambientCandidates ) {
1301
+ const candidateFileName = getEffectiveFileNameOfDefinition ( candidate , project . getLanguageService ( ) . getProgram ( ) ! ) ;
1302
+ if ( candidateFileName ) {
1303
+ const fileNameToSearch = findImplementationFileFromDtsFileName ( candidateFileName , file , auxiliaryProject ) ;
1304
+ const scriptInfo = fileNameToSearch ? auxiliaryProject . getScriptInfo ( fileNameToSearch ) : undefined ;
1305
+ if ( ! scriptInfo ) {
1306
+ continue ;
1307
+ }
1308
+ if ( ! auxiliaryProject . containsScriptInfo ( scriptInfo ) ) {
1309
+ auxiliaryProject . addRoot ( scriptInfo ) ;
1310
+ }
1311
+ const auxiliaryProgram = auxiliaryProject . getLanguageService ( ) . getProgram ( ) ! ;
1312
+ const fileToSearch = Debug . checkDefined ( auxiliaryProgram . getSourceFile ( fileNameToSearch ! ) ) ;
1313
+ const matches = FindAllReferences . Core . getTopMostDeclarationsInFile ( candidate . name , fileToSearch ) ;
1314
+ for ( const match of matches ) {
1315
+ const symbol = auxiliaryProgram . getTypeChecker ( ) . getSymbolAtLocation ( match ) ;
1316
+ if ( symbol ) {
1317
+ pushIfUnique ( definitions , GoToDefinition . createDefinitionInfo ( match , auxiliaryProgram . getTypeChecker ( ) , symbol , match ) ) ;
1318
+ }
1319
+ }
1320
+ }
1321
+ }
1294
1322
}
1295
1323
} ) ;
1296
1324
}
@@ -1309,6 +1337,72 @@ namespace ts.server {
1309
1337
definitions : definitions . map ( Session . mapToOriginalLocation ) ,
1310
1338
textSpan,
1311
1339
} ;
1340
+
1341
+ function getEffectiveFileNameOfDefinition ( definition : DefinitionInfo , program : Program ) {
1342
+ const sourceFile = program . getSourceFile ( definition . fileName ) ! ;
1343
+ const checker = program . getTypeChecker ( ) ;
1344
+ const symbol = checker . getSymbolAtLocation ( getTouchingPropertyName ( sourceFile , definition . textSpan . start ) ) ;
1345
+ if ( symbol ) {
1346
+ let parent = symbol . parent ;
1347
+ while ( parent && ! isExternalModuleSymbol ( parent ) ) {
1348
+ parent = parent . parent ;
1349
+ }
1350
+ if ( parent ?. declarations && some ( parent . declarations , isExternalModuleAugmentation ) ) {
1351
+ // Always CommonJS right now, but who knows in the future
1352
+ const mode = getModeForUsageLocation ( sourceFile , find ( parent . declarations , isExternalModuleAugmentation ) ! . name as StringLiteral ) ;
1353
+ const fileName = sourceFile . resolvedModules ?. get ( stripQuotes ( parent . name ) , mode ) ?. resolvedFileName ;
1354
+ if ( fileName ) {
1355
+ return fileName ;
1356
+ }
1357
+ }
1358
+ const fileName = tryCast ( parent ?. valueDeclaration , isSourceFile ) ?. fileName ;
1359
+ if ( fileName ) {
1360
+ return fileName ;
1361
+ }
1362
+ }
1363
+ }
1364
+
1365
+ function findImplementationFileFromDtsFileName ( fileName : string , resolveFromFile : string , auxiliaryProject : Project ) {
1366
+ const nodeModulesPathParts = getNodeModulePathParts ( fileName ) ;
1367
+ if ( nodeModulesPathParts && fileName . lastIndexOf ( nodeModulesPathPart ) === nodeModulesPathParts . topLevelNodeModulesIndex ) {
1368
+ // Second check ensures the fileName only contains one `/node_modules/`. If there's more than one I give up.
1369
+ const packageDirectory = fileName . substring ( 0 , nodeModulesPathParts . packageRootIndex ) ;
1370
+ const packageJsonCache = project . getModuleResolutionCache ( ) ?. getPackageJsonInfoCache ( ) ;
1371
+ const compilerOptions = project . getCompilationSettings ( ) ;
1372
+ const packageJson = getPackageScopeForPath ( project . toPath ( packageDirectory + "/package.json" ) , packageJsonCache , project , compilerOptions ) ;
1373
+ if ( ! packageJson ) return undefined ;
1374
+ // Use fake options instead of actual compiler options to avoid following export map if the project uses node12 or nodenext -
1375
+ // Mapping from an export map entry across packages is out of scope for now. Returned entrypoints will only be what can be
1376
+ // resolved from the package root under --moduleResolution node
1377
+ const entrypoints = getEntrypointsFromPackageJsonInfo (
1378
+ packageJson ,
1379
+ { moduleResolution : ModuleResolutionKind . NodeJs } ,
1380
+ project ,
1381
+ project . getModuleResolutionCache ( ) ) ;
1382
+ // This substring is correct only because we checked for a single `/node_modules/` at the top.
1383
+ const packageNamePathPart = fileName . substring (
1384
+ nodeModulesPathParts . topLevelPackageNameIndex + 1 ,
1385
+ nodeModulesPathParts . packageRootIndex ) ;
1386
+ const packageName = getPackageNameFromTypesPackageName ( unmangleScopedPackageName ( packageNamePathPart ) ) ;
1387
+ const path = project . toPath ( fileName ) ;
1388
+ if ( entrypoints && some ( entrypoints , e => project . toPath ( e ) === path ) ) {
1389
+ // This file was the main entrypoint of a package. Try to resolve that same package name with
1390
+ // the auxiliary project that only resolves to implementation files.
1391
+ const [ implementationResolution ] = auxiliaryProject . resolveModuleNames ( [ packageName ] , resolveFromFile ) ;
1392
+ return implementationResolution ?. resolvedFileName ;
1393
+ }
1394
+ else {
1395
+ // It wasn't the main entrypoint but we are in node_modules. Try a subpath into the package.
1396
+ const pathToFileInPackage = fileName . substring ( nodeModulesPathParts . packageRootIndex + 1 ) ;
1397
+ const specifier = `${ packageName } /${ removeFileExtension ( pathToFileInPackage ) } ` ;
1398
+ const [ implementationResolution ] = auxiliaryProject . resolveModuleNames ( [ specifier ] , resolveFromFile ) ;
1399
+ return implementationResolution ?. resolvedFileName ;
1400
+ }
1401
+ }
1402
+ // We're not in node_modules, and we only get to this function if non-dts module resolution failed.
1403
+ // I'm not sure what else I can do here that isn't already covered by that module resolution.
1404
+ return undefined ;
1405
+ }
1312
1406
}
1313
1407
1314
1408
private getEmitOutput ( args : protocol . EmitOutputRequestArgs ) : EmitOutput | protocol . EmitOutput {
0 commit comments