@@ -34,7 +34,7 @@ function mkdirp(p) {
34
34
return __awaiter ( this , void 0 , void 0 , function * ( ) {
35
35
if ( p && ! ( yield exists ( p ) ) ) {
36
36
yield mkdirp ( path . dirname ( p ) ) ;
37
- log_verbose ( `mkdir( ${ p } ) ` ) ;
37
+ log_verbose ( `creating directory ${ p } in ${ process . cwd ( ) } ` ) ;
38
38
try {
39
39
yield fs . promises . mkdir ( p ) ;
40
40
}
@@ -98,7 +98,10 @@ function deleteDirectory(p) {
98
98
}
99
99
function symlink ( target , p ) {
100
100
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 } ` ) ;
102
105
try {
103
106
yield fs . promises . symlink ( target , p , 'junction' ) ;
104
107
return true ;
@@ -117,33 +120,24 @@ function symlink(target, p) {
117
120
}
118
121
} ) ;
119
122
}
120
- function resolveRoot ( root , startCwd , isExecroot , runfiles ) {
123
+ function resolveExternalWorkspacePath ( workspace , startCwd , isExecroot , execroot , runfiles ) {
121
124
return __awaiter ( this , void 0 , void 0 , function * ( ) {
122
125
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 } ` ;
131
127
}
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 } ` ) ;
136
130
}
137
- const fromManifest = runfiles . lookupDirectory ( root ) ;
131
+ const fromManifest = runfiles . lookupDirectory ( workspace ) ;
138
132
if ( fromManifest ) {
139
133
return fromManifest ;
140
134
}
141
135
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 ) ) {
144
138
return maybe ;
145
139
}
146
- return path . resolve ( `${ startCwd } /../${ root } ` ) ;
140
+ return path . resolve ( `${ startCwd } /../${ workspace } ` ) ;
147
141
}
148
142
} ) ;
149
143
}
@@ -211,7 +205,7 @@ class Runfiles {
211
205
if ( result ) {
212
206
return result ;
213
207
}
214
- const e = new Error ( `could not resolve modulePath ${ modulePath } ` ) ;
208
+ const e = new Error ( `could not resolve module ${ modulePath } ` ) ;
215
209
e . code = 'MODULE_NOT_FOUND' ;
216
210
throw e ;
217
211
}
@@ -272,6 +266,9 @@ function exists(p) {
272
266
} ) ;
273
267
}
274
268
function existsSync ( p ) {
269
+ if ( ! p ) {
270
+ return false ;
271
+ }
275
272
try {
276
273
fs . lstatSync ( p ) ;
277
274
return true ;
@@ -328,19 +325,6 @@ function liftElement(element) {
328
325
}
329
326
return element ;
330
327
}
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
- }
344
328
function allElementsAlignUnder ( parentName , parentLink , elements ) {
345
329
for ( const { name, link, children } of elements ) {
346
330
if ( ! link || children ) {
@@ -361,16 +345,10 @@ function allElementsAlignUnder(parentName, parentLink, elements) {
361
345
function isDirectChildPath ( parent , child ) {
362
346
return parent === path . dirname ( child ) ;
363
347
}
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 ) ;
372
350
}
373
- function isNameLinkPathTopAligned ( namePath , [ , linkPath ] ) {
351
+ function isNameLinkPathTopAligned ( namePath , linkPath ) {
374
352
return path . basename ( namePath ) === path . basename ( linkPath ) ;
375
353
}
376
354
function visitDirectoryPreserveLinks ( dirPath , visit ) {
@@ -390,28 +368,96 @@ function visitDirectoryPreserveLinks(dirPath, visit) {
390
368
}
391
369
} ) ;
392
370
}
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
+ }
393
378
function main ( args , runfiles ) {
394
379
return __awaiter ( this , void 0 , void 0 , function * ( ) {
395
380
if ( ! args || args . length < 1 )
396
381
throw new Error ( 'requires one argument: modulesManifest path' ) ;
397
382
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 ) ) ;
399
385
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 ) ) ;
402
387
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' ) ;
415
461
function isLeftoverDirectoryFromLinker ( stats , modulePath ) {
416
462
return __awaiter ( this , void 0 , void 0 , function * ( ) {
417
463
if ( runfiles . manifest === undefined ) {
@@ -451,44 +497,43 @@ function main(args, runfiles) {
451
497
return __awaiter ( this , void 0 , void 0 , function * ( ) {
452
498
yield mkdirp ( path . dirname ( m . name ) ) ;
453
499
if ( m . link ) {
454
- const [ root , modulePath ] = m . link ;
500
+ const modulePath = m . link ;
455
501
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 ;
485
527
}
486
528
}
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 ) ;
492
537
}
493
538
const stats = yield gracefulLstat ( m . name ) ;
494
539
const isLeftOver = ( stats !== null && ( yield isLeftoverDirectoryFromLinker ( stats , m . name ) ) ) ;
@@ -497,7 +542,7 @@ function main(args, runfiles) {
497
542
yield createSymlinkAndPreserveContents ( stats , m . name , target ) ;
498
543
}
499
544
else {
500
- yield symlink ( target , m . name ) ;
545
+ yield symlinkWithUnlink ( target , m . name , stats ) ;
501
546
}
502
547
}
503
548
else {
0 commit comments