@@ -48,20 +48,26 @@ function log_verbose(...m: any[]) {
4848 if ( ! ! process . env [ 'VERBOSE_LOGS' ] ) console . error ( '[generate_build_file.ts]' , ...m ) ;
4949}
5050
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-
5951const args = process . argv . slice ( 2 ) ;
6052const WORKSPACE = args [ 0 ] ;
6153const 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+ }
6571
6672if ( require . main === module ) {
6773 main ( ) ;
@@ -91,8 +97,11 @@ function writeFileSync(p: string, content: string) {
9197 * Main entrypoint.
9298 */
9399export function main ( ) {
100+ // get a set of all the direct dependencies for visibility
101+ const deps = getDirectDependencySet ( PKG_JSON_FILE_PATH ) ;
102+
94103 // find all packages (including packages in nested node_modules)
95- const pkgs = findPackages ( ) ;
104+ const pkgs = findPackages ( 'node_modules' , deps ) ;
96105
97106 // flatten dependencies
98107 flattenDependencies ( pkgs ) ;
@@ -157,7 +166,7 @@ function generateRootBuildFile(pkgs: Dep[]) {
157166` ;
158167 } ) } ) ;
159168
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")
161170
162171exports_files([
163172${ exportsStarlark } ])
@@ -198,12 +207,18 @@ function generatePackageBuildFiles(pkg: Dep) {
198207 buildFilePath = 'BUILD.bazel'
199208 }
200209
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+
201216 // If the package didn't ship a bin/BUILD file, generate one.
202217 if ( ! pkg . _files . includes ( 'bin/BUILD.bazel' ) && ! pkg . _files . includes ( 'bin/BUILD' ) ) {
203218 const binBuildFile = printPackageBin ( pkg ) ;
204219 if ( binBuildFile . length ) {
205220 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 ) ;
207222 }
208223 }
209224
@@ -241,7 +256,7 @@ exports_files(["index.bzl"])
241256 }
242257 }
243258
244- writeFileSync ( path . posix . join ( pkg . _dir , buildFilePath ) , BUILD_FILE_HEADER + buildFile ) ;
259+ writeFileSync ( path . posix . join ( pkg . _dir , buildFilePath ) , generateBuildFileHeader ( visibility ) + buildFile ) ;
245260}
246261
247262/**
@@ -389,7 +404,7 @@ You can suppress this message by passing "suppress_warning = True" to install_ba
389404 * Generate build files for a scope.
390405 */
391406function generateScopeBuildFiles ( scope : string , pkgs : Dep [ ] ) {
392- const buildFile = BUILD_FILE_HEADER + printScope ( scope , pkgs ) ;
407+ const buildFile = generateBuildFileHeader ( ) + printScope ( scope , pkgs ) ;
393408 writeFileSync ( path . posix . join ( scope , 'BUILD.bazel' ) , buildFile ) ;
394409}
395410
@@ -407,6 +422,13 @@ function isDirectory(p: string) {
407422 return fs . existsSync ( p ) && fs . statSync ( p ) . isDirectory ( ) ;
408423}
409424
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+
410432/**
411433 * Returns an array of all the files under a directory as relative
412434 * paths to the directory.
@@ -468,10 +490,24 @@ function hasRootBuildFile(pkg: Dep, rootPath: string) {
468490 return false ;
469491}
470492
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+
471507/**
472508 * Finds and returns an array of all packages under a given path.
473509 */
474- function findPackages ( p = 'node_modules' ) {
510+ function findPackages ( p : string , dependencies : Set < string > ) {
475511 if ( ! isDirectory ( p ) ) {
476512 return [ ] ;
477513 }
@@ -490,13 +526,13 @@ function findPackages(p = 'node_modules') {
490526 . filter ( f => isDirectory ( f ) ) ;
491527
492528 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 ) ) ;
494530 } ) ;
495531
496532 const scopes = listing . filter ( f => f . startsWith ( '@' ) )
497533 . map ( f => path . posix . join ( p , f ) )
498534 . filter ( f => isDirectory ( f ) ) ;
499- scopes . forEach ( f => pkgs . push ( ...findPackages ( f ) ) ) ;
535+ scopes . forEach ( f => pkgs . push ( ...findPackages ( f , dependencies ) ) ) ;
500536
501537 return pkgs ;
502538}
@@ -525,10 +561,9 @@ function findScopes() {
525561 * package json and return it as an object along with
526562 * some additional internal attributes prefixed with '_'.
527563 */
528- export function parsePackage ( p : string ) : Dep {
564+ export function parsePackage ( p : string , dependencies : Set < string > = new Set ( ) ) : Dep {
529565 // Parse the package.json file of this package
530566 const packageJson = path . posix . join ( p , 'package.json' ) ;
531- const stripBom = ( s : string ) => s . charCodeAt ( 0 ) === 0xFEFF ? s . slice ( 1 ) : s ;
532567 const pkg = isFile ( packageJson ) ?
533568 JSON . parse ( stripBom ( fs . readFileSync ( packageJson , { encoding : 'utf8' } ) ) ) :
534569 { version : '0.0.0' } ;
@@ -559,6 +594,10 @@ export function parsePackage(p: string): Dep {
559594 // which is later filled with the flattened dependency list
560595 pkg . _dependencies = [ ] ;
561596
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+
562601 return pkg ;
563602}
564603
@@ -1131,6 +1170,7 @@ type Dep = {
11311170 _dependencies : Dep [ ] ,
11321171 _files : string [ ] ,
11331172 _runfiles : string [ ] ,
1173+ _directDependency : boolean ,
11341174 [ k : string ] : any
11351175}
11361176
0 commit comments