@@ -34,7 +34,7 @@ function mkdirp(p) {
3434 return __awaiter ( this , void 0 , void 0 , function * ( ) {
3535 if ( p && ! ( yield exists ( p ) ) ) {
3636 yield mkdirp ( path . dirname ( p ) ) ;
37- log_verbose ( `mkdir( ${ p } ) ` ) ;
37+ log_verbose ( `creating directory ${ p } in ${ process . cwd ( ) } ` ) ;
3838 try {
3939 yield fs . promises . mkdir ( p ) ;
4040 }
@@ -98,7 +98,10 @@ function deleteDirectory(p) {
9898}
9999function symlink ( target , p ) {
100100 return __awaiter ( this , void 0 , void 0 , function * ( ) {
101- log_verbose ( `symlink( ${ p } -> ${ target } )` ) ;
101+ if ( ! path . isAbsolute ( target ) ) {
102+ target = path . resolve ( process . cwd ( ) , target ) ;
103+ }
104+ log_verbose ( `creating symlink ${ p } -> ${ target } ` ) ;
102105 try {
103106 yield fs . promises . symlink ( target , p , 'junction' ) ;
104107 return true ;
@@ -117,33 +120,24 @@ function symlink(target, p) {
117120 }
118121 } ) ;
119122}
120- function resolveRoot ( root , startCwd , isExecroot , runfiles ) {
123+ function resolveExternalWorkspacePath ( workspace , startCwd , isExecroot , execroot , runfiles ) {
121124 return __awaiter ( this , void 0 , void 0 , function * ( ) {
122125 if ( isExecroot ) {
123- return root ? `${ startCwd } /external/${ root } ` : `${ startCwd } /node_modules` ;
124- }
125- const match = startCwd . match ( BAZEL_OUT_REGEX ) ;
126- if ( ! match ) {
127- if ( ! root ) {
128- return `${ startCwd } /node_modules` ;
129- }
130- return path . resolve ( `${ startCwd } /../${ root } ` ) ;
126+ return `${ execroot } /external/${ workspace } ` ;
131127 }
132- const symlinkRoot = startCwd . slice ( 0 , match . index ) ;
133- process . chdir ( symlinkRoot ) ;
134- if ( ! root ) {
135- return `${ symlinkRoot } /node_modules` ;
128+ if ( ! execroot ) {
129+ return path . resolve ( `${ startCwd } /../${ workspace } ` ) ;
136130 }
137- const fromManifest = runfiles . lookupDirectory ( root ) ;
131+ const fromManifest = runfiles . lookupDirectory ( workspace ) ;
138132 if ( fromManifest ) {
139133 return fromManifest ;
140134 }
141135 else {
142- const maybe = path . resolve ( `${ symlinkRoot } /external/${ root } ` ) ;
143- if ( fs . existsSync ( maybe ) ) {
136+ const maybe = path . resolve ( `${ execroot } /external/${ workspace } ` ) ;
137+ if ( yield exists ( maybe ) ) {
144138 return maybe ;
145139 }
146- return path . resolve ( `${ startCwd } /../${ root } ` ) ;
140+ return path . resolve ( `${ startCwd } /../${ workspace } ` ) ;
147141 }
148142 } ) ;
149143}
@@ -211,7 +205,7 @@ class Runfiles {
211205 if ( result ) {
212206 return result ;
213207 }
214- const e = new Error ( `could not resolve modulePath ${ modulePath } ` ) ;
208+ const e = new Error ( `could not resolve module ${ modulePath } ` ) ;
215209 e . code = 'MODULE_NOT_FOUND' ;
216210 throw e ;
217211 }
@@ -272,6 +266,9 @@ function exists(p) {
272266 } ) ;
273267}
274268function existsSync ( p ) {
269+ if ( ! p ) {
270+ return false ;
271+ }
275272 try {
276273 fs . lstatSync ( p ) ;
277274 return true ;
@@ -328,19 +325,6 @@ function liftElement(element) {
328325 }
329326 return element ;
330327}
331- function toParentLink ( link ) {
332- return [ link [ 0 ] , path . dirname ( link [ 1 ] ) ] ;
333- }
334- function allElementsAlign ( name , elements ) {
335- if ( ! elements [ 0 ] . link ) {
336- return false ;
337- }
338- const parentLink = toParentLink ( elements [ 0 ] . link ) ;
339- if ( ! elements . every ( e => ! ! e . link && isDirectChildLink ( parentLink , e . link ) ) ) {
340- return false ;
341- }
342- return ! ! elements [ 0 ] . link && allElementsAlignUnder ( name , parentLink , elements ) ;
343- }
344328function allElementsAlignUnder ( parentName , parentLink , elements ) {
345329 for ( const { name, link, children } of elements ) {
346330 if ( ! link || children ) {
@@ -361,16 +345,10 @@ function allElementsAlignUnder(parentName, parentLink, elements) {
361345function isDirectChildPath ( parent , child ) {
362346 return parent === path . dirname ( child ) ;
363347}
364- function isDirectChildLink ( [ parentRel , parentPath ] , [ childRel , childPath ] ) {
365- if ( parentRel !== childRel ) {
366- return false ;
367- }
368- if ( ! isDirectChildPath ( parentPath , childPath ) ) {
369- return false ;
370- }
371- return true ;
348+ function isDirectChildLink ( parentLink , childLink ) {
349+ return parentLink === path . dirname ( childLink ) ;
372350}
373- function isNameLinkPathTopAligned ( namePath , [ , linkPath ] ) {
351+ function isNameLinkPathTopAligned ( namePath , linkPath ) {
374352 return path . basename ( namePath ) === path . basename ( linkPath ) ;
375353}
376354function visitDirectoryPreserveLinks ( dirPath , visit ) {
@@ -390,28 +368,96 @@ function visitDirectoryPreserveLinks(dirPath, visit) {
390368 }
391369 } ) ;
392370}
371+ function findExecroot ( startCwd ) {
372+ if ( existsSync ( `${ startCwd } /bazel-out` ) ) {
373+ return startCwd ;
374+ }
375+ const bazelOutMatch = startCwd . match ( BAZEL_OUT_REGEX ) ;
376+ return bazelOutMatch ? startCwd . slice ( 0 , bazelOutMatch . index ) : undefined ;
377+ }
393378function main ( args , runfiles ) {
394379 return __awaiter ( this , void 0 , void 0 , function * ( ) {
395380 if ( ! args || args . length < 1 )
396381 throw new Error ( 'requires one argument: modulesManifest path' ) ;
397382 const [ modulesManifest ] = args ;
398- let { bin, root, modules, workspace } = JSON . parse ( fs . readFileSync ( modulesManifest ) ) ;
383+ log_verbose ( 'manifest file:' , modulesManifest ) ;
384+ let { workspace, bin, roots, modules } = JSON . parse ( fs . readFileSync ( modulesManifest ) ) ;
399385 modules = modules || { } ;
400- log_verbose ( 'manifest file' , modulesManifest ) ;
401- log_verbose ( 'manifest contents' , JSON . stringify ( { workspace, bin, root, modules } , null , 2 ) ) ;
386+ log_verbose ( 'manifest contents:' , JSON . stringify ( { workspace, bin, roots, modules } , null , 2 ) ) ;
402387 const startCwd = process . cwd ( ) . replace ( / \\ / g, '/' ) ;
403- log_verbose ( 'startCwd' , startCwd ) ;
404- const isExecroot = existsSync ( `${ startCwd } /bazel-out` ) ;
405- log_verbose ( 'isExecroot' , isExecroot . toString ( ) ) ;
406- const rootDir = yield resolveRoot ( root , startCwd , isExecroot , runfiles ) ;
407- log_verbose ( 'resolved node_modules root' , root , 'to' , rootDir ) ;
408- log_verbose ( 'cwd' , process . cwd ( ) ) ;
409- if ( ! ( yield exists ( rootDir ) ) ) {
410- log_verbose ( 'no third-party packages; mkdir node_modules at' , root ) ;
411- yield mkdirp ( rootDir ) ;
412- }
413- yield symlink ( rootDir , 'node_modules' ) ;
414- process . chdir ( rootDir ) ;
388+ log_verbose ( 'startCwd:' , startCwd ) ;
389+ const execroot = findExecroot ( startCwd ) ;
390+ log_verbose ( 'execroot:' , execroot ? execroot : 'not found' ) ;
391+ const isExecroot = startCwd == execroot ;
392+ log_verbose ( 'isExecroot:' , isExecroot . toString ( ) ) ;
393+ if ( ! isExecroot && execroot ) {
394+ process . chdir ( execroot ) ;
395+ log_verbose ( 'changed directory to execroot' , execroot ) ;
396+ }
397+ function symlinkWithUnlink ( target , p , stats = null ) {
398+ return __awaiter ( this , void 0 , void 0 , function * ( ) {
399+ if ( ! path . isAbsolute ( target ) ) {
400+ target = path . resolve ( process . cwd ( ) , target ) ;
401+ }
402+ if ( stats === null ) {
403+ stats = yield gracefulLstat ( p ) ;
404+ }
405+ if ( runfiles . manifest && execroot && stats !== null && stats . isSymbolicLink ( ) ) {
406+ const symlinkPath = fs . readlinkSync ( p ) . replace ( / \\ / g, '/' ) ;
407+ if ( path . relative ( symlinkPath , target ) != '' &&
408+ ! path . relative ( execroot , symlinkPath ) . startsWith ( '..' ) ) {
409+ log_verbose ( `Out-of-date symlink for ${ p } to ${ symlinkPath } detected. Target should be ${ target } . Unlinking.` ) ;
410+ yield unlink ( p ) ;
411+ }
412+ }
413+ return symlink ( target , p ) ;
414+ } ) ;
415+ }
416+ for ( const packagePath of Object . keys ( roots ) ) {
417+ const workspace = roots [ packagePath ] ;
418+ const workspacePath = yield resolveExternalWorkspacePath ( workspace , startCwd , isExecroot , execroot , runfiles ) ;
419+ log_verbose ( `resolved ${ workspace } workspace path to ${ workspacePath } ` ) ;
420+ const workspaceNodeModules = `${ workspacePath } /node_modules` ;
421+ if ( packagePath ) {
422+ if ( yield exists ( workspaceNodeModules ) ) {
423+ let resolvedPackagePath ;
424+ if ( yield exists ( packagePath ) ) {
425+ yield symlinkWithUnlink ( workspaceNodeModules , `${ packagePath } /node_modules` ) ;
426+ resolvedPackagePath = packagePath ;
427+ }
428+ if ( ! isExecroot ) {
429+ const runfilesPackagePath = `${ startCwd } /${ packagePath } ` ;
430+ if ( yield exists ( runfilesPackagePath ) ) {
431+ if ( resolvedPackagePath ) {
432+ yield symlinkWithUnlink ( `${ resolvedPackagePath } /node_modules` , `${ runfilesPackagePath } /node_modules` ) ;
433+ }
434+ else {
435+ yield symlinkWithUnlink ( workspaceNodeModules , `${ runfilesPackagePath } /node_modules` ) ;
436+ }
437+ resolvedPackagePath = runfilesPackagePath ;
438+ }
439+ }
440+ const packagePathBin = `${ bin } /${ packagePath } ` ;
441+ if ( resolvedPackagePath && ( yield exists ( packagePathBin ) ) ) {
442+ yield symlinkWithUnlink ( `${ resolvedPackagePath } /node_modules` , `${ packagePathBin } /node_modules` ) ;
443+ }
444+ }
445+ }
446+ else {
447+ if ( yield exists ( workspaceNodeModules ) ) {
448+ yield symlinkWithUnlink ( workspaceNodeModules , `node_modules` ) ;
449+ }
450+ else {
451+ log_verbose ( 'no root npm workspace node_modules folder to link to; creating node_modules directory in' , process . cwd ( ) ) ;
452+ yield mkdirp ( 'node_modules' ) ;
453+ }
454+ }
455+ }
456+ if ( ! roots || ! roots [ '' ] ) {
457+ log_verbose ( 'no root npm workspace; creating node_modules directory in ' , process . cwd ( ) ) ;
458+ yield mkdirp ( 'node_modules' ) ;
459+ }
460+ process . chdir ( 'node_modules' ) ;
415461 function isLeftoverDirectoryFromLinker ( stats , modulePath ) {
416462 return __awaiter ( this , void 0 , void 0 , function * ( ) {
417463 if ( runfiles . manifest === undefined ) {
@@ -451,44 +497,43 @@ function main(args, runfiles) {
451497 return __awaiter ( this , void 0 , void 0 , function * ( ) {
452498 yield mkdirp ( path . dirname ( m . name ) ) ;
453499 if ( m . link ) {
454- const [ root , modulePath ] = m . link ;
500+ const modulePath = m . link ;
455501 let target ;
456- switch ( root ) {
457- case 'execroot' :
458- if ( isExecroot ) {
459- target = `${ startCwd } /${ modulePath } ` ;
460- break ;
461- }
462- case 'runfiles' :
463- let runfilesPath = modulePath ;
464- if ( runfilesPath . startsWith ( `${ bin } /` ) ) {
465- runfilesPath = runfilesPath . slice ( bin . length + 1 ) ;
466- }
467- else if ( runfilesPath === bin ) {
468- runfilesPath = '' ;
469- }
470- const externalPrefix = 'external/' ;
471- if ( runfilesPath . startsWith ( externalPrefix ) ) {
472- runfilesPath = runfilesPath . slice ( externalPrefix . length ) ;
473- }
474- else {
475- runfilesPath = `${ workspace } /${ runfilesPath } ` ;
476- }
477- try {
478- target = runfiles . resolve ( runfilesPath ) ;
479- if ( runfiles . manifest && root == 'execroot' && modulePath . startsWith ( `${ bin } /` ) ) {
480- if ( ! target . includes ( `/${ bin } /` ) ) {
481- const e = new Error ( `could not resolve modulePath ${ modulePath } ` ) ;
482- e . code = 'MODULE_NOT_FOUND' ;
483- throw e ;
484- }
502+ if ( isExecroot ) {
503+ target = `${ startCwd } /${ modulePath } ` ;
504+ }
505+ if ( ! isExecroot || ! existsSync ( target ) ) {
506+ let runfilesPath = modulePath ;
507+ if ( runfilesPath . startsWith ( `${ bin } /` ) ) {
508+ runfilesPath = runfilesPath . slice ( bin . length + 1 ) ;
509+ }
510+ else if ( runfilesPath === bin ) {
511+ runfilesPath = '' ;
512+ }
513+ const externalPrefix = 'external/' ;
514+ if ( runfilesPath . startsWith ( externalPrefix ) ) {
515+ runfilesPath = runfilesPath . slice ( externalPrefix . length ) ;
516+ }
517+ else {
518+ runfilesPath = `${ workspace } /${ runfilesPath } ` ;
519+ }
520+ try {
521+ target = runfiles . resolve ( runfilesPath ) ;
522+ if ( runfiles . manifest && modulePath . startsWith ( `${ bin } /` ) ) {
523+ if ( ! target . match ( BAZEL_OUT_REGEX ) ) {
524+ const e = new Error ( `could not resolve module ${ runfilesPath } in output tree` ) ;
525+ e . code = 'MODULE_NOT_FOUND' ;
526+ throw e ;
485527 }
486528 }
487- catch ( err ) {
488- target = undefined ;
489- log_verbose ( `runfiles resolve failed for module '${ m . name } ': ${ err . message } ` ) ;
490- }
491- break ;
529+ }
530+ catch ( err ) {
531+ target = undefined ;
532+ log_verbose ( `runfiles resolve failed for module '${ m . name } ': ${ err . message } ` ) ;
533+ }
534+ }
535+ if ( target && ! path . isAbsolute ( target ) ) {
536+ target = path . resolve ( process . cwd ( ) , target ) ;
492537 }
493538 const stats = yield gracefulLstat ( m . name ) ;
494539 const isLeftOver = ( stats !== null && ( yield isLeftoverDirectoryFromLinker ( stats , m . name ) ) ) ;
@@ -497,7 +542,7 @@ function main(args, runfiles) {
497542 yield createSymlinkAndPreserveContents ( stats , m . name , target ) ;
498543 }
499544 else {
500- yield symlink ( target , m . name ) ;
545+ yield symlinkWithUnlink ( target , m . name , stats ) ;
501546 }
502547 }
503548 else {
0 commit comments