From 063e714b1d73f0de4c61b02dac3d403a6b6e3bc6 Mon Sep 17 00:00:00 2001 From: Matt Mackay Date: Wed, 2 Jun 2021 06:42:18 -0400 Subject: [PATCH] feat(esbuild): add support for toolchains (#2704) Add toolchain support for esbuild, removing the need for the tool attribute. Users can load the esbuild_repositories macro and call it within their WORKSPACE, this will by default define esbuild binaries for Windows, MacOS and Linux (all amd64). esbuild package now uses toolchains, and the tools attribute has been removed. By default, toolchains for Windows, Mac (amd64, arm64) and Linux (amd64, arm64) are included. See the docs for configure_esbuild_toolchain for defining custom toolchains Add the following to the `WORKSPACE` file (changing `@npm` as required), see the docs for an alternate load method ``` load("@npm//@bazel/esbuild:esbuild_repositories.bzl", "esbuild_repositories") esbuild_repositories() ``` --- WORKSPACE | 4 +- docs/esbuild.md | 111 +++++++++++------- examples/esbuild/WORKSPACE | 36 +----- examples/esbuild/src/BUILD.bazel | 5 - package.json | 2 +- packages/esbuild/BUILD.bazel | 25 ++-- packages/esbuild/esbuild.bzl | 15 +-- packages/esbuild/esbuild_packages.bzl | 70 +++++++++++ packages/esbuild/esbuild_repo.bzl | 42 ------- packages/esbuild/esbuild_repositories.bzl | 30 +++++ packages/esbuild/index.bzl | 5 + packages/esbuild/index.docs.bzl | 68 +++++------ .../esbuild/test/alias-mapping/BUILD.bazel | 2 +- packages/esbuild/test/bundle/BUILD.bazel | 2 +- packages/esbuild/test/css/BUILD.bazel | 2 +- packages/esbuild/test/define/BUILD.bazel | 2 +- packages/esbuild/test/entries/BUILD.bazel | 2 +- .../esbuild/test/external-flag/BUILD.bazel | 2 +- packages/esbuild/test/js-library/BUILD.bazel | 2 +- packages/esbuild/test/node/BUILD.bazel | 2 +- packages/esbuild/test/output/BUILD.bazel | 2 +- packages/esbuild/test/sourcemap/BUILD.bazel | 2 +- packages/esbuild/test/splitting/BUILD.bazel | 2 +- packages/esbuild/test/tests.bzl | 13 -- packages/esbuild/test/typescript/BUILD.bazel | 2 +- .../test/typescript/ts_as_srcs/BUILD.bazel | 2 +- packages/esbuild/test/vanilla_js/BUILD.bazel | 2 +- .../test/workspace-mapping/BUILD.bazel | 2 +- packages/esbuild/toolchain/BUILD.bazel | 29 +++++ packages/esbuild/toolchain/toolchain.bzl | 61 ++++++++++ scripts/update-esbuild-versions.js | 64 +++------- 31 files changed, 351 insertions(+), 259 deletions(-) create mode 100644 packages/esbuild/esbuild_packages.bzl delete mode 100644 packages/esbuild/esbuild_repo.bzl create mode 100644 packages/esbuild/esbuild_repositories.bzl delete mode 100644 packages/esbuild/test/tests.bzl create mode 100644 packages/esbuild/toolchain/BUILD.bazel create mode 100644 packages/esbuild/toolchain/toolchain.bzl diff --git a/WORKSPACE b/WORKSPACE index 4cd59655b9..621b229923 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -122,9 +122,9 @@ browser_repositories( ) # Setup esbuild dependencies -load("//packages/esbuild:esbuild_repo.bzl", "esbuild_dependencies") +load("//packages/esbuild:esbuild_repositories.bzl", "esbuild_repositories") -esbuild_dependencies() +esbuild_repositories() # # Dependencies to run stardoc & generating documentation diff --git a/docs/esbuild.md b/docs/esbuild.md index 9b8fcd762a..41dab1a1fb 100755 --- a/docs/esbuild.md +++ b/docs/esbuild.md @@ -22,64 +22,40 @@ or using yarn yarn add -D @bazel/esbuild ``` -Add an `http_archive` fetching the esbuild binary for each platform that you need to support. +The esbuild binary is fetched from npm automatically and exposed via toolchains. Add the `esbuild_repositories` rule to the `WORKSPACE`: ```python -_ESBUILD_VERSION = "0.12.1" # reminder: update SHAs below when changing this value -http_archive( - name = "esbuild_darwin", - urls = [ - "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-%s.tgz" % _ESBUILD_VERSION, - ], - strip_prefix = "package", - build_file_content = """exports_files(["bin/esbuild"])""", - sha256 = "efb34692bfa34db61139eb8e46cd6cf767a42048f41c8108267279aaf58a948f", -) +load("@npm//@bazel/esbuild:esbuild_repositories.bzl", "esbuild_repositories") -http_archive( - name = "esbuild_windows", - urls = [ - "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-%s.tgz" % _ESBUILD_VERSION, - ], - strip_prefix = "package", - build_file_content = """exports_files(["esbuild.exe"])""", - sha256 = "10439647b11c7fd1d9647fd98d022fe2188b4877d2d0b4acbe857f4e764b17a9", -) +esbuild_repositories() +``` + +As esbuild is being fetched from `npm`, the load statement above can cause eager fetches of the `@npm` external repository. +To work around this, it's possible to fetch the `@bazel/esbuild` package via an `http_archive` +```python http_archive( - name = "esbuild_linux", + name = "bazel_esbuild", urls = [ - "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-%s.tgz" % _ESBUILD_VERSION, + "https://registry.npmjs.org/@bazel/esbuild/-/esbuild-4.0.0.tgz", ], strip_prefix = "package", - build_file_content = """exports_files(["bin/esbuild"])""", - sha256 = "de8409b90ec3c018ffd899b49ed5fc462c61b8c702ea0f9da013e0e1cd71549a", ) -``` -These can then be referenced on the `tool` attribute of the `esbuild` rule. +load("@bazel_esbuild//:esbuild_repositories.bzl", "esbuild_repositories") -```python -esbuild( - name = "bundle", - ... - tool = select({ - "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild", - "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe", - "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild", - }), -) +esbuild_repositories() ``` -It might be useful to wrap this locally in a macro for better reuseability, see `packages/esbuild/test/tests.bzl` for an example. +## Overview The `esbuild` rule can take a JS or TS dependency tree and bundle it to a single file, or split across multiple files, outputting a directory. ```python load("@npm//@bazel/esbuild:index.bzl", "esbuild") -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_project") -ts_library( +ts_project( name = "lib", srcs = ["a.ts"], ) @@ -97,9 +73,9 @@ To create a code split bundle, set `splitting = True` on the `esbuild` rule. ```python load("@npm//@bazel/esbuild:index.bzl", "esbuild") -load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_project") -ts_library( +ts_project( name = "lib", srcs = ["a.ts"], deps = [ @@ -125,7 +101,7 @@ This will create an output directory containing all the code split chunks, along
 esbuild(name, args, define, deps, entry_point, entry_points, external, format, launcher,
         link_workspace_root, max_threads, minify, output, output_css, output_dir, output_map,
-        platform, sourcemap, sources_content, srcs, target, tool)
+        platform, sourcemap, sources_content, srcs, target)
 
Runs the esbuild bundler under Bazel @@ -302,9 +278,56 @@ See https://esbuild.github.io/api/#target for more details Defaults to `"es2015"` -

tool

-(*Label, mandatory*): An executable for the esbuild binary +## configure_esbuild_toolchain + +**USAGE** + +
+configure_esbuild_toolchain(name, binary, exec_compatible_with)
+
+ +Defines a toolchain for esbuild given the binary path and platform constraints + +**PARAMETERS** + +

name

+ +unique name for this toolchain, generally in the form "esbuild_platform_arch" + + + +

binary

+ +label for the esbuild binary + + + +

exec_compatible_with

+ +list of platform constraints + + + + +## esbuild_repositories + +**USAGE** + +
+esbuild_repositories(name)
+
+ +Helper for fetching and setting up the esbuild versions and toolchains + +**PARAMETERS** + + +

name

+ +currently unused + +Defaults to `""` diff --git a/examples/esbuild/WORKSPACE b/examples/esbuild/WORKSPACE index 0614a0fa7f..24fa6b20dd 100644 --- a/examples/esbuild/WORKSPACE +++ b/examples/esbuild/WORKSPACE @@ -25,38 +25,6 @@ http_archive( urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/3.5.1/rules_nodejs-3.5.1.tar.gz"], ) -_ESBUILD_VERSION = "0.12.1" - -http_archive( - name = "esbuild_darwin", - build_file_content = """exports_files(["bin/esbuild"])""", - sha256 = "efb34692bfa34db61139eb8e46cd6cf767a42048f41c8108267279aaf58a948f", - strip_prefix = "package", - urls = [ - "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-%s.tgz" % _ESBUILD_VERSION, - ], -) - -http_archive( - name = "esbuild_windows", - build_file_content = """exports_files(["esbuild.exe"])""", - sha256 = "10439647b11c7fd1d9647fd98d022fe2188b4877d2d0b4acbe857f4e764b17a9", - strip_prefix = "package", - urls = [ - "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-%s.tgz" % _ESBUILD_VERSION, - ], -) - -http_archive( - name = "esbuild_linux", - build_file_content = """exports_files(["bin/esbuild"])""", - sha256 = "de8409b90ec3c018ffd899b49ed5fc462c61b8c702ea0f9da013e0e1cd71549a", - strip_prefix = "package", - urls = [ - "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-%s.tgz" % _ESBUILD_VERSION, - ], -) - load("@build_bazel_rules_nodejs//:index.bzl", "npm_install") npm_install( @@ -64,3 +32,7 @@ npm_install( package_json = "//:package.json", package_lock_json = "//:package-lock.json", ) + +load("@npm//@bazel/esbuild:esbuild_repositories.bzl", "esbuild_repositories") + +esbuild_repositories() diff --git a/examples/esbuild/src/BUILD.bazel b/examples/esbuild/src/BUILD.bazel index ebffce910a..d2de5a24c1 100644 --- a/examples/esbuild/src/BUILD.bazel +++ b/examples/esbuild/src/BUILD.bazel @@ -26,11 +26,6 @@ esbuild( minify = True, # setting node as the platform will default us to CJS platform = "node", - tool = select({ - "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild", - "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild", - "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe", - }), deps = [ ":lib", ], diff --git a/package.json b/package.json index 11526aec72..63e73cab67 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "bazel:update-deleted-packages": "sed -i.bak \"/--deleted_packages/s#=.*#=$(find {examples,e2e}/*/* \\( -name BUILD -or -name BUILD.bazel \\) | xargs -n 1 dirname | paste -sd, -)#\" .bazelrc && rm .bazelrc.bak", "update-codeowners": "./scripts/update_codeowners.sh", "update-nodejs-versions": "node ./scripts/update-nodejs-versions.js > internal/node/node_versions.bzl", - "update-esbuild-versions": "node ./scripts/update-esbuild-versions.js > packages/esbuild/esbuild_repo.bzl", + "update-esbuild-versions": "node ./scripts/update-esbuild-versions.js", "format": "git-clang-format", "format-all": "clang-format --glob='{internal/**/,examples/**/}*.{js,ts}' -i", "version": "conventional-changelog -p angular -i CHANGELOG.md -s && node ./scripts/on-version.js && bazel build //:release && node ./scripts/on-release.js && git stage version.bzl docs/install.md packages/create/index.js README.md CHANGELOG.md e2e/*/WORKSPACE examples/*/WORKSPACE", diff --git a/packages/esbuild/BUILD.bazel b/packages/esbuild/BUILD.bazel index d9ff35e7aa..42feadd7f7 100644 --- a/packages/esbuild/BUILD.bazel +++ b/packages/esbuild/BUILD.bazel @@ -27,8 +27,11 @@ codeowners( bzl_library( name = "bzl", - srcs = glob(["**/*.bzl"]), + srcs = glob(["**/*.bzl"]) + [ + "@bazel_tools//tools:bzl_srcs", + ], deps = [ + "//packages/esbuild/toolchain:bzl", "@bazel_skylib//lib:paths", "@build_bazel_rules_nodejs//:bzl", "@build_bazel_rules_nodejs//internal/common:bzl", @@ -51,20 +54,28 @@ copy_file( out = ":npm_version_check.js", ) -pkg_npm( - name = "npm_package", +filegroup( + name = "srcs", srcs = [ "esbuild.bzl", + "esbuild_packages.bzl", + "esbuild_repositories.bzl", "helpers.bzl", "index.bzl", "launcher.js", "package.json", ], - build_file_content = """exports_files(["launcher.js"]) - """, +) + +pkg_npm( + name = "npm_package", + srcs = [ + ":srcs", + "//packages/esbuild/toolchain:srcs", + ], + build_file_content = """exports_files(["launcher.js"])""", substitutions = dict({ - "@build_bazel_rules_nodejs//packages/esbuild:esbuild.bzl": "//@bazel/esbuild:esbuild.bzl", - "@build_bazel_rules_nodejs//packages/esbuild:launcher.js": "//@bazel/esbuild:launcher.js", + "@build_bazel_rules_nodejs//packages/esbuild": "//@bazel/esbuild", }), deps = [ ":README.md", diff --git a/packages/esbuild/esbuild.bzl b/packages/esbuild/esbuild.bzl index ba9b0cebee..fdddb1dabf 100644 --- a/packages/esbuild/esbuild.bzl +++ b/packages/esbuild/esbuild.bzl @@ -5,6 +5,7 @@ esbuild rule load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") load("@build_bazel_rules_nodejs//:providers.bzl", "ExternalNpmPackageInfo", "JSEcmaScriptModuleInfo", "JSModuleInfo", "node_modules_aspect", "run_node") load("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl", "MODULE_MAPPINGS_ASPECT_RESULTS_NAME", "module_mappings_aspect") +load("@build_bazel_rules_nodejs//packages/esbuild/toolchain:toolchain.bzl", "TOOLCHAIN") load(":helpers.bzl", "desugar_entry_point_names", "filter_files", "generate_path_mapping", "resolve_entry_point", "write_jsconfig_file") def _esbuild_impl(ctx): @@ -135,7 +136,7 @@ def _esbuild_impl(ctx): execution_requirements = {"no-remote-exec": "1"} launcher_args = ctx.actions.args() - launcher_args.add("--esbuild=%s" % ctx.executable.tool.path) + launcher_args.add("--esbuild=%s" % ctx.toolchains[TOOLCHAIN].binary.path) run_node( ctx = ctx, @@ -148,7 +149,7 @@ def _esbuild_impl(ctx): env = env, executable = "launcher", link_workspace_root = ctx.attr.link_workspace_root, - tools = [ctx.executable.tool], + tools = [ctx.toolchains[TOOLCHAIN].binary], ) outputs_depset = depset(outputs) @@ -307,15 +308,11 @@ edge16, node10, esnext). Default es2015. See https://esbuild.github.io/api/#target for more details """, ), - "tool": attr.label( - allow_single_file = True, - mandatory = True, - executable = True, - cfg = "exec", - doc = "An executable for the esbuild binary", - ), }, implementation = _esbuild_impl, + toolchains = [ + str(TOOLCHAIN), + ], doc = """Runs the esbuild bundler under Bazel For further information about esbuild, see https://esbuild.github.io/ diff --git a/packages/esbuild/esbuild_packages.bzl b/packages/esbuild/esbuild_packages.bzl new file mode 100644 index 0000000000..524f4f4c4e --- /dev/null +++ b/packages/esbuild/esbuild_packages.bzl @@ -0,0 +1,70 @@ +"""Info for the esbuild packages used""" + +### These values are updated automaticly via `yarn update-esbuild-versions` +_VERSION = "0.12.5" +_DARWIN_AMD64_SHA = "6037c279c2bd09ae6ff490f18ea95ab514db044df63db47268411dfc48a49b16" +_DARWIN_ARM64_SHA = "bb950e084302652a11c5d38aeae773fa6f46b864e7b89264c9354222aa8d326d" +_LINUX_AMD64_SHA = "ea9bae194f4d18297ccb00139c62326e3da40835bbadbe21848c3e2cded35a3a" +_LINUX_ARM64_SHA = "f70fbe27a5dab5dd6f19f76f60660ad59b8a6883652bb1fd878078cf922915ad" +_WINDOWS_AMD64_SHA = "ce16dc8d565f8b30b538496961ba17c1977ddd400d0f20b7e89482ecd0944d5d" + +ESBUILD_PACKAGES = struct( + version = _VERSION, + platforms = dict({ + "darwin_amd64": struct( + sha = _DARWIN_AMD64_SHA, + urls = [ + "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-%s.tgz" % _VERSION, + ], + binary_path = "bin/esbuild", + exec_compatible_with = [ + "@platforms//os:macos", + "@platforms//cpu:x86_64", + ], + ), + "darwin_arm64": struct( + sha = _DARWIN_ARM64_SHA, + urls = [ + "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-%s.tgz" % _VERSION, + ], + binary_path = "bin/esbuild", + exec_compatible_with = [ + "@platforms//os:macos", + "@platforms//cpu:aarch64", + ], + ), + "linux_amd64": struct( + sha = _LINUX_AMD64_SHA, + urls = [ + "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-%s.tgz" % _VERSION, + ], + binary_path = "bin/esbuild", + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + ), + "linux_arm64": struct( + sha = _LINUX_ARM64_SHA, + urls = [ + "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-%s.tgz" % _VERSION, + ], + binary_path = "bin/esbuild", + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], + ), + "windows_amd64": struct( + sha = _WINDOWS_AMD64_SHA, + urls = [ + "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-%s.tgz" % _VERSION, + ], + binary_path = "esbuild.exe", + exec_compatible_with = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], + ), + }), +) diff --git a/packages/esbuild/esbuild_repo.bzl b/packages/esbuild/esbuild_repo.bzl deleted file mode 100644 index da474a90e8..0000000000 --- a/packages/esbuild/esbuild_repo.bzl +++ /dev/null @@ -1,42 +0,0 @@ -""" Generated code; do not edit -Update by running yarn update-esbuild-versions - -Helper macro for fetching esbuild versions for internal tests and examples in rules_nodejs -""" - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -_VERSION = "0.12.1" - -def esbuild_dependencies(): - """Helper to install required dependencies for the esbuild rules""" - - version = _VERSION - - http_archive( - name = "esbuild_darwin", - urls = [ - "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-%s.tgz" % version, - ], - strip_prefix = "package", - build_file_content = """exports_files(["bin/esbuild"])""", - sha256 = "efb34692bfa34db61139eb8e46cd6cf767a42048f41c8108267279aaf58a948f", - ) - http_archive( - name = "esbuild_windows", - urls = [ - "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-%s.tgz" % version, - ], - strip_prefix = "package", - build_file_content = """exports_files(["esbuild.exe"])""", - sha256 = "10439647b11c7fd1d9647fd98d022fe2188b4877d2d0b4acbe857f4e764b17a9", - ) - http_archive( - name = "esbuild_linux", - urls = [ - "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-%s.tgz" % version, - ], - strip_prefix = "package", - build_file_content = """exports_files(["bin/esbuild"])""", - sha256 = "de8409b90ec3c018ffd899b49ed5fc462c61b8c702ea0f9da013e0e1cd71549a", - ) diff --git a/packages/esbuild/esbuild_repositories.bzl b/packages/esbuild/esbuild_repositories.bzl new file mode 100644 index 0000000000..315c3d2feb --- /dev/null +++ b/packages/esbuild/esbuild_repositories.bzl @@ -0,0 +1,30 @@ +""" +Helper macro for fetching esbuild versions +""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load(":esbuild_packages.bzl", "ESBUILD_PACKAGES") + +def _maybe(repo_rule, name, **kwargs): + if name not in native.existing_rules(): + repo_rule(name = name, **kwargs) + +def esbuild_repositories(name = ""): + """Helper for fetching and setting up the esbuild versions and toolchains + + Args: + name: currently unused + """ + + for name, meta in ESBUILD_PACKAGES.platforms.items(): + _maybe( + http_archive, + name = "esbuild_%s" % name, + urls = meta.urls, + strip_prefix = "package", + build_file_content = """exports_files(["%s"])""" % meta.binary_path, + sha256 = meta.sha, + ) + + toolchain_label = Label("@build_bazel_rules_nodejs//packages/esbuild/toolchain:esbuild_%s_toolchain" % name) + native.register_toolchains("@%s//%s:%s" % (toolchain_label.workspace_name, toolchain_label.package, toolchain_label.name)) diff --git a/packages/esbuild/index.bzl b/packages/esbuild/index.bzl index 8ff188399a..298d89d8c5 100644 --- a/packages/esbuild/index.bzl +++ b/packages/esbuild/index.bzl @@ -19,5 +19,10 @@ load( "@build_bazel_rules_nodejs//packages/esbuild:esbuild.bzl", _esbuild_macro = "esbuild_macro", ) +load( + "@build_bazel_rules_nodejs//packages/esbuild/toolchain:toolchain.bzl", + _configure_esbuild_toolchain = "configure_esbuild_toolchain", +) esbuild = _esbuild_macro +configure_esbuild_toolchain = _configure_esbuild_toolchain diff --git a/packages/esbuild/index.docs.bzl b/packages/esbuild/index.docs.bzl index 7d4cd6a246..0a47606797 100644 --- a/packages/esbuild/index.docs.bzl +++ b/packages/esbuild/index.docs.bzl @@ -16,64 +16,40 @@ or using yarn yarn add -D @bazel/esbuild ``` -Add an `http_archive` fetching the esbuild binary for each platform that you need to support. +The esbuild binary is fetched from npm automatically and exposed via toolchains. Add the `esbuild_repositories` rule to the `WORKSPACE`: ```python -_ESBUILD_VERSION = "0.12.1" # reminder: update SHAs below when changing this value -http_archive( - name = "esbuild_darwin", - urls = [ - "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-%s.tgz" % _ESBUILD_VERSION, - ], - strip_prefix = "package", - build_file_content = \"""exports_files(["bin/esbuild"])\""", - sha256 = "efb34692bfa34db61139eb8e46cd6cf767a42048f41c8108267279aaf58a948f", -) +load("@npm//@bazel/esbuild:esbuild_repositories.bzl", "esbuild_repositories") -http_archive( - name = "esbuild_windows", - urls = [ - "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-%s.tgz" % _ESBUILD_VERSION, - ], - strip_prefix = "package", - build_file_content = \"""exports_files(["esbuild.exe"])\""", - sha256 = "10439647b11c7fd1d9647fd98d022fe2188b4877d2d0b4acbe857f4e764b17a9", -) +esbuild_repositories() +``` +As esbuild is being fetched from `npm`, the load statement above can cause eager fetches of the `@npm` external repository. +To work around this, it's possible to fetch the `@bazel/esbuild` package via an `http_archive` + +```python http_archive( - name = "esbuild_linux", + name = "bazel_esbuild", urls = [ - "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-%s.tgz" % _ESBUILD_VERSION, + "https://registry.npmjs.org/@bazel/esbuild/-/esbuild-4.0.0.tgz", ], strip_prefix = "package", - build_file_content = \"""exports_files(["bin/esbuild"])\""", - sha256 = "de8409b90ec3c018ffd899b49ed5fc462c61b8c702ea0f9da013e0e1cd71549a", ) -``` -These can then be referenced on the `tool` attribute of the `esbuild` rule. +load("@bazel_esbuild//:esbuild_repositories.bzl", "esbuild_repositories") -```python -esbuild( - name = "bundle", - ... - tool = select({ - "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild", - "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe", - "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild", - }), -) +esbuild_repositories() ``` -It might be useful to wrap this locally in a macro for better reuseability, see `packages/esbuild/test/tests.bzl` for an example. +## Overview The `esbuild` rule can take a JS or TS dependency tree and bundle it to a single file, or split across multiple files, outputting a directory. ```python load("//packages/esbuild:index.bzl", "esbuild") -load("//packages/typescript:index.bzl", "ts_library") +load("//packages/typescript:index.bzl", "ts_project") -ts_library( +ts_project( name = "lib", srcs = ["a.ts"], ) @@ -91,9 +67,9 @@ To create a code split bundle, set `splitting = True` on the `esbuild` rule. ```python load("//packages/esbuild:index.bzl", "esbuild") -load("//packages/typescript:index.bzl", "ts_library") +load("//packages/typescript:index.bzl", "ts_project") -ts_library( +ts_project( name = "lib", srcs = ["a.ts"], deps = [ @@ -116,5 +92,15 @@ load( "@build_bazel_rules_nodejs//packages/esbuild:esbuild.bzl", _esbuild = "esbuild", ) +load( + "@build_bazel_rules_nodejs//packages/esbuild:esbuild_repositories.bzl", + _esbuild_repositories = "esbuild_repositories", +) +load( + "@build_bazel_rules_nodejs//packages/esbuild/toolchain:toolchain.bzl", + _configure_esbuild_toolchain = "configure_esbuild_toolchain", +) esbuild = _esbuild +esbuild_repositories = _esbuild_repositories +configure_esbuild_toolchain = _configure_esbuild_toolchain diff --git a/packages/esbuild/test/alias-mapping/BUILD.bazel b/packages/esbuild/test/alias-mapping/BUILD.bazel index ad015e587e..3e3c69edb6 100644 --- a/packages/esbuild/test/alias-mapping/BUILD.bazel +++ b/packages/esbuild/test/alias-mapping/BUILD.bazel @@ -1,5 +1,5 @@ load("//:index.bzl", "generated_file_test") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") esbuild( name = "bundle", diff --git a/packages/esbuild/test/bundle/BUILD.bazel b/packages/esbuild/test/bundle/BUILD.bazel index e491106da4..e7db81d516 100644 --- a/packages/esbuild/test/bundle/BUILD.bazel +++ b/packages/esbuild/test/bundle/BUILD.bazel @@ -1,5 +1,5 @@ load("//:index.bzl", "generated_file_test") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") load("//packages/typescript:index.bzl", "ts_library") ts_library( diff --git a/packages/esbuild/test/css/BUILD.bazel b/packages/esbuild/test/css/BUILD.bazel index 8b9dbdb13e..c612dde96f 100644 --- a/packages/esbuild/test/css/BUILD.bazel +++ b/packages/esbuild/test/css/BUILD.bazel @@ -1,5 +1,5 @@ load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") load("//packages/jasmine:index.bzl", "jasmine_node_test") load("//packages/typescript:index.bzl", "ts_library") diff --git a/packages/esbuild/test/define/BUILD.bazel b/packages/esbuild/test/define/BUILD.bazel index a6dca40447..c6a2a268d2 100644 --- a/packages/esbuild/test/define/BUILD.bazel +++ b/packages/esbuild/test/define/BUILD.bazel @@ -1,4 +1,4 @@ -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") load("//packages/jasmine:index.bzl", "jasmine_node_test") load("//packages/typescript:index.bzl", "ts_library") diff --git a/packages/esbuild/test/entries/BUILD.bazel b/packages/esbuild/test/entries/BUILD.bazel index 962d5ecce3..2063a159ae 100644 --- a/packages/esbuild/test/entries/BUILD.bazel +++ b/packages/esbuild/test/entries/BUILD.bazel @@ -1,5 +1,5 @@ load("//:index.bzl", "nodejs_test") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") load("//packages/typescript:index.bzl", "ts_library") ts_library( diff --git a/packages/esbuild/test/external-flag/BUILD.bazel b/packages/esbuild/test/external-flag/BUILD.bazel index 705c02e540..a29977e8bc 100644 --- a/packages/esbuild/test/external-flag/BUILD.bazel +++ b/packages/esbuild/test/external-flag/BUILD.bazel @@ -1,4 +1,4 @@ -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") load("//packages/jasmine:index.bzl", "jasmine_node_test") load("//packages/typescript:index.bzl", "ts_library") diff --git a/packages/esbuild/test/js-library/BUILD.bazel b/packages/esbuild/test/js-library/BUILD.bazel index 508e65d9da..955096209c 100644 --- a/packages/esbuild/test/js-library/BUILD.bazel +++ b/packages/esbuild/test/js-library/BUILD.bazel @@ -1,5 +1,5 @@ load("//:index.bzl", "generated_file_test", "js_library", "nodejs_binary", "npm_package_bin") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") js_library( name = "lib", diff --git a/packages/esbuild/test/node/BUILD.bazel b/packages/esbuild/test/node/BUILD.bazel index c7c3aced4c..b2a25e7f2a 100644 --- a/packages/esbuild/test/node/BUILD.bazel +++ b/packages/esbuild/test/node/BUILD.bazel @@ -1,5 +1,5 @@ load("//:index.bzl", "generated_file_test", "nodejs_binary", "npm_package_bin") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") esbuild( name = "bundle", diff --git a/packages/esbuild/test/output/BUILD.bazel b/packages/esbuild/test/output/BUILD.bazel index fa004b8b5a..97dbe35b87 100644 --- a/packages/esbuild/test/output/BUILD.bazel +++ b/packages/esbuild/test/output/BUILD.bazel @@ -1,4 +1,4 @@ -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") load("//packages/jasmine:index.bzl", "jasmine_node_test") load("//packages/typescript:index.bzl", "ts_library") diff --git a/packages/esbuild/test/sourcemap/BUILD.bazel b/packages/esbuild/test/sourcemap/BUILD.bazel index 7138fc8f27..7871430f63 100644 --- a/packages/esbuild/test/sourcemap/BUILD.bazel +++ b/packages/esbuild/test/sourcemap/BUILD.bazel @@ -1,4 +1,4 @@ -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") load("//packages/jasmine:index.bzl", "jasmine_node_test") load("//packages/typescript:index.bzl", "ts_library") diff --git a/packages/esbuild/test/splitting/BUILD.bazel b/packages/esbuild/test/splitting/BUILD.bazel index 70a146e551..f33b7a08d6 100644 --- a/packages/esbuild/test/splitting/BUILD.bazel +++ b/packages/esbuild/test/splitting/BUILD.bazel @@ -1,6 +1,6 @@ load("//:index.bzl", "nodejs_test") load("//:tools/defaults.bzl", "pkg_tar") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") load("//packages/typescript:index.bzl", "ts_library") ts_library( diff --git a/packages/esbuild/test/tests.bzl b/packages/esbuild/test/tests.bzl deleted file mode 100644 index aed4aed16d..0000000000 --- a/packages/esbuild/test/tests.bzl +++ /dev/null @@ -1,13 +0,0 @@ -"""Default esbuild binary selection for tests""" - -load("//packages/esbuild:index.bzl", _esbuild = "esbuild") - -def esbuild(**kwargs): - _esbuild( - tool = select({ - "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild", - "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild", - "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe", - }), - **kwargs - ) diff --git a/packages/esbuild/test/typescript/BUILD.bazel b/packages/esbuild/test/typescript/BUILD.bazel index 7ac2d609eb..ce0e04afb1 100644 --- a/packages/esbuild/test/typescript/BUILD.bazel +++ b/packages/esbuild/test/typescript/BUILD.bazel @@ -1,5 +1,5 @@ load("//:index.bzl", "generated_file_test") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") load("//packages/typescript:index.bzl", "ts_library") exports_files(["tsconfig.json"]) diff --git a/packages/esbuild/test/typescript/ts_as_srcs/BUILD.bazel b/packages/esbuild/test/typescript/ts_as_srcs/BUILD.bazel index dd436a379b..a010d0d7de 100644 --- a/packages/esbuild/test/typescript/ts_as_srcs/BUILD.bazel +++ b/packages/esbuild/test/typescript/ts_as_srcs/BUILD.bazel @@ -1,5 +1,5 @@ load("//:index.bzl", "generated_file_test", "nodejs_binary", "npm_package_bin") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") esbuild( name = "lib", diff --git a/packages/esbuild/test/vanilla_js/BUILD.bazel b/packages/esbuild/test/vanilla_js/BUILD.bazel index 664112bfc8..e414ae7c09 100644 --- a/packages/esbuild/test/vanilla_js/BUILD.bazel +++ b/packages/esbuild/test/vanilla_js/BUILD.bazel @@ -1,5 +1,5 @@ load("//:index.bzl", "generated_file_test", "nodejs_binary", "npm_package_bin") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") esbuild( name = "lib", diff --git a/packages/esbuild/test/workspace-mapping/BUILD.bazel b/packages/esbuild/test/workspace-mapping/BUILD.bazel index 7d4f7d5f5d..7d4a92d5fb 100644 --- a/packages/esbuild/test/workspace-mapping/BUILD.bazel +++ b/packages/esbuild/test/workspace-mapping/BUILD.bazel @@ -1,5 +1,5 @@ load("//:index.bzl", "generated_file_test") -load("//packages/esbuild/test:tests.bzl", "esbuild") +load("//packages/esbuild:index.bzl", "esbuild") esbuild( name = "bundle", diff --git a/packages/esbuild/toolchain/BUILD.bazel b/packages/esbuild/toolchain/BUILD.bazel new file mode 100644 index 0000000000..7916c3fdaa --- /dev/null +++ b/packages/esbuild/toolchain/BUILD.bazel @@ -0,0 +1,29 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("@build_bazel_rules_nodejs//packages/esbuild:esbuild_packages.bzl", "ESBUILD_PACKAGES") +load(":toolchain.bzl", "configure_esbuild_toolchains") + +toolchain_type( + name = "toolchain_type", + visibility = ["//visibility:public"], +) + +configure_esbuild_toolchains( + platforms = ESBUILD_PACKAGES.platforms, +) + +bzl_library( + name = "bzl", + srcs = [ + "toolchain.bzl", + ], + visibility = ["//packages/esbuild:__pkg__"], +) + +filegroup( + name = "srcs", + srcs = [ + "BUILD.bazel", + "toolchain.bzl", + ], + visibility = ["//packages/esbuild:__pkg__"], +) diff --git a/packages/esbuild/toolchain/toolchain.bzl b/packages/esbuild/toolchain/toolchain.bzl new file mode 100644 index 0000000000..22c2666b3d --- /dev/null +++ b/packages/esbuild/toolchain/toolchain.bzl @@ -0,0 +1,61 @@ +"""Toolchain and helper definitions for esbuild""" + +def _esbuild_toolchain_impl(ctx): + return [ + platform_common.ToolchainInfo( + binary = ctx.executable.binary, + ), + platform_common.TemplateVariableInfo({ + "ESBUILD_PATH": ctx.executable.binary.path, + }), + ] + +_esbuild_toolchain = rule( + implementation = _esbuild_toolchain_impl, + attrs = { + "binary": attr.label( + allow_single_file = True, + executable = True, + cfg = "exec", + ), + }, +) + +TOOLCHAIN = Label("@build_bazel_rules_nodejs//packages/esbuild/toolchain:toolchain_type") + +def configure_esbuild_toolchain(name, binary, exec_compatible_with): + """Defines a toolchain for esbuild given the binary path and platform constraints + + Args: + name: unique name for this toolchain, generally in the form "esbuild_platform_arch" + binary: label for the esbuild binary + exec_compatible_with: list of platform constraints + """ + + _esbuild_toolchain( + name = name, + binary = binary, + ) + + native.toolchain( + name = "%s_toolchain" % name, + exec_compatible_with = exec_compatible_with, + toolchain = name, + toolchain_type = TOOLCHAIN, + ) + +def configure_esbuild_toolchains(name = "", platforms = {}): + """Configures esbuild toolchains for a list of supported platforms + + Args: + name: unused + platforms: dict of platforms to configure toolchains for + """ + + for name, meta in platforms.items(): + repo = "esbuild_%s" % name + configure_esbuild_toolchain( + name = repo, + binary = "@%s//:%s" % (repo, meta.binary_path), + exec_compatible_with = meta.exec_compatible_with, + ) diff --git a/scripts/update-esbuild-versions.js b/scripts/update-esbuild-versions.js index e440c37fef..4b2436b332 100644 --- a/scripts/update-esbuild-versions.js +++ b/scripts/update-esbuild-versions.js @@ -1,13 +1,15 @@ -const https = require("https"); +const https = require('https'); const { exec } = require('shelljs'); const { mkdirSync, rmdirSync, createWriteStream, readFileSync, writeFileSync } = require('fs'); const { join } = require('path'); const { tmpdir } = require('os'); const PLATFORMS = { - "esbuild_darwin": "esbuild-darwin-64", - "esbuild_windows": "esbuild-windows-64", - "esbuild_linux": "esbuild-linux-64" + "_DARWIN_AMD64": "esbuild-darwin-64", + "_DARWIN_ARM64": "esbuild-darwin-arm64", + "_LINUX_AMD64": "esbuild-linux-64", + "_LINUX_ARM64": "esbuild-linux-arm64", + "_WINDOWS_AMD64": "esbuild-windows-64" } function replaceFileContent(filepath, replacements) { @@ -43,24 +45,15 @@ function downloadFile(url, dest) { return new Promise((resolve, reject) => { const file = createWriteStream(dest); - const request = https.get(url, (response) => { - response.pipe(file); - }); - + https.get(url, (response) => response.pipe(file)); file.on('finish', () => { file.end(); resolve(); - }); + }); }); }; async function main() { - const content = []; - const fileReplacements = []; - - content.push('""" Generated code; do not edit\nUpdate by running yarn update-esbuild-versions\n\nHelper macro for fetching esbuild versions for internal tests and examples in rules_nodejs\n"""\n'); - content.push('load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")\n'); - if(process.argv.length !== 2 && process.argv.length !== 3) { console.log("Expected number of arguments is 0 or 1"); process.exit(1); @@ -73,45 +66,20 @@ async function main() { version = (await fetch('https://registry.npmjs.org/esbuild/latest')).version; } - content.push(`_VERSION = "${version}"\n`); - fileReplacements.push([/_ESBUILD_VERSION = "(.+?)"/, version]); - - content.push('def esbuild_dependencies():'); - content.push(' """Helper to install required dependencies for the esbuild rules"""\n'); - content.push(' version = _VERSION\n'); - const tmpDir = tmpdir(); mkdirSync(tmpDir, {recursive: true}); - for(const platform of Object.keys(PLATFORMS)) { - const downloadUrl = `https://registry.npmjs.org/${PLATFORMS[platform]}/-/${PLATFORMS[platform]}-${version}.tgz`; - - const downloadPath = join(tmpDir, PLATFORMS[platform]); - await downloadFile(downloadUrl, downloadPath); - const shasumOutput = exec(`shasum -a 256 ${downloadPath}`, { silent: true }).stdout; - const shasum = shasumOutput.split(' ')[0]; - - fileReplacements.push([new RegExp(`"${platform}",.+?sha256 = "(.+?)"`, 's'), shasum]); - - content.push(' http_archive('); - content.push(` name = "${platform}",`); - content.push(' urls = ['); - content.push(` "https://registry.npmjs.org/${PLATFORMS[platform]}/-/${PLATFORMS[platform]}-%s.tgz" % version,`); - content.push(' ],'); - content.push(' strip_prefix = "package",'); - content.push(` build_file_content = """exports_files(["${platform === 'esbuild_windows' ? 'esbuild.exe' : 'bin/esbuild'}"])""",`); - content.push(` sha256 = "${shasum}",`); - content.push(' )'); - } - + const replacements = await Promise.all(Object.entries(PLATFORMS).map(async ([_var, platform]) => { + const downloadPath = join(tmpDir, platform); - rmdirSync(tmpDir, {recursive: true}); + await downloadFile(`https://registry.npmjs.org/${platform}/-/${platform}-${version}.tgz`, downloadPath); + const shasum = exec(`shasum -a 256 ${downloadPath}`, {silent: true}).stdout.split(' ')[0]; - console.log(content.join('\n')); + return [new RegExp(`${_var}_SHA = "(.+?)"`, 's'), shasum]; + })); - // replace shasums in some manually edited files - replaceFileContent('examples/esbuild/WORKSPACE', fileReplacements); - replaceFileContent('packages/esbuild/index.docs.bzl', fileReplacements); + replacements.push([/_VERSION = "(.+?)"/, version]); + replaceFileContent('packages/esbuild/esbuild_packages.bzl', replacements); } main(); \ No newline at end of file