@@ -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