In v7 of Angular we removed `tsickle` from the default `ngc` pipeline.
This had the negative potential of breaking ES2015 output and SSR due
to a limitation in TypeScript.
TypeScript by default preserves type information for decorated constructor
parameters when `emitDecoratorMetadata` is enabled. For example,
consider this snippet below:
```
@Directive()
export class MyDirective {
constructor(button: MyButton) {}
}
export class MyButton {}
```
TypeScript would generate metadata for the `MyDirective` class it has
a decorator applied. This metadata would be needed in JIT mode, or
for libraries that provide `MyDirective` through NPM. The metadata would
look as followed:
```
let MyDirective = class MyDir {}
MyDirective = __decorate([
Directive(),
__metadata("design:paramtypes", [MyButton]),
], MyDirective);
let MyButton = class MyButton {}
```
Notice that TypeScript generated calls to `__decorate` and
`__metadata`. These calls are needed so that the Angular compiler
is able to determine whether `MyDirective` is actually an directive,
and what types are needed for dependency injection.
The limitation surfaces in this concrete example because `MyButton`
is declared after the `__metadata(..)` call, while `__metadata`
actually directly references `MyButton`. This is illegal though because
`MyButton` has not been declared at this point. This is due to the
so-called temporal dead zone in JavaScript. Errors like followed will
be reported at runtime when such file/code evaluates:
```
Uncaught ReferenceError: Cannot access 'MyButton' before initialization
```
As noted, this is a TypeScript limitation because ideally TypeScript
shouldn't evaluate `__metadata`/reference `MyButton` immediately.
Instead, it should defer the reference until `MyButton` is actually
declared. This limitation will not be fixed by the TypeScript team
though because it's a limitation as per current design and they will
only revisit this once the tc39 decorator proposal is finalized
(currently stage-2 at time of writing).
Given this wontfix on the TypeScript side, and our heavy reliance on
this metadata in libraries (and for JIT mode), we intend to fix this
from within the Angular compiler by downleveling decorators to static
properties that don't need to evaluate directly. For example:
```
MyDirective.ctorParameters = () => [MyButton];
```
With this snippet above, `MyButton` is not referenced directly. Only
lazily when the Angular runtime needs it. This mitigates the temporal
dead zone issue caused by a limitation in TypeScript's decorator
metadata output. See: https://github.com/microsoft/TypeScript/issues/27519.
In the past (as noted; before version 7), the Angular compiler by
default used tsickle that already performed this transformation. We
moved the transformation to the CLI for JIT and `ng-packager`, but now
we realize that we can move this all to a single place in the compiler
so that standalone ngc consumers can benefit too, and that we can
disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently
still relies on tsickle to perform this decorator processing).
This transformation also has another positive side-effect of making
Angular application/library code more compatible with server-side
rendering. In principle, TypeScript would also preserve type information
for decorated class members (similar to how it did that for constructor
parameters) at runtime. This becomes an issue when your application
relies on native DOM globals for decorated class member types. e.g.
```
@Input() panelElement: HTMLElement;
```
Your application code would then reference `HTMLElement` directly
whenever the source file is loaded in NodeJS for SSR. `HTMLElement`
does not exist on the server though, so that will become an invalid
reference. One could work around this by providing global mocks for
these DOM symbols, but that doesn't match up with other places where
dependency injection is used for mocking DOM/browser specific symbols.
More context in this issue: #30586. The TL;DR here is that the Angular
compiler does not care about types for these class members, so it won't
ever reference `HTMLElement` at runtime.
Fixes#30106. Fixes#30586. Fixes#30141.
Resolves FW-2196. Resolves FW-2199.
PR Close#37382
In #37221 we disabled tsickle passes from transforming the tsc output that is used to publish all
Angular framework and components packages (@angular/*).
This change however revealed a bug in the ngc that caused __decorate and __metadata calls to still
be emitted in the JS code even though we don't depend on them.
Additionally it was these calls that caused code in @angular/material packages to fail at runtime
due to circular dependency in the emitted decorator code documeted as
https://github.com/microsoft/TypeScript/issues/27519.
This change partially rolls back #37221 by reenabling the decorator to static fields (static
properties) downleveling.
This is just a temporary workaround while we are also fixing root cause in `ngc` - tracked as
FW-2199.
Resolves FW-2198.
Related to FW-2196
PR Close#37317
As of TypeScript 3.9, the tsc emit is not compatible with Closure
Compiler due to
https://github.com/microsoft/TypeScript/pull/32011.
There is some hope that this will be fixed by a solution like the one
proposed in
https://github.com/microsoft/TypeScript/issues/38374 but currently it's
unclear if / when that will
happen.
Since the Closure support has been somewhat already broken, and the
tsickle pass has been a source
of headaches for some time for Angular packages, we are removing it for
now while we rethink our
strategy to make Angular Closure compatible outside of Google.
This change has no effect on our Closure compatibility within Google
which work well because all the
code is compiled from sources and passed through tsickle.
This change only disables the tsickle pass but doesn't remove it.
A follow up PR should either remove all the traces of tscikle or
re-enable the fixed version.
BREAKING CHANGE: Angular npm packages no longer contain jsdoc comments
to support Closure Compiler's advanced optimizations
The support for Closure compiler in Angular packages has been
experimental and broken for quite some
time.
As of TS3.9 Closure is unusable with the JavaScript emit. Please follow
https://github.com/microsoft/TypeScript/issues/38374 for more
information and updates.
If you used Closure compiler with Angular in the past, you will likely
be better off consuming
Angular packages built from sources directly rather than consuming the
version we publish on npm
which is primarily optimized for Webpack/Rollup + Terser build pipeline.
As a temporary workaround you might consider using your current build
pipeline with Closure flag
`--compilation_level=SIMPLE`. This flag will ensure that your build
pipeline produces buildable and
runnable artifacts, at the cost of increased payload size due to
advanced optimizations being disabled.
If you were affected by this change, please help us understand your
needs by leaving a comment on https://github.com/angular/angular/issues/37234.
PR Close#37221
Currently, when Angular code is built with Bazel and with Ivy, generated
factory shims (.ngfactory files) are not processed via the majority of
tsickle's transforms. This is a subtle effect of the build infrastructure,
but it boils down to a TsickleHost method `shouldSkipTsickleProcessing`.
For ngc_wrapped builds (Bazel + Angular), this method is defined in the
`@bazel/typescript` (aka bazel rules_typescript) implementation of
`CompilerHost`. The default behavior is to skip tsickle processing for files
which are not present in the original `srcs[]` of the build rule. In
Angular's case, this includes all generated shim files.
For View Engine factories this is probably desirable as they're quite
complex and they've never been tested with tsickle. Ivy factories however
are smaller and very straightforward, and it makes sense to treat them like
any other output.
This commit adjusts two independent implementations of
`shouldSkipTsickleProcessing` to enable transformation of Ivy shims:
* in `@angular/bazel` aka ngc_wrapped, the upstream `@bazel/typescript`
`CompilerHost` is patched to treat .ngfactory files the same as their
original source file, with respect to tsickle processing.
It is currently not possible to test this change as we don't have any test
that inspects tsickle output with bazel. It will be extensively tested in
g3.
* in `ngc`, Angular's own implementation is adjusted to allow for the
processing of shims when compiling with Ivy. This enables a unit test to
be written to validate the correct behavior of tsickle when given a host
that's appropriately configured to process factory shims.
For ngtsc-as-a-plugin, a similar fix will need to be submitted upstream in
tsc_wrapped.
PR Close#35848
PR Close#35975
Currently, when Angular code is built with Bazel and with Ivy, generated
factory shims (.ngfactory files) are not processed via the majority of
tsickle's transforms. This is a subtle effect of the build infrastructure,
but it boils down to a TsickleHost method `shouldSkipTsickleProcessing`.
For ngc_wrapped builds (Bazel + Angular), this method is defined in the
`@bazel/typescript` (aka bazel rules_typescript) implementation of
`CompilerHost`. The default behavior is to skip tsickle processing for files
which are not present in the original `srcs[]` of the build rule. In
Angular's case, this includes all generated shim files.
For View Engine factories this is probably desirable as they're quite
complex and they've never been tested with tsickle. Ivy factories however
are smaller and very straightforward, and it makes sense to treat them like
any other output.
This commit adjusts two independent implementations of
`shouldSkipTsickleProcessing` to enable transformation of Ivy shims:
* in `@angular/bazel` aka ngc_wrapped, the upstream `@bazel/typescript`
`CompilerHost` is patched to treat .ngfactory files the same as their
original source file, with respect to tsickle processing.
It is currently not possible to test this change as we don't have any test
that inspects tsickle output with bazel. It will be extensively tested in
g3.
* in `ngc`, Angular's own implementation is adjusted to allow for the
processing of shims when compiling with Ivy. This enables a unit test to
be written to validate the correct behavior of tsickle when given a host
that's appropriately configured to process factory shims.
For ngtsc-as-a-plugin, a similar fix will need to be submitted upstream in
tsc_wrapped.
PR Close#35848
Currently, the `ng_module` rule incorrectly uses manifest paths for
generated imports from the Angular compiler.
This breaks packaging as prodmode output (i.e. `esnext`) is copied in
various targets (`es5` and `es2015`) to the npm package output.
e.g. imports are generated like:
_node_modules/my-pkg/es2015/imports/public-api.js_
```ts
import * as i1 from "angular/packages/bazel/test/ng_package/example/imports/second";
```
while it should be actually:
```ts
import * as i1 from "./second";
```
The imports can, and should be relative so that the files are
self-contained and do not rely on custom module resolution.
PR Close#35841
This reverts commit cb142b6df9fa3b43e6905358c880c11f6a9abb45.
The intention of this commit was for a consumer of the `compile` function to
pass the `bazelHost` it returns into future invocations, reusing the
`FileCache` between them. However, first-party ngc_wrapped does not do this,
which caused a performance regression as the `FileCache` was no longer
shared between compilations.
PR Close#35063
Refactored Bazel host creation to export some of the relevant utility functions. google3 needs to make some modifications to the Bazel host to enable the Ivy migration. The functionality exported here is still needed in g3, but it allows g3 code to create the Bazel host, make necessary modifications, and then provide it to the external codebase here while everything is still compatible. For external users, this is a no-op.
PR Close#34686
We need to migrate to using angular_ivy_enabled value to determine whether to use
Ivy or ViewEngine for package building scripts and for size-tracking and
symbol-extract tooling.
PR Close#33983
This option makes ngc behave as tsc, and was originally implemented before
ngtsc existed. It was designed so we could build JIT-only versions of
Angular packages to begin testing Ivy early, and is not used at all in our
current setup.
PR Close#32219
Switch back to passing a custom bazel host instead of rewriting one that
is passed to `compile` now that the Angular indexer is stable.
Revert "feat(bazel): allow passing and rewriting an old bazel host"
This reverts commit 0a4c1c8f803a65f5c156af90e67f7d7d68ebc7f8.
PR Close#31496
Updates the decision made in #31341; this is for the Angular indexer
inside Google. The indexer currently passes (and ngc-wrapped#compile
accepts) a bazel host to use, but because many methods are overwritten
specially for Angular compilation a better approach is to pass an old
bazel compiler host and siphon methods needed off of it before creating
a new host. This enables that.
PR Close#31381
Enable users to pass a custom Bazel CompilerHost to use for an Angular
compilation. This supports users who must override the TypeScript
compiler host.
PR Close#31341
* Fixes that the flat module out files do not have a proper AMD module name on Windows. This is currently blocking serving a `ng_module` using the Bazel TypeScript `devserver` on Windows.
PR Close#27839
This turns on generation of ngfactory.js files when compiling in Ivy
mode in g3. They're not turned on for Bazel users as there appears to
be a strange interaction with the way our tests run in Bazel mode.
PR Close#25392
This change turns on preserve-symlinks in nodejs to verify hermeticity of the Angular build.
BREAKING CHANGE: Use of @angular/bazel rules now requires calling ng_setup_workspace() in your WORKSPACE file.
For example:
local_repository(
name = "angular",
path = "node_modules/@angular/bazel",
)
load("@angular//:index.bzl", "ng_setup_workspace")
ng_setup_workspace()
PR Close#24881
Bazel has a restriction that a single output (eg. a compiled version of
//packages/common) can only be produced by a single rule. This precludes
the Angular repo from having multiple rules that build the same code. And
the complexity of having a single rule produce multiple outputs (eg. an
ngc-compiled version of //packages/common and an Ivy-enabled version) is
too high.
Additionally, the Angular repo has lots of existing tests which could be
executed as-is under Ivy. Such testing is very valuable, and it would be
nice to share not only the code, but the dependency graph / build config
as well.
Thus, this change introduces a --define flag 'compile' with three potential
values. When --define=compile=X is set, the entire build system runs in a
particular mode - the behavior of all existing targets is controlled by
the flag. This allows us to reuse our entire build structure for testing
in a variety of different manners. The flag has three possible settings:
* legacy (the default): the traditional View Engine (ngc) build
* local: runs the prototype ngtsc compiler, which does not rely on global
analysis
* jit: runs ngtsc in a mode which executes tsickle, but excludes the
Angular related transforms, which approximates the behavior of plain
tsc. This allows the main packages such as common to be tested with
the JIT compiler.
Additionally, the ivy_ng_module() rule still exists and runs ngc in a mode
where Ivy-compiled output is produced from global analysis information, as
a stopgap while ngtsc is being developed.
PR Close#24056