@@ -23,7 +23,20 @@ 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 BinSymlinkMap = Map < PortablePath , Map < Filename , PortablePath > > ;
26+
27+ /**
28+ * @example
29+ * Map {
30+ * 'example-package' -> Map {
31+ * 'bin-file' ->
32+ * [
33+ * '/example-package/node_modules/example-dependency/bin/bin-file',
34+ * '/example-package/node_modules/example-dependency/'
35+ * ]
36+ * }
37+ * }
38+ */
39+ type BinSymlinkMap = Map < PortablePath , Map < Filename , [ PortablePath , PortablePath ] > > ;
2740type LoadManifest = ( locator : LocatorKey , installLocation : PortablePath ) => Promise < Pick < Manifest , `bin`> > ;
2841
2942export enum NodeModulesMode {
@@ -436,7 +449,7 @@ async function writeInstallState(project: Project, locatorMap: NodeModulesLocato
436449 throw new Error ( `Assertion failed: Expected the path to be within the project (${ location } )` ) ;
437450
438451 locatorState += ` ${ JSON . stringify ( internalPath ) } :\n` ;
439- for ( const [ name , target ] of symlinks ) {
452+ for ( const [ name , [ target ] ] of symlinks ) {
440453 const relativePath = ppath . relative ( ppath . join ( location , NODE_MODULES ) , target ) ;
441454 locatorState += ` ${ JSON . stringify ( name ) } : ${ JSON . stringify ( relativePath ) } \n` ;
442455 }
@@ -493,7 +506,7 @@ async function findInstallState(project: Project, {unrollAliases = false}: {unro
493506 const location = ppath . join ( rootPath , npath . toPortablePath ( relativeLocation ) ) ;
494507 const symlinks = miscUtils . getMapWithDefault ( binSymlinks , location ) ;
495508 for ( const [ name , target ] of Object . entries ( locationSymlinks as any ) ) {
496- symlinks . set ( name as Filename , npath . toPortablePath ( [ location , NODE_MODULES , target ] . join ( ppath . sep ) ) ) ;
509+ symlinks . set ( name as Filename , [ npath . toPortablePath ( [ location , NODE_MODULES , target ] . join ( ppath . sep ) ) , location ] ) ;
497510 }
498511 }
499512 }
@@ -990,14 +1003,14 @@ async function createBinSymlinkMap(installState: NodeModulesLocatorMap, location
9901003
9911004 const binSymlinks : BinSymlinkMap = new Map ( ) ;
9921005
993- const getBinSymlinks = ( location : PortablePath , parentLocatorLocation : PortablePath , node : LocationNode ) : Map < Filename , PortablePath > => {
1006+ const getBinSymlinks = ( location : PortablePath , parentLocatorLocation : PortablePath , node : LocationNode ) : Map < Filename , [ PortablePath , PortablePath ] > => {
9941007 const symlinks = new Map ( ) ;
9951008 const internalPath = ppath . contains ( projectRoot , location ) ;
9961009 if ( node . locator && internalPath !== null ) {
9971010 const binScripts = locatorScriptMap . get ( node . locator ) ! ;
9981011 for ( const [ filename , scriptPath ] of binScripts ) {
9991012 const symlinkTarget = ppath . join ( location , npath . toPortablePath ( scriptPath ) ) ;
1000- symlinks . set ( filename , symlinkTarget ) ;
1013+ symlinks . set ( filename , [ symlinkTarget , location ] ) ;
10011014 }
10021015 for ( const [ childLocation , childNode ] of node . children ) {
10031016 const absChildLocation = ppath . join ( location , childLocation ) ;
@@ -1009,8 +1022,8 @@ async function createBinSymlinkMap(installState: NodeModulesLocatorMap, location
10091022 } else {
10101023 for ( const [ childLocation , childNode ] of node . children ) {
10111024 const childSymlinks = getBinSymlinks ( ppath . join ( location , childLocation ) , parentLocatorLocation , childNode ) ;
1012- for ( const [ name , symlinkTarget ] of childSymlinks ) {
1013- symlinks . set ( name , symlinkTarget ) ;
1025+ for ( const [ name , value ] of childSymlinks ) {
1026+ symlinks . set ( name , value ) ;
10141027 }
10151028 }
10161029 }
@@ -1315,7 +1328,9 @@ async function persistNodeModules(preinstallState: InstallState, installState: N
13151328 await xfs . mkdirPromise ( rootNmDirPath , { recursive : true } ) ;
13161329
13171330 const binSymlinks = await createBinSymlinkMap ( installState , locationTree , project . cwd , { loadManifest} ) ;
1318- await persistBinSymlinks ( prevBinSymlinks , binSymlinks , project . cwd , windowsLinkType ) ;
1331+ const addedDirs = new Set ( addList . map ( l => l . dstDir ) ) ;
1332+
1333+ await persistBinSymlinks ( prevBinSymlinks , binSymlinks , project . cwd , windowsLinkType , addedDirs ) ;
13191334
13201335 await writeInstallState ( project , installState , binSymlinks , nmMode , { installChangedByUser} ) ;
13211336
@@ -1327,7 +1342,7 @@ async function persistNodeModules(preinstallState: InstallState, installState: N
13271342 }
13281343}
13291344
1330- async function persistBinSymlinks ( previousBinSymlinks : BinSymlinkMap , binSymlinks : BinSymlinkMap , projectCwd : PortablePath , windowsLinkType : WindowsLinkType ) {
1345+ async function persistBinSymlinks ( previousBinSymlinks : BinSymlinkMap , binSymlinks : BinSymlinkMap , projectCwd : PortablePath , windowsLinkType : WindowsLinkType , addedDirs : Set < PortablePath > ) {
13311346 // Delete outdated .bin folders
13321347 for ( const location of previousBinSymlinks . keys ( ) ) {
13331348 if ( ppath . contains ( projectCwd , location ) === null )
@@ -1354,11 +1369,11 @@ async function persistBinSymlinks(previousBinSymlinks: BinSymlinkMap, binSymlink
13541369 }
13551370 }
13561371
1357- for ( const [ name , target ] of symlinks ) {
1372+ for ( const [ name , [ target , binPackageLocation ] ] of symlinks ) {
13581373 const prevTarget = prevSymlinks . get ( name ) ;
13591374 const symlinkPath = ppath . join ( binDir , name ) ;
1360- // Skip unchanged .bin symlinks
1361- if ( prevTarget === target )
1375+ // Skip unchanged .bin symlinks except for added/changed packages since they need permissions applied
1376+ if ( prevTarget === target && ! addedDirs . has ( binPackageLocation ) )
13621377 continue ;
13631378
13641379 if ( process . platform === `win32` ) {
0 commit comments