@@ -259,26 +259,56 @@ namespace ts {
259259
260260 /**
261261 * Returns the path to every node_modules/@types directory from some ancestor directory.
262- * Returns undefined if there are none.
263262 */
264- function getDefaultTypeRoots ( currentDirectory : string , host : { directoryExists ?: ( directoryName : string ) => boolean } ) : string [ ] | undefined {
263+ function getNodeModulesTypeRoots ( currentDirectory : string , host : { directoryExists ?: ( directoryName : string ) => boolean } ) {
265264 if ( ! host . directoryExists ) {
266265 return [ combinePaths ( currentDirectory , nodeModulesAtTypes ) ] ;
267266 // And if it doesn't exist, tough.
268267 }
269268
270- let typeRoots : string [ ] | undefined ;
269+ const typeRoots : string [ ] = [ ] ;
271270 forEachAncestorDirectory ( normalizePath ( currentDirectory ) , directory => {
272271 const atTypes = combinePaths ( directory , nodeModulesAtTypes ) ;
273272 if ( host . directoryExists ! ( atTypes ) ) {
274- ( typeRoots || ( typeRoots = [ ] ) ) . push ( atTypes ) ;
273+ typeRoots . push ( atTypes ) ;
275274 }
276275 return undefined ;
277276 } ) ;
277+
278278 return typeRoots ;
279279 }
280280 const nodeModulesAtTypes = combinePaths ( "node_modules" , "@types" ) ;
281281
282+ function getPnpTypeRoots ( currentDirectory : string ) {
283+ if ( ! isPnpAvailable ( ) ) {
284+ return [ ] ;
285+ }
286+
287+ const pnpapi = getPnpApi ( ) ;
288+ const locator = pnpapi . findPackageLocator ( `${ currentDirectory } /` ) ;
289+ const { packageDependencies} = pnpapi . getPackageInformation ( locator ) ;
290+
291+ const typeRoots : string [ ] = [ ] ;
292+ for ( const [ name , referencish ] of Array . from < any > ( packageDependencies . entries ( ) ) ) {
293+ if ( name . startsWith ( typesPackagePrefix ) && referencish !== null ) {
294+ const dependencyLocator = pnpapi . getLocator ( name , referencish ) ;
295+ const { packageLocation} = pnpapi . getPackageInformation ( dependencyLocator ) ;
296+
297+ typeRoots . push ( getDirectoryPath ( packageLocation ) ) ;
298+ }
299+ }
300+
301+ return typeRoots ;
302+ }
303+ const typesPackagePrefix = "@types/" ;
304+
305+ function getDefaultTypeRoots ( currentDirectory : string , host : { directoryExists ?: ( directoryName : string ) => boolean } ) : string [ ] | undefined {
306+ const nmTypes = getNodeModulesTypeRoots ( currentDirectory , host ) ;
307+ const pnpTypes = getPnpTypeRoots ( currentDirectory ) ;
308+
309+ return [ ...nmTypes , ...pnpTypes ] ;
310+ }
311+
282312 /**
283313 * @param {string | undefined } containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
284314 * This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
@@ -951,8 +981,14 @@ namespace ts {
951981 if ( traceEnabled ) {
952982 trace ( host , Diagnostics . Loading_module_0_from_node_modules_folder_target_file_type_1 , moduleName , Extensions [ extensions ] ) ;
953983 }
954- const resolved = loadModuleFromNearestNodeModulesDirectory ( extensions , moduleName , containingDirectory , state , cache , redirectedReference ) ;
955- if ( ! resolved ) return undefined ;
984+
985+ const resolved = isPnpAvailable ( )
986+ ? tryLoadModuleUsingPnpResolution ( extensions , moduleName , containingDirectory , state )
987+ : loadModuleFromNearestNodeModulesDirectory ( extensions , moduleName , containingDirectory , state , cache , redirectedReference ) ;
988+
989+ if ( ! resolved ) {
990+ return undefined ;
991+ }
956992
957993 let resolvedValue = resolved . value ;
958994 if ( ! compilerOptions . preserveSymlinks && resolvedValue && ! resolvedValue . originalPath ) {
@@ -1524,4 +1560,58 @@ namespace ts {
15241560 function toSearchResult < T > ( value : T | undefined ) : SearchResult < T > {
15251561 return value !== undefined ? { value } : undefined ;
15261562 }
1563+
1564+ /**
1565+ * We only allow PnP to be used as a resolution strategy if TypeScript
1566+ * itself is executed under a PnP runtime (and we only allow it to access
1567+ * the current PnP runtime, not any on the disk). This ensures that we
1568+ * don't execute potentially malicious code that didn't already have a
1569+ * chance to be executed (if we're running within the runtime, it means
1570+ * that the runtime has already been executed).
1571+ * @internal
1572+ */
1573+ export function isPnpAvailable ( ) {
1574+ // @ts -ignore
1575+ return process . versions . pnp ;
1576+ }
1577+
1578+ function getPnpApi ( ) {
1579+ return require ( "pnpapi" ) ;
1580+ }
1581+
1582+ function loadPnpPackageResolution ( packageName : string , issuer : string ) {
1583+ return getPnpApi ( ) . resolveToUnqualified ( packageName , issuer , { considerBuiltins : false } ) ;
1584+ }
1585+
1586+ function loadPnpTypePackageResolution ( packageName : string , issuer : string ) {
1587+ return loadPnpPackageResolution ( getTypesPackageName ( packageName ) , issuer ) ;
1588+ }
1589+
1590+ /* @internal */
1591+ export function tryLoadModuleUsingPnpResolution ( extensions : Extensions , moduleName : string , issuer : string , state : ModuleResolutionState ) {
1592+ const { packageName, rest} = parsePackageName ( moduleName ) ;
1593+
1594+ const packageResolution = loadPnpPackageResolution ( packageName , issuer ) ;
1595+ const packageFullResolution = packageResolution !== null
1596+ ? nodeLoadModuleByRelativeName ( extensions , combinePaths ( packageResolution , rest ) , /*onlyRecordFailures*/ false , state , /*considerPackageJson*/ true )
1597+ : undefined ;
1598+
1599+ let resolved ;
1600+ if ( packageFullResolution ) {
1601+ resolved = packageFullResolution ;
1602+ } else if ( extensions === Extensions . TypeScript || extensions === Extensions . DtsOnly ) {
1603+ const typePackageResolution = loadPnpTypePackageResolution ( packageName , issuer ) ;
1604+ const typePackageFullResolution = typePackageResolution !== null
1605+ ? nodeLoadModuleByRelativeName ( Extensions . DtsOnly , combinePaths ( typePackageResolution , rest ) , /*onlyRecordFailures*/ false , state , /*considerPackageJson*/ true )
1606+ : undefined ;
1607+
1608+ if ( typePackageFullResolution ) {
1609+ resolved = typePackageFullResolution ;
1610+ }
1611+ }
1612+
1613+ if ( resolved ) {
1614+ return toSearchResult ( resolved ) ;
1615+ }
1616+ }
15271617}
0 commit comments