In Angular programs, changing a file may require other files to be emitted as well due to implicit NgModule dependencies. For example, if the selector of a directive is changed then all components that have that directive in their compilation scope need to be recompiled, as the change of selector may affect the directive matching results. Until now, the compiler solved this problem using a single dependency graph. The implicit NgModule dependencies were represented in this graph, such that a changed file would correctly also cause other files to be re-emitted. This approach is limited in a few ways: 1. The file dependency graph is used to determine whether it is safe to reuse the analysis data of an Angular decorated class. This analysis data is invariant to unrelated changes to the NgModule scope, but because the single dependency graph also tracked the implicit NgModule dependencies the compiler had to consider analysis data as stale far more often than necessary. 2. It is typical for a change to e.g. a directive to not affect its public API—its selector, inputs, outputs, or exportAs clause—in which case there is no need to re-emit all declarations in scope, as their compilation output wouldn't have changed. This commit implements a mechanism by which the compiler is able to determine the impact of a change by comparing it to the prior compilation. To achieve this, a new graph is maintained that tracks all public API information of all Angular decorated symbols. During an incremental compilation this information is compared to the information that was captured in the most recently succeeded compilation. This determines the exact impact of the changes to the public API, which is then used to determine which files need to be re-emitted. Note that the file dependency graph remains, as it is still used to track the dependencies of analysis data. This graph does no longer track the implicit NgModule dependencies, which allows for better reuse of analysis data. These changes also fix a bug where template type-checking would fail to incorporate changes made to a transitive base class of a directive/component. This used to be a problem because transitive base classes were not recorded as a transitive dependency in the file dependency graph, such that prior type-check blocks would erroneously be reused. This commit also fixes an incorrectness where a change to a declaration in NgModule `A` would not cause the declarations in NgModules that import from NgModule `A` to be re-emitted. This was intentionally incorrect as otherwise the performance of incremental rebuilds would have been far worse. This is no longer a concern, as the compiler is now able to only re-emit when actually necessary. Fixes #34867 Fixes #40635 Closes #40728 PR Close #40947
117 lines
3.9 KiB
Python
117 lines
3.9 KiB
Python
load("@npm//@babel/cli:index.bzl", "babel")
|
|
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
|
|
|
|
package(default_visibility = ["//visibility:public"])
|
|
|
|
ts_library(
|
|
name = "test_lib",
|
|
testonly = True,
|
|
srcs = glob(
|
|
["**/*.ts"],
|
|
exclude = ["integration/**/*.ts"],
|
|
),
|
|
deps = [
|
|
"//packages/compiler",
|
|
"//packages/compiler-cli",
|
|
"//packages/compiler-cli/ngcc",
|
|
"//packages/compiler-cli/ngcc/test/helpers",
|
|
"//packages/compiler-cli/src/ngtsc/diagnostics",
|
|
"//packages/compiler-cli/src/ngtsc/file_system",
|
|
"//packages/compiler-cli/src/ngtsc/file_system/testing",
|
|
"//packages/compiler-cli/src/ngtsc/imports",
|
|
"//packages/compiler-cli/src/ngtsc/incremental/semantic_graph",
|
|
"//packages/compiler-cli/src/ngtsc/logging/testing",
|
|
"//packages/compiler-cli/src/ngtsc/partial_evaluator",
|
|
"//packages/compiler-cli/src/ngtsc/reflection",
|
|
"//packages/compiler-cli/src/ngtsc/sourcemaps",
|
|
"//packages/compiler-cli/src/ngtsc/testing",
|
|
"//packages/compiler-cli/src/ngtsc/transform",
|
|
"//packages/compiler-cli/src/ngtsc/translator",
|
|
"@npm//@types/convert-source-map",
|
|
"@npm//convert-source-map",
|
|
"@npm//dependency-graph",
|
|
"@npm//magic-string",
|
|
"@npm//sourcemap-codec",
|
|
"@npm//typescript",
|
|
"@npm//yargs",
|
|
],
|
|
)
|
|
|
|
jasmine_node_test(
|
|
name = "test",
|
|
bootstrap = ["//tools/testing:node_no_angular_es5"],
|
|
data = [
|
|
"//packages/compiler-cli/src/ngtsc/testing/fake_core:npm_package",
|
|
],
|
|
deps = [
|
|
":test_lib",
|
|
],
|
|
)
|
|
|
|
ts_library(
|
|
name = "integration_lib",
|
|
testonly = True,
|
|
srcs = glob(
|
|
["integration/**/*.ts"],
|
|
),
|
|
deps = [
|
|
"//packages/compiler-cli/ngcc",
|
|
"//packages/compiler-cli/ngcc/test/helpers",
|
|
"//packages/compiler-cli/src/ngtsc/file_system",
|
|
"//packages/compiler-cli/src/ngtsc/file_system/testing",
|
|
"//packages/compiler-cli/src/ngtsc/logging/testing",
|
|
"//packages/compiler-cli/src/ngtsc/testing",
|
|
"@npm//rxjs",
|
|
"@npm//typescript",
|
|
],
|
|
)
|
|
|
|
# As of version 10, the release packages do not contain esm5 output anymore. The ngcc integration
|
|
# tests intend to test ES5 features though, so we downlevel the flat esm2015 file to ES5 using
|
|
# Babel. We can then link that into the mock file system as if the Angular core package is still
|
|
# built with previous APF versions where esm5 output was shipped. This allows us to ensure that ngcc
|
|
# properly processes libraries with esm5 output.
|
|
# **Note**: We are using Babel instead of `tsc` as TypeScript does not allow us to downlevel the
|
|
# file without setting the module resolution to either `amd` or `system`. We want to preserve ES
|
|
# modules.
|
|
babel(
|
|
name = "fesm5_angular_core",
|
|
outs = ["fesm5_angular_core.js"],
|
|
args = [
|
|
"$(execpath //packages/core:npm_package)/fesm2015/core.js",
|
|
"--presets @babel/preset-env",
|
|
"--out-file $(execpath fesm5_angular_core.js)",
|
|
],
|
|
data = [
|
|
"//packages/core:npm_package",
|
|
"@npm//@babel/preset-env",
|
|
],
|
|
)
|
|
|
|
jasmine_node_test(
|
|
name = "integration",
|
|
timeout = "long",
|
|
bootstrap = ["//tools/testing:node_no_angular_es5"],
|
|
data = [
|
|
":fesm5_angular_core",
|
|
"//packages/common:npm_package",
|
|
"//packages/core:npm_package",
|
|
"@npm//rxjs",
|
|
],
|
|
shard_count = 4,
|
|
tags = [
|
|
# Disabled in AOT mode because we want ngcc to compile non-AOT Angular packages.
|
|
"no-ivy-aot",
|
|
],
|
|
templated_args = [
|
|
# TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver
|
|
# See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324
|
|
"--bazel_patch_module_resolver",
|
|
],
|
|
deps = [
|
|
":integration_lib",
|
|
"@npm//canonical-path",
|
|
"@npm//convert-source-map",
|
|
],
|
|
)
|