refactor(compiler): add @nocollapse
annotation using a synthetic comment (#35932)
In Ivy, Angular decorators are compiled into static fields that are inserted into a class declaration in a TypeScript transform. When targeting Closure compiler such fields need to be annotated with `@nocollapse` to prevent them from being lifted from a static field into a variable, as that would prevent the Ivy runtime from being able to find the compiled definitions. Previously, there was a bug in TypeScript where synthetic comments added in a transform would not be emitted at all, so as a workaround a global regex-replace was done in the emit's `writeFile` callback that would add the `@nocollapse` annotation to all static Ivy definition fields. This approach is no longer possible when ngtsc is running as TypeScript plugin, as a plugin cannot control emit behavior. The workaround is no longer necessary, as synthetic comments are now properly emitted, likely as of https://github.com/microsoft/TypeScript/pull/22141 which has been released with TypeScript 2.8. This change is required for running ngtsc as TypeScript plugin in Bazel's `ts_library` rule, to move away from the custom `ngc_wrapped` approach. Resolves FW-1952 PR Close #35932
This commit is contained in:
@ -10,7 +10,6 @@ import {GeneratedFile} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import * as api from '../transformers/api';
|
||||
import {nocollapseHack} from '../transformers/nocollapse_hack';
|
||||
import {verifySupportedTypeScriptVersion} from '../typescript_support';
|
||||
|
||||
import {NgCompilerHost} from './core';
|
||||
@ -193,16 +192,6 @@ export class NgtscProgram implements api.Program {
|
||||
this.compiler.incrementalDriver.recordSuccessfulEmit(writtenSf);
|
||||
}
|
||||
}
|
||||
|
||||
// If Closure annotations are being produced, tsickle should be adding `@nocollapse` to
|
||||
// any static fields present. However, tsickle doesn't yet handle synthetic fields added
|
||||
// during other transformations, so this hack is in place to ensure Ivy definitions get
|
||||
// properly annotated, pending an upstream fix in tsickle.
|
||||
//
|
||||
// TODO(alxhub): remove when tsickle properly annotates synthetic fields.
|
||||
if (this.closureCompilerEnabled && fileName.endsWith('.js')) {
|
||||
data = nocollapseHack(data);
|
||||
}
|
||||
this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
};
|
||||
|
||||
|
@ -47,7 +47,8 @@ class IvyVisitor extends Visitor {
|
||||
constructor(
|
||||
private compilation: TraitCompiler, private reflector: ReflectionHost,
|
||||
private importManager: ImportManager, private defaultImportRecorder: DefaultImportRecorder,
|
||||
private isCore: boolean, private constantPool: ConstantPool) {
|
||||
private isClosureCompilerEnabled: boolean, private isCore: boolean,
|
||||
private constantPool: ConstantPool) {
|
||||
super();
|
||||
}
|
||||
|
||||
@ -73,6 +74,16 @@ class IvyVisitor extends Visitor {
|
||||
undefined, [ts.createToken(ts.SyntaxKind.StaticKeyword)], field.name, undefined,
|
||||
undefined, exprNode);
|
||||
|
||||
if (this.isClosureCompilerEnabled) {
|
||||
// Closure compiler transforms the form `Service.ɵprov = X` into `Service$ɵprov = X`. To
|
||||
// prevent this transformation, such assignments need to be annotated with @nocollapse.
|
||||
// Note that tsickle is typically responsible for adding such annotations, however it
|
||||
// doesn't yet handle synthetic fields added during other transformations.
|
||||
ts.addSyntheticLeadingComment(
|
||||
property, ts.SyntaxKind.MultiLineCommentTrivia, '* @nocollapse ',
|
||||
/* hasTrailingNewLine */ false);
|
||||
}
|
||||
|
||||
field.statements
|
||||
.map(
|
||||
stmt => translateStatement(
|
||||
@ -215,7 +226,8 @@ function transformIvySourceFile(
|
||||
|
||||
// Recursively scan through the AST and perform any updates requested by the IvyCompilation.
|
||||
const visitor = new IvyVisitor(
|
||||
compilation, reflector, importManager, defaultImportRecorder, isCore, constantPool);
|
||||
compilation, reflector, importManager, defaultImportRecorder, isClosureCompilerEnabled,
|
||||
isCore, constantPool);
|
||||
let sf = visit(file, visitor, context);
|
||||
|
||||
// Generate the constant statements first, as they may involve adding additional imports
|
||||
|
Reference in New Issue
Block a user