Skip to content

Commit 16f8789

Browse files
committed
fix(compiler-cli): downlevel angular decorators to static properties
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: microsoft/TypeScript#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: angular#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 angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
1 parent b2ccc34 commit 16f8789

File tree

12 files changed

+1413
-420
lines changed

12 files changed

+1413
-420
lines changed

packages/bazel/src/ngc-wrapped/index.ts

+20-21
Original file line numberDiff line numberDiff line change
@@ -191,23 +191,8 @@ export function compile({
191191
fileLoader = new UncachedFileLoader();
192192
}
193193

194-
compilerOpts.annotationsAs = 'static fields';
195-
if (!bazelOpts.es5Mode) {
196-
if (bazelOpts.workspaceName === 'google3') {
197-
compilerOpts.annotateForClosureCompiler = true;
198-
} else {
199-
compilerOpts.annotateForClosureCompiler = false;
200-
}
201-
}
202-
203194
// Detect from compilerOpts whether the entrypoint is being invoked in Ivy mode.
204195
const isInIvyMode = !!compilerOpts.enableIvy;
205-
206-
// Disable downleveling and Closure annotation if in Ivy mode.
207-
if (isInIvyMode) {
208-
compilerOpts.annotationsAs = 'decorators';
209-
}
210-
211196
if (!compilerOpts.rootDirs) {
212197
throw new Error('rootDirs is not set!');
213198
}
@@ -264,9 +249,6 @@ export function compile({
264249
}
265250

266251
if (isInIvyMode) {
267-
// Also need to disable decorator downleveling in the BazelHost in Ivy mode.
268-
bazelHost.transformDecorators = false;
269-
270252
const delegate = bazelHost.shouldSkipTsickleProcessing.bind(bazelHost);
271253
bazelHost.shouldSkipTsickleProcessing = (fileName: string) => {
272254
// The base implementation of shouldSkipTsickleProcessing checks whether `fileName` is part of
@@ -277,12 +259,29 @@ export function compile({
277259
};
278260
}
279261

262+
// Always disable tsickle decorator transforming in the tsickle compiler host. The
263+
// Angular compilers have their own logic for decorator processing and we wouldn't
264+
// want tsickle to interfere with that.
265+
bazelHost.transformDecorators = false;
266+
267+
// By default in the `prodmode` output, we do not add annotations for closure compiler.
268+
// Though, if we are building inside `google3`, closure annotations are desired for
269+
// prodmode output, so we enable it by default. The defaults can be overridden by
270+
// setting the `annotateForClosureCompiler` compiler option in the user tsconfig.
271+
if (!bazelOpts.es5Mode && compilerOpts.annotateForClosureCompiler === undefined) {
272+
if (bazelOpts.workspaceName === 'google3') {
273+
compilerOpts.annotateForClosureCompiler = true;
274+
} else {
275+
compilerOpts.annotateForClosureCompiler = false;
276+
}
277+
}
278+
279+
// The `annotateForClosureCompiler` Angular compiler option is not respected by default
280+
// as ngc-wrapped handles tsickle emit on its own. This means that we need to update
281+
// the tsickle compiler host based on the `annotateForClosureCompiler` flag.
280282
if (compilerOpts.annotateForClosureCompiler) {
281283
bazelHost.transformTypesToClosure = true;
282284
}
283-
if (compilerOpts.annotateForClosureCompiler || compilerOpts.annotationsAs === 'static fields') {
284-
bazelHost.transformDecorators = true;
285-
}
286285

287286
const origBazelHostFileExist = bazelHost.fileExists;
288287
bazelHost.fileExists = (fileName: string) => {

packages/bazel/test/ng_package/example_package.golden

+10-13
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,10 @@ Hello
253253
var MySecondService = /** @class */ (function () {
254254
function MySecondService() {
255255
}
256+
MySecondService.ɵprov = i0.ɵɵdefineInjectable({ factory: function MySecondService_Factory() { return new MySecondService(); }, token: MySecondService, providedIn: "root" });
256257
MySecondService.decorators = [
257258
{ type: i0.Injectable, args: [{ providedIn: 'root' },] }
258259
];
259-
MySecondService.ɵprov = i0.ɵɵdefineInjectable({ factory: function MySecondService_Factory() { return new MySecondService(); }, token: MySecondService, providedIn: "root" });
260260
return MySecondService;
261261
}());
262262

@@ -271,14 +271,13 @@ Hello
271271
function MyService(secondService) {
272272
this.secondService = secondService;
273273
}
274+
MyService.ɵprov = i0.ɵɵdefineInjectable({ factory: function MyService_Factory() { return new MyService(i0.ɵɵinject(MySecondService)); }, token: MyService, providedIn: "root" });
274275
MyService.decorators = [
275276
{ type: i0.Injectable, args: [{ providedIn: 'root' },] }
276277
];
277-
/** @nocollapse */
278278
MyService.ctorParameters = function () { return [
279279
{ type: MySecondService }
280280
]; };
281-
MyService.ɵprov = i0.ɵɵdefineInjectable({ factory: function MyService_Factory() { return new MyService(i0.ɵɵinject(MySecondService)); }, token: MyService, providedIn: "root" });
282281
return MyService;
283282
}());
284283

@@ -317,7 +316,7 @@ Hello
317316
*
318317
* Use of this source code is governed by an MIT-style license that can be
319318
* found in the LICENSE file at https://angular.io/license
320-
*/var n=function(){function e(){}return e.decorators=[{type:t.Injectable,args:[{providedIn:"root"}]}],e.ɵprov=t.ɵɵdefineInjectable({factory:function t(){return new e},token:e,providedIn:"root"}),e}(),r=function(){function e(e){this.secondService=e}return e.decorators=[{type:t.Injectable,args:[{providedIn:"root"}]}],e.ctorParameters=function(){return[{type:n}]},e.ɵprov=t.ɵɵdefineInjectable({factory:function r(){return new e(t.ɵɵinject(n))},token:e,providedIn:"root"}),e}();
319+
*/var n=function(){function e(){}return e.ɵprov=t.ɵɵdefineInjectable({factory:function t(){return new e},token:e,providedIn:"root"}),e.decorators=[{type:t.Injectable,args:[{providedIn:"root"}]}],e}(),r=function(){function e(e){this.secondService=e}return e.ɵprov=t.ɵɵdefineInjectable({factory:function r(){return new e(t.ɵɵinject(n))},token:e,providedIn:"root"}),e.decorators=[{type:t.Injectable,args:[{providedIn:"root"}]}],e.ctorParameters=function(){return[{type:n}]},e}();
321320
/**
322321
* @license
323322
* Copyright Google LLC All Rights Reserved.
@@ -602,18 +601,17 @@ let MyService = /** @class */ (() => {
602601
this.secondService = secondService;
603602
}
604603
}
604+
MyService.ɵprov = i0.ɵɵdefineInjectable({ factory: function MyService_Factory() { return new MyService(i0.ɵɵinject(i1.MySecondService)); }, token: MyService, providedIn: "root" });
605605
MyService.decorators = [
606606
{ type: Injectable, args: [{ providedIn: 'root' },] }
607607
];
608-
/** @nocollapse */
609608
MyService.ctorParameters = () => [
610609
{ type: MySecondService }
611610
];
612-
MyService.ɵprov = i0.ɵɵdefineInjectable({ factory: function MyService_Factory() { return new MyService(i0.ɵɵinject(i1.MySecondService)); }, token: MyService, providedIn: "root" });
613611
return MyService;
614612
})();
615613
export { MyService };
616-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2JhemVsL3Rlc3QvbmdfcGFja2FnZS9leGFtcGxlL2ltcG9ydHMvcHVibGljLWFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSxVQUFVLENBQUM7OztBQUV6QztJQUFBLE1BQ2EsU0FBUztRQUNwQixZQUFtQixhQUE4QjtZQUE5QixrQkFBYSxHQUFiLGFBQWEsQ0FBaUI7UUFBRyxDQUFDOzs7Z0JBRnRELFVBQVUsU0FBQyxFQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUM7Ozs7Z0JBRnhCLGVBQWU7OztvQkFUdkI7S0FjQztTQUZZLFNBQVMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgR29vZ2xlIExMQyBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGFuIE1JVC1zdHlsZSBsaWNlbnNlIHRoYXQgY2FuIGJlXG4gKiBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGF0IGh0dHBzOi8vYW5ndWxhci5pby9saWNlbnNlXG4gKi9cblxuaW1wb3J0IHtJbmplY3RhYmxlfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7TXlTZWNvbmRTZXJ2aWNlfSBmcm9tICcuL3NlY29uZCc7XG5cbkBJbmplY3RhYmxlKHtwcm92aWRlZEluOiAncm9vdCd9KVxuZXhwb3J0IGNsYXNzIE15U2VydmljZSB7XG4gIGNvbnN0cnVjdG9yKHB1YmxpYyBzZWNvbmRTZXJ2aWNlOiBNeVNlY29uZFNlcnZpY2UpIHt9XG59XG4iXX0=
614+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2JhemVsL3Rlc3QvbmdfcGFja2FnZS9leGFtcGxlL2ltcG9ydHMvcHVibGljLWFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSxVQUFVLENBQUM7OztBQUV6QztJQUFBLE1BQ2EsU0FBUztRQUNwQixZQUFtQixhQUE4QjtZQUE5QixrQkFBYSxHQUFiLGFBQWEsQ0FBaUI7UUFBRyxDQUFDOzs7O2dCQUZ0RCxVQUFVLFNBQUMsRUFBQyxVQUFVLEVBQUUsTUFBTSxFQUFDOzs7Z0JBRUksZUFBZTs7b0JBYm5EO0tBY0M7U0FGWSxTQUFTIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IEdvb2dsZSBMTEMgQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGUgbGljZW5zZSB0aGF0IGNhbiBiZVxuICogZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBhdCBodHRwczovL2FuZ3VsYXIuaW8vbGljZW5zZVxuICovXG5cbmltcG9ydCB7SW5qZWN0YWJsZX0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge015U2Vjb25kU2VydmljZX0gZnJvbSAnLi9zZWNvbmQnO1xuXG5ASW5qZWN0YWJsZSh7cHJvdmlkZWRJbjogJ3Jvb3QnfSlcbmV4cG9ydCBjbGFzcyBNeVNlcnZpY2Uge1xuICBjb25zdHJ1Y3RvcihwdWJsaWMgc2Vjb25kU2VydmljZTogTXlTZWNvbmRTZXJ2aWNlKSB7fVxufVxuIl19
617615

618616
--- esm2015/imports/second.js ---
619617

@@ -629,14 +627,14 @@ import * as i0 from "@angular/core";
629627
let MySecondService = /** @class */ (() => {
630628
class MySecondService {
631629
}
630+
MySecondService.ɵprov = i0.ɵɵdefineInjectable({ factory: function MySecondService_Factory() { return new MySecondService(); }, token: MySecondService, providedIn: "root" });
632631
MySecondService.decorators = [
633632
{ type: Injectable, args: [{ providedIn: 'root' },] }
634633
];
635-
MySecondService.ɵprov = i0.ɵɵdefineInjectable({ factory: function MySecondService_Factory() { return new MySecondService(); }, token: MySecondService, providedIn: "root" });
636634
return MySecondService;
637635
})();
638636
export { MySecondService };
639-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Vjb25kLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vcGFja2FnZXMvYmF6ZWwvdGVzdC9uZ19wYWNrYWdlL2V4YW1wbGUvaW1wb3J0cy9zZWNvbmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBRUgsT0FBTyxFQUFDLFVBQVUsRUFBQyxNQUFNLGVBQWUsQ0FBQzs7QUFFekM7SUFBQSxNQUNhLGVBQWU7OztnQkFEM0IsVUFBVSxTQUFDLEVBQUMsVUFBVSxFQUFFLE1BQU0sRUFBQzs7OzBCQVZoQztLQVlDO1NBRFksZUFBZSIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQge0luamVjdGFibGV9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5ASW5qZWN0YWJsZSh7cHJvdmlkZWRJbjogJ3Jvb3QnfSlcbmV4cG9ydCBjbGFzcyBNeVNlY29uZFNlcnZpY2Uge1xufVxuIl19
637+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Vjb25kLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vcGFja2FnZXMvYmF6ZWwvdGVzdC9uZ19wYWNrYWdlL2V4YW1wbGUvaW1wb3J0cy9zZWNvbmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBRUgsT0FBTyxFQUFDLFVBQVUsRUFBQyxNQUFNLGVBQWUsQ0FBQzs7QUFFekM7SUFBQSxNQUNhLGVBQWU7Ozs7Z0JBRDNCLFVBQVUsU0FBQyxFQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUM7OzBCQVZoQztLQVlDO1NBRFksZUFBZSIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQge0luamVjdGFibGV9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5ASW5qZWN0YWJsZSh7cHJvdmlkZWRJbjogJ3Jvb3QnfSlcbmV4cG9ydCBjbGFzcyBNeVNlY29uZFNlcnZpY2Uge1xufVxuIl19
640638

641639
--- esm2015/index.js ---
642640

@@ -800,7 +798,7 @@ export { A11yModule };
800798
* License: MIT
801799
*/
802800

803-
import { Injectable, ɵɵdefineInjectable, ɵɵinject } from '@angular/core';
801+
import { ɵɵdefineInjectable, Injectable, ɵɵinject } from '@angular/core';
804802

805803
/**
806804
* @license
@@ -812,10 +810,10 @@ import { Injectable, ɵɵdefineInjectable, ɵɵinject } from '@angular/core';
812810
let MySecondService = /** @class */ (() => {
813811
class MySecondService {
814812
}
813+
MySecondService.ɵprov = ɵɵdefineInjectable({ factory: function MySecondService_Factory() { return new MySecondService(); }, token: MySecondService, providedIn: "root" });
815814
MySecondService.decorators = [
816815
{ type: Injectable, args: [{ providedIn: 'root' },] }
817816
];
818-
MySecondService.ɵprov = ɵɵdefineInjectable({ factory: function MySecondService_Factory() { return new MySecondService(); }, token: MySecondService, providedIn: "root" });
819817
return MySecondService;
820818
})();
821819

@@ -832,14 +830,13 @@ let MyService = /** @class */ (() => {
832830
this.secondService = secondService;
833831
}
834832
}
833+
MyService.ɵprov = ɵɵdefineInjectable({ factory: function MyService_Factory() { return new MyService(ɵɵinject(MySecondService)); }, token: MyService, providedIn: "root" });
835834
MyService.decorators = [
836835
{ type: Injectable, args: [{ providedIn: 'root' },] }
837836
];
838-
/** @nocollapse */
839837
MyService.ctorParameters = () => [
840838
{ type: MySecondService }
841839
];
842-
MyService.ɵprov = ɵɵdefineInjectable({ factory: function MyService_Factory() { return new MyService(ɵɵinject(MySecondService)); }, token: MyService, providedIn: "root" });
843840
return MyService;
844841
})();
845842

packages/compiler-cli/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ts_library(
2929
"//packages/compiler-cli/src/ngtsc/file_system",
3030
"//packages/compiler-cli/src/ngtsc/indexer",
3131
"//packages/compiler-cli/src/ngtsc/perf",
32+
"//packages/compiler-cli/src/ngtsc/reflection",
3233
"//packages/compiler-cli/src/ngtsc/typecheck",
3334
"@npm//@bazel/typescript",
3435
"@npm//@types/node",

packages/compiler-cli/src/main.ts

+22-43
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,9 @@ export function mainDiagnosticsForTest(
8989
}
9090

9191
function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined {
92-
const transformDecorators =
93-
(options.enableIvy === false && options.annotationsAs !== 'decorators');
94-
const transformTypesToClosure = options.annotateForClosureCompiler;
95-
if (!transformDecorators && !transformTypesToClosure) {
92+
if (!options.annotateForClosureCompiler) {
9693
return undefined;
9794
}
98-
if (transformDecorators) {
99-
// This is needed as a workaround for https://github.com./angular/tsickle/issues/635
100-
// Otherwise tsickle might emit references to non imported values
101-
// as TypeScript elided the import.
102-
options.emitDecoratorMetadata = true;
103-
}
10495
const tsickleHost: Pick<
10596
tsickle.TsickleHost,
10697
'shouldSkipTsickleProcessing'|'pathToModuleName'|'shouldIgnoreWarningsForPath'|
@@ -115,41 +106,29 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
115106
googmodule: false,
116107
untyped: true,
117108
convertIndexImportShorthand: false,
118-
transformDecorators,
119-
transformTypesToClosure,
109+
// Decorators are transformed as part of the Angular compiler programs. To avoid
110+
// conflicts, we disable decorator transformations for tsickle.
111+
transformDecorators: false,
112+
transformTypesToClosure: true,
120113
};
121114

122-
if (options.annotateForClosureCompiler || options.annotationsAs === 'static fields') {
123-
return ({
124-
program,
125-
targetSourceFile,
126-
writeFile,
127-
cancellationToken,
128-
emitOnlyDtsFiles,
129-
customTransformers = {},
130-
host,
131-
options
132-
}) =>
133-
// tslint:disable-next-line:no-require-imports only depend on tsickle if requested
134-
require('tsickle').emitWithTsickle(
135-
program, {...tsickleHost, options, host, moduleResolutionHost: host}, host, options,
136-
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, {
137-
beforeTs: customTransformers.before,
138-
afterTs: customTransformers.after,
139-
});
140-
} else {
141-
return ({
142-
program,
143-
targetSourceFile,
144-
writeFile,
145-
cancellationToken,
146-
emitOnlyDtsFiles,
147-
customTransformers = {},
148-
}) =>
149-
program.emit(
150-
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles,
151-
{after: customTransformers.after, before: customTransformers.before});
152-
}
115+
return ({
116+
program,
117+
targetSourceFile,
118+
writeFile,
119+
cancellationToken,
120+
emitOnlyDtsFiles,
121+
customTransformers = {},
122+
host,
123+
options
124+
}) =>
125+
// tslint:disable-next-line:no-require-imports only depend on tsickle if requested
126+
require('tsickle').emitWithTsickle(
127+
program, {...tsickleHost, options, host, moduleResolutionHost: host}, host, options,
128+
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, {
129+
beforeTs: customTransformers.before,
130+
afterTs: customTransformers.after,
131+
});
153132
}
154133

155134
export interface NgcParsedConfiguration extends ParsedConfiguration {

packages/compiler-cli/src/ngtsc/core/src/compiler.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ export class NgCompiler {
752752
/**
753753
* Determine if the given `Program` is @angular/core.
754754
*/
755-
function isAngularCorePackage(program: ts.Program): boolean {
755+
export function isAngularCorePackage(program: ts.Program): boolean {
756756
// Look for its_just_angular.ts somewhere in the program.
757757
const r3Symbols = getR3SymbolsFile(program);
758758
if (r3Symbols === null) {

0 commit comments

Comments
 (0)