fix(ngcc): use annotateForClosureCompiler option (#36652)
Adds @nocollapse to static properties added by ngcc iff annotateForClosureCompiler is true. The Closure Compiler will collapse static properties into the global namespace. Adding this annotation keeps the properties attached to their respective object, which allows them to be referenced via a class's constructor. The annotation is already added by ngtsc and ngc under the same option, this commit extends the functionality to ngcc. Closes #36618. PR Close #36652
This commit is contained in:
parent
e5b09cc49a
commit
8c682c52b1
|
@ -99,13 +99,13 @@ export class DecorationAnalyzer {
|
||||||
/* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat,
|
/* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat,
|
||||||
/* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer,
|
/* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer,
|
||||||
this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, NOOP_DEPENDENCY_TRACKER,
|
this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, NOOP_DEPENDENCY_TRACKER,
|
||||||
this.injectableRegistry, /* annotateForClosureCompiler */ false),
|
this.injectableRegistry, !!this.compilerOptions.annotateForClosureCompiler),
|
||||||
// See the note in ngtsc about why this cast is needed.
|
// See the note in ngtsc about why this cast is needed.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
new DirectiveDecoratorHandler(
|
new DirectiveDecoratorHandler(
|
||||||
this.reflectionHost, this.evaluator, this.fullRegistry, this.scopeRegistry,
|
this.reflectionHost, this.evaluator, this.fullRegistry, this.scopeRegistry,
|
||||||
this.fullMetaReader, NOOP_DEFAULT_IMPORT_RECORDER, this.injectableRegistry, this.isCore,
|
this.fullMetaReader, NOOP_DEFAULT_IMPORT_RECORDER, this.injectableRegistry, this.isCore,
|
||||||
/* annotateForClosureCompiler */ false,
|
!!this.compilerOptions.annotateForClosureCompiler,
|
||||||
// In ngcc we want to compile undecorated classes with Angular features. As of
|
// In ngcc we want to compile undecorated classes with Angular features. As of
|
||||||
// version 10, undecorated classes that use Angular features are no longer handled
|
// version 10, undecorated classes that use Angular features are no longer handled
|
||||||
// in ngtsc, but we want to ensure compatibility in ngcc for outdated libraries that
|
// in ngtsc, but we want to ensure compatibility in ngcc for outdated libraries that
|
||||||
|
@ -126,7 +126,7 @@ export class DecorationAnalyzer {
|
||||||
this.scopeRegistry, this.referencesRegistry, this.isCore, /* routeAnalyzer */ null,
|
this.scopeRegistry, this.referencesRegistry, this.isCore, /* routeAnalyzer */ null,
|
||||||
this.refEmitter,
|
this.refEmitter,
|
||||||
/* factoryTracker */ null, NOOP_DEFAULT_IMPORT_RECORDER,
|
/* factoryTracker */ null, NOOP_DEFAULT_IMPORT_RECORDER,
|
||||||
/* annotateForClosureCompiler */ false, this.injectableRegistry),
|
!!this.compilerOptions.annotateForClosureCompiler, this.injectableRegistry),
|
||||||
];
|
];
|
||||||
compiler = new NgccTraitCompiler(this.handlers, this.reflectionHost);
|
compiler = new NgccTraitCompiler(this.handlers, this.reflectionHost);
|
||||||
migrations: Migration[] = [
|
migrations: Migration[] = [
|
||||||
|
|
|
@ -94,7 +94,8 @@ export class Transformer {
|
||||||
// Transform the source files and source maps.
|
// Transform the source files and source maps.
|
||||||
const srcFormatter = this.getRenderingFormatter(ngccReflectionHost, bundle);
|
const srcFormatter = this.getRenderingFormatter(ngccReflectionHost, bundle);
|
||||||
|
|
||||||
const renderer = new Renderer(reflectionHost, srcFormatter, this.fs, this.logger, bundle);
|
const renderer =
|
||||||
|
new Renderer(reflectionHost, srcFormatter, this.fs, this.logger, bundle, this.tsConfig);
|
||||||
let renderedFiles = renderer.renderProgram(
|
let renderedFiles = renderer.renderProgram(
|
||||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,13 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {ConstantPool, Expression, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler';
|
import {CommentStmt, ConstantPool, Expression, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler';
|
||||||
import MagicString from 'magic-string';
|
import MagicString from 'magic-string';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {FileSystem} from '../../../src/ngtsc/file_system';
|
import {FileSystem} from '../../../src/ngtsc/file_system';
|
||||||
import {ImportManager} from '../../../src/ngtsc/translator';
|
import {ImportManager} from '../../../src/ngtsc/translator';
|
||||||
|
import {ParsedConfiguration} from '../../../src/perform_compile';
|
||||||
import {PrivateDeclarationsAnalyses} from '../analysis/private_declarations_analyzer';
|
import {PrivateDeclarationsAnalyses} from '../analysis/private_declarations_analyzer';
|
||||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
|
import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
|
||||||
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/types';
|
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/types';
|
||||||
|
@ -32,7 +33,8 @@ import {FileToWrite, getImportRewriter, stripExtension} from './utils';
|
||||||
export class Renderer {
|
export class Renderer {
|
||||||
constructor(
|
constructor(
|
||||||
private host: NgccReflectionHost, private srcFormatter: RenderingFormatter,
|
private host: NgccReflectionHost, private srcFormatter: RenderingFormatter,
|
||||||
private fs: FileSystem, private logger: Logger, private bundle: EntryPointBundle) {}
|
private fs: FileSystem, private logger: Logger, private bundle: EntryPointBundle,
|
||||||
|
private tsConfig: ParsedConfiguration|null = null) {}
|
||||||
|
|
||||||
renderProgram(
|
renderProgram(
|
||||||
decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses,
|
decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses,
|
||||||
|
@ -82,8 +84,9 @@ export class Renderer {
|
||||||
this.srcFormatter.removeDecorators(outputText, decoratorsToRemove);
|
this.srcFormatter.removeDecorators(outputText, decoratorsToRemove);
|
||||||
|
|
||||||
compiledFile.compiledClasses.forEach(clazz => {
|
compiledFile.compiledClasses.forEach(clazz => {
|
||||||
const renderedDefinition =
|
const renderedDefinition = this.renderDefinitions(
|
||||||
this.renderDefinitions(compiledFile.sourceFile, clazz, importManager);
|
compiledFile.sourceFile, clazz, importManager,
|
||||||
|
!!this.tsConfig?.options.annotateForClosureCompiler);
|
||||||
this.srcFormatter.addDefinitions(outputText, clazz, renderedDefinition);
|
this.srcFormatter.addDefinitions(outputText, clazz, renderedDefinition);
|
||||||
|
|
||||||
const renderedStatements =
|
const renderedStatements =
|
||||||
|
@ -160,12 +163,14 @@ export class Renderer {
|
||||||
* @param imports An object that tracks the imports that are needed by the rendered definitions.
|
* @param imports An object that tracks the imports that are needed by the rendered definitions.
|
||||||
*/
|
*/
|
||||||
private renderDefinitions(
|
private renderDefinitions(
|
||||||
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager): string {
|
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager,
|
||||||
|
annotateForClosureCompiler: boolean): string {
|
||||||
const name = this.host.getInternalNameOfClass(compiledClass.declaration);
|
const name = this.host.getInternalNameOfClass(compiledClass.declaration);
|
||||||
const statements: Statement[] = compiledClass.compilation.map(c => {
|
const statements: Statement[][] = compiledClass.compilation.map(c => {
|
||||||
return createAssignmentStatement(name, c.name, c.initializer);
|
return createAssignmentStatements(
|
||||||
|
name, c.name, c.initializer, annotateForClosureCompiler ? '* @nocollapse ' : undefined);
|
||||||
});
|
});
|
||||||
return this.renderStatements(sourceFile, statements, imports);
|
return this.renderStatements(sourceFile, Array.prototype.concat.apply([], statements), imports);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -208,8 +213,16 @@ export function renderConstantPool(
|
||||||
* compiled decorator to be applied to the class.
|
* compiled decorator to be applied to the class.
|
||||||
* @param analyzedClass The info about the class whose statement we want to create.
|
* @param analyzedClass The info about the class whose statement we want to create.
|
||||||
*/
|
*/
|
||||||
function createAssignmentStatement(
|
function createAssignmentStatements(
|
||||||
receiverName: ts.DeclarationName, propName: string, initializer: Expression): Statement {
|
receiverName: ts.DeclarationName, propName: string, initializer: Expression,
|
||||||
|
leadingComment?: string): Statement[] {
|
||||||
const receiver = new WrappedNodeExpr(receiverName);
|
const receiver = new WrappedNodeExpr(receiverName);
|
||||||
return new WritePropExpr(receiver, propName, initializer).toStmt();
|
const statements =
|
||||||
|
[new WritePropExpr(
|
||||||
|
receiver, propName, initializer, /* type */ undefined, /* sourceSpan */ undefined)
|
||||||
|
.toStmt()];
|
||||||
|
if (leadingComment !== undefined) {
|
||||||
|
statements.unshift(new CommentStmt(leadingComment, true));
|
||||||
|
}
|
||||||
|
return statements;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1541,6 +1541,22 @@ runInEachFileSystem(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('with Closure Compiler', () => {
|
||||||
|
it('should give closure annotated output with annotateForClosureCompiler: true', () => {
|
||||||
|
fs.writeFile(
|
||||||
|
_('/tsconfig.json'),
|
||||||
|
JSON.stringify({angularCompilerOptions: {annotateForClosureCompiler: true}}));
|
||||||
|
mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']});
|
||||||
|
const jsContents = fs.readFile(_(`/dist/local-package/index.js`));
|
||||||
|
expect(jsContents).toContain('/** @nocollapse */ \nAppComponent.ɵcmp =');
|
||||||
|
});
|
||||||
|
it('should default to not give closure annotated output', () => {
|
||||||
|
mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']});
|
||||||
|
const jsContents = fs.readFile(_(`/dist/local-package/index.js`));
|
||||||
|
expect(jsContents).not.toContain('/** @nocollapse */');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('with configuration files', () => {
|
describe('with configuration files', () => {
|
||||||
it('should process a configured deep-import as an entry-point', () => {
|
it('should process a configured deep-import as an entry-point', () => {
|
||||||
loadTestFiles([
|
loadTestFiles([
|
||||||
|
|
|
@ -187,8 +187,14 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor
|
||||||
return ts.createThrow(stmt.error.visitExpression(this, context.withExpressionMode));
|
return ts.createThrow(stmt.error.visitExpression(this, context.withExpressionMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitCommentStmt(stmt: CommentStmt, context: Context): never {
|
visitCommentStmt(stmt: CommentStmt, context: Context): ts.NotEmittedStatement {
|
||||||
throw new Error('Method not implemented.');
|
const commentStmt = ts.createNotEmittedStatement(ts.createLiteral(''));
|
||||||
|
ts.addSyntheticLeadingComment(
|
||||||
|
commentStmt,
|
||||||
|
stmt.multiline ? ts.SyntaxKind.MultiLineCommentTrivia :
|
||||||
|
ts.SyntaxKind.SingleLineCommentTrivia,
|
||||||
|
stmt.comment, /** hasTrailingNewLine */ false);
|
||||||
|
return commentStmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: Context): ts.NotEmittedStatement {
|
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: Context): ts.NotEmittedStatement {
|
||||||
|
|
Loading…
Reference in New Issue