From 56644fe70e07344a8a80cc4312f780aae2d2ee20 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Wed, 31 Jul 2019 15:20:56 -0700 Subject: [PATCH] fix(ivy): move setClassMetadata calls into a pure iife This commit transforms the setClassMetadata calls generated by ngtsc from: ```typescript /*@__PURE__*/ setClassMetadata(...); ``` to: ```typescript /*@__PURE__*/ (function() { setClassMetadata(...); })(); ``` This is required for the build optimizer and side-effect-free analysis to function correctly. --- .../ngcc/test/rendering/renderer_spec.ts | 12 ++++++------ .../src/ngtsc/annotations/src/metadata.ts | 10 +++++++--- .../src/ngtsc/annotations/test/metadata_spec.ts | 4 ++-- packages/compiler-cli/test/ngtsc/ngtsc_spec.ts | 15 +++++++++++++++ packages/compiler/src/compiler.ts | 2 +- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts index 4dbd749e4263d1..a27a1f39a3fcbc 100644 --- a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts @@ -194,10 +194,10 @@ A.ɵcmp = ɵngcc0.ɵɵdefineComponent({ type: A, selectors: [["a"]], decls: 1, v } if (rf & 2) { ɵngcc0.ɵɵtextInterpolate(ctx.person.name); } }, encapsulation: 2 }); -/*@__PURE__*/ ɵngcc0.ɵsetClassMetadata(A, [{ +/*@__PURE__*/ (function () { ɵngcc0.ɵsetClassMetadata(A, [{ type: Component, args: [{ selector: 'a', template: '{{ person!.name }}' }] - }], null, null);`); + }], null, null); })();`); }); @@ -231,10 +231,10 @@ A.ɵcmp = ɵngcc0.ɵɵdefineComponent({ type: A, selectors: [["a"]], decls: 1, v expect(addDefinitionsSpy.calls.first().args[2]) .toEqual(`A.ɵfac = function A_Factory(t) { return new (t || A)(); }; A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] }); -/*@__PURE__*/ ɵngcc0.ɵsetClassMetadata(A, [{ +/*@__PURE__*/ (function () { ɵngcc0.ɵsetClassMetadata(A, [{ type: Directive, args: [{ selector: '[a]' }] - }], null, { foo: [] });`); + }], null, { foo: [] }); })();`); }); it('should call removeDecorators with the source code, a map of class decorators that have been analyzed', @@ -388,7 +388,7 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] }); decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addDefinitionsSpy = testFormatter.addDefinitions as jasmine.Spy; expect(addDefinitionsSpy.calls.first().args[2]) - .toContain(`/*@__PURE__*/ ɵngcc0.setClassMetadata(`); + .toContain(`/*@__PURE__*/ (function () { ɵngcc0.setClassMetadata(`); const addImportsSpy = testFormatter.addImports as jasmine.Spy; expect(addImportsSpy.calls.first().args[1]).toEqual([ {specifier: './r3_symbols', qualifier: 'ɵngcc0'} @@ -408,7 +408,7 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] }); decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addDefinitionsSpy = testFormatter.addDefinitions as jasmine.Spy; expect(addDefinitionsSpy.calls.first().args[2]) - .toContain(`/*@__PURE__*/ setClassMetadata(`); + .toContain(`/*@__PURE__*/ (function () { setClassMetadata(`); const addImportsSpy = testFormatter.addImports as jasmine.Spy; expect(addImportsSpy.calls.first().args[1]).toEqual([]); }); diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts b/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts index 595a9957cf675c..d29caa1c049a55 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, ReturnStatement, Statement, WrappedNodeExpr, literalMap} from '@angular/compiler'; +import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NONE_TYPE, ReturnStatement, Statement, WrappedNodeExpr, literalMap} from '@angular/compiler'; import * as ts from 'typescript'; import {DefaultImportRecorder} from '../../imports'; @@ -74,11 +74,15 @@ export function generateSetClassMetadataCall( new WrappedNodeExpr(metaDecorators), metaCtorParameters, new WrappedNodeExpr(metaPropDecorators), - ], + ]); + const iifeFn = new FunctionExpr([], [fnCall.toStmt()], NONE_TYPE); + const iife = new InvokeFunctionExpr( + /* fn */ iifeFn, + /* args */[], /* type */ undefined, /* sourceSpan */ undefined, /* pure */ true); - return fnCall.toStmt(); + return iife.toStmt(); } /** diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts index de27ae47befc4b..783dd7990fba9e 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts @@ -23,7 +23,7 @@ runInEachFileSystem(() => { @Component('metadata') class Target {} `); expect(res).toEqual( - `/*@__PURE__*/ i0.ɵsetClassMetadata(Target, [{ type: Component, args: ['metadata'] }], null, null);`); + `/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(Target, [{ type: Component, args: ['metadata'] }], null, null); })();`); }); it('should convert namespaced decorated class metadata', () => { @@ -33,7 +33,7 @@ runInEachFileSystem(() => { @core.Component('metadata') class Target {} `); expect(res).toEqual( - `/*@__PURE__*/ i0.ɵsetClassMetadata(Target, [{ type: core.Component, args: ['metadata'] }], null, null);`); + `/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(Target, [{ type: core.Component, args: ['metadata'] }], null, null); })();`); }); it('should convert decorated class constructor parameter metadata', () => { diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 35c8e36c204f93..012fb55e14929c 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -2597,6 +2597,21 @@ runInEachFileSystem(os => { expect(jsContents).toContain('directives: function () { return [CmpB]; }'); }); + it('should wrap setClassMetadata in an iife', () => { + env.write('test.ts', ` + import {Injectable} from '@angular/core'; + + @Injectable({providedIn: 'root'}) + export class Service {} + `); + + env.driveMain(); + const jsContents = env.getContents('test.js').replace(/\s+/g, ' '); + expect(jsContents) + .toContain( + `/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(Service, [{ type: Injectable, args: [{ providedIn: 'root' }] }], null, null); })();`); + }); + it('should emit setClassMetadata calls for all types', () => { env.write('test.ts', ` import {Component, Directive, Injectable, NgModule, Pipe} from '@angular/core'; diff --git a/packages/compiler/src/compiler.ts b/packages/compiler/src/compiler.ts index 7f9f6bb9dd6e34..10c1a994da1dd2 100644 --- a/packages/compiler/src/compiler.ts +++ b/packages/compiler/src/compiler.ts @@ -78,7 +78,7 @@ export * from './ml_parser/tags'; export {LexerRange} from './ml_parser/lexer'; export * from './ml_parser/xml_parser'; export {NgModuleCompiler} from './ng_module_compiler'; -export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences} from './output/output_ast'; +export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, NONE_TYPE, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences} from './output/output_ast'; export {EmitterVisitorContext} from './output/abstract_emitter'; export {JitEvaluator} from './output/output_jit'; export * from './output/ts_emitter';