@@ -23,18 +23,28 @@ const INSTALL_STATE_FILE = `.yarn-state.yml` as Filename;
2323const MTIME_ACCURANCY = 1000 ;
2424
2525type InstallState = { locatorMap : NodeModulesLocatorMap , locationTree : LocationTree , binSymlinks : BinSymlinkMap , nmMode : NodeModulesMode , mtimeMs : number } ;
26+ type InstallStateCacheEntry = { statKey : string | null , promise : Promise < InstallState | null > } ;
2627type BinSymlinkMap = Map < PortablePath , Map < Filename , PortablePath > > ;
2728type LoadManifest = ( locator : LocatorKey , installLocation : PortablePath ) => Promise < Pick < Manifest , 'bin' > > ;
2829
30+ const installStateCache : Map < string , InstallStateCacheEntry > = new Map ( ) ;
31+
32+ function getInstallStateCacheKey ( project : Project , { unrollAliases} : { unrollAliases : boolean } ) {
33+ return `${ project . cwd } \0${ unrollAliases ? `unroll` : `plain` } ` ;
34+ }
35+
36+ function clearInstallStateCache ( project : Project ) {
37+ installStateCache . delete ( getInstallStateCacheKey ( project , { unrollAliases : true } ) ) ;
38+ installStateCache . delete ( getInstallStateCacheKey ( project , { unrollAliases : false } ) ) ;
39+ }
40+
2941export enum NodeModulesMode {
3042 CLASSIC = `classic` ,
3143 HARDLINKS_LOCAL = `hardlinks-local` ,
3244 HARDLINKS_GLOBAL = `hardlinks-global` ,
3345}
3446
3547export class NodeModulesLinker implements Linker {
36- private installStateCache : Map < string , Promise < InstallState | null > > = new Map ( ) ;
37-
3848 getCustomDataKey ( ) {
3949 return JSON . stringify ( {
4050 name : `NodeModulesLinker` ,
@@ -54,9 +64,7 @@ export class NodeModulesLinker implements Linker {
5464 if ( workspace )
5565 return workspace . cwd ;
5666
57- const installState = await miscUtils . getFactoryWithDefault ( this . installStateCache , opts . project . cwd , async ( ) => {
58- return await findInstallState ( opts . project , { unrollAliases : true } ) ;
59- } ) ;
67+ const installState = await this . findInstallState ( opts . project , { unrollAliases : true } ) ;
6068
6169 if ( installState === null )
6270 throw new UsageError ( `Couldn't find the node_modules state file - running an install might help (findPackageLocation)` ) ;
@@ -79,9 +87,7 @@ export class NodeModulesLinker implements Linker {
7987 if ( ! this . isEnabled ( opts ) )
8088 return null ;
8189
82- const installState = await miscUtils . getFactoryWithDefault ( this . installStateCache , opts . project . cwd , async ( ) => {
83- return await findInstallState ( opts . project , { unrollAliases : true } ) ;
84- } ) ;
90+ const installState = await this . findInstallState ( opts . project , { unrollAliases : true } ) ;
8591
8692 if ( installState === null )
8793 return null ;
@@ -107,6 +113,19 @@ export class NodeModulesLinker implements Linker {
107113 return new NodeModulesInstaller ( opts ) ;
108114 }
109115
116+ private async findInstallState ( project : Project , { unrollAliases} : { unrollAliases : boolean } ) {
117+ const cacheKey = getInstallStateCacheKey ( project , { unrollAliases} ) ;
118+ const statKey = await getInstallStateStatKey ( project ) ;
119+ const cached = installStateCache . get ( cacheKey ) ;
120+
121+ if ( cached ?. statKey === statKey )
122+ return await cached . promise ;
123+
124+ const promise = findInstallState ( project , { unrollAliases} ) ;
125+ installStateCache . set ( cacheKey , { statKey, promise} ) ;
126+ return await promise ;
127+ }
128+
110129 private isEnabled ( opts : MinimalLinkOptions ) {
111130 return opts . project . configuration . get ( `nodeLinker` ) === `node-modules` ;
112131 }
@@ -447,13 +466,29 @@ async function writeInstallState(project: Project, locatorMap: NodeModulesLocato
447466 const rootPath = project . cwd ;
448467 const installStatePath = ppath . join ( rootPath , NODE_MODULES , INSTALL_STATE_FILE ) ;
449468
450- // Force install state file rewrite, so that it has mtime bigger than all node_modules subfolders
451- if ( installChangedByUser )
452- await xfs . removePromise ( installStatePath ) ;
469+ try {
470+ // Force install state file rewrite, so that it has mtime bigger than all node_modules subfolders
471+ if ( installChangedByUser )
472+ await xfs . removePromise ( installStatePath ) ;
453473
454- await xfs . changeFilePromise ( installStatePath , locatorState , {
455- automaticNewlines : true ,
456- } ) ;
474+ await xfs . changeFilePromise ( installStatePath , locatorState , {
475+ automaticNewlines : true ,
476+ } ) ;
477+ } finally {
478+ clearInstallStateCache ( project ) ;
479+ }
480+ }
481+
482+ async function getInstallStateStatKey ( project : Project ) {
483+ const rootPath = project . cwd ;
484+ const installStatePath = ppath . join ( rootPath , NODE_MODULES , INSTALL_STATE_FILE ) ;
485+
486+ try {
487+ const stats = await xfs . statPromise ( installStatePath ) ;
488+ return `${ stats . mtimeMs } :${ stats . size } ` ;
489+ } catch {
490+ return null ;
491+ }
457492}
458493
459494async function findInstallState ( project : Project , { unrollAliases = false } : { unrollAliases ?: boolean } = { } ) : Promise < InstallState | null > {
0 commit comments