| # Copyright 2017 The Bazel Authors. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """Install NodeJS & Yarn |
| |
| This is a set of repository rules for setting up hermetic copies of NodeJS and Yarn. |
| See https://docs.bazel.build/versions/master/skylark/repository_rules.html |
| """ |
| |
| load("//internal/common:check_bazel_version.bzl", "check_bazel_version") |
| load("//internal/common:check_version.bzl", "check_version") |
| load("//internal/common:os_name.bzl", "OS_ARCH_NAMES", "is_windows_os", "os_name") |
| load("//internal/npm_install:npm_install.bzl", "yarn_install") |
| load("//third_party/github.com/bazelbuild/bazel-skylib:lib/paths.bzl", "paths") |
| load("//toolchains/node:node_toolchain_configure.bzl", "node_toolchain_configure") |
| load(":node_labels.bzl", "get_yarn_node_repositories_label") |
| |
| # Callers that don't specify a particular version will get these. |
| DEFAULT_NODE_VERSION = "10.16.0" |
| DEFAULT_YARN_VERSION = "1.13.0" |
| |
| # Dictionary mapping NodeJS versions to sets of hosts and their correspoding (filename, strip_prefix, sha256) tuples. |
| NODE_REPOSITORIES = { |
| # 10.10.0 |
| "10.10.0-darwin_amd64": ("node-v10.10.0-darwin-x64.tar.gz", "node-v10.10.0-darwin-x64", "00b7a8426e076e9bf9d12ba2d571312e833fe962c70afafd10ad3682fdeeaa5e"), |
| "10.10.0-linux_amd64": ("node-v10.10.0-linux-x64.tar.xz", "node-v10.10.0-linux-x64", "686d2c7b7698097e67bcd68edc3d6b5d28d81f62436c7cf9e7779d134ec262a9"), |
| "10.10.0-windows_amd64": ("node-v10.10.0-win-x64.zip", "node-v10.10.0-win-x64", "70c46e6451798be9d052b700ce5dadccb75cf917f6bf0d6ed54344c856830cfb"), |
| # 10.13.0 |
| "10.13.0-darwin_amd64": ("node-v10.13.0-darwin-x64.tar.gz", "node-v10.13.0-darwin-x64", "815a5d18516934a3963ace9f0574f7d41f0c0ce9186a19be3d89e039e57598c5"), |
| "10.13.0-linux_amd64": ("node-v10.13.0-linux-x64.tar.xz", "node-v10.13.0-linux-x64", "0dc6dba645550b66f8f00541a428c29da7c3cde32fb7eda2eb626a9db3bbf08d"), |
| "10.13.0-windows_amd64": ("node-v10.13.0-win-x64.zip", "node-v10.13.0-win-x64", "eb09c9e9677f1919ec1ca78623c09b2a718ec5388b72b7662d5c41e5f628a52c"), |
| # 10.16.0 |
| "10.16.0-darwin_amd64": ("node-v10.16.0-darwin-x64.tar.gz", "node-v10.16.0-darwin-x64", "6c009df1b724026d84ae9a838c5b382662e30f6c5563a0995532f2bece39fa9c"), |
| "10.16.0-linux_amd64": ("node-v10.16.0-linux-x64.tar.xz", "node-v10.16.0-linux-x64", "1827f5b99084740234de0c506f4dd2202a696ed60f76059696747c34339b9d48"), |
| "10.16.0-windows_amd64": ("node-v10.16.0-win-x64.zip", "node-v10.16.0-win-x64", "aa22cb357f0fb54ccbc06b19b60e37eefea5d7dd9940912675d3ed988bf9a059"), |
| # 10.3.0 |
| "10.3.0-darwin_amd64": ("node-v10.3.0-darwin-x64.tar.gz", "node-v10.3.0-darwin-x64", "0bb5b7e3fe8cccda2abda958d1eb0408f1518a8b0cb58b75ade5d507cd5d6053"), |
| "10.3.0-linux_amd64": ("node-v10.3.0-linux-x64.tar.xz", "node-v10.3.0-linux-x64", "eb3c3e2585494699716ad3197c8eedf4003d3f110829b30c5a0dc34414c47423"), |
| "10.3.0-windows_amd64": ("node-v10.3.0-win-x64.zip", "node-v10.3.0-win-x64", "65d586afb087406a2800d8e51f664c88b26d510f077b85a3b177a1bb79f73677"), |
| # 10.9.0 |
| "10.9.0-darwin_amd64": ("node-v10.9.0-darwin-x64.tar.gz", "node-v10.9.0-darwin-x64", "3c4fe75dacfcc495a432a7ba2dec9045cff359af2a5d7d0429c84a424ef686fc"), |
| "10.9.0-linux_amd64": ("node-v10.9.0-linux-x64.tar.xz", "node-v10.9.0-linux-x64", "c5acb8b7055ee0b6ac653dc4e458c5db45348cecc564b388f4ed1def84a329ff"), |
| "10.9.0-windows_amd64": ("node-v10.9.0-win-x64.zip", "node-v10.9.0-win-x64", "6a75cdbb69d62ed242d6cbf0238a470bcbf628567ee339d4d098a5efcda2401e"), |
| # 8.11.1 |
| "8.11.1-darwin_amd64": ("node-v8.11.1-darwin-x64.tar.gz", "node-v8.11.1-darwin-x64", "5c7b05899ff56910a2b8180f139d48612f349ac2c5d20f08dbbeffbed9e3a089"), |
| "8.11.1-linux_amd64": ("node-v8.11.1-linux-x64.tar.xz", "node-v8.11.1-linux-x64", "6617e245fa0f7fbe0e373e71d543fea878315324ab31dc64b4eba10e42d04c11"), |
| "8.11.1-windows_amd64": ("node-v8.11.1-win-x64.zip", "node-v8.11.1-win-x64", "7d49b59c2b5d73a14c138e8a215d558a64a5241cd5035d9824f608e7bba097b1"), |
| # 8.12.0 |
| "8.12.0-darwin_amd64": ("node-v8.12.0-darwin-x64.tar.gz", "node-v8.12.0-darwin-x64", "ca131b84dfcf2b6f653a6521d31f7a108ad7d83f4d7e781945b2eca8172064aa"), |
| "8.12.0-linux_amd64": ("node-v8.12.0-linux-x64.tar.xz", "node-v8.12.0-linux-x64", "29a20479cd1e3a03396a4e74a1784ccdd1cf2f96928b56f6ffa4c8dae40c88f2"), |
| "8.12.0-windows_amd64": ("node-v8.12.0-win-x64.zip", "node-v8.12.0-win-x64", "9b22c9b23148b61ea0052826b3ac0255b8a3a542c125272b8f014f15bf11b091"), |
| # 8.9.1 |
| "8.9.1-darwin_amd64": ("node-v8.9.1-darwin-x64.tar.gz", "node-v8.9.1-darwin-x64", "05c992a6621d28d564b92bf3051a5dc0adf83839237c0d4653a8cdb8a1c73b94"), |
| "8.9.1-linux_amd64": ("node-v8.9.1-linux-x64.tar.xz", "node-v8.9.1-linux-x64", "8be82805f7c1ab3e64d4569fb9a90ded2de78dd27cadbb91bad1bf975dae1e2d"), |
| "8.9.1-windows_amd64": ("node-v8.9.1-win-x64.zip", "node-v8.9.1-win-x64", "db89c6e041da359561fbe7da075bb4f9881a0f7d3e98c203e83732cfb283fa4a"), |
| # 9.11.1 |
| "9.11.1-darwin_amd64": ("node-v9.11.1-darwin-x64.tar.gz", "node-v9.11.1-darwin-x64", "7b1fb394aa41a62b477e36df16644bd383cc9084808511f6cd318b835a06aac6"), |
| "9.11.1-linux_amd64": ("node-v9.11.1-linux-x64.tar.xz", "node-v9.11.1-linux-x64", "4d27a95d5c2f1c8ef99118794c9c4903e63963418d3e16ca7576760cff39879b"), |
| "9.11.1-windows_amd64": ("node-v9.11.1-win-x64.zip", "node-v9.11.1-win-x64", "0a3566d57ccb7fed95d18fc6c3bc1552a1b1e4753f9bc6c5d45e04f325e1ee53"), |
| } |
| |
| # Dictionary mapping Yarn versions to their correspoding (filename, strip_prefix, sha256) tuples. |
| YARN_REPOSITORIES = { |
| "1.12.1": ("yarn-v1.12.1.tar.gz", "yarn-v1.12.1", "09bea8f4ec41e9079fa03093d3b2db7ac5c5331852236d63815f8df42b3ba88d"), |
| "1.12.3": ("yarn-v1.12.3.tar.gz", "yarn-v1.12.3", "02cd4b589ec22c4bdbd2bc5ebbfd99c5e99b07242ad68a539cb37896b93a24f2"), |
| "1.13.0": ("yarn-v1.13.0.tar.gz", "yarn-v1.13.0", "125d40ebf621ebb08e3f66a618bd2cc5cd77fa317a312900a1ab4360ed38bf14"), |
| "1.3.2": ("yarn-v1.3.2.tar.gz", "yarn-v1.3.2", "6cfe82e530ef0837212f13e45c1565ba53f5199eec2527b85ecbcd88bf26821d"), |
| "1.5.1": ("yarn-v1.5.1.tar.gz", "yarn-v1.5.1", "cd31657232cf48d57fdbff55f38bfa058d2fb4950450bd34af72dac796af4de1"), |
| "1.6.0": ("yarn-v1.6.0.tar.gz", "yarn-v1.6.0", "a57b2fdb2bfeeb083d45a883bc29af94d5e83a21c25f3fc001c295938e988509"), |
| "1.9.2": ("yarn-v1.9.2.tar.gz", "yarn-v1.9.2", "3ad69cc7f68159a562c676e21998eb21b44138cae7e8fe0749a7d620cf940204"), |
| "1.9.4": ("yarn-v1.9.4.tar.gz", "yarn-v1.9.4", "7667eb715077b4bad8e2a832e7084e0e6f1ba54d7280dc573c8f7031a7fb093e"), |
| } |
| |
| # Urls patterns for downloading node & yarn distributions |
| NODE_URLS = [ |
| "https://mirror.bazel.build/nodejs.org/dist/v{version}/{filename}", |
| "https://nodejs.org/dist/v{version}/{filename}", |
| ] |
| YARN_URLS = [ |
| "https://mirror.bazel.build/github.com/yarnpkg/yarn/releases/download/v{version}/{filename}", |
| "https://github.com/yarnpkg/yarn/releases/download/v{version}/{filename}", |
| ] |
| |
| NODE_DIR = "bin/nodejs" |
| YARN_DIR = "bin/yarnpkg" |
| |
| GET_SCRIPT_DIR = """ |
| # From stackoverflow.com |
| SOURCE="${BASH_SOURCE[0]}" |
| # Resolve $SOURCE until the file is no longer a symlink |
| while [ -h "$SOURCE" ]; do |
| DIR="$(cd -P "$(dirname "$SOURCE" )" >/dev/null && pwd)" |
| SOURCE="$(readlink "$SOURCE")" |
| # if $SOURCE was a relative symlink, we need to resolve it relative to the |
| # path where the symlink file was located. |
| [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" |
| done |
| SCRIPT_DIR="$(cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd)" |
| """ |
| |
| # def _write_node_modules_impl(repository_ctx): |
| # WORKAROUND for https://github.com/bazelbuild/bazel/issues/374#issuecomment-296217940 |
| # Bazel does not allow labels to start with `@`, so when installing eg. the `@types/node` |
| # module from the @types scoped package, you'll get an error. |
| # The workaround is to move the rule up one level, from /node_modules to the project root. |
| # For now, users must instead write their own /BUILD file on setup. |
| |
| # repository_ctx.symlink(project_dir.get_child("node_modules"), "node_modules") |
| # add a BUILD file inside the user's node_modules project folder |
| # repository_ctx.file("installed/BUILD", """ |
| # filegroup(name = "node_modules", srcs = glob(["node_modules/**/*"]), visibility = ["//visibility:public"]) |
| # """) |
| |
| # _write_node_modules = repository_rule( |
| # _write_node_modules_impl, |
| # attrs = { "package_json": attr.label() }, |
| # ) |
| |
| def _download_node(repository_ctx): |
| """Used to download a NodeJS runtime package. |
| |
| Args: |
| repository_ctx: The repository rule context |
| """ |
| if repository_ctx.attr.vendored_node: |
| return |
| if repository_ctx.name == "nodejs": |
| host = os_name(repository_ctx) |
| else: |
| host = repository_ctx.name.split("nodejs_", 1)[1] |
| node_version = repository_ctx.attr.node_version |
| node_repositories = repository_ctx.attr.node_repositories |
| node_urls = repository_ctx.attr.node_urls |
| |
| # Download node & npm |
| node_host_version = "{}-{}".format(node_version, host) |
| if node_host_version in node_repositories: |
| filename, strip_prefix, sha256 = node_repositories[node_host_version] |
| else: |
| fail("Unknown NodeJS host/version {}".format(node_host_version)) |
| |
| repository_ctx.download_and_extract( |
| url = [url.format(version = node_version, filename = filename) for url in node_urls], |
| output = NODE_DIR, |
| stripPrefix = strip_prefix, |
| sha256 = sha256, |
| ) |
| |
| def _download_yarn(repository_ctx): |
| """Used to download a yarn tool package. |
| |
| Args: |
| repository_ctx: The repository rule context |
| """ |
| if repository_ctx.attr.vendored_yarn: |
| return |
| |
| yarn_version = repository_ctx.attr.yarn_version |
| yarn_repositories = repository_ctx.attr.yarn_repositories |
| yarn_urls = repository_ctx.attr.yarn_urls |
| |
| if yarn_version in yarn_repositories: |
| filename, strip_prefix, sha256 = yarn_repositories[yarn_version] |
| else: |
| fail("Unknown Yarn version {}".format(yarn_version)) |
| |
| repository_ctx.download_and_extract( |
| url = [url.format(version = yarn_version, filename = filename) for url in yarn_urls], |
| output = YARN_DIR, |
| stripPrefix = strip_prefix, |
| sha256 = sha256, |
| ) |
| |
| def _prepare_node(repository_ctx): |
| """Sets up BUILD files and shell wrappers for the versions of NodeJS, npm & yarn just set up. |
| |
| Windows and other OSes set up the node runtime with different names and paths, which we hide away via |
| the BUILD file here. |
| In addition, we create a bash script wrapper around NPM that passes a given NPM command to all package.json labels |
| passed into here. |
| Finally, we create a reusable template bash script around NPM that is used by rules like npm_package to access |
| NPM. |
| |
| Args: |
| repository_ctx: The repository rule context |
| """ |
| |
| # TODO: Maybe we want to encode the OS as a specific attribute rather than do it based on naming? |
| is_windows = is_windows_os(repository_ctx) or "_windows_" in repository_ctx.attr.name |
| if repository_ctx.attr.vendored_node: |
| node_exec = "/".join([f for f in [ |
| "../../..", |
| repository_ctx.attr.vendored_node.workspace_root, |
| repository_ctx.attr.vendored_node.package, |
| repository_ctx.attr.vendored_node.name, |
| "bin/node" if not is_windows else "node.exe", |
| ] if f]) |
| node_exec_label = "@%s//%s:%s/%s" % ( |
| repository_ctx.attr.vendored_node.workspace_name, |
| repository_ctx.attr.vendored_node.package, |
| repository_ctx.attr.vendored_node.name, |
| "bin/node" if not is_windows else "node.exe", |
| ) |
| npm_script = "/".join([f for f in [ |
| "../../..", |
| repository_ctx.attr.vendored_node.workspace_root, |
| repository_ctx.attr.vendored_node.package, |
| repository_ctx.attr.vendored_node.name, |
| "lib/node_modules/npm/bin/npm-cli.js" if not is_windows else "node_modules/npm/bin/npm-cli.js", |
| ] if f]) |
| else: |
| node_exec = "{}/bin/node".format(NODE_DIR) if not is_windows else "{}/node.exe".format(NODE_DIR) |
| npm_script = "{}/lib/node_modules/npm/bin/npm-cli.js".format(NODE_DIR) if not is_windows else "{}/node_modules/npm/bin/npm-cli.js".format(NODE_DIR) |
| node_exec_label = node_exec |
| if repository_ctx.attr.vendored_yarn: |
| yarn_script = "/".join([f for f in [ |
| "../../..", |
| repository_ctx.attr.vendored_yarn.workspace_root, |
| repository_ctx.attr.vendored_yarn.package, |
| repository_ctx.attr.vendored_yarn.name, |
| "bin/yarn.js", |
| ] if f]) |
| else: |
| yarn_script = "{}/bin/yarn.js".format(YARN_DIR) |
| node_entry = "bin/node" if not is_windows else "bin/node.cmd" |
| npm_node_repositories_entry = "bin/npm_node_repositories" if not is_windows else "bin/npm_node_repositories.cmd" |
| yarn_node_repositories_entry = "bin/yarn_node_repositories" if not is_windows else "bin/yarn_node_repositories.cmd" |
| |
| node_exec_relative = node_exec if repository_ctx.attr.vendored_node else paths.relativize(node_exec, "bin") |
| npm_script_relative = npm_script if repository_ctx.attr.vendored_node else paths.relativize(npm_script, "bin") |
| yarn_script_relative = yarn_script if repository_ctx.attr.vendored_yarn else paths.relativize(yarn_script, "bin") |
| |
| if not repository_ctx.attr.preserve_symlinks: |
| print("\nWARNING: The preserve_symlinks option is deprecated and will go away in the future.\n") |
| |
| if repository_ctx.attr.preserve_symlinks: |
| # --preserve-symlinks-main flag added in node 10.2.0 |
| # See https://nodejs.org/api/cli.html#cli_preserve_symlinks_main |
| preserve_symlinks_main_support = check_version(repository_ctx.attr.node_version, "10.2.0") |
| if preserve_symlinks_main_support: |
| node_args = "--preserve-symlinks --preserve-symlinks-main" |
| node_repo_args = "\"--node_options=--preserve-symlinks --node_options=--preserve-symlinks-main\"" |
| else: |
| node_args = "--preserve-symlinks" |
| node_repo_args = "--node_options=--preserve-symlinks" |
| else: |
| node_args = "" |
| node_repo_args = "" |
| |
| # The entry points for node for osx/linux and windows |
| if not is_windows: |
| # Sets PATH and runs the application |
| repository_ctx.file("bin/node", content = """#!/usr/bin/env bash |
| # Generated by node_repositories.bzl |
| # Immediately exit if any command fails. |
| set -e |
| {get_script_dir} |
| export PATH="$SCRIPT_DIR":$PATH |
| exec "$SCRIPT_DIR/{node}" {args} "$@" |
| """.format( |
| get_script_dir = GET_SCRIPT_DIR, |
| node = node_exec_relative, |
| args = node_args, |
| )) |
| else: |
| # Sets PATH for node, npm & yarn and run user script |
| repository_ctx.file("bin/node.cmd", content = """ |
| @echo off |
| SET SCRIPT_DIR=%~dp0 |
| SET PATH=%SCRIPT_DIR%;%PATH% |
| CALL "%SCRIPT_DIR%\\{node}" {args} %* |
| """.format(node = node_exec_relative, args = node_args)) |
| |
| # Shell script to set repository arguments for node used by nodejs_binary & nodejs_test launcher |
| repository_ctx.file("bin/node_repo_args.sh", content = """#!/usr/bin/env bash |
| # Immediately exit if any command fails. |
| set -e |
| # Generated by node_repositories.bzl |
| export NODE_REPOSITORY_ARGS={} |
| """.format(node_repo_args), executable = True) |
| |
| # The entry points for npm for osx/linux and windows |
| # Runs npm using appropriate node entry point |
| # --scripts-prepend-node-path is set to false since the correct paths |
| # for the Bazel entry points of node, npm & yarn are set in the node |
| # entry point |
| if not is_windows: |
| # Npm entry point |
| repository_ctx.file( |
| "bin/npm", |
| content = """#!/usr/bin/env bash |
| # Generated by node_repositories.bzl |
| # Immediately exit if any command fails. |
| set -e |
| {get_script_dir} |
| "$SCRIPT_DIR/{node}" "$SCRIPT_DIR/{script}" --scripts-prepend-node-path=false "$@" |
| """.format( |
| get_script_dir = GET_SCRIPT_DIR, |
| node = paths.relativize(node_entry, "bin"), |
| script = npm_script_relative, |
| ), |
| executable = True, |
| ) |
| |
| # Npm entry point for node_repositories |
| repository_ctx.file("bin/npm_node_repositories", content = """#!/usr/bin/env bash |
| # Generated by node_repositories.bzl |
| # Immediately exit if any command fails. |
| set -e |
| # Executes the given npm command over each of the package.json folders provided in node_repositories. |
| """ + GET_SCRIPT_DIR + "".join([ |
| """ |
| echo Running npm "$@" in {root} |
| (cd "{root}"; "$SCRIPT_DIR/{node}" "$SCRIPT_DIR/{script}" --scripts-prepend-node-path=false "$@") |
| """.format( |
| root = repository_ctx.path(package_json).dirname, |
| node = paths.relativize(node_entry, "bin"), |
| script = npm_script_relative, |
| ) |
| for package_json in repository_ctx.attr.package_json |
| ]), executable = True) |
| else: |
| # Npm entry point |
| repository_ctx.file( |
| "bin/npm.cmd", |
| content = """@echo off |
| SET SCRIPT_DIR=%~dp0 |
| "%SCRIPT_DIR%\\{node}" "%SCRIPT_DIR%\\{script}" --scripts-prepend-node-path=false %* |
| """.format( |
| node = paths.relativize(node_entry, "bin"), |
| script = npm_script_relative, |
| ), |
| executable = True, |
| ) |
| |
| # Npm entry point for node_repositories |
| repository_ctx.file("bin/npm_node_repositories.cmd", content = """@echo off |
| """ + "".join([ |
| """ |
| SET SCRIPT_DIR=%~dp0 |
| echo Running npm %* in {root} |
| cd "{root}" |
| call "%SCRIPT_DIR%\\{node}" "%SCRIPT_DIR%\\{script}" --scripts-prepend-node-path=false %* |
| if %errorlevel% neq 0 exit /b %errorlevel% |
| """.format( |
| root = repository_ctx.path(package_json).dirname, |
| node = paths.relativize(node_entry, "bin"), |
| script = npm_script_relative, |
| ) |
| for package_json in repository_ctx.attr.package_json |
| ]), executable = True) |
| |
| # This template file is used by the packager tool and the npm_package rule. |
| # `yarn publish` is not ready for use under Bazel, see https://github.com/yarnpkg/yarn/issues/610 |
| repository_ctx.file("run_npm.sh.template", content = """ |
| "{node}" "{script}" TMPL_args "$@" |
| """.format( |
| node = repository_ctx.path(node_entry), |
| script = repository_ctx.path(npm_script), |
| )) |
| |
| # The entry points for yarn for osx/linux and windows |
| # Runs yarn using appropriate node entry point |
| if not is_windows: |
| # Yarn entry point |
| repository_ctx.file( |
| "bin/yarn", |
| content = """#!/usr/bin/env bash |
| # Generated by node_repositories.bzl |
| # Immediately exit if any command fails. |
| set -e |
| {get_script_dir} |
| "$SCRIPT_DIR/{node}" "$SCRIPT_DIR/{script}" "$@" |
| """.format( |
| get_script_dir = GET_SCRIPT_DIR, |
| node = paths.relativize(node_entry, "bin"), |
| script = yarn_script_relative, |
| ), |
| executable = True, |
| ) |
| |
| # Yarn entry point for node_repositories |
| repository_ctx.file("bin/yarn_node_repositories", content = """#!/usr/bin/env bash |
| # Generated by node_repositories.bzl |
| # Immediately exit if any command fails. |
| set -e |
| # Executes the given yarn command over each of the package.json folders provided in node_repositories. |
| """ + GET_SCRIPT_DIR + "".join([ |
| """ |
| echo Running yarn --cwd "{root}" "$@" |
| "$SCRIPT_DIR/{node}" "$SCRIPT_DIR/{script}" --cwd "{root}" "$@" |
| """.format( |
| root = repository_ctx.path(package_json).dirname, |
| node = paths.relativize(node_entry, "bin"), |
| script = yarn_script_relative, |
| ) |
| for package_json in repository_ctx.attr.package_json |
| ]), executable = True) |
| else: |
| # Yarn entry point |
| repository_ctx.file( |
| "bin/yarn.cmd", |
| content = """@echo off |
| SET SCRIPT_DIR=%~dp0 |
| "%SCRIPT_DIR%\\{node}" "%SCRIPT_DIR%\\{script}" %* |
| """.format( |
| node = paths.relativize(node_entry, "bin"), |
| script = yarn_script_relative, |
| ), |
| executable = True, |
| ) |
| |
| # Yarn entry point for node_repositories |
| repository_ctx.file("bin/yarn_node_repositories.cmd", content = """@echo off |
| SET SCRIPT_DIR=%~dp0 |
| """ + "".join([ |
| """ |
| echo Running yarn --cwd "{root}" %* |
| CALL "%SCRIPT_DIR%\\{node}" "%SCRIPT_DIR%\\{script}" --cwd "{root}" %* |
| if %errorlevel% neq 0 exit /b %errorlevel% |
| """.format( |
| root = repository_ctx.path(package_json).dirname, |
| node = paths.relativize(node_entry, "bin"), |
| script = yarn_script_relative, |
| ) |
| for package_json in repository_ctx.attr.package_json |
| ]), executable = True) |
| |
| # Base BUILD file for this repository |
| repository_ctx.file("BUILD.bazel", content = """# Generated by node_repositories.bzl |
| package(default_visibility = ["//visibility:public"]) |
| exports_files([ |
| "run_npm.sh.template", |
| "bin/node_repo_args.sh",{exported_node_bin} |
| "bin/node{entry_ext}", |
| "bin/npm{entry_ext}", |
| "bin/npm_node_repositories{entry_ext}", |
| "bin/yarn{entry_ext}", |
| "bin/yarn_node_repositories{entry_ext}", |
| ]) |
| alias(name = "node_bin", actual = "{node_bin_actual}") |
| alias(name = "node", actual = "{node_actual}") |
| alias(name = "npm", actual = "{npm_actual}") |
| alias(name = "yarn", actual = "{yarn_actual}") |
| """.format( |
| entry_ext = ".cmd" if is_windows else "", |
| exported_node_bin = "" if repository_ctx.attr.vendored_node else ("\n \"%s\"," % node_exec_label), |
| node_bin_actual = node_exec_label, |
| node_actual = node_entry, |
| npm_actual = npm_node_repositories_entry, |
| yarn_actual = yarn_node_repositories_entry, |
| )) |
| |
| def _nodejs_repo_impl(repository_ctx): |
| _download_node(repository_ctx) |
| _download_yarn(repository_ctx) |
| _prepare_node(repository_ctx) |
| |
| _nodejs_repo = repository_rule( |
| _nodejs_repo_impl, |
| attrs = { |
| "node_repositories": attr.string_list_dict(default = NODE_REPOSITORIES), |
| "node_urls": attr.string_list(default = NODE_URLS), |
| # Options to override node version |
| "node_version": attr.string(default = DEFAULT_NODE_VERSION), |
| "package_json": attr.label_list(), |
| "preserve_symlinks": attr.bool(default = True), |
| "vendored_node": attr.label(allow_single_file = True), |
| "vendored_yarn": attr.label(allow_single_file = True), |
| "yarn_repositories": attr.string_list_dict(default = YARN_REPOSITORIES), |
| "yarn_urls": attr.string_list(default = YARN_URLS), |
| "yarn_version": attr.string(default = DEFAULT_YARN_VERSION), |
| }, |
| ) |
| |
| def _yarn_repo_impl(repository_ctx): |
| # Base build file for this repository - exposes yarn |
| repository_ctx.file("BUILD.bazel", content = """# Generated by node_repositories.bzl |
| package(default_visibility = ["//visibility:public"]) |
| alias(name = "yarn", actual = "{yarn}") |
| """.format(yarn = get_yarn_node_repositories_label(repository_ctx))) |
| |
| _yarn_repo = repository_rule( |
| _yarn_repo_impl, |
| attrs = {"package_json": attr.label_list()}, |
| ) |
| |
| def _nodejs_host_os_alias_impl(repository_ctx): |
| host_os = os_name(repository_ctx) |
| node_repository = "@nodejs_%s" % host_os |
| is_windows_host = is_windows_os(repository_ctx) |
| file_ending = ".cmd" if is_windows_host else "" |
| actual_node_bin = "bin/nodejs/node.exe" if is_windows_host else "bin/nodejs/bin/node" |
| repository_ctx.template( |
| "BUILD.bazel", |
| Label("@build_bazel_rules_nodejs//internal/node:BUILD.nodejs_host_os_alias.tpl"), |
| substitutions = { |
| "TEMPLATE__npm_node_repositories": "%s//:bin/npm_node_repositories%s" % (node_repository, file_ending), |
| "TEMPLATE__yarn_node_repositories": "%s//:bin/yarn_node_repositories%s" % (node_repository, file_ending), |
| "TEMPLATE_actual_node_bin": "%s//:%s" % (node_repository, actual_node_bin), |
| "TEMPLATE_node_repo_args": "%s//:bin/node_repo_args.sh" % node_repository, |
| "TEMPLATE_npm": "%s//:bin/npm%s" % (node_repository, file_ending), |
| "TEMPLATE_run_npm": "%s//:run_npm.sh.template" % node_repository, |
| "TEMPLATE_wrapped_node_bin": "%s//:bin/node%s" % (node_repository, file_ending), |
| "TEMPLATE_yarn": "%s//:bin/yarn%s" % (node_repository, file_ending), |
| }, |
| executable = False, |
| ) |
| |
| _nodejs_repo_host_os_alias = repository_rule( |
| _nodejs_host_os_alias_impl, |
| ) |
| |
| def node_repositories( |
| package_json = [], |
| node_version = DEFAULT_NODE_VERSION, |
| yarn_version = DEFAULT_YARN_VERSION, |
| vendored_node = None, |
| vendored_yarn = None, |
| node_repositories = NODE_REPOSITORIES, |
| yarn_repositories = YARN_REPOSITORIES, |
| node_urls = NODE_URLS, |
| yarn_urls = YARN_URLS, |
| preserve_symlinks = True): |
| """To be run in user's WORKSPACE to install rules_nodejs dependencies. |
| |
| This rule sets up node, npm, and yarn. |
| |
| The versions of these tools can be specified in one of three ways: |
| - Normal Usage: |
| Specify no explicit versions. This will download and use the latest NodeJS & Yarn that were available when the |
| version of rules_nodejs you're using was released. |
| - Forced version(s): |
| You can select the version of NodeJS and/or Yarn to download & use by specifying it when you call node_repositories, |
| but you must use a value that matches a known version. |
| - Using a custom version: |
| You can pass in a custom list of NodeJS and/or Yarn repositories and URLs for node_resositories to use. |
| - Using a local version: |
| To avoid downloads, you can check in vendored copies of NodeJS and/or Yarn and set vendored_node and or vendored_yarn |
| to point to those before calling node_repositories. |
| |
| This rule exposes the `@nodejs` workspace containing some rules the user can call later: |
| |
| - Run node: `bazel run @nodejs//:node path/to/program.js` |
| - Install dependencies using npm: `bazel run @nodejs//:npm install` |
| - Install dependencies using yarn: `bazel run @nodejs//:yarn` |
| |
| This rule also exposes the `@yarn` workspace for backwards compatibility: |
| |
| - Alternately install dependencies using yarn: `bazel run @yarn//:yarn` |
| |
| Note that the dependency installation scripts will run in each subpackage indicated by the `package_json` attribute. |
| |
| This approach uses npm/yarn as the package manager. You could instead have Bazel act as the package manager, running the install behind the scenes. |
| See the `npm_install` and `yarn_install` rules, and the discussion in the README. |
| |
| Example: |
| |
| ``` |
| load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories") |
| node_repositories(package_json = ["//:package.json", "//subpkg:package.json"]) |
| ``` |
| |
| Running `bazel run @nodejs//:yarn` in this repo would create `/node_modules` and `/subpkg/node_modules`. |
| |
| Args: |
| package_json: a list of labels, which indicate the package.json files that will be installed |
| when you manually run the package manager, e.g. with |
| `bazel run @nodejs//:yarn` or `bazel run @nodejs//:npm install`. |
| If you use bazel-managed dependencies, you can omit this attribute. |
| node_version: optional; the specific version of NodeJS to install or, if |
| vendored_node is specified, the vendored version of node. |
| yarn_version: optional; the specific version of Yarn to install. |
| vendored_node: optional; the local path to a pre-installed NodeJS runtime. |
| If set then also set node_version to the version that of node that is vendored. |
| Bazel will automatically turn on features such as --preserve-symlinks-main if they |
| are supported by the node version being used. |
| vendored_yarn: optional; the local path to a pre-installed yarn tool. |
| node_repositories: optional; custom list of node repositories to use. |
| yarn_repositories: optional; custom list of yarn repositories to use. |
| node_urls: optional; custom list of URLs to use to download NodeJS. |
| yarn_urls: optional; custom list of URLs to use to download Yarn. |
| preserve_symlinks: Turn on --node_options=--preserve-symlinks for nodejs_binary and nodejs_test rules. |
| The default for this is currently True but the options is deprecated and will be removed in the future. |
| When this option is turned on, node will preserve the symlinked path for resolves instead of the default |
| behavior of resolving to the real path. This means that all required files must be in be included in your |
| runfiles as it prevents the default behavior of potentially resolving outside of the runfiles. For example, |
| all required files need to be included in your node_modules filegroup. This option is desirable as it gives |
| a stronger guarantee of hermiticity which is required for remote execution. |
| """ |
| |
| # 0.14.0: @bazel_tools//tools/bash/runfiles is required for nodejs |
| # 0.17.1: allow @ in package names is required for fine grained deps |
| # 0.21.0: repository_ctx.report_progress API |
| check_bazel_version( |
| message = """ |
| A minimum Bazel version of 0.21.0 is required to use build_bazel_rules_nodejs. |
| """, |
| minimum_bazel_version = "0.21.0", |
| ) |
| |
| # This needs to be setup so toolchains can access nodejs for all different versions |
| for os_arch_name in OS_ARCH_NAMES: |
| os_name = "_".join(os_arch_name) |
| node_repository_name = "nodejs_%s" % os_name |
| _maybe( |
| _nodejs_repo, |
| name = node_repository_name, |
| package_json = package_json, |
| node_version = node_version, |
| yarn_version = yarn_version, |
| vendored_node = vendored_node, |
| vendored_yarn = vendored_yarn, |
| node_repositories = node_repositories, |
| yarn_repositories = yarn_repositories, |
| node_urls = node_urls, |
| yarn_urls = yarn_urls, |
| preserve_symlinks = preserve_symlinks, |
| ) |
| native.register_toolchains("@build_bazel_rules_nodejs//toolchains/node:node_%s_toolchain" % os_arch_name[0]) |
| node_toolchain_configure( |
| name = "%s_config" % node_repository_name, |
| target_tool = "@%s//:node_bin" % node_repository_name, |
| ) |
| |
| # This "nodejs" repo is just for convinience so one does not have to target @nodejs_<os_name>//... |
| # All it does is create aliases to the @nodejs_<host_os>_<host_arch> repository |
| _maybe( |
| _nodejs_repo_host_os_alias, |
| name = "nodejs", |
| ) |
| |
| _maybe( |
| _yarn_repo, |
| name = "yarn", |
| package_json = package_json, |
| ) |
| |
| _maybe( |
| yarn_install, |
| name = "build_bazel_rules_nodejs_rollup_deps", |
| package_json = "@build_bazel_rules_nodejs//internal/rollup:package.json", |
| yarn_lock = "@build_bazel_rules_nodejs//internal/rollup:yarn.lock", |
| data = ["@build_bazel_rules_nodejs//internal/rollup:postinstall-patches.js"], |
| # Do not symlink node_modules as when used in downstream repos we should not create |
| # node_modules folders in the @build_bazel_rules_nodejs external repository. This is |
| # not supported by managed_directories. |
| symlink_node_modules = False, |
| ) |
| |
| _maybe( |
| yarn_install, |
| name = "history-server_runtime_deps", |
| package_json = "@build_bazel_rules_nodejs//internal/history-server:package.json", |
| yarn_lock = "@build_bazel_rules_nodejs//internal/history-server:yarn.lock", |
| # Do not symlink node_modules as when used in downstream repos we should not create |
| # node_modules folders in the @build_bazel_rules_nodejs external repository. This is |
| # not supported by managed_directories. |
| symlink_node_modules = False, |
| ) |
| |
| _maybe( |
| yarn_install, |
| name = "http-server_runtime_deps", |
| package_json = "@build_bazel_rules_nodejs//internal/http-server:package.json", |
| yarn_lock = "@build_bazel_rules_nodejs//internal/http-server:yarn.lock", |
| # Do not symlink node_modules as when used in downstream repos we should not create |
| # node_modules folders in the @build_bazel_rules_nodejs external repository. This is |
| # not supported by managed_directories. |
| symlink_node_modules = False, |
| ) |
| |
| def _maybe(repo_rule, name, **kwargs): |
| if name not in native.existing_rules(): |
| repo_rule(name = name, **kwargs) |