From cac130eff9b9cb608f2308ae40c42c9cd1850c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mis=CC=8Cko=20Hevery?= Date: Tue, 8 Aug 2017 14:03:27 -0700 Subject: [PATCH] perf(core): Remove decorator DSL which depends on Reflect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE It is no longer possible to declare classes in this format. ``` Component({...}). Class({ constructor: function() {...} }) ``` This format would only work with JIT and with ES5. This mode doesn’t allow build tools like Webpack to process and optimize the code, which results in prohibitively large bundles. We are removing this API because we are trying to ensure that everyone is on the fast path by default, and it is not possible to get on the fast path using the ES5 DSL. The replacement is to use TypeScript and `@Decorator` format. ``` @Component({...}) class { constructor() {...} } ``` --- packages/core/src/core.ts | 2 +- packages/core/src/di/reflective_injector.ts | 5 +- packages/core/src/metadata/di.ts | 12 - .../platform_reflection_capabilities.ts | 12 + .../src/reflection/reflection_capabilities.ts | 22 +- packages/core/src/util/decorators.ts | 261 +------ .../core/test/metadata/decorators_spec.ts | 32 - packages/core/test/util/decorators_spec.ts | 101 +-- .../upgrade/src/dynamic/upgrade_adapter.ts | 26 +- .../src/dynamic/upgrade_ng1_adapter.ts | 37 +- packages/upgrade/test/dynamic/upgrade_spec.ts | 688 ++++++++++-------- tools/public_api_guard/core/core.d.ts | 13 - 12 files changed, 456 insertions(+), 755 deletions(-) delete mode 100644 packages/core/test/metadata/decorators_spec.ts diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 772da782a8..2ce897f2de 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -13,7 +13,7 @@ */ export * from './metadata'; export * from './version'; -export {Class, ClassDefinition, TypeDecorator} from './util/decorators'; +export {TypeDecorator} from './util/decorators'; export * from './di'; export {createPlatform, assertPlatform, destroyPlatform, getPlatform, PlatformRef, ApplicationRef, enableProdMode, isDevMode, createPlatformFactory, NgProbeToken} from './application_ref'; export {APP_ID, PACKAGE_ROOT_URL, PLATFORM_INITIALIZER, PLATFORM_ID, APP_BOOTSTRAP_LISTENER} from './application_tokens'; diff --git a/packages/core/src/di/reflective_injector.ts b/packages/core/src/di/reflective_injector.ts index 5e7e0a481c..6b7f4c47d0 100644 --- a/packages/core/src/di/reflective_injector.ts +++ b/packages/core/src/di/reflective_injector.ts @@ -277,6 +277,7 @@ export abstract class ReflectiveInjector implements Injector { } export class ReflectiveInjector_ implements ReflectiveInjector { + private static INJECTOR_KEY = ReflectiveKey.get(Injector); /** @internal */ _constructionCounter: number = 0; /** @internal */ @@ -389,7 +390,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector { } private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf|null, notFoundValue: any): any { - if (key === INJECTOR_KEY) { + if (key === ReflectiveInjector_.INJECTOR_KEY) { return this; } @@ -463,8 +464,6 @@ export class ReflectiveInjector_ implements ReflectiveInjector { toString(): string { return this.displayName; } } -const INJECTOR_KEY = ReflectiveKey.get(Injector); - function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] { const res: any[] = new Array(injector._providers.length); for (let i = 0; i < injector._providers.length; ++i) { diff --git a/packages/core/src/metadata/di.ts b/packages/core/src/metadata/di.ts index 5efabd0033..e12522be12 100644 --- a/packages/core/src/metadata/di.ts +++ b/packages/core/src/metadata/di.ts @@ -73,18 +73,6 @@ export interface AttributeDecorator { * * {@example core/ts/metadata/metadata.ts region='attributeFactory'} * - * ### Example as ES5 DSL - * - * ``` - * var MyComponent = ng - * .Component({...}) - * .Class({ - * constructor: [new ng.Attribute('title'), function(title) { - * ... - * }] - * }) - * ``` - * * ### Example as ES5 annotation * * ``` diff --git a/packages/core/src/reflection/platform_reflection_capabilities.ts b/packages/core/src/reflection/platform_reflection_capabilities.ts index 22fa0cd656..8cc5b7862b 100644 --- a/packages/core/src/reflection/platform_reflection_capabilities.ts +++ b/packages/core/src/reflection/platform_reflection_capabilities.ts @@ -13,8 +13,20 @@ export interface PlatformReflectionCapabilities { isReflectionEnabled(): boolean; factory(type: Type): Function; hasLifecycleHook(type: any, lcProperty: string): boolean; + + /** + * Return a list of annotations/types for constructor parameters + */ parameters(type: Type): any[][]; + + /** + * Return a list of annotations declared on the class + */ annotations(type: Type): any[]; + + /** + * Return a object literal which describes the annotations on Class fields/properties. + */ propMetadata(typeOrFunc: Type): {[key: string]: any[]}; getter(name: string): GetterFn; setter(name: string): SetterFn; diff --git a/packages/core/src/reflection/reflection_capabilities.ts b/packages/core/src/reflection/reflection_capabilities.ts index b7aa346ebf..4f74baf886 100644 --- a/packages/core/src/reflection/reflection_capabilities.ts +++ b/packages/core/src/reflection/reflection_capabilities.ts @@ -8,9 +8,12 @@ import {Type, isType} from '../type'; import {global, stringify} from '../util'; +import {ANNOTATIONS, PARAMETERS, PROP_METADATA} from '../util/decorators'; + import {PlatformReflectionCapabilities} from './platform_reflection_capabilities'; import {GetterFn, MethodFn, SetterFn} from './types'; + /** * Attention: This regex has to hold even if the code is minified! */ @@ -85,12 +88,11 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities { } // API for metadata created by invoking the decorators. - if (this._reflect != null && this._reflect.getOwnMetadata != null) { - const paramAnnotations = this._reflect.getOwnMetadata('parameters', type); - const paramTypes = this._reflect.getOwnMetadata('design:paramtypes', type); - if (paramTypes || paramAnnotations) { - return this._zipTypesAndAnnotations(paramTypes, paramAnnotations); - } + const paramAnnotations = type.hasOwnProperty(PARAMETERS) && (type as any)[PARAMETERS]; + const paramTypes = this._reflect && this._reflect.getOwnMetadata && + this._reflect.getOwnMetadata('design:paramtypes', type); + if (paramTypes || paramAnnotations) { + return this._zipTypesAndAnnotations(paramTypes, paramAnnotations); } // If a class has no decorators, at least create metadata @@ -130,8 +132,8 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities { } // API for metadata created by invoking the decorators. - if (this._reflect && this._reflect.getOwnMetadata) { - return this._reflect.getOwnMetadata('annotations', typeOrFunc); + if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) { + return (typeOrFunc as any)[ANNOTATIONS]; } return null; } @@ -169,8 +171,8 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities { } // API for metadata created by invoking the decorators. - if (this._reflect && this._reflect.getOwnMetadata) { - return this._reflect.getOwnMetadata('propMetadata', typeOrFunc); + if (typeOrFunc.hasOwnProperty(PROP_METADATA)) { + return (typeOrFunc as any)[PROP_METADATA]; } return null; } diff --git a/packages/core/src/util/decorators.ts b/packages/core/src/util/decorators.ts index 1acf99b60b..2aae8c6a1d 100644 --- a/packages/core/src/util/decorators.ts +++ b/packages/core/src/util/decorators.ts @@ -7,54 +7,12 @@ */ import {Type} from '../type'; -import {global, stringify} from '../util'; - -let _nextClassId = 0; -const Reflect = global['Reflect']; - -/** - * Declares the interface to be used with {@link Class}. - * - * @stable - */ -export type ClassDefinition = { - /** - * Optional argument for specifying the superclass. - */ - extends?: Type; - - /** - * Required constructor function for a class. - * - * The function may be optionally wrapped in an `Array`, in which case additional parameter - * annotations may be specified. - * The number of arguments and the number of parameter annotations must match. - * - * See {@link Class} for example of usage. - */ - constructor: Function | any[]; -} & -{ - /** - * Other methods on the class. Note that values should have type 'Function' but TS requires - * all properties to have a narrower type than the index signature. - */ - [x: string]: Type|Function|any[]; -}; /** * An interface implemented by all Angular type decorators, which allows them to be used as ES7 * decorators as well as * Angular DSL syntax. * - * DSL syntax: - * - * ``` - * var MyClass = ng - * .Component({...}) - * .Class({...}); - * ``` - * * ES7 syntax: * * ``` @@ -74,189 +32,11 @@ export interface TypeDecorator { // so we cannot declare this interface as a subtype. // see https://github.com/angular/angular/issues/3379#issuecomment-126169417 (target: Object, propertyKey?: string|symbol, parameterIndex?: number): void; - - /** - * Storage for the accumulated annotations so far used by the DSL syntax. - * - * Used by {@link Class} to annotate the generated class. - */ - annotations: any[]; - - /** - * Generate a class from the definition and annotate it with {@link TypeDecorator#annotations}. - */ - Class(obj: ClassDefinition): Type; } -function extractAnnotation(annotation: any): any { - if (typeof annotation === 'function' && annotation.hasOwnProperty('annotation')) { - // it is a decorator, extract annotation - annotation = annotation.annotation; - } - return annotation; -} - -function applyParams(fnOrArray: Function | any[] | undefined, key: string): Function { - if (fnOrArray === Object || fnOrArray === String || fnOrArray === Function || - fnOrArray === Number || fnOrArray === Array) { - throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`); - } - - if (typeof fnOrArray === 'function') { - return fnOrArray; - } - - if (Array.isArray(fnOrArray)) { - const annotations: any[] = fnOrArray as any[]; - const annoLength = annotations.length - 1; - const fn: Function = fnOrArray[annoLength]; - if (typeof fn !== 'function') { - throw new Error( - `Last position of Class method array must be Function in key ${key} was '${stringify(fn)}'`); - } - if (annoLength != fn.length) { - throw new Error( - `Number of annotations (${annoLength}) does not match number of arguments (${fn.length}) in the function: ${stringify(fn)}`); - } - const paramsAnnotations: any[][] = []; - for (let i = 0, ii = annotations.length - 1; i < ii; i++) { - const paramAnnotations: any[] = []; - paramsAnnotations.push(paramAnnotations); - const annotation = annotations[i]; - if (Array.isArray(annotation)) { - for (let j = 0; j < annotation.length; j++) { - paramAnnotations.push(extractAnnotation(annotation[j])); - } - } else if (typeof annotation === 'function') { - paramAnnotations.push(extractAnnotation(annotation)); - } else { - paramAnnotations.push(annotation); - } - } - Reflect.defineMetadata('parameters', paramsAnnotations, fn); - return fn; - } - - throw new Error( - `Only Function or Array is supported in Class definition for key '${key}' is '${stringify(fnOrArray)}'`); -} - -/** - * Provides a way for expressing ES6 classes with parameter annotations in ES5. - * - * ## Basic Example - * - * ``` - * var Greeter = ng.Class({ - * constructor: function(name) { - * this.name = name; - * }, - * - * greet: function() { - * alert('Hello ' + this.name + '!'); - * } - * }); - * ``` - * - * is equivalent to ES6: - * - * ``` - * class Greeter { - * constructor(name) { - * this.name = name; - * } - * - * greet() { - * alert('Hello ' + this.name + '!'); - * } - * } - * ``` - * - * or equivalent to ES5: - * - * ``` - * var Greeter = function (name) { - * this.name = name; - * } - * - * Greeter.prototype.greet = function () { - * alert('Hello ' + this.name + '!'); - * } - * ``` - * - * ### Example with parameter annotations - * - * ``` - * var MyService = ng.Class({ - * constructor: [String, [new Optional(), Service], function(name, myService) { - * ... - * }] - * }); - * ``` - * - * is equivalent to ES6: - * - * ``` - * class MyService { - * constructor(name: string, @Optional() myService: Service) { - * ... - * } - * } - * ``` - * - * ### Example with inheritance - * - * ``` - * var Shape = ng.Class({ - * constructor: (color) { - * this.color = color; - * } - * }); - * - * var Square = ng.Class({ - * extends: Shape, - * constructor: function(color, size) { - * Shape.call(this, color); - * this.size = size; - * } - * }); - * ``` - * @suppress {globalThis} - * @stable - */ -export function Class(clsDef: ClassDefinition): Type { - const constructor = applyParams( - clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor'); - - let proto = constructor.prototype; - - if (clsDef.hasOwnProperty('extends')) { - if (typeof clsDef.extends === 'function') { - (constructor).prototype = proto = - Object.create((clsDef.extends).prototype); - } else { - throw new Error( - `Class definition 'extends' property must be a constructor function was: ${stringify(clsDef.extends)}`); - } - } - - for (const key in clsDef) { - if (key !== 'extends' && key !== 'prototype' && clsDef.hasOwnProperty(key)) { - proto[key] = applyParams(clsDef[key], key); - } - } - - if (this && this.annotations instanceof Array) { - Reflect.defineMetadata('annotations', this.annotations, constructor); - } - - const constructorName = constructor['name']; - if (!constructorName || constructorName === 'constructor') { - (constructor as any)['overriddenName'] = `class${_nextClassId++}`; - } - - return >constructor; -} +export const ANNOTATIONS = '__annotations__'; +export const PARAMETERS = '__paramaters__'; +export const PROP_METADATA = '__prop__metadata__'; /** * @suppress {globalThis} @@ -268,27 +48,21 @@ export function makeDecorator( const metaCtor = makeMetadataCtor(props); function DecoratorFactory(objOrType: any): (cls: any) => any { - if (!(Reflect && Reflect.getOwnMetadata)) { - throw 'reflect-metadata shim is required when using class decorators'; - } - if (this instanceof DecoratorFactory) { metaCtor.call(this, objOrType); return this; } const annotationInstance = new (DecoratorFactory)(objOrType); - const chainAnnotation = - typeof this === 'function' && Array.isArray(this.annotations) ? this.annotations : []; - chainAnnotation.push(annotationInstance); const TypeDecorator: TypeDecorator = function TypeDecorator(cls: Type) { - const annotations = Reflect.getOwnMetadata('annotations', cls) || []; + // Use of Object.defineProperty is important since it creates non-enumerable property which + // prevents the property is copied during subclassing. + const annotations = cls.hasOwnProperty(ANNOTATIONS) ? + (cls as any)[ANNOTATIONS] : + Object.defineProperty(cls, ANNOTATIONS, {value: []})[ANNOTATIONS]; annotations.push(annotationInstance); - Reflect.defineMetadata('annotations', annotations, cls); return cls; }; - TypeDecorator.annotations = chainAnnotation; - TypeDecorator.Class = Class; if (chainFn) chainFn(TypeDecorator); return TypeDecorator; } @@ -327,7 +101,11 @@ export function makeParamDecorator( return ParamDecorator; function ParamDecorator(cls: any, unusedKey: any, index: number): any { - const parameters: (any[] | null)[] = Reflect.getOwnMetadata('parameters', cls) || []; + // Use of Object.defineProperty is important since it creates non-enumerable property which + // prevents the property is copied during subclassing. + const parameters = cls.hasOwnProperty(PARAMETERS) ? + (cls as any)[PARAMETERS] : + Object.defineProperty(cls, PARAMETERS, {value: []})[PARAMETERS]; // there might be gaps if some in between parameters do not have annotations. // we pad with nulls. @@ -335,10 +113,7 @@ export function makeParamDecorator( parameters.push(null); } - parameters[index] = parameters[index] || []; - parameters[index] !.push(annotationInstance); - - Reflect.defineMetadata('parameters', parameters, cls); + (parameters[index] = parameters[index] || []).push(annotationInstance); return cls; } } @@ -363,10 +138,14 @@ export function makePropDecorator( const decoratorInstance = new (PropDecoratorFactory)(...args); return function PropDecorator(target: any, name: string) { - const meta = Reflect.getOwnMetadata('propMetadata', target.constructor) || {}; + const constructor = target.constructor; + // Use of Object.defineProperty is important since it creates non-enumerable property which + // prevents the property is copied during subclassing. + const meta = constructor.hasOwnProperty(PROP_METADATA) ? + (constructor as any)[PROP_METADATA] : + Object.defineProperty(constructor, PROP_METADATA, {value: {}})[PROP_METADATA]; meta[name] = meta.hasOwnProperty(name) && meta[name] || []; meta[name].unshift(decoratorInstance); - Reflect.defineMetadata('propMetadata', meta, target.constructor); }; } diff --git a/packages/core/test/metadata/decorators_spec.ts b/packages/core/test/metadata/decorators_spec.ts deleted file mode 100644 index bff20fbf66..0000000000 --- a/packages/core/test/metadata/decorators_spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * 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 - */ - -import {Component, Directive} from '@angular/core'; -import {reflector} from '@angular/core/src/reflection/reflection'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; - -export function main() { - describe('es5 decorators', () => { - it('should declare directive class', () => { - const MyDirective = Directive({}).Class({constructor: function() { this.works = true; }}); - expect(new MyDirective().works).toEqual(true); - }); - - it('should declare Component class', () => { - const MyComponent = Component({}).Class({constructor: function() { this.works = true; }}); - expect(new MyComponent().works).toEqual(true); - }); - - it('should create type in ES5', () => { - class MyComponent {}; - let as: any /** TODO #9100 */; - (MyComponent).annotations = as = Component({}); - expect(reflector.annotations(MyComponent)).toEqual(as.annotations); - }); - }); -} diff --git a/packages/core/test/util/decorators_spec.ts b/packages/core/test/util/decorators_spec.ts index aa7a8cadda..7de8af451f 100644 --- a/packages/core/test/util/decorators_spec.ts +++ b/packages/core/test/util/decorators_spec.ts @@ -6,17 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject} from '@angular/core'; -import {reflector} from '@angular/core/src/reflection/reflection'; -import {global} from '@angular/core/src/util'; -import {Class, makeDecorator, makePropDecorator} from '@angular/core/src/util/decorators'; +import {reflector} from '../../src/reflection/reflection'; +import {ANNOTATIONS, makeDecorator, makePropDecorator} from '../../src/util/decorators'; class DecoratedParent {} class DecoratedChild extends DecoratedParent {} export function main() { - const Reflect = global['Reflect']; - const TerminalDecorator = makeDecorator('TerminalDecorator', (data: any) => ({terminal: true, ...data})); const TestDecorator = makeDecorator( @@ -54,7 +50,7 @@ export function main() { it('should invoke as decorator', () => { function Type() {} TestDecorator({marker: 'WORKS'})(Type); - const annotations = Reflect.getOwnMetadata('annotations', Type); + const annotations = (Type as any)[ANNOTATIONS]; expect(annotations[0].marker).toEqual('WORKS'); }); @@ -64,102 +60,13 @@ export function main() { expect(annotation.marker).toEqual('WORKS'); }); - it('should invoke as chain', () => { - let chain: any = TestDecorator({marker: 'WORKS'}); - expect(typeof chain.Terminal).toEqual('function'); - chain = chain.Terminal(); - expect(chain.annotations[0] instanceof TestDecorator).toEqual(true); - expect(chain.annotations[0].marker).toEqual('WORKS'); - expect(chain.annotations[1] instanceof TerminalDecorator).toEqual(true); - }); - it('should not apply decorators from the prototype chain', function() { TestDecorator({marker: 'parent'})(DecoratedParent); TestDecorator({marker: 'child'})(DecoratedChild); - const annotations = Reflect.getOwnMetadata('annotations', DecoratedChild); + const annotations = (DecoratedChild as any)[ANNOTATIONS]; expect(annotations.length).toBe(1); expect(annotations[0].marker).toEqual('child'); }); - - describe('Class', () => { - it('should create a class', () => { - let i0: any; - let i1: any; - const MyClass = (TestDecorator({marker: 'test-works'})).Class({ - extends: Class({ - constructor: function() {}, - extendWorks: function() { return 'extend ' + this.arg; } - }), - constructor: [String, function(arg: any) { this.arg = arg; }], - methodA: [ - i0 = new Inject(String), - [i1 = Inject(String), Number], - function(a: any, b: any) {}, - ], - works: function() { return this.arg; }, - prototype: 'IGNORE' - }); - - const obj: any = new MyClass('WORKS'); - expect(obj.arg).toEqual('WORKS'); - expect(obj.works()).toEqual('WORKS'); - expect(obj.extendWorks()).toEqual('extend WORKS'); - expect(reflector.parameters(MyClass)).toEqual([[String]]); - expect(reflector.parameters(obj.methodA)).toEqual([[i0], [i1.annotation, Number]]); - - const proto = (MyClass).prototype; - expect(proto.extends).toEqual(undefined); - expect(proto.prototype).toEqual(undefined); - - expect(reflector.annotations(MyClass)[0].marker).toEqual('test-works'); - }); - - describe('errors', () => { - it('should ensure that last constructor is required', () => { - expect(() => { (Class)({}); }) - .toThrowError( - 'Only Function or Array is supported in Class definition for key \'constructor\' is \'undefined\''); - }); - - - it('should ensure that we dont accidentally patch native objects', () => { - expect(() => { - (Class)({constructor: Object}); - }).toThrowError('Can not use native Object as constructor'); - }); - - - it('should ensure that last position is function', () => { - expect(() => { Class({constructor: []}); }) - .toThrowError( - 'Last position of Class method array must be Function in key constructor was \'undefined\''); - }); - - it('should ensure that annotation count matches parameters count', () => { - expect(() => { - Class({constructor: [String, function MyType() {}]}); - }) - .toThrowError( - 'Number of annotations (1) does not match number of arguments (0) in the function: MyType'); - }); - - it('should ensure that only Function|Arrays are supported', () => { - expect(() => { Class({constructor: function() {}, method: 'non_function'}); }) - .toThrowError( - 'Only Function or Array is supported in Class definition for key \'method\' is \'non_function\''); - }); - - it('should ensure that extends is a Function', () => { - expect(() => { Class({extends: 'non_type', constructor: function() {}}); }) - .toThrowError( - 'Class definition \'extends\' property must be a constructor function was: non_type'); - }); - - it('should assign an overridden name for anonymous constructor functions', () => { - expect((Class({constructor: function() {}}) as any).overriddenName).not.toBeUndefined(); - }); - }); - }); }); } diff --git a/packages/upgrade/src/dynamic/upgrade_adapter.ts b/packages/upgrade/src/dynamic/upgrade_adapter.ts index f4e744007b..8cc8e65c36 100644 --- a/packages/upgrade/src/dynamic/upgrade_adapter.ts +++ b/packages/upgrade/src/dynamic/upgrade_adapter.ts @@ -551,19 +551,19 @@ export class UpgradeAdapter { .then(() => { // At this point we have ng1 injector and we have prepared // ng1 components to be upgraded, we now can bootstrap ng2. - const DynamicNgUpgradeModule = - NgModule({ - providers: [ - {provide: $INJECTOR, useFactory: () => ng1Injector}, - {provide: $COMPILE, useFactory: () => ng1Injector.get($COMPILE)}, - this.upgradedProviders - ], - imports: [this.ng2AppModule], - entryComponents: this.downgradedComponents - }).Class({ - constructor: function DynamicNgUpgradeModule() {}, - ngDoBootstrap: function() {} - }); + @NgModule({ + providers: [ + {provide: $INJECTOR, useFactory: () => ng1Injector}, + {provide: $COMPILE, useFactory: () => ng1Injector.get($COMPILE)}, + this.upgradedProviders + ], + imports: [this.ng2AppModule], + entryComponents: this.downgradedComponents + }) + class DynamicNgUpgradeModule { + constructor() {} + ngDoBootstrap() {} + } (platformRef as any) ._bootstrapModuleWithZone( DynamicNgUpgradeModule, this.compilerOptions, this.ngZone) diff --git a/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts b/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts index 366fd82f0b..59d5bace78 100644 --- a/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts +++ b/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts @@ -38,23 +38,26 @@ export class UpgradeNg1ComponentAdapterBuilder { name.replace(CAMEL_CASE, (all: string, next: string) => '-' + next.toLowerCase()); const self = this; - this.type = - Directive({selector: selector, inputs: this.inputsRename, outputs: this.outputsRename}) - .Class({ - constructor: [ - new Inject($SCOPE), Injector, ElementRef, - function(scope: angular.IScope, injector: Injector, elementRef: ElementRef) { - const helper = new UpgradeHelper(injector, name, elementRef, this.directive); - return new UpgradeNg1ComponentAdapter( - helper, scope, self.template, self.inputs, self.outputs, self.propertyOutputs, - self.checkProperties, self.propertyMap); - } - ], - ngOnInit: function() { /* needs to be here for ng2 to properly detect it */ }, - ngOnChanges: function() { /* needs to be here for ng2 to properly detect it */ }, - ngDoCheck: function() { /* needs to be here for ng2 to properly detect it */ }, - ngOnDestroy: function() { /* needs to be here for ng2 to properly detect it */ }, - }); + @Directive({selector: selector, inputs: this.inputsRename, outputs: this.outputsRename}) + class MyClass { + directive: angular.IDirective; + constructor( + @Inject($SCOPE) scope: angular.IScope, injector: Injector, elementRef: ElementRef) { + const helper = new UpgradeHelper(injector, name, elementRef, this.directive); + return new UpgradeNg1ComponentAdapter( + helper, scope, self.template, self.inputs, self.outputs, self.propertyOutputs, + self.checkProperties, self.propertyMap) as any; + } + ngOnInit() { /* needs to be here for ng2 to properly detect it */ + } + ngOnChanges() { /* needs to be here for ng2 to properly detect it */ + } + ngDoCheck() { /* needs to be here for ng2 to properly detect it */ + } + ngOnDestroy() { /* needs to be here for ng2 to properly detect it */ + } + }; + this.type = MyClass; } extractBindings() { diff --git a/packages/upgrade/test/dynamic/upgrade_spec.ts b/packages/upgrade/test/dynamic/upgrade_spec.ts index ff35abb500..9a32de8acd 100644 --- a/packages/upgrade/test/dynamic/upgrade_spec.ts +++ b/packages/upgrade/test/dynamic/upgrade_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectorRef, Class, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgZone, OnChanges, SimpleChange, SimpleChanges, Testability, destroyPlatform, forwardRef} from '@angular/core'; +import {ChangeDetectorRef, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgZone, OnChanges, SimpleChange, SimpleChanges, Testability, destroyPlatform, forwardRef} from '@angular/core'; import {async, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; @@ -25,14 +25,16 @@ export function main() { it('should instantiate ng2 in ng1 template and project content', async(() => { const ng1Module = angular.module('ng1', []); - const Ng2 = Component({ - selector: 'ng2', - template: `{{ 'NG2' }}()`, - }).Class({constructor: function() {}}); + @Component({ + selector: 'ng2', + template: `{{ 'NG2' }}()`, + }) + class Ng2 { + } - const Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({ - constructor: function() {} - }); + @NgModule({declarations: [Ng2], imports: [BrowserModule]}) + class Ng2Module { + } const element = html('
{{ \'ng1[\' }}~{{ \'ng-content\' }}~{{ \']\' }}
'); @@ -49,15 +51,19 @@ export function main() { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const ng1Module = angular.module('ng1', []); - const Ng2 = Component({ - selector: 'ng2', - template: `{{ 'ng2(' }}{{'transclude'}}{{ ')' }}`, - }).Class({constructor: function Ng2() {}}); + @Component({ + selector: 'ng2', + template: `{{ 'ng2(' }}{{'transclude'}}{{ ')' }}`, + }) + class Ng2 { + }; - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function Ng2Module() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng1', () => { return {transclude: true, template: '{{ "ng1" }}()'}; @@ -77,19 +83,20 @@ export function main() { spyOn(platformRef, '_bootstrapModuleWithZone').and.callThrough(); const ng1Module = angular.module('ng1', []); - const Ng2 = Component({ - selector: 'ng2', - template: `{{ 'NG2' }}()` - }).Class({constructor: function() {}}); + @Component({selector: 'ng2', template: `{{ 'NG2' }}()`}) + class Ng2 { + } const element = html('
{{ \'ng1[\' }}~{{ \'ng-content\' }}~{{ \']\' }}
'); - const Ng2AppModule = - NgModule({ - declarations: [Ng2], - imports: [BrowserModule], - }).Class({constructor: function Ng2AppModule() {}, ngDoBootstrap: function() {}}); + @NgModule({ + declarations: [Ng2], + imports: [BrowserModule], + }) + class Ng2AppModule { + ngDoBootstrap() {} + }; const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2AppModule, {providers: []}); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); @@ -107,15 +114,19 @@ export function main() { beforeEach(() => { angular.module('ng1', []); - const ng2Component = Component({ - selector: 'ng2', - template: ``, - }).Class({constructor: function() {}}); + @Component({ + selector: 'ng2', + template: ``, + }) + class ng2Component { + } - const Ng2Module = NgModule({ - declarations: [ng2Component], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [ng2Component], + imports: [BrowserModule], + }) + class Ng2Module { + } adapter = new UpgradeAdapter(Ng2Module); }); @@ -161,18 +172,22 @@ export function main() { $rootScope.reset = () => log.length = 0; }); - const Ng2 = Component({ - selector: 'ng2', - template: `{{l('2A')}}{{l('2B')}}{{l('2C')}}` - }).Class({constructor: function() { this.l = l; }}); + @Component({ + selector: 'ng2', + template: `{{l('2A')}}{{l('2B')}}{{l('2C')}}` + }) + class Ng2 { + l: any; + constructor() { this.l = l; } + } - const Ng2Module = - NgModule({ - declarations: [ - adapter.upgradeNg1Component('ng1a'), adapter.upgradeNg1Component('ng1b'), Ng2 - ], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: + [adapter.upgradeNg1Component('ng1a'), adapter.upgradeNg1Component('ng1b'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); @@ -302,37 +317,35 @@ export function main() { $rootScope.eventA = '?'; $rootScope.eventB = '?'; }); - const Ng2 = Component({ - selector: 'ng2', - inputs: - ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'], - outputs: [ - 'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange', - 'twoWayBEmitter: twoWayBChange' - ], - template: 'ignore: {{ignore}}; ' + - 'literal: {{literal}}; interpolate: {{interpolate}}; ' + - 'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' + - 'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})' - }).Class({ - constructor: function() { - this.ngOnChangesCount = 0; - this.ignore = '-'; - this.literal = '?'; - this.interpolate = '?'; - this.oneWayA = '?'; - this.oneWayB = '?'; - this.twoWayA = '?'; - this.twoWayB = '?'; - this.eventA = new EventEmitter(); - this.eventB = new EventEmitter(); - this.twoWayAEmitter = new EventEmitter(); - this.twoWayBEmitter = new EventEmitter(); - }, - ngOnChanges: function(changes: SimpleChanges) { + @Component({ + selector: 'ng2', + inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'], + outputs: [ + 'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange', 'twoWayBEmitter: twoWayBChange' + ], + template: 'ignore: {{ignore}}; ' + + 'literal: {{literal}}; interpolate: {{interpolate}}; ' + + 'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' + + 'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})' + }) + class Ng2 { + ngOnChangesCount = 0; + ignore = '-'; + literal = '?'; + interpolate = '?'; + oneWayA = '?'; + oneWayB = '?'; + twoWayA = '?'; + twoWayB = '?'; + eventA = new EventEmitter(); + eventB = new EventEmitter(); + twoWayAEmitter = new EventEmitter(); + twoWayBEmitter = new EventEmitter(); + ngOnChanges(changes: SimpleChanges) { const assert = (prop: string, value: any) => { - if (this[prop] != value) { - throw new Error(`Expected: '${prop}' to be '${value}' but was '${this[prop]}'`); + if ((this as any)[prop] != value) { + throw new Error( + `Expected: '${prop}' to be '${value}' but was '${(this as any)[prop]}'`); } }; @@ -374,13 +387,15 @@ export function main() { throw new Error('Called too many times! ' + JSON.stringify(changes)); } } - }); + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); - const Ng2Module = NgModule({ - declarations: [Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + }; const element = html(`
| {{modelA}}
`); - const Ng2Module = NgModule({ - declarations: [Ng2], - imports: [BrowserModule], - schemas: [NO_ERRORS_SCHEMA], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [Ng2], + imports: [BrowserModule], + schemas: [NO_ERRORS_SCHEMA], + }) + class Ng2Module { + } adapter.bootstrap(element, ['ng1']).ready((ref) => { let $rootScope: any = ref.ng1RootScope; @@ -537,15 +554,17 @@ export function main() { }; }); - const Ng2 = Component({selector: 'ng2', template: 'test'}).Class({ - constructor: function() {}, - ngOnDestroy: function() { onDestroyed.emit('destroyed'); } - }); + @Component({selector: 'ng2', template: 'test'}) + class Ng2 { + ngOnDestroy() { onDestroyed.emit('destroyed'); } + } - const Ng2Module = NgModule({ - declarations: [Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(''); @@ -571,13 +590,16 @@ export function main() { } ]); - const Ng2 = - Component({selector: 'ng2', template: 'test'}).Class({constructor: function() {}}); + @Component({selector: 'ng2', template: 'test'}) + class Ng2 { + }; - const Ng2Module = NgModule({ - declarations: [Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(''); @@ -590,15 +612,17 @@ export function main() { it('should support multi-slot projection', async(() => { const ng1Module = angular.module('ng1', []); - const Ng2 = Component({ - selector: 'ng2', - template: '2a()' + - '2b()' - }).Class({constructor: function() {}}); + @Component({ + selector: 'ng2', + template: '2a()' + + '2b()' + }) + class Ng2 { + } - const Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({ - constructor: function() {} - }); + @NgModule({declarations: [Ng2], imports: [BrowserModule]}) + class Ng2Module { + } // The ng-if on one of the projected children is here to make sure // the correct slot is targeted even with structural directives in play. @@ -942,27 +966,27 @@ export function main() { }; }; ng1Module.directive('ng1', ng1); - const Ng2 = - Component({ - selector: 'ng2', - template: - '' + - '' + - '{{event}}-{{last}}, {{first}}, {{city}}' - }).Class({ - constructor: function() { - this.first = 'Victor'; - this.last = 'Savkin'; - this.city = 'SF'; - this.event = '?'; - } - }); + @Component({ + selector: 'ng2', + template: + '' + + '' + + '{{event}}-{{last}}, {{first}}, {{city}}' + }) + class Ng2 { + first = 'Victor'; + last = 'Savkin'; + city = 'SF'; + event = '?'; + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -989,23 +1013,24 @@ export function main() { }; }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({ - selector: 'ng2', - template: '' + - '' + - '' + - '' - }).Class({ - constructor: function() { - this.first = 'Victor'; - this.last = 'Savkin'; - } - }); + @Component({ + selector: 'ng2', + template: '' + + '' + + '' + + '' + }) + class Ng2 { + first = 'Victor'; + last = 'Savkin'; + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1036,23 +1061,22 @@ export function main() { }; ng1Module.directive('ng1', ng1); - const Ng2 = - Component({ - selector: 'ng2', - template: - '{{someText}} - Length: {{dataList.length}} | ' - }).Class({ + @Component({ + selector: 'ng2', + template: + '{{someText}} - Length: {{dataList.length}} | ' + }) + class Ng2 { + dataList = [1, 2, 3]; + someText = 'ng2'; + } - constructor: function() { - this.dataList = [1, 2, 3]; - this.someText = 'ng2'; - } - }); - - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1081,23 +1105,22 @@ export function main() { }; ng1Module.directive('ng1', ng1); - const Ng2 = - Component({ - selector: 'ng2', - template: - '{{someText}} - Length: {{dataList.length}} | ' - }).Class({ + @Component({ + selector: 'ng2', + template: + '{{someText}} - Length: {{dataList.length}} | ' + }) + class Ng2 { + dataList = [1, 2, 3]; + someText = 'ng2'; + } - constructor: function() { - this.dataList = [1, 2, 3]; - this.someText = 'ng2'; - } - }); - - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1122,14 +1145,16 @@ export function main() { const ng1 = () => { return {templateUrl: 'url.html'}; }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1149,14 +1174,16 @@ export function main() { const ng1 = () => { return {templateUrl() { return 'url.html'; }}; }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1173,14 +1200,16 @@ export function main() { const ng1 = () => { return {template: ''}; }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1197,14 +1226,16 @@ export function main() { const ng1 = () => { return {template() { return ''; }}; }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1222,14 +1253,16 @@ export function main() { const ng1 = () => { return {templateUrl: 'url.html'}; }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1249,30 +1282,31 @@ export function main() { template: '{{ctl.scope}}; {{ctl.isClass}}; {{ctl.hasElement}}; {{ctl.isPublished()}}', controllerAs: 'ctl', - controller: Class({ - constructor: function($scope: any, $element: any) { - (this).verifyIAmAClass(); + controller: class { + scope: any; hasElement: string; $element: any; isClass: any; + constructor($scope: any, $element: any) { + this.verifyIAmAClass(); this.scope = $scope.$parent.$parent == $scope.$root ? 'scope' : 'wrong-scope'; this.hasElement = $element[0].nodeName; this.$element = $element; - }, - verifyIAmAClass: function() { this.isClass = 'isClass'; }, - isPublished: function() { + } verifyIAmAClass() { this.isClass = 'isClass'; } isPublished() { return this.$element.controller('ng1') == this ? 'published' : 'not-published'; } - }) + } }; }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1292,19 +1326,21 @@ export function main() { bindToController: true, template: '{{ctl.title}}', controllerAs: 'ctl', - controller: Class({constructor: function() {}}) + controller: class {} }; }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1324,19 +1360,21 @@ export function main() { bindToController: {title: '@'}, template: '{{ctl.title}}', controllerAs: 'ctl', - controller: Class({constructor: function() {}}) + controller: class {} }; }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1357,7 +1395,7 @@ export function main() { template: '{{ctl.status}}', require: 'ng1', controllerAs: 'ctrl', - controller: Class({constructor: function() { this.status = 'WORKS'; }}), + controller: class {status = 'WORKS';}, link: function(scope: any, element: any, attrs: any, linkController: any) { expect(scope.$root).toEqual($rootScope); expect(element[0].nodeName).toEqual('NG1'); @@ -1368,14 +1406,16 @@ export function main() { }; ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1389,9 +1429,7 @@ export function main() { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const ng1Module = angular.module('ng1', []); - const parent = () => { - return {controller: Class({constructor: function() { this.parent = 'PARENT'; }})}; - }; + const parent = () => { return {controller: class {parent = 'PARENT';}}; }; const ng1 = () => { return { scope: {title: '@'}, @@ -1399,7 +1437,7 @@ export function main() { template: '{{parent.parent}}:{{ng1.status}}', require: ['ng1', '^parent', '?^^notFound'], controllerAs: 'ctrl', - controller: Class({constructor: function() { this.status = 'WORKS'; }}), + controller: class {status = 'WORKS';}, link: function(scope: any, element: any, attrs: any, linkControllers: any) { expect(linkControllers[0].status).toEqual('WORKS'); expect(linkControllers[1].parent).toEqual('PARENT'); @@ -1412,14 +1450,16 @@ export function main() { ng1Module.directive('parent', parent); ng1Module.directive('ng1', ng1); - const Ng2 = Component({selector: 'ng2', template: ''}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); const element = html(`
`); @@ -1835,7 +1875,8 @@ export function main() { } // On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3), - // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be on + // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be + // on // the queue at the end of the test, causing it to fail. // Mocking animations (via `ngAnimateMock`) avoids the issue. angular.module('ng1', ['ngAnimateMock']) @@ -1923,7 +1964,8 @@ export function main() { } // On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3), - // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be on + // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be + // on // the queue at the end of the test, causing it to fail. // Mocking animations (via `ngAnimateMock`) avoids the issue. angular.module('ng1', ['ngAnimateMock']) @@ -2618,19 +2660,21 @@ export function main() { const ng1 = { bindings: {personProfile: '<'}, template: 'Hello {{$ctrl.personProfile.firstName}} {{$ctrl.personProfile.lastName}}', - controller: Class({constructor: function() {}}) + controller: class {} }; ng1Module.component('ng1', ng1); - const Ng2 = - Component({selector: 'ng2', template: ''}).Class({ - constructor: function() { this.goku = {firstName: 'GOKU', lastName: 'SAN'}; } - }); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + goku = {firstName: 'GOKU', lastName: 'SAN'}; + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); @@ -2650,19 +2694,22 @@ export function main() { }; ng1Module.component('ng1', ng1); - const Ng2a = Component({selector: 'ng2a', template: 'ng2a()'}).Class({ - constructor: function() {} - }); + @Component({selector: 'ng2a', template: 'ng2a()'}) + class Ng2a { + } ng1Module.directive('ng2a', adapter.downgradeNg2Component(Ng2a)); - const Ng2b = - Component({selector: 'ng2b', template: 'ng2b'}).Class({constructor: function() {}}); + @Component({selector: 'ng2b', template: 'ng2b'}) + class Ng2b { + } ng1Module.directive('ng2b', adapter.downgradeNg2Component(Ng2b)); - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2a, Ng2b], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2a, Ng2b], + imports: [BrowserModule], + }) + class Ng2Module { + } const element = html(`
`); adapter.bootstrap(element, ['ng1']).ready((ref) => { @@ -2675,10 +2722,12 @@ export function main() { function SomeToken() {} it('should export ng2 instance to ng1', async(() => { - const MyNg2Module = NgModule({ - providers: [{provide: SomeToken, useValue: 'correct_value'}], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + providers: [{provide: SomeToken, useValue: 'correct_value'}], + imports: [BrowserModule], + }) + class MyNg2Module { + } const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module); const module = angular.module('myExample', []); @@ -2690,8 +2739,9 @@ export function main() { })); it('should export ng1 instance to ng2', async(() => { - const MyNg2Module = - NgModule({imports: [BrowserModule]}).Class({constructor: function() {}}); + @NgModule({imports: [BrowserModule]}) + class MyNg2Module { + }; const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module); const module = angular.module('myExample', []); @@ -2710,18 +2760,17 @@ export function main() { it('should respect hierarchical dependency injection for ng2', async(() => { const ng1Module = angular.module('ng1', []); - const Ng2Parent = Component({ - selector: 'ng2-parent', - template: `ng2-parent()` - }).Class({constructor: function() {}}); - const Ng2Child = Component({selector: 'ng2-child', template: `ng2-child`}).Class({ - constructor: [Ng2Parent, function(parent: any) {}] - }); + @Component({selector: 'ng2-parent', template: `ng2-parent()`}) + class Ng2Parent { + } + @Component({selector: 'ng2-child', template: `ng2-child`}) + class Ng2Child { + constructor(parent: Ng2Parent) {} + } - const Ng2Module = - NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]}).Class({ - constructor: function() {} - }); + @NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]}) + class Ng2Module { + } const element = html(''); @@ -2737,8 +2786,9 @@ export function main() { describe('testability', () => { it('should handle deferred bootstrap', async(() => { - const MyNg2Module = - NgModule({imports: [BrowserModule]}).Class({constructor: function() {}}); + @NgModule({imports: [BrowserModule]}) + class MyNg2Module { + } const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module); angular.module('ng1', []); @@ -2759,8 +2809,9 @@ export function main() { })); it('should wait for ng2 testability', async(() => { - const MyNg2Module = - NgModule({imports: [BrowserModule]}).Class({constructor: function() {}}); + @NgModule({imports: [BrowserModule]}) + class MyNg2Module { + } const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module); angular.module('ng1', []); @@ -2797,17 +2848,20 @@ export function main() { }; module.directive('ng1', ng1); - const Ng2 = - Component({ - selector: 'ng2', - inputs: ['name'], - template: 'ng2[transclude]()' - }).Class({constructor: function() {}}); + @Component({ + selector: 'ng2', + inputs: ['name'], + template: 'ng2[transclude]()' + }) + class Ng2 { + } - const Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }).Class({constructor: function() {}}); + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } module.directive('ng2', adapter.downgradeNg2Component(Ng2)); @@ -2829,14 +2883,16 @@ export function main() { beforeEach(() => { const ng1Module = angular.module('ng1', []); - const Ng2 = Component({ - selector: 'ng2', - template: 'Hello World', - }).Class({constructor: function() {}}); + @Component({ + selector: 'ng2', + template: 'Hello World', + }) + class Ng2 { + } - const Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({ - constructor: function() {} - }); + @NgModule({declarations: [Ng2], imports: [BrowserModule]}) + class Ng2Module { + } const upgradeAdapter = new UpgradeAdapter(Ng2Module); ng1Module.directive('ng2', upgradeAdapter.downgradeNg2Component(Ng2)); diff --git a/tools/public_api_guard/core/core.d.ts b/tools/public_api_guard/core/core.d.ts index 936b0a0a58..97233f5bc6 100644 --- a/tools/public_api_guard/core/core.d.ts +++ b/tools/public_api_guard/core/core.d.ts @@ -163,17 +163,6 @@ export declare abstract class ChangeDetectorRef { abstract reattach(): void; } -/** @stable */ -export declare function Class(clsDef: ClassDefinition): Type; - -/** @stable */ -export declare type ClassDefinition = { - extends?: Type; - constructor: Function | any[]; -} & { - [x: string]: Type | Function | any[]; -}; - /** @stable */ export interface ClassProvider { multi?: boolean; @@ -1029,10 +1018,8 @@ export declare const Type: FunctionConstructor; /** @stable */ export interface TypeDecorator { - annotations: any[]; (target: Object, propertyKey?: string | symbol, parameterIndex?: number): void; >(type: T): T; - Class(obj: ClassDefinition): Type; } /** @stable */