@@ -48,20 +48,26 @@ function log_verbose(...m: any[]) {
48
48
if ( ! ! process . env [ 'VERBOSE_LOGS' ] ) console . error ( '[generate_build_file.ts]' , ...m ) ;
49
49
}
50
50
51
- const BUILD_FILE_HEADER = `# Generated file from yarn_install/npm_install rule.
52
- # See rules_nodejs/internal/npm_install/generate_build_file.ts
53
-
54
- # All rules in other repositories can use these targets
55
- package(default_visibility = ["//visibility:public"])
56
-
57
- `
58
-
59
51
const args = process . argv . slice ( 2 ) ;
60
52
const WORKSPACE = args [ 0 ] ;
61
53
const RULE_TYPE = args [ 1 ] ;
62
- const LOCK_FILE_PATH = args [ 2 ] ;
63
- const INCLUDED_FILES = args [ 3 ] ? args [ 3 ] . split ( ',' ) : [ ] ;
64
- const BAZEL_VERSION = args [ 4 ] ;
54
+ const PKG_JSON_FILE_PATH = args [ 2 ] ;
55
+ const LOCK_FILE_PATH = args [ 3 ] ;
56
+ const STRICT_VISIBILITY = args [ 4 ] ?. toLowerCase ( ) === 'true' ;
57
+ const INCLUDED_FILES = args [ 5 ] ? args [ 5 ] . split ( ',' ) : [ ] ;
58
+ const BAZEL_VERSION = args [ 6 ] ;
59
+
60
+ const PUBLIC_VISIBILITY = '//visibility:public' ;
61
+ const LIMITED_VISIBILITY = `@${ WORKSPACE } //:__subpackages__` ;
62
+
63
+ function generateBuildFileHeader ( visibility = PUBLIC_VISIBILITY ) : string {
64
+ return `# Generated file from ${ RULE_TYPE } rule.
65
+ # See rules_nodejs/internal/npm_install/generate_build_file.ts
66
+
67
+ package(default_visibility = ["${ visibility } "])
68
+
69
+ ` ;
70
+ }
65
71
66
72
if ( require . main === module ) {
67
73
main ( ) ;
@@ -91,8 +97,11 @@ function writeFileSync(p: string, content: string) {
91
97
* Main entrypoint.
92
98
*/
93
99
export function main ( ) {
100
+ // get a set of all the direct dependencies for visibility
101
+ const deps = getDirectDependencySet ( PKG_JSON_FILE_PATH ) ;
102
+
94
103
// find all packages (including packages in nested node_modules)
95
- const pkgs = findPackages ( ) ;
104
+ const pkgs = findPackages ( 'node_modules' , deps ) ;
96
105
97
106
// flatten dependencies
98
107
flattenDependencies ( pkgs ) ;
@@ -157,7 +166,7 @@ function generateRootBuildFile(pkgs: Dep[]) {
157
166
` ;
158
167
} ) } ) ;
159
168
160
- let buildFile = BUILD_FILE_HEADER + `load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
169
+ let buildFile = generateBuildFileHeader ( ) + `load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
161
170
162
171
exports_files([
163
172
${ exportsStarlark } ])
@@ -198,12 +207,18 @@ function generatePackageBuildFiles(pkg: Dep) {
198
207
buildFilePath = 'BUILD.bazel'
199
208
}
200
209
210
+ // if the dependency doesn't appear in the given package.json file, and the 'strict_visibility' flag is set
211
+ // on the npm_install / yarn_install rule, then set the visibility to be limited internally to the @repo workspace
212
+ // if the dependency is listed, set it as public
213
+ // if the flag is false, then always set public visibility
214
+ const visibility = ! pkg . _directDependency && STRICT_VISIBILITY ? LIMITED_VISIBILITY : PUBLIC_VISIBILITY ;
215
+
201
216
// If the package didn't ship a bin/BUILD file, generate one.
202
217
if ( ! pkg . _files . includes ( 'bin/BUILD.bazel' ) && ! pkg . _files . includes ( 'bin/BUILD' ) ) {
203
218
const binBuildFile = printPackageBin ( pkg ) ;
204
219
if ( binBuildFile . length ) {
205
220
writeFileSync (
206
- path . posix . join ( pkg . _dir , 'bin' , 'BUILD.bazel' ) , BUILD_FILE_HEADER + binBuildFile ) ;
221
+ path . posix . join ( pkg . _dir , 'bin' , 'BUILD.bazel' ) , generateBuildFileHeader ( visibility ) + binBuildFile ) ;
207
222
}
208
223
}
209
224
@@ -241,7 +256,7 @@ exports_files(["index.bzl"])
241
256
}
242
257
}
243
258
244
- writeFileSync ( path . posix . join ( pkg . _dir , buildFilePath ) , BUILD_FILE_HEADER + buildFile ) ;
259
+ writeFileSync ( path . posix . join ( pkg . _dir , buildFilePath ) , generateBuildFileHeader ( visibility ) + buildFile ) ;
245
260
}
246
261
247
262
/**
@@ -389,7 +404,7 @@ You can suppress this message by passing "suppress_warning = True" to install_ba
389
404
* Generate build files for a scope.
390
405
*/
391
406
function generateScopeBuildFiles ( scope : string , pkgs : Dep [ ] ) {
392
- const buildFile = BUILD_FILE_HEADER + printScope ( scope , pkgs ) ;
407
+ const buildFile = generateBuildFileHeader ( ) + printScope ( scope , pkgs ) ;
393
408
writeFileSync ( path . posix . join ( scope , 'BUILD.bazel' ) , buildFile ) ;
394
409
}
395
410
@@ -407,6 +422,13 @@ function isDirectory(p: string) {
407
422
return fs . existsSync ( p ) && fs . statSync ( p ) . isDirectory ( ) ;
408
423
}
409
424
425
+ /**
426
+ * Strips the byte order mark from a string if present
427
+ */
428
+ function stripBom ( s : string ) {
429
+ return s . charCodeAt ( 0 ) === 0xFEFF ? s . slice ( 1 ) : s ;
430
+ }
431
+
410
432
/**
411
433
* Returns an array of all the files under a directory as relative
412
434
* paths to the directory.
@@ -468,10 +490,24 @@ function hasRootBuildFile(pkg: Dep, rootPath: string) {
468
490
return false ;
469
491
}
470
492
493
+ /**
494
+ * Returns a set of the root package.json files direct dependencies
495
+ */
496
+ export function getDirectDependencySet ( pkgJsonPath : string ) : Set < string > {
497
+ const pkgJson = JSON . parse (
498
+ stripBom ( fs . readFileSync ( pkgJsonPath , { encoding : 'utf8' } ) )
499
+ ) ;
500
+
501
+ const dependencies : string [ ] = Object . keys ( pkgJson . dependencies || { } ) ;
502
+ const devDependencies : string [ ] = Object . keys ( pkgJson . devDependencies || { } ) ;
503
+
504
+ return new Set ( [ ...dependencies , ...devDependencies ] ) ;
505
+ }
506
+
471
507
/**
472
508
* Finds and returns an array of all packages under a given path.
473
509
*/
474
- function findPackages ( p = 'node_modules' ) {
510
+ function findPackages ( p : string , dependencies : Set < string > ) {
475
511
if ( ! isDirectory ( p ) ) {
476
512
return [ ] ;
477
513
}
@@ -490,13 +526,13 @@ function findPackages(p = 'node_modules') {
490
526
. filter ( f => isDirectory ( f ) ) ;
491
527
492
528
packages . forEach ( f => {
493
- pkgs . push ( parsePackage ( f ) , ...findPackages ( path . posix . join ( f , 'node_modules' ) ) ) ;
529
+ pkgs . push ( parsePackage ( f , dependencies ) , ...findPackages ( path . posix . join ( f , 'node_modules' ) , dependencies ) ) ;
494
530
} ) ;
495
531
496
532
const scopes = listing . filter ( f => f . startsWith ( '@' ) )
497
533
. map ( f => path . posix . join ( p , f ) )
498
534
. filter ( f => isDirectory ( f ) ) ;
499
- scopes . forEach ( f => pkgs . push ( ...findPackages ( f ) ) ) ;
535
+ scopes . forEach ( f => pkgs . push ( ...findPackages ( f , dependencies ) ) ) ;
500
536
501
537
return pkgs ;
502
538
}
@@ -525,10 +561,9 @@ function findScopes() {
525
561
* package json and return it as an object along with
526
562
* some additional internal attributes prefixed with '_'.
527
563
*/
528
- export function parsePackage ( p : string ) : Dep {
564
+ export function parsePackage ( p : string , dependencies : Set < string > = new Set ( ) ) : Dep {
529
565
// Parse the package.json file of this package
530
566
const packageJson = path . posix . join ( p , 'package.json' ) ;
531
- const stripBom = ( s : string ) => s . charCodeAt ( 0 ) === 0xFEFF ? s . slice ( 1 ) : s ;
532
567
const pkg = isFile ( packageJson ) ?
533
568
JSON . parse ( stripBom ( fs . readFileSync ( packageJson , { encoding : 'utf8' } ) ) ) :
534
569
{ version : '0.0.0' } ;
@@ -559,6 +594,10 @@ export function parsePackage(p: string): Dep {
559
594
// which is later filled with the flattened dependency list
560
595
pkg . _dependencies = [ ] ;
561
596
597
+ // set if this is a direct dependency of the root package.json file
598
+ // which is later used to determine the generated rules visibility
599
+ pkg . _directDependency = dependencies . has ( pkg . _moduleName ) ;
600
+
562
601
return pkg ;
563
602
}
564
603
@@ -1131,6 +1170,7 @@ type Dep = {
1131
1170
_dependencies : Dep [ ] ,
1132
1171
_files : string [ ] ,
1133
1172
_runfiles : string [ ] ,
1173
+ _directDependency : boolean ,
1134
1174
[ k : string ] : any
1135
1175
}
1136
1176
0 commit comments