diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 0000000000..a1fd437354 --- /dev/null +++ b/.bazelignore @@ -0,0 +1,2 @@ +.bazel-user-root +.bazelisk-home diff --git a/.bazelrc b/.bazelrc index 393e2a552e..c059370e1f 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,5 +1,33 @@ +common --noenable_bzlmod +# Let the OS-specific sections below (`common:linux`, `common:macos`, etc.) +# take effect so one `.bazelrc` can carry the required per-platform C++ flags. +common --enable_platform_specific_config common --experimental_repo_remote_exec # from TensorFlow +# Bazel-generated Python package init files can shadow TensorBoard's real +# compat package init at test runtime; keep this test-only so pip packaging +# still copies the intended package tree. +test --incompatible_default_to_explicit_init_py # Use C++ backing implementations for Python proto parsing and deserialization, # which is much faster (~10x). build --define=use_fast_cpp_protos=true + +# Protobuf 6.31.1 requires C++17 in this build. Keep the host and target +# language level aligned across supported platforms. +common:linux --cxxopt=-std=c++17 +common:linux --host_cxxopt=-std=c++17 +common:macos --cxxopt=-std=c++17 +common:macos --host_cxxopt=-std=c++17 +common:windows --cxxopt=/std:c++17 +common:windows --host_cxxopt=/std:c++17 + +# Local shells and virtualenvs can leak Python import state into Bazel tests, +# which then import from the wrong environment instead of the test runfiles. +test --test_env=PYTHONPATH= +test --test_env=PYTHONHOME= +test --test_env=PYTHONSTARTUP= +test --test_env=PYTHONSAFEPATH= +test --test_env=PYTHONNOUSERSITE=1 +test --test_env=PYTHONUSERBASE= +test --test_env=BUILD_WORKSPACE_DIRECTORY= +test --test_env=BUILD_WORKING_DIRECTORY= diff --git a/.bazelversion b/.bazelversion index f22d756da3..1985849fb5 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -6.5.0 +7.7.0 diff --git a/.gitattributes b/.gitattributes index 752a9642c0..75e06739b0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,10 @@ +* text=auto +*.bzl text eol=lf +*.sh text eol=lf +.bazel* text eol=lf +BUILD text eol=lf +BUILD.bazel text eol=lf +WORKSPACE text eol=lf +MODULE.bazel text eol=lf + third_party/rust/** -diff -merge linguist-generated=true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e09c955a8d..634d9675f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,8 +25,8 @@ permissions: env: # Keep this Bazel version in sync with the `versions.check` directive # in our WORKSPACE file. - BAZEL_VERSION: '6.5.0' - BAZEL_SHA256SUM: 'a40ac69263440761199fcb8da47ad4e3f328cbe79ffbf4ecc14e5ba252857307' + BAZEL_VERSION: '7.7.0' + BAZEL_SHA256SUM: 'fe7e799cbc9140f986b063e06800a3d4c790525075c877d00a7112669824acbf' BUILDTOOLS_VERSION: '3.0.0' BUILDIFIER_SHA256SUM: 'e92a6793c7134c5431c58fbc34700664f101e5c9b1c1fcd93b97978e8b7f88db' BUILDOZER_SHA256SUM: '3d58a0b6972e4535718cdd6c12778170ea7382de7c75bc3728f5719437ffb84d' @@ -48,13 +48,40 @@ jobs: fail-fast: false matrix: tf_version_id: ['tf', 'notf'] - python_version: ['3.9'] + python_version: ['3.10'] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - uses: actions/setup-python@13ae5bb136fac2878aff31522b9efb785519f984 # v4.3.0 - with: - python-version: ${{ matrix.python_version }} - architecture: 'x64' + # Use the container's system Python installation path instead of + # actions/setup-python so Bazel's @system_python repo sees a standard + # interpreter/header layout. + - name: 'Set up Python and system dependencies' + run: | + py_abi="python${{ matrix.python_version }}" + sudo apt-get update + sudo apt-get install -y \ + "${py_abi}" \ + "${py_abi}-dev" \ + "${py_abi}-venv" \ + libgbm-dev \ + libxss1 \ + libasound2 + "/usr/bin/${py_abi}" -m venv /tmp/tb-build-venv + echo "/tmp/tb-build-venv/bin" >> "${GITHUB_PATH}" + echo "VIRTUAL_ENV=/tmp/tb-build-venv" >> "${GITHUB_ENV}" + /tmp/tb-build-venv/bin/python -m pip install -U pip setuptools wheel virtualenv + - name: 'Check Python toolchain' + run: | + python --version + python - <<'PY' + import pathlib + import sys + import sysconfig + + print("sys.executable =", sys.executable) + include_dir = pathlib.Path(sysconfig.get_config_var("INCLUDEPY")) + print("INCLUDEPY =", include_dir) + print("Python.h exists =", (include_dir / "Python.h").exists()) + PY - name: 'Set up Bazel' run: | ci/download_bazel.sh "${BAZEL_VERSION}" "${BAZEL_SHA256SUM}" ~/bazel @@ -65,6 +92,11 @@ jobs: run: | python -m pip install -U pip pip install "${TENSORFLOW_VERSION}" + # tf-nightly currently pulls in tb-nightly as a dependency. Bazel's + # source-tree tests must import TensorBoard from runfiles rather than + # from site-packages, otherwise tests that rely on local-only modules + # like `tensorboard.test` resolve against the installed wheel and fail. + pip uninstall -y tensorboard tb-nightly || true if: matrix.tf_version_id != 'notf' - name: 'Install Python dependencies' run: | @@ -73,10 +105,6 @@ jobs: -r ./tensorboard/pip_package/requirements.txt \ -r ./tensorboard/pip_package/requirements_dev.txt \ ; - - name: 'Install Chrome dependencies' - run: | - sudo apt-get update - sudo apt-get install -y libgbm-dev libxss1 libasound2 - name: 'Check Pip state' run: pip freeze --all - name: 'Bazel: fetch' @@ -98,13 +126,21 @@ jobs: if: matrix.tf_version_id == 'notf' - name: 'Bazel: run Pip package test (with TensorFlow support)' run: | - bazel run //tensorboard/pip_package:test_pip_package -- \ + # `bazel run` has been flaky under this self-hosted container runner + # even when the smoke test itself completes successfully. Build the + # launcher target, invoke the generated binary directly, then shut + # down the Bazel server so the step exits cleanly. + bazel build //tensorboard/pip_package:test_pip_package + ./bazel-bin/tensorboard/pip_package/test_pip_package \ --tf-version "${TENSORFLOW_VERSION}" + bazel shutdown if: matrix.tf_version_id != 'notf' - name: 'Bazel: run Pip package test (non-TensorFlow only)' run: | - bazel run //tensorboard/pip_package:test_pip_package -- \ + bazel build //tensorboard/pip_package:test_pip_package + ./bazel-bin/tensorboard/pip_package/test_pip_package \ --tf-version notf + bazel shutdown if: matrix.tf_version_id == 'notf' - name: 'Bazel: run manual tests' run: | @@ -310,7 +346,8 @@ jobs: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: - node-version: 16 + # Angular v17 supports node.js versions: v18.13.0 and newer + node-version: 18 - run: yarn install --ignore-engines # You can run `yarn fix-lint` to fix all Prettier complaints, although at this point this will try to fix too many things. # To fix only the files changed in this PR, see the command below. @@ -321,7 +358,7 @@ jobs: # Make sure no one depends on Angular material and CDK directly. Please # import the indirection in //tensorboard/webapp/angular. - run: | - ! git grep -E '"@npm//@angular/material"|"@npm//@angular/cdk"' 'tensorboard/*/BUILD' ':!tensorboard/webapp/BUILD' ':!tensorboard/webapp/angular/BUILD' + ! git grep -E '"@npm//@angular/material"|"@npm//@angular/cdk"' 'tensorboard/*/BUILD' ':!tensorboard/webapp/angular/BUILD' ':!tensorboard/webapp/angular_components/BUILD' # Cannot directly depend on d3 in webapp. Must depend on # `//tensorboard/webapp/third_party:d3` instead. - run: | diff --git a/WORKSPACE b/WORKSPACE index 67e90a0915..be04cfe68a 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,6 +1,9 @@ workspace(name = "org_tensorflow_tensorboard") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:java.bzl", "java_import_external") +load("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository") +load("//third_party:repo.bzl", "tb_http_archive", "tb_mirror_urls") http_archive( name = "bazel_skylib", @@ -18,10 +21,10 @@ versions.check( # Preemptively assume the next Bazel major version will break us, since historically they do, # and provide a clean error message in that case. Since the maximum version is inclusive rather # than exclusive, we set it to the 999th patch release of the current major version. - maximum_bazel_version = "6.999.0", + maximum_bazel_version = "7.999.0", # Keep this version in sync with: # * The BAZEL environment variable defined in .github/workflows/ci.yml, which is used for CI and nightly builds. - minimum_bazel_version = "6.5.0", + minimum_bazel_version = "7.7.0", ) http_archive( @@ -35,6 +38,34 @@ load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories" web_test_repositories(omit_bazel_skylib = True) +http_archive( + name = "io_bazel_rules_go", + sha256 = "278b7ff5a826f3dc10f04feaf0b70d48b68748ccd512d7f98bf442077f043fe3", + urls = [ + "http://mirror.tensorflow.org/github.com/bazelbuild/rules_go/releases/download/v0.41.0/rules_go-v0.41.0.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.41.0/rules_go-v0.41.0.zip", + ], +) + +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") + +go_rules_dependencies() + +go_register_toolchains(version = "1.20.5") + +http_archive( + name = "bazel_gazelle", + sha256 = "29218f8e0cebe583643cbf93cae6f971be8a2484cdcfa1e45057658df8d54002", + urls = [ + "http://mirror.tensorflow.org/github.com/bazelbuild/bazel-gazelle/releases/download/v0.32.0/bazel-gazelle-v0.32.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.32.0/bazel-gazelle-v0.32.0.tar.gz", + ], +) + +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") + +gazelle_dependencies() + # rules_python has to be placed before load("@io_bazel_rules_closure//closure:repositories.bzl") # in the dependencies list, otherwise we get "cannot load '@rules_python//python:py_xxx.bzl': no such file" http_archive( @@ -53,6 +84,8 @@ py_repositories() http_archive( name = "io_bazel_rules_closure", + patch_args = ["-p1"], + patches = ["//patches:rules_closure_soy_cli.patch"], sha256 = "ae060075a7c468eee42e6a08ddbb83f5a6663bdfdbd461261a465f4a3ae8598c", strip_prefix = "rules_closure-7f3d3351a8cc31fbaa403de7d35578683c17b447", urls = [ @@ -60,20 +93,112 @@ http_archive( ], ) +# rules_closure's Soy toolchain still expects safe-html-types classes that are +# compatible with protobuf-java 6.x. We use a local repository here because +# TensorBoard needs a protobuf-6-compatible adjusted copy of those classes, +# not just any upstream safe-html-types release. +local_repository( + name = "com_google_common_html_types", + path = "third_party/safe_html_types", +) + +java_import_external( + name = "com_google_flogger_flogger", + jar_sha256 = "b5ecd1483e041197012786f749968a62063c1964d3ecfbf96ba92a95797bb8f5", + jar_urls = [ + "http://mirror.tensorflow.org/repo1.maven.org/maven2/com/google/flogger/flogger/0.5.1/flogger-0.5.1.jar", + "https://repo1.maven.org/maven2/com/google/flogger/flogger/0.5.1/flogger-0.5.1.jar", + ], + licenses = ["notice"], +) + +java_import_external( + name = "com_google_flogger_google_extensions", + jar_sha256 = "8b0862cad85b9549f355fe383c6c63816d2f19529634e033ae06d0107ab110b9", + jar_urls = [ + "http://mirror.tensorflow.org/repo1.maven.org/maven2/com/google/flogger/google-extensions/0.5.1/google-extensions-0.5.1.jar", + "https://repo1.maven.org/maven2/com/google/flogger/google-extensions/0.5.1/google-extensions-0.5.1.jar", + ], + licenses = ["notice"], + deps = ["@com_google_flogger_flogger"], +) + +java_import_external( + name = "com_google_flogger_flogger_system_backend", + jar_sha256 = "685de33b53eb313049bbeee7f4b7a80dd09e8e754e96b048a3edab2cebb36442", + jar_urls = [ + "http://mirror.tensorflow.org/repo1.maven.org/maven2/com/google/flogger/flogger-system-backend/0.5.1/flogger-system-backend-0.5.1.jar", + "https://repo1.maven.org/maven2/com/google/flogger/flogger-system-backend/0.5.1/flogger-system-backend-0.5.1.jar", + ], + licenses = ["notice"], + deps = ["@com_google_flogger_flogger"], +) + +# rules_closure's Java/Soy path still needs an explicit Soy compiler jar and +# its runtime deps. We wire those here so Closure/Soy keeps working while using +# our protobuf-6-compatible safe-html-types and protobuf-java dependencies. +java_import_external( + name = "com_google_template_soy", + extra_build_file_content = "\n".join([ + ("java_binary(\n" + + " name = \"%s\",\n" + + " main_class = \"com.google.template.soy.%s\",\n" + + " output_licenses = [\"unencumbered\"],\n" + + " runtime_deps = [\":com_google_template_soy\"],\n" + + ")\n") % (name, name) + for name in ( + "SoyParseInfoGenerator", + "SoyToJbcSrcCompiler", + "SoyToJsSrcCompiler", + "SoyToPySrcCompiler", + ) + ]), + jar_sha256 = "643440022e247ef8ad25bacb83ba099ccd2ae4b1fd078d9e9e3d3dd4af00411f", + jar_urls = [ + "https://repo1.maven.org/maven2/com/google/template/soy/2022-03-07/soy-2022-03-07.jar", + ], + licenses = ["notice"], + deps = [ + "@args4j", + "@com_google_code_findbugs_jsr305", + "@com_google_code_gson", + "@com_google_common_html_types", + "@com_google_flogger_flogger", + "@com_google_flogger_flogger_system_backend", + "@com_google_flogger_google_extensions", + "@com_google_guava", + "@com_google_inject_extensions_guice_assistedinject", + "@com_google_inject_extensions_guice_multibindings", + "@com_google_inject_guice", + "@com_google_protobuf//:protobuf_java", + "@com_ibm_icu_icu4j", + "@javax_inject", + "@org_json", + "@org_ow2_asm", + "@org_ow2_asm_analysis", + "@org_ow2_asm_commons", + "@org_ow2_asm_util", + ], +) + load("@io_bazel_rules_closure//closure:repositories.bzl", "rules_closure_dependencies") +# Omit the Closure-provided copies of these repositories so that rules_closure +# uses the adjusted/local versions declared above instead of re-introducing +# older transitive Java deps that conflict with protobuf 6.x in this setup. rules_closure_dependencies( omit_bazel_skylib = True, + omit_com_google_common_html_types = True, omit_com_google_protobuf = True, omit_com_google_protobuf_js = True, + omit_com_google_template_soy = True, ) http_archive( name = "build_bazel_rules_nodejs", - sha256 = "c29944ba9b0b430aadcaf3bf2570fece6fc5ebfb76df145c6cdad40d65c20811", + sha256 = "f02557f31d4110595ca6e93660018bcd7fdfdbe7d0086089308f1b3af3a7a7ee", urls = [ - "http://mirror.tensorflow.org/github.com/bazelbuild/rules_nodejs/releases/download/5.7.0/rules_nodejs-5.7.0.tar.gz", - "https://github.com/bazelbuild/rules_nodejs/releases/download/5.7.0/rules_nodejs-5.7.0.tar.gz", + "https://github.com/bazelbuild/rules_nodejs/releases/download/5.8.1/rules_nodejs-5.8.1.tar.gz", ], ) @@ -81,20 +206,40 @@ load("@build_bazel_rules_nodejs//:repositories.bzl", "build_bazel_rules_nodejs_d build_bazel_rules_nodejs_dependencies() -load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install") +load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "yarn_install") + +# Angular 17 needs Node.js 18.17 or higher. rules_nodejs 5.8.1 does not +# include Node 18, so we add it here manually. +# @TODO(@cdavalos7): We plan to upgrade to a newer version of rules_nodejs that includes Node 18 and remove this manual addition in next version upgrade. +node_repositories( + node_repositories = { + "18.20.8-darwin_arm64": ("node-v18.20.8-darwin-arm64.tar.gz", "node-v18.20.8-darwin-arm64", "bae4965d29d29bd32f96364eefbe3bca576a03e917ddbb70b9330d75f2cacd76"), + "18.20.8-darwin_amd64": ("node-v18.20.8-darwin-x64.tar.gz", "node-v18.20.8-darwin-x64", "ed2554677188f4afc0d050ecd8bd56effb2572d6518f8da6d40321ede6698509"), + "18.20.8-linux_arm64": ("node-v18.20.8-linux-arm64.tar.xz", "node-v18.20.8-linux-arm64", "224e569dbe7b0ea4628ce383d9d482494b57ee040566583f1c54072c86d1116b"), + "18.20.8-linux_amd64": ("node-v18.20.8-linux-x64.tar.xz", "node-v18.20.8-linux-x64", "5467ee62d6af1411d46b6a10e3fb5cacc92734dbcef465fea14e7b90993001c9"), + "18.20.8-windows_amd64": ("node-v18.20.8-win-x64.zip", "node-v18.20.8-win-x64", "1a1e40260a6facba83636e4cd0ba01eb5bd1386896824b36645afba44857384a"), + }, + node_version = "18.20.8", +) yarn_install( name = "npm", - data = [ - "//patches:@angular+build-tooling+0.0.0-7d103b83a07f132629592fc9918ce17d42a5e382.patch", - "//patches:@bazel+concatjs+5.7.0.patch", - ], # "Some rules only work by referencing labels nested inside npm packages # and therefore require turning off exports_directories_only." # This includes "ts_library". # See: https://github.com/bazelbuild/rules_nodejs/wiki/Migrating-to-5.0#exports_directories_only exports_directories_only = False, package_json = "//:package.json", + package_json_remove = ["scripts.postinstall"], + patch_args = ["-p1"], + # We still author these npm patches with patch-package, but applying them + # by invoking patch-package inside the repository rule proved unreliable in + # this Bazel/CI setup. Apply the generated patch artifacts directly during + # yarn_install instead. + post_install_patches = [ + "//patches:@angular+build-tooling+0.0.0-2113cd7f66a089ac0208ea84eee672b2529f4f6c.patch", + "//patches:@bazel+concatjs+5.8.1.patch", + ], yarn_lock = "//:yarn.lock", ) @@ -107,15 +252,15 @@ esbuild_repositories(npm_repository = "npm") # rules_sass release information is difficult to find but it does seem to # regularly release with same cadence and version as core sass. # We typically upgrade this library whenever we upgrade rules_nodejs. +# This rules_sass version is the compatible with rules_nodejs 5.8.1 # -# rules_sass 1.55.0: https://github.com/bazelbuild/rules_sass/tree/1.55.0 +# rules_sass: https://github.com/bazelbuild/rules_sass/releases/tag/1.69.5 http_archive( name = "io_bazel_rules_sass", - sha256 = "1ea0103fa6adcb7d43ff26373b5082efe1d4b2e09c4f34f8a8f8b351e9a8a9b0", - strip_prefix = "rules_sass-1.55.0", + sha256 = "4285781b24dfd07cb01fcc2324faec87818d0f2174b02e0ed9038f6f809de80a", + strip_prefix = "rules_sass-1.69.5", urls = [ - "http://mirror.tensorflow.org/github.com/bazelbuild/rules_sass/archive/1.55.0.zip", - "https://github.com/bazelbuild/rules_sass/archive/1.55.0.zip", + "https://github.com/bazelbuild/rules_sass/archive/refs/tags/1.69.5.tar.gz", ], ) @@ -133,65 +278,117 @@ sass_repositories() # high as the version of protobuf we depend on below, and we cannot increase the # version below without bumping the requirements.txt version. # -# TODO(#6185): Remove the TODO below once the TF constraint no longer applies. -# -# NOTE: This dependency currently cannot be advanced past 3.19.x. This is because -# TF is currently unable to use a runtime any greater than 3.19.x, see details here: -# https://github.com/tensorflow/tensorflow/blob/9d22f4a0a9499c8e10a4312503e63e0da35ccd94/tensorflow/tools/pip_package/setup.py#L100-L107 -# -# As a result of TF's constraint and the above <= requirement, 3.19.x is the most recent -# possible protoc we can use while remaining cross-compatible with TF. At the same time, -# 3.19.x is the minimum possible protoc that will generate compiled proto code that *is* -# compatible with protobuf runtimes >= 4, as discussed here: -# https://developers.google.com/protocol-buffers/docs/news/2022-05-06 +# TensorFlow 2.21 uses protobuf 6.31.1 in its own build and pip package +# constraints. Keep this Bazel-side protoc version aligned with that runtime +# floor so generated Python code and the ambient `protobuf` package remain +# compatible. +tb_http_archive( + name = "tb_rules_cc", + patch_file = ["//patches:rules_cc_protobuf.patch"], + repo_mapping = { + "@rules_cc": "@tb_rules_cc", + }, + sha256 = "4b12149a041ddfb8306a8fd0e904e39d673552ce82e4296e96fac9cbf0780e59", + strip_prefix = "rules_cc-0.1.0", + urls = tb_mirror_urls("https://github.com/bazelbuild/rules_cc/archive/refs/tags/0.1.0.tar.gz"), +) + http_archive( + name = "tb_rules_java", + sha256 = "b2519fabcd360529071ade8732f208b3755489ed7668b118f8f90985c0e51324", + strip_prefix = "rules_java-8.6.1", + urls = tb_mirror_urls("https://github.com/bazelbuild/rules_java/archive/refs/tags/8.6.1.tar.gz"), +) + +local_repository( + name = "compatibility_proxy", + path = "third_party/compatibility_proxy", +) + +# Protobuf 6.31.1 still expects a pip-style requirements helper repo from its +# WORKSPACE macros. TensorBoard only needs the narrow `install_deps()` / +# `requirement()` surface that protobuf actually loads in this configuration. +local_repository( + name = "protobuf_pip_deps", + path = "third_party/protobuf_pip_deps", +) + +# Protobuf's python/dist BUILD also references +# `external/protobuf_pip_deps_setuptools/site-packages`, so keep this separate +# repo shape even though it only vendors an empty placeholder tree here. +local_repository( + name = "protobuf_pip_deps_setuptools", + path = "third_party/protobuf_pip_deps_setuptools", +) + +load("@tb_rules_java//java:rules_java_deps.bzl", "rules_java_dependencies") + +tb_http_archive( + name = "com_google_absl", + repo_mapping = { + "@rules_cc": "@tb_rules_cc", + }, + sha256 = "d8ae9aa794a571ee39c77085ee69f1d4ac276212a7d99734974d95df7baa8d13", + strip_prefix = "abseil-cpp-9ac7062b1860d895fb5a8cbf58c3e9ef8f674b5f", + urls = tb_mirror_urls("https://github.com/abseil/abseil-cpp/archive/9ac7062b1860d895fb5a8cbf58c3e9ef8f674b5f.zip"), +) + +tb_http_archive( name = "com_google_protobuf", - sha256 = "9a301cf94a8ddcb380b901e7aac852780b826595075577bb967004050c835056", - strip_prefix = "protobuf-3.19.6", - urls = [ - "http://mirror.tensorflow.org/github.com/protocolbuffers/protobuf/archive/v3.19.6.tar.gz", - "https://github.com/protocolbuffers/protobuf/archive/v3.19.6.tar.gz", # 2022-09-29 - ], + patch_file = ["//patches:protobuf_6_31_1_java_export.patch"], + repo_mapping = { + "@abseil-cpp": "@com_google_absl", + "@rules_cc": "@tb_rules_cc", + "@rules_java": "@tb_rules_java", + }, + sha256 = "6e09bbc950ba60c3a7b30280210cd285af8d7d8ed5e0a6ed101c72aff22e8d88", + strip_prefix = "protobuf-6.31.1", + urls = tb_mirror_urls("https://github.com/protocolbuffers/protobuf/archive/refs/tags/v6.31.1.zip"), ) +rules_java_dependencies() + +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") + +protobuf_deps() + # gRPC. # -# NOTE: The version used here must be cross-compatible with our protobuf version. -# As 2023-01-13, 1.48.2 is the most recent gRPC release that was still using a 3.19.x -# version of protobuf in its own builds (more recent releases move to 3.21.x). +# Keep this aligned with TensorFlow 2.21's Bazel-side gRPC dependency so the +# grpc plugin and protobuf repository stay on a known-compatible combination. http_archive( name = "com_github_grpc_grpc", - sha256 = "bdb8e98145469d58c69ab9f2c9e0bd838c2836a99b5760bc0ebf658623768f52", - strip_prefix = "grpc-1.48.2", - urls = [ - "http://mirror.tensorflow.org/github.com/grpc/grpc/archive/v1.48.2.tar.gz", - "https://github.com/grpc/grpc/archive/v1.48.2.tar.gz", # 2022-09-21 - ], + sha256 = "dd6a2fa311ba8441bbefd2764c55b99136ff10f7ea42954be96006a2723d33fc", + strip_prefix = "grpc-1.74.0", + urls = tb_mirror_urls("https://github.com/grpc/grpc/archive/refs/tags/v1.74.0.tar.gz"), +) + +http_archive( + name = "build_bazel_rules_swift", + sha256 = "9919ed1d8dae509645bfd380537ae6501528d8de971caebed6d5185b9970dc4d", + urls = tb_mirror_urls("https://github.com/bazelbuild/rules_swift/releases/download/2.1.1/rules_swift.2.1.1.tar.gz"), ) load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") grpc_deps() -load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps") - -grpc_extra_deps() - http_archive( name = "rules_rust", sha256 = "08109dccfa5bbf674ff4dba82b15d40d85b07436b02e62ab27e0b894f45bb4a3", strip_prefix = "rules_rust-d5ab4143245af8b33d1947813d411a6cae838409", urls = [ - # Master branch as of 2022-01-31 + # Pinned to this upstream commit as of 2022-01-31 "http://mirror.tensorflow.org/github.com/bazelbuild/rules_rust/archive/d5ab4143245af8b33d1947813d411a6cae838409.tar.gz", "https://github.com/bazelbuild/rules_rust/archive/d5ab4143245af8b33d1947813d411a6cae838409.tar.gz", ], ) -# WORKAROUND for rules_webtesting not declaring used com_github_gorilla_mux repo: -load("@io_bazel_rules_webtesting//web:go_repositories.bzl", "com_github_gorilla_mux") +load("@io_bazel_rules_webtesting//web:go_repositories.bzl", "go_internal_repositories", "go_repositories") + +go_repositories() -com_github_gorilla_mux() +go_internal_repositories() # Please add all new dependencies in workspace.bzl. load("//third_party:workspace.bzl", "tensorboard_workspace") diff --git a/ci/bazelrc b/ci/bazelrc index 4bda9653d6..3cf4037d3d 100644 --- a/ci/bazelrc +++ b/ci/bazelrc @@ -6,6 +6,12 @@ build --worker_max_instances=2 # Ensure sandboxing is on to increase hermeticity. build --spawn_strategy=sandboxed build --worker_sandboxing +# Bazel 7 may materialize frontend/npm assets with CopyFile/CopyDirectory +# actions that are not executable under processwrapper-sandbox alone. +# Keep sandboxing for normal actions, but allow these copy helpers to run +# locally so Angular/sass/esbuild repository actions remain buildable in CI. +build --strategy=CopyFile=local +build --strategy=CopyDirectory=local # Ensure the PATH env var from our virtualenv propagates into tests, which is # no longer on by default in Bazel 0.21.0 and possibly again in the future. diff --git a/package.json b/package.json index 5e44f96363..4ba5142a4a 100644 --- a/package.json +++ b/package.json @@ -28,18 +28,18 @@ }, "homepage": "https://github.com/tensorflow/tensorboard#readme", "devDependencies": { - "@angular-devkit/build-angular": "^15.2.9", - "@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#fb42478534df7d48ec23a6834fea94a776cb89a0", - "@angular/cli": "^16.2.0", - "@angular/compiler": "16.2.12", - "@angular/compiler-cli": "^16.2.12", + "@angular-devkit/build-angular": "^17.0.0", + "@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#3069be882e3e41cdb3dad58788d878e31d7d82e8", + "@angular/cli": "^17.0.0", + "@angular/compiler": "17.3.12", + "@angular/compiler-cli": "^17.0.0", "@babel/core": "^7.16.12", - "@bazel/concatjs": "5.7.0", - "@bazel/esbuild": "5.7.0", + "@bazel/concatjs": "5.8.1", + "@bazel/esbuild": "5.8.1", "@bazel/ibazel": "^0.15.9", - "@bazel/jasmine": "5.7.0", - "@bazel/terser": "5.7.0", - "@bazel/typescript": "5.7.0", + "@bazel/jasmine": "5.8.1", + "@bazel/terser": "5.8.1", + "@bazel/typescript": "5.8.1", "@types/d3": "5.7.2", "@types/jasmine": "^3.8.2", "@types/lodash": "^4.14.172", @@ -61,22 +61,22 @@ "prettier-plugin-organize-imports": "2.3.4", "requirejs": "^2.3.7", "tslib": "^2.3.0", - "typescript": "4.9.5", + "typescript": "5.2.2", "yarn-deduplicate": "^5.0.0" }, "dependencies": { - "@angular/animations": "^16.2.12", - "@angular/cdk": "^16.2.14", - "@angular/common": "16.2.12", - "@angular/core": "^16.2.12", - "@angular/forms": "^16.2.12", - "@angular/localize": "^16.2.12", - "@angular/material": "^16.2.14", - "@angular/platform-browser": "^16.2.12", - "@angular/platform-browser-dynamic": "^16.2.12", - "@angular/router": "^16.2.12", - "@ngrx/effects": "^15.4.0", - "@ngrx/store": "^15.4.0", + "@angular/animations": "^17.0.0", + "@angular/cdk": "^17.0.0", + "@angular/common": "17.3.12", + "@angular/core": "^17.0.0", + "@angular/forms": "^17.0.0", + "@angular/localize": "^17.0.0", + "@angular/material": "^17.0.0", + "@angular/platform-browser": "^17.0.0", + "@angular/platform-browser-dynamic": "^17.0.0", + "@angular/router": "^17.0.0", + "@ngrx/effects": "^17.0.0", + "@ngrx/store": "^17.0.0", "@polymer/decorators": "^3.0.0", "@polymer/iron-behaviors": "^3.0.1", "@polymer/iron-collapse": "^3.0.1", @@ -131,7 +131,7 @@ "three": "~0.137.0", "umap-js": "^1.3.2", "web-animations-js": "^2.3.2", - "zone.js": "^0.13.3" + "zone.js": "^0.14.0" }, "resolutions": { "@types/d3-brush": "1.1.8", diff --git a/patches/@angular+build-tooling+0.0.0-2113cd7f66a089ac0208ea84eee672b2529f4f6c.patch b/patches/@angular+build-tooling+0.0.0-2113cd7f66a089ac0208ea84eee672b2529f4f6c.patch new file mode 100644 index 0000000000..3c546418ef --- /dev/null +++ b/patches/@angular+build-tooling+0.0.0-2113cd7f66a089ac0208ea84eee672b2529f4f6c.patch @@ -0,0 +1,33 @@ +diff --git a/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel b/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel +index 870da1b..3f1e5c5 100755 +--- a/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel ++++ b/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel +@@ -23,6 +23,7 @@ js_library( + deps = [ + "@npm//@babel/core", + "@npm//@babel/helper-annotate-as-pure", ++ "@npm//@babel/helper-split-export-declaration", + ], + ) + +diff --git a/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs b/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs +index 6d5ec3f..ad4217f 100755 +--- a/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs ++++ b/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs +@@ -86,11 +86,11 @@ export async function createEsbuildAngularOptimizePlugin(opts, additionalBabelPl + devkitOptimizePlugins.adjustTypeScriptEnumsPlugin, + ); + +- // If the current file is denoted as explicit side effect free, add the pure +- // top-level functions optimization plugin for this file. +- if (opts.optimize.isSideEffectFree && opts.optimize.isSideEffectFree(args.path)) { +- plugins.push(devkitOptimizePlugins.pureToplevelFunctionsPlugin); +- } ++ // For TensorBoard: This plugin aggressively culls symbols in a way that ++ // is incompatible with TensorBoard source. Disable it. As result the binary is bigger. ++ //if (opts.optimize.isSideEffectFree && opts.optimize.isSideEffectFree(args.path)) { ++ // plugins.push(devkitOptimizePlugins.pureToplevelFunctionsPlugin); ++ //} + } + + const shouldRunLinker = diff --git a/patches/@angular+build-tooling+0.0.0-7d103b83a07f132629592fc9918ce17d42a5e382.patch b/patches/@angular+build-tooling+0.0.0-7d103b83a07f132629592fc9918ce17d42a5e382.patch deleted file mode 100644 index db8c439cd3..0000000000 --- a/patches/@angular+build-tooling+0.0.0-7d103b83a07f132629592fc9918ce17d42a5e382.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel b/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel -index d5a8645..4b57378 100755 ---- a/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel -+++ b/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel -@@ -23,6 +23,7 @@ js_library( - deps = [ - "@npm//@babel/core", - "@npm//@babel/helper-annotate-as-pure", -+ "@npm//@babel/helper-split-export-declaration", - ], - ) - -diff --git a/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs b/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs -index 57cd2b9..2e5eaf1 100755 ---- a/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs -+++ b/node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs -@@ -43,9 +43,12 @@ export function createEsbuildAngularOptimizePlugin( - - // If the current file is denoted as explicit side effect free, add the pure - // top-level functions optimization plugin for this file. -- if (isSideEffectFreeFn !== null && isSideEffectFreeFn(args.path)) { -- plugins.push(pureToplevelFunctionsPlugin); -- } -+ // For TensorBoard: This plugin aggressively culls symbols in a way that -+ // is incompatible with TensorBoard source. Remove it. The binary is -+ // bigger than it otherwise could be but the bundle also happens faster. -+ //if (isSideEffectFreeFn !== null && isSideEffectFreeFn(args.path)) { -+ // plugins.push(pureToplevelFunctionsPlugin); -+ //} - - const {code} = await babel.transformAsync(content, { - filename: filePath, diff --git a/patches/@bazel+concatjs+5.7.0.patch b/patches/@bazel+concatjs+5.8.1.patch similarity index 62% rename from patches/@bazel+concatjs+5.7.0.patch rename to patches/@bazel+concatjs+5.8.1.patch index c322830858..f2fea97ab1 100644 --- a/patches/@bazel+concatjs+5.7.0.patch +++ b/patches/@bazel+concatjs+5.8.1.patch @@ -28,24 +28,17 @@ index fed787a..377915a 100755 return struct( closure_js = closure_js_files, devmode_js = devmode_js_files, -diff --git a/node_modules/@bazel/concatjs/web_test/karma.conf.js b/node_modules/@bazel/concatjs/web_test/karma.conf.js -index 90a03ef..28778c9 100755 ---- a/node_modules/@bazel/concatjs/web_test/karma.conf.js -+++ b/node_modules/@bazel/concatjs/web_test/karma.conf.js -@@ -384,7 +384,15 @@ try { - conf.browsers.push(launcher); - } else { - const launcher = 'CustomChrome'; -- conf.customLaunchers = {[launcher]: {base: browser, flags: additionalArgs}}; -+ // For TensorBoard: Patch the CustomChrome launcher so that even it -+ // specifies the --no-sandbox flag. This is to workaround -+ // incompatibilities with some environments. -+ // -+ // Specifically we were seeing errors like: -+ // [WARNING:gpu_process_host.cc(1228)] The GPU process has crashed 6 time(s) -+ // [FATAL:gpu_data_manager_impl_private.cc(439)] GPU process isn't usable. Goodbye. -+ conf.customLaunchers = -+ {[launcher]: {base: browser, flags: ['--no-sandbox', ...additionalArgs]}}; - conf.browsers.push(launcher); - } - } +diff --git a/node_modules/@bazel/concatjs/package.json b/node_modules/@bazel/concatjs/package.json +index 1234567..abcdefg 100755 +--- a/node_modules/@bazel/concatjs/package.json ++++ b/node_modules/@bazel/concatjs/package.json +@@ -24,7 +24,8 @@ + "dependencies": { + "protobufjs": "6.8.8", + "source-map-support": "0.5.9", +- "tsutils": "3.21.0" ++ "tsutils": "3.21.0", ++ "typescript": "5.2.2" + }, + "peerDependencies": { + "karma": ">=4.0.0", diff --git a/patches/README.md b/patches/README.md index d95fc2391e..4224fe93fe 100644 --- a/patches/README.md +++ b/patches/README.md @@ -1,21 +1,88 @@ # TensorBoard patches using patch-package. -We use [patch-package](https://www.npmjs.com/package/patch-package) to apply +We use [patch-package](https://www.npmjs.com/package/patch-package) to author TensorBoard-specific patches to some of our npm/yarn dependencies. +At build time, `WORKSPACE` applies the generated patch artifacts via +`yarn_install(post_install_patches = ...)` instead of invoking +`patch-package` inside the repository rule. In the current Bazel/CI setup, that +install-time invocation was less reliable than applying the generated patch +files directly. + After creating or updating a patch, ensure there is no trailing whitespace on any line (CI runs `./tensorboard/tools/whitespace_hygiene_test.py`). You can strip it with `sed -i '' 's/[[:space:]]*$//' patches/.patch`. -To regenerate @bazel/concatjs patch: -* `vi node_modules/@bazel/concatjs/web_test/karma.conf.js` +## `@bazel+concatjs+5.8.1.patch` + +**Modified files:** +- `node_modules/@bazel/concatjs/internal/common/compilation.bzl` +- `node_modules/@bazel/concatjs/package.json` + +**What it does:** +Updated patch from 5.7.0 to 5.8.1. This version already includes the TypeScript 5.x fix and Chrome sandbox fix that we had to patch manually in 5.7.0. +Added typescript as a direct dependency because the Bazel sandbox can't find it otherwise. + +Why 5.8.1 and not 6.x: rules_nodejs 6.x removed most of the build rules we depend on (concatjs, esbuild, typescript, etc.) and moved them to a separate project (rules_js). This effort will be done in future upgrades. + + +To regenerate: +* `vi node_modules/@bazel/concatjs/internal/common/compilation.bzl` +* `vi node_modules/@bazel/concatjs/package.json` * make edits * `yarn patch-package "@bazel/concatjs"` * update the WORKSPACE file with the name of the new patch file -To regenerate @angular/build-tooling patch: +## `@angular+build-tooling+0.0.0-2113cd7f66a089ac0208ea84eee672b2529f4f6c.patch` + +**Modified files:** +- `node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel` +- `node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs` + +**What it does:** +Updated for the Angular 17 version of build-tooling, adding the missing Babel dependency and +Disables an optimization plugin that incorrectly removes function calls that Tensorboard depends on runtime. + +To regenerate: +* `vi node_modules/@angular/build-tooling/shared-scripts/angular-optimization/BUILD.bazel` * `vi node_modules/@angular/build-tooling/shared-scripts/angular-optimization/esbuild-plugin.mjs` * make edits * `yarn patch-package "@angular/build-tooling"` * update the WORKSPACE file with the name of the new patch file + + +## `protobuf_6_31_1_java_export.patch` + +**Modified files:** +- `build_defs/java_opts.bzl` +- `bazel/private/proto_library_rule.bzl` + +**What it does:** +- Drops the older javadocopts workaround from protobuf's Java export helper on + the current rules_java/protobuf stack. +- Relaxes the import-prefix normalization check so empty-but-normalized values + continue to work under the newer path handling used here. + + +## `rules_cc_protobuf.patch` + +**Modified files:** +- `cc/defs.bzl` + +**What it does:** +- Re-exports `cc_proto_library` from protobuf's Bazel definitions so callers on + this repository can keep loading the symbol through `rules_cc` while using the + protobuf 6.31.1 repository layout. + + +## `rules_closure_soy_cli.patch` + +**Modified files:** +- `closure/templates/closure_java_template_library.bzl` + +**What it does:** +- Updates rules_closure's Soy invocation for the compiler/jar combination used + here. +- Switches to the `--depHeaders` flag expected by this compiler and drops the + older `--allowExternalCalls` flag that is not accepted here. diff --git a/patches/protobuf_6_31_1_java_export.patch b/patches/protobuf_6_31_1_java_export.patch new file mode 100644 index 0000000000..bf0291c524 --- /dev/null +++ b/patches/protobuf_6_31_1_java_export.patch @@ -0,0 +1,49 @@ +diff --git a/build_defs/java_opts.bzl b/build_defs/java_opts.bzl +--- a/build_defs/java_opts.bzl ++++ b/build_defs/java_opts.bzl +@@ -17,14 +17,5 @@ def protobuf_java_export(**kwargs): + def protobuf_java_export(**kwargs): + java_export( + javacopts = JAVA_RELEASE_OPTS, +- # https://github.com/bazelbuild/rules_jvm_external/issues/1245 +- javadocopts = [ +- "-notimestamp", +- "-use", +- "-quiet", +- "-Xdoclint:-missing", +- "-encoding", +- "UTF8", +- ], + **kwargs + ) +diff --git a/bazel/private/proto_library_rule.bzl b/bazel/private/proto_library_rule.bzl +--- a/bazel/private/proto_library_rule.bzl ++++ b/bazel/private/proto_library_rule.bzl +@@ -29,6 +29,9 @@ def _check_srcs_package(target_package, srcs): + for src in srcs: + if target_package != src.label.package: + fail("Proto source with label '%s' must be in same package as consuming rule." % src.label) ++ ++def _is_normalized(path): ++ return path == "" or paths.normalize(path) == path + + def _get_import_prefix(ctx): + """Gets and verifies import_prefix attribute if it is declared.""" +@@ -36,7 +39,7 @@ def _get_import_prefix(ctx): + + import_prefix = ctx.attr.import_prefix + +- if not paths.is_normalized(import_prefix): ++ if not _is_normalized(import_prefix): + fail("should be normalized (without uplevel references or '.' path segments)", attr = "import_prefix") + if paths.is_absolute(import_prefix): + fail("should be a relative path", attr = "import_prefix") +@@ -48,7 +51,7 @@ def _get_strip_import_prefix(ctx): + + strip_import_prefix = ctx.attr.strip_import_prefix + +- if not paths.is_normalized(strip_import_prefix): ++ if not _is_normalized(strip_import_prefix): + fail("should be normalized (without uplevel references or '.' path segments)", attr = "strip_import_prefix") + + if paths.is_absolute(strip_import_prefix): diff --git a/patches/rules_cc_protobuf.patch b/patches/rules_cc_protobuf.patch new file mode 100644 index 0000000000..944962033d --- /dev/null +++ b/patches/rules_cc_protobuf.patch @@ -0,0 +1,16 @@ +diff --git a/cc/defs.bzl b/cc/defs.bzl +index 3448e77..9c298ab 100644 +--- a/cc/defs.bzl ++++ b/cc/defs.bzl +@@ -30,9 +30,11 @@ load("//cc/toolchains:cc_toolchain_suite.bzl", _cc_toolchain_suite = "cc_toolcha + load("//cc/toolchains:compiler_flag.bzl", _compiler_flag = "compiler_flag") + load("//cc/toolchains:fdo_prefetch_hints.bzl", _fdo_prefetch_hints = "fdo_prefetch_hints") + load("//cc/toolchains:fdo_profile.bzl", _fdo_profile = "fdo_profile") ++load("@com_google_protobuf//bazel:cc_proto_library.bzl", _protobuf_cc_proto_library = "cc_proto_library") + + # Rules + ++cc_proto_library = _protobuf_cc_proto_library + cc_library = _cc_library + cc_binary = _cc_binary + cc_test = _cc_test diff --git a/patches/rules_closure_soy_cli.patch b/patches/rules_closure_soy_cli.patch new file mode 100644 index 0000000000..1559d81ef6 --- /dev/null +++ b/patches/rules_closure_soy_cli.patch @@ -0,0 +1,19 @@ +diff --git a/closure/templates/closure_java_template_library.bzl b/closure/templates/closure_java_template_library.bzl +--- a/closure/templates/closure_java_template_library.bzl ++++ b/closure/templates/closure_java_template_library.bzl +@@ -133,7 +133,7 @@ def _soy_java_template_genrule_impl( + out_name = deps_flag_file_name, + targets = deps, +- flag = "--deps", ++ flag = "--depHeaders", + compatible_with = compatible_with, + ) + native.genrule( +@@ -145,7 +145,6 @@ def _soy_java_template_genrule_impl( + cmd = "$(location %s)" % soycompilerbin + + " --outputDirectory=$(@D)" + + " --javaPackage=" + java_package + + " --javaClassNameSource=filename" + +- " --allowExternalCalls=" + str(allow_external_calls) + + additional_flags + + # Include the sources and deps files as command line flags. diff --git a/tensorboard/BUILD b/tensorboard/BUILD index 181314b31a..a61cc8ed44 100644 --- a/tensorboard/BUILD +++ b/tensorboard/BUILD @@ -45,6 +45,7 @@ py_library( srcs_version = "PY3", deps = [ "//tensorboard:expect_absl_logging_installed", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", ], ) diff --git a/tensorboard/backend/event_processing/BUILD b/tensorboard/backend/event_processing/BUILD index 5444b20c56..9ff453ffe6 100644 --- a/tensorboard/backend/event_processing/BUILD +++ b/tensorboard/backend/event_processing/BUILD @@ -13,6 +13,7 @@ py_library( srcs_version = "PY3", visibility = ["//visibility:public"], deps = [ + "//tensorboard/compat", "//tensorboard/compat:tensorflow", "//tensorboard/util:io_util", "//tensorboard/util:tb_logging", @@ -38,6 +39,7 @@ py_library( ":data_provider", ":event_multiplexer", ":tag_types", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", "//tensorboard/data:ingester", "//tensorboard/plugins/audio:metadata", @@ -104,6 +106,7 @@ py_library( deps = [ ":directory_watcher", ":io_wrapper", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", "//tensorboard/util:tb_logging", ], @@ -130,6 +133,7 @@ py_library( visibility = ["//visibility:public"], deps = [ ":io_wrapper", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", "//tensorboard/util:io_util", "//tensorboard/util:tb_logging", @@ -172,6 +176,7 @@ py_library( deps = [ "//tensorboard:data_compat", "//tensorboard:dataclass_compat", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", "//tensorboard/compat/proto:protos_all_py_pb2", "//tensorboard/util:platform_util", @@ -354,6 +359,7 @@ py_library( srcs_version = "PY3", visibility = ["//visibility:public"], deps = [ + "//tensorboard/compat", "//tensorboard/compat:tensorflow", ], ) @@ -366,6 +372,7 @@ py_library( deps = [ ":event_accumulator", ":io_wrapper", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", "//tensorboard/compat/proto:protos_all_py_pb2", ], diff --git a/tensorboard/compat/BUILD b/tensorboard/compat/BUILD index e04bc3eb31..38ae4a7d0f 100644 --- a/tensorboard/compat/BUILD +++ b/tensorboard/compat/BUILD @@ -22,8 +22,16 @@ py_library( srcs = ["__init__.py"], srcs_version = "PY3", visibility = ["//visibility:public"], + deps = ["//tensorboard:lazy"], +) + +py_test( + name = "compat_test", + srcs = ["compat_test.py"], + srcs_version = "PY3", deps = [ - "//tensorboard:lazy", + ":compat", + "//tensorboard:expect_tensorflow_installed", ], ) @@ -38,9 +46,13 @@ py_library( # as written but can be swapped out for a real dependency. py_library( name = "tensorflow", + srcs = ["__init__.py"], srcs_version = "PY3", + # Keep the package's real __init__.py on this target instead of depending + # on :compat so Bazel test/binary runfiles expose the lazy tf/tf2 exports from + # the actual package init rather than a synthesized empty package. deps = [ - ":compat", + "//tensorboard:lazy", "//tensorboard/compat/tensorflow_stub", ], ) @@ -50,10 +62,16 @@ py_library( # Depend on this rule if your code should only use the TF stub. py_library( name = "no_tensorflow", - srcs = ["notf.py"], + srcs = [ + "__init__.py", + "notf.py", + ], srcs_version = "PY3", + # Mirror :tensorflow's package layout so the no-TF variant still exposes + # the compat package exports while forcing the sentinel import. deps = [ - ":tensorflow", + "//tensorboard:lazy", + "//tensorboard/compat/tensorflow_stub", ], ) diff --git a/tensorboard/compat/__init__.py b/tensorboard/compat/__init__.py index 6786669fe2..aedef948dd 100644 --- a/tensorboard/compat/__init__.py +++ b/tensorboard/compat/__init__.py @@ -19,6 +19,7 @@ and defer the search and loading of the module until necessary. """ +import importlib import tensorboard.lazy as _lazy @@ -39,7 +40,7 @@ def tf(): ImportError: if a TF-like API is not available. """ try: - from tensorboard.compat import notf # noqa: F401 + importlib.import_module("tensorboard.compat.notf") except ImportError: try: import tensorflow @@ -47,9 +48,7 @@ def tf(): return tensorflow except ImportError: pass - from tensorboard.compat import tensorflow_stub - - return tensorflow_stub + return importlib.import_module("tensorboard.compat.tensorflow_stub") @_lazy.lazy_load("tensorboard.compat.tf2") diff --git a/tensorboard/compat/compat_test.py b/tensorboard/compat/compat_test.py new file mode 100644 index 0000000000..d24bf31509 --- /dev/null +++ b/tensorboard/compat/compat_test.py @@ -0,0 +1,33 @@ +# Copyright 2026 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. + +"""Tests for tensorboard.compat lazy exports.""" + +import unittest + + +class CompatTest(unittest.TestCase): + def test_tf_export(self): + from tensorboard.compat import tf + + self.assertTrue(hasattr(tf, "compat")) + + def test_tf2_export(self): + from tensorboard.compat import tf2 + + self.assertTrue(hasattr(tf2, "summary")) + + +if __name__ == "__main__": + unittest.main() diff --git a/tensorboard/data/server/DEVELOPMENT.md b/tensorboard/data/server/DEVELOPMENT.md index 025249abdf..b28c883aa2 100644 --- a/tensorboard/data/server/DEVELOPMENT.md +++ b/tensorboard/data/server/DEVELOPMENT.md @@ -150,7 +150,7 @@ When done, commit the changes to `Cargo.toml`, `Cargo.lock`, and the Test datasets are stored on Google Cloud Storage in the world-readable `gs://tensorboard-bench-logs` bucket, whose [bucket README] is online. To run -against this data, use `gsutil` to copy it to your local machine. +against this data, use `gcloud storage` to copy it to your local machine. [bucket README]: https://storage.googleapis.com/tensorboard-bench-logs/README diff --git a/tensorboard/data/server/descriptor.bin b/tensorboard/data/server/descriptor.bin index bd230a3947..a0475fb3e9 100644 Binary files a/tensorboard/data/server/descriptor.bin and b/tensorboard/data/server/descriptor.bin differ diff --git a/tensorboard/data/server/gcs/gsutil.rs b/tensorboard/data/server/gcs/gsutil.rs index 09d609b9fc..5b741d10f3 100644 --- a/tensorboard/data/server/gcs/gsutil.rs +++ b/tensorboard/data/server/gcs/gsutil.rs @@ -21,7 +21,7 @@ use std::io::Write; use rustboard_core::gcs; #[derive(Clap, Debug)] -#[clap(name = "gsutil")] +#[clap(name = "gcloud storage")] struct Opts { #[clap(long, default_value = "info")] log_level: String, diff --git a/tensorboard/defs/defs.bzl b/tensorboard/defs/defs.bzl index 3f3ac904f2..745aab34dc 100644 --- a/tensorboard/defs/defs.bzl +++ b/tensorboard/defs/defs.bzl @@ -14,10 +14,9 @@ """External-only delegates for various BUILD rules.""" load("@bazel_skylib//rules:copy_file.bzl", "copy_file") -load("@io_bazel_rules_sass//:defs.bzl", "npm_sass_library", "sass_binary", "sass_library") +load("@npm//@angular/build-tooling/bazel:extract_js_module_output.bzl", "extract_js_module_output") load("@npm//@angular/build-tooling/bazel/app-bundling:index.bzl", "app_bundle") load("@npm//@angular/build-tooling/bazel/spec-bundling:index.bzl", "spec_bundle") -load("@npm//@angular/build-tooling/bazel:extract_js_module_output.bzl", "extract_js_module_output") load("@npm//@bazel/concatjs:index.bzl", "karma_web_test_suite", "ts_library") load("@npm//@bazel/esbuild:index.bzl", "esbuild") load("@npm//@bazel/typescript:index.bzl", "ts_config") @@ -179,7 +178,8 @@ def tf_ts_library(srcs = [], strict_checks = True, **kwargs): **kwargs ) -def tf_ng_web_test_suite(name, deps = [], **kwargs): +# The external list is used to exclude node-fetch from the browser bundle since it is not compatible with browsers. This allows us to use tfjs in our web application without running into issues caused by node-fetch. @tensorflow/tfjs-core is used by vz_projector plugin. +def tf_ng_web_test_suite(name, deps = [], external = [], **kwargs): """TensorBoard wrapper for the rule for a Karma web test suite. This uses the Angular team's internal toolchain for bundling @@ -218,6 +218,7 @@ def tf_ng_web_test_suite(name, deps = [], **kwargs): workspace_name = "org_tensorflow_tensorboard", run_angular_linker = False, platform = "browser", + external = external, ) karma_web_test_suite( @@ -234,6 +235,10 @@ def tf_ng_web_test_suite(name, deps = [], **kwargs): deps = [ "%s_bundle" % name, ], + # rules_nodejs passes this through to rules_webtesting. An empty dict + # avoids forwarding a stray None-valued attribute to web_test under + # Bazel 7.7.0 with the currently pinned rules_webtesting stack. + browser_overrides = {}, ) def tf_svg_bundle(name, srcs, out): @@ -247,45 +252,6 @@ def tf_svg_bundle(name, srcs, out): ], ) -def tf_sass_binary(deps = [], include_paths = [], strict_deps = True, **kwargs): - """TensorBoard wrap for declaring SASS binary. - - It adds dependency on theme by default then add include Angular material - theme library paths for better node_modules library resolution. - - strict_deps is included here and intentionally ignored so it can be used - internally. - """ - sass_binary( - deps = deps, - include_paths = include_paths + [ - "external/npm/node_modules", - ], - sourcemap = False, - **kwargs - ) - -def tf_sass_library(**kwargs): - """TensorBoard wrap for declaring SASS library. - - It re-exports the sass_libray symbol so users do not have to depend on - "@io_bazel_rules_sass//:defs.bzl". - """ - sass_library( - **kwargs - ) - -def tf_external_sass_libray(**kwargs): - """TensorBoard wrapper for declaring external SASS dependency. - - When an external (NPM) package have SASS files that has `import` statements, - TensorBoard has to depdend on them very specifically. This rule allows SASS - modules in NPM packages to be built properly. - """ - npm_sass_library( - **kwargs - ) - def tf_ng_module(assets = [], **kwargs): """TensorBoard wrapper for Angular modules.""" tf_ts_library( diff --git a/tensorboard/defs/protos.bzl b/tensorboard/defs/protos.bzl index e1dbe485d3..5fd5df99a3 100644 --- a/tensorboard/defs/protos.bzl +++ b/tensorboard/defs/protos.bzl @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@com_google_protobuf//:protobuf.bzl", "proto_gen") -load("@rules_python//python:py_library.bzl", "py_library") +load("@com_google_protobuf//bazel:py_proto_library.bzl", "py_proto_library") +load("@com_github_grpc_grpc//bazel:protobuf.bzl", "well_known_proto_libs") +load("@com_github_grpc_grpc//bazel:python_rules.bzl", "py_grpc_library") -# TODO(#6185): try to reduce the complexity in this rule. def tb_proto_library( name, srcs = None, @@ -25,66 +25,29 @@ def tb_proto_library( has_services = False, # The `exports` arg is unused here, but required internally for compatibility. exports = []): - outs_proto = _PyOuts(srcs, grpc = False) - outs_grpc = _PyOuts(srcs, grpc = True) if has_services else [] - outs_all = outs_proto + outs_grpc + proto_deps = [s + "_proto" for s in deps] + well_known_proto_libs() - # Dependencies we need to operate protoc (the protobuf compiler), including - # protoc itself, the intermediate generated proto output from the runtime - # bundled with protoc (to provide proto types used during the protoc code - # generation process itself), and the grpc plugin to protoc used for gRPC - # service generation. - protoc = "@com_google_protobuf//:protoc" - protoc_runtime_genproto = "@com_google_protobuf//:protobuf_python_genproto" - grpc_python_plugin = "//external:grpc_python_plugin" - - # Python generated code relies on a Python protobuf runtime to be present. - # The runtime version must be compatible (typically, >=) with the protoc - # that was used to generate the code. There is a runtime provided along - # with protoc as part of our build-time dependency on protobuf (the target - # is "@com_google_protobuf//:protobuf_python"), but we deliberately don't - # use it, because our tests may need to use a protobuf runtime that is - # higher than our protoc version in order to be compatible with generated - # protobuf code used by our dependencies (namely, TensorFlow). Instead, we - # rely on picking up protobuf ambiently from the virtual environment, the - # same way that it will behave when released in our pip package. - runtime = "//tensorboard:expect_protobuf_installed" - - proto_gen( - name = name + "_genproto", + native.proto_library( + name = name + "_proto", srcs = srcs, - deps = [s + "_genproto" for s in deps] + [protoc_runtime_genproto], - includes = [], - protoc = protoc, - gen_py = True, - outs = outs_all, + deps = proto_deps, + testonly = testonly, visibility = ["//visibility:public"], - plugin = grpc_python_plugin if has_services else None, - plugin_language = "grpc", ) - py_deps = [s + "_py_pb2" for s in deps] + [runtime] - py_library( + py_proto_library( name = name + "_py_pb2", - srcs = outs_proto, - imports = [], - srcs_version = "PY3", - deps = py_deps, + deps = [name + "_proto"], testonly = testonly, visibility = visibility, ) + if has_services: - py_library( + py_grpc_library( name = name + "_py_pb2_grpc", - srcs = outs_grpc, - imports = [], - srcs_version = "PY3", - deps = [name + "_py_pb2"] + py_deps, + srcs = [name + "_proto"], + deps = [name + "_py_pb2"], + grpc_library = "//tensorboard:expect_grpc_installed", testonly = testonly, visibility = visibility, ) - -def _PyOuts(srcs, grpc): - # Adapted from @com_google_protobuf//:protobuf.bzl. - ext = "_pb2.py" if not grpc else "_pb2_grpc.py" - return [s[:-len(".proto")] + ext for s in srcs] diff --git a/tensorboard/manager.py b/tensorboard/manager.py index 823735bd47..4d6ec770d0 100644 --- a/tensorboard/manager.py +++ b/tensorboard/manager.py @@ -32,6 +32,29 @@ from tensorboard.util import tb_logging +_SUBPROCESS_ENV_DENYLIST = frozenset( + ( + "BUILD_WORKING_DIRECTORY", + "BUILD_WORKSPACE_DIRECTORY", + "JAVA_RUNFILES", + "PYTHONHOME", + "PYTHONPATH", + "PYTHONSAFEPATH", + "PYTHONSTARTUP", + "PYTHONUSERBASE", + "PYTHON_RUNFILES", + "RUNFILES", + "RUNFILES_DIR", + "RUNFILES_MANIFEST_FILE", + "RUNFILES_MANIFEST_ONLY", + "RUNFILES_REPO_MAPPING", + "TEST_BINARY", + "TEST_SRCDIR", + "TEST_WORKSPACE", + ) +) + + @dataclasses.dataclass(frozen=True) class TensorBoardInfo: """Holds the information about a running TensorBoard instance. @@ -385,6 +408,20 @@ class StartTimedOut: pid: int +def _subprocess_environ(): + """Create an environment for a managed TensorBoard subprocess. + + TensorBoard processes launched from Bazel tests should resolve their own + runfiles and Python import paths. Inheriting Bazel's test-only Python and + runfiles variables can make the child binary execute against the parent's + runfiles tree instead of its own. + """ + environ = os.environ.copy() + for key in _SUBPROCESS_ENV_DENYLIST: + environ.pop(key, None) + return environ + + def start(arguments, timeout=datetime.timedelta(seconds=60)): """Start a new TensorBoard instance, or reuse a compatible one. @@ -427,6 +464,7 @@ def start(arguments, timeout=datetime.timedelta(seconds=60)): try: p = subprocess.Popen( ["tensorboard" if explicit_tb is None else explicit_tb] + arguments, + env=_subprocess_environ(), stdout=stdout_fd, stderr=stderr_fd, ) diff --git a/tensorboard/manager_test.py b/tensorboard/manager_test.py index 6911398c51..e5659d1cf6 100644 --- a/tensorboard/manager_test.py +++ b/tensorboard/manager_test.py @@ -233,6 +233,30 @@ def test_arguments_list_vs_tuple_irrelevant(self): self.assertEqual(with_list, with_tuple) +class SubprocessEnvironTest(tb_test.TestCase): + def test_strips_bazel_and_python_path_state(self): + with mock.patch.dict( + os.environ, + { + "PATH": "/bin:/usr/bin", + "TMPDIR": "/tmp/tb", + "PYTHONPATH": "/tmp/runfiles", + "RUNFILES_DIR": "/tmp/runfiles", + "TEST_BINARY": "/tmp/test_binary", + "BUILD_WORKSPACE_DIRECTORY": "/tmp/workspace", + }, + clear=True, + ): + actual = manager._subprocess_environ() + + self.assertEqual(actual["PATH"], "/bin:/usr/bin") + self.assertEqual(actual["TMPDIR"], "/tmp/tb") + self.assertNotIn("PYTHONPATH", actual) + self.assertNotIn("RUNFILES_DIR", actual) + self.assertNotIn("TEST_BINARY", actual) + self.assertNotIn("BUILD_WORKSPACE_DIRECTORY", actual) + + class TensorBoardInfoIoTest(tb_test.TestCase): """Tests for `write_info_file`, `remove_info_file`, and `get_all`.""" diff --git a/tensorboard/pip_package/requirements.txt b/tensorboard/pip_package/requirements.txt index ac31021fbb..7ee4b97b1a 100644 --- a/tensorboard/pip_package/requirements.txt +++ b/tensorboard/pip_package/requirements.txt @@ -17,22 +17,15 @@ absl-py >= 0.4 # NOTE: this version should be >= the grpc version in our WORKSPACE file. -grpcio >= 1.48.2 +grpcio >= 1.74.0, < 2.0 markdown >= 2.6.8 numpy >= 1.12.0 -# NOTE: The packaging dependency was introduced in order to compare installed -# package versions and conditionally use different supported kwargs -# (specifically the protobuf dependency). If we restrict protobuf >= 5.0.0 we -# can get rid of the packaging dependency. packaging pillow # NOTE: this version must be >= the protoc version in our WORKSPACE file. -# At the same time, any constraints we specify here must allow at least some -# version to be installed that is also compatible with TensorFlow's constraints: -# https://github.com/tensorflow/tensorflow/blob/25adc4fccb4b0bb5a933eba1d246380e7b87d7f7/tensorflow/tools/pip_package/setup.py#L101 -# 4.24.0 had an issue that broke our tests, so we should avoid that release: -# https://github.com/protocolbuffers/protobuf/issues/13485 -protobuf >= 3.19.6, != 4.24.0 +# TensorFlow 2.21 requires protobuf >= 6.31.1, < 8.0.0, so keep our open-source +# runtime floor aligned with that range. +protobuf >= 6.31.1, < 8.0.0 setuptools >= 41.0.0 # Note: provides pkg_resources as well as setuptools tensorboard-data-server >= 0.7.0, < 0.8.0 werkzeug >= 1.0.1 diff --git a/tensorboard/pip_package/requirements_dev.txt b/tensorboard/pip_package/requirements_dev.txt index f9a7a3ae70..5b2ea9c454 100644 --- a/tensorboard/pip_package/requirements_dev.txt +++ b/tensorboard/pip_package/requirements_dev.txt @@ -16,11 +16,11 @@ # Dependencies of TensorBoard used only for testing or development. # For tests -grpcio-testing==1.24.3 +grpcio-testing==1.74.0 pandas~=2.0 # For gfile S3 test -boto3==1.9.86 -moto==1.3.7 +boto3==1.34.162 +moto==4.2.14 # For gfile fsspec test fsspec>=2021.06.0 diff --git a/tensorboard/pip_package/test_pip_package.sh b/tensorboard/pip_package/test_pip_package.sh index 1595bcd860..a4b1cb097f 100755 --- a/tensorboard/pip_package/test_pip_package.sh +++ b/tensorboard/pip_package/test_pip_package.sh @@ -194,4 +194,6 @@ test_tf_summary() { printf '%s\n' "${import_from}" "${import_as}" "${import_attr}" | python - } +printf >&2 'All smoke tests passed.' + main "$@" diff --git a/tensorboard/plugins/audio/BUILD b/tensorboard/plugins/audio/BUILD index 8e0d8965ba..4075740989 100644 --- a/tensorboard/plugins/audio/BUILD +++ b/tensorboard/plugins/audio/BUILD @@ -92,6 +92,7 @@ py_library( deps = [ ":metadata", "//tensorboard/compat", + "//tensorboard/compat:tensorflow", "//tensorboard/util:lazy_tensor_creator", ], ) diff --git a/tensorboard/plugins/custom_scalar/BUILD b/tensorboard/plugins/custom_scalar/BUILD index 83b59203ff..795d5c5fc1 100644 --- a/tensorboard/plugins/custom_scalar/BUILD +++ b/tensorboard/plugins/custom_scalar/BUILD @@ -20,6 +20,7 @@ py_library( "//tensorboard:expect_protobuf_installed", "//tensorboard:plugin_util", "//tensorboard/backend:http_util", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", "//tensorboard/plugins:base_plugin", "//tensorboard/plugins/scalar:metadata", diff --git a/tensorboard/plugins/debugger_v2/debugger_v2_plugin_test.py b/tensorboard/plugins/debugger_v2/debugger_v2_plugin_test.py index ae30000138..115abfec4b 100644 --- a/tensorboard/plugins/debugger_v2/debugger_v2_plugin_test.py +++ b/tensorboard/plugins/debugger_v2/debugger_v2_plugin_test.py @@ -1494,7 +1494,7 @@ def testServeSourceFileContentOfThisTestFile(self): data = json.loads(response.get_data()) self.assertEqual(data["host_name"], _HOST_NAME) self.assertEqual(data["file_path"], _CURRENT_FILE_FULL_PATH) - with open(__file__, "r") as f: + with open(__file__, "r", newline="") as f: lines = f.read().split("\n") self.assertEqual(data["lines"], lines) diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/BUILD b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/BUILD index b849cc35c7..7f21c22cec 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/BUILD +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/BUILD @@ -1,13 +1,15 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ng_web_test_suite", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") load("@rules_python//python:py_binary.bzl", "py_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ng_web_test_suite", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "debugger_component_style", src = "debugger_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/debugger_container.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/debugger_container.ts index 6519ca9bc2..ee11d252cd 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/debugger_container.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/debugger_container.ts @@ -12,13 +12,19 @@ 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. ==============================================================================*/ -import {Component, OnDestroy, OnInit} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + OnDestroy, + OnInit, +} from '@angular/core'; import {createSelector, select, Store} from '@ngrx/store'; import {debuggerLoaded, debuggerUnloaded} from './actions'; import {getActiveRunId, getDebuggerRunListing} from './store'; import {State} from './store/debugger_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tf-debugger-v2', template: ` diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/effects/debugger_effects_test.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/effects/debugger_effects_test.ts index 41d84d4e23..a91fc05b0e 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/effects/debugger_effects_test.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/effects/debugger_effects_test.ts @@ -401,9 +401,12 @@ describe('Debugger effects', () => { }).compileComponents(); store = TestBed.inject>(Store) as MockStore; - dispatchSpy = spyOn(store, 'dispatch').and.callFake((action: Action) => { - dispatchedActions.push(action); - }); + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + dispatchSpy = (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake( + (action: Action) => { + dispatchedActions.push(action); + } + ); store.overrideSelector(getActivePlugin, ''); }); @@ -436,6 +439,7 @@ describe('Debugger effects', () => { begin: number, end: number, alertsResponse: AlertsResponse, + // tslint:disable-next-line:enforce-name-casing alert_type?: string ) { if (alert_type === undefined) { @@ -1403,6 +1407,7 @@ describe('Debugger effects', () => { }, begin: 0, end: 2, + // tslint:disable-next-line:enforce-name-casing alert_type: AlertType.INF_NAN_ALERT, per_type_alert_limit: 1000, alerts: [alert0, alert1], @@ -1657,7 +1662,9 @@ describe('Debugger effects', () => { .withArgs(runId, fileIndex) .and.returnValue( of({ + // tslint:disable-next-line:enforce-name-casing host_name: hostName, + // tslint:disable-next-line:enforce-name-casing file_path: filePath, lines, }) diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/testing/index.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/testing/index.ts index 700a92c286..9455e8ab5f 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/testing/index.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/testing/index.ts @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, NgModule} from '@angular/core'; +import {ChangeDetectionStrategy, Component, NgModule} from '@angular/core'; import {Store} from '@ngrx/store'; import { createInitialExecutionsState, @@ -354,6 +354,7 @@ export function createState( // that use it. @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tf-debugger-v2', template: ``, diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/alerts/alerts_component.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/alerts/alerts_component.ts index a2054c410f..c4af4398b5 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/alerts/alerts_component.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/alerts/alerts_component.ts @@ -12,7 +12,13 @@ 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. ==============================================================================*/ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; import {AlertType} from '../../store/debugger_types'; export interface AlertTypeDisplay { @@ -23,6 +29,7 @@ export interface AlertTypeDisplay { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'alerts-component', templateUrl: './alerts_component.ng.html', diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/common/BUILD b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/common/BUILD index 9cdf208548..912022e2f9 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/common/BUILD +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/common/BUILD @@ -1,11 +1,11 @@ -load("//tensorboard/defs:defs.bzl", "tf_sass_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_library( +sass_library( name = "style_lib", srcs = ["_lib.scss"], - deps = ["//tensorboard/webapp:angular_material_sass_deps"], + deps = ["//tensorboard/webapp/angular_components:material_sass"], ) diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/debug_tensor_value/debug_tensor_value_component.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/debug_tensor_value/debug_tensor_value_component.ts index 21455d1196..b0e86b71c8 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/debug_tensor_value/debug_tensor_value_component.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/debug_tensor_value/debug_tensor_value_component.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {DebugTensorValue} from '../../store/debugger_types'; const basicDebugInfoStyle = ` @@ -30,6 +30,7 @@ const basicDebugInfoStyle = ` `; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'debug-tensor-dtype', template: ` {{ dtype }} `, @@ -41,6 +42,7 @@ export class DebugTensorDTypeComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'debug-tensor-rank', template: ` {{ rank }}D `, @@ -52,6 +54,7 @@ export class DebugTensorRankComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'debug-tensor-shape', template: ` shape:{{ shapeString }} `, @@ -75,6 +78,7 @@ export class DebugTensorShapeComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'debug-tensor-numeric-breakdown', template: ` @@ -209,6 +213,7 @@ export class DebugTensorNumericBreakdownComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'debug-tensor-has-inf-or-nan', template: ` @@ -247,6 +252,7 @@ export class DebugTensorHasInfOrNaNComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'debug-tensor-value', template: ` diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/BUILD b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/BUILD index 1eceb24a18..900375c08b 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/BUILD +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/BUILD @@ -1,14 +1,16 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "execution_data_styles", src = "execution_data_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/execution_data_component.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/execution_data_component.ts index 5ac2f42409..a9cfa2b920 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/execution_data_component.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/execution_data_component.ts @@ -12,11 +12,12 @@ 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. ==============================================================================*/ -import {Component, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {Execution, TensorDebugMode} from '../../store/debugger_types'; import {parseDebugTensorValue} from '../../store/debug_tensor_value'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'execution-data-component', templateUrl: './execution_data_component.ng.html', diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/execution_data_container.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/execution_data_container.ts index c53c8586ff..c8db0d280b 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/execution_data_container.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/execution_data/execution_data_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {createSelector, select, Store} from '@ngrx/store'; import {getFocusedExecutionData} from '../../store'; import {Execution, State, TensorDebugMode} from '../../store/debugger_types'; @@ -21,6 +21,7 @@ import {DTYPE_ENUM_TO_NAME} from '../../tf_dtypes'; const UNKNOWN_DTYPE_NAME = 'Unknown dtype'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tf-debugger-v2-execution-data', template: ` diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/BUILD b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/BUILD index 5305d0a033..6a5b5004db 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/BUILD +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/BUILD @@ -1,24 +1,27 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "graph_styles", src = "graph_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ "//tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/common:style_lib", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "graph_op_styles", src = "graph_op_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ "//tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/common:style_lib", - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/graph_container.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/graph_container.ts index 82e929758e..e080c8e28b 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/graph_container.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/graph_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {select, Store} from '@ngrx/store'; import {graphOpFocused} from '../../actions'; import { @@ -23,6 +23,7 @@ import { import {State} from '../../store/debugger_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tf-debugger-v2-graph', template: ` diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/graph_op_component.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/graph_op_component.ts index b7e5c0a9f1..beafbe22d9 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/graph_op_component.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph/graph_op_component.ts @@ -13,10 +13,17 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; import {GraphOpInfo} from '../../store/debugger_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'graph-op', templateUrl: 'graph_op_component.ng.html', diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph_executions/BUILD b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph_executions/BUILD index f4b85ab165..47e77627a8 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph_executions/BUILD +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph_executions/BUILD @@ -1,12 +1,14 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "graph_executions_styles", src = "graph_executions_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ "//tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/common:style_lib", "//tensorboard/webapp/theme", diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph_executions/graph_executions_container.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph_executions/graph_executions_container.ts index b2d1e0c349..eaaeae3704 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph_executions/graph_executions_container.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/graph_executions/graph_executions_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {createSelector, select, Store} from '@ngrx/store'; import { graphExecutionFocused, @@ -27,6 +27,7 @@ import { import {State} from '../../store/debugger_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tf-debugger-v2-graph-executions', template: ` diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/inactive/inactive_component.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/inactive/inactive_component.ts index a45c209ec5..e69eb6ccf4 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/inactive/inactive_component.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/inactive/inactive_component.ts @@ -12,9 +12,10 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'inactive-component', templateUrl: './inactive_component.ng.html', diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/inactive/inactive_container.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/inactive/inactive_container.ts index 455190f0c6..f55dfe4e7a 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/inactive/inactive_container.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/inactive/inactive_container.ts @@ -12,13 +12,14 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {Store} from '@ngrx/store'; // TODO(cais): Move to a separate file. export interface InactiveState {} @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tf-debugger-v2-inactive', template: ` `, diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/BUILD b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/BUILD index 8c0ce03dd9..521eaf1028 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/BUILD +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/BUILD @@ -1,12 +1,14 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "source_files_component_style", src = "source_files_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/source_files_component.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/source_files_component.ts index 4dda714fc2..1e7e87a518 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/source_files_component.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/source_files_component.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {SourceFileContent, StackFrame} from '../../store/debugger_types'; /** @@ -24,6 +24,7 @@ import {SourceFileContent, StackFrame} from '../../store/debugger_types'; * displayed by this component. */ @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'source-files-component', templateUrl: './source_files_component.ng.html', diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/source_files_container.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/source_files_container.ts index dd9e2089d4..ce7b5d779c 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/source_files_container.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/source_files/source_files_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {Store} from '@ngrx/store'; import {Observable} from 'rxjs'; import {State as OtherAppState} from '../../../../../webapp/app_state'; @@ -24,6 +24,7 @@ import { import {State as DebuggerState} from '../../store/debugger_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tf-debugger-v2-source-files', template: ` diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/BUILD b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/BUILD index f755476ccf..4a427994aa 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/BUILD +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/BUILD @@ -1,12 +1,14 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "stack_trace_styles", src = "stack_trace_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ "//tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/common:style_lib", "//tensorboard/webapp/theme", diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/stack_trace_component.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/stack_trace_component.ts index ca274d93bf..01cf833b71 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/stack_trace_component.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/stack_trace_component.ts @@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ import { AfterViewChecked, + ChangeDetectionStrategy, Component, ElementRef, EventEmitter, @@ -40,6 +41,7 @@ export interface StackFrameForDisplay { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'stack-trace-component', templateUrl: './stack_trace_component.ng.html', diff --git a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/stack_trace_container.ts b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/stack_trace_container.ts index e35802566f..546affeb69 100644 --- a/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/stack_trace_container.ts +++ b/tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/views/stack_trace/stack_trace_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {createSelector, select, Store} from '@ngrx/store'; import {sourceLineFocused} from '../../actions'; import { @@ -25,6 +25,7 @@ import {CodeLocationType, State} from '../../store/debugger_types'; import {StackFrameForDisplay} from './stack_trace_component'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tf-debugger-v2-stack-trace', template: ` diff --git a/tensorboard/plugins/histogram/BUILD b/tensorboard/plugins/histogram/BUILD index 77fa2d1f0f..413d3aaf93 100644 --- a/tensorboard/plugins/histogram/BUILD +++ b/tensorboard/plugins/histogram/BUILD @@ -107,6 +107,7 @@ py_library( ":metadata", "//tensorboard:expect_numpy_installed", "//tensorboard/compat", + "//tensorboard/compat:tensorflow", "//tensorboard/compat/proto:protos_all_py_pb2", "//tensorboard/util:lazy_tensor_creator", "//tensorboard/util:tensor_util", diff --git a/tensorboard/plugins/hparams/BUILD b/tensorboard/plugins/hparams/BUILD index 74667a2819..51d5155b50 100644 --- a/tensorboard/plugins/hparams/BUILD +++ b/tensorboard/plugins/hparams/BUILD @@ -251,6 +251,7 @@ py_library( ":protos_all_py_pb2", "//tensorboard:expect_numpy_installed", "//tensorboard/compat", + "//tensorboard/compat:tensorflow", "//tensorboard/compat/proto:protos_all_py_pb2", ], ) diff --git a/tensorboard/plugins/image/BUILD b/tensorboard/plugins/image/BUILD index b32e02d2a3..3ba85ff1d2 100644 --- a/tensorboard/plugins/image/BUILD +++ b/tensorboard/plugins/image/BUILD @@ -103,6 +103,7 @@ py_library( deps = [ ":metadata", "//tensorboard/compat", + "//tensorboard/compat:tensorflow", "//tensorboard/compat/proto:protos_all_py_pb2", "//tensorboard/util:lazy_tensor_creator", ], diff --git a/tensorboard/plugins/mesh/BUILD b/tensorboard/plugins/mesh/BUILD index 14cc2d6459..54e7796c31 100644 --- a/tensorboard/plugins/mesh/BUILD +++ b/tensorboard/plugins/mesh/BUILD @@ -108,6 +108,7 @@ py_library( srcs_version = "PY3", deps = [ ":metadata", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", "//tensorboard/compat/proto:protos_all_py_pb2", "//tensorboard/util:tensor_util", @@ -151,6 +152,7 @@ py_library( srcs_version = "PY3", deps = [ "//tensorboard:expect_numpy_installed", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", ], ) diff --git a/tensorboard/plugins/projector/BUILD b/tensorboard/plugins/projector/BUILD index 628c344cfd..5a52c36de0 100644 --- a/tensorboard/plugins/projector/BUILD +++ b/tensorboard/plugins/projector/BUILD @@ -24,6 +24,7 @@ py_library( "//tensorboard:expect_protobuf_installed", "//tensorboard/backend:http_util", "//tensorboard/backend/event_processing:plugin_asset_util", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", "//tensorboard/plugins:base_plugin", "//tensorboard/util:img_mime_type_detector", @@ -41,6 +42,7 @@ py_library( ":metadata", ":protos_all_py_pb2", "//tensorboard:expect_protobuf_installed", + "//tensorboard/compat", "//tensorboard/compat:tensorflow", ], ) diff --git a/tensorboard/plugins/projector/projector_plugin.py b/tensorboard/plugins/projector/projector_plugin.py index 90bf78af1f..d0371bd670 100644 --- a/tensorboard/plugins/projector/projector_plugin.py +++ b/tensorboard/plugins/projector/projector_plugin.py @@ -210,10 +210,23 @@ def _parse_positive_int_param(request, param_name): def _rel_to_abs_asset_path(fpath, config_fpath): - fpath = os.path.expanduser(fpath) - if not os.path.isabs(fpath): - return os.path.join(os.path.dirname(config_fpath), fpath) - return fpath + config_dir = os.path.realpath( + os.path.dirname(os.path.expanduser(config_fpath)) + ) + candidate = os.path.expanduser(fpath) + if not os.path.isabs(candidate): + candidate = os.path.join(config_dir, candidate) + candidate = os.path.realpath(candidate) + error_message = 'Asset path "%s" resolves outside the config directory' % ( + fpath + ) + try: + common_path = os.path.commonpath([config_dir, candidate]) + except ValueError as e: + raise ValueError(error_message) from e + if common_path != config_dir: + raise ValueError(error_message) + return candidate def _using_tf(): @@ -363,9 +376,18 @@ def _augment_configs_with_checkpoint_info(self): embedding.tensor_name = embedding.tensor_name[:-2] # Find the size of embeddings associated with a tensors file. if embedding.tensor_path: - fpath = _rel_to_abs_asset_path( - embedding.tensor_path, self.config_fpaths[run] - ) + try: + fpath = _rel_to_abs_asset_path( + embedding.tensor_path, self.config_fpaths[run] + ) + except ValueError as e: + logger.warning( + 'Skipping tensor path "%s" for run "%s": %s', + embedding.tensor_path, + run, + e, + ) + continue tensor = self.tensor_cache.get((run, embedding.tensor_name)) if tensor is None: try: @@ -594,7 +616,10 @@ def _serve_metadata(self, request): "text/plain", 400, ) - fpath = _rel_to_abs_asset_path(fpath, self.config_fpaths[run]) + try: + fpath = _rel_to_abs_asset_path(fpath, self.config_fpaths[run]) + except ValueError as e: + return Respond(request, str(e), "text/plain", 400) if not tf.io.gfile.exists(fpath) or tf.io.gfile.isdir(fpath): return Respond( request, @@ -651,9 +676,12 @@ def _serve_tensor(self, request): embedding = self._get_embedding(name, config) if embedding and embedding.tensor_path: - fpath = _rel_to_abs_asset_path( - embedding.tensor_path, self.config_fpaths[run] - ) + try: + fpath = _rel_to_abs_asset_path( + embedding.tensor_path, self.config_fpaths[run] + ) + except ValueError as e: + return Respond(request, str(e), "text/plain", 400) if not tf.io.gfile.exists(fpath): return Respond( request, @@ -720,7 +748,10 @@ def _serve_bookmarks(self, request): "text/plain", 400, ) - fpath = _rel_to_abs_asset_path(fpath, self.config_fpaths[run]) + try: + fpath = _rel_to_abs_asset_path(fpath, self.config_fpaths[run]) + except ValueError as e: + return Respond(request, str(e), "text/plain", 400) if not tf.io.gfile.exists(fpath) or tf.io.gfile.isdir(fpath): return Respond( request, @@ -766,7 +797,10 @@ def _serve_sprite_image(self, request): ) fpath = os.path.expanduser(embedding_info.sprite.image_path) - fpath = _rel_to_abs_asset_path(fpath, self.config_fpaths[run]) + try: + fpath = _rel_to_abs_asset_path(fpath, self.config_fpaths[run]) + except ValueError as e: + return Respond(request, str(e), "text/plain", 400) if not tf.io.gfile.exists(fpath) or tf.io.gfile.isdir(fpath): return Respond( request, diff --git a/tensorboard/plugins/projector/projector_plugin_test.py b/tensorboard/plugins/projector/projector_plugin_test.py index 64dd581451..e7f9df9bf3 100644 --- a/tensorboard/plugins/projector/projector_plugin_test.py +++ b/tensorboard/plugins/projector/projector_plugin_test.py @@ -55,7 +55,11 @@ def __init__(self, *args, **kwargs): self.server = None def setUp(self): - self.log_dir = self.get_temp_dir() + self.test_dir = self.get_temp_dir() + self.log_dir = os.path.join(self.test_dir, "log_dir") + self.restricted_dir = os.path.join(self.test_dir, "restricted_dir") + tf.io.gfile.makedirs(self.log_dir) + tf.io.gfile.makedirs(self.restricted_dir) def testRunsWithValidCheckpoint(self): self._GenerateProjectorTestData() @@ -197,6 +201,93 @@ def testBookmarks(self): bookmark = self._GetJson(url) self.assertEqual(bookmark, {"a": "b"}) + def testMetadataServesRelativeFileWithinLogdir(self): + self._GenerateProjectorAssetsTestData(metadata_path="metadata.tsv") + self._WriteTextFile( + os.path.join(self.log_dir, "metadata.tsv"), "label\nvalue\n" + ) + self._SetupWSGIApp() + + response = self._Get( + "/data/plugin/projector/metadata?run=.&name=embedding" + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, b"label\nvalue\n") + + def testMetadataRejectsTraversalOutsideLogdir(self): + outside_metadata_path = os.path.join( + self.restricted_dir, "outside_metadata.tsv" + ) + traversal_path = "../restricted_dir/outside_metadata.tsv" + self._WriteTextFile(outside_metadata_path, "secret\n") + self._GenerateProjectorAssetsTestData(metadata_path=traversal_path) + self._SetupWSGIApp() + + response = self._Get( + "/data/plugin/projector/metadata?run=.&name=embedding" + ) + self._AssertOutsideConfigDirResponse(response) + + def testMetadataRejectsSymlinkOutsideLogdir(self): + outside_metadata_path = os.path.join( + self.restricted_dir, "outside_metadata.tsv" + ) + symlink_path = os.path.join(self.log_dir, "metadata-link.tsv") + self._WriteTextFile(outside_metadata_path, "secret\n") + try: + os.symlink(outside_metadata_path, symlink_path) + except (AttributeError, NotImplementedError, OSError) as e: + self.skipTest("symlinks unavailable: %s" % e) + self._GenerateProjectorAssetsTestData(metadata_path="metadata-link.tsv") + self._SetupWSGIApp() + + response = self._Get( + "/data/plugin/projector/metadata?run=.&name=embedding" + ) + self._AssertOutsideConfigDirResponse(response) + + def testTensorRejectsAbsolutePathOutsideLogdir(self): + outside_tensor_path = os.path.join( + self.restricted_dir, "outside_tensor.tsv" + ) + self._WriteTextFile(outside_tensor_path, "1.0\t2.0\n") + self._GenerateProjectorAssetsTestData(tensor_path=outside_tensor_path) + self._SetupWSGIApp() + + response = self._Get( + "/data/plugin/projector/tensor?run=.&name=embedding" + ) + self._AssertOutsideConfigDirResponse(response) + + def testBookmarksRejectAbsolutePathOutsideLogdir(self): + outside_bookmarks_path = os.path.join( + self.restricted_dir, "outside_bookmarks.json" + ) + self._WriteTextFile(outside_bookmarks_path, '{"label": "secret"}') + self._GenerateProjectorAssetsTestData( + bookmarks_path=outside_bookmarks_path + ) + self._SetupWSGIApp() + + response = self._Get( + "/data/plugin/projector/bookmarks?run=.&name=embedding" + ) + self._AssertOutsideConfigDirResponse(response) + + def testSpriteImageRejectsTraversalOutsideLogdir(self): + outside_sprite_path = os.path.join( + self.restricted_dir, "outside_sprite.png" + ) + traversal_path = "../restricted_dir/outside_sprite.png" + self._WriteTextFile(outside_sprite_path, "not-an-image") + self._GenerateProjectorAssetsTestData(sprite_image_path=traversal_path) + self._SetupWSGIApp() + + response = self._Get( + "/data/plugin/projector/sprite_image?run=.&name=embedding" + ) + self._AssertOutsideConfigDirResponse(response) + def testEndpointsNoAssets(self): g = tf.Graph() @@ -213,6 +304,10 @@ def _AssertTensorResponse(self, tensor_bytes, expected_tensor): ) self.assertTrue(np.array_equal(tensor, expected_tensor)) + def _AssertOutsideConfigDirResponse(self, response): + self.assertEqual(response.status_code, 400) + self.assertIn(b"resolves outside the config directory", response.data) + # TODO(#2007): Cleanly separate out projector tests that require real TF @unittest.skipUnless(USING_REAL_TF, "Test only passes when using real TF") def testPluginIsActive(self): @@ -336,6 +431,44 @@ def _GenerateProjectorTestData(self): ) saver.save(sess, checkpoint_path) + def _GenerateProjectorAssetsTestData( + self, + tensor_path="tensor.tsv", + metadata_path=None, + bookmarks_path=None, + sprite_image_path=None, + ): + self._WriteTextFile(self._ResolveAssetPath(tensor_path), "1.0\t2.0\n") + + config = projector_config_pb2.ProjectorConfig() + embedding = config.embeddings.add() + embedding.tensor_name = "embedding" + embedding.tensor_path = tensor_path + if metadata_path is not None: + embedding.metadata_path = metadata_path + if bookmarks_path is not None: + embedding.bookmarks_path = bookmarks_path + if sprite_image_path is not None: + embedding.sprite.image_path = sprite_image_path + + with tf.io.gfile.GFile( + os.path.join(self.log_dir, "projector_config.pbtxt"), "w" + ) as f: + f.write(text_format.MessageToString(config)) + + def _ResolveAssetPath(self, path): + path = os.path.expanduser(path) + if os.path.isabs(path): + return os.path.realpath(path) + return os.path.realpath(os.path.join(self.log_dir, path)) + + def _WriteTextFile(self, path, contents): + parent = os.path.dirname(path) + if parent: + tf.io.gfile.makedirs(parent) + with tf.io.gfile.GFile(path, "w") as f: + f.write(contents) + class MetadataColumnsTest(tf.test.TestCase): def testLengthDoesNotMatch(self): diff --git a/tensorboard/plugins/projector/vz_projector/BUILD b/tensorboard/plugins/projector/vz_projector/BUILD index 7d9777cb09..befb0e27be 100644 --- a/tensorboard/plugins/projector/vz_projector/BUILD +++ b/tensorboard/plugins/projector/vz_projector/BUILD @@ -140,6 +140,20 @@ tf_ts_library( tf_ng_web_test_suite( name = "vz_projector_test", + # This vz_projector plugin depends on @tensorflow/tfjs library. + # tfjs is designed to run in the browser and in Node.js server, so it includes node-fetch for the Node.js side. + # When esbuild bundles everything for the browser, it tries to include node-fetch and fails because it can't resolve Node.js built-ins. The external list just tells esbuild to ignore those server only packages since they'll never be used in the browser. + external = [ + "node-fetch", + "stream", + "http", + "https", + "url", + "zlib", + "buffer", + "punycode", + "string_decoder", + ], deps = [ ":vz_projector_test_lib", ], diff --git a/tensorboard/plugins/scalar/BUILD b/tensorboard/plugins/scalar/BUILD index 6a6d044f4e..fd5c3cea12 100644 --- a/tensorboard/plugins/scalar/BUILD +++ b/tensorboard/plugins/scalar/BUILD @@ -111,6 +111,7 @@ py_library( deps = [ ":metadata", "//tensorboard/compat", + "//tensorboard/compat:tensorflow", "//tensorboard/compat/proto:protos_all_py_pb2", "//tensorboard/util:tensor_util", ], diff --git a/tensorboard/plugins/text/BUILD b/tensorboard/plugins/text/BUILD index b7aa4bedc8..15ecdeb24e 100644 --- a/tensorboard/plugins/text/BUILD +++ b/tensorboard/plugins/text/BUILD @@ -101,6 +101,7 @@ py_library( deps = [ ":metadata", "//tensorboard/compat", + "//tensorboard/compat:tensorflow", "//tensorboard/compat/proto:protos_all_py_pb2", "//tensorboard/util:tensor_util", ], diff --git a/tensorboard/tools/whitespace_hygiene_test.py b/tensorboard/tools/whitespace_hygiene_test.py index c4b4a46308..cff34e33aa 100755 --- a/tensorboard/tools/whitespace_hygiene_test.py +++ b/tensorboard/tools/whitespace_hygiene_test.py @@ -25,7 +25,15 @@ import sys -exceptions = frozenset([]) +# @TODO(@cdavalos7): Remove this exception when the patch file is no longer needed. +# Patch files use a trailing space on blank lines to mark them as context. +# This is required by patch-package and cannot be removed. +exceptions = frozenset( + [ + "patches/@angular+build-tooling+0.0.0-2113cd7f66a089ac0208ea84eee672b2529f4f6c.patch", + "patches/protobuf_6_31_1_java_export.patch", + ] +) @dataclasses.dataclass(frozen=True) diff --git a/tensorboard/util/BUILD b/tensorboard/util/BUILD index ef1b57fd27..656c33054d 100644 --- a/tensorboard/util/BUILD +++ b/tensorboard/util/BUILD @@ -69,6 +69,7 @@ py_library( srcs_version = "PY3", deps = [ "//tensorboard/compat", + "//tensorboard/compat:tensorflow", ], ) diff --git a/tensorboard/webapp/BUILD b/tensorboard/webapp/BUILD index e701e6ad82..4bfc675fac 100644 --- a/tensorboard/webapp/BUILD +++ b/tensorboard/webapp/BUILD @@ -1,4 +1,5 @@ -load("//tensorboard/defs:defs.bzl", "tf_external_sass_libray", "tf_ng_module", "tf_ng_prod_js_binary", "tf_ng_web_test_suite", "tf_sass_binary", "tf_svg_bundle", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ng_prod_js_binary", "tf_ng_web_test_suite", "tf_svg_bundle", "tf_ts_library") load("//tensorboard/defs:js.bzl", "tf_resource_digest_suffixer") load("//tensorboard/defs:web.bzl", "tb_combine_html", "tf_web_library") @@ -86,9 +87,10 @@ tf_ts_library( ], ) -tf_sass_binary( +sass_binary( name = "app_styles", src = "app_container.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) @@ -379,13 +381,9 @@ tf_svg_bundle( out = "icon_bundle.svg", ) -tf_external_sass_libray( - name = "angular_material_sass_deps", - deps = ["@npm//@angular/material"], -) - -tf_sass_binary( +sass_binary( name = "styles", src = "styles.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) diff --git a/tensorboard/webapp/alert/alert_action_test.ts b/tensorboard/webapp/alert/alert_action_test.ts index 45d0e0ebaa..3f2a43c082 100644 --- a/tensorboard/webapp/alert/alert_action_test.ts +++ b/tensorboard/webapp/alert/alert_action_test.ts @@ -59,7 +59,8 @@ describe('alert_effects', () => { store = TestBed.inject>(Store) as MockStore; recordedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { recordedActions.push(action); }); }); diff --git a/tensorboard/webapp/alert/views/BUILD b/tensorboard/webapp/alert/views/BUILD index 8e846f9621..18cc93958b 100644 --- a/tensorboard/webapp/alert/views/BUILD +++ b/tensorboard/webapp/alert/views/BUILD @@ -1,10 +1,12 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "alert_display_snackbar_styles", src = "alert_display_snackbar_container.scss", + include_paths = ["external/npm/node_modules"], ) tf_ng_module( diff --git a/tensorboard/webapp/alert/views/alert_snackbar_test.ts b/tensorboard/webapp/alert/views/alert_snackbar_test.ts index 2870ac2f3a..9fdb69b5c0 100644 --- a/tensorboard/webapp/alert/views/alert_snackbar_test.ts +++ b/tensorboard/webapp/alert/views/alert_snackbar_test.ts @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ import {OverlayContainer} from '@angular/cdk/overlay'; -import {TestBed} from '@angular/core/testing'; +import {fakeAsync, flush, TestBed} from '@angular/core/testing'; import {MatButtonModule} from '@angular/material/button'; import {MatSnackBar, MatSnackBarModule} from '@angular/material/snack-bar'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; @@ -56,7 +56,8 @@ describe('alert snackbar', () => { }).compileComponents(); store = TestBed.inject>(Store) as MockStore; recordedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { recordedActions.push(action); }); overlayContainer = TestBed.inject(OverlayContainer); @@ -129,7 +130,8 @@ describe('alert snackbar', () => { }); }); - it('closes the snackbar on click', async () => { + /* Replaced async/await with fakeAsync/flush(). Angular Material 17 no longer triggers immediate change detection when dismissing the snackbar. Since the snackbar lives in an overlay outside the test component, fixture.detectChanges() doesn't reach it. flush() forces all pending tasks to complete globally, including the overlay */ + it('closes the snackbar on click', fakeAsync(() => { const fixture = TestBed.createComponent(AlertSnackbarContainer); fixture.detectChanges(); store.overrideSelector(selectors.getLatestAlert, { @@ -143,14 +145,13 @@ describe('alert snackbar', () => { .querySelector(Selectors.DISMISS_BUTTON); expect(dismissEl).toBeTruthy(); (dismissEl as HTMLButtonElement).click(); - fixture.detectChanges(); - await fixture.whenStable(); + flush(); const snackbarAfterEl = overlayContainer .getContainerElement() .querySelector(Selectors.SNACKBAR); expect(snackbarAfterEl).not.toBeTruthy(); - }); + })); it('shows the followup action if needed', () => { const fixture = TestBed.createComponent(AlertSnackbarContainer); @@ -187,7 +188,7 @@ describe('alert snackbar', () => { expect(followupEl).not.toBeTruthy(); }); - it('dispatches a followup action and closes', async () => { + it('dispatches a followup action and closes', fakeAsync(() => { const fixture = TestBed.createComponent(AlertSnackbarContainer); fixture.detectChanges(); store.overrideSelector(selectors.getLatestAlert, { @@ -204,17 +205,16 @@ describe('alert snackbar', () => { .getContainerElement() .querySelector(Selectors.FOLLOWUP_BUTTON); (followupEl as HTMLButtonElement).click(); - fixture.detectChanges(); - await fixture.whenStable(); + flush(); expect(recordedActions).toEqual([testAction()]); const snackbarAfterEl = overlayContainer .getContainerElement() .querySelector(Selectors.SNACKBAR); expect(snackbarAfterEl).not.toBeTruthy(); - }); + })); - it('dispatches a followup action with payload and closes', async () => { + it('dispatches a followup action with payload and closes', fakeAsync(() => { const fixture = TestBed.createComponent(AlertSnackbarContainer); fixture.detectChanges(); store.overrideSelector(selectors.getLatestAlert, { @@ -231,13 +231,12 @@ describe('alert snackbar', () => { .getContainerElement() .querySelector(Selectors.FOLLOWUP_BUTTON); (followupEl as HTMLButtonElement).click(); - fixture.detectChanges(); - await fixture.whenStable(); + flush(); expect(recordedActions).toEqual([testActionWithProps({foo: true})]); const snackbarAfterEl = overlayContainer .getContainerElement() .querySelector(Selectors.SNACKBAR); expect(snackbarAfterEl).not.toBeTruthy(); - }); + })); }); diff --git a/tensorboard/webapp/angular_components/BUILD b/tensorboard/webapp/angular_components/BUILD new file mode 100644 index 0000000000..6dad4ea28b --- /dev/null +++ b/tensorboard/webapp/angular_components/BUILD @@ -0,0 +1,10 @@ +# Open-source-only file (synced as BUILD.OPENSOURCE). Internally this dependency +# is managed directly by Blaze, so npm_sass_library is not needed. +load("@io_bazel_rules_sass//:defs.bzl", "npm_sass_library") + +package(default_visibility = ["//tensorboard:internal"]) + +npm_sass_library( + name = "material_sass", + deps = ["@npm//@angular/material"], +) diff --git a/tensorboard/webapp/app_container.ts b/tensorboard/webapp/app_container.ts index 64f21f7446..6fe181ba77 100644 --- a/tensorboard/webapp/app_container.ts +++ b/tensorboard/webapp/app_container.ts @@ -12,9 +12,14 @@ 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. ==============================================================================*/ -import {Component, ViewContainerRef} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + ViewContainerRef, +} from '@angular/core'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tb-webapp', templateUrl: './app_container.ng.html', diff --git a/tensorboard/webapp/app_routing/effects/app_routing_effects_test.ts b/tensorboard/webapp/app_routing/effects/app_routing_effects_test.ts index afa8834701..af5aeddf7c 100644 --- a/tensorboard/webapp/app_routing/effects/app_routing_effects_test.ts +++ b/tensorboard/webapp/app_routing/effects/app_routing_effects_test.ts @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {fakeAsync, flush, TestBed, tick} from '@angular/core/testing'; import {provideMockActions} from '@ngrx/effects/testing'; import {Action, createAction, createSelector, props, Store} from '@ngrx/store'; @@ -48,7 +48,13 @@ import { } from '../types'; import {AppRoutingEffects, TEST_ONLY} from './app_routing_effects'; -@Component({standalone: false, selector: 'test', template: '', jit: true}) +@Component({ + changeDetection: ChangeDetectionStrategy.Default, + standalone: false, + selector: 'test', + template: '', + jit: true, +}) class TestableComponent {} const testAction = createAction('[TEST] test action'); @@ -237,7 +243,8 @@ describe('app_routing_effects', () => { store.overrideSelector(getRehydratedDeepLinks, []); actualActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { actualActions.push(action); }); diff --git a/tensorboard/webapp/app_routing/route_config_test.ts b/tensorboard/webapp/app_routing/route_config_test.ts index eac5e5d67d..e51e6cc42c 100644 --- a/tensorboard/webapp/app_routing/route_config_test.ts +++ b/tensorboard/webapp/app_routing/route_config_test.ts @@ -13,13 +13,18 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {createAction, props} from '@ngrx/store'; import {RouteConfigs, RouteMatch} from './route_config'; import {ConcreteRouteDef, RedirectionRouteDef} from './route_config_types'; import {Navigation, RouteKind} from './types'; -@Component({standalone: false, selector: 'test', template: ''}) +@Component({ + changeDetection: ChangeDetectionStrategy.Default, + standalone: false, + selector: 'test', + template: '', +}) class TestableComponent {} function buildConcreteRouteDef(override: Partial) { diff --git a/tensorboard/webapp/app_routing/route_config_types.ts b/tensorboard/webapp/app_routing/route_config_types.ts index b8c0fc0803..6d83248c40 100644 --- a/tensorboard/webapp/app_routing/route_config_types.ts +++ b/tensorboard/webapp/app_routing/route_config_types.ts @@ -12,10 +12,10 @@ 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. ==============================================================================*/ -import {Component, Type} from '@angular/core'; +import {Type} from '@angular/core'; +import {Action} from '@ngrx/store'; import {DeepLinkProvider} from './deep_link_provider'; import {RouteKind, SerializableQueryParams} from './types'; -import {Action} from '@ngrx/store'; export interface ConcreteRouteDef { routeKind: RouteKind; @@ -26,7 +26,7 @@ export interface ConcreteRouteDef { // Parameter has to be denoted with ":" prefix and "/" has to precede it. path: string; - ngComponent: Type; + ngComponent: Type; // Redirect to this `path` if current navigation does not match any known // routes. Only one RouteConfig can have defaultRoute = true. diff --git a/tensorboard/webapp/app_routing/route_registry_module.ts b/tensorboard/webapp/app_routing/route_registry_module.ts index ddb2b12d07..2f09c83bc7 100644 --- a/tensorboard/webapp/app_routing/route_registry_module.ts +++ b/tensorboard/webapp/app_routing/route_registry_module.ts @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ import { - Component, Inject, ModuleWithProviders, NgModule, @@ -28,10 +27,7 @@ import {RouteKind} from './types'; @NgModule({}) export class RouteRegistryModule { private readonly routeConfigs: RouteConfigs; - private readonly routeKindToNgComponent = new Map< - RouteKind, - Type - >(); + private readonly routeKindToNgComponent = new Map>(); constructor( @Optional() @Inject(ROUTE_CONFIGS_TOKEN) configsList: RouteDef[][] @@ -67,7 +63,7 @@ export class RouteRegistryModule { return this.routeConfigs; } - getNgComponentByRouteKind(routeKind: RouteKind): Type | null { + getNgComponentByRouteKind(routeKind: RouteKind): Type | null { return this.routeKindToNgComponent.get(routeKind) || null; } diff --git a/tensorboard/webapp/app_routing/route_registry_module_test.ts b/tensorboard/webapp/app_routing/route_registry_module_test.ts index 5229882f57..32370b39af 100644 --- a/tensorboard/webapp/app_routing/route_registry_module_test.ts +++ b/tensorboard/webapp/app_routing/route_registry_module_test.ts @@ -13,12 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {RouteRegistryModule} from './route_registry_module'; import {RouteKind} from './types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'experiment', template: 'I am experiment', @@ -26,6 +27,7 @@ import {RouteKind} from './types'; class Experiment {} @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'experiments', template: 'List of experiment', @@ -33,6 +35,7 @@ class Experiment {} class Experiments {} @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'not_found', template: 'Unknown route', diff --git a/tensorboard/webapp/app_routing/views/router_link_test.ts b/tensorboard/webapp/app_routing/views/router_link_test.ts index 1e01818dd1..879990682e 100644 --- a/tensorboard/webapp/app_routing/views/router_link_test.ts +++ b/tensorboard/webapp/app_routing/views/router_link_test.ts @@ -13,7 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, DebugElement, Input, NO_ERRORS_SCHEMA} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + DebugElement, + Input, + NO_ERRORS_SCHEMA, +} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; @@ -27,6 +33,7 @@ import {LocationModule} from '../location_module'; import {RouterLinkDirectiveContainer} from './router_link_directive_container'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'test', template: 'testable link', @@ -37,6 +44,7 @@ class TestableComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'test-with-reset', template: @@ -73,7 +81,8 @@ describe('router_link', () => { AppRootProvider ) as TestableAppRootProvider; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { actualDispatches.push(action); }); }); diff --git a/tensorboard/webapp/app_routing/views/router_outlet_component.ts b/tensorboard/webapp/app_routing/views/router_outlet_component.ts index 7729dd76b4..2b28f9f361 100644 --- a/tensorboard/webapp/app_routing/views/router_outlet_component.ts +++ b/tensorboard/webapp/app_routing/views/router_outlet_component.ts @@ -33,14 +33,14 @@ export class RouterOutletComponent implements OnChanges { @ViewChild('routeContainer', {static: true, read: ViewContainerRef}) private readonly routeContainer!: ViewContainerRef; - @Input() activeNgComponent!: Type | null; + @Input() activeNgComponent!: Type | null; ngOnChanges(changes: SimpleChanges) { const activeComponentChange = changes['activeNgComponent']; if (activeComponentChange) { this.routeContainer.clear(); const componentType = - activeComponentChange.currentValue as Type | null; + activeComponentChange.currentValue as Type | null; if (componentType) { this.routeContainer.createComponent(componentType); } diff --git a/tensorboard/webapp/app_routing/views/router_outlet_test.ts b/tensorboard/webapp/app_routing/views/router_outlet_test.ts index bb26173e72..d3afebc4e1 100644 --- a/tensorboard/webapp/app_routing/views/router_outlet_test.ts +++ b/tensorboard/webapp/app_routing/views/router_outlet_test.ts @@ -13,7 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, NO_ERRORS_SCHEMA} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + NO_ERRORS_SCHEMA, +} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {BrowserDynamicTestingModule} from '@angular/platform-browser-dynamic/testing'; @@ -33,6 +37,7 @@ import {RouterOutletComponent} from './router_outlet_component'; import {RouterOutletContainer} from './router_outlet_container'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'first', template: 'I am a test', @@ -40,6 +45,7 @@ import {RouterOutletContainer} from './router_outlet_container'; class FirstTestableComponent {} @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'second', template: 'I am inevitable', diff --git a/tensorboard/webapp/core/effects/core_effects_test.ts b/tensorboard/webapp/core/effects/core_effects_test.ts index f969974b58..dfc2ba44ea 100644 --- a/tensorboard/webapp/core/effects/core_effects_test.ts +++ b/tensorboard/webapp/core/effects/core_effects_test.ts @@ -111,7 +111,8 @@ describe('core_effects', () => { store = TestBed.inject>(Store) as MockStore; recordedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { recordedActions.push(action); }); diff --git a/tensorboard/webapp/core/views/BUILD b/tensorboard/webapp/core/views/BUILD index 7cd765e171..52ef18642a 100644 --- a/tensorboard/webapp/core/views/BUILD +++ b/tensorboard/webapp/core/views/BUILD @@ -1,10 +1,12 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "layout_styles", src = "layout_container.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) diff --git a/tensorboard/webapp/core/views/layout_test.ts b/tensorboard/webapp/core/views/layout_test.ts index 5f2850a4d2..f210fd1788 100644 --- a/tensorboard/webapp/core/views/layout_test.ts +++ b/tensorboard/webapp/core/views/layout_test.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; @@ -30,6 +30,7 @@ import { import {LayoutContainer} from './layout_container'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'sidebar', template: `sidebar content`, @@ -37,6 +38,7 @@ import {LayoutContainer} from './layout_container'; class Sidebar {} @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'main', template: `main content`, @@ -44,6 +46,7 @@ class Sidebar {} class Main {} @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-component', template: ` @@ -85,7 +88,8 @@ describe('layout test', () => { dispatchedActions = []; store = TestBed.inject>(Store) as MockStore; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { dispatchedActions.push(action); }); store.overrideSelector(getSideBarWidthInPercent, 10); diff --git a/tensorboard/webapp/core/views/page_title_test.ts b/tensorboard/webapp/core/views/page_title_test.ts index 1d208e6a7c..08653b63ef 100644 --- a/tensorboard/webapp/core/views/page_title_test.ts +++ b/tensorboard/webapp/core/views/page_title_test.ts @@ -12,7 +12,11 @@ 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. ==============================================================================*/ -import {Component, NO_ERRORS_SCHEMA} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + NO_ERRORS_SCHEMA, +} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {Store} from '@ngrx/store'; import {MockStore} from '@ngrx/store/testing'; @@ -112,6 +116,7 @@ describe('page title test', () => { }); @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'my-tester', template: ` `, diff --git a/tensorboard/webapp/customization/customizable_component.ts b/tensorboard/webapp/customization/customizable_component.ts index 78a3101594..a1866d2d31 100644 --- a/tensorboard/webapp/customization/customizable_component.ts +++ b/tensorboard/webapp/customization/customizable_component.ts @@ -12,7 +12,14 @@ 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. ==============================================================================*/ -import {Component, Input, OnInit, Type, ViewContainerRef} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + OnInit, + Type, + ViewContainerRef, +} from '@angular/core'; /** * A Component that defines a customization point. Ideal for use for small @@ -24,7 +31,7 @@ import {Component, Input, OnInit, Type, ViewContainerRef} from '@angular/core'; * * 1. Define a customizable Component, for example a button: * - * const CustomizableButton = new InjectionToken>('Customizable Button'); + * const CustomizableButton = new InjectionToken>('Customizable Button'); * * 2. Where the customization point is desired, use this Component to wrap some * default behavior. Bind to some possibly-empty variable with the @@ -39,7 +46,7 @@ import {Component, Input, OnInit, Type, ViewContainerRef} from '@angular/core'; * * constructor( * @Optional() @Inject(CustomizableButton) - * readonly customButtonIfProvided: Type) + * readonly customButtonIfProvided: Type) * * If you do not wish to customize the behavior for a certain TensorBoard * service (in this case, a button), you're done. The TensorBoard service @@ -68,6 +75,7 @@ import {Component, Input, OnInit, Type, ViewContainerRef} from '@angular/core'; * }) */ @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tb-customization', template: ` @@ -77,7 +85,7 @@ import {Component, Input, OnInit, Type, ViewContainerRef} from '@angular/core'; `, }) export class CustomizableComponent implements OnInit { - @Input() customizableComponent!: Type | undefined; + @Input() customizableComponent!: Type | undefined; constructor(private readonly viewContainerRef: ViewContainerRef) {} diff --git a/tensorboard/webapp/customization/customization_test.ts b/tensorboard/webapp/customization/customization_test.ts index 88ebb97242..e7f6d5d593 100644 --- a/tensorboard/webapp/customization/customization_test.ts +++ b/tensorboard/webapp/customization/customization_test.ts @@ -12,7 +12,12 @@ 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. ==============================================================================*/ -import {Component, NgModule, Optional} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + NgModule, + Optional, +} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {CustomizationModule} from './customization_module'; @@ -26,6 +31,7 @@ export class CustomizableComponentType {} * Parent class that uses the component. */ @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'parent-component', template: ` @@ -55,6 +61,7 @@ export class ParentComponentModule {} * into the ParentComponent for some tests. */ @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'customizable-component', template: `
Showing Customized Text!
`, diff --git a/tensorboard/webapp/feature_flag/directives/feature_flag_directive_test.ts b/tensorboard/webapp/feature_flag/directives/feature_flag_directive_test.ts index 042762d1b1..6d236ad77c 100644 --- a/tensorboard/webapp/feature_flag/directives/feature_flag_directive_test.ts +++ b/tensorboard/webapp/feature_flag/directives/feature_flag_directive_test.ts @@ -12,20 +12,25 @@ 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. ==============================================================================*/ -import {Component, DebugElement, Input} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + DebugElement, + Input, +} from '@angular/core'; import {TestBed, fakeAsync, tick} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {Store} from '@ngrx/store'; import {MockStore} from '@ngrx/store/testing'; import {provideMockTbStore} from '../../testing/utils'; -import {FEATURE_FLAGS_HEADER_NAME} from '../http/const'; import {getFeatureFlagsToSendToServer} from '../store/feature_flag_selectors'; import {State as FeatureFlagState} from '../store/feature_flag_types'; import {FeatureFlagDirective} from './feature_flag_directive'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'test-matching-selector', template: ` @@ -41,6 +46,7 @@ export class TestMatchingComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'test-nonmatching-selector', template: ` diff --git a/tensorboard/webapp/feature_flag/views/BUILD b/tensorboard/webapp/feature_flag/views/BUILD index 75842f0aba..220be0ca00 100644 --- a/tensorboard/webapp/feature_flag/views/BUILD +++ b/tensorboard/webapp/feature_flag/views/BUILD @@ -1,14 +1,16 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "style", src = "feature_flag_dialog_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/webapp/feature_flag/views/feature_flag_dialog_component.ts b/tensorboard/webapp/feature_flag/views/feature_flag_dialog_component.ts index b749959e72..82cdc0e59a 100644 --- a/tensorboard/webapp/feature_flag/views/feature_flag_dialog_component.ts +++ b/tensorboard/webapp/feature_flag/views/feature_flag_dialog_component.ts @@ -12,12 +12,19 @@ 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. ==============================================================================*/ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; import {FeatureFlagType} from '../store/feature_flag_metadata'; import {FeatureFlags} from '../types'; import {FeatureFlagStatus, FeatureFlagStatusEvent} from './types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'feature-flag-dialog-component', styleUrls: ['feature_flag_dialog_component.css'], diff --git a/tensorboard/webapp/feature_flag/views/feature_flag_dialog_container.ts b/tensorboard/webapp/feature_flag/views/feature_flag_dialog_container.ts index 80af1f193f..b29cb822d7 100644 --- a/tensorboard/webapp/feature_flag/views/feature_flag_dialog_container.ts +++ b/tensorboard/webapp/feature_flag/views/feature_flag_dialog_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {Store} from '@ngrx/store'; import {Observable} from 'rxjs'; import {map, withLatestFrom} from 'rxjs/operators'; @@ -40,6 +40,7 @@ import { } from './types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'feature-flag-dialog', template: `Test', diff --git a/tensorboard/webapp/header/BUILD b/tensorboard/webapp/header/BUILD index c8bcd55b95..fdf57acf28 100644 --- a/tensorboard/webapp/header/BUILD +++ b/tensorboard/webapp/header/BUILD @@ -1,18 +1,21 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "plugin_selector_styles", src = "plugin_selector_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) -tf_sass_binary( +sass_binary( name = "header_styles", src = "header_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) diff --git a/tensorboard/webapp/header/dark_mode_toggle_component.ts b/tensorboard/webapp/header/dark_mode_toggle_component.ts index 40aae27cc2..a3183b6e86 100644 --- a/tensorboard/webapp/header/dark_mode_toggle_component.ts +++ b/tensorboard/webapp/header/dark_mode_toggle_component.ts @@ -12,7 +12,13 @@ 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. ==============================================================================*/ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; export enum DarkModeOverride { DEFAULT, @@ -21,6 +27,7 @@ export enum DarkModeOverride { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'app-header-dark-mode-toggle-component', template: ` diff --git a/tensorboard/webapp/header/dark_mode_toggle_container.ts b/tensorboard/webapp/header/dark_mode_toggle_container.ts index 1a01cbec62..52a1362fdd 100644 --- a/tensorboard/webapp/header/dark_mode_toggle_container.ts +++ b/tensorboard/webapp/header/dark_mode_toggle_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {Store} from '@ngrx/store'; import {Observable} from 'rxjs'; import {map} from 'rxjs/operators'; @@ -23,6 +23,7 @@ import {State as FeatureFlagState} from '../feature_flag/store/feature_flag_type import {DarkModeOverride} from './dark_mode_toggle_component'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'app-header-dark-mode-toggle', template: ` diff --git a/tensorboard/webapp/header/dark_mode_toggle_test.ts b/tensorboard/webapp/header/dark_mode_toggle_test.ts index 6cedfad724..3365cb742a 100644 --- a/tensorboard/webapp/header/dark_mode_toggle_test.ts +++ b/tensorboard/webapp/header/dark_mode_toggle_test.ts @@ -50,7 +50,8 @@ describe('dark mode toggle test', () => { store.overrideSelector(getEnableDarkModeOverride, null); dispatchedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { dispatchedActions.push(action); }); diff --git a/tensorboard/webapp/header/header_component.ts b/tensorboard/webapp/header/header_component.ts index bbf8da9bbb..c6d95692f4 100644 --- a/tensorboard/webapp/header/header_component.ts +++ b/tensorboard/webapp/header/header_component.ts @@ -12,9 +12,10 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'app-header', template: ` diff --git a/tensorboard/webapp/header/plugin_selector_component.ts b/tensorboard/webapp/header/plugin_selector_component.ts index b143a9609c..4680736a1e 100644 --- a/tensorboard/webapp/header/plugin_selector_component.ts +++ b/tensorboard/webapp/header/plugin_selector_component.ts @@ -12,12 +12,19 @@ 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. ==============================================================================*/ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; import {MatSelectChange} from '@angular/material/select'; import {PluginId} from '../types/api'; import {UiPluginMetadata} from './types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'plugin-selector-component', templateUrl: './plugin_selector_component.ng.html', diff --git a/tensorboard/webapp/header/plugin_selector_container.ts b/tensorboard/webapp/header/plugin_selector_container.ts index 0c2eb8b60d..a678c6ec63 100644 --- a/tensorboard/webapp/header/plugin_selector_container.ts +++ b/tensorboard/webapp/header/plugin_selector_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {createSelector, select, Store} from '@ngrx/store'; import {changePlugin} from '../core/actions'; import {getActivePlugin, getPlugins, State} from '../core/store'; @@ -29,6 +29,7 @@ const getDisabledPlugins = createSelector( ); @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'plugin-selector', template: ` diff --git a/tensorboard/webapp/header/reload_container.ts b/tensorboard/webapp/header/reload_container.ts index 742ac0e4c4..d8a6d1078f 100644 --- a/tensorboard/webapp/header/reload_container.ts +++ b/tensorboard/webapp/header/reload_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {createSelector, Store} from '@ngrx/store'; import {Observable} from 'rxjs'; import {combineLatestWith, map} from 'rxjs/operators'; @@ -36,6 +36,7 @@ const isReloadDisabledByPlugin = createSelector( ); @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'app-header-reload', template: ` diff --git a/tensorboard/webapp/hparams/_redux/hparams_effects_test.ts b/tensorboard/webapp/hparams/_redux/hparams_effects_test.ts index e8b8e1e1d1..0669f67f1e 100644 --- a/tensorboard/webapp/hparams/_redux/hparams_effects_test.ts +++ b/tensorboard/webapp/hparams/_redux/hparams_effects_test.ts @@ -65,9 +65,12 @@ describe('hparams effects', () => { store = TestBed.inject>(Store) as MockStore; actualActions = []; - dispatchSpy = spyOn(store, 'dispatch').and.callFake((action: Action) => { - actualActions.push(action); - }); + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + dispatchSpy = (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake( + (action: Action) => { + actualActions.push(action); + } + ); effects = TestBed.inject(HparamsEffects); dataSource = TestBed.inject( diff --git a/tensorboard/webapp/metrics/effects/metrics_effects_test.ts b/tensorboard/webapp/metrics/effects/metrics_effects_test.ts index c56c4d08ec..1abcd90567 100644 --- a/tensorboard/webapp/metrics/effects/metrics_effects_test.ts +++ b/tensorboard/webapp/metrics/effects/metrics_effects_test.ts @@ -85,7 +85,8 @@ describe('metrics effects', () => { }).compileComponents(); store = TestBed.inject>(Store) as MockStore; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { actualActions.push(action); }); effects = TestBed.inject(MetricsEffects); diff --git a/tensorboard/webapp/metrics/views/BUILD b/tensorboard/webapp/metrics/views/BUILD index 5544836ce1..0812e35767 100644 --- a/tensorboard/webapp/metrics/views/BUILD +++ b/tensorboard/webapp/metrics/views/BUILD @@ -1,19 +1,21 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_sass_library", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary", "sass_library") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_library( +sass_library( name = "metrics_common_styles", srcs = [ "_common.scss", ], ) -tf_sass_binary( +sass_binary( name = "metrics_container_styles", src = "metrics_container.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/webapp/metrics/views/card_renderer/BUILD b/tensorboard/webapp/metrics/views/card_renderer/BUILD index 288f052416..014159b6c4 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/BUILD +++ b/tensorboard/webapp/metrics/views/card_renderer/BUILD @@ -1,10 +1,12 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "card_view_styles", src = "card_view_container.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) @@ -39,9 +41,10 @@ tf_ng_module( ], ) -tf_sass_binary( +sass_binary( name = "data_download_dialog_styles", src = "data_download_dialog_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) @@ -76,9 +79,10 @@ tf_ng_module( ], ) -tf_sass_binary( +sass_binary( name = "histogram_card_styles", src = "histogram_card_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ "//tensorboard/webapp/metrics/views:metrics_common_styles", "//tensorboard/webapp/theme", @@ -124,11 +128,12 @@ tf_ng_module( ], ) -tf_sass_binary( +sass_binary( name = "image_card_styles", src = "image_card_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/metrics/views:metrics_common_styles", "//tensorboard/webapp/theme", ], @@ -171,9 +176,10 @@ tf_ng_module( ], ) -tf_sass_binary( +sass_binary( name = "run_name_styles", src = "run_name_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) @@ -200,11 +206,12 @@ tf_ng_module( ], ) -tf_sass_binary( +sass_binary( name = "vis_linked_time_selection_warning_styles", src = "vis_linked_time_selection_warning_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) @@ -251,24 +258,27 @@ tf_ts_library( ], ) -tf_sass_binary( +sass_binary( name = "scalar_card_styles", src = "scalar_card_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/metrics/views:metrics_common_styles", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "scalar_card_fob_controller_styles", src = "scalar_card_fob_controller.scss", + include_paths = ["external/npm/node_modules"], ) -tf_sass_binary( +sass_binary( name = "scalar_card_data_table_styles", src = "scalar_card_data_table.scss", + include_paths = ["external/npm/node_modules"], ) tf_ng_module( @@ -358,9 +368,10 @@ tf_ng_module( ], ) -tf_sass_binary( +sass_binary( name = "scalar_card_line_chart_styles", src = "scalar_card_line_chart_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) diff --git a/tensorboard/webapp/metrics/views/card_renderer/card_lazy_loader_test.ts b/tensorboard/webapp/metrics/views/card_renderer/card_lazy_loader_test.ts index 90b9f02090..d20ea095ed 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/card_lazy_loader_test.ts +++ b/tensorboard/webapp/metrics/views/card_renderer/card_lazy_loader_test.ts @@ -12,7 +12,12 @@ 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. ==============================================================================*/ -import {Component, Input, NO_ERRORS_SCHEMA} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + NO_ERRORS_SCHEMA, +} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; @@ -25,6 +30,7 @@ import {CardId} from '../../types'; import {CardLazyLoader, CardObserver} from '../card_renderer/card_lazy_loader'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'card-view', template: `{{ cardId }}`, @@ -39,6 +45,7 @@ interface TestableCardConfig { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-cards', template: ` diff --git a/tensorboard/webapp/metrics/views/card_renderer/card_view_test.ts b/tensorboard/webapp/metrics/views/card_renderer/card_view_test.ts index 323dfe5762..04de6e3c20 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/card_view_test.ts +++ b/tensorboard/webapp/metrics/views/card_renderer/card_view_test.ts @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ import { + ChangeDetectionStrategy, Component, EventEmitter, Input, @@ -35,6 +36,7 @@ import {CardViewComponent} from './card_view_component'; import {CardViewContainer} from './card_view_container'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'scalar-card', template: ``, diff --git a/tensorboard/webapp/metrics/views/card_renderer/histogram_card_test.ts b/tensorboard/webapp/metrics/views/card_renderer/histogram_card_test.ts index 0141459630..3daafd045c 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/histogram_card_test.ts +++ b/tensorboard/webapp/metrics/views/card_renderer/histogram_card_test.ts @@ -12,7 +12,13 @@ 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. ==============================================================================*/ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {By} from '@angular/platform-browser'; @@ -58,6 +64,7 @@ import {RunNameModule} from './run_name_module'; import {VisLinkedTimeSelectionWarningModule} from './vis_linked_time_selection_warning_module'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tb-histogram', template: ``, diff --git a/tensorboard/webapp/metrics/views/card_renderer/image_card_test.ts b/tensorboard/webapp/metrics/views/card_renderer/image_card_test.ts index 515330e2df..6b32b34bdc 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/image_card_test.ts +++ b/tensorboard/webapp/metrics/views/card_renderer/image_card_test.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {MatButtonModule} from '@angular/material/button'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; @@ -45,6 +45,7 @@ import {RunNameModule} from './run_name_module'; import {VisLinkedTimeSelectionWarningModule} from './vis_linked_time_selection_warning_module'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'card-view', template: ` diff --git a/tensorboard/webapp/metrics/views/card_renderer/scalar_card_line_chart_test.ts b/tensorboard/webapp/metrics/views/card_renderer/scalar_card_line_chart_test.ts index 21334debeb..3d65413b97 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/scalar_card_line_chart_test.ts +++ b/tensorboard/webapp/metrics/views/card_renderer/scalar_card_line_chart_test.ts @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, @@ -35,6 +36,7 @@ import {State} from '../../../app_state'; import {ExperimentAlias} from '../../../experiments/types'; import * as selectors from '../../../selectors'; import {MatIconTestingModule} from '../../../testing/mat_icon_module'; +import {provideMockTbStore} from '../../../testing/utils'; import {CardFobComponent} from '../../../widgets/card_fob/card_fob_component'; import { CardFobControllerComponent, @@ -54,6 +56,7 @@ import { relativeTimeFormatter, siNumberFormatter, } from '../../../widgets/line_chart_v2/lib/formatter'; +import {Extent} from '../../../widgets/line_chart_v2/lib/public_types'; import { DataSeries, DataSeriesMetadataMap, @@ -69,22 +72,21 @@ import { } from '../../actions'; import {getMetricsCardRangeSelectionEnabled} from '../../store'; import {TooltipSort, XAxisType} from '../../types'; +import {ScalarCardFobController} from './scalar_card_fob_controller'; import {ScalarCardLineChartComponent} from './scalar_card_line_chart_component'; import {ScalarCardLineChartContainer} from './scalar_card_line_chart_container'; -import {ScalarCardFobController} from './scalar_card_fob_controller'; import { MinMaxStep, OriginalSeriesMetadata, + ScalarCardDataSeries, ScalarCardPoint, ScalarCardSeriesMetadata, ScalarCardSeriesMetadataMap, - ScalarCardDataSeries, SeriesType, } from './scalar_card_types'; -import {Extent} from '../../../widgets/line_chart_v2/lib/public_types'; -import {provideMockTbStore} from '../../../testing/utils'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'line-chart', template: ` @@ -166,6 +168,7 @@ class TestableLineChart { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'test-scalar-card-line-chart', template: ` diff --git a/tensorboard/webapp/metrics/views/card_renderer/scalar_card_test.ts b/tensorboard/webapp/metrics/views/card_renderer/scalar_card_test.ts index b514bc64af..79c5b72dfd 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/scalar_card_test.ts +++ b/tensorboard/webapp/metrics/views/card_renderer/scalar_card_test.ts @@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ import {OverlayContainer} from '@angular/cdk/overlay'; import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, @@ -30,7 +31,7 @@ import { TestBed, tick, } from '@angular/core/testing'; -import {MatDialogModule, MAT_DIALOG_DATA} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogModule} from '@angular/material/dialog'; import {MatMenuModule} from '@angular/material/menu'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {By} from '@angular/platform-browser'; @@ -40,10 +41,16 @@ import {MockStore} from '@ngrx/store/testing'; import {Observable, of, ReplaySubject} from 'rxjs'; import {State} from '../../../app_state'; import {ExperimentAlias} from '../../../experiments/types'; +import * as hparamsActions from '../../../hparams/_redux/hparams_actions'; +import * as hparamsSelectors from '../../../hparams/_redux/hparams_selectors'; +import {HparamFilter} from '../../../hparams/_redux/types'; +import * as runsSelectors from '../../../runs/store/runs_selectors'; import {Run} from '../../../runs/store/runs_types'; import {buildRun} from '../../../runs/store/testing'; import * as selectors from '../../../selectors'; +import {getIsScalarColumnContextMenusEnabled} from '../../../selectors'; import {MatIconTestingModule} from '../../../testing/mat_icon_module'; +import {provideMockTbStore} from '../../../testing/utils'; import {DataLoadState} from '../../../types/data'; import {CardFobComponent} from '../../../widgets/card_fob/card_fob_component'; import { @@ -55,8 +62,23 @@ import { TimeSelectionAffordance, TimeSelectionToggleAffordance, } from '../../../widgets/card_fob/card_fob_types'; +import {ContentCellComponent} from '../../../widgets/data_table/content_cell_component'; +import {ContentRowComponent} from '../../../widgets/data_table/content_row_component'; import {DataTableComponent} from '../../../widgets/data_table/data_table_component'; import {DataTableModule} from '../../../widgets/data_table/data_table_module'; +import {HeaderCellComponent} from '../../../widgets/data_table/header_cell_component'; +import { + AddColumnEvent, + ColumnHeader, + ColumnHeaderType, + DataTableMode, + DomainType, + FilterAddedEvent, + IntervalFilter, + ReorderColumnEvent, + Side, + SortingOrder, +} from '../../../widgets/data_table/types'; import {ExperimentAliasModule} from '../../../widgets/experiment_alias/experiment_alias_module'; import {IntersectionObserverTestingModule} from '../../../widgets/intersection_observer/intersection_observer_testing_module'; import { @@ -64,6 +86,7 @@ import { relativeTimeFormatter, siNumberFormatter, } from '../../../widgets/line_chart_v2/lib/formatter'; +import {Extent} from '../../../widgets/line_chart_v2/lib/public_types'; import { DataSeries, DataSeriesMetadataMap, @@ -76,13 +99,13 @@ import {ResizeDetectorTestingModule} from '../../../widgets/resize_detector_test import {TruncatedPathModule} from '../../../widgets/text/truncated_path_module'; import { cardViewBoxChanged, + dataTableColumnOrderChanged, + dataTableColumnToggled, metricsCardFullSizeToggled, metricsCardStateUpdated, + metricsSlideoutMenuOpened, stepSelectorToggled, timeSelectionChanged, - metricsSlideoutMenuOpened, - dataTableColumnOrderChanged, - dataTableColumnToggled, } from '../../actions'; import {PluginType} from '../../data_source'; import { @@ -104,6 +127,7 @@ import { provideMockCardRunToSeriesData, } from '../../testing'; import {TooltipSort, XAxisType} from '../../types'; +import * as commonSelectors from '../main_view/common_selectors'; import {ScalarCardComponent} from './scalar_card_component'; import {ScalarCardContainer} from './scalar_card_container'; import {ScalarCardDataTable} from './scalar_card_data_table'; @@ -113,32 +137,10 @@ import { ScalarCardSeriesMetadata, SeriesType, } from './scalar_card_types'; -import { - AddColumnEvent, - ColumnHeader, - ColumnHeaderType, - DataTableMode, - DomainType, - FilterAddedEvent, - IntervalFilter, - ReorderColumnEvent, - Side, - SortingOrder, -} from '../../../widgets/data_table/types'; import {VisLinkedTimeSelectionWarningModule} from './vis_linked_time_selection_warning_module'; -import {Extent} from '../../../widgets/line_chart_v2/lib/public_types'; -import {provideMockTbStore} from '../../../testing/utils'; -import * as commonSelectors from '../main_view/common_selectors'; -import {ContentCellComponent} from '../../../widgets/data_table/content_cell_component'; -import {ContentRowComponent} from '../../../widgets/data_table/content_row_component'; -import {HeaderCellComponent} from '../../../widgets/data_table/header_cell_component'; -import {HparamFilter} from '../../../hparams/_redux/types'; -import * as hparamsSelectors from '../../../hparams/_redux/hparams_selectors'; -import * as hparamsActions from '../../../hparams/_redux/hparams_actions'; -import * as runsSelectors from '../../../runs/store/runs_selectors'; -import {getIsScalarColumnContextMenusEnabled} from '../../../selectors'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'line-chart', template: ` @@ -223,6 +225,7 @@ class TestableLineChart { // DataDownloadContainer pulls in entire redux and, for this test, we don't want to // know about their data requirements. @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-data-download-dialog', template: `{{ cardId }}`, diff --git a/tensorboard/webapp/metrics/views/main_view/BUILD b/tensorboard/webapp/metrics/views/main_view/BUILD index d06a786578..8a431f5c3d 100644 --- a/tensorboard/webapp/metrics/views/main_view/BUILD +++ b/tensorboard/webapp/metrics/views/main_view/BUILD @@ -1,66 +1,74 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "main_view_styles", src = "main_view_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/metrics/views:metrics_common_styles", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "filter_input_component_styles", src = "filter_input_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "filtered_view_component_styles", src = "filtered_view_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ "//tensorboard/webapp/metrics/views:metrics_common_styles", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "pinned_view_component_styles", src = "pinned_view_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/metrics/views:metrics_common_styles", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "card_grid_component_styles", src = "card_grid_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ "//tensorboard/webapp/metrics/views:metrics_common_styles", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "card_groups_component_styles", src = "card_groups_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ "//tensorboard/webapp/metrics/views:metrics_common_styles", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "card_group_toolbar_component_styles", src = "card_group_toolbar_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ "//tensorboard/webapp/metrics/views:metrics_common_styles", "//tensorboard/webapp/theme", diff --git a/tensorboard/webapp/metrics/views/main_view/card_grid_test.ts b/tensorboard/webapp/metrics/views/main_view/card_grid_test.ts index 5b01ed6bd0..23704a1e6c 100644 --- a/tensorboard/webapp/metrics/views/main_view/card_grid_test.ts +++ b/tensorboard/webapp/metrics/views/main_view/card_grid_test.ts @@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ import {ScrollingModule} from '@angular/cdk/scrolling'; import { + ChangeDetectionStrategy, Component, EventEmitter, Input, @@ -48,6 +49,7 @@ import {CardGridContainer} from './card_grid_container'; const scrollElementHeight = 100; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-scrolling-container', template: ` @@ -82,7 +84,11 @@ class TestableScrollingContainer { /** * Stub 'card-view' component for ease of testing. */ -@Component({standalone: false, selector: 'card-view'}) +@Component({ + changeDetection: ChangeDetectionStrategy.Default, + standalone: false, + selector: 'card-view', +}) class TestableCardView { @Output() fullHeightChanged = new EventEmitter(); @Output() fullWidthChanged = new EventEmitter(); diff --git a/tensorboard/webapp/metrics/views/main_view/main_view_component.ts b/tensorboard/webapp/metrics/views/main_view/main_view_component.ts index 1ac764b3ab..3e09ab87d1 100644 --- a/tensorboard/webapp/metrics/views/main_view/main_view_component.ts +++ b/tensorboard/webapp/metrics/views/main_view/main_view_component.ts @@ -17,18 +17,18 @@ import { Component, ElementRef, EventEmitter, - Input, Inject, + InjectionToken, + Input, + Optional, Output, Type, - Optional, - InjectionToken, } from '@angular/core'; import {PluginType} from '../../types'; import {CardObserver} from '../card_renderer/card_lazy_loader'; -export const SHARE_BUTTON_COMPONENT = new InjectionToken>( +export const SHARE_BUTTON_COMPONENT = new InjectionToken>( 'Customizable Share Button' ); @@ -62,7 +62,7 @@ export class MainViewComponent { private readonly host: ElementRef, @Optional() @Inject(SHARE_BUTTON_COMPONENT) - readonly customShareButton: Type + readonly customShareButton: Type ) { this.cardObserver = new CardObserver( this.host.nativeElement, diff --git a/tensorboard/webapp/metrics/views/main_view/main_view_test.ts b/tensorboard/webapp/metrics/views/main_view/main_view_test.ts index a871fae720..3229b0cec3 100644 --- a/tensorboard/webapp/metrics/views/main_view/main_view_test.ts +++ b/tensorboard/webapp/metrics/views/main_view/main_view_test.ts @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ import { + ChangeDetectionStrategy, Component, DebugElement, Input, @@ -39,6 +40,7 @@ import { } from '../../../selectors'; import {selectors as settingsSelectors} from '../../../settings'; import {KeyType, sendKey, sendKeys} from '../../../testing/dom'; +import {buildMockState} from '../../../testing/utils'; import {DataLoadState} from '../../../types/data'; import {RunColorScale} from '../../../types/ui'; import * as actions from '../../actions'; @@ -53,25 +55,25 @@ import {CardLazyLoader, CardObserver} from '../card_renderer/card_lazy_loader'; import {CardIdWithMetadata} from '../metrics_view_types'; import {CardGridComponent} from './card_grid_component'; import {CardGridContainer} from './card_grid_container'; -import {CardGroupsComponent} from './card_groups_component'; -import {CardGroupsContainer} from './card_groups_container'; import {CardGroupToolBarComponent} from './card_group_toolbar_component'; import {CardGroupToolBarContainer} from './card_group_toolbar_container'; +import {CardGroupsComponent} from './card_groups_component'; +import {CardGroupsContainer} from './card_groups_container'; import * as common_selectors from './common_selectors'; import {EmptyTagMatchMessageComponent} from './empty_tag_match_message_component'; import {EmptyTagMatchMessageContainer} from './empty_tag_match_message_container'; import {FilteredViewComponent} from './filtered_view_component'; import { - FilteredViewContainer, FILTER_VIEW_DEBOUNCE_IN_MS, + FilteredViewContainer, } from './filtered_view_container'; import {MainViewComponent, SHARE_BUTTON_COMPONENT} from './main_view_component'; import {MainViewContainer} from './main_view_container'; import {PinnedViewComponent} from './pinned_view_component'; import {PinnedViewContainer} from './pinned_view_container'; -import {buildMockState} from '../../../testing/utils'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'card-view', template: `{{ pluginType }}: {{ cardId }}`, @@ -83,6 +85,7 @@ class TestableCard { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'test-share-button', template: ``, diff --git a/tensorboard/webapp/metrics/views/right_pane/BUILD b/tensorboard/webapp/metrics/views/right_pane/BUILD index 2c6c21901e..82fc761be1 100644 --- a/tensorboard/webapp/metrics/views/right_pane/BUILD +++ b/tensorboard/webapp/metrics/views/right_pane/BUILD @@ -1,21 +1,24 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "settings_view_styles", src = "settings_view_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "saving_pins_checkbox_styles", src = "saving_pins_checkbox_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/webapp/metrics/views/right_pane/saving_pins_dialog/BUILD b/tensorboard/webapp/metrics/views/right_pane/saving_pins_dialog/BUILD index 540bcff6a8..b2aa6e283a 100644 --- a/tensorboard/webapp/metrics/views/right_pane/saving_pins_dialog/BUILD +++ b/tensorboard/webapp/metrics/views/right_pane/saving_pins_dialog/BUILD @@ -1,10 +1,12 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "saving_pins_dialog_component_styles", src = "saving_pins_dialog_component.scss", + include_paths = ["external/npm/node_modules"], ) tf_ng_module( diff --git a/tensorboard/webapp/metrics/views/right_pane/scalar_column_editor/BUILD b/tensorboard/webapp/metrics/views/right_pane/scalar_column_editor/BUILD index 66b5b13e31..f249161957 100644 --- a/tensorboard/webapp/metrics/views/right_pane/scalar_column_editor/BUILD +++ b/tensorboard/webapp/metrics/views/right_pane/scalar_column_editor/BUILD @@ -1,12 +1,14 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "scalar_column_editor_styles", src = "scalar_column_editor_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/webapp/metrics/views/right_pane/scalar_column_editor/scalar_column_editor_test.ts b/tensorboard/webapp/metrics/views/right_pane/scalar_column_editor/scalar_column_editor_test.ts index d8c781b78d..287c746606 100644 --- a/tensorboard/webapp/metrics/views/right_pane/scalar_column_editor/scalar_column_editor_test.ts +++ b/tensorboard/webapp/metrics/views/right_pane/scalar_column_editor/scalar_column_editor_test.ts @@ -236,9 +236,12 @@ describe('scalar column editor', () => { let dispatchedActions: Action[] = []; beforeEach(() => { dispatchedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { - dispatchedActions.push(action); - }); + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake( + (action: Action) => { + dispatchedActions.push(action); + } + ); }); it('dispatches dataTableColumnToggled action with singe selection when checkbox is clicked', fakeAsync(() => { @@ -320,9 +323,12 @@ describe('scalar column editor', () => { let dispatchedActions: Action[] = []; beforeEach(() => { dispatchedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { - dispatchedActions.push(action); - }); + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake( + (action: Action) => { + dispatchedActions.push(action); + } + ); }); it('dispatches dataTableColumnOrderChanged action with single selection when header is dragged', fakeAsync(() => { @@ -508,9 +514,12 @@ describe('scalar column editor', () => { let dispatchedActions: Action[] = []; beforeEach(() => { dispatchedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { - dispatchedActions.push(action); - }); + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake( + (action: Action) => { + dispatchedActions.push(action); + } + ); }); it('dispatches metricsSlideoutMenuClosed', () => { const fixture = createComponent(); @@ -527,9 +536,12 @@ describe('scalar column editor', () => { let dispatchedActions: Action[] = []; beforeEach(() => { dispatchedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { - dispatchedActions.push(action); - }); + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake( + (action: Action) => { + dispatchedActions.push(action); + } + ); }); it('dispatches tableEditorTabChanged action when tab is clicked', fakeAsync(() => { const fixture = createComponent(); diff --git a/tensorboard/webapp/notification_center/_redux/notification_center_effects_test.ts b/tensorboard/webapp/notification_center/_redux/notification_center_effects_test.ts index 5c0c3d1b5f..a468ca77c0 100644 --- a/tensorboard/webapp/notification_center/_redux/notification_center_effects_test.ts +++ b/tensorboard/webapp/notification_center/_redux/notification_center_effects_test.ts @@ -60,7 +60,8 @@ describe('notification center effects', () => { }).compileComponents(); store = TestBed.inject>(Store) as MockStore; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { actualActions.push(action); }); effects = TestBed.inject(NotificationCenterEffects); @@ -96,7 +97,7 @@ describe('notification center effects', () => { it('dispatches failed action when notification fetch failed', () => { fetchNotificationsSpy.and.returnValue( - throwError(new Error('Request failed')) + throwError(() => new Error('Request failed')) ); actions$.next(TEST_ONLY.initAction()); diff --git a/tensorboard/webapp/notification_center/_views/BUILD b/tensorboard/webapp/notification_center/_views/BUILD index da3a90082e..d8b4fe2ad6 100644 --- a/tensorboard/webapp/notification_center/_views/BUILD +++ b/tensorboard/webapp/notification_center/_views/BUILD @@ -1,14 +1,16 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard/webapp/notification_center:__subpackages__"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "notification_center_styles", src = "notification_center_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/webapp/notification_center/_views/notification_center_component.ts b/tensorboard/webapp/notification_center/_views/notification_center_component.ts index 093fd48ddf..7585485be9 100644 --- a/tensorboard/webapp/notification_center/_views/notification_center_component.ts +++ b/tensorboard/webapp/notification_center/_views/notification_center_component.ts @@ -12,11 +12,18 @@ 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. ==============================================================================*/ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; import {CategoryEnum} from '../_redux/notification_center_types'; import {ViewNotificationExt} from './view_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'notification-center-component', templateUrl: './notification_center_component.ng.html', diff --git a/tensorboard/webapp/notification_center/_views/notification_center_container.ts b/tensorboard/webapp/notification_center/_views/notification_center_container.ts index 2d8bbb25a1..94fb9ee783 100644 --- a/tensorboard/webapp/notification_center/_views/notification_center_container.ts +++ b/tensorboard/webapp/notification_center/_views/notification_center_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {Store} from '@ngrx/store'; import {combineLatest, Observable} from 'rxjs'; import {map, shareReplay} from 'rxjs/operators'; @@ -28,6 +28,7 @@ import {ViewNotificationExt} from './view_types'; const iconMap = new Map([[CategoryEnum.WHATS_NEW, 'info_outline_24px']]); @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'notification-center', template: ` diff --git a/tensorboard/webapp/notification_center/_views/notification_center_test.ts b/tensorboard/webapp/notification_center/_views/notification_center_test.ts index c8277479e9..adb4369c44 100644 --- a/tensorboard/webapp/notification_center/_views/notification_center_test.ts +++ b/tensorboard/webapp/notification_center/_views/notification_center_test.ts @@ -52,7 +52,8 @@ describe('notification center', () => { }).compileComponents(); store = TestBed.inject>(Store) as MockStore; recordedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { recordedActions.push(action); }); store.overrideSelector(selectors.getNotifications, []); diff --git a/tensorboard/webapp/persistent_settings/_redux/persistent_settings_effects_test.ts b/tensorboard/webapp/persistent_settings/_redux/persistent_settings_effects_test.ts index ea6c6eb10e..46d490eb4f 100644 --- a/tensorboard/webapp/persistent_settings/_redux/persistent_settings_effects_test.ts +++ b/tensorboard/webapp/persistent_settings/_redux/persistent_settings_effects_test.ts @@ -78,9 +78,12 @@ describe('persistent_settings effects test', () => { }).compileComponents(); store = TestBed.inject>(Store) as MockStore; - dispatchSpy = spyOn(store, 'dispatch').and.callFake((action: Action) => { - actualActions.push(action); - }); + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + dispatchSpy = (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake( + (action: Action) => { + actualActions.push(action); + } + ); effects = TestBed.inject(PersistentSettingsEffects); const dataSource = TestBed.inject(PersistentSettingsTestingDataSource); getSettingsSpy = spyOn(dataSource, 'getSettings').and.returnValue(of({})); diff --git a/tensorboard/webapp/plugins/BUILD b/tensorboard/webapp/plugins/BUILD index ef34c03baf..00e30c9682 100644 --- a/tensorboard/webapp/plugins/BUILD +++ b/tensorboard/webapp/plugins/BUILD @@ -1,12 +1,14 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "plugins_component_styles", src = "plugins_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) diff --git a/tensorboard/webapp/plugins/plugin_registry_module.ts b/tensorboard/webapp/plugins/plugin_registry_module.ts index f9ce7850da..f7901ae7ac 100644 --- a/tensorboard/webapp/plugins/plugin_registry_module.ts +++ b/tensorboard/webapp/plugins/plugin_registry_module.ts @@ -13,16 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ import { - Component, Inject, ModuleWithProviders, NgModule, Optional, Type, } from '@angular/core'; -import {PluginConfig, PLUGIN_CONFIG_TOKEN} from './plugin_registry_types'; +import {PLUGIN_CONFIG_TOKEN, PluginConfig} from './plugin_registry_types'; -const pluginNameToComponent = new Map>(); +const pluginNameToComponent = new Map>(); @NgModule({}) export class PluginRegistryModule { @@ -40,7 +39,7 @@ export class PluginRegistryModule { for (const config of configs) { const {pluginName, componentClass} = config; - pluginNameToComponent.set(pluginName, componentClass as Type); + pluginNameToComponent.set(pluginName, componentClass as Type); } } @@ -71,7 +70,7 @@ export class PluginRegistryModule { }; } - getComponent(pluginName: string): Type | null { + getComponent(pluginName: string): Type | null { return pluginNameToComponent.get(pluginName) || null; } } diff --git a/tensorboard/webapp/plugins/plugins_component.ts b/tensorboard/webapp/plugins/plugins_component.ts index 4f40d1d58b..d697fdc24a 100644 --- a/tensorboard/webapp/plugins/plugins_component.ts +++ b/tensorboard/webapp/plugins/plugins_component.ts @@ -37,8 +37,8 @@ import { LoadingMechanismType, } from '../types/api'; import {DataLoadState} from '../types/data'; -import {UiPluginMetadata} from './plugins_container'; import {PluginRegistryModule} from './plugin_registry_module'; +import {UiPluginMetadata} from './plugins_container'; interface PolymerDashboard extends HTMLElement { reload?: () => void; diff --git a/tensorboard/webapp/plugins/plugins_container_test.ts b/tensorboard/webapp/plugins/plugins_container_test.ts index bbbe4172ee..9c588d02e3 100644 --- a/tensorboard/webapp/plugins/plugins_container_test.ts +++ b/tensorboard/webapp/plugins/plugins_container_test.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {Store} from '@ngrx/store'; @@ -47,9 +47,9 @@ import { PluginId, } from '../types/api'; import {DataLoadState} from '../types/data'; +import {PluginRegistryModule} from './plugin_registry_module'; import {PluginsComponent} from './plugins_component'; import {PluginsContainer} from './plugins_container'; -import {PluginRegistryModule} from './plugin_registry_module'; import {ExtraDashboardModule} from './testing'; function expectPluginIframe(element: HTMLElement, name: string) { @@ -64,6 +64,7 @@ function expectPluginIframe(element: HTMLElement, name: string) { * the `plugins` component. */ @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, template: ` diff --git a/tensorboard/webapp/plugins/testing/index.ts b/tensorboard/webapp/plugins/testing/index.ts index 78807d8a61..f6cd198081 100644 --- a/tensorboard/webapp/plugins/testing/index.ts +++ b/tensorboard/webapp/plugins/testing/index.ts @@ -12,10 +12,11 @@ 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. ==============================================================================*/ -import {Component, NgModule} from '@angular/core'; +import {ChangeDetectionStrategy, Component, NgModule} from '@angular/core'; import {PluginRegistryModule} from '../plugin_registry_module'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'extra-dashboard', template: `
I'm the extra Angular dashboard!
`, diff --git a/tensorboard/webapp/routes/index.ts b/tensorboard/webapp/routes/index.ts index 4ee5ad1941..dfa03d2cfb 100644 --- a/tensorboard/webapp/routes/index.ts +++ b/tensorboard/webapp/routes/index.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component, Type} from '@angular/core'; +import {Type} from '@angular/core'; import {RouteDef} from '../app_routing/route_config_types'; import {RouteKind} from '../app_routing/types'; import {TensorBoardWrapperComponent} from '../tb_wrapper/tb_wrapper_component'; @@ -23,7 +23,7 @@ export function routesFactory(): RouteDef[] { { routeKind: RouteKind.EXPERIMENT, path: '/', - ngComponent: TensorBoardWrapperComponent as Type, + ngComponent: TensorBoardWrapperComponent as Type, defaultRoute: true, deepLinkProvider: new DashboardDeepLinkProvider(), }, diff --git a/tensorboard/webapp/runs/effects/runs_effects_test.ts b/tensorboard/webapp/runs/effects/runs_effects_test.ts index fccad4c6ba..1971cfe034 100644 --- a/tensorboard/webapp/runs/effects/runs_effects_test.ts +++ b/tensorboard/webapp/runs/effects/runs_effects_test.ts @@ -91,7 +91,8 @@ describe('runs_effects', () => { selectSpy = spyOn(store, 'select').and.callThrough(); actualActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { actualActions.push(action); }); effects = TestBed.inject(RunsEffects); diff --git a/tensorboard/webapp/runs/views/runs_table/BUILD b/tensorboard/webapp/runs/views/runs_table/BUILD index 75f47fe218..fb6ec4fcb8 100644 --- a/tensorboard/webapp/runs/views/runs_table/BUILD +++ b/tensorboard/webapp/runs/views/runs_table/BUILD @@ -1,35 +1,40 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "runs_group_menu_button_styles", src = "runs_group_menu_button_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "regex_edit_dialog_styles", src = "regex_edit_dialog_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) -tf_sass_binary( +sass_binary( name = "filterbar_styles", src = "filterbar_component.scss", + include_paths = ["external/npm/node_modules"], deps = ["//tensorboard/webapp/theme"], ) -tf_sass_binary( +sass_binary( name = "runs_data_table_styles", src = "runs_data_table.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/webapp/runs/views/runs_table/filterbar_test.ts b/tensorboard/webapp/runs/views/runs_table/filterbar_test.ts index c4b97d348d..0998b131bb 100644 --- a/tensorboard/webapp/runs/views/runs_table/filterbar_test.ts +++ b/tensorboard/webapp/runs/views/runs_table/filterbar_test.ts @@ -12,32 +12,36 @@ 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. ==============================================================================*/ +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import { + ChangeDetectionStrategy, + Component, + NO_ERRORS_SCHEMA, +} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {Component, NO_ERRORS_SCHEMA} from '@angular/core'; -import {FilterbarComponent} from './filterbar_component'; -import {FilterbarContainer} from './filterbar_container'; +import {MatChipRemove, MatChipsModule} from '@angular/material/chips'; +import {MatChipHarness} from '@angular/material/chips/testing'; +import {By} from '@angular/platform-browser'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {provideMockTbStore} from '../../../testing/utils'; +import {Action, Store} from '@ngrx/store'; import {MockStore} from '@ngrx/store/testing'; import {State} from '../../../app_state'; -import {Action, Store} from '@ngrx/store'; -import {By} from '@angular/platform-browser'; import { actions as hparamsActions, selectors as hparamsSelectors, } from '../../../hparams'; +import {MatIconTestingModule} from '../../../testing/mat_icon_module'; +import {provideMockTbStore} from '../../../testing/utils'; +import {CustomModal} from '../../../widgets/custom_modal/custom_modal'; +import {FilterDialog} from '../../../widgets/data_table/filter_dialog_component'; +import {FilterDialogModule} from '../../../widgets/data_table/filter_dialog_module'; import { + DiscreteFilter, DomainType, IntervalFilter, - DiscreteFilter, } from '../../../widgets/data_table/types'; -import {MatChipHarness} from '@angular/material/chips/testing'; -import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; -import {MatChipRemove, MatChipsModule} from '@angular/material/chips'; -import {MatIconTestingModule} from '../../../testing/mat_icon_module'; -import {FilterDialogModule} from '../../../widgets/data_table/filter_dialog_module'; -import {FilterDialog} from '../../../widgets/data_table/filter_dialog_component'; -import {CustomModal} from '../../../widgets/custom_modal/custom_modal'; +import {FilterbarComponent} from './filterbar_component'; +import {FilterbarContainer} from './filterbar_container'; const discreteFilter: DiscreteFilter = { type: DomainType.DISCRETE, @@ -61,6 +65,7 @@ const fakeFilterMap = new Map([ ]); @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-component', template: ` `, @@ -95,9 +100,12 @@ describe('hparam_filterbar', () => { function createComponent(): ComponentFixture { store = TestBed.inject>(Store) as MockStore; actualActions = []; - dispatchSpy = spyOn(store, 'dispatch').and.callFake((action: Action) => { - actualActions.push(action); - }); + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + dispatchSpy = (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake( + (action: Action) => { + actualActions.push(action); + } + ); const fixture = TestBed.createComponent(TestableComponent); return fixture; diff --git a/tensorboard/webapp/runs/views/runs_table/regex_edit_dialog_container.ts b/tensorboard/webapp/runs/views/runs_table/regex_edit_dialog_container.ts index eb267417c7..d0eb31bb0e 100644 --- a/tensorboard/webapp/runs/views/runs_table/regex_edit_dialog_container.ts +++ b/tensorboard/webapp/runs/views/runs_table/regex_edit_dialog_container.ts @@ -12,8 +12,8 @@ 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. ==============================================================================*/ -import {Component, inject} from '@angular/core'; -import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; +import {ChangeDetectionStrategy, Component, inject} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {Store} from '@ngrx/store'; import {combineLatest, defer, merge, Observable, Subject} from 'rxjs'; import { @@ -28,17 +28,17 @@ import { import {State} from '../../../app_state'; import { getDarkModeEnabled, + getDashboardExperimentNames, getEnableColorByExperiment, } from '../../../selectors'; import {selectors as settingsSelectors} from '../../../settings/'; import {runGroupByChanged} from '../../actions'; import { getColorGroupRegexString, - getRunIdsForExperiment, getRunGroupBy, + getRunIdsForExperiment, getRuns, } from '../../store/runs_selectors'; -import {getDashboardExperimentNames} from '../../../selectors'; import {groupRuns} from '../../store/utils'; import {GroupByKey, Run} from '../../types'; import {ColorGroup} from './regex_edit_dialog_component'; @@ -46,6 +46,7 @@ import {ColorGroup} from './regex_edit_dialog_component'; const INPUT_CHANGE_DEBOUNCE_INTERVAL_MS = 500; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'regex-edit-dialog', template: ` { }); store.overrideSelector(getEnableColorByExperiment, true); actualActions = []; - dispatchSpy = spyOn(store, 'dispatch').and.callFake((action: Action) => { - actualActions.push(action); - }); + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + dispatchSpy = (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake( + (action: Action) => { + actualActions.push(action); + } + ); return TestBed.createComponent(RegexEditDialogContainer); } diff --git a/tensorboard/webapp/runs/views/runs_table/runs_data_table_test.ts b/tensorboard/webapp/runs/views/runs_table/runs_data_table_test.ts index b266c1dce8..0c778530db 100644 --- a/tensorboard/webapp/runs/views/runs_table/runs_data_table_test.ts +++ b/tensorboard/webapp/runs/views/runs_table/runs_data_table_test.ts @@ -13,27 +13,34 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, Input, NO_ERRORS_SCHEMA, ViewChild} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + NO_ERRORS_SCHEMA, + ViewChild, +} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {RunsDataTable} from './runs_data_table'; -import {DataTableModule} from '../../../widgets/data_table/data_table_module'; -import {MatIconTestingModule} from '../../../testing/mat_icon_module'; import {MatCheckboxModule} from '@angular/material/checkbox'; +import {By} from '@angular/platform-browser'; +import {sendKeys} from '../../../testing/dom'; +import {MatIconTestingModule} from '../../../testing/mat_icon_module'; +import {ContentCellComponent} from '../../../widgets/data_table/content_cell_component'; +import {DataTableComponent} from '../../../widgets/data_table/data_table_component'; +import {DataTableModule} from '../../../widgets/data_table/data_table_module'; +import {HeaderCellComponent} from '../../../widgets/data_table/header_cell_component'; import { - SortingOrder, - SortingInfo, - TableData, ColumnHeader, ColumnHeaderType, + SortingInfo, + SortingOrder, + TableData, } from '../../../widgets/data_table/types'; -import {By} from '@angular/platform-browser'; -import {HeaderCellComponent} from '../../../widgets/data_table/header_cell_component'; -import {DataTableComponent} from '../../../widgets/data_table/data_table_component'; -import {ContentCellComponent} from '../../../widgets/data_table/content_cell_component'; import {FilterInputModule} from '../../../widgets/filter_input/filter_input_module'; -import {sendKeys} from '../../../testing/dom'; +import {RunsDataTable} from './runs_data_table'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-comp', template: ` diff --git a/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_component.ts b/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_component.ts index abbf82ee04..c9c61f3f1f 100644 --- a/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_component.ts +++ b/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_component.ts @@ -14,6 +14,7 @@ limitations under the License. ==============================================================================*/ import { AfterViewInit, + ChangeDetectionStrategy, Component, ElementRef, EventEmitter, @@ -26,6 +27,7 @@ interface PolymerChangeEvent extends CustomEvent { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tb-legacy-runs-selector-component', template: ` `, diff --git a/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_container.ts b/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_container.ts index 05fd8d92ec..fc3b0436fb 100644 --- a/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_container.ts +++ b/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_container.ts @@ -12,12 +12,13 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {Store} from '@ngrx/store'; import {State} from '../../../app_state'; import {polymerInteropRunSelectionChanged} from '../../../core/actions'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tb-legacy-runs-selector', template: ` diff --git a/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_test.ts b/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_test.ts index c5cfb870fb..64ecc144df 100644 --- a/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_test.ts +++ b/tensorboard/webapp/runs_legacy/views/legacy_runs_selector/legacy_runs_selector_test.ts @@ -43,7 +43,8 @@ describe('legacy_runs_selector test', () => { store = TestBed.inject>(Store) as MockStore; recordedActions = []; - spyOn(store, 'dispatch').and.callFake((action: Action) => { + // Cast to jasmine.Spy for compatibility between NgRx dispatch signature overloads. + (spyOn(store, 'dispatch') as jasmine.Spy).and.callFake((action: Action) => { recordedActions.push(action); }); diff --git a/tensorboard/webapp/settings/_views/settings_button_component.ts b/tensorboard/webapp/settings/_views/settings_button_component.ts index 4e65ce9732..3a56cf3529 100644 --- a/tensorboard/webapp/settings/_views/settings_button_component.ts +++ b/tensorboard/webapp/settings/_views/settings_button_component.ts @@ -12,12 +12,13 @@ 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. ==============================================================================*/ -import {Component, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {DataLoadState} from '../../types/data'; import {SettingsDialogContainer} from './settings_dialog_container'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'settings-button-component', template: ` diff --git a/tensorboard/webapp/settings/_views/settings_button_container.ts b/tensorboard/webapp/settings/_views/settings_button_container.ts index 83ec77f7be..b938b0ad20 100644 --- a/tensorboard/webapp/settings/_views/settings_button_container.ts +++ b/tensorboard/webapp/settings/_views/settings_button_container.ts @@ -12,12 +12,13 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {Store} from '@ngrx/store'; import {getSettingsLoadState} from '../_redux/settings_selectors'; import {State} from '../_redux/settings_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'settings-button', template: ` diff --git a/tensorboard/webapp/settings/_views/settings_dialog_component.ts b/tensorboard/webapp/settings/_views/settings_dialog_component.ts index 86f0504347..4b97c0da11 100644 --- a/tensorboard/webapp/settings/_views/settings_dialog_component.ts +++ b/tensorboard/webapp/settings/_views/settings_dialog_component.ts @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ import { + ChangeDetectionStrategy, Component, EventEmitter, Input, @@ -41,6 +42,7 @@ export function createIntegerValidator(): ValidatorFn { } @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'settings-dialog-component', templateUrl: 'settings_dialog_component.ng.html', diff --git a/tensorboard/webapp/settings/_views/settings_dialog_container.ts b/tensorboard/webapp/settings/_views/settings_dialog_container.ts index 85ea32cf80..6ebd189c88 100644 --- a/tensorboard/webapp/settings/_views/settings_dialog_container.ts +++ b/tensorboard/webapp/settings/_views/settings_dialog_container.ts @@ -12,7 +12,7 @@ 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. ==============================================================================*/ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {Store} from '@ngrx/store'; import { changePageSize, @@ -27,6 +27,7 @@ import { import {State} from '../_redux/settings_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'settings-dialog', template: ` diff --git a/tensorboard/webapp/testing/integration_test_module.ts b/tensorboard/webapp/testing/integration_test_module.ts index 3aca9baff3..585403d7fd 100644 --- a/tensorboard/webapp/testing/integration_test_module.ts +++ b/tensorboard/webapp/testing/integration_test_module.ts @@ -17,7 +17,7 @@ limitations under the License. * * This module does not facilitate any screenshot testing. */ -import {Component, NgModule} from '@angular/core'; +import {ChangeDetectionStrategy, Component, NgModule} from '@angular/core'; import {EffectsModule as NgrxEffectsModule} from '@ngrx/effects'; import {StoreModule as NgrxStoreModule} from '@ngrx/store'; import {AppRoutingModule} from '../app_routing/app_routing_module'; @@ -31,6 +31,7 @@ import {RunsModule} from '../runs/runs_module'; import {MatIconTestingModule} from './mat_icon_module'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'test', template: 'hello', diff --git a/tensorboard/webapp/testing/mat_icon_module.ts b/tensorboard/webapp/testing/mat_icon_module.ts index c37a7f3626..caa12707c9 100644 --- a/tensorboard/webapp/testing/mat_icon_module.ts +++ b/tensorboard/webapp/testing/mat_icon_module.ts @@ -12,7 +12,12 @@ 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. ==============================================================================*/ -import {Component, Input, NgModule} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + NgModule, +} from '@angular/core'; import {MatIconRegistry} from '@angular/material/icon'; import {FakeMatIconRegistry} from '@angular/material/icon/testing'; @@ -74,6 +79,7 @@ const KNOWN_SVG_ICON = new Set([ * compilation time due to unknown input onto the template. */ @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, template: '{{svgIcon}}', selector: 'mat-icon', diff --git a/tensorboard/webapp/theme/BUILD b/tensorboard/webapp/theme/BUILD index 6afa168248..bb07a8ee7f 100644 --- a/tensorboard/webapp/theme/BUILD +++ b/tensorboard/webapp/theme/BUILD @@ -1,17 +1,17 @@ -load("//tensorboard/defs:defs.bzl", "tf_sass_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_library( +sass_library( name = "theme", srcs = [ "_tb_palette.scss", "_tb_theme.scss", ], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", ], ) diff --git a/tensorboard/webapp/theme/_tb_theme.template.scss b/tensorboard/webapp/theme/_tb_theme.template.scss index dcfd5327f4..b89d269a23 100644 --- a/tensorboard/webapp/theme/_tb_theme.template.scss +++ b/tensorboard/webapp/theme/_tb_theme.template.scss @@ -271,15 +271,10 @@ $tb-dark-theme: map_merge( // Apply themed style for the global stylesheet (styles.scss). @mixin tb-global-themed-styles() { - // TODO(JamesHollyer): remove legacy component themes once all components - // are migrated. @include mat.core(); - @include mat.all-legacy-component-typographies(); @include mat.all-component-typographies(); // Include all theme-styles for the components based on the current theme. @include mat.all-component-themes($tb-theme); - @include mat.all-legacy-component-themes($tb-theme); - @include mat.all-component-themes($tb-theme); body { // Prevents ngx-color-picker from creating a scrollbar and misposition. @@ -370,9 +365,6 @@ $tb-dark-theme: map_merge( } } - // TODO(JamesHollyer): remove legacy component themes once all components - // are migrated. @include mat.all-component-themes($tb-dark-theme); - @include mat.all-legacy-component-themes($tb-dark-theme); } } diff --git a/tensorboard/webapp/widgets/card_fob/BUILD b/tensorboard/webapp/widgets/card_fob/BUILD index b4a848ff6a..0308842955 100644 --- a/tensorboard/webapp/widgets/card_fob/BUILD +++ b/tensorboard/webapp/widgets/card_fob/BUILD @@ -1,19 +1,22 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "card_fob_styles", src = "card_fob_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "card_fob_controller_styles", src = "card_fob_controller_component.scss", + include_paths = ["external/npm/node_modules"], ) tf_ng_module( diff --git a/tensorboard/webapp/widgets/card_fob/card_fob_controller_test.ts b/tensorboard/webapp/widgets/card_fob/card_fob_controller_test.ts index 061cd09504..56eceb304c 100644 --- a/tensorboard/webapp/widgets/card_fob/card_fob_controller_test.ts +++ b/tensorboard/webapp/widgets/card_fob/card_fob_controller_test.ts @@ -13,7 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, Input, NO_ERRORS_SCHEMA, ViewChild} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + NO_ERRORS_SCHEMA, + ViewChild, +} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {CardFobComponent} from './card_fob_component'; @@ -27,6 +33,7 @@ import { } from './card_fob_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-comp', template: ` diff --git a/tensorboard/webapp/widgets/card_fob/card_fob_test.ts b/tensorboard/webapp/widgets/card_fob/card_fob_test.ts index 30b7ee2a03..581cd6b82a 100644 --- a/tensorboard/webapp/widgets/card_fob/card_fob_test.ts +++ b/tensorboard/webapp/widgets/card_fob/card_fob_test.ts @@ -13,13 +13,20 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, Input, NO_ERRORS_SCHEMA, ViewChild} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + NO_ERRORS_SCHEMA, + ViewChild, +} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {CardFobComponent} from './card_fob_component'; import {AxisDirection} from './card_fob_types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-fob-comp', template: ` diff --git a/tensorboard/webapp/widgets/content_wrapping_input/BUILD b/tensorboard/webapp/widgets/content_wrapping_input/BUILD index a5dc764026..4abf3e991f 100644 --- a/tensorboard/webapp/widgets/content_wrapping_input/BUILD +++ b/tensorboard/webapp/widgets/content_wrapping_input/BUILD @@ -1,12 +1,14 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "content_wrapping_input_component_styles", src = "content_wrapping_input_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/webapp/widgets/content_wrapping_input/content_wrapping_input_test.ts b/tensorboard/webapp/widgets/content_wrapping_input/content_wrapping_input_test.ts index a47deaf39d..f25f60040e 100644 --- a/tensorboard/webapp/widgets/content_wrapping_input/content_wrapping_input_test.ts +++ b/tensorboard/webapp/widgets/content_wrapping_input/content_wrapping_input_test.ts @@ -13,13 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {KeyType, sendKey} from '../../testing/dom'; import {ContentWrappingInputComponent} from './content_wrapping_input_component'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testing-component', template: ` diff --git a/tensorboard/webapp/widgets/custom_modal/custom_modal_test.ts b/tensorboard/webapp/widgets/custom_modal/custom_modal_test.ts index 9326e2299f..011892a013 100644 --- a/tensorboard/webapp/widgets/custom_modal/custom_modal_test.ts +++ b/tensorboard/webapp/widgets/custom_modal/custom_modal_test.ts @@ -12,25 +12,27 @@ 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. ==============================================================================*/ -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing'; -import {By} from '@angular/platform-browser'; -import {CustomModal} from './custom_modal'; +import {Overlay, OverlayModule, OverlayRef} from '@angular/cdk/overlay'; import {CommonModule} from '@angular/common'; import { + ChangeDetectionStrategy, Component, TemplateRef, ViewChild, ViewContainerRef, } from '@angular/core'; -import {Overlay, OverlayModule, OverlayRef} from '@angular/cdk/overlay'; +import { + ComponentFixture, + TestBed, + fakeAsync, + tick, +} from '@angular/core/testing'; +import {By} from '@angular/platform-browser'; import {first} from 'rxjs/operators'; +import {CustomModal} from './custom_modal'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'fake-modal-view-container', template: ` diff --git a/tensorboard/webapp/widgets/data_table/BUILD b/tensorboard/webapp/widgets/data_table/BUILD index be4d4863d2..fff1442995 100644 --- a/tensorboard/webapp/widgets/data_table/BUILD +++ b/tensorboard/webapp/widgets/data_table/BUILD @@ -1,58 +1,66 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) -tf_sass_binary( +sass_binary( name = "data_table_styles", src = "data_table_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "header_cell_styles", src = "header_cell_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "content_cell_styles", src = "content_cell_component.scss", + include_paths = ["external/npm/node_modules"], ) -tf_sass_binary( +sass_binary( name = "data_table_header_styles", src = "data_table_header_component.scss", + include_paths = ["external/npm/node_modules"], ) -tf_sass_binary( +sass_binary( name = "column_selector_component_styles", src = "column_selector_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "context_menu_component_styles", src = "context_menu_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) -tf_sass_binary( +sass_binary( name = "filter_dialog_styles", src = "filter_dialog_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/webapp/widgets/data_table/content_cell_component_test.ts b/tensorboard/webapp/widgets/data_table/content_cell_component_test.ts index 93b48e2ba7..36f2303a69 100644 --- a/tensorboard/webapp/widgets/data_table/content_cell_component_test.ts +++ b/tensorboard/webapp/widgets/data_table/content_cell_component_test.ts @@ -13,15 +13,21 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, Input, ViewChild} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + ViewChild, +} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {MatIconTestingModule} from '../../testing/mat_icon_module'; import {By} from '@angular/platform-browser'; -import {ColumnHeader, ColumnHeaderType} from './types'; -import {DataTableModule} from './data_table_module'; +import {MatIconTestingModule} from '../../testing/mat_icon_module'; import {ContentCellComponent} from './content_cell_component'; +import {DataTableModule} from './data_table_module'; +import {ColumnHeader, ColumnHeaderType} from './types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-comp', template: ` diff --git a/tensorboard/webapp/widgets/data_table/data_table_test.ts b/tensorboard/webapp/widgets/data_table/data_table_test.ts index 4e7e8faa2d..caf9ee08df 100644 --- a/tensorboard/webapp/widgets/data_table/data_table_test.ts +++ b/tensorboard/webapp/widgets/data_table/data_table_test.ts @@ -15,6 +15,7 @@ limitations under the License. import { ApplicationRef, + ChangeDetectionStrategy, Component, EventEmitter, Input, @@ -22,32 +23,33 @@ import { ViewChild, } from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {MatIconTestingModule} from '../../testing/mat_icon_module'; import {By} from '@angular/platform-browser'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {MatIconTestingModule} from '../../testing/mat_icon_module'; +import {CustomModal} from '../custom_modal/custom_modal'; +import {ColumnSelectorComponent} from './column_selector_component'; +import {ColumnSelectorModule} from './column_selector_module'; +import {ContentCellComponent} from './content_cell_component'; +import {DataTableComponent} from './data_table_component'; +import {DataTableModule} from './data_table_module'; +import {FilterDialog} from './filter_dialog_component'; +import {HeaderCellComponent} from './header_cell_component'; import { ColumnHeader, ColumnHeaderType, - TableData, - SortingInfo, - SortingOrder, DiscreteFilter, - IntervalFilter, DomainType, + IntervalFilter, Side, + SortingInfo, + SortingOrder, + TableData, } from './types'; -import {DataTableComponent} from './data_table_component'; -import {DataTableModule} from './data_table_module'; -import {HeaderCellComponent} from './header_cell_component'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {ColumnSelectorComponent} from './column_selector_component'; -import {ContentCellComponent} from './content_cell_component'; -import {ColumnSelectorModule} from './column_selector_module'; -import {FilterDialog} from './filter_dialog_component'; -import {CustomModal} from '../custom_modal/custom_modal'; const ADD_BUTTON_PREDICATE = By.css('.add-button'); @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-comp', template: ` diff --git a/tensorboard/webapp/widgets/data_table/filter_dialog_component.ts b/tensorboard/webapp/widgets/data_table/filter_dialog_component.ts index fd9de24a97..47da4015db 100644 --- a/tensorboard/webapp/widgets/data_table/filter_dialog_component.ts +++ b/tensorboard/webapp/widgets/data_table/filter_dialog_component.ts @@ -12,16 +12,23 @@ 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. ==============================================================================*/ -import {Component, EventEmitter, Input, Output} from '@angular/core'; import { - DomainType, + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; +import {RangeValues} from '../range_input/types'; +import { DiscreteFilter, - IntervalFilter, DiscreteFilterValue, + DomainType, + IntervalFilter, } from './types'; -import {RangeValues} from '../range_input/types'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'tb-data-table-filter', templateUrl: 'filter_dialog_component.ng.html', diff --git a/tensorboard/webapp/widgets/data_table/header_cell_component_test.ts b/tensorboard/webapp/widgets/data_table/header_cell_component_test.ts index bf57bd9c6a..9bfede287a 100644 --- a/tensorboard/webapp/widgets/data_table/header_cell_component_test.ts +++ b/tensorboard/webapp/widgets/data_table/header_cell_component_test.ts @@ -13,25 +13,28 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'; import { - ComponentFixture, - TestBed, - fakeAsync, - flush, -} from '@angular/core/testing'; -import {MatIconTestingModule} from '../../testing/mat_icon_module'; + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, + ViewChild, +} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; +import {MatIconTestingModule} from '../../testing/mat_icon_module'; +import {DataTableModule} from './data_table_module'; +import {HeaderCellComponent} from './header_cell_component'; import { ColumnHeader, ColumnHeaderType, SortingInfo, SortingOrder, } from './types'; -import {DataTableModule} from './data_table_module'; -import {HeaderCellComponent} from './header_cell_component'; @Component({ + changeDetection: ChangeDetectionStrategy.Default, standalone: false, selector: 'testable-comp', template: ` diff --git a/tensorboard/webapp/widgets/dropdown/BUILD b/tensorboard/webapp/widgets/dropdown/BUILD index ff69ccddd4..28cab5503b 100644 --- a/tensorboard/webapp/widgets/dropdown/BUILD +++ b/tensorboard/webapp/widgets/dropdown/BUILD @@ -1,14 +1,16 @@ -load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_sass_binary", "tf_ts_library") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tensorboard/defs:defs.bzl", "tf_ng_module", "tf_ts_library") package(default_visibility = ["//tensorboard:internal"]) licenses(["notice"]) -tf_sass_binary( +sass_binary( name = "dropdown_styles", src = "dropdown_component.scss", + include_paths = ["external/npm/node_modules"], deps = [ - "//tensorboard/webapp:angular_material_sass_deps", + "//tensorboard/webapp/angular_components:material_sass", "//tensorboard/webapp/theme", ], ) diff --git a/tensorboard/webapp/widgets/dropdown/dropdown_component.ts b/tensorboard/webapp/widgets/dropdown/dropdown_component.ts index ce48cca182..8bc18511fc 100644 --- a/tensorboard/webapp/widgets/dropdown/dropdown_component.ts +++ b/tensorboard/webapp/widgets/dropdown/dropdown_component.ts @@ -12,7 +12,13 @@ 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. ==============================================================================*/ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; export interface DropdownOption { value: any; @@ -31,6 +37,7 @@ export interface DropdownOption { * A generic dropdown with options, similar to