diff --git a/WORKSPACE b/WORKSPACE
index 74c23b7a9b..1773c250c4 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -88,6 +88,12 @@ yarn_install(
yarn_lock = "//:yarn.lock",
)
+# Load esbuild rules for bazel.
+# https://bazelbuild.github.io/rules_nodejs/esbuild.html
+load("@build_bazel_rules_nodejs//toolchains/esbuild:esbuild_repositories.bzl", "esbuild_repositories")
+
+esbuild_repositories(npm_repository = "npm")
+
http_archive(
name = "io_bazel_rules_sass",
sha256 = "ee6d527550d42af182673c3718da98bb9205cabdeb08eacc0e3767fa3f2b051a",
diff --git a/package.json b/package.json
index f786138816..940ee17bf0 100644
--- a/package.json
+++ b/package.json
@@ -32,13 +32,10 @@
"@angular/compiler": "^12.2.0",
"@angular/compiler-cli": "^12.2.0",
"@bazel/concatjs": "^4.6.1",
+ "@bazel/esbuild": "^4.6.2",
"@bazel/ibazel": "^0.15.9",
"@bazel/jasmine": "^4.6.1",
- "@bazel/rollup": "^4.6.1",
- "@bazel/terser": "^4.6.1",
"@bazel/typescript": "^4.6.1",
- "@rollup/plugin-commonjs": "^20.0.0",
- "@rollup/plugin-node-resolve": "^13.0.4",
"@types/d3": "5.7.2",
"@types/jasmine": "^3.8.2",
"@types/lodash": "^4.14.172",
@@ -58,8 +55,6 @@
"prettier": "2.4.1",
"prettier-plugin-organize-imports": "2.3.4",
"requirejs": "^2.3.6",
- "rollup": "^2.56.2",
- "terser": "^5.14.2",
"tslib": "^2.3.0",
"typescript": "4.5.4",
"yarn-deduplicate": "^5.0.0"
diff --git a/tensorboard/defs/BUILD b/tensorboard/defs/BUILD
index f6d8e83a0e..4cfb61ed28 100644
--- a/tensorboard/defs/BUILD
+++ b/tensorboard/defs/BUILD
@@ -42,11 +42,6 @@ tb_proto_library(
],
)
-exports_files([
- "rollup_config.js",
- "terser_config.json",
-])
-
ts_library(
name = "strict_types",
srcs = ["strict_type_check.d.ts"],
diff --git a/tensorboard/defs/defs.bzl b/tensorboard/defs/defs.bzl
index 0e991280c0..2112e72746 100644
--- a/tensorboard/defs/defs.bzl
+++ b/tensorboard/defs/defs.bzl
@@ -13,14 +13,11 @@
# limitations under the License.
"""External-only delegates for various BUILD rules."""
-load("@npm//@bazel/rollup:index.bzl", "rollup_bundle")
+load("@io_bazel_rules_sass//:defs.bzl", "npm_sass_library", "sass_binary", "sass_library")
load("@npm//@bazel/concatjs:index.bzl", "karma_web_test_suite")
+load("@npm//@bazel/esbuild:index.bzl", "esbuild")
load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_library")
-load("@io_bazel_rules_sass//:defs.bzl", "npm_sass_library", "sass_binary", "sass_library")
-load("@npm//@bazel/terser:index.bzl", "terser_minified")
-load("//tensorboard/defs/internal:js.bzl", _tf_dev_js_binary = "tf_dev_js_binary")
-tf_dev_js_binary = _tf_dev_js_binary
def tensorboard_webcomponent_library(**kwargs):
"""Rules referencing this will be deleted from the codebase soon."""
@@ -29,7 +26,6 @@ def tensorboard_webcomponent_library(**kwargs):
def tf_js_binary(
name,
compile,
- deps,
visibility = None,
dev_mode_only = False,
includes_polymer = False,
@@ -39,61 +35,50 @@ def tf_js_binary(
Args:
name: Name of the target.
compile: whether to compile when bundling. Only used internally.
- deps: dependencies of the js_binary.
visibility: visibility of the target.
dev_mode_only: whether the binary is for development. When True, it will
- omit the Terser.
+ omit the minification step.
includes_polymer: whether this binary contains Polymer. Only used
internally.
- **kwargs: keyword arguments to rollup_bundle. Please refer to
- https://bazelbuild.github.io/rules_nodejs/Built-ins.html#rollup_bundle
+ **kwargs: Other keyword arguments to esbuild(). Typically used for
+ entry_point and deps. Please refer to https://esbuild.github.io/api/
for more details.
"""
- # `compile` option is used internally but is not used by rollup_bundle.
- # Discard it.
- internal_rollup_name = name + "_rollup_internal_dbg"
- rollup_bundle(
- name = internal_rollup_name,
- config_file = "//tensorboard/defs:rollup_config.js",
- # Must pass `true` here specifically, else the input file argument to
- # Rollup (appended by `rollup_binary`) is interpreted as a value for
- # the preceding option.
- args = ["--failAfterWarnings", "true", "--silent", "true"],
- deps = deps + [
- "@npm//@rollup/plugin-commonjs",
- "@npm//@rollup/plugin-node-resolve",
- ],
- format = "iife",
- sourcemap = "false",
- visibility = ["//visibility:private"],
- **kwargs
- )
-
- if dev_mode_only:
- internal_result_name = internal_rollup_name
- else:
- internal_result_name = name + "_terser_internal_min"
- terser_minified(
- name = internal_result_name,
- src = internal_rollup_name,
- # Notes about the terser config:
- # compress.passes - this is set to '1' to workaround issue with
- # terser and threejs. In practice it (surprisingly) generates
- # smaller results than when it was previously set to '3'.
- config_file = "//tensorboard/defs:terser_config.json",
- visibility = ["//visibility:private"],
- sourcemap = False,
- )
-
- # For some reason, terser_minified is not visible from other targets. Copy
- # or re-export seems to work okay.
- native.genrule(
+ # esbuild is a fast JavaScript bundler[1] appropriate for both production
+ # and development builds.
+ #
+ # Bazel documents[2] how to use esbuild bundling with ts_project but we use
+ # the not-quite-deprecated ts_library rule instead of ts_project. We've
+ # managed to get esbuild working with ts_library but its long-term support
+ # is unknown.
+ #
+ # [1]: https://esbuild.github.io/
+ # [2]: https://www.npmjs.com/package/@bazel/esbuild
+ esbuild(
name = name,
- srcs = [internal_result_name],
- outs = [name + ".js"],
visibility = visibility,
- cmd = "cat $(SRCS) > $@",
+ # Use "iife" format instead of "esm" because "esm" writes symbols at
+ # the global level and tends to overwrite `window` functions. "iife" is
+ # just a thin wrapper around "esm" (it adds 11 bytes) and doesn't
+ # suffer from the same overwriting problem.
+ format="iife",
+ minify= False if dev_mode_only else True,
+ args = {
+ # Must specify that 'mjs' extensions are preferred, since that is
+ # the extension that is used for es2015/esm code generated by
+ # ts_library.
+ # https://github.com/bazelbuild/rules_nodejs/issues/2691#issuecomment-846429871
+ "resolveExtensions": [".mjs", ".js"],
+ # The reasoning for these particular mainFields values are lost to
+ # history. These come from the old rollup bundler configuration.
+ # We do know that the esbuild default values for mainFields do not
+ # work for us. In particular we ran into problems with
+ # esbuild pulling in "node"-specific versions of some libraries that
+ # are incompatible with browsers.
+ "mainFields": ["browser", "es2015", "module", "jsnext:main", "main"],
+ },
+ **kwargs
)
def tf_ts_config(**kwargs):
diff --git a/tensorboard/defs/internal/js.bzl b/tensorboard/defs/internal/js.bzl
deleted file mode 100644
index 88d1e79855..0000000000
--- a/tensorboard/defs/internal/js.bzl
+++ /dev/null
@@ -1,149 +0,0 @@
-# Copyright 2021 The TensorFlow 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.
-"""JavaScript related TensorBoard build rules."""
-
-load("@bazel_skylib//lib:paths.bzl", "paths")
-load("@build_bazel_rules_nodejs//:providers.bzl", "JSNamedModuleInfo", "NpmPackageInfo", "node_modules_aspect")
-
-def _tf_dev_js_binary_impl(ctx):
- files_depsets = []
-
- bootstrap_and_deps = ctx.attr._ambient_deps + ctx.attr.deps
- for dep in bootstrap_and_deps:
- if JSNamedModuleInfo in dep:
- # Collect UMD modules compiled by tf_ts_library
- files_depsets.append(dep[JSNamedModuleInfo].sources)
- elif NpmPackageInfo not in dep and hasattr(dep, "files"):
- # Collect manually specified files or File from npm dependencies. It omits
- # package.json (i.e., ones in `NpmPackageInfo`).
- files_depsets.append(dep.files)
-
- for target in ctx.attr._anonymous_umd_deps:
- file = target.files.to_list()[0]
- module_name = ctx.attr._anonymous_umd_deps[target]
- named_file = ctx.actions.declare_file(file.path + ".named_umd.js")
-
- # Patch anonymous umd modules to have named in their declarations. For instance,
- # it converts `define(['exports'], ...` to `define('d3', ['exports'], ...`.
- # `define`'s argument behaves differently based on arity. For instance:
- # 1: expects the argument to be a factory function to be invoked. Anonymous and
- # no dependency.
- # 2: expects an array then a function. First arguments define dependencies to be
- # injected into the factory. Anonymous with dependencies.
- # 3: expects string, an array, then, a function. First argument is name of the
- # module. Named module with deps.
- ctx.actions.expand_template(
- template = file,
- output = named_file,
- substitutions = {
- # d3, three, umap-js, and tfjs
- "define([": "define('%s', [" % module_name,
- # Lodash
- "define(function()": "define('%s', function()" % module_name,
- # Zone.js
- "define(factory": "define('%s', factory" % module_name,
- },
- is_executable = False,
- )
- files_depsets.append(depset([named_file]))
-
- files = depset(transitive = files_depsets)
-
- file_list = files.to_list()
-
- concat_command = """
- output="$1" && shift
- entry_point="$1" && shift
- {
- awk 'BEGINFILE { print "// file: " FILENAME } { print }' "$@"
- printf ';require(["%s"]);\n' "${entry_point}"
- } >"${output}"
- """
-
- entry_point_module_name = _get_module_name(ctx, ctx.file.entry_point)
-
- concat_args = (
- [ctx.outputs.js.path, entry_point_module_name] +
- [file.path for file in file_list]
- )
-
- ctx.actions.run_shell(
- mnemonic = "ConcatJs",
- progress_message = "concatenating JavaScript files from dependencies",
- inputs = file_list,
- outputs = [ctx.outputs.js],
- command = concat_command,
- arguments = concat_args,
- )
-
-def _get_module_name(ctx, entry_point_file):
- path_without_ext = paths.replace_extension(entry_point_file.short_path, "")
- return ctx.workspace_name + "/" + path_without_ext
-
-tf_dev_js_binary = rule(
- _tf_dev_js_binary_impl,
- attrs = {
- "deps": attr.label_list(
- aspects = [node_modules_aspect],
- doc = """Targets that produce JavaScript, such as tf_ts_library, are
- dependencies of the application.""",
- mandatory = True,
- ),
- "entry_point": attr.label(
- allow_single_file = [".ts"],
- doc = """A module that should be executed as script gets parsed. Generally
- entry to the application.""",
- mandatory = True,
- ),
- # Due to the nature of Angular and certain libraries, they assume presence of
- # library in the bundle. Dependencies appearing in `_ambient_deps` are loaded
- # before the `deps`.
- "_ambient_deps": attr.label_list(
- default = [
- "@npm//:node_modules/requirejs/require.js",
- ":common_umd_lib",
- "@npm//:node_modules/reflect-metadata/Reflect.js",
- "@npm//:node_modules/@angular/localize/bundles/localize-init.umd.js",
- ],
- allow_files = True,
- ),
- # Libraries like d3 and lodash export UMD compatible bundled in their node_modules
- # but they are using "anonymous module" of requirejs. Anonymous module is where
- # you define a module without a name (e.g., `define(factory) or
- # `define([dep1], factory)`). They are often intended to be loaded via
- # `
+
+