@@ -290,7 +290,27 @@ namespace ts.moduleSpecifiers {
290
290
if ( ! host . fileExists || ! host . readFile ) {
291
291
return undefined ;
292
292
}
293
- const parts : NodeModulePathParts = getNodeModulePathParts ( moduleFileName ) ! ;
293
+
294
+ let parts : NodeModulePathParts | PackagePathParts | undefined
295
+ = getNodeModulePathParts ( moduleFileName ) ;
296
+
297
+ let packageName : string | undefined ;
298
+ if ( ! parts && isPnpAvailable ( ) ) {
299
+ const pnpApi = require ( `pnpapi` ) as any ;
300
+ const locator = pnpApi . findPackageLocator ( moduleFileName ) ;
301
+ // eslint-disable-next-line no-null/no-null
302
+ if ( locator !== null ) {
303
+ const information = pnpApi . getPackageInformation ( locator ) ;
304
+ packageName = locator . name ;
305
+ parts = {
306
+ topLevelNodeModulesIndex : undefined ,
307
+ topLevelPackageNameIndex : undefined ,
308
+ packageRootIndex : information . packageLocation . length ,
309
+ fileNameIndex : moduleFileName . lastIndexOf ( `/` ) ,
310
+ } ;
311
+ }
312
+ }
313
+
294
314
if ( ! parts ) {
295
315
return undefined ;
296
316
}
@@ -322,21 +342,33 @@ namespace ts.moduleSpecifiers {
322
342
323
343
// If the module could be imported by a directory name, use that directory's name
324
344
const moduleSpecifier = packageNameOnly ? moduleFileName : getDirectoryOrExtensionlessFileName ( moduleFileName ) ;
325
- const globalTypingsCacheLocation = host . getGlobalTypingsCacheLocation && host . getGlobalTypingsCacheLocation ( ) ;
326
- // Get a path that's relative to node_modules or the importing file's path
327
- // if node_modules folder is in this folder or any of its parent folders, no need to keep it.
328
- const pathToTopLevelNodeModules = getCanonicalFileName ( moduleSpecifier . substring ( 0 , parts . topLevelNodeModulesIndex ) ) ;
345
+ // If we already know the package name, no need to go further
346
+ if ( packageName !== undefined ) {
347
+ const typelessPackageName = getPackageNameFromTypesPackageName ( packageName ) ;
348
+ return typelessPackageName + moduleSpecifier . substring ( parts . packageRootIndex ) ;
349
+ }
350
+
351
+ if ( parts . topLevelNodeModulesIndex === undefined || parts . topLevelPackageNameIndex === undefined ) {
352
+ return undefined ;
353
+ }
354
+
329
355
// If PnP is enabled the node_modules entries we'll get will always be relevant even if they
330
356
// are located in a weird path apparently outside of the source directory
331
- if ( ! isPnpAvailable ( ) && ! ( startsWith ( sourceDirectory , pathToTopLevelNodeModules ) || globalTypingsCacheLocation && startsWith ( getCanonicalFileName ( globalTypingsCacheLocation ) , pathToTopLevelNodeModules ) ) ) {
332
- return undefined ;
357
+ if ( ! isPnpAvailable ( ) ) {
358
+ const globalTypingsCacheLocation = host . getGlobalTypingsCacheLocation && host . getGlobalTypingsCacheLocation ( ) ;
359
+ // Get a path that's relative to node_modules or the importing file's path
360
+ // if node_modules folder is in this folder or any of its parent folders, no need to keep it.
361
+ const pathToTopLevelNodeModules = getCanonicalFileName ( moduleSpecifier . substring ( 0 , parts . topLevelNodeModulesIndex ) ) ;
362
+ if ( ! ( startsWith ( sourceDirectory , pathToTopLevelNodeModules ) || globalTypingsCacheLocation && startsWith ( getCanonicalFileName ( globalTypingsCacheLocation ) , pathToTopLevelNodeModules ) ) ) {
363
+ return undefined ;
364
+ }
333
365
}
334
366
335
367
// If the module was found in @types , get the actual Node package name
336
368
const nodeModulesDirectoryName = moduleSpecifier . substring ( parts . topLevelPackageNameIndex + 1 ) ;
337
- const packageName = getPackageNameFromTypesPackageName ( nodeModulesDirectoryName ) ;
369
+ const packageNameFromPath = getPackageNameFromTypesPackageName ( nodeModulesDirectoryName ) ;
338
370
// For classic resolution, only allow importing from node_modules/@types, not other node_modules
339
- return getEmitModuleResolutionKind ( options ) !== ModuleResolutionKind . NodeJs && packageName === nodeModulesDirectoryName ? undefined : packageName ;
371
+ return getEmitModuleResolutionKind ( options ) !== ModuleResolutionKind . NodeJs && packageNameFromPath === nodeModulesDirectoryName ? undefined : packageNameFromPath ;
340
372
341
373
function getDirectoryOrExtensionlessFileName ( path : string ) : string {
342
374
// If the file is the main module, it can be imported by the package name
@@ -355,8 +387,8 @@ namespace ts.moduleSpecifiers {
355
387
356
388
// If the file is /index, it can be imported by its directory name
357
389
// IFF there is not _also_ a file by the same name
358
- if ( getCanonicalFileName ( fullModulePathWithoutExtension . substring ( parts . fileNameIndex ) ) === "/index" && ! tryGetAnyFileFromPath ( host , fullModulePathWithoutExtension . substring ( 0 , parts . fileNameIndex ) ) ) {
359
- return fullModulePathWithoutExtension . substring ( 0 , parts . fileNameIndex ) ;
390
+ if ( getCanonicalFileName ( fullModulePathWithoutExtension . substring ( parts ! . fileNameIndex ) ) === "/index" && ! tryGetAnyFileFromPath ( host , fullModulePathWithoutExtension . substring ( 0 , parts ! . fileNameIndex ) ) ) {
391
+ return fullModulePathWithoutExtension . substring ( 0 , parts ! . fileNameIndex ) ;
360
392
}
361
393
362
394
return fullModulePathWithoutExtension ;
@@ -381,6 +413,12 @@ namespace ts.moduleSpecifiers {
381
413
readonly packageRootIndex : number ;
382
414
readonly fileNameIndex : number ;
383
415
}
416
+ interface PackagePathParts {
417
+ readonly topLevelNodeModulesIndex : undefined ;
418
+ readonly topLevelPackageNameIndex : undefined ;
419
+ readonly packageRootIndex : number ;
420
+ readonly fileNameIndex : number ;
421
+ }
384
422
function getNodeModulePathParts ( fullPath : string ) : NodeModulePathParts | undefined {
385
423
// If fullPath can't be valid module file within node_modules, returns undefined.
386
424
// Example of expected pattern: /base/path/node_modules/[@scope /otherpackage/@otherscope /node_modules/]package/[subdirectory/]file.js
0 commit comments