diff --git a/README.md b/README.md index 06497f4809..f42824ffad 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,31 @@ As of Bazel 0.26 this feature is still experimental, so also add this line to th common --experimental_allow_incremental_repository_updates ``` +#### @bazel/hide-bazel-files + +We recommend adding the `@bazel/hide-bazel-files` utility as a postinstall step to any `package.json` files that +are being used by `yarn_install` or `npm_install`. This utility hides Bazel files that may be shipped with npm +packages you are using by renaming them with a `_` prefix. + +Bazel files such as `BUILD` or `BUILD.bazel` in node_modules will cause build failures when using Bazel-managed dependencies. If you see an error such as + +``` +ERROR: /private/var/tmp/_bazel_greg/37b273501bbecefcf5ce4f3afcd7c47a/external/npm/BUILD.bazel:9:1: Label '@npm//:node_modules/rxjs/src/AsyncSubject.ts' crosses boundary of subpackage '@npm//node_modules/rxjs/src' (perhaps you meant to put the colon here: '@npm//node_modules/rxjs/src:AsyncSubject.ts'?) +``` + +then chances are there is an npm package in your dependencies that contains a `BUILD` file. To resolve this, add `@bazel/hide-bazel-files` to your `devDependencies` and `hide-bazel-files` to your `postinstall` script like so: + +``` +"devDependencies": { + "@bazel/hide-bazel-files": "0.0.0-PLACEHOLDER" +}, +"scripts": { + "postinstall": "hide-bazel-files" +} +``` + +Note: The commonly used npm package rxjs contains `BUILD` files from version 5.5.5 to 6.4.0 inclusive. These have now been removed in version 6.5.0. If you are using an rxjs version in that range and that is the only npm package in your dependencies that contains `BUILD` files then you can try upgrading to rxjs 6.4.0 instead of using `hide-bazel-files`. + #### yarn_install vs. npm_install `yarn_install` is the preferred rule for setting up Bazel-managed dependencies for a number of reasons: diff --git a/package.json b/package.json index 84de17c60b..8e70cb06b4 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "devDependencies": { "@bazel/bazel": "0.27.0", "@bazel/buildifier": "^0.25.1", + "@bazel/hide-bazel-files": "file:./packages/hide-bazel-files", "@bazel/ibazel": "0.10.1", "@commitlint/cli": "^8.0.0", "@commitlint/config-conventional": "^8.0.0", @@ -44,6 +45,7 @@ "zone.js": "0.8.29" }, "scripts": { + "postinstall": "hide-bazel-files", "build_packages_all": "./scripts/build_packages_all.sh", "build_packages": "./scripts/build_packages.sh", "build_release": "./scripts/build_release.sh", diff --git a/packages/hide-bazel-files/BUILD.bazel b/packages/hide-bazel-files/BUILD.bazel new file mode 100644 index 0000000000..79ebb90d29 --- /dev/null +++ b/packages/hide-bazel-files/BUILD.bazel @@ -0,0 +1,10 @@ +load("@build_bazel_rules_nodejs//:defs.bzl", "npm_package") + +npm_package( + name = "npm_package", + srcs = [ + "README.md", + "index.js", + "package.json", + ], +) diff --git a/packages/hide-bazel-files/README.md b/packages/hide-bazel-files/README.md new file mode 100644 index 0000000000..b6c6973f29 --- /dev/null +++ b/packages/hide-bazel-files/README.md @@ -0,0 +1,24 @@ +# @bazel/hide-bazel-files + +A tool to hide Bazel files that may be shipped with some npm packages. Packages with these files cause build failures when used with `npm_install` or `yarn_install`. + +This tool renames all `BUILD` and `BUILD.bazel` files under node_modules to `_BUILD` and `_BUILD.bazel` respectively. + +If you see an error such as + +``` +ERROR: /private/var/tmp/_bazel_greg/37b273501bbecefcf5ce4f3afcd7c47a/external/npm/BUILD.bazel:9:1: Label '@npm//:node_modules/rxjs/src/AsyncSubject.ts' crosses boundary of subpackage '@npm//node_modules/rxjs/src' (perhaps you meant to put the colon here: '@npm//node_modules/rxjs/src:AsyncSubject.ts'?) +``` + +then chances are there is an npm package in your dependencies that contains a `BUILD` file. To resolve this, add `@bazel/hide-bazel-files` to your `devDependencies` and `hide-bazel-files` to your `postinstall` script like so: + +``` +"devDependencies": { + "@bazel/hide-bazel-files": "0.0.0-PLACEHOLDER" +}, +"scripts": { + "postinstall": "hide-bazel-files" +} +``` + +Note: The commonly used npm package rxjs contains `BUILD` files from version 5.5.5 to 6.4.0 inclusive. These have now been removed in version 6.5.0. If you are using an rxjs version in that range and that is the only npm package in your dependencies that contains `BUILD` files then you can try upgrading to rxjs 6.4.0 instead of using `hide-bazel-files`. diff --git a/packages/hide-bazel-files/index.js b/packages/hide-bazel-files/index.js new file mode 100644 index 0000000000..a9694e85ef --- /dev/null +++ b/packages/hide-bazel-files/index.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +function findBazelFiles(dir) { + return fs.readdirSync(dir).reduce((files, file) => { + const fullPath = path.posix.join(dir, file); + const isSymbolicLink = fs.lstatSync(fullPath).isSymbolicLink(); + let stat; + try { + stat = fs.statSync(fullPath); + } catch (e) { + if (isSymbolicLink) { + // Filter out broken symbolic links. These cause fs.statSync(fullPath) + // to fail with `ENOENT: no such file or directory ...` + return files; + } + throw e; + } + const isDirectory = stat.isDirectory(); + if (isDirectory && isSymbolicLink) { + // Filter out symbolic links to directories. An issue in yarn versions + // older than 1.12.1 creates symbolic links to folders in the .bin folder + // which leads to Bazel targets that cross package boundaries. + // See https://github.com/bazelbuild/rules_nodejs/issues/428 and + // https://github.com/bazelbuild/rules_nodejs/issues/438. + // This is tested in internal/e2e/fine_grained_symlinks. + return files; + } + if (isDirectory) { + return files.concat(findBazelFiles(fullPath)); + } else { + const fileUc = file.toUpperCase(); + if (fileUc == 'BUILD' || fileUc == 'BUILD.BAZEL') { + return files.concat(fullPath); + } + return files; + } + }, []); +} + +function main() { + // Rename all bazel files found by prefixing them with `_` + for (f of findBazelFiles('node_modules')) { + const d = path.posix.join(path.dirname(f), `_${path.basename(f)}`); + fs.renameSync(f, d); + } + return 0; +} + +module.exports = {main}; + +if (require.main === module) { + process.exitCode = main(); +} diff --git a/packages/hide-bazel-files/package.json b/packages/hide-bazel-files/package.json new file mode 100644 index 0000000000..06e32d8524 --- /dev/null +++ b/packages/hide-bazel-files/package.json @@ -0,0 +1,13 @@ +{ + "name": "@bazel/hide-bazel-files", + "bin": { + "hide-bazel-files": "index.js" + }, + "license": "Apache-2.0", + "version": "0.0.0-PLACEHOLDER", + "keywords": [ + "bazel", + "javascript" + ], + "description": "A tool that hides all Bazel files in node_modules by prefixing them with an underscore" +} diff --git a/scripts/publish_release.sh b/scripts/publish_release.sh index 6fc6066532..5c1af4c0ef 100755 --- a/scripts/publish_release.sh +++ b/scripts/publish_release.sh @@ -29,3 +29,6 @@ for pkg in ${PACKAGES[@]} ; do ( # packages/create is not a nested workspace and has no deps echo_and_run node_modules/.bin/bazel --output_base=$TMP run --workspace_status_command=scripts/current_version.sh //packages/create:npm_package.${NPM_COMMAND} + +# packages/hide-bazel-files is not a nested workspace and has no deps +echo_and_run node_modules/.bin/bazel --output_base=$TMP run --workspace_status_command=scripts/current_version.sh //packages/hide-bazel-files:npm_package.${NPM_COMMAND} diff --git a/yarn.lock b/yarn.lock index e3f33ec924..abb1fdc326 100644 --- a/yarn.lock +++ b/yarn.lock @@ -50,6 +50,9 @@ "@bazel/buildifier-linux_x64" "0.25.1" "@bazel/buildifier-win32_x64" "0.25.1" +"@bazel/hide-bazel-files@file:./packages/hide-bazel-files": + version "0.0.0-PLACEHOLDER" + "@bazel/ibazel@0.10.1": version "0.10.1" resolved "https://registry.yarnpkg.com/@bazel/ibazel/-/ibazel-0.10.1.tgz#cebcc5cf045fd0e7957e2c491b60094fce28667a"