Deliver web apps with confidence 🚀
Find a file
Paul Gschwendtner 401ef71ae5 fix(compiler-cli): downlevel angular decorators to static properties (#37382)
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
2020-06-10 09:24:11 -07:00
.circleci docs: fix various typos (#37443) 2020-06-04 16:03:54 -07:00
.devcontainer build: update the recommended Dockerfile for VSCode remote development (#34697) 2020-01-09 13:31:14 -08:00
.github docs: update the stackblitz in the GitHub Issue template (#37219) 2020-06-03 15:55:44 -07:00
.ng-dev docs(dev-infra): add comment about what the requiredBaseCommit is (#37509) 2020-06-09 13:29:24 -07:00
.vscode build: fix @bazel/bazel to bazelisk leftovers (#36132) 2020-03-19 08:58:47 -07:00
.yarn build: update to latest version of yarn (#36464) 2020-04-14 12:47:30 -07:00
aio docs: add note on publishing libraries in ivy (#36556) 2020-06-08 15:05:51 -07:00
dev-infra fix(dev-infra): local changes check not working (#37489) 2020-06-09 08:27:32 -07:00
docs docs: add blank line before header (#37391) 2020-06-04 09:20:26 -07:00
goldens perf(core): avoid pulling in jit-specific code in aot bundles (#37372) 2020-06-09 13:34:43 -07:00
integration build: upgrade to bazel 3.2.0 and rules_nodejs 1.7.0 (#37358) 2020-06-08 09:15:50 -07:00
modules refactor(dev-infra): small changes and fixes (#36800) 2020-06-03 13:12:30 -07:00
packages fix(compiler-cli): downlevel angular decorators to static properties (#37382) 2020-06-10 09:24:11 -07:00
scripts build: update to latest stable Chromium 83.0.4103 in both rules_webtesting and puppeteer (#37427) 2020-06-08 09:16:40 -07:00
third_party ci: Remove old vendoring solution in favor of relying on yarn-path (#35083) 2020-02-06 15:30:51 -08:00
tools refactor(dev-infra): small changes and fixes (#36800) 2020-06-03 13:12:30 -07:00
.bazelignore build: add npm package manifest to npm_integration_test (#35669) 2020-02-26 12:58:35 -08:00
.bazelrc build: do not build runfile trees unnecessarily (#36914) 2020-05-05 12:00:04 -07:00
.bazelversion build: upgrade to bazel 3.2.0 and rules_nodejs 1.7.0 (#37358) 2020-06-08 09:15:50 -07:00
.clang-format feat(tooling): Add a .clang-format for automated JavaScript formatting. 2015-04-02 08:44:34 -07:00
.editorconfig build: use https link to editorconfig.org in .editorconfig (#27664) 2018-12-18 09:30:09 -08:00
.gitattributes test: fix ts api guardian and public guard tests on windows (#30105) 2019-04-26 16:32:22 -07:00
.gitignore ci: do not run benchmark measurements in circleci (#34753) 2020-01-29 09:22:27 -08:00
.mailmap build: add a Git .mailmap with my new name (#19550) 2017-10-09 14:35:30 -07:00
.nvmrc build: migrate to node@12.14.1 (#34955) 2020-01-27 09:31:22 -08:00
.pullapprove.yml ci: remove IgorMinar from reviewers list for pullapprove fallback group (#36456) 2020-06-08 10:07:45 -07:00
.yarnrc build: update to latest version of yarn (#36464) 2020-04-14 12:47:30 -07:00
browser-providers.conf.js ci: temporarily disable Android 10 browser unit tests on Saucelabs (#37399) 2020-06-02 17:32:34 -04:00
BUILD.bazel revert: "revert: "feat(dev-infra): exposed new rule 'component_benchmark' via dev_infra (#36434)" (#36798)" (#36800) 2020-06-03 13:12:30 -07:00
CHANGELOG.md docs: release notes for the v9.1.10 release 2020-06-09 15:25:31 -07:00
CODE_OF_CONDUCT.md docs: fix community tab in GitHub by copying CoC 2018-02-27 19:02:30 -08:00
CONTRIBUTING.md docs(dev-infra): update triage and contributing docs for dev-infra (#35995) 2020-03-10 21:02:17 -04:00
gulpfile.js build: update license headers to reference Google LLC (#37205) 2020-05-26 14:26:58 -04:00
karma-js.conf.js build: update license headers to reference Google LLC (#37205) 2020-05-26 14:26:58 -04:00
LICENSE build: bump year (#34651) 2020-01-13 07:21:43 -08:00
package.json build: update to latest stable Chromium 83.0.4103 in both rules_webtesting and puppeteer (#37427) 2020-06-08 09:16:40 -07:00
README.md docs: remove browserstack badge from readme (#35684) 2020-03-17 09:29:43 -07:00
shims_for_IE.js Revert "refactor: add license header to JS files & format files (#12035)" 2016-10-04 14:06:41 -07:00
test-events.js build: update license headers to reference Google LLC (#37205) 2020-05-26 14:26:58 -04:00
test-main.js build: update license headers to reference Google LLC (#37205) 2020-05-26 14:26:58 -04:00
tslint.json build: Update file-header lint rule to Google LLC (#37205) 2020-05-26 14:26:58 -04:00
WORKSPACE build: upgrade to bazel 3.2.0 and rules_nodejs 1.7.0 (#37358) 2020-06-08 09:15:50 -07:00
yarn.lock build: update to latest stable Chromium 83.0.4103 in both rules_webtesting and puppeteer (#37427) 2020-06-08 09:16:40 -07:00
yarn.lock.readme.md build: remove travisci leftovers (#27979) 2019-01-09 10:41:16 -08:00

CircleCI Join the chat at https://gitter.im/angular/angular npm version

Angular

Angular is a development platform for building mobile and desktop web applications using TypeScript/JavaScript and other languages.

Quickstart

Get started in 5 minutes.

Changelog

Learn about the latest improvements.

Want to help?

Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for contributing and then check out one of our issues in the hotlist: community-help.