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:
JoostK
2020-03-07 17:14:25 +01:00
committed by Kara Erickson
parent a5eb0e56b6
commit 75afd80ae8
10 changed files with 65 additions and 136 deletions
@@ -22,7 +22,6 @@ import {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalRef
import {InlineResourcesMetadataTransformer, getInlineResourcesTransformFactory} from './inline_resources';
import {LowerMetadataTransform, getExpressionLoweringTransformFactory} from './lower_expressions';
import {MetadataCache, MetadataTransformer} from './metadata_cache';
import {nocollapseHack} from './nocollapse_hack';
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
import {PartialModuleMetadataTransformer} from './r3_metadata_transform';
import {StripDecoratorsMetadataTransformer, getDecoratorStripTransformerFactory} from './r3_strip_decorators';
@@ -281,12 +280,6 @@ class AngularCompilerProgram implements Program {
const writeTsFile: ts.WriteFileCallback =
(outFileName, outData, writeByteOrderMark, onError?, sourceFiles?) => {
const sourceFile = sourceFiles && sourceFiles.length == 1 ? sourceFiles[0] : null;
let genFile: GeneratedFile|undefined;
if (this.options.annotateForClosureCompiler && sourceFile &&
TS.test(sourceFile.fileName)) {
outData = nocollapseHack(outData);
}
this.writeFile(outFileName, outData, writeByteOrderMark, onError, undefined, sourceFiles);
};
@@ -377,9 +370,6 @@ class AngularCompilerProgram implements Program {
emittedSourceFiles.push(originalFile);
}
}
if (this.options.annotateForClosureCompiler && TS.test(sourceFile.fileName)) {
outData = nocollapseHack(outData);
}
}
this.writeFile(outFileName, outData, writeByteOrderMark, onError, genFile, sourceFiles);
};
@@ -572,11 +562,13 @@ class AngularCompilerProgram implements Program {
getExpressionLoweringTransformFactory(this.loweringMetadataTransform, this.tsProgram));
metadataTransforms.push(this.loweringMetadataTransform);
}
const annotateForClosureCompiler = this.options.annotateForClosureCompiler || false;
if (genFiles) {
beforeTs.push(getAngularEmitterTransformFactory(genFiles, this.getTsProgram()));
beforeTs.push(getAngularEmitterTransformFactory(
genFiles, this.getTsProgram(), annotateForClosureCompiler));
}
if (partialModules) {
beforeTs.push(getAngularClassTransformerFactory(partialModules));
beforeTs.push(getAngularClassTransformerFactory(partialModules, annotateForClosureCompiler));
// If we have partial modules, the cached metadata might be incorrect as it doesn't reflect
// the partial module transforms.