diff --git a/modules/@angular/common/index.ts b/modules/@angular/common/index.ts index ef9e7c4982..9ed2cda6e9 100644 --- a/modules/@angular/common/index.ts +++ b/modules/@angular/common/index.ts @@ -6,9 +6,25 @@ * found in the LICENSE file at https://angular.io/license */ +import {NgModule} from '@angular/core'; +import {COMMON_DIRECTIVES} from './src/common_directives'; +import {COMMON_PIPES} from './src/pipes'; + export * from './src/pipes'; export * from './src/directives'; export * from './src/forms-deprecated'; export * from './src/common_directives'; export * from './src/location'; -export {NgLocalization} from './src/localization'; \ No newline at end of file +export {NgLocalization} from './src/localization'; + +// Note: This does not contain the location providers, +// as they need some platform specific implementations to work. +/** + * The module that includes all the basic Angular directives like {@link NgIf}, ${link NgFor}, ... + * + * @experimental + */ +@NgModule( + {declarations: [COMMON_DIRECTIVES, COMMON_PIPES], exports: [COMMON_DIRECTIVES, COMMON_PIPES]}) +export class CommonModule { +} diff --git a/modules/@angular/common/src/forms-deprecated.ts b/modules/@angular/common/src/forms-deprecated.ts index d6484feaa9..a8f4c4dd11 100644 --- a/modules/@angular/common/src/forms-deprecated.ts +++ b/modules/@angular/common/src/forms-deprecated.ts @@ -18,7 +18,7 @@ * Forms providers are not included in default providers; you must import these providers * explicitly. */ -import {AppModule, Type} from '@angular/core'; +import {NgModule, Type} from '@angular/core'; import {FORM_DIRECTIVES} from './forms-deprecated/directives'; import {RadioControlRegistry} from './forms-deprecated/directives/radio_control_value_accessor'; @@ -61,15 +61,15 @@ export const FORM_PROVIDERS: Type[] = /*@ts2dart_const*/[FormBuilder, RadioContr /** - * The app module for the deprecated forms API. + * The ng module for the deprecated forms API. * @deprecated */ -@AppModule({ +@NgModule({ providers: [ FORM_PROVIDERS, ], - directives: FORM_DIRECTIVES, - pipes: [] + declarations: FORM_DIRECTIVES, + exports: FORM_DIRECTIVES }) export class DeprecatedFormsModule { } diff --git a/modules/@angular/common/test/forms-deprecated/integration_spec.ts b/modules/@angular/common/test/forms-deprecated/integration_spec.ts index 56979f0f34..7b5f2755c7 100644 --- a/modules/@angular/common/test/forms-deprecated/integration_spec.ts +++ b/modules/@angular/common/test/forms-deprecated/integration_spec.ts @@ -22,7 +22,7 @@ import {PromiseWrapper} from '../../src/facade/promise'; export function main() { describe('integration tests', () => { - beforeEach(() => { configureModule({modules: [DeprecatedFormsModule]}); }); + beforeEach(() => configureModule({imports: [DeprecatedFormsModule]})); it('should initialize DOM elements with the given form object', inject( diff --git a/modules/@angular/compiler-cli/README.md b/modules/@angular/compiler-cli/README.md index 75fa4d97e4..b626c34bbd 100644 --- a/modules/@angular/compiler-cli/README.md +++ b/modules/@angular/compiler-cli/README.md @@ -30,13 +30,14 @@ generated code: main_module.ts ------------- import {BrowserModule} from '@angular/platform-browser'; -import {Component, AppModule, ApplicationRef} from '@angular/core'; +import {Component, NgModule, ApplicationRef} from '@angular/core'; @Component(...) export class MyComponent {} -@AppModule({ - modules: [BrowserModule], +@NgModule({ + imports: [BrowserModule], + declarations: [MyComponent], precompile: [MyComponent] }) export class MainModule { diff --git a/modules/@angular/compiler-cli/integrationtest/src/a/multiple_components.ts b/modules/@angular/compiler-cli/integrationtest/src/a/multiple_components.ts index 854634661b..3204a30527 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/a/multiple_components.ts +++ b/modules/@angular/compiler-cli/integrationtest/src/a/multiple_components.ts @@ -8,10 +8,7 @@ import {Component} from '@angular/core'; -@Component({ - selector: 'my-comp', - template: '
', -}) +@Component({selector: 'my-comp', template: '
', directives: [NextComp]}) export class MultipleComponentsMyComp { } diff --git a/modules/@angular/compiler-cli/integrationtest/src/basic.html b/modules/@angular/compiler-cli/integrationtest/src/basic.html index 4d1aebaf95..f36206577f 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/basic.html +++ b/modules/@angular/compiler-cli/integrationtest/src/basic.html @@ -1,4 +1,4 @@
{{ctxProp}}
-
+
diff --git a/modules/@angular/compiler-cli/integrationtest/src/basic.ts b/modules/@angular/compiler-cli/integrationtest/src/basic.ts index 62cd26427a..8c06cf3798 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/basic.ts +++ b/modules/@angular/compiler-cli/integrationtest/src/basic.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {FORM_DIRECTIVES, NgFor, NgIf} from '@angular/common'; +import {NgFor, NgIf} from '@angular/common'; import {Component, Inject} from '@angular/core'; +import {FORM_DIRECTIVES} from '@angular/forms'; import {MultipleComponentsMyComp} from './a/multiple_components'; diff --git a/modules/@angular/compiler-cli/integrationtest/src/bootstrap.ts b/modules/@angular/compiler-cli/integrationtest/src/bootstrap.ts index 49e82322c2..2d00a897e5 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/bootstrap.ts +++ b/modules/@angular/compiler-cli/integrationtest/src/bootstrap.ts @@ -10,4 +10,4 @@ import {browserPlatform} from '@angular/platform-browser'; import {BasicComp} from './basic'; import {MainModuleNgFactory} from './module.ngfactory'; -MainModuleNgFactory.create().instance.appRef.bootstrap(BasicComp); +MainModuleNgFactory.create(null).instance.appRef.bootstrap(BasicComp); diff --git a/modules/@angular/compiler-cli/integrationtest/src/module.ts b/modules/@angular/compiler-cli/integrationtest/src/module.ts index c8469aade3..e6fec9aa31 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/module.ts +++ b/modules/@angular/compiler-cli/integrationtest/src/module.ts @@ -6,20 +6,29 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModule, ApplicationRef} from '@angular/core'; +import {ApplicationRef, NgModule} from '@angular/core'; +import {FormsModule} from '@angular/forms'; import {BrowserModule} from '@angular/platform-browser'; import {AnimateCmp} from './animate'; import {BasicComp} from './basic'; +import {CompWithProviders, CompWithReferences} from './features'; +import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomeLibModule, SomePipeInRootModule, SomeService} from './module_fixtures'; import {CompWithAnalyzePrecompileProvider, CompWithPrecompile} from './precompile'; import {ProjectingComp} from './projection'; -import {CompWithChildQuery} from './queries'; +import {CompWithChildQuery, CompWithDirectiveChild} from './queries'; -@AppModule({ - modules: [BrowserModule], +@NgModule({ + declarations: [ + SomeDirectiveInRootModule, SomePipeInRootModule, AnimateCmp, BasicComp, CompWithPrecompile, + CompWithAnalyzePrecompileProvider, ProjectingComp, CompWithChildQuery, CompWithDirectiveChild, + CompUsingRootModuleDirectiveAndPipe, CompWithProviders, CompWithReferences + ], + imports: [BrowserModule, FormsModule, SomeLibModule], + providers: [SomeService], precompile: [ AnimateCmp, BasicComp, CompWithPrecompile, CompWithAnalyzePrecompileProvider, ProjectingComp, - CompWithChildQuery + CompWithChildQuery, CompUsingRootModuleDirectiveAndPipe ] }) export class MainModule { diff --git a/modules/@angular/compiler-cli/integrationtest/src/module_fixtures.ts b/modules/@angular/compiler-cli/integrationtest/src/module_fixtures.ts index 469055d056..db3a01a91d 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/module_fixtures.ts +++ b/modules/@angular/compiler-cli/integrationtest/src/module_fixtures.ts @@ -7,7 +7,7 @@ */ import {LowerCasePipe, NgIf} from '@angular/common'; -import {ANALYZE_FOR_PRECOMPILE, AppModule, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, OpaqueToken, Pipe} from '@angular/core'; +import {ANALYZE_FOR_PRECOMPILE, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, NgModule, OpaqueToken, Pipe} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; @Injectable() @@ -16,50 +16,37 @@ export class SomeService { } @Injectable() -export class NestedService { +export class ServiceUsingLibModule { } @Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) -export class SomeDirective { +export class SomeDirectiveInRootModule { + @Input() + someDir: string; +} + +@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) +export class SomeDirectiveInLibModule { @Input() someDir: string; } @Pipe({name: 'somePipe'}) -export class SomePipe { +export class SomePipeInRootModule { transform(value: string): any { return `transformed ${value}`; } } -@Component({selector: 'cmp', template: `
`}) -export class SomeComp { - constructor() {} +@Pipe({name: 'somePipe'}) +export class SomePipeInLibModule { + transform(value: string): any { return `transformed ${value}`; } } -@Component({selector: 'parent', template: ``, directives: [SomeComp]}) -export class ParentComp { +@Component({selector: 'comp', template: `
`}) +export class CompUsingRootModuleDirectiveAndPipe { } -@AppModule({providers: [NestedService]}) -export class NestedModule { -} - -@AppModule({ - directives: [SomeDirective], - pipes: [SomePipe], - providers: [SomeService], - precompile: [SomeComp], - modules: [NestedModule, BrowserModule] -}) -export class SomeModule { -} - -@AppModule({ - directives: [SomeDirective], - pipes: [SomePipe], - precompile: [ParentComp], - modules: [BrowserModule] -}) -export class SomeModuleUsingParentComp { +@Component({selector: 'comp', template: `
`}) +export class CompUsingLibModuleDirectiveAndPipe { } export const SOME_TOKEN = new OpaqueToken('someToken'); @@ -71,7 +58,13 @@ export function provideValueWithPrecompile(value: any) { ]; } -@AppModule({providers: [provideValueWithPrecompile([{a: 'b', component: SomeComp}])]}) -export class SomeModuleWithAnalyzePrecompileProvider { - constructor(@Inject(SOME_TOKEN) public providedValue: any) {} +@NgModule({ + declarations: [SomeDirectiveInLibModule, SomePipeInLibModule, CompUsingLibModuleDirectiveAndPipe], + precompile: [CompUsingLibModuleDirectiveAndPipe], + providers: [ + ServiceUsingLibModule, + provideValueWithPrecompile([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}]) + ], +}) +export class SomeLibModule { } diff --git a/modules/@angular/compiler-cli/integrationtest/test/app_module_spec.ts b/modules/@angular/compiler-cli/integrationtest/test/app_module_spec.ts deleted file mode 100644 index 21c646406a..0000000000 --- a/modules/@angular/compiler-cli/integrationtest/test/app_module_spec.ts +++ /dev/null @@ -1,62 +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 './init'; -import {NestedModule, NestedService, ParentComp, SomeComp, SomeModule, SomeService} from '../src/module_fixtures'; -import {SomeModuleNgFactory, SomeModuleUsingParentCompNgFactory, SomeModuleWithAnalyzePrecompileProviderNgFactory} from '../src/module_fixtures.ngfactory'; -import {createComponent, createModule} from './util'; - -describe('AppModule', () => { - it('should support providers', () => { - var moduleRef = createModule(SomeModuleNgFactory); - expect(moduleRef.instance instanceof SomeModule).toBe(true); - expect(moduleRef.injector.get(SomeModule) instanceof SomeModule).toBe(true); - expect(moduleRef.injector.get(SomeService) instanceof SomeService).toBe(true); - }); - - it('should support precompile components', () => { - var moduleRef = createModule(SomeModuleNgFactory); - var cf = moduleRef.componentFactoryResolver.resolveComponentFactory(SomeComp); - expect(cf.componentType).toBe(SomeComp); - var compRef = cf.create(moduleRef.injector); - expect(compRef.instance instanceof SomeComp).toBe(true); - }); - - it('should support precompile via the ANALYZE_FOR_PRECOMPILE provider and function providers in components', - () => { - const moduleRef = createModule(SomeModuleWithAnalyzePrecompileProviderNgFactory); - const cf = moduleRef.componentFactoryResolver.resolveComponentFactory(SomeComp); - expect(cf.componentType).toBe(SomeComp); - // check that the function call that created the provider for ANALYZE_FOR_PRECOMPILE worked. - expect(moduleRef.instance.providedValue).toEqual([{a: 'b', component: SomeComp}]); - }); - - it('should support module directives and pipes', () => { - var compFixture = createComponent(SomeComp, SomeModuleNgFactory); - compFixture.detectChanges(); - - var debugElement = compFixture.debugElement; - expect(debugElement.children[0].properties['title']).toBe('transformed someValue'); - }); - - it('should support module directives and pipes on nested components', () => { - var compFixture = createComponent(ParentComp, SomeModuleUsingParentCompNgFactory); - compFixture.detectChanges(); - - var debugElement = compFixture.debugElement; - debugElement = debugElement.children[0]; - expect(debugElement.children[0].properties['title']).toBe('transformed someValue'); - }); - - it('should support child moduless', () => { - var moduleRef = createModule(SomeModuleNgFactory); - expect(moduleRef.instance instanceof SomeModule).toBe(true); - expect(moduleRef.injector.get(NestedModule) instanceof NestedModule).toBe(true); - expect(moduleRef.injector.get(NestedService) instanceof NestedService).toBe(true); - }); - -}); diff --git a/modules/@angular/compiler-cli/integrationtest/test/ng_module_spec.ts b/modules/@angular/compiler-cli/integrationtest/test/ng_module_spec.ts new file mode 100644 index 0000000000..77e8dc14b4 --- /dev/null +++ b/modules/@angular/compiler-cli/integrationtest/test/ng_module_spec.ts @@ -0,0 +1,63 @@ +/** + * @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 './init'; + +import {MainModule} from '../src/module'; +import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures'; + +import {createComponent, createModule} from './util'; + +describe('NgModule', () => { + it('should support providers', () => { + const moduleRef = createModule(); + expect(moduleRef.instance instanceof MainModule).toBe(true); + expect(moduleRef.injector.get(MainModule) instanceof MainModule).toBe(true); + expect(moduleRef.injector.get(SomeService) instanceof SomeService).toBe(true); + }); + + it('should support precompile components', () => { + const moduleRef = createModule(); + const cf = moduleRef.componentFactoryResolver.resolveComponentFactory( + CompUsingRootModuleDirectiveAndPipe); + expect(cf.componentType).toBe(CompUsingRootModuleDirectiveAndPipe); + const compRef = cf.create(moduleRef.injector); + expect(compRef.instance instanceof CompUsingRootModuleDirectiveAndPipe).toBe(true); + }); + + it('should support precompile via the ANALYZE_FOR_PRECOMPILE provider and function providers in components', + () => { + const moduleRef = createModule(); + const cf = moduleRef.componentFactoryResolver.resolveComponentFactory( + CompUsingRootModuleDirectiveAndPipe); + expect(cf.componentType).toBe(CompUsingRootModuleDirectiveAndPipe); + // check that the function call that created the provider for ANALYZE_FOR_PRECOMPILE worked. + expect(moduleRef.injector.get(SOME_TOKEN)).toEqual([ + {a: 'b', component: CompUsingLibModuleDirectiveAndPipe} + ]); + }); + + it('should support module directives and pipes', () => { + const compFixture = createComponent(CompUsingRootModuleDirectiveAndPipe); + compFixture.detectChanges(); + + const debugElement = compFixture.debugElement; + expect(debugElement.children[0].properties['title']).toBe('transformed someValue'); + }); + + it('should support module directives and pipes on lib modules', () => { + const compFixture = createComponent(CompUsingLibModuleDirectiveAndPipe); + compFixture.detectChanges(); + + const debugElement = compFixture.debugElement; + expect(debugElement.children[0].properties['title']).toBe('transformed someValue'); + + expect(debugElement.injector.get(SomeLibModule) instanceof SomeLibModule).toBe(true); + expect(debugElement.injector.get(ServiceUsingLibModule) instanceof ServiceUsingLibModule) + .toBe(true); + }); +}); diff --git a/modules/@angular/compiler-cli/integrationtest/test/util.ts b/modules/@angular/compiler-cli/integrationtest/test/util.ts index 786dfe4cbb..681dacebd3 100644 --- a/modules/@angular/compiler-cli/integrationtest/test/util.ts +++ b/modules/@angular/compiler-cli/integrationtest/test/util.ts @@ -6,23 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModuleFactory, AppModuleRef, bootstrapModuleFactory} from '@angular/core'; +import {NgModuleFactory, NgModuleRef, bootstrapModuleFactory} from '@angular/core'; import {ComponentFixture} from '@angular/core/testing'; import {serverPlatform} from '@angular/platform-server'; +import {MainModule} from '../src/module'; import {MainModuleNgFactory} from '../src/module.ngfactory'; -export function createModule(factory: AppModuleFactory): AppModuleRef { - return bootstrapModuleFactory(factory, serverPlatform()); +export function createModule(): NgModuleRef { + return bootstrapModuleFactory(MainModuleNgFactory, serverPlatform()); } -export function createComponent( - comp: {new (...args: any[]): C}, - moduleFactory: AppModuleFactory = null): ComponentFixture { - if (!moduleFactory) { - moduleFactory = MainModuleNgFactory; - } - const moduleRef = createModule(moduleFactory); +export function createComponent(comp: {new (...args: any[]): C}): ComponentFixture { + const moduleRef = createModule(); const compRef = moduleRef.componentFactoryResolver.resolveComponentFactory(comp).create(moduleRef.injector); return new ComponentFixture(compRef, null, null); diff --git a/modules/@angular/compiler-cli/src/codegen.ts b/modules/@angular/compiler-cli/src/codegen.ts index 387cd9947f..bec68aef07 100644 --- a/modules/@angular/compiler-cli/src/codegen.ts +++ b/modules/@angular/compiler-cli/src/codegen.ts @@ -11,12 +11,12 @@ * Intended to be used in a build step. */ import * as compiler from '@angular/compiler'; -import {AppModuleMetadata, ComponentMetadata, ViewEncapsulation} from '@angular/core'; +import {ComponentMetadata, NgModuleMetadata, ViewEncapsulation} from '@angular/core'; import {AngularCompilerOptions} from '@angular/tsc-wrapped'; import * as path from 'path'; import * as ts from 'typescript'; -import {AppModuleCompiler, CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './compiler_private'; +import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, NgModuleCompiler, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './compiler_private'; import {ReflectorHost, ReflectorHostContext} from './reflector_host'; import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities'; import {StaticReflector, StaticSymbol} from './static_reflector'; @@ -39,7 +39,7 @@ export class CodeGenerator { private readFileMetadata(absSourcePath: string): FileMetadata { const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath); - const result: FileMetadata = {components: [], appModules: [], fileUrl: absSourcePath}; + const result: FileMetadata = {components: [], ngModules: [], fileUrl: absSourcePath}; if (!moduleMetadata) { console.log(`WARNING: no metadata found for ${absSourcePath}`); return result; @@ -57,8 +57,8 @@ export class CodeGenerator { const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath); const annotations = this.staticReflector.annotations(staticType); annotations.forEach((annotation) => { - if (annotation instanceof AppModuleMetadata) { - result.appModules.push(staticType); + if (annotation instanceof NgModuleMetadata) { + result.ngModules.push(staticType); } else if (annotation instanceof ComponentMetadata) { result.components.push(staticType); } @@ -86,17 +86,17 @@ export class CodeGenerator { let filePaths = this.program.getSourceFiles().map(sf => sf.fileName).filter(f => !GENERATED_FILES.test(f)); let fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath)); - let appModules = fileMetas.reduce((appModules, fileMeta) => { - appModules.push(...fileMeta.appModules); - return appModules; + let ngModules = fileMetas.reduce((ngModules, fileMeta) => { + ngModules.push(...fileMeta.ngModules); + return ngModules; }, []); - let analyzedAppModules = this.compiler.analyzeModules(appModules); + let analyzedNgModules = this.compiler.analyzeModules(ngModules); return Promise .all(fileMetas.map( (fileMeta) => this.compiler .compile( - fileMeta.fileUrl, analyzedAppModules, fileMeta.components, - fileMeta.appModules) + fileMeta.fileUrl, analyzedNgModules, fileMeta.components, + fileMeta.ngModules) .then((generatedModules) => { generatedModules.forEach((generatedModule) => { const sourceFile = this.program.getSourceFile(fileMeta.fileUrl); @@ -139,11 +139,12 @@ export class CodeGenerator { expressionParser, new DomElementSchemaRegistry(), htmlParser, /*console*/ null, []); const resolver = new CompileMetadataResolver( + new compiler.NgModuleResolver(staticReflector), new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), - new compiler.ViewResolver(staticReflector), config, staticReflector); + new compiler.ViewResolver(staticReflector), config, /*console*/ null, staticReflector); const offlineCompiler = new compiler.OfflineCompiler( resolver, normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config), - new AppModuleCompiler(), new TypeScriptEmitter(reflectorHost)); + new NgModuleCompiler(), new TypeScriptEmitter(reflectorHost)); return new CodeGenerator( options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost); @@ -153,5 +154,5 @@ export class CodeGenerator { interface FileMetadata { fileUrl: string; components: StaticSymbol[]; - appModules: StaticSymbol[]; -} \ No newline at end of file + ngModules: StaticSymbol[]; +} diff --git a/modules/@angular/compiler-cli/src/compiler_private.ts b/modules/@angular/compiler-cli/src/compiler_private.ts index 06617f3c16..cfc92777cf 100644 --- a/modules/@angular/compiler-cli/src/compiler_private.ts +++ b/modules/@angular/compiler-cli/src/compiler_private.ts @@ -61,8 +61,8 @@ export var StyleCompiler: typeof _c.StyleCompiler = _c.StyleCompiler; export type ViewCompiler = _c.ViewCompiler; export var ViewCompiler: typeof _c.ViewCompiler = _c.ViewCompiler; -export type AppModuleCompiler = _c.AppModuleCompiler; -export var AppModuleCompiler: typeof _c.AppModuleCompiler = _c.AppModuleCompiler; +export type NgModuleCompiler = _c.NgModuleCompiler; +export var NgModuleCompiler: typeof _c.NgModuleCompiler = _c.NgModuleCompiler; export type TypeScriptEmitter = _c.TypeScriptEmitter; export var TypeScriptEmitter: typeof _c.TypeScriptEmitter = _c.TypeScriptEmitter; diff --git a/modules/@angular/compiler-cli/src/extract_i18n.ts b/modules/@angular/compiler-cli/src/extract_i18n.ts index 759128cc7e..c0c7ba0680 100644 --- a/modules/@angular/compiler-cli/src/extract_i18n.ts +++ b/modules/@angular/compiler-cli/src/extract_i18n.ts @@ -76,7 +76,7 @@ class Extractor { for (const symbol of symbols) { const staticType = this._reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath); let directive: compiler.CompileDirectiveMetadata; - directive = this._resolver.maybeGetDirectiveMetadata(staticType); + directive = this._resolver.getDirectiveMetadata(staticType, false); if (directive && directive.isComponent) { let promise = this._normalizer.normalizeDirective(directive).asyncResult; @@ -147,8 +147,9 @@ class Extractor { const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config); const expressionParser = new Parser(new Lexer()); const resolver = new CompileMetadataResolver( + new compiler.NgModuleResolver(staticReflector), new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), - new compiler.ViewResolver(staticReflector), config, staticReflector); + new compiler.ViewResolver(staticReflector), config, /*console*/ null, staticReflector); // TODO(vicb): handle implicit const extractor = new MessageExtractor(htmlParser, expressionParser, [], {}); diff --git a/modules/@angular/compiler-cli/src/static_reflector.ts b/modules/@angular/compiler-cli/src/static_reflector.ts index 78f400f61a..8b9203faec 100644 --- a/modules/@angular/compiler-cli/src/static_reflector.ts +++ b/modules/@angular/compiler-cli/src/static_reflector.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModuleMetadata, AttributeMetadata, ComponentMetadata, ContentChildMetadata, ContentChildrenMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, HostMetadata, InjectMetadata, InjectableMetadata, InputMetadata, OptionalMetadata, OutputMetadata, PipeMetadata, Provider, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; +import {AttributeMetadata, ComponentMetadata, ContentChildMetadata, ContentChildrenMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, HostMetadata, InjectMetadata, InjectableMetadata, InputMetadata, NgModuleMetadata, OptionalMetadata, OutputMetadata, PipeMetadata, Provider, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {ReflectorReader} from './core_private'; @@ -217,7 +217,7 @@ export class StaticReflector implements ReflectorReader { this.registerDecoratorOrConstructor( this.host.findDeclaration(coreDecorators, 'Component'), ComponentMetadata); this.registerDecoratorOrConstructor( - this.host.findDeclaration(coreDecorators, 'AppModule'), AppModuleMetadata); + this.host.findDeclaration(coreDecorators, 'NgModule'), NgModuleMetadata); // Note: Some metadata classes can be used directly with Provider.deps. this.registerDecoratorOrConstructor( @@ -619,4 +619,4 @@ function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean { function shouldIgnore(value: any): boolean { return value && value.__symbolic == 'ignore'; -} \ No newline at end of file +} diff --git a/modules/@angular/compiler/compiler.ts b/modules/@angular/compiler/compiler.ts index 9d0826b891..c4eef85043 100644 --- a/modules/@angular/compiler/compiler.ts +++ b/modules/@angular/compiler/compiler.ts @@ -11,7 +11,7 @@ * @description * Starting point to import all compiler APIs. */ -export {COMPILER_PROVIDERS, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileFactoryMetadata, CompileIdentifierMetadata, CompileMetadataWithIdentifier, CompileMetadataWithType, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, CompilerConfig, DEFAULT_PACKAGE_URL_PROVIDER, DirectiveResolver, OfflineCompiler, PipeResolver, RUNTIME_COMPILER_FACTORY, RenderTypes, RuntimeCompiler, SourceModule, TEMPLATE_TRANSFORMS, UrlResolver, ViewResolver, XHR, createOfflineCompileUrlResolver} from './src/compiler'; +export {COMPILER_PROVIDERS, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileFactoryMetadata, CompileIdentifierMetadata, CompileMetadataWithIdentifier, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, CompilerConfig, DEFAULT_PACKAGE_URL_PROVIDER, DirectiveResolver, NgModuleResolver, OfflineCompiler, PipeResolver, RenderTypes, RuntimeCompiler, SourceModule, TEMPLATE_TRANSFORMS, UrlResolver, ViewResolver, XHR, analyzeAppProvidersForDeprecatedConfiguration, coreDynamicPlatform, createOfflineCompileUrlResolver} from './src/compiler'; export {ElementSchemaRegistry} from './src/schema/element_schema_registry'; export * from './src/template_ast'; diff --git a/modules/@angular/compiler/core_private.ts b/modules/@angular/compiler/core_private.ts index e9c70fbbce..590a9cffec 100644 --- a/modules/@angular/compiler/core_private.ts +++ b/modules/@angular/compiler/core_private.ts @@ -20,8 +20,6 @@ export var LifecycleHooks: typeof t.LifecycleHooks = r.LifecycleHooks; export var LIFECYCLE_HOOKS_VALUES: typeof t.LIFECYCLE_HOOKS_VALUES = r.LIFECYCLE_HOOKS_VALUES; export type ReflectorReader = t.ReflectorReader; export var ReflectorReader: typeof t.ReflectorReader = r.ReflectorReader; -export var ReflectorComponentResolver: typeof t.ReflectorComponentResolver = - r.ReflectorComponentResolver; export type AppElement = t.AppElement; export var AppElement: typeof t.AppElement = r.AppElement; export var CodegenComponentFactoryResolver: typeof t.CodegenComponentFactoryResolver = @@ -29,7 +27,7 @@ export var CodegenComponentFactoryResolver: typeof t.CodegenComponentFactoryReso export var AppView: typeof t.AppView = r.AppView; export type DebugAppView = t.DebugAppView; export var DebugAppView: typeof t.DebugAppView = r.DebugAppView; -export var AppModuleInjector: typeof t.AppModuleInjector = r.AppModuleInjector; +export var NgModuleInjector: typeof t.NgModuleInjector = r.NgModuleInjector; export type ViewType = t.ViewType; export var ViewType: typeof t.ViewType = r.ViewType; export var MAX_INTERPOLATION_VALUES: typeof t.MAX_INTERPOLATION_VALUES = r.MAX_INTERPOLATION_VALUES; @@ -68,6 +66,8 @@ export var Console: typeof t.Console = r.Console; export var reflector: t.Reflector = r.reflector; export var Reflector: typeof t.Reflector = r.Reflector; export type Reflector = t.Reflector; +export var ReflectionCapabilities: typeof t.ReflectionCapabilities = r.ReflectionCapabilities; +export type ReflectionCapabilities = t.ReflectionCapabilities; export type NoOpAnimationPlayer = t.NoOpAnimationPlayer; export var NoOpAnimationPlayer: typeof t.NoOpAnimationPlayer = r.NoOpAnimationPlayer; export type AnimationPlayer = t.AnimationPlayer; diff --git a/modules/@angular/compiler/private_export.ts b/modules/@angular/compiler/private_export.ts index be6557d3ab..73ee0575e7 100644 --- a/modules/@angular/compiler/private_export.ts +++ b/modules/@angular/compiler/private_export.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import * as app_module_compiler from './src/app_module_compiler'; import * as directive_normalizer from './src/directive_normalizer'; import * as lexer from './src/expression_parser/lexer'; import * as parser from './src/expression_parser/parser'; @@ -16,6 +15,7 @@ import * as i18n_message from './src/i18n/message'; import * as i18n_extractor from './src/i18n/message_extractor'; import * as xmb_serializer from './src/i18n/xmb_serializer'; import * as metadata_resolver from './src/metadata_resolver'; +import * as ng_module_compiler from './src/ng_module_compiler'; import * as path_util from './src/output/path_util'; import * as ts_emitter from './src/output/ts_emitter'; import * as parse_util from './src/parse_util'; @@ -99,8 +99,8 @@ export var StyleCompiler = style_compiler.StyleCompiler; export type ViewCompiler = view_compiler.ViewCompiler; export var ViewCompiler = view_compiler.ViewCompiler; -export type AppModuleCompiler = app_module_compiler.AppModuleCompiler; -export var AppModuleCompiler = app_module_compiler.AppModuleCompiler; +export type NgModuleCompiler = ng_module_compiler.NgModuleCompiler; +export var NgModuleCompiler = ng_module_compiler.NgModuleCompiler; export type TypeScriptEmitter = ts_emitter.TypeScriptEmitter; export var TypeScriptEmitter = ts_emitter.TypeScriptEmitter; diff --git a/modules/@angular/compiler/src/compile_metadata.ts b/modules/@angular/compiler/src/compile_metadata.ts index 993dcf15d8..6ca72057cc 100644 --- a/modules/@angular/compiler/src/compile_metadata.ts +++ b/modules/@angular/compiler/src/compile_metadata.ts @@ -22,139 +22,55 @@ import {sanitizeIdentifier, splitAtColon} from './util'; // group 1: "prop" from "[prop]" // group 2: "event" from "(event)" // group 3: "@trigger" from "@trigger" -var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/g; +const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/g; +const UNDEFINED = new Object(); export abstract class CompileMetadataWithIdentifier { - abstract toJson(): {[key: string]: any}; - get identifier(): CompileIdentifierMetadata { return unimplemented(); } -} -export abstract class CompileMetadataWithType extends CompileMetadataWithIdentifier { - abstract toJson(): {[key: string]: any}; + get runtimeCacheKey(): any { return unimplemented(); } - get type(): CompileTypeMetadata { return unimplemented(); } + get assetCacheKey(): any { return unimplemented(); } - get identifier(): CompileIdentifierMetadata { return unimplemented(); } -} - -export function metadataFromJson(data: {[key: string]: any}): any { - return (_COMPILE_METADATA_FROM_JSON as any)[data['class']](data); + equalsTo(id2: CompileMetadataWithIdentifier): boolean { return unimplemented(); } } export class CompileAnimationEntryMetadata { - static fromJson(data: {[key: string]: any}): CompileAnimationEntryMetadata { - var value = data['value']; - var defs = _arrayFromJson(value['definitions'], metadataFromJson); - return new CompileAnimationEntryMetadata(value['name'], defs); - } - constructor( public name: string = null, public definitions: CompileAnimationStateMetadata[] = null) {} - - toJson(): {[key: string]: any} { - return { - 'class': 'AnimationEntryMetadata', - 'value': {'name': this.name, 'definitions': _arrayToJson(this.definitions)} - }; - } } export abstract class CompileAnimationStateMetadata {} export class CompileAnimationStateDeclarationMetadata extends CompileAnimationStateMetadata { - static fromJson(data: {[key: string]: any}): CompileAnimationStateDeclarationMetadata { - var value = data['value']; - var styles = _objFromJson(value['styles'], metadataFromJson); - return new CompileAnimationStateDeclarationMetadata(value['stateNameExpr'], styles); - } - constructor(public stateNameExpr: string, public styles: CompileAnimationStyleMetadata) { super(); } - - toJson(): {[key: string]: any} { - return { - 'class': 'AnimationStateDeclarationMetadata', - 'value': {'stateNameExpr': this.stateNameExpr, 'styles': this.styles.toJson()} - }; - } } export class CompileAnimationStateTransitionMetadata extends CompileAnimationStateMetadata { - static fromJson(data: {[key: string]: any}): CompileAnimationStateTransitionMetadata { - var value = data['value']; - var steps = _objFromJson(value['steps'], metadataFromJson); - return new CompileAnimationStateTransitionMetadata(value['stateChangeExpr'], steps); - } - constructor(public stateChangeExpr: string, public steps: CompileAnimationMetadata) { super(); } - - toJson(): {[key: string]: any} { - return { - 'class': 'AnimationStateTransitionMetadata', - 'value': {'stateChangeExpr': this.stateChangeExpr, 'steps': this.steps.toJson()} - }; - } } -export abstract class CompileAnimationMetadata { abstract toJson(): {[key: string]: any}; } +export abstract class CompileAnimationMetadata {} export class CompileAnimationKeyframesSequenceMetadata extends CompileAnimationMetadata { - static fromJson(data: {[key: string]: any}): CompileAnimationKeyframesSequenceMetadata { - var steps = _arrayFromJson(data['value'], metadataFromJson); - return new CompileAnimationKeyframesSequenceMetadata(steps); - } - constructor(public steps: CompileAnimationStyleMetadata[] = []) { super(); } - - toJson(): {[key: string]: any} { - return {'class': 'AnimationKeyframesSequenceMetadata', 'value': _arrayToJson(this.steps)}; - } } export class CompileAnimationStyleMetadata extends CompileAnimationMetadata { - static fromJson(data: {[key: string]: any}): CompileAnimationStyleMetadata { - var value = data['value']; - var offsetVal = value['offset']; - var offset = isPresent(offsetVal) ? NumberWrapper.parseFloat(offsetVal) : null; - var styles = >value['styles']; - return new CompileAnimationStyleMetadata(offset, styles); - } - constructor( public offset: number, public styles: Array = null) { super(); } - - toJson(): {[key: string]: any} { - return { - 'class': 'AnimationStyleMetadata', - 'value': {'offset': this.offset, 'styles': this.styles} - }; - } } export class CompileAnimationAnimateMetadata extends CompileAnimationMetadata { - static fromJson(data: {[key: string]: any}): CompileAnimationAnimateMetadata { - var value = data['value']; - var timings = value['timings']; - var styles = _objFromJson(value['styles'], metadataFromJson); - return new CompileAnimationAnimateMetadata(timings, styles); - } - constructor( public timings: string|number = 0, public styles: CompileAnimationStyleMetadata| CompileAnimationKeyframesSequenceMetadata = null) { super(); } - - toJson(): {[key: string]: any} { - return { - 'class': 'AnimationAnimateMetadata', - 'value': {'timings': this.timings, 'styles': _objToJson(this.styles)} - }; - } } export abstract class CompileAnimationWithStepsMetadata extends CompileAnimationMetadata { @@ -162,29 +78,11 @@ export abstract class CompileAnimationWithStepsMetadata extends CompileAnimation } export class CompileAnimationSequenceMetadata extends CompileAnimationWithStepsMetadata { - static fromJson(data: {[key: string]: any}): CompileAnimationSequenceMetadata { - var steps = _arrayFromJson(data['value'], metadataFromJson); - return new CompileAnimationSequenceMetadata(steps); - } - constructor(steps: CompileAnimationMetadata[] = null) { super(steps); } - - toJson(): {[key: string]: any} { - return {'class': 'AnimationSequenceMetadata', 'value': _arrayToJson(this.steps)}; - } } export class CompileAnimationGroupMetadata extends CompileAnimationWithStepsMetadata { - static fromJson(data: {[key: string]: any}): CompileAnimationGroupMetadata { - var steps = _arrayFromJson(data['value'], metadataFromJson); - return new CompileAnimationGroupMetadata(steps); - } - constructor(steps: CompileAnimationMetadata[] = null) { super(steps); } - - toJson(): {[key: string]: any} { - return {'class': 'AnimationGroupMetadata', 'value': _arrayToJson(this.steps)}; - } } export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier { @@ -193,6 +91,7 @@ export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier prefix: string; moduleUrl: string; value: any; + private _assetCacheKey: any = UNDEFINED; constructor( {runtime, name, moduleUrl, prefix, value}: @@ -204,26 +103,28 @@ export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier this.value = value; } - static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata { - let value = isArray(data['value']) ? _arrayFromJson(data['value'], metadataFromJson) : - _objFromJson(data['value'], metadataFromJson); - return new CompileIdentifierMetadata( - {name: data['name'], prefix: data['prefix'], moduleUrl: data['moduleUrl'], value: value}); - } - - toJson(): {[key: string]: any} { - let value = isArray(this.value) ? _arrayToJson(this.value) : _objToJson(this.value); - return { - // Note: Runtime type can't be serialized... - 'class': 'Identifier', - 'name': this.name, - 'moduleUrl': this.moduleUrl, - 'prefix': this.prefix, - 'value': value - }; - } - get identifier(): CompileIdentifierMetadata { return this; } + + get runtimeCacheKey(): any { return this.identifier.runtime; } + + get assetCacheKey(): any { + if (this._assetCacheKey === UNDEFINED) { + if (isPresent(this.moduleUrl) && isPresent(getUrlScheme(this.moduleUrl))) { + var uri = reflector.importUri({'filePath': this.moduleUrl, 'name': this.name}); + this._assetCacheKey = `${this.name}|${uri}`; + } else { + this._assetCacheKey = null; + } + } + return this._assetCacheKey; + } + + equalsTo(id2: CompileIdentifierMetadata): boolean { + var rk = this.runtimeCacheKey; + var ak = this.assetCacheKey; + return (isPresent(rk) && rk == id2.runtimeCacheKey) || + (isPresent(ak) && ak == id2.assetCacheKey); + } } export class CompileDiDependencyMetadata { @@ -263,36 +164,6 @@ export class CompileDiDependencyMetadata { this.token = token; this.value = value; } - - static fromJson(data: {[key: string]: any}): CompileDiDependencyMetadata { - return new CompileDiDependencyMetadata({ - token: _objFromJson(data['token'], CompileTokenMetadata.fromJson), - query: _objFromJson(data['query'], CompileQueryMetadata.fromJson), - viewQuery: _objFromJson(data['viewQuery'], CompileQueryMetadata.fromJson), - value: data['value'], - isAttribute: data['isAttribute'], - isSelf: data['isSelf'], - isHost: data['isHost'], - isSkipSelf: data['isSkipSelf'], - isOptional: data['isOptional'], - isValue: data['isValue'] - }); - } - - toJson(): {[key: string]: any} { - return { - 'token': _objToJson(this.token), - 'query': _objToJson(this.query), - 'viewQuery': _objToJson(this.viewQuery), - 'value': this.value, - 'isAttribute': this.isAttribute, - 'isSelf': this.isSelf, - 'isHost': this.isHost, - 'isSkipSelf': this.isSkipSelf, - 'isOptional': this.isOptional, - 'isValue': this.isValue - }; - } } export class CompileProviderMetadata { @@ -321,41 +192,9 @@ export class CompileProviderMetadata { this.deps = normalizeBlank(deps); this.multi = normalizeBool(multi); } - - static fromJson(data: {[key: string]: any}): CompileProviderMetadata { - return new CompileProviderMetadata({ - token: _objFromJson(data['token'], CompileTokenMetadata.fromJson), - useClass: _objFromJson(data['useClass'], CompileTypeMetadata.fromJson), - useExisting: _objFromJson(data['useExisting'], CompileTokenMetadata.fromJson), - useValue: _objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson), - useFactory: _objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson), - multi: data['multi'], - deps: _arrayFromJson(data['deps'], CompileDiDependencyMetadata.fromJson) - }); - } - - toJson(): {[key: string]: any} { - return { - // Note: Runtime type can't be serialized... - 'class': 'Provider', - 'token': _objToJson(this.token), - 'useClass': _objToJson(this.useClass), - 'useExisting': _objToJson(this.useExisting), - 'useValue': _objToJson(this.useValue), - 'useFactory': _objToJson(this.useFactory), - 'multi': this.multi, - 'deps': _arrayToJson(this.deps) - }; - } } -export class CompileFactoryMetadata implements CompileIdentifierMetadata, - CompileMetadataWithIdentifier { - runtime: Function; - name: string; - prefix: string; - moduleUrl: string; - value: any; +export class CompileFactoryMetadata extends CompileIdentifierMetadata { diDeps: CompileDiDependencyMetadata[]; constructor({runtime, name, moduleUrl, prefix, diDeps, value}: { @@ -366,45 +205,15 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata, value?: boolean, diDeps?: CompileDiDependencyMetadata[] }) { - this.runtime = runtime; - this.name = name; - this.prefix = prefix; - this.moduleUrl = moduleUrl; + super({runtime: runtime, name: name, prefix: prefix, moduleUrl: moduleUrl, value: value}); this.diDeps = _normalizeArray(diDeps); - this.value = value; - } - - get identifier(): CompileIdentifierMetadata { return this; } - - static fromJson(data: {[key: string]: any}): CompileFactoryMetadata { - return new CompileFactoryMetadata({ - name: data['name'], - prefix: data['prefix'], - moduleUrl: data['moduleUrl'], - value: data['value'], - diDeps: _arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson) - }); - } - - toJson(): {[key: string]: any} { - return { - 'class': 'Factory', - 'name': this.name, - 'prefix': this.prefix, - 'moduleUrl': this.moduleUrl, - 'value': this.value, - 'diDeps': _arrayToJson(this.diDeps) - }; } } -var UNDEFINED = new Object(); - export class CompileTokenMetadata implements CompileMetadataWithIdentifier { value: any; identifier: CompileIdentifierMetadata; identifierIsInstance: boolean; - private _assetCacheKey = UNDEFINED; constructor( {value, identifier, identifierIsInstance}: @@ -414,46 +223,20 @@ export class CompileTokenMetadata implements CompileMetadataWithIdentifier { this.identifierIsInstance = normalizeBool(identifierIsInstance); } - static fromJson(data: {[key: string]: any}): CompileTokenMetadata { - return new CompileTokenMetadata({ - value: data['value'], - identifier: _objFromJson(data['identifier'], CompileIdentifierMetadata.fromJson), - identifierIsInstance: data['identifierIsInstance'] - }); - } - - toJson(): {[key: string]: any} { - return { - 'value': this.value, - 'identifier': _objToJson(this.identifier), - 'identifierIsInstance': this.identifierIsInstance - }; - } - get runtimeCacheKey(): any { if (isPresent(this.identifier)) { - return this.identifier.runtime; + return this.identifier.runtimeCacheKey; } else { return this.value; } } get assetCacheKey(): any { - if (this._assetCacheKey === UNDEFINED) { - if (isPresent(this.identifier)) { - if (isPresent(this.identifier.moduleUrl) && - isPresent(getUrlScheme(this.identifier.moduleUrl))) { - var uri = reflector.importUri( - {'filePath': this.identifier.moduleUrl, 'name': this.identifier.name}); - this._assetCacheKey = `${this.identifier.name}|${uri}|${this.identifierIsInstance}`; - } else { - this._assetCacheKey = null; - } - } else { - this._assetCacheKey = this.value; - } + if (isPresent(this.identifier)) { + return this.identifier.assetCacheKey; + } else { + return this.value; } - return this._assetCacheKey; } equalsTo(token2: CompileTokenMetadata): boolean { @@ -468,15 +251,24 @@ export class CompileTokenMetadata implements CompileMetadataWithIdentifier { } } -export class CompileTokenMap { +/** + * Note: We only need this in places where we need to support identifiers that + * don't have a `runtime` value given by the `StaticReflector`. E.g. see the `identifiers` + * file where we have some identifiers hard coded by name/module path. + * + * TODO(tbosch): Eventually, all of these places should go through the static reflector + * as well, providing them with a valid `StaticSymbol` that is again a singleton. + */ +export class CompileIdentifierMap { private _valueMap = new Map(); private _values: VALUE[] = []; - private _tokens: CompileTokenMetadata[] = []; + private _tokens: KEY[] = []; - add(token: CompileTokenMetadata, value: VALUE) { + add(token: KEY, value: VALUE) { var existing = this.get(token); if (isPresent(existing)) { - throw new BaseException(`Can only add to a TokenMap! Token: ${token.name}`); + throw new BaseException( + `Cannot overwrite in a CompileIdentifierMap! Token: ${token.identifier.name}`); } this._tokens.push(token); this._values.push(value); @@ -489,7 +281,7 @@ export class CompileTokenMap { this._valueMap.set(ak, value); } } - get(token: CompileTokenMetadata): VALUE { + get(token: KEY): VALUE { var rk = token.runtimeCacheKey; var ak = token.assetCacheKey; var result: VALUE; @@ -501,7 +293,7 @@ export class CompileTokenMap { } return result; } - keys(): CompileTokenMetadata[] { return this._tokens; } + keys(): KEY[] { return this._tokens; } values(): VALUE[] { return this._values; } get size(): number { return this._values.length; } } @@ -509,13 +301,8 @@ export class CompileTokenMap { /** * Metadata regarding compilation of a type. */ -export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMetadataWithType { - runtime: Type; - name: string; - prefix: string; - moduleUrl: string; +export class CompileTypeMetadata extends CompileIdentifierMetadata { isHost: boolean; - value: any; diDeps: CompileDiDependencyMetadata[]; constructor({runtime, name, moduleUrl, prefix, isHost, value, diDeps}: { @@ -527,41 +314,10 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe value?: any, diDeps?: CompileDiDependencyMetadata[] } = {}) { - this.runtime = runtime; - this.name = name; - this.moduleUrl = moduleUrl; - this.prefix = prefix; + super({runtime: runtime, name: name, moduleUrl: moduleUrl, prefix: prefix, value: value}); this.isHost = normalizeBool(isHost); - this.value = value; this.diDeps = _normalizeArray(diDeps); } - - static fromJson(data: {[key: string]: any}): CompileTypeMetadata { - return new CompileTypeMetadata({ - name: data['name'], - moduleUrl: data['moduleUrl'], - prefix: data['prefix'], - isHost: data['isHost'], - value: data['value'], - diDeps: _arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson) - }); - } - - get identifier(): CompileIdentifierMetadata { return this; } - get type(): CompileTypeMetadata { return this; } - - toJson(): {[key: string]: any} { - return { - // Note: Runtime type can't be serialized... - 'class': 'Type', - 'name': this.name, - 'moduleUrl': this.moduleUrl, - 'prefix': this.prefix, - 'isHost': this.isHost, - 'value': this.value, - 'diDeps': _arrayToJson(this.diDeps) - }; - } } export class CompileQueryMetadata { @@ -584,26 +340,6 @@ export class CompileQueryMetadata { this.propertyName = propertyName; this.read = read; } - - static fromJson(data: {[key: string]: any}): CompileQueryMetadata { - return new CompileQueryMetadata({ - selectors: _arrayFromJson(data['selectors'], CompileTokenMetadata.fromJson), - descendants: data['descendants'], - first: data['first'], - propertyName: data['propertyName'], - read: _objFromJson(data['read'], CompileTokenMetadata.fromJson) - }); - } - - toJson(): {[key: string]: any} { - return { - 'selectors': _arrayToJson(this.selectors), - 'descendants': this.descendants, - 'first': this.first, - 'propertyName': this.propertyName, - 'read': _objToJson(this.read) - }; - } } /** @@ -620,15 +356,6 @@ export class CompileStylesheetMetadata { this.styles = _normalizeArray(styles); this.styleUrls = _normalizeArray(styleUrls); } - - static fromJson(data: {[key: string]: any}): CompileStylesheetMetadata { - return new CompileStylesheetMetadata( - {moduleUrl: data['moduleUrl'], styles: data['styles'], styleUrls: data['styleUrls']}); - } - - toJson(): {[key: string]: any} { - return {'moduleUrl': this.moduleUrl, 'styles': this.styles, 'styleUrls': this.styleUrls}; - } } /** @@ -670,46 +397,12 @@ export class CompileTemplateMetadata { } this.interpolation = interpolation; } - - static fromJson(data: {[key: string]: any}): CompileTemplateMetadata { - var animations = - _arrayFromJson(data['animations'], metadataFromJson); - return new CompileTemplateMetadata({ - encapsulation: isPresent(data['encapsulation']) ? - VIEW_ENCAPSULATION_VALUES[data['encapsulation']] : - data['encapsulation'], - template: data['template'], - templateUrl: data['templateUrl'], - styles: data['styles'], - styleUrls: data['styleUrls'], - externalStylesheets: - _arrayFromJson(data['externalStylesheets'], CompileStylesheetMetadata.fromJson), - animations: animations, - ngContentSelectors: data['ngContentSelectors'], - interpolation: data['interpolation'] - }); - } - - toJson(): {[key: string]: any} { - return { - 'encapsulation': isPresent(this.encapsulation) ? serializeEnum(this.encapsulation) : - this.encapsulation, - 'template': this.template, - 'templateUrl': this.templateUrl, - 'styles': this.styles, - 'styleUrls': this.styleUrls, - 'externalStylesheets': _objToJson(this.externalStylesheets), - 'animations': _objToJson(this.animations), - 'ngContentSelectors': this.ngContentSelectors, - 'interpolation': this.interpolation - }; - } } /** * Metadata regarding compilation of a directive. */ -export class CompileDirectiveMetadata implements CompileMetadataWithType { +export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier { static create( {type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, lifecycleHooks, providers, viewProviders, queries, viewQueries, precompile, template}: { @@ -796,6 +489,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { viewProviders: CompileProviderMetadata[]; queries: CompileQueryMetadata[]; viewQueries: CompileQueryMetadata[]; + // Note: Need to keep types here to prevent cycles! precompile: CompileTypeMetadata[]; template: CompileTemplateMetadata; @@ -844,68 +538,26 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { get identifier(): CompileIdentifierMetadata { return this.type; } - static fromJson(data: {[key: string]: any}): CompileDirectiveMetadata { - return new CompileDirectiveMetadata({ - isComponent: data['isComponent'], - selector: data['selector'], - exportAs: data['exportAs'], - type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'], - changeDetection: isPresent(data['changeDetection']) ? - CHANGE_DETECTION_STRATEGY_VALUES[data['changeDetection']] : - data['changeDetection'], - inputs: data['inputs'], - outputs: data['outputs'], - hostListeners: data['hostListeners'], - hostProperties: data['hostProperties'], - hostAttributes: data['hostAttributes'], - lifecycleHooks: - (data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]), - template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) : - data['template'], - providers: _arrayFromJson(data['providers'], metadataFromJson), - viewProviders: _arrayFromJson(data['viewProviders'], metadataFromJson), - queries: _arrayFromJson(data['queries'], CompileQueryMetadata.fromJson), - viewQueries: _arrayFromJson(data['viewQueries'], CompileQueryMetadata.fromJson), - precompile: _arrayFromJson(data['precompile'], CompileTypeMetadata.fromJson) - }); - } + get runtimeCacheKey(): any { return this.type.runtimeCacheKey; } - toJson(): {[key: string]: any} { - return { - 'class': 'Directive', - 'isComponent': this.isComponent, - 'selector': this.selector, - 'exportAs': this.exportAs, - 'type': isPresent(this.type) ? this.type.toJson() : this.type, - 'changeDetection': isPresent(this.changeDetection) ? serializeEnum(this.changeDetection) : - this.changeDetection, - 'inputs': this.inputs, - 'outputs': this.outputs, - 'hostListeners': this.hostListeners, - 'hostProperties': this.hostProperties, - 'hostAttributes': this.hostAttributes, - 'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)), - 'template': isPresent(this.template) ? this.template.toJson() : this.template, - 'providers': _arrayToJson(this.providers), - 'viewProviders': _arrayToJson(this.viewProviders), - 'queries': _arrayToJson(this.queries), - 'viewQueries': _arrayToJson(this.viewQueries), - 'precompile': _arrayToJson(this.precompile) - }; + get assetCacheKey(): any { return this.type.assetCacheKey; } + + equalsTo(other: CompileMetadataWithIdentifier): boolean { + return this.type.equalsTo(other.identifier); } } /** * Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector. */ -export function createHostComponentMeta( - componentType: CompileTypeMetadata, componentSelector: string): CompileDirectiveMetadata { - var template = CssSelector.parse(componentSelector)[0].getMatchingElementTemplate(); +export function createHostComponentMeta(compMeta: CompileDirectiveMetadata): + CompileDirectiveMetadata { + var template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate(); return CompileDirectiveMetadata.create({ type: new CompileTypeMetadata({ runtime: Object, - name: `${componentType.name}_Host`, - moduleUrl: componentType.moduleUrl, + name: `${compMeta.type.name}_Host`, + moduleUrl: compMeta.type.moduleUrl, isHost: true }), template: new CompileTemplateMetadata({ @@ -931,7 +583,7 @@ export function createHostComponentMeta( } -export class CompilePipeMetadata implements CompileMetadataWithType { +export class CompilePipeMetadata implements CompileMetadataWithIdentifier { type: CompileTypeMetadata; name: string; pure: boolean; @@ -949,114 +601,91 @@ export class CompilePipeMetadata implements CompileMetadataWithType { this.lifecycleHooks = _normalizeArray(lifecycleHooks); } get identifier(): CompileIdentifierMetadata { return this.type; } + get runtimeCacheKey(): any { return this.type.runtimeCacheKey; } - static fromJson(data: {[key: string]: any}): CompilePipeMetadata { - return new CompilePipeMetadata({ - type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'], - name: data['name'], - pure: data['pure'] - }); - } + get assetCacheKey(): any { return this.type.assetCacheKey; } - toJson(): {[key: string]: any} { - return { - 'class': 'Pipe', - 'type': isPresent(this.type) ? this.type.toJson() : null, - 'name': this.name, - 'pure': this.pure - }; + equalsTo(other: CompileMetadataWithIdentifier): boolean { + return this.type.equalsTo(other.identifier); } } /** * Metadata regarding compilation of a directive. */ -export class CompileAppModuleMetadata implements CompileMetadataWithType { +export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier { type: CompileTypeMetadata; - providers: CompileProviderMetadata[]; - directives: CompileTypeMetadata[]; - pipes: CompileTypeMetadata[]; + declaredDirectives: CompileDirectiveMetadata[]; + exportedDirectives: CompileDirectiveMetadata[]; + declaredPipes: CompilePipeMetadata[]; + exportedPipes: CompilePipeMetadata[]; + // Note: See CompileDirectiveMetadata.precompile why this has to be a type. precompile: CompileTypeMetadata[]; - modules: CompileTypeMetadata[]; + providers: CompileProviderMetadata[]; - constructor({type, providers, directives, pipes, precompile, modules}: { - type?: CompileTypeMetadata, - providers?: Array, - directives?: CompileTypeMetadata[], - pipes?: CompileTypeMetadata[], - precompile?: CompileTypeMetadata[], - modules?: CompileTypeMetadata[] - } = {}) { + importedModules: CompileNgModuleMetadata[]; + exportedModules: CompileNgModuleMetadata[]; + + transitiveModule: TransitiveCompileNgModuleMetadata; + + constructor( + {type, providers, declaredDirectives, exportedDirectives, declaredPipes, exportedPipes, + precompile, importedModules, exportedModules, transitiveModule}: { + type?: CompileTypeMetadata, + providers?: + Array, + declaredDirectives?: CompileDirectiveMetadata[], + exportedDirectives?: CompileDirectiveMetadata[], + declaredPipes?: CompilePipeMetadata[], + exportedPipes?: CompilePipeMetadata[], + precompile?: CompileTypeMetadata[], + importedModules?: CompileNgModuleMetadata[], + exportedModules?: CompileNgModuleMetadata[], + transitiveModule?: TransitiveCompileNgModuleMetadata + } = {}) { this.type = type; - this.directives = _normalizeArray(directives); - this.pipes = _normalizeArray(pipes); + this.declaredDirectives = _normalizeArray(declaredDirectives); + this.exportedDirectives = _normalizeArray(exportedDirectives); + this.declaredPipes = _normalizeArray(declaredPipes); + this.exportedPipes = _normalizeArray(exportedPipes); this.providers = _normalizeArray(providers); this.precompile = _normalizeArray(precompile); - this.modules = _normalizeArray(modules); + this.importedModules = _normalizeArray(importedModules); + this.exportedModules = _normalizeArray(exportedModules); + this.transitiveModule = transitiveModule; } get identifier(): CompileIdentifierMetadata { return this.type; } + get runtimeCacheKey(): any { return this.type.runtimeCacheKey; } - static fromJson(data: {[key: string]: any}): CompileAppModuleMetadata { - return new CompileAppModuleMetadata({ - type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'], - providers: _arrayFromJson(data['providers'], metadataFromJson), - directives: _arrayFromJson(data['directives'], metadataFromJson), - pipes: _arrayFromJson(data['pipes'], metadataFromJson), - precompile: _arrayFromJson(data['precompile'], CompileTypeMetadata.fromJson), - modules: _arrayFromJson(data['modules'], CompileTypeMetadata.fromJson) - }); - } + get assetCacheKey(): any { return this.type.assetCacheKey; } - toJson(): {[key: string]: any} { - return { - 'class': 'AppModule', - 'type': isPresent(this.type) ? this.type.toJson() : this.type, - 'providers': _arrayToJson(this.providers), - 'directives': _arrayToJson(this.directives), - 'pipes': _arrayToJson(this.pipes), - 'precompile': _arrayToJson(this.precompile), - 'modules': _arrayToJson(this.modules) - }; + equalsTo(other: CompileMetadataWithIdentifier): boolean { + return this.type.equalsTo(other.identifier); } } -var _COMPILE_METADATA_FROM_JSON = { - 'AppModule': CompileAppModuleMetadata.fromJson, - 'Directive': CompileDirectiveMetadata.fromJson, - 'Pipe': CompilePipeMetadata.fromJson, - 'Type': CompileTypeMetadata.fromJson, - 'Provider': CompileProviderMetadata.fromJson, - 'Identifier': CompileIdentifierMetadata.fromJson, - 'Factory': CompileFactoryMetadata.fromJson, - 'AnimationEntryMetadata': CompileAnimationEntryMetadata.fromJson, - 'AnimationStateDeclarationMetadata': CompileAnimationStateDeclarationMetadata.fromJson, - 'AnimationStateTransitionMetadata': CompileAnimationStateTransitionMetadata.fromJson, - 'AnimationSequenceMetadata': CompileAnimationSequenceMetadata.fromJson, - 'AnimationGroupMetadata': CompileAnimationGroupMetadata.fromJson, - 'AnimationAnimateMetadata': CompileAnimationAnimateMetadata.fromJson, - 'AnimationStyleMetadata': CompileAnimationStyleMetadata.fromJson, - 'AnimationKeyframesSequenceMetadata': CompileAnimationKeyframesSequenceMetadata.fromJson -}; - -function _arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any { - return isBlank(obj) ? null : obj.map(o => _objFromJson(o, fn)); +export class TransitiveCompileNgModuleMetadata { + directivesSet = new Set(); + pipesSet = new Set(); + constructor( + public modules: CompileNgModuleMetadata[], public providers: CompileProviderMetadata[], + public precompile: CompileTypeMetadata[], public directives: CompileDirectiveMetadata[], + public pipes: CompilePipeMetadata[]) { + directives.forEach(dir => this.directivesSet.add(dir.type.runtime)); + pipes.forEach(pipe => this.pipesSet.add(pipe.type.runtime)); + } } -function _arrayToJson(obj: any[]): string|{[key: string]: any} { - return isBlank(obj) ? null : obj.map(_objToJson); -} - -function _objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any { - if (isArray(obj)) return _arrayFromJson(obj, fn); - if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj; - return fn(obj); -} - -function _objToJson(obj: any): string|{[key: string]: any} { - if (isArray(obj)) return _arrayToJson(obj); - if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj; - return obj.toJson(); +export function removeIdentifierDuplicates(items: T[]): + T[] { + const map = new CompileIdentifierMap(); + items.forEach((item) => { + if (!map.get(item)) { + map.add(item, item); + } + }); + return map.keys(); } function _normalizeArray(obj: any[]): any[] { diff --git a/modules/@angular/compiler/src/compiler.ts b/modules/@angular/compiler/src/compiler.ts index 0c3bb2fd30..50b83aeb08 100644 --- a/modules/@angular/compiler/src/compiler.ts +++ b/modules/@angular/compiler/src/compiler.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Compiler, CompilerFactory, CompilerOptions, ComponentResolver, Injectable, PLATFORM_DIRECTIVES, PLATFORM_PIPES, ReflectiveInjector, Type, ViewEncapsulation, isDevMode} from '@angular/core'; +import {Compiler, CompilerFactory, CompilerOptions, Component, ComponentResolver, Inject, Injectable, NgModule, PLATFORM_DIRECTIVES, PLATFORM_INITIALIZER, PLATFORM_PIPES, PlatformRef, ReflectiveInjector, Type, ViewEncapsulation, corePlatform, createPlatformFactory, disposePlatform, isDevMode} from '@angular/core'; export * from './template_ast'; export {TEMPLATE_TRANSFORMS} from './template_parser'; @@ -20,14 +20,17 @@ export * from './xhr'; export {ViewResolver} from './view_resolver'; export {DirectiveResolver} from './directive_resolver'; export {PipeResolver} from './pipe_resolver'; +export {NgModuleResolver} from './ng_module_resolver'; +import {stringify} from './facade/lang'; +import {ListWrapper} from './facade/collection'; import {TemplateParser} from './template_parser'; import {HtmlParser} from './html_parser'; import {DirectiveNormalizer} from './directive_normalizer'; import {CompileMetadataResolver} from './metadata_resolver'; import {StyleCompiler} from './style_compiler'; import {ViewCompiler} from './view_compiler/view_compiler'; -import {AppModuleCompiler} from './app_module_compiler'; +import {NgModuleCompiler} from './ng_module_compiler'; import {CompilerConfig} from './config'; import {RuntimeCompiler} from './runtime_compiler'; import {ElementSchemaRegistry} from './schema/element_schema_registry'; @@ -38,19 +41,24 @@ import {Lexer} from './expression_parser/lexer'; import {ViewResolver} from './view_resolver'; import {DirectiveResolver} from './directive_resolver'; import {PipeResolver} from './pipe_resolver'; -import {Console, Reflector, reflector, ReflectorReader} from '../core_private'; +import {NgModuleResolver} from './ng_module_resolver'; +import {Console, Reflector, reflector, ReflectorReader, ReflectionCapabilities} from '../core_private'; import {XHR} from './xhr'; +const _NO_XHR: XHR = { + get(url: string): Promise{ + throw new Error(`No XHR implementation has been provided. Can't read the url "${url}"`);} +}; + /** * A set of providers that provide `RuntimeCompiler` and its dependencies to use for * template compilation. */ export const COMPILER_PROVIDERS: Array = /*@ts2dart_const*/[ - {provide: PLATFORM_DIRECTIVES, useValue: [], multi: true}, - {provide: PLATFORM_PIPES, useValue: [], multi: true}, {provide: Reflector, useValue: reflector}, {provide: ReflectorReader, useExisting: Reflector}, + {provide: XHR, useValue: _NO_XHR}, Console, Lexer, Parser, @@ -61,112 +69,153 @@ export const COMPILER_PROVIDERS: Array = DEFAULT_PACKAGE_URL_PROVIDER, StyleCompiler, ViewCompiler, - AppModuleCompiler, + NgModuleCompiler, /*@ts2dart_Provider*/ {provide: CompilerConfig, useValue: new CompilerConfig()}, RuntimeCompiler, - /*@ts2dart_Provider*/ {provide: ComponentResolver, useExisting: RuntimeCompiler}, /*@ts2dart_Provider*/ {provide: Compiler, useExisting: RuntimeCompiler}, DomElementSchemaRegistry, /*@ts2dart_Provider*/ {provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry}, UrlResolver, ViewResolver, DirectiveResolver, - PipeResolver + PipeResolver, + NgModuleResolver ]; + +export function analyzeAppProvidersForDeprecatedConfiguration(appProviders: any[] = []): + {compilerOptions: CompilerOptions, moduleDeclarations: Type[], deprecationMessages: string[]} { + let platformDirectives: any[] = []; + let platformPipes: any[] = []; + + let compilerProviders: any[] = []; + let useDebug: boolean; + let useJit: boolean; + let defaultEncapsulation: ViewEncapsulation; + const deprecationMessages: string[] = []; + + // Note: This is a hack to still support the old way + // of configuring platform directives / pipes and the compiler xhr. + // This will soon be deprecated! + const tempInj = ReflectiveInjector.resolveAndCreate(appProviders); + const compilerConfig: CompilerConfig = tempInj.get(CompilerConfig, null); + if (compilerConfig) { + platformDirectives = compilerConfig.platformDirectives; + platformPipes = compilerConfig.platformPipes; + useJit = compilerConfig.useJit; + useDebug = compilerConfig.genDebugInfo; + defaultEncapsulation = compilerConfig.defaultEncapsulation; + deprecationMessages.push( + `Passing CompilerConfig as a regular provider is deprecated. Use the "compilerOptions" parameter of "bootstrap()" or use a custom "CompilerFactory" platform provider instead.`); + } else { + // If nobody provided a CompilerConfig, use the + // PLATFORM_DIRECTIVES / PLATFORM_PIPES values directly if existing + platformDirectives = tempInj.get(PLATFORM_DIRECTIVES, []); + platformPipes = tempInj.get(PLATFORM_PIPES, []); + } + platformDirectives = ListWrapper.flatten(platformDirectives); + platformPipes = ListWrapper.flatten(platformPipes); + const xhr = tempInj.get(XHR, null); + if (xhr) { + compilerProviders.push([{provide: XHR, useValue: xhr}]); + deprecationMessages.push( + `Passing XHR as regular provider is deprecated. Pass the provider via "compilerOptions" instead.`); + } + + if (platformDirectives.length > 0) { + deprecationMessages.push( + `The PLATFORM_DIRECTIVES provider and CompilerConfig.platformDirectives is deprecated. Add the directives to an NgModule instead! ` + + `(Directives: ${platformDirectives.map(type => stringify(type))})`); + } + if (platformPipes.length > 0) { + deprecationMessages.push( + `The PLATFORM_PIPES provider and CompilerConfig.platformPipes is deprecated. Add the pipes to an NgModule instead! ` + + `(Pipes: ${platformPipes.map(type => stringify(type))})`); + } + const compilerOptions: CompilerOptions = { + useJit: useJit, + useDebug: useDebug, + defaultEncapsulation: defaultEncapsulation, + providers: compilerProviders + }; + + // Declare a component that uses @Component.directives / pipes as these + // will be added to the module declarations only if they are not already + // imported by other modules. + @Component({directives: platformDirectives, pipes: platformPipes, template: ''}) + class DynamicComponent { + } + + return { + compilerOptions, + moduleDeclarations: [DynamicComponent], + deprecationMessages: deprecationMessages + }; +} + @Injectable() -export class _RuntimeCompilerFactory extends CompilerFactory { - createCompiler(options: CompilerOptions): Compiler { - const deprecationMessages: string[] = []; - let platformDirectivesFromAppProviders: any[] = []; - let platformPipesFromAppProviders: any[] = []; - let compilerProvidersFromAppProviders: any[] = []; - let useDebugFromAppProviders: boolean; - let useJitFromAppProviders: boolean; - let defaultEncapsulationFromAppProviders: ViewEncapsulation; - - if (options.deprecatedAppProviders && options.deprecatedAppProviders.length > 0) { - // Note: This is a hack to still support the old way - // of configuring platform directives / pipes and the compiler xhr. - // This will soon be deprecated! - const inj = ReflectiveInjector.resolveAndCreate(options.deprecatedAppProviders); - const compilerConfig: CompilerConfig = inj.get(CompilerConfig, null); - if (compilerConfig) { - platformDirectivesFromAppProviders = compilerConfig.deprecatedPlatformDirectives; - platformPipesFromAppProviders = compilerConfig.deprecatedPlatformPipes; - useJitFromAppProviders = compilerConfig.useJit; - useDebugFromAppProviders = compilerConfig.genDebugInfo; - defaultEncapsulationFromAppProviders = compilerConfig.defaultEncapsulation; - deprecationMessages.push( - `Passing a CompilerConfig to "bootstrap()" as provider is deprecated. Pass the provider via the new parameter "compilerOptions" of "bootstrap()" instead.`); - } else { - // If nobody provided a CompilerConfig, use the - // PLATFORM_DIRECTIVES / PLATFORM_PIPES values directly if existing - platformDirectivesFromAppProviders = inj.get(PLATFORM_DIRECTIVES, []); - if (platformDirectivesFromAppProviders.length > 0) { - deprecationMessages.push( - `Passing PLATFORM_DIRECTIVES to "bootstrap()" as provider is deprecated. Use the new parameter "directives" of "bootstrap()" instead.`); - } - platformPipesFromAppProviders = inj.get(PLATFORM_PIPES, []); - if (platformPipesFromAppProviders.length > 0) { - deprecationMessages.push( - `Passing PLATFORM_PIPES to "bootstrap()" as provider is deprecated. Use the new parameter "pipes" of "bootstrap()" instead.`); - } - } - const xhr = inj.get(XHR, null); - if (xhr) { - compilerProvidersFromAppProviders.push([{provide: XHR, useValue: xhr}]); - deprecationMessages.push( - `Passing an instance of XHR to "bootstrap()" as provider is deprecated. Pass the provider via the new parameter "compilerOptions" of "bootstrap()" instead.`); - } - // Need to copy console from deprecatedAppProviders to compiler providers - // as well so that we can test the above deprecation messages in old style bootstrap - // where we only have app providers! - const console = inj.get(Console, null); - if (console) { - compilerProvidersFromAppProviders.push([{provide: Console, useValue: console}]); - } - } - +export class RuntimeCompilerFactory implements CompilerFactory { + private _defaultOptions: CompilerOptions[]; + constructor(@Inject(CompilerOptions) defaultOptions: CompilerOptions[]) { + this._defaultOptions = [{ + useDebug: isDevMode(), + useJit: true, + defaultEncapsulation: ViewEncapsulation.Emulated + }].concat(defaultOptions); + } + createCompiler(options: CompilerOptions[] = []): Compiler { + const mergedOptions = _mergeOptions(this._defaultOptions.concat(options)); const injector = ReflectiveInjector.resolveAndCreate([ COMPILER_PROVIDERS, { provide: CompilerConfig, - useFactory: (platformDirectives: any[], platformPipes: any[]) => { + useFactory: () => { return new CompilerConfig({ - deprecatedPlatformDirectives: - _mergeArrays(platformDirectivesFromAppProviders, platformDirectives), - deprecatedPlatformPipes: _mergeArrays(platformPipesFromAppProviders, platformPipes), // let explicit values from the compiler options overwrite options // from the app providers. E.g. important for the testing platform. - genDebugInfo: _firstDefined(options.useDebug, useDebugFromAppProviders, isDevMode()), + genDebugInfo: mergedOptions.useDebug, // let explicit values from the compiler options overwrite options // from the app providers - useJit: _firstDefined(options.useJit, useJitFromAppProviders, true), + useJit: mergedOptions.useJit, // let explicit values from the compiler options overwrite options // from the app providers - defaultEncapsulation: _firstDefined( - options.defaultEncapsulation, defaultEncapsulationFromAppProviders, - ViewEncapsulation.Emulated) + defaultEncapsulation: mergedOptions.defaultEncapsulation, + logBindingUpdate: mergedOptions.useDebug }); }, - deps: [PLATFORM_DIRECTIVES, PLATFORM_PIPES] + deps: [] }, - // options.providers will always contain a provider for XHR as well - // (added by platforms). So allow compilerProvidersFromAppProviders to overwrite this - _mergeArrays(options.providers, compilerProvidersFromAppProviders) + mergedOptions.providers ]); - const console: Console = injector.get(Console); - deprecationMessages.forEach((msg) => { console.warn(msg); }); - return injector.get(Compiler); } } +function _initReflector() { + reflector.reflectionCapabilities = new ReflectionCapabilities(); +} -export const RUNTIME_COMPILER_FACTORY = new _RuntimeCompilerFactory(); +/** + * A platform that included corePlatform and the compiler. + * + * @experimental + */ +export const coreDynamicPlatform = createPlatformFactory(corePlatform, 'coreDynamic', [ + {provide: CompilerOptions, useValue: {}, multi: true}, + {provide: CompilerFactory, useClass: RuntimeCompilerFactory}, + {provide: PLATFORM_INITIALIZER, useValue: _initReflector, multi: true}, +]); -function _firstDefined(...args: T[]): T { - for (var i = 0; i < args.length; i++) { +function _mergeOptions(optionsArr: CompilerOptions[]): CompilerOptions { + return { + useDebug: _lastDefined(optionsArr.map(options => options.useDebug)), + useJit: _lastDefined(optionsArr.map(options => options.useJit)), + defaultEncapsulation: _lastDefined(optionsArr.map(options => options.defaultEncapsulation)), + providers: _mergeArrays(optionsArr.map(options => options.providers)) + }; +} + +function _lastDefined(args: T[]): T { + for (var i = args.length - 1; i >= 0; i--) { if (args[i] !== undefined) { return args[i]; } @@ -174,7 +223,7 @@ function _firstDefined(...args: T[]): T { return undefined; } -function _mergeArrays(...parts: any[][]): any[] { +function _mergeArrays(parts: any[][]): any[] { let result: any[] = []; parts.forEach((part) => result.push(...part)); return result; diff --git a/modules/@angular/compiler/src/config.ts b/modules/@angular/compiler/src/config.ts index 4e7f74ae00..a765f34324 100644 --- a/modules/@angular/compiler/src/config.ts +++ b/modules/@angular/compiler/src/config.ts @@ -20,17 +20,15 @@ export class CompilerConfig { private _logBindingUpdate: boolean; public useJit: boolean; /** - * @deprecated Providing platform directives via the {@link CompilerConfig} deprecated. Provide - * platform - * directives via an {@link AppModule} instead. + * @deprecated Providing platform directives via the {@link CompilerConfig} is deprecated. Provide + * platform directives via an {@link NgModule} instead. */ - public deprecatedPlatformDirectives: any[]; + public platformDirectives: any[]; /** - * @deprecated Providing platform pipes via the {@link CompilerConfig} deprecated. Provide - * platform pipes - * via an {@link AppModule} instead. + * @deprecated Providing platform pipes via the {@link CompilerConfig} is deprecated. Provide + * platform pipes via an {@link NgModule} instead. */ - public deprecatedPlatformPipes: any[]; + public platformPipes: any[]; constructor( {renderTypes = new DefaultRenderTypes(), defaultEncapsulation = ViewEncapsulation.Emulated, @@ -49,8 +47,8 @@ export class CompilerConfig { this._genDebugInfo = genDebugInfo; this._logBindingUpdate = logBindingUpdate; this.useJit = useJit; - this.deprecatedPlatformDirectives = deprecatedPlatformDirectives; - this.deprecatedPlatformPipes = deprecatedPlatformPipes; + this.platformDirectives = deprecatedPlatformDirectives; + this.platformPipes = deprecatedPlatformPipes; } get genDebugInfo(): boolean { diff --git a/modules/@angular/compiler/src/directive_resolver.ts b/modules/@angular/compiler/src/directive_resolver.ts index 2c18904041..939541163c 100644 --- a/modules/@angular/compiler/src/directive_resolver.ts +++ b/modules/@angular/compiler/src/directive_resolver.ts @@ -33,7 +33,7 @@ export class DirectiveResolver { /** * Return {@link DirectiveMetadata} for a given `Type`. */ - resolve(type: Type): DirectiveMetadata { + resolve(type: Type, throwIfNotFound = true): DirectiveMetadata { var typeMetadata = this._reflector.annotations(resolveForwardRef(type)); if (isPresent(typeMetadata)) { var metadata = typeMetadata.find(_isDirectiveMetadata); @@ -42,8 +42,10 @@ export class DirectiveResolver { return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type); } } - - throw new BaseException(`No Directive annotation found on ${stringify(type)}`); + if (throwIfNotFound) { + throw new BaseException(`No Directive annotation found on ${stringify(type)}`); + } + return null; } private _mergeWithPropertyMetadata( diff --git a/modules/@angular/compiler/src/identifiers.ts b/modules/@angular/compiler/src/identifiers.ts index bf07e272d4..304674fdc4 100644 --- a/modules/@angular/compiler/src/identifiers.ts +++ b/modules/@angular/compiler/src/identifiers.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {ANALYZE_FOR_PRECOMPILE, AppModuleFactory, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; +import {ANALYZE_FOR_PRECOMPILE, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; -import {AnimationGroupPlayer as AnimationGroupPlayer_, AnimationKeyframe as AnimationKeyframe_, AnimationSequencePlayer as AnimationSequencePlayer_, AnimationStyles as AnimationStyles_, AppElement, AppModuleInjector, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NoOpAnimationPlayer as NoOpAnimationPlayer_, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes as impBalanceAnimationKeyframes, castByValue, checkBinding, clearStyles as impClearStyles, collectAndResolveStyles as impCollectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles as impBalanceAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, renderStyles as impRenderStyles} from '../core_private'; +import {AnimationGroupPlayer as AnimationGroupPlayer_, AnimationKeyframe as AnimationKeyframe_, AnimationSequencePlayer as AnimationSequencePlayer_, AnimationStyles as AnimationStyles_, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer as NoOpAnimationPlayer_, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes as impBalanceAnimationKeyframes, castByValue, checkBinding, clearStyles as impClearStyles, collectAndResolveStyles as impCollectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles as impBalanceAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, renderStyles as impRenderStyles} from '../core_private'; import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata'; import {assetUrl} from './util'; @@ -118,15 +118,15 @@ export class Identifiers { runtime: ComponentFactory, moduleUrl: assetUrl('core', 'linker/component_factory') }); - static AppModuleFactory = new CompileIdentifierMetadata({ - name: 'AppModuleFactory', - runtime: AppModuleFactory, - moduleUrl: assetUrl('core', 'linker/app_module_factory') + static NgModuleFactory = new CompileIdentifierMetadata({ + name: 'NgModuleFactory', + runtime: NgModuleFactory, + moduleUrl: assetUrl('core', 'linker/ng_module_factory') }); - static AppModuleInjector = new CompileIdentifierMetadata({ - name: 'AppModuleInjector', - runtime: AppModuleInjector, - moduleUrl: assetUrl('core', 'linker/app_module_factory') + static NgModuleInjector = new CompileIdentifierMetadata({ + name: 'NgModuleInjector', + runtime: NgModuleInjector, + moduleUrl: assetUrl('core', 'linker/ng_module_factory') }); static ValueUnwrapper = new CompileIdentifierMetadata( {name: 'ValueUnwrapper', moduleUrl: CD_MODULE_URL, runtime: impValueUnwrapper}); diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index 278f9c7074..e8615891c5 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, AppModuleMetadata, AttributeMetadata, ChangeDetectionStrategy, ComponentMetadata, HostMetadata, Inject, InjectMetadata, Injectable, Optional, OptionalMetadata, Provider, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewMetadata, ViewQueryMetadata, resolveForwardRef} from '@angular/core'; +import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, AttributeMetadata, ChangeDetectionStrategy, ComponentMetadata, HostMetadata, Inject, InjectMetadata, Injectable, NgModule, NgModuleMetadata, Optional, OptionalMetadata, Provider, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewMetadata, ViewQueryMetadata, resolveForwardRef} from '@angular/core'; -import {LIFECYCLE_HOOKS_VALUES, ReflectorReader, createProvider, isProviderLiteral, reflector} from '../core_private'; -import {StringMapWrapper} from '../src/facade/collection'; +import {Console, LIFECYCLE_HOOKS_VALUES, ReflectorReader, createProvider, isProviderLiteral, reflector} from '../core_private'; +import {MapWrapper, StringMapWrapper} from '../src/facade/collection'; import {BaseException} from '../src/facade/exceptions'; import {Type, isArray, isBlank, isPresent, isString, isStringMap, stringify} from '../src/facade/lang'; @@ -19,6 +19,7 @@ import {CompilerConfig} from './config'; import {hasLifecycleHook} from './directive_lifecycle_reflector'; import {DirectiveResolver} from './directive_resolver'; import {Identifiers, identifierToken} from './identifiers'; +import {NgModuleResolver} from './ng_module_resolver'; import {PipeResolver} from './pipe_resolver'; import {getUrlScheme} from './url_resolver'; import {MODULE_SUFFIX, ValueTransformer, sanitizeIdentifier, visitValue} from './util'; @@ -28,13 +29,15 @@ import {ViewResolver} from './view_resolver'; export class CompileMetadataResolver { private _directiveCache = new Map(); private _pipeCache = new Map(); - private _appModuleCache = new Map(); + private _ngModuleCache = new Map(); + private _ngModuleOfTypes = new Map(); private _anonymousTypes = new Map(); private _anonymousTypeIndex = 0; constructor( - private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver, - private _viewResolver: ViewResolver, private _config: CompilerConfig, + private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver, + private _pipeResolver: PipeResolver, private _viewResolver: ViewResolver, + private _config: CompilerConfig, private _console: Console, private _reflector: ReflectorReader = reflector) {} private sanitizeTokenName(token: any): string { @@ -54,13 +57,16 @@ export class CompileMetadataResolver { clearCacheFor(type: Type) { this._directiveCache.delete(type); this._pipeCache.delete(type); - this._appModuleCache.delete(type); + this._ngModuleOfTypes.delete(type); + // Clear all of the NgModuleMetadata as they contain transitive information! + this._ngModuleCache.clear(); } clearCache() { this._directiveCache.clear(); this._pipeCache.clear(); - this._appModuleCache.clear(); + this._ngModuleCache.clear(); + this._ngModuleOfTypes.clear(); } getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata { @@ -105,11 +111,14 @@ export class CompileMetadataResolver { return null; } - getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata { + getDirectiveMetadata(directiveType: Type, throwIfNotFound = true): cpl.CompileDirectiveMetadata { directiveType = resolveForwardRef(directiveType); var meta = this._directiveCache.get(directiveType); if (isBlank(meta)) { - var dirMeta = this._directiveResolver.resolve(directiveType); + var dirMeta = this._directiveResolver.resolve(directiveType, throwIfNotFound); + if (!dirMeta) { + return null; + } var templateMeta: cpl.CompileTemplateMetadata = null; var changeDetectionStrategy: ChangeDetectionStrategy = null; var viewProviders: Array = []; @@ -182,83 +191,254 @@ export class CompileMetadataResolver { return meta; } - getAppModuleMetadata(moduleType: any, meta: AppModuleMetadata = null): - cpl.CompileAppModuleMetadata { - // Only cache if we read the metadata via the reflector, - // as we use the moduleType as cache key. - let useCache = !meta; + getNgModuleMetadata(moduleType: any, throwIfNotFound = true): cpl.CompileNgModuleMetadata { moduleType = resolveForwardRef(moduleType); - var compileMeta = this._appModuleCache.get(moduleType); - if (isBlank(compileMeta) || !useCache) { + var compileMeta = this._ngModuleCache.get(moduleType); + if (!compileMeta) { + const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound); if (!meta) { - meta = this._reflector.annotations(moduleType) - .find((meta) => meta instanceof AppModuleMetadata); + return null; } - if (!meta) { - throw new BaseException( - `Could not compile '${stringify(moduleType)}' because it is not an AppModule.`); - } - let modules: cpl.CompileTypeMetadata[] = []; - let providers: any[] = []; - let directives: cpl.CompileTypeMetadata[] = []; - let pipes: cpl.CompileTypeMetadata[] = []; - let precompile: cpl.CompileTypeMetadata[] = []; - if (meta.modules) { - flattenArray(meta.modules).forEach((moduleType) => { - var meta = this.getAppModuleMetadata(moduleType); - providers.push(...meta.providers); - directives.push(...meta.directives); - pipes.push(...meta.pipes); - precompile.push(...meta.precompile); - modules.push(meta.type); - modules.push(...meta.modules); + const declaredDirectives: cpl.CompileDirectiveMetadata[] = []; + const exportedDirectives: cpl.CompileDirectiveMetadata[] = []; + const declaredPipes: cpl.CompilePipeMetadata[] = []; + const exportedPipes: cpl.CompilePipeMetadata[] = []; + const importedModules: cpl.CompileNgModuleMetadata[] = []; + const exportedModules: cpl.CompileNgModuleMetadata[] = []; + + if (meta.imports) { + flattenArray(meta.imports).forEach((importedType) => { + if (!isValidType(importedType)) { + throw new BaseException( + `Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`); + } + let importedModuleMeta: cpl.CompileNgModuleMetadata; + if (importedModuleMeta = this.getNgModuleMetadata(importedType, false)) { + importedModules.push(importedModuleMeta); + } else { + throw new BaseException( + `Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`); + } }); } + if (meta.exports) { + flattenArray(meta.exports).forEach((exportedType) => { + if (!isValidType(exportedType)) { + throw new BaseException( + `Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`); + } + let exportedDirMeta: cpl.CompileDirectiveMetadata; + let exportedPipeMeta: cpl.CompilePipeMetadata; + let exportedModuleMeta: cpl.CompileNgModuleMetadata; + if (exportedDirMeta = this.getDirectiveMetadata(exportedType, false)) { + exportedDirectives.push(exportedDirMeta); + } else if (exportedPipeMeta = this.getPipeMetadata(exportedType, false)) { + exportedPipes.push(exportedPipeMeta); + } else if (exportedModuleMeta = this.getNgModuleMetadata(exportedType, false)) { + exportedModules.push(exportedModuleMeta); + } else { + throw new BaseException( + `Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`); + } + }); + } + + // Note: This will be modified later, so we rely on + // getting a new instance every time! + const transitiveModule = + this._getTransitiveNgModuleMetadata(importedModules, exportedModules); + if (meta.declarations) { + flattenArray(meta.declarations).forEach((declaredType) => { + if (!isValidType(declaredType)) { + throw new BaseException( + `Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`); + } + let declaredDirMeta: cpl.CompileDirectiveMetadata; + let declaredPipeMeta: cpl.CompilePipeMetadata; + if (declaredDirMeta = this.getDirectiveMetadata(declaredType, false)) { + this._addDirectiveToModule( + declaredDirMeta, moduleType, transitiveModule, declaredDirectives, true); + // Collect @Component.directives/pipes/precompile into our declared directives/pipes. + this._getTransitiveViewDirectivesAndPipes( + declaredDirMeta, moduleType, transitiveModule, declaredDirectives, declaredPipes); + } else if (declaredPipeMeta = this.getPipeMetadata(declaredType, false)) { + this._addPipeToModule( + declaredPipeMeta, moduleType, transitiveModule, declaredPipes, true); + } else { + throw new BaseException( + `Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`); + } + }); + } + + const providers: any[] = []; + const precompile: cpl.CompileTypeMetadata[] = []; if (meta.providers) { providers.push(...this.getProvidersMetadata(meta.providers, precompile)); } - if (meta.directives) { - directives.push(...flattenArray(meta.directives) - .map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type)))); - } - if (meta.pipes) { - pipes.push(...flattenArray(meta.pipes) - .map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type)))); - } if (meta.precompile) { precompile.push(...flattenArray(meta.precompile) .map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type)))); } - compileMeta = new cpl.CompileAppModuleMetadata({ + transitiveModule.precompile.push(...precompile); + transitiveModule.providers.push(...providers); + + compileMeta = new cpl.CompileNgModuleMetadata({ type: this.getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)), providers: providers, - directives: directives, - pipes: pipes, precompile: precompile, - modules: modules + declaredDirectives: declaredDirectives, + exportedDirectives: exportedDirectives, + declaredPipes: declaredPipes, + exportedPipes: exportedPipes, + importedModules: importedModules, + exportedModules: exportedModules, + transitiveModule: transitiveModule }); - if (useCache) { - this._appModuleCache.set(moduleType, compileMeta); - } + transitiveModule.modules.push(compileMeta); + this._verifyModule(compileMeta); + this._ngModuleCache.set(moduleType, compileMeta); } return compileMeta; } - /** - * @param someType a symbol which may or may not be a directive type - * @returns {cpl.CompileDirectiveMetadata} if possible, otherwise null. - */ - maybeGetDirectiveMetadata(someType: Type): cpl.CompileDirectiveMetadata { - try { - return this.getDirectiveMetadata(someType); - } catch (e) { - if (e.message.indexOf('No Directive annotation') !== -1) { - return null; + addComponentToModule(moduleType: Type, compType: Type) { + const moduleMeta = this.getNgModuleMetadata(moduleType); + // Collect @Component.directives/pipes/precompile into our declared directives/pipes. + const compMeta = this.getDirectiveMetadata(compType, false); + this._addDirectiveToModule( + compMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, + moduleMeta.declaredDirectives); + this._getTransitiveViewDirectivesAndPipes( + compMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, + moduleMeta.declaredDirectives, moduleMeta.declaredPipes); + + moduleMeta.transitiveModule.precompile.push(compMeta.type); + moduleMeta.precompile.push(compMeta.type); + + this._verifyModule(moduleMeta); + } + + private _verifyModule(moduleMeta: cpl.CompileNgModuleMetadata) { + moduleMeta.exportedDirectives.forEach((dirMeta) => { + if (!moduleMeta.transitiveModule.directivesSet.has(dirMeta.type.runtime)) { + throw new BaseException( + `Can't export directive ${stringify(dirMeta.type.runtime)} from ${stringify(moduleMeta.type.runtime)} as it was neither declared nor imported!`); } - throw e; + }); + moduleMeta.exportedPipes.forEach((pipeMeta) => { + if (!moduleMeta.transitiveModule.pipesSet.has(pipeMeta.type.runtime)) { + throw new BaseException( + `Can't export pipe ${stringify(pipeMeta.type.runtime)} from ${stringify(moduleMeta.type.runtime)} as it was neither declared nor imported!`); + } + }); + moduleMeta.declaredDirectives.forEach((dirMeta) => { + dirMeta.precompile.forEach((precompileComp) => { + if (!moduleMeta.transitiveModule.directivesSet.has(precompileComp.runtime)) { + throw new BaseException( + `Component ${stringify(dirMeta.type.runtime)} in NgModule ${stringify(moduleMeta.type.runtime)} uses ${stringify(precompileComp.runtime)} via "precompile" but it was neither declared nor imported into the module!`); + } + }); + }); + moduleMeta.precompile.forEach((precompileType) => { + if (!moduleMeta.transitiveModule.directivesSet.has(precompileType.runtime)) { + throw new BaseException( + `NgModule ${stringify(moduleMeta.type.runtime)} uses ${stringify(precompileType.runtime)} via "precompile" but it was neither declared nor imported!`); + } + }); + } + + private _addTypeToModule(type: Type, moduleType: Type) { + const oldModule = this._ngModuleOfTypes.get(type); + if (oldModule && oldModule !== moduleType) { + throw new BaseException( + `Type ${stringify(type)} is part of the declarations of 2 modules: ${stringify(oldModule)} and ${stringify(moduleType)}!`); } + this._ngModuleOfTypes.set(type, moduleType); + } + + + private _getTransitiveViewDirectivesAndPipes( + compMeta: cpl.CompileDirectiveMetadata, moduleType: any, + transitiveModule: cpl.TransitiveCompileNgModuleMetadata, + declaredDirectives: cpl.CompileDirectiveMetadata[], + declaredPipes: cpl.CompilePipeMetadata[]) { + if (!compMeta.isComponent) { + return; + } + const addPipe = (pipeType: Type) => { + if (!pipeType) { + throw new BaseException( + `Unexpected pipe value '${pipeType}' on the View of component '${stringify(compMeta.type.runtime)}'`); + } + const pipeMeta = this.getPipeMetadata(pipeType); + this._addPipeToModule(pipeMeta, moduleType, transitiveModule, declaredPipes); + }; + + const addDirective = (dirType: Type) => { + if (!dirType) { + throw new BaseException( + `Unexpected directive value '${dirType}' on the View of component '${stringify(compMeta.type.runtime)}'`); + } + const dirMeta = this.getDirectiveMetadata(dirType); + if (this._addDirectiveToModule(dirMeta, moduleType, transitiveModule, declaredDirectives)) { + this._getTransitiveViewDirectivesAndPipes( + dirMeta, moduleType, transitiveModule, declaredDirectives, declaredPipes); + } + }; + const view = this._viewResolver.resolve(compMeta.type.runtime); + if (view.pipes) { + flattenArray(view.pipes).forEach(addPipe); + } + if (view.directives) { + flattenArray(view.directives).forEach(addDirective); + } + } + + private _getTransitiveNgModuleMetadata( + importedModules: cpl.CompileNgModuleMetadata[], + exportedModules: cpl.CompileNgModuleMetadata[]): cpl.TransitiveCompileNgModuleMetadata { + // collect `providers` / `precompile` from all imported and all exported modules + const transitiveModules = getTransitiveModules(importedModules.concat(exportedModules), true); + const providers = flattenArray(transitiveModules.map((ngModule) => ngModule.providers)); + const precompile = flattenArray(transitiveModules.map((ngModule) => ngModule.precompile)); + + const transitiveExportedModules = getTransitiveModules(importedModules, false); + const directives = + flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedDirectives)); + const pipes = flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedPipes)); + return new cpl.TransitiveCompileNgModuleMetadata( + transitiveModules, providers, precompile, directives, pipes); + } + + private _addDirectiveToModule( + dirMeta: cpl.CompileDirectiveMetadata, moduleType: any, + transitiveModule: cpl.TransitiveCompileNgModuleMetadata, + declaredDirectives: cpl.CompileDirectiveMetadata[], force: boolean = false): boolean { + if (force || !transitiveModule.directivesSet.has(dirMeta.type.runtime)) { + transitiveModule.directivesSet.add(dirMeta.type.runtime); + transitiveModule.directives.push(dirMeta); + declaredDirectives.push(dirMeta); + this._addTypeToModule(dirMeta.type.runtime, moduleType); + return true; + } + return false; + } + + private _addPipeToModule( + pipeMeta: cpl.CompilePipeMetadata, moduleType: any, + transitiveModule: cpl.TransitiveCompileNgModuleMetadata, + declaredPipes: cpl.CompilePipeMetadata[], force: boolean = false): boolean { + if (force || !transitiveModule.pipesSet.has(pipeMeta.type.runtime)) { + transitiveModule.pipesSet.add(pipeMeta.type.runtime); + transitiveModule.pipes.push(pipeMeta); + declaredPipes.push(pipeMeta); + this._addTypeToModule(pipeMeta.type.runtime, moduleType); + return true; + } + return false; } getTypeMetadata(type: Type, moduleUrl: string, dependencies: any[] = null): @@ -283,11 +463,14 @@ export class CompileMetadataResolver { }); } - getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata { + getPipeMetadata(pipeType: Type, throwIfNotFound = true): cpl.CompilePipeMetadata { pipeType = resolveForwardRef(pipeType); var meta = this._pipeCache.get(pipeType); if (isBlank(meta)) { - var pipeMeta = this._pipeResolver.resolve(pipeType); + var pipeMeta = this._pipeResolver.resolve(pipeType, throwIfNotFound); + if (!pipeMeta) { + return null; + } meta = new cpl.CompilePipeMetadata({ type: this.getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)), name: pipeMeta.name, @@ -299,30 +482,6 @@ export class CompileMetadataResolver { return meta; } - getViewDirectivesMetadata(component: Type): cpl.CompileDirectiveMetadata[] { - var view = this._viewResolver.resolve(component); - var directives = flattenDirectives(view, this._config.deprecatedPlatformDirectives); - for (var i = 0; i < directives.length; i++) { - if (!isValidType(directives[i])) { - throw new BaseException( - `Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`); - } - } - return directives.map(type => this.getDirectiveMetadata(type)); - } - - getViewPipesMetadata(component: Type): cpl.CompilePipeMetadata[] { - var view = this._viewResolver.resolve(component); - var pipes = flattenPipes(view, this._config.deprecatedPlatformPipes); - for (var i = 0; i < pipes.length; i++) { - if (!isValidType(pipes[i])) { - throw new BaseException( - `Unexpected piped value '${stringify(pipes[i])}' on the View of component '${stringify(component)}'`); - } - } - return pipes.map(type => this.getPipeMetadata(type)); - } - getDependenciesMetadata(typeOrFunc: Type|Function, dependencies: any[]): cpl.CompileDiDependencyMetadata[] { let hasUnknownDeps = false; @@ -454,7 +613,7 @@ export class CompileMetadataResolver { } convertToCompileValue(provider.useValue, collectedIdentifiers); collectedIdentifiers.forEach((identifier) => { - let dirMeta = this.maybeGetDirectiveMetadata(identifier.runtime); + let dirMeta = this.getDirectiveMetadata(identifier.runtime, false); if (dirMeta) { components.push(dirMeta.type); } @@ -523,35 +682,35 @@ export class CompileMetadataResolver { } } -function flattenDirectives(view: ViewMetadata, platformDirectives: any[]): Type[] { - let directives: Type[] = []; - if (isPresent(platformDirectives)) { - flattenArray(platformDirectives, directives); - } - if (isPresent(view.directives)) { - flattenArray(view.directives, directives); - } - return directives; +function getTransitiveModules( + modules: cpl.CompileNgModuleMetadata[], includeImports: boolean, + targetModules: cpl.CompileNgModuleMetadata[] = [], + visitedModules = new Set()): cpl.CompileNgModuleMetadata[] { + modules.forEach((ngModule) => { + if (!visitedModules.has(ngModule.type.runtime)) { + visitedModules.add(ngModule.type.runtime); + const nestedModules = includeImports ? + ngModule.importedModules.concat(ngModule.exportedModules) : + ngModule.exportedModules; + getTransitiveModules(nestedModules, includeImports, targetModules, visitedModules); + // Add after recursing so imported/exported modules are before the module itself. + // This is important for overwriting providers of imported modules! + targetModules.push(ngModule); + } + }); + return targetModules; } -function flattenPipes(view: ViewMetadata, platformPipes: any[]): Type[] { - let pipes: Type[] = []; - if (isPresent(platformPipes)) { - flattenArray(platformPipes, pipes); - } - if (isPresent(view.pipes)) { - flattenArray(view.pipes, pipes); - } - return pipes; -} -function flattenArray(tree: any[], out: Array = []): Array { - for (var i = 0; i < tree.length; i++) { - var item = resolveForwardRef(tree[i]); - if (isArray(item)) { - flattenArray(item, out); - } else { - out.push(item); +function flattenArray(tree: any[], out: Array = []): Array { + if (tree) { + for (var i = 0; i < tree.length; i++) { + var item = resolveForwardRef(tree[i]); + if (isArray(item)) { + flattenArray(item, out); + } else { + out.push(item); + } } } return out; diff --git a/modules/@angular/compiler/src/app_module_compiler.ts b/modules/@angular/compiler/src/ng_module_compiler.ts similarity index 79% rename from modules/@angular/compiler/src/app_module_compiler.ts rename to modules/@angular/compiler/src/ng_module_compiler.ts index b0ac17ff1c..cfc7d56dcf 100644 --- a/modules/@angular/compiler/src/app_module_compiler.ts +++ b/modules/@angular/compiler/src/ng_module_compiler.ts @@ -8,13 +8,13 @@ import {Injectable} from '@angular/core'; -import {CompileAppModuleMetadata, CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileTokenMap, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata'; +import {CompileDiDependencyMetadata, CompileIdentifierMap, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata'; import {isBlank, isPresent} from './facade/lang'; import {Identifiers, identifierToken} from './identifiers'; import * as o from './output/output_ast'; import {convertValueToOutputAst} from './output/value_util'; import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util'; -import {AppModuleProviderParser} from './provider_parser'; +import {NgModuleProviderAnalyzer} from './provider_analyzer'; import {ProviderAst, ProviderAstType} from './template_ast'; import {createDiTokenExpression} from './util'; @@ -23,57 +23,58 @@ export class ComponentFactoryDependency { public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {} } -export class AppModuleCompileResult { +export class NgModuleCompileResult { constructor( - public statements: o.Statement[], public appModuleFactoryVar: string, + public statements: o.Statement[], public ngModuleFactoryVar: string, public dependencies: ComponentFactoryDependency[]) {} } @Injectable() -export class AppModuleCompiler { - compile(appModuleMeta: CompileAppModuleMetadata): AppModuleCompileResult { - var sourceFileName = isPresent(appModuleMeta.type.moduleUrl) ? - `in AppModule ${appModuleMeta.type.name} in ${appModuleMeta.type.moduleUrl}` : - `in AppModule ${appModuleMeta.type.name}`; +export class NgModuleCompiler { + compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]): + NgModuleCompileResult { + var sourceFileName = isPresent(ngModuleMeta.type.moduleUrl) ? + `in NgModule ${ngModuleMeta.type.name} in ${ngModuleMeta.type.moduleUrl}` : + `in NgModule ${ngModuleMeta.type.name}`; var sourceFile = new ParseSourceFile('', sourceFileName); var sourceSpan = new ParseSourceSpan( new ParseLocation(sourceFile, null, null, null), new ParseLocation(sourceFile, null, null, null)); var deps: ComponentFactoryDependency[] = []; - var precompileComponents = appModuleMeta.precompile.map((precompileComp) => { + var precompileComponents = ngModuleMeta.transitiveModule.precompile.map((precompileComp) => { var id = new CompileIdentifierMetadata({name: precompileComp.name}); deps.push(new ComponentFactoryDependency(precompileComp, id)); return id; }); - var builder = new _InjectorBuilder(appModuleMeta, precompileComponents, sourceSpan); + var builder = new _InjectorBuilder(ngModuleMeta, precompileComponents, sourceSpan); - var providerParser = new AppModuleProviderParser(appModuleMeta, sourceSpan); + var providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan); providerParser.parse().forEach((provider) => builder.addProvider(provider)); var injectorClass = builder.build(); - var appModuleFactoryVar = `${appModuleMeta.type.name}NgFactory`; - var appModuleFactoryStmt = - o.variable(appModuleFactoryVar) - .set(o.importExpr(Identifiers.AppModuleFactory) + var ngModuleFactoryVar = `${ngModuleMeta.type.name}NgFactory`; + var ngModuleFactoryStmt = + o.variable(ngModuleFactoryVar) + .set(o.importExpr(Identifiers.NgModuleFactory) .instantiate( - [o.variable(injectorClass.name), o.importExpr(appModuleMeta.type)], + [o.variable(injectorClass.name), o.importExpr(ngModuleMeta.type)], o.importType( - Identifiers.AppModuleFactory, [o.importType(appModuleMeta.type)], + Identifiers.NgModuleFactory, [o.importType(ngModuleMeta.type)], [o.TypeModifier.Const]))) .toDeclStmt(null, [o.StmtModifier.Final]); - return new AppModuleCompileResult( - [injectorClass, appModuleFactoryStmt], appModuleFactoryVar, deps); + return new NgModuleCompileResult( + [injectorClass, ngModuleFactoryStmt], ngModuleFactoryVar, deps); } } class _InjectorBuilder { - private _instances = new CompileTokenMap(); + private _instances = new CompileIdentifierMap(); private _fields: o.ClassField[] = []; private _createStmts: o.Statement[] = []; private _getters: o.ClassGetter[] = []; constructor( - private _appModuleMeta: CompileAppModuleMetadata, + private _ngModuleMeta: CompileNgModuleMetadata, private _precompileComponents: CompileIdentifierMetadata[], private _sourceSpan: ParseSourceSpan) {} @@ -97,8 +98,8 @@ class _InjectorBuilder { var methods = [ new o.ClassMethod( 'createInternal', [], this._createStmts.concat( - new o.ReturnStatement(this._instances.get(identifierToken(this._appModuleMeta.type))) - ), o.importType(this._appModuleMeta.type) + new o.ReturnStatement(this._instances.get(identifierToken(this._ngModuleMeta.type))) + ), o.importType(this._ngModuleMeta.type) ), new o.ClassMethod( 'getInternal', @@ -120,10 +121,10 @@ class _InjectorBuilder { ]) .toStmt()]); - var injClassName = `${this._appModuleMeta.type.name}Injector`; + var injClassName = `${this._ngModuleMeta.type.name}Injector`; return new o.ClassStmt( injClassName, - o.importExpr(Identifiers.AppModuleInjector, [o.importType(this._appModuleMeta.type)]), + o.importExpr(Identifiers.NgModuleInjector, [o.importType(this._ngModuleMeta.type)]), this._fields, this._getters, ctor, methods); } diff --git a/modules/@angular/compiler/src/ng_module_resolver.ts b/modules/@angular/compiler/src/ng_module_resolver.ts new file mode 100644 index 0000000000..d9f6851186 --- /dev/null +++ b/modules/@angular/compiler/src/ng_module_resolver.ts @@ -0,0 +1,39 @@ +/** + * @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 {Injectable, NgModuleMetadata} from '@angular/core'; + +import {ReflectorReader, reflector} from '../core_private'; +import {BaseException} from '../src/facade/exceptions'; +import {Type, isBlank, isPresent, stringify} from '../src/facade/lang'; + +function _isNgModuleMetadata(obj: any): obj is NgModuleMetadata { + return obj instanceof NgModuleMetadata; +} + +/** + * Resolves types to {@link NgModuleMetadata}. + */ +@Injectable() +export class NgModuleResolver { + constructor(private _reflector: ReflectorReader = reflector) {} + + resolve(type: Type, throwIfNotFound = true): NgModuleMetadata { + const ngModuleMeta: NgModuleMetadata = + this._reflector.annotations(type).find(_isNgModuleMetadata); + + if (isPresent(ngModuleMeta)) { + return ngModuleMeta; + } else { + if (throwIfNotFound) { + throw new BaseException(`No NgModule metadata found for '${stringify(type)}'.`); + } + return null; + } + } +} diff --git a/modules/@angular/compiler/src/offline_compiler.ts b/modules/@angular/compiler/src/offline_compiler.ts index 9add04a270..66d8d7875d 100644 --- a/modules/@angular/compiler/src/offline_compiler.ts +++ b/modules/@angular/compiler/src/offline_compiler.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModuleCompiler} from './app_module_compiler'; -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata'; import {DirectiveNormalizer} from './directive_normalizer'; import {ListWrapper} from './facade/collection'; import {BaseException} from './facade/exceptions'; import {Identifiers} from './identifiers'; import {CompileMetadataResolver} from './metadata_resolver'; +import {NgModuleCompiler} from './ng_module_compiler'; import {OutputEmitter} from './output/abstract_emitter'; import * as o from './output/output_ast'; import {CompiledStylesheet, StyleCompiler} from './style_compiler'; @@ -23,61 +23,29 @@ export class SourceModule { constructor(public moduleUrl: string, public source: string) {} } -export class AppModulesSummary { - private _compAppModule = new Map(); - private _hashKey(type: StaticSymbol) { return `${type.filePath}#${type.name}`; } - - hasComponent(component: StaticSymbol): boolean { - return this._compAppModule.has(this._hashKey(component)); - } - - addComponent(module: StaticSymbol, component: StaticSymbol) { - this._compAppModule.set(this._hashKey(component), module); - } - - getModule(comp: StaticSymbol): StaticSymbol { - return this._compAppModule.get(this._hashKey(comp)); - } +export class NgModulesSummary { + constructor(public ngModuleByComponent: Map) {} } + export class OfflineCompiler { constructor( private _metadataResolver: CompileMetadataResolver, private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, - private _appModuleCompiler: AppModuleCompiler, private _outputEmitter: OutputEmitter) {} + private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter) {} - analyzeModules(appModules: StaticSymbol[]): AppModulesSummary { - let result = new AppModulesSummary(); - appModules.forEach((appModule) => { - let appModuleMeta = this._metadataResolver.getAppModuleMetadata(appModule); - appModuleMeta.precompile.forEach( - (precompileComp) => - this._getTransitiveComponents(appModule, precompileComp.runtime, result)); - }); - return result; - } + analyzeModules(ngModules: StaticSymbol[]): NgModulesSummary { + const ngModuleByComponent = new Map(); - private _getTransitiveComponents( - appModule: StaticSymbol, component: StaticSymbol, - target: AppModulesSummary = new AppModulesSummary()): AppModulesSummary { - var compMeta = this._metadataResolver.getDirectiveMetadata(component); - // TODO(tbosch): preserve all modules per component, not just one. - // Then run the template parser with the union and the intersection of the modules (regarding - // directives/pipes) - // and report an error if some directives/pipes are only matched with the union but not with the - // intersection! - // -> this means that a component is used in the wrong way! - if (!compMeta.isComponent || target.hasComponent(component)) { - return target; - } - target.addComponent(appModule, component); - this._metadataResolver.getViewDirectivesMetadata(component).forEach((dirMeta) => { - this._getTransitiveComponents(appModule, dirMeta.type.runtime); + ngModules.forEach((ngModule) => { + const ngModuleMeta = this._metadataResolver.getNgModuleMetadata(ngModule); + ngModuleMeta.declaredDirectives.forEach((dirMeta) => { + if (dirMeta.isComponent) { + ngModuleByComponent.set(dirMeta.type.runtime, ngModuleMeta); + } + }); }); - compMeta.precompile.forEach((precompileComp) => { - this._getTransitiveComponents(appModule, precompileComp.type.runtime); - }); - return target; + return new NgModulesSummary(ngModuleByComponent); } clearCache() { @@ -86,55 +54,45 @@ export class OfflineCompiler { } compile( - moduleUrl: string, appModulesSummary: AppModulesSummary, components: StaticSymbol[], - appModules: StaticSymbol[]): Promise { + moduleUrl: string, ngModulesSummary: NgModulesSummary, components: StaticSymbol[], + ngModules: StaticSymbol[]): Promise { let fileSuffix = _splitLastSuffix(moduleUrl)[1]; let statements: o.Statement[] = []; let exportedVars: string[] = []; let outputSourceModules: SourceModule[] = []; - // compile app modules + // compile all ng modules exportedVars.push( - ...appModules.map((appModule) => this._compileAppModule(appModule, statements))); + ...ngModules.map((ngModuleType) => this._compileModule(ngModuleType, statements))); // compile components return Promise .all(components.map((compType) => { - let appModule = appModulesSummary.getModule(compType); - let appModuleDirectives: CompileDirectiveMetadata[] = []; - let appModulePipes: CompilePipeMetadata[] = []; - if (appModule) { - let appModuleMeta = this._metadataResolver.getAppModuleMetadata(appModule); - appModuleDirectives.push(...appModuleMeta.directives.map( - type => this._metadataResolver.getDirectiveMetadata(type.runtime))); - appModulePipes.push(...appModuleMeta.pipes.map( - type => this._metadataResolver.getPipeMetadata(type.runtime))); + const compMeta = this._metadataResolver.getDirectiveMetadata(compType); + let ngModule = ngModulesSummary.ngModuleByComponent.get(compType); + if (!ngModule) { + throw new BaseException( + `Cannot determine the module for component ${compMeta.type.name}!`); } return Promise - .all([ - this._metadataResolver.getDirectiveMetadata(compType), ...appModuleDirectives, - ...this._metadataResolver.getViewDirectivesMetadata(compType) - ].map(dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult)) + .all([compMeta, ...ngModule.transitiveModule.directives].map( + dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult)) .then((normalizedCompWithDirectives) => { - let compMeta = normalizedCompWithDirectives[0]; - let dirMetas = normalizedCompWithDirectives.slice(1); + const compMeta = normalizedCompWithDirectives[0]; + const dirMetas = normalizedCompWithDirectives.slice(1); _assertComponent(compMeta); // compile styles - let stylesCompileResults = this._styleCompiler.compileComponent(compMeta); + const stylesCompileResults = this._styleCompiler.compileComponent(compMeta); stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => { outputSourceModules.push(this._codgenStyles(compiledStyleSheet, fileSuffix)); }); // compile components exportedVars.push(this._compileComponentFactory(compMeta, fileSuffix, statements)); - let pipeMetas = [ - ...appModulePipes, - ...this._metadataResolver.getViewPipesMetadata(compMeta.type.runtime) - ]; exportedVars.push(this._compileComponent( - compMeta, dirMetas, pipeMetas, stylesCompileResults.componentStylesheet, - fileSuffix, statements)); + compMeta, dirMetas, ngModule.transitiveModule.pipes, + stylesCompileResults.componentStylesheet, fileSuffix, statements)); }); })) .then(() => { @@ -146,21 +104,21 @@ export class OfflineCompiler { }); } - private _compileAppModule(appModuleType: StaticSymbol, targetStatements: o.Statement[]): string { - let appModuleMeta = this._metadataResolver.getAppModuleMetadata(appModuleType); - let appCompileResult = this._appModuleCompiler.compile(appModuleMeta); + private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string { + const ngModule = this._metadataResolver.getNgModuleMetadata(ngModuleType); + let appCompileResult = this._ngModuleCompiler.compile(ngModule, []); appCompileResult.dependencies.forEach((dep) => { dep.placeholder.name = _componentFactoryName(dep.comp); dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl); }); targetStatements.push(...appCompileResult.statements); - return appCompileResult.appModuleFactoryVar; + return appCompileResult.ngModuleFactoryVar; } private _compileComponentFactory( compMeta: CompileDirectiveMetadata, fileSuffix: string, targetStatements: o.Statement[]): string { - var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); + var hostMeta = createHostComponentMeta(compMeta); var hostViewFactoryVar = this._compileComponent(hostMeta, [compMeta], [], null, fileSuffix, targetStatements); var compFactoryVar = _componentFactoryName(compMeta.type); diff --git a/modules/@angular/compiler/src/pipe_resolver.ts b/modules/@angular/compiler/src/pipe_resolver.ts index bc85132677..d6c955e7db 100644 --- a/modules/@angular/compiler/src/pipe_resolver.ts +++ b/modules/@angular/compiler/src/pipe_resolver.ts @@ -30,7 +30,7 @@ export class PipeResolver { /** * Return {@link PipeMetadata} for a given `Type`. */ - resolve(type: Type): PipeMetadata { + resolve(type: Type, throwIfNotFound = true): PipeMetadata { var metas = this._reflector.annotations(resolveForwardRef(type)); if (isPresent(metas)) { var annotation = metas.find(_isPipeMetadata); @@ -38,6 +38,9 @@ export class PipeResolver { return annotation; } } - throw new BaseException(`No Pipe decorator found on ${stringify(type)}`); + if (throwIfNotFound) { + throw new BaseException(`No Pipe decorator found on ${stringify(type)}`); + } + return null; } -} \ No newline at end of file +} diff --git a/modules/@angular/compiler/src/provider_parser.ts b/modules/@angular/compiler/src/provider_analyzer.ts similarity index 87% rename from modules/@angular/compiler/src/provider_parser.ts rename to modules/@angular/compiler/src/provider_analyzer.ts index 4f45d10071..d54b4e9c13 100644 --- a/modules/@angular/compiler/src/provider_parser.ts +++ b/modules/@angular/compiler/src/provider_analyzer.ts @@ -10,7 +10,7 @@ import {ListWrapper} from '../src/facade/collection'; import {BaseException} from '../src/facade/exceptions'; import {isArray, isBlank, isPresent, normalizeBlank} from '../src/facade/lang'; -import {CompileAppModuleMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMap, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata'; +import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMap, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata'; import {Identifiers, identifierToken} from './identifiers'; import {ParseError, ParseSourceSpan} from './parse_util'; import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst, VariableAst} from './template_ast'; @@ -23,16 +23,16 @@ export class ProviderViewContext { /** * @internal */ - viewQueries: CompileTokenMap; + viewQueries: CompileIdentifierMap; /** * @internal */ - viewProviders: CompileTokenMap; + viewProviders: CompileIdentifierMap; errors: ProviderError[] = []; constructor(public component: CompileDirectiveMetadata, public sourceSpan: ParseSourceSpan) { this.viewQueries = _getViewQueries(component); - this.viewProviders = new CompileTokenMap(); + this.viewProviders = new CompileIdentifierMap(); _normalizeProviders(component.viewProviders, sourceSpan, this.errors).forEach((provider) => { if (isBlank(this.viewProviders.get(provider.token))) { this.viewProviders.add(provider.token, true); @@ -42,11 +42,11 @@ export class ProviderViewContext { } export class ProviderElementContext { - private _contentQueries: CompileTokenMap; + private _contentQueries: CompileIdentifierMap; - private _transformedProviders = new CompileTokenMap(); - private _seenProviders = new CompileTokenMap(); - private _allProviders: CompileTokenMap; + private _transformedProviders = new CompileIdentifierMap(); + private _seenProviders = new CompileIdentifierMap(); + private _allProviders: CompileIdentifierMap; private _attrs: {[key: string]: string}; private _hasViewContainer: boolean = false; @@ -60,7 +60,7 @@ export class ProviderElementContext { this._allProviders = _resolveProvidersFromDirectives(directivesMeta, _sourceSpan, _viewContext.errors); this._contentQueries = _getContentQueries(directivesMeta); - var queriedTokens = new CompileTokenMap(); + var queriedTokens = new CompileIdentifierMap(); this._allProviders.values().forEach( (provider) => { this._addQueryReadsTo(provider.token, queriedTokens); }); refs.forEach((refAst) => { @@ -100,7 +100,9 @@ export class ProviderElementContext { get transformedHasViewContainer(): boolean { return this._hasViewContainer; } - private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: CompileTokenMap) { + private _addQueryReadsTo( + token: CompileTokenMetadata, + queryReadTokens: CompileIdentifierMap) { this._getQueriesFor(token).forEach((query) => { const queryReadToken = isPresent(query.read) ? query.read : token; if (isBlank(queryReadTokens.get(queryReadToken))) { @@ -272,24 +274,28 @@ export class ProviderElementContext { } -export class AppModuleProviderParser { - private _transformedProviders = new CompileTokenMap(); - private _seenProviders = new CompileTokenMap(); +export class NgModuleProviderAnalyzer { + private _transformedProviders = new CompileIdentifierMap(); + private _seenProviders = new CompileIdentifierMap(); private _unparsedProviders: any[] = []; - private _allProviders: CompileTokenMap; + private _allProviders: CompileIdentifierMap; private _errors: ProviderError[] = []; - constructor(appModule: CompileAppModuleMetadata, sourceSpan: ParseSourceSpan) { - this._allProviders = new CompileTokenMap(); - [appModule.type].concat(appModule.modules).forEach((appModuleType: CompileTypeMetadata) => { - const appModuleProvider = new CompileProviderMetadata( - {token: new CompileTokenMetadata({identifier: appModuleType}), useClass: appModuleType}); + constructor( + ngModule: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[], + sourceSpan: ParseSourceSpan) { + this._allProviders = new CompileIdentifierMap(); + const ngModuleTypes = ngModule.transitiveModule.modules.map((moduleMeta) => moduleMeta.type); + ngModuleTypes.forEach((ngModuleType: CompileTypeMetadata) => { + const ngModuleProvider = new CompileProviderMetadata( + {token: new CompileTokenMetadata({identifier: ngModuleType}), useClass: ngModuleType}); _resolveProviders( - [appModuleProvider], ProviderAstType.PublicService, true, sourceSpan, this._errors, + [ngModuleProvider], ProviderAstType.PublicService, true, sourceSpan, this._errors, this._allProviders); }); _resolveProviders( - _normalizeProviders(appModule.providers, sourceSpan, this._errors), + _normalizeProviders( + ngModule.transitiveModule.providers.concat(extraProviders), sourceSpan, this._errors), ProviderAstType.PublicService, false, sourceSpan, this._errors, this._allProviders); } @@ -436,8 +442,8 @@ function _normalizeProviders( function _resolveProvidersFromDirectives( directives: CompileDirectiveMetadata[], sourceSpan: ParseSourceSpan, - targetErrors: ParseError[]): CompileTokenMap { - var providersByToken = new CompileTokenMap(); + targetErrors: ParseError[]): CompileIdentifierMap { + var providersByToken = new CompileIdentifierMap(); directives.forEach((directive) => { var dirProvider = new CompileProviderMetadata( {token: new CompileTokenMetadata({identifier: directive.type}), useClass: directive.type}); @@ -464,7 +470,7 @@ function _resolveProvidersFromDirectives( function _resolveProviders( providers: CompileProviderMetadata[], providerType: ProviderAstType, eager: boolean, sourceSpan: ParseSourceSpan, targetErrors: ParseError[], - targetProvidersByToken: CompileTokenMap) { + targetProvidersByToken: CompileIdentifierMap) { providers.forEach((provider) => { var resolvedProvider = targetProvidersByToken.get(provider.token); if (isPresent(resolvedProvider) && resolvedProvider.multiProvider !== provider.multi) { @@ -487,8 +493,8 @@ function _resolveProviders( function _getViewQueries(component: CompileDirectiveMetadata): - CompileTokenMap { - var viewQueries = new CompileTokenMap(); + CompileIdentifierMap { + var viewQueries = new CompileIdentifierMap(); if (isPresent(component.viewQueries)) { component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query)); } @@ -501,8 +507,8 @@ function _getViewQueries(component: CompileDirectiveMetadata): } function _getContentQueries(directives: CompileDirectiveMetadata[]): - CompileTokenMap { - var contentQueries = new CompileTokenMap(); + CompileIdentifierMap { + var contentQueries = new CompileIdentifierMap(); directives.forEach(directive => { if (isPresent(directive.queries)) { directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query)); @@ -517,7 +523,8 @@ function _getContentQueries(directives: CompileDirectiveMetadata[]): } function _addQueryToTokenMap( - map: CompileTokenMap, query: CompileQueryMetadata) { + map: CompileIdentifierMap, + query: CompileQueryMetadata) { query.selectors.forEach((token: CompileTokenMetadata) => { var entry = map.get(token); if (isBlank(entry)) { diff --git a/modules/@angular/compiler/src/runtime_compiler.ts b/modules/@angular/compiler/src/runtime_compiler.ts index 26a59646f7..273b99fe38 100644 --- a/modules/@angular/compiler/src/runtime_compiler.ts +++ b/modules/@angular/compiler/src/runtime_compiler.ts @@ -6,19 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModuleFactory, AppModuleMetadata, Compiler, ComponentFactory, ComponentResolver, ComponentStillLoadingError, Injectable, Injector, OptionalMetadata, Provider, SkipSelfMetadata} from '@angular/core'; -import {Console} from '../core_private'; +import {Compiler, ComponentFactory, ComponentResolver, ComponentStillLoadingError, Injectable, Injector, NgModule, NgModuleFactory, NgModuleMetadata, OptionalMetadata, Provider, SkipSelfMetadata} from '@angular/core'; +import {Console} from '../core_private'; import {BaseException} from '../src/facade/exceptions'; import {ConcreteType, IS_DART, Type, isBlank, isString, stringify} from '../src/facade/lang'; import {ListWrapper,} from '../src/facade/collection'; import {PromiseWrapper} from '../src/facade/async'; -import {createHostComponentMeta, CompileDirectiveMetadata, CompilePipeMetadata, CompileIdentifierMetadata} from './compile_metadata'; +import {createHostComponentMeta, CompileDirectiveMetadata, CompilePipeMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata} from './compile_metadata'; import {TemplateAst,} from './template_ast'; import {StyleCompiler, StylesCompileDependency, CompiledStylesheet} from './style_compiler'; import {ViewCompiler, ViewCompileResult, ViewFactoryDependency, ComponentFactoryDependency} from './view_compiler/view_compiler'; -import {AppModuleCompiler} from './app_module_compiler'; +import {NgModuleCompiler} from './ng_module_compiler'; import {TemplateParser} from './template_parser'; import {DirectiveNormalizer} from './directive_normalizer'; import {CompileMetadataResolver} from './metadata_resolver'; @@ -38,122 +38,123 @@ import {SyncAsyncResult} from './util'; * application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security). */ @Injectable() -export class RuntimeCompiler implements ComponentResolver, Compiler { +export class RuntimeCompiler implements Compiler { private _compiledTemplateCache = new Map(); private _compiledHostTemplateCache = new Map(); - private _compiledAppModuleCache = new Map>(); - - private _warnOnComponentResolver = true; + private _compiledNgModuleCache = new Map>(); constructor( private _injector: Injector, private _metadataResolver: CompileMetadataResolver, private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, - private _appModuleCompiler: AppModuleCompiler, private _genConfig: CompilerConfig, - private _console: Console) { - const flatDeprecatedPlatformDirectives = - ListWrapper.flatten(_genConfig.deprecatedPlatformDirectives); - if (flatDeprecatedPlatformDirectives.length > 0) { - this._console.warn( - `Providing platform directives via the PLATFORM_DIRECTIVES provider or the "CompilerConfig" is deprecated. Provide platform directives via an @AppModule instead. Directives: ` + - flatDeprecatedPlatformDirectives.map(stringify)); - } - const flatDeprecatedPlatformPipes = ListWrapper.flatten(_genConfig.deprecatedPlatformPipes); - if (flatDeprecatedPlatformPipes.length > 0) { - this._console.warn( - `Providing platform pipes via the PLATFORM_PIPES provider or the "CompilerConfig" is deprecated. Provide platform pipes via an @AppModule instead. Pipes: ` + - flatDeprecatedPlatformPipes.map(stringify)); - } - } + private _ngModuleCompiler: NgModuleCompiler, private _compilerConfig: CompilerConfig, + private _console: Console) {} get injector(): Injector { return this._injector; } - resolveComponent(component: Type|string): Promise> { - if (isString(component)) { - return PromiseWrapper.reject( - new BaseException(`Cannot resolve component using '${component}'.`), null); + compileModuleSync(moduleType: ConcreteType): NgModuleFactory { + return this._compileModuleAndComponents(moduleType, true).syncResult; + } + + compileModuleAsync(moduleType: ConcreteType): Promise> { + return this._compileModuleAndComponents(moduleType, false).asyncResult; + } + + compileComponentAsync(compType: ConcreteType, ngModule: ConcreteType = null): + Promise> { + if (!ngModule) { + throw new BaseException( + `Calling compileComponentAsync on the root compiler without a module is not allowed! (Compiling component ${stringify(compType)})`); } - if (this._warnOnComponentResolver) { - this._console.warn(ComponentResolver.DynamicCompilationDeprecationMsg); - this._warnOnComponentResolver = false; + return this._compileComponentInModule(compType, false, ngModule).asyncResult; + } + + compileComponentSync(compType: ConcreteType, ngModule: ConcreteType = null): + ComponentFactory { + if (!ngModule) { + throw new BaseException( + `Calling compileComponentSync on the root compiler without a module is not allowed! (Compiling component ${stringify(compType)})`); } - return this.compileComponentAsync(>component); + return this._compileComponentInModule(compType, true, ngModule).syncResult; } - compileAppModuleSync(moduleType: ConcreteType, metadata: AppModuleMetadata = null): - AppModuleFactory { - return this._compileAppModule(moduleType, true, metadata).syncResult; + private _compileModuleAndComponents(moduleType: ConcreteType, isSync: boolean): + SyncAsyncResult> { + const componentPromise = this._compileComponents(moduleType, isSync); + const ngModuleFactory = this._compileModule(moduleType); + return new SyncAsyncResult(ngModuleFactory, componentPromise.then(() => ngModuleFactory)); } - compileAppModuleAsync(moduleType: ConcreteType, metadata: AppModuleMetadata = null): - Promise> { - return this._compileAppModule(moduleType, false, metadata).asyncResult; - } - - private _compileAppModule( - moduleType: ConcreteType, isSync: boolean, - metadata: AppModuleMetadata = null): SyncAsyncResult> { - // Only cache if we read the metadata via the reflector, - // as we use the moduleType as cache key. - let useCache = !metadata; - let appModuleFactory = this._compiledAppModuleCache.get(moduleType); - let componentCompilePromises: Promise[] = []; - if (!appModuleFactory || !useCache) { - var compileModuleMeta = this._metadataResolver.getAppModuleMetadata(moduleType, metadata); - let boundCompilerFactory = (parentResolver: ComponentResolver) => new BoundCompiler( - this, compileModuleMeta.directives.map(dir => dir.type.runtime), - compileModuleMeta.pipes.map((pipe) => pipe.type.runtime), parentResolver); + private _compileModule(moduleType: ConcreteType): NgModuleFactory { + let ngModuleFactory = this._compiledNgModuleCache.get(moduleType); + if (!ngModuleFactory) { + const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType); + const transitiveModuleMeta = moduleMeta.transitiveModule; + let boundCompilerFactory = (parentResolver: ComponentResolver) => + new ModuleBoundCompiler(this, moduleMeta.type.runtime, parentResolver, this._console); // Always provide a bound Compiler and ComponentResolver - compileModuleMeta.providers.push( - this._metadataResolver.getProviderMetadata(new Provider(Compiler, { - useFactory: boundCompilerFactory, - deps: [[new OptionalMetadata(), new SkipSelfMetadata(), ComponentResolver]] - }))); - compileModuleMeta.providers.push(this._metadataResolver.getProviderMetadata( - new Provider(ComponentResolver, {useExisting: Compiler}))); - var compileResult = this._appModuleCompiler.compile(compileModuleMeta); + const extraProviders = [ + this._metadataResolver.getProviderMetadata(new Provider(Compiler, { + useFactory: boundCompilerFactory, + deps: [[new OptionalMetadata(), new SkipSelfMetadata(), ComponentResolver]] + })), + this._metadataResolver.getProviderMetadata( + new Provider(ComponentResolver, {useExisting: Compiler})) + ]; + var compileResult = this._ngModuleCompiler.compile(moduleMeta, extraProviders); compileResult.dependencies.forEach((dep) => { - let compileResult = this._compileComponent( - dep.comp.runtime, isSync, - compileModuleMeta.directives.map(compileType => compileType.runtime), - compileModuleMeta.pipes.map(compileType => compileType.runtime)); - dep.placeholder.runtime = compileResult.syncResult; - componentCompilePromises.push(compileResult.asyncResult); + dep.placeholder.runtime = + this._assertComponentKnown(dep.comp.runtime, true).proxyComponentFactory; dep.placeholder.name = `compFactory_${dep.comp.name}`; }); - if (IS_DART || !this._genConfig.useJit) { - appModuleFactory = - interpretStatements(compileResult.statements, compileResult.appModuleFactoryVar); + if (IS_DART || !this._compilerConfig.useJit) { + ngModuleFactory = + interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar); } else { - appModuleFactory = jitStatements( - `${compileModuleMeta.type.name}.ngfactory.js`, compileResult.statements, - compileResult.appModuleFactoryVar); - } - if (useCache) { - this._compiledAppModuleCache.set(moduleType, appModuleFactory); + ngModuleFactory = jitStatements( + `${moduleMeta.type.name}.ngfactory.js`, compileResult.statements, + compileResult.ngModuleFactoryVar); } + this._compiledNgModuleCache.set(moduleMeta.type.runtime, ngModuleFactory); } - return new SyncAsyncResult( - appModuleFactory, Promise.all(componentCompilePromises).then(() => appModuleFactory)); + return ngModuleFactory; } - compileComponentAsync(compType: ConcreteType): Promise> { - return this._compileComponent(compType, false, [], []).asyncResult; - } + private _compileComponentInModule( + compType: ConcreteType, isSync: boolean, + moduleType: ConcreteType): SyncAsyncResult> { + this._metadataResolver.addComponentToModule(moduleType, compType); - compileComponentSync(compType: ConcreteType): ComponentFactory { - return this._compileComponent(compType, true, [], []).syncResult; + const componentPromise = this._compileComponents(moduleType, isSync); + const componentFactory: ComponentFactory = + this._assertComponentKnown(compType, true).proxyComponentFactory; + + return new SyncAsyncResult(componentFactory, componentPromise.then(() => componentFactory)); } /** * @internal */ - _compileComponent( - compType: ConcreteType, isSync: boolean, moduleDirectives: ConcreteType[], - modulePipes: ConcreteType[]): SyncAsyncResult> { - var templates = - this._getTransitiveCompiledTemplates(compType, true, moduleDirectives, modulePipes); + _compileComponents(mainModule: Type, isSync: boolean): Promise { + const templates = new Set(); var loadingPromises: Promise[] = []; + + const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule); + ngModule.transitiveModule.modules.forEach((localModuleMeta) => { + localModuleMeta.declaredDirectives.forEach((dirMeta) => { + if (dirMeta.isComponent) { + templates.add(this._createCompiledTemplate( + dirMeta, localModuleMeta.transitiveModule.directives, + localModuleMeta.transitiveModule.pipes)); + dirMeta.precompile.forEach((precompileType) => { + templates.add(this._createCompiledHostTemplate(precompileType.runtime)); + }); + } + }); + localModuleMeta.precompile.forEach((precompileType) => { + templates.add(this._createCompiledHostTemplate(precompileType.runtime)); + }); + }); templates.forEach((template) => { if (template.loading) { if (isSync) { @@ -167,16 +168,14 @@ export class RuntimeCompiler implements ComponentResolver, Compiler { () => { templates.forEach((template) => { this._compileTemplate(template); }); }; if (isSync) { compile(); + return Promise.resolve(null); + } else { + return Promise.all(loadingPromises).then(compile); } - let result = this._compiledHostTemplateCache.get(compType).proxyComponentFactory; - return new SyncAsyncResult(result, Promise.all(loadingPromises).then(() => { - compile(); - return result; - })); } clearCacheFor(type: Type) { - this._compiledAppModuleCache.delete(type); + this._compiledNgModuleCache.delete(type); this._metadataResolver.clearCacheFor(type); this._compiledHostTemplateCache.delete(type); var compiledTemplate = this._compiledTemplateCache.get(type); @@ -191,71 +190,54 @@ export class RuntimeCompiler implements ComponentResolver, Compiler { this._compiledTemplateCache.clear(); this._compiledHostTemplateCache.clear(); this._templateNormalizer.clearCache(); - this._compiledAppModuleCache.clear(); + this._compiledNgModuleCache.clear(); } - private _createCompiledHostTemplate(type: Type): CompiledTemplate { - var compiledTemplate = this._compiledHostTemplateCache.get(type); + private _createCompiledHostTemplate(compType: Type): CompiledTemplate { + var compiledTemplate = this._compiledHostTemplateCache.get(compType); if (isBlank(compiledTemplate)) { - var compMeta = this._metadataResolver.getDirectiveMetadata(type); + var compMeta = this._metadataResolver.getDirectiveMetadata(compType); assertComponent(compMeta); - var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); + var hostMeta = createHostComponentMeta(compMeta); compiledTemplate = new CompiledTemplate( - true, compMeta.selector, compMeta.type, [], [type], [], [], + true, compMeta.selector, compMeta.type, [compMeta], [], this._templateNormalizer.normalizeDirective(hostMeta)); - this._compiledHostTemplateCache.set(type, compiledTemplate); + this._compiledHostTemplateCache.set(compType, compiledTemplate); } return compiledTemplate; } private _createCompiledTemplate( - type: Type, moduleDirectives: ConcreteType[], - modulePipes: ConcreteType[]): CompiledTemplate { - var compiledTemplate = this._compiledTemplateCache.get(type); + compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[], + pipes: CompilePipeMetadata[]): CompiledTemplate { + var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.runtime); if (isBlank(compiledTemplate)) { - const compMeta = this._metadataResolver.getDirectiveMetadata(type); assertComponent(compMeta); - const viewDirectives: CompileDirectiveMetadata[] = []; - moduleDirectives.forEach( - (type) => viewDirectives.push(this._metadataResolver.getDirectiveMetadata(type))); - const viewComponentTypes: Type[] = []; - this._metadataResolver.getViewDirectivesMetadata(type).forEach(dirOrComp => { - if (dirOrComp.isComponent) { - viewComponentTypes.push(dirOrComp.type.runtime); - } else { - viewDirectives.push(dirOrComp); - } - }); - const precompileComponentTypes = compMeta.precompile.map((typeMeta) => typeMeta.runtime); - const pipes = [ - ...modulePipes.map((type) => this._metadataResolver.getPipeMetadata(type)), - ...this._metadataResolver.getViewPipesMetadata(type) - ]; compiledTemplate = new CompiledTemplate( - false, compMeta.selector, compMeta.type, viewDirectives, viewComponentTypes, - precompileComponentTypes, pipes, this._templateNormalizer.normalizeDirective(compMeta)); - this._compiledTemplateCache.set(type, compiledTemplate); + false, compMeta.selector, compMeta.type, directives, pipes, + this._templateNormalizer.normalizeDirective(compMeta)); + this._compiledTemplateCache.set(compMeta.type.runtime, compiledTemplate); } return compiledTemplate; } - private _getTransitiveCompiledTemplates( - compType: Type, isHost: boolean, moduleDirectives: ConcreteType[], - modulePipes: ConcreteType[], - target: Set = new Set()): Set { - const template = isHost ? this._createCompiledHostTemplate(compType) : - this._createCompiledTemplate(compType, moduleDirectives, modulePipes); - if (!target.has(template)) { - target.add(template); - template.viewComponentTypes.forEach((compType) => { - this._getTransitiveCompiledTemplates( - compType, false, moduleDirectives, modulePipes, target); - }); - template.precompileHostComponentTypes.forEach((compType) => { - this._getTransitiveCompiledTemplates(compType, true, moduleDirectives, modulePipes, target); - }); + private _assertComponentKnown(compType: any, isHost: boolean): CompiledTemplate { + const compiledTemplate = isHost ? this._compiledHostTemplateCache.get(compType) : + this._compiledTemplateCache.get(compType); + if (!compiledTemplate) { + throw new BaseException( + `Illegal state: CompiledTemplate for ${stringify(compType)} (isHost: ${isHost}) does not exist!`); } - return target; + return compiledTemplate; + } + + private _assertComponentLoaded(compType: any, isHost: boolean): CompiledTemplate { + const compiledTemplate = this._assertComponentKnown(compType, isHost); + if (compiledTemplate.loading) { + throw new BaseException( + `Illegal state: CompiledTemplate for ${stringify(compType)} (isHost: ${isHost}) is still loading!`); + } + return compiledTemplate; } private _compileTemplate(template: CompiledTemplate) { @@ -270,7 +252,7 @@ export class RuntimeCompiler implements ComponentResolver, Compiler { this._resolveStylesCompileResult( stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl); const viewCompMetas = template.viewComponentTypes.map( - (compType) => this._compiledTemplateCache.get(compType).normalizedCompMeta); + (compType) => this._assertComponentLoaded(compType, false).normalizedCompMeta); const parsedTemplate = this._templateParser.parse( compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas), template.viewPipes, compMeta.type.name); @@ -281,12 +263,12 @@ export class RuntimeCompiler implements ComponentResolver, Compiler { let depTemplate: CompiledTemplate; if (dep instanceof ViewFactoryDependency) { let vfd = dep; - depTemplate = this._compiledTemplateCache.get(vfd.comp.runtime); + depTemplate = this._assertComponentLoaded(vfd.comp.runtime, false); vfd.placeholder.runtime = depTemplate.proxyViewFactory; vfd.placeholder.name = `viewFactory_${vfd.comp.name}`; } else if (dep instanceof ComponentFactoryDependency) { let cfd = dep; - depTemplate = this._compiledHostTemplateCache.get(cfd.comp.runtime); + depTemplate = this._assertComponentLoaded(cfd.comp.runtime, true); cfd.placeholder.runtime = depTemplate.proxyComponentFactory; cfd.placeholder.name = `compFactory_${cfd.comp.name}`; } @@ -294,7 +276,7 @@ export class RuntimeCompiler implements ComponentResolver, Compiler { const statements = stylesCompileResult.componentStylesheet.statements.concat(compileResult.statements); let factory: any; - if (IS_DART || !this._genConfig.useJit) { + if (IS_DART || !this._compilerConfig.useJit) { factory = interpretStatements(statements, compileResult.viewFactoryVar); } else { factory = jitStatements( @@ -318,7 +300,7 @@ export class RuntimeCompiler implements ComponentResolver, Compiler { result: CompiledStylesheet, externalStylesheetsByModuleUrl: Map): string[] { this._resolveStylesCompileResult(result, externalStylesheetsByModuleUrl); - if (IS_DART || !this._genConfig.useJit) { + if (IS_DART || !this._compilerConfig.useJit) { return interpretStatements(result.statements, result.stylesVar); } else { return jitStatements(`${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar); @@ -334,13 +316,28 @@ class CompiledTemplate { private _normalizedCompMeta: CompileDirectiveMetadata = null; isCompiled = false; isCompiledWithDeps = false; + viewComponentTypes: Type[] = []; + viewDirectives: CompileDirectiveMetadata[] = []; constructor( public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata, - public viewDirectives: CompileDirectiveMetadata[], public viewComponentTypes: Type[], - public precompileHostComponentTypes: Type[], public viewPipes: CompilePipeMetadata[], + viewDirectivesAndComponents: CompileDirectiveMetadata[], + public viewPipes: CompilePipeMetadata[], _normalizeResult: SyncAsyncResult) { - this.proxyViewFactory = (...args: any[]) => this._viewFactory.apply(null, args); + viewDirectivesAndComponents.forEach((dirMeta) => { + if (dirMeta.isComponent) { + this.viewComponentTypes.push(dirMeta.type.runtime); + } else { + this.viewDirectives.push(dirMeta); + } + }); + this.proxyViewFactory = (...args: any[]) => { + if (!this._viewFactory) { + throw new BaseException( + `Illegal state: CompiledTemplate for ${stringify(this.compType)} is not compiled yet!`); + } + return this._viewFactory.apply(null, args); + }; this.proxyComponentFactory = isHost ? new ComponentFactory(selector, this.proxyViewFactory, compType.runtime) : null; @@ -376,13 +373,15 @@ function assertComponent(meta: CompileDirectiveMetadata) { } /** - * A wrapper around `Compiler` and `ComponentResolver` that - * provides default patform directives / pipes. + * Implements `Compiler` and `ComponentResolver` by delegating + * to the RuntimeCompiler using a known module. */ -class BoundCompiler implements Compiler, ComponentResolver { +class ModuleBoundCompiler implements Compiler, ComponentResolver { + private _warnOnComponentResolver = true; + constructor( - private _delegate: RuntimeCompiler, private _directives: any[], private _pipes: any[], - private _parentComponentResolver: ComponentResolver) {} + private _delegate: RuntimeCompiler, private _ngModule: ConcreteType, + private _parentComponentResolver: ComponentResolver, private _console: Console) {} get injector(): Injector { return this._delegate.injector; } @@ -395,27 +394,29 @@ class BoundCompiler implements Compiler, ComponentResolver { new BaseException(`Cannot resolve component using '${component}'.`), null); } } + if (this._warnOnComponentResolver) { + this._console.warn(ComponentResolver.DynamicCompilationDeprecationMsg); + this._warnOnComponentResolver = false; + } return this.compileComponentAsync(>component); } - compileComponentAsync(compType: ConcreteType): Promise> { - return this._delegate._compileComponent(compType, false, this._directives, this._pipes) - .asyncResult; + compileComponentAsync(compType: ConcreteType, ngModule: ConcreteType = null): + Promise> { + return this._delegate.compileComponentAsync(compType, ngModule ? ngModule : this._ngModule); } - compileComponentSync(compType: ConcreteType): ComponentFactory { - return this._delegate._compileComponent(compType, true, this._directives, this._pipes) - .syncResult; + compileComponentSync(compType: ConcreteType, ngModule: ConcreteType = null): + ComponentFactory { + return this._delegate.compileComponentSync(compType, ngModule ? ngModule : this._ngModule); } - compileAppModuleSync(moduleType: ConcreteType, metadata: AppModuleMetadata = null): - AppModuleFactory { - return this._delegate.compileAppModuleSync(moduleType, metadata); + compileModuleSync(moduleType: ConcreteType): NgModuleFactory { + return this._delegate.compileModuleSync(moduleType); } - compileAppModuleAsync(moduleType: ConcreteType, metadata: AppModuleMetadata = null): - Promise> { - return this._delegate.compileAppModuleAsync(moduleType, metadata); + compileModuleAsync(moduleType: ConcreteType): Promise> { + return this._delegate.compileModuleAsync(moduleType); } /** @@ -429,7 +430,7 @@ class BoundCompiler implements Compiler, ComponentResolver { } /** - * Clears the cache for the given component/appModule. + * Clears the cache for the given component/ngModule. */ clearCacheFor(type: Type) { this._delegate.clearCacheFor(type); } -} \ No newline at end of file +} diff --git a/modules/@angular/compiler/src/template_parser.ts b/modules/@angular/compiler/src/template_parser.ts index 7ab90da12c..c22e91ee98 100644 --- a/modules/@angular/compiler/src/template_parser.ts +++ b/modules/@angular/compiler/src/template_parser.ts @@ -13,7 +13,7 @@ import {RegExpWrapper, isPresent, StringWrapper, isBlank} from '../src/facade/la import {BaseException} from '../src/facade/exceptions'; import {AST, Interpolation, ASTWithSource, TemplateBinding, RecursiveAstVisitor, BindingPipe, ParserError} from './expression_parser/ast'; import {Parser} from './expression_parser/parser'; -import {CompileDirectiveMetadata, CompilePipeMetadata, CompileMetadataWithType, CompileTokenMetadata,} from './compile_metadata'; +import {CompileDirectiveMetadata, CompilePipeMetadata, CompileMetadataWithIdentifier, CompileTokenMetadata, CompileIdentifierMap, removeIdentifierDuplicates} from './compile_metadata'; import {HtmlParser, HtmlParseTreeResult} from './html_parser'; import {splitNsName, mergeNsAndName} from './html_tags'; import {ParseSourceSpan, ParseError, ParseErrorLevel} from './parse_util'; @@ -27,7 +27,7 @@ import {HtmlAstVisitor, HtmlElementAst, HtmlAttrAst, HtmlTextAst, HtmlCommentAst import {splitAtColon} from './util'; import {identifierToken, Identifiers} from './identifiers'; import {expandNodes} from './expander'; -import {ProviderElementContext, ProviderViewContext} from './provider_parser'; +import {ProviderElementContext, ProviderViewContext} from './provider_analyzer'; // Group 1 = "bind-" // Group 2 = "var-" @@ -118,8 +118,8 @@ export class TemplateParser { } if (htmlAstWithErrors.rootNodes.length > 0) { - const uniqDirectives = removeDuplicates(directives); - const uniqPipes = removeDuplicates(pipes); + const uniqDirectives = removeIdentifierDuplicates(directives); + const uniqPipes = removeIdentifierDuplicates(pipes); const providerViewContext = new ProviderViewContext(component, htmlAstWithErrors.rootNodes[0].sourceSpan); const parseVisitor = new TemplateParseVisitor( @@ -181,7 +181,6 @@ class TemplateParseVisitor implements HtmlAstVisitor { const tempMeta = providerViewContext.component.template; - // TODO if (isPresent(tempMeta) && isPresent(tempMeta.interpolation)) { this._interpolationConfig = { start: tempMeta.interpolation[0], @@ -1015,18 +1014,3 @@ export class PipeCollector extends RecursiveAstVisitor { return null; } } - -function removeDuplicates(items: CompileMetadataWithType[]): CompileMetadataWithType[] { - let res: CompileMetadataWithType[] = []; - items.forEach(item => { - let hasMatch = - res.filter( - r => r.type.name == item.type.name && r.type.moduleUrl == item.type.moduleUrl && - r.type.runtime == item.type.runtime) - .length > 0; - if (!hasMatch) { - res.push(item); - } - }); - return res; -} diff --git a/modules/@angular/compiler/src/view_compiler/compile_element.ts b/modules/@angular/compiler/src/view_compiler/compile_element.ts index c1fbae1972..202c7ce4c3 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_element.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_element.ts @@ -18,7 +18,7 @@ import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../templa import {CompileView} from './compile_view'; import {InjectMethodVars} from './constants'; -import {CompileTokenMap, CompileDirectiveMetadata, CompileTokenMetadata, CompileQueryMetadata, CompileProviderMetadata, CompileDiDependencyMetadata, CompileIdentifierMetadata,} from '../compile_metadata'; +import {CompileIdentifierMap, CompileDirectiveMetadata, CompileTokenMetadata, CompileQueryMetadata, CompileProviderMetadata, CompileDiDependencyMetadata, CompileIdentifierMetadata,} from '../compile_metadata'; import {getPropertyInView, injectFromViewParentInjector} from './util'; import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query'; import {CompileMethod} from './compile_method'; @@ -43,11 +43,11 @@ export class CompileElement extends CompileNode { public appElement: o.ReadPropExpr; public elementRef: o.Expression; public injector: o.Expression; - private _instances = new CompileTokenMap(); - private _resolvedProviders: CompileTokenMap; + private _instances = new CompileIdentifierMap(); + private _resolvedProviders: CompileIdentifierMap; private _queryCount = 0; - private _queries = new CompileTokenMap(); + private _queries = new CompileIdentifierMap(); private _componentConstructorViewQueryLists: o.Expression[] = []; public contentNodesByNgContentIndex: Array[] = null; @@ -144,7 +144,7 @@ export class CompileElement extends CompileNode { identifierToken(Identifiers.ViewContainerRef), this.appElement.prop('vcRef')); } - this._resolvedProviders = new CompileTokenMap(); + this._resolvedProviders = new CompileIdentifierMap(); this._resolvedProvidersArray.forEach( provider => this._resolvedProviders.add(provider.token, provider)); diff --git a/modules/@angular/compiler/src/view_compiler/compile_query.ts b/modules/@angular/compiler/src/view_compiler/compile_query.ts index 29343485d3..f847ff0f4b 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_query.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_query.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileQueryMetadata, CompileTokenMap} from '../compile_metadata'; +import {CompileIdentifierMap, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata'; import {ListWrapper} from '../facade/collection'; import {isBlank, isPresent} from '../facade/lang'; import {Identifiers} from '../identifiers'; @@ -125,7 +125,8 @@ export function createQueryList( return expr; } -export function addQueryToTokenMap(map: CompileTokenMap, query: CompileQuery) { +export function addQueryToTokenMap( + map: CompileIdentifierMap, query: CompileQuery) { query.meta.selectors.forEach((selector) => { var entry = map.get(selector); if (isBlank(entry)) { diff --git a/modules/@angular/compiler/src/view_compiler/compile_view.ts b/modules/@angular/compiler/src/view_compiler/compile_view.ts index 1220360fa3..1506be51a1 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_view.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_view.ts @@ -8,7 +8,7 @@ import {ViewType} from '../../core_private'; import {CompiledAnimation} from '../animation/animation_compiler'; -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, CompileTokenMap} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompileIdentifierMap, CompileIdentifierMetadata, CompilePipeMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompilerConfig} from '../config'; import {ListWrapper} from '../facade/collection'; import {isBlank, isPresent} from '../facade/lang'; @@ -27,7 +27,7 @@ import {createPureProxy, getPropertyInView, getViewFactoryName, injectFromViewPa export class CompileView implements NameResolver { public viewType: ViewType; - public viewQueries: CompileTokenMap; + public viewQueries: CompileIdentifierMap; public nodes: CompileNode[] = []; // root nodes or AppElements for ViewContainers @@ -98,7 +98,7 @@ export class CompileView implements NameResolver { this.componentContext = getPropertyInView(o.THIS_EXPR.prop('context'), this, this.componentView); - var viewQueries = new CompileTokenMap(); + var viewQueries = new CompileIdentifierMap(); if (this.viewType === ViewType.COMPONENT) { var directiveInstance = o.THIS_EXPR.prop('context'); ListWrapper.forEachWithIndex(this.component.viewQueries, (queryMeta, queryIndex) => { diff --git a/modules/@angular/compiler/src/view_resolver.ts b/modules/@angular/compiler/src/view_resolver.ts index ddf566064d..99f1233b34 100644 --- a/modules/@angular/compiler/src/view_resolver.ts +++ b/modules/@angular/compiler/src/view_resolver.ts @@ -22,7 +22,7 @@ function _isComponentMetadata(obj: any): obj is ComponentMetadata { export class ViewResolver { constructor(private _reflector: ReflectorReader = reflector) {} - resolve(component: Type): ViewMetadata { + resolve(component: Type, throwIfNotFound = true): ViewMetadata { const compMeta: ComponentMetadata = this._reflector.annotations(component).find(_isComponentMetadata); @@ -45,8 +45,11 @@ export class ViewResolver { }); } } else { - throw new BaseException( - `Could not compile '${stringify(component)}' because it is not a component.`); + if (throwIfNotFound) { + throw new BaseException( + `Could not compile '${stringify(component)}' because it is not a component.`); + } + return null; } } } diff --git a/modules/@angular/compiler/test/compile_metadata_spec.ts b/modules/@angular/compiler/test/compile_metadata_spec.ts deleted file mode 100644 index 883bdccdb2..0000000000 --- a/modules/@angular/compiler/test/compile_metadata_spec.ts +++ /dev/null @@ -1,256 +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 {beforeEach, ddescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal'; - -import {CompileDirectiveMetadata, CompileTypeMetadata, CompileTemplateMetadata, CompileProviderMetadata, CompileDiDependencyMetadata, CompileQueryMetadata, CompileIdentifierMetadata, CompileFactoryMetadata, CompileTokenMetadata, CompileAnimationEntryMetadata, CompileAnimationStyleMetadata, CompileAnimationAnimateMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationGroupMetadata} from '@angular/compiler/src/compile_metadata'; -import {ViewEncapsulation} from '@angular/core/src/metadata/view'; -import {ChangeDetectionStrategy} from '@angular/core/src/change_detection'; -import {LifecycleHooks} from '@angular/core/src/metadata/lifecycle_hooks'; - -export function main() { - describe('CompileMetadata', () => { - var fullTypeMeta: CompileTypeMetadata; - var fullTemplateMeta: CompileTemplateMetadata; - var fullDirectiveMeta: CompileDirectiveMetadata; - - beforeEach(() => { - var diDep = new CompileDiDependencyMetadata({ - isAttribute: true, - isSelf: true, - isHost: true, - isSkipSelf: true, - isOptional: true, - token: new CompileTokenMetadata({value: 'someToken'}), - query: new CompileQueryMetadata({ - selectors: [new CompileTokenMetadata({value: 'one'})], - descendants: true, - first: true, - propertyName: 'one' - }), - viewQuery: new CompileQueryMetadata({ - selectors: [new CompileTokenMetadata({value: 'one'})], - descendants: true, - first: true, - propertyName: 'one' - }) - }); - - fullTypeMeta = new CompileTypeMetadata( - {name: 'SomeType', moduleUrl: 'someUrl', isHost: true, diDeps: [diDep]}); - fullTemplateMeta = new CompileTemplateMetadata({ - encapsulation: ViewEncapsulation.Emulated, - template: '', - templateUrl: 'someTemplateUrl', - styles: ['someStyle'], - styleUrls: ['someStyleUrl'], - animations: [new CompileAnimationEntryMetadata( - 'animation', - [new CompileAnimationStateTransitionMetadata( - '* => *', new CompileAnimationSequenceMetadata([ - new CompileAnimationStyleMetadata(0, [{'opacity': 0}]), - new CompileAnimationAnimateMetadata( - 1000, new CompileAnimationStyleMetadata(0, [{'opacity': 1}])) - ]))])], - ngContentSelectors: ['*'], - interpolation: ['{{', '}}'] - }); - fullDirectiveMeta = CompileDirectiveMetadata.create({ - selector: 'someSelector', - isComponent: true, - type: fullTypeMeta, - template: fullTemplateMeta, - changeDetection: ChangeDetectionStrategy.Default, - inputs: ['someProp'], - outputs: ['someEvent'], - host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'}, - lifecycleHooks: [LifecycleHooks.OnChanges], - providers: [new CompileProviderMetadata({ - token: new CompileTokenMetadata({value: 'token'}), - multi: true, - useClass: fullTypeMeta, - useExisting: new CompileTokenMetadata({ - identifier: new CompileIdentifierMetadata({name: 'someName'}), - identifierIsInstance: true - }), - useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}), - useValue: 'someValue', - })], - viewProviders: [new CompileProviderMetadata({ - token: new CompileTokenMetadata({value: 'token'}), - useClass: fullTypeMeta, - useExisting: new CompileTokenMetadata( - {identifier: new CompileIdentifierMetadata({name: 'someName'})}), - useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}), - useValue: 'someValue' - })], - queries: [new CompileQueryMetadata({ - selectors: [new CompileTokenMetadata({value: 'selector'})], - descendants: true, - first: false, - propertyName: 'prop', - read: new CompileTokenMetadata({value: 'readToken'}) - })], - viewQueries: [new CompileQueryMetadata({ - selectors: [new CompileTokenMetadata({value: 'selector'})], - descendants: true, - first: false, - propertyName: 'prop', - read: new CompileTokenMetadata({value: 'readToken'}) - })] - }); - - }); - - describe('CompileIdentifierMetadata', () => { - it('should serialize with full data', () => { - let full = new CompileIdentifierMetadata( - {name: 'name', moduleUrl: 'module', value: ['one', ['two']]}); - expect(CompileIdentifierMetadata.fromJson(full.toJson())).toEqual(full); - }); - - it('should serialize with no data', () => { - let empty = new CompileIdentifierMetadata(); - expect(CompileIdentifierMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - }); - - describe('DirectiveMetadata', () => { - it('should serialize with full data', () => { - expect(CompileDirectiveMetadata.fromJson(fullDirectiveMeta.toJson())) - .toEqual(fullDirectiveMeta); - }); - - it('should serialize with no data', () => { - var empty = CompileDirectiveMetadata.create(); - expect(CompileDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - }); - - describe('TypeMetadata', () => { - it('should serialize with full data', () => { - expect(CompileTypeMetadata.fromJson(fullTypeMeta.toJson())).toEqual(fullTypeMeta); - }); - - it('should serialize with no data', () => { - var empty = new CompileTypeMetadata(); - expect(CompileTypeMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - }); - - describe('TemplateMetadata', () => { - - it('should serialize with full data', () => { - expect(CompileTemplateMetadata.fromJson(fullTemplateMeta.toJson())) - .toEqual(fullTemplateMeta); - }); - - it('should serialize with no data', () => { - var empty = new CompileTemplateMetadata(); - expect(CompileTemplateMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - - it('should throw an error with invalid interpolation symbols', () => { - expect(() => new CompileTemplateMetadata({interpolation: ['{{']})) - .toThrowError(`'interpolation' should have a start and an end symbol.`); - }); - }); - - describe('CompileAnimationStyleMetadata', () => { - it('should serialize with full data', () => { - let full = new CompileAnimationStyleMetadata(0, [{'opacity': 0, 'color': 'red'}]); - expect(CompileAnimationStyleMetadata.fromJson(full.toJson())).toEqual(full); - }); - - it('should serialize with no data', () => { - let empty = new CompileAnimationStyleMetadata(0, []); - expect(CompileAnimationStyleMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - }); - - describe('CompileAnimationAnimateMetadata', () => { - it('should serialize with full data', () => { - let full = new CompileAnimationAnimateMetadata( - '1s linear', new CompileAnimationStyleMetadata(0, [{'opacity': 0.5, 'color': 'blue'}])); - expect(CompileAnimationAnimateMetadata.fromJson(full.toJson())).toEqual(full); - }); - - it('should serialize with no data', () => { - let empty = new CompileAnimationAnimateMetadata(); - expect(CompileAnimationAnimateMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - }); - - describe('CompileAnimationSequenceMetadata', () => { - it('should serialize with full data', () => { - let full = new CompileAnimationSequenceMetadata([ - new CompileAnimationStyleMetadata(0, [{'opacity': 0.5, 'width': 100}]), - new CompileAnimationAnimateMetadata( - 1000, new CompileAnimationStyleMetadata(0, [{'opacity': 1, 'width': 0}])) - ]); - expect(CompileAnimationSequenceMetadata.fromJson(full.toJson())).toEqual(full); - }); - - it('should serialize with no data', () => { - let empty = new CompileAnimationSequenceMetadata(); - expect(CompileAnimationSequenceMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - }); - - describe('CompileAnimationGroupMetadata', () => { - it('should serialize with full data', () => { - let full = new CompileAnimationGroupMetadata([ - new CompileAnimationStyleMetadata(0, [{'width': 100, 'border': '1px solid red'}]), - new CompileAnimationAnimateMetadata( - 1000, new CompileAnimationStyleMetadata( - 0, [{'width': 900, 'border': '10px solid blue'}])) - ]); - expect(CompileAnimationGroupMetadata.fromJson(full.toJson())).toEqual(full); - }); - - it('should serialize with no data', () => { - let empty = new CompileAnimationGroupMetadata(); - expect(CompileAnimationGroupMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - }); - - describe('CompileAnimationKeyframesSequenceMetadata', () => { - it('should serialize with full data', () => { - let full = new CompileAnimationKeyframesSequenceMetadata([ - new CompileAnimationStyleMetadata(0, [{'width': 0}]), - new CompileAnimationStyleMetadata(0.5, [{'width': 100}]), - new CompileAnimationStyleMetadata(1, [{'width': 200}]), - ]); - expect(CompileAnimationKeyframesSequenceMetadata.fromJson(full.toJson())).toEqual(full); - }); - - it('should serialize with no data', () => { - let empty = new CompileAnimationKeyframesSequenceMetadata(); - expect(CompileAnimationKeyframesSequenceMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - }); - - describe('CompileAnimationEntryMetadata', () => { - it('should serialize with full data', () => { - let full = new CompileAnimationEntryMetadata( - 'name', [new CompileAnimationStateTransitionMetadata( - 'key => value', new CompileAnimationSequenceMetadata([ - new CompileAnimationStyleMetadata(0, [{'color': 'red'}]), - new CompileAnimationAnimateMetadata( - 1000, new CompileAnimationStyleMetadata(0, [{'color': 'blue'}])) - ]))]); - expect(CompileAnimationEntryMetadata.fromJson(full.toJson())).toEqual(full); - }); - - it('should serialize with no data', () => { - let empty = new CompileAnimationEntryMetadata(); - expect(CompileAnimationEntryMetadata.fromJson(empty.toJson())).toEqual(empty); - }); - }); - }); -} diff --git a/modules/@angular/compiler/test/metadata_resolver_spec.ts b/modules/@angular/compiler/test/metadata_resolver_spec.ts index d4d8b350c8..9a3e53e97e 100644 --- a/modules/@angular/compiler/test/metadata_resolver_spec.ts +++ b/modules/@angular/compiler/test/metadata_resolver_spec.ts @@ -12,6 +12,7 @@ import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks import {configureCompiler} from '@angular/core/testing'; import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {CompileNgModuleMetadata} from '../src/compile_metadata'; import {IS_DART, stringify} from '../src/facade/lang'; import {CompileMetadataResolver} from '../src/metadata_resolver'; @@ -22,7 +23,7 @@ export function main() { describe('CompileMetadataResolver', () => { beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); - describe('getMetadata', () => { + describe('getDirectiveMetadata', () => { it('should read metadata', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { var meta = resolver.getDirectiveMetadata(ComponentWithEverything); @@ -102,35 +103,6 @@ export function main() { })); }); - describe('getViewDirectivesMetadata', () => { - - it('should return the directive metadatas', - inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - expect(resolver.getViewDirectivesMetadata(ComponentWithEverything)) - .toContain(resolver.getDirectiveMetadata(SomeDirective)); - })); - - describe('platform directives', () => { - beforeEach(() => { - configureCompiler({ - providers: [{ - provide: CompilerConfig, - useValue: new CompilerConfig( - {genDebugInfo: true, deprecatedPlatformDirectives: [ADirective]}) - }] - }); - }); - - it('should include platform directives when available', - inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - expect(resolver.getViewDirectivesMetadata(ComponentWithEverything)) - .toContain(resolver.getDirectiveMetadata(ADirective)); - expect(resolver.getViewDirectivesMetadata(ComponentWithEverything)) - .toContain(resolver.getDirectiveMetadata(SomeDirective)); - })); - }); - }); - }); } diff --git a/modules/@angular/compiler/test/ng_module_resolver_mock_spec.ts b/modules/@angular/compiler/test/ng_module_resolver_mock_spec.ts new file mode 100644 index 0000000000..b1b2f88f95 --- /dev/null +++ b/modules/@angular/compiler/test/ng_module_resolver_mock_spec.ts @@ -0,0 +1,46 @@ +/** + * @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 {beforeEach, ddescribe, describe, expect, iit, it, inject,} from '@angular/core/testing/testing_internal'; + +import {stringify, isBlank} from '../src/facade/lang'; +import {MockNgModuleResolver} from '../testing'; +import {NgModule, NgModuleMetadata, Injector} from '@angular/core'; + +export function main() { + describe('MockNgModuleResolver', () => { + var ngModuleResolver: MockNgModuleResolver; + + beforeEach(inject([Injector], (injector: Injector) => { + ngModuleResolver = new MockNgModuleResolver(injector); + })); + + describe('NgModule overriding', () => { + it('should fallback to the default NgModuleResolver when templates are not overridden', + () => { + var ngModule = ngModuleResolver.resolve(SomeNgModule); + expect(ngModule.declarations).toEqual([SomeDirective]); + }); + + it('should allow overriding the @NgModule', () => { + ngModuleResolver.setNgModule( + SomeNgModule, new NgModuleMetadata({declarations: [SomeOtherDirective]})); + var ngModule = ngModuleResolver.resolve(SomeNgModule); + expect(ngModule.declarations).toEqual([SomeOtherDirective]); + }); + }); + }); +} + +class SomeDirective {} + +class SomeOtherDirective {} + +@NgModule({declarations: [SomeDirective]}) +class SomeNgModule { +} diff --git a/modules/@angular/compiler/test/ng_module_resolver_spec.ts b/modules/@angular/compiler/test/ng_module_resolver_spec.ts new file mode 100644 index 0000000000..442766c508 --- /dev/null +++ b/modules/@angular/compiler/test/ng_module_resolver_spec.ts @@ -0,0 +1,53 @@ +/** + * @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 {NgModuleResolver} from '@angular/compiler/src/ng_module_resolver'; +import {NgModule, NgModuleMetadata} from '@angular/core/src/metadata'; +import {stringify} from '../src/facade/lang'; + +class SomeClass1 {} +class SomeClass2 {} +class SomeClass3 {} +class SomeClass4 {} +class SomeClass5 {} + +@NgModule({ + declarations: [SomeClass1], + imports: [SomeClass2], + exports: [SomeClass3], + providers: [SomeClass4], + precompile: [SomeClass5] +}) +class SomeModule { +} + +class SimpleClass {} + +export function main() { + describe('NgModuleResolver', () => { + var resolver: NgModuleResolver; + + beforeEach(() => { resolver = new NgModuleResolver(); }); + + it('should read out the metadata from the class', () => { + var viewMetadata = resolver.resolve(SomeModule); + expect(viewMetadata).toEqual(new NgModuleMetadata({ + declarations: [SomeClass1], + imports: [SomeClass2], + exports: [SomeClass3], + providers: [SomeClass4], + precompile: [SomeClass5] + })); + }); + + it('should throw when simple class has no component decorator', () => { + expect(() => resolver.resolve(SimpleClass)) + .toThrowError(`No NgModule metadata found for '${stringify(SimpleClass)}'.`); + }); + }); +} diff --git a/modules/@angular/compiler/test/runtime_compiler_spec.ts b/modules/@angular/compiler/test/runtime_compiler_spec.ts index 2f5b44b9b3..548b9be7ed 100644 --- a/modules/@angular/compiler/test/runtime_compiler_spec.ts +++ b/modules/@angular/compiler/test/runtime_compiler_spec.ts @@ -8,7 +8,7 @@ import {beforeEach, ddescribe, xdescribe, describe, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; -import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector, AppModule, AppModuleMetadata, AppModuleFactory} from '@angular/core'; +import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector, NgModule, NgModuleFactory} from '@angular/core'; import {ConcreteType, stringify} from '../src/facade/lang'; import {fakeAsync, tick, TestComponentBuilder, ComponentFixture, configureCompiler} from '@angular/core/testing'; import {XHR, ViewResolver} from '@angular/compiler'; @@ -28,10 +28,6 @@ class SomeComp { class SomeCompWithUrlTemplate { } -@AppModule({}) -class SomeModule { -} - export function main() { describe('RuntimeCompiler', () => { let compiler: Compiler; @@ -118,49 +114,61 @@ export function main() { })); }); - describe('compileAppModuleAsync', () => { + describe('compileModuleAsync', () => { it('should allow to use templateUrl components', fakeAsync(() => { + @NgModule( + {declarations: [SomeCompWithUrlTemplate], precompile: [SomeCompWithUrlTemplate]}) + class SomeModule { + } + xhr.spy('get').andCallFake(() => Promise.resolve('hello')); - let appModuleFactory: AppModuleFactory; - compiler - .compileAppModuleAsync( - SomeModule, new AppModuleMetadata({precompile: [SomeCompWithUrlTemplate]})) - .then((f) => appModuleFactory = f); + let ngModuleFactory: NgModuleFactory; + compiler.compileModuleAsync(SomeModule).then((f) => ngModuleFactory = f); tick(); - expect(appModuleFactory.moduleType).toBe(SomeModule); + expect(ngModuleFactory.moduleType).toBe(SomeModule); })); }); - describe('compileAppModuleSync', () => { + describe('compileModuleSync', () => { it('should throw when using a templateUrl that has not been compiled before', () => { + @NgModule({declarations: [SomeCompWithUrlTemplate], precompile: [SomeCompWithUrlTemplate]}) + class SomeModule { + } + xhr.spy('get').andCallFake(() => Promise.resolve('')); - expect( - () => compiler.compileAppModuleSync( - SomeModule, new AppModuleMetadata({precompile: [SomeCompWithUrlTemplate]}))) + expect(() => compiler.compileModuleSync(SomeModule)) .toThrowError( `Can't compile synchronously as ${stringify(SomeCompWithUrlTemplate)} is still being loaded!`); }); it('should throw when using a templateUrl in a nested component that has not been compiled before', () => { + @NgModule({declarations: [SomeComp], precompile: [SomeComp]}) + class SomeModule { + } + xhr.spy('get').andCallFake(() => Promise.resolve('')); viewResolver.setView( SomeComp, new ViewMetadata({template: '', directives: [ChildComp]})); viewResolver.setView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'})); - expect( - () => compiler.compileAppModuleSync( - SomeModule, new AppModuleMetadata({precompile: [SomeComp]}))) + expect(() => compiler.compileModuleSync(SomeModule)) .toThrowError( `Can't compile synchronously as ${stringify(ChildComp)} is still being loaded!`); }); it('should allow to use templateUrl components that have been loaded before', fakeAsync(() => { + @NgModule( + {declarations: [SomeCompWithUrlTemplate], precompile: [SomeCompWithUrlTemplate]}) + class SomeModule { + } + xhr.spy('get').andCallFake(() => Promise.resolve('hello')); - tcb.createFakeAsync(SomeCompWithUrlTemplate); - let appModuleFactory = compiler.compileAppModuleSync( - SomeModule, new AppModuleMetadata({precompile: [SomeCompWithUrlTemplate]})); - expect(appModuleFactory).toBeTruthy(); + compiler.compileModuleAsync(SomeModule); + tick(); + + let ngModuleFactory = compiler.compileModuleSync(SomeModule); + expect(ngModuleFactory).toBeTruthy(); })); }); }); diff --git a/modules/@angular/compiler/test/test_component_builder_spec.ts b/modules/@angular/compiler/test/test_component_builder_spec.ts index 9312d36d47..e9d469b3ea 100644 --- a/modules/@angular/compiler/test/test_component_builder_spec.ts +++ b/modules/@angular/compiler/test/test_component_builder_spec.ts @@ -9,7 +9,7 @@ import {AsyncTestCompleter, beforeEach, ddescribe, xdescribe, describe, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; import {TestComponentBuilder, ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, withProviders} from '@angular/core/testing'; -import {Injectable, Component, Input, ViewMetadata} from '@angular/core'; +import {Injectable, Component, Input, ViewMetadata, Pipe, NgModule} from '@angular/core'; import {NgIf} from '@angular/common'; import {TimerWrapper} from '../src/facade/async'; import {PromiseWrapper} from '../src/facade/promise'; @@ -327,6 +327,27 @@ export function main() { expect(componentFixture.nativeElement).toHaveText('Mock'); })); + it('should create components synchronously with a custom module', + inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + @Pipe({name: 'somePipe'}) + class SomePipe { + transform(value: any) { return `transformed ${value}`; } + } + + @NgModule({declarations: [SomePipe]}) + class SomeModule { + } + + @Component({selector: 'comp', template: `{{'hello' | somePipe}}`}) + class SomeComponent { + } + + + let componentFixture = tcb.createSync(SomeComponent, SomeModule); + componentFixture.detectChanges(); + expect(componentFixture.nativeElement).toHaveText('transformed hello'); + })); + describe('ComponentFixture', () => { it('should auto detect changes if autoDetectChanges is called', inject( diff --git a/modules/@angular/compiler/testing.ts b/modules/@angular/compiler/testing.ts index fcc0ff94c1..3c1664e08a 100644 --- a/modules/@angular/compiler/testing.ts +++ b/modules/@angular/compiler/testing.ts @@ -10,3 +10,29 @@ export * from './testing/schema_registry_mock'; export * from './testing/view_resolver_mock'; export * from './testing/test_component_builder'; export * from './testing/directive_resolver_mock'; +export * from './testing/ng_module_resolver_mock'; + +import {createPlatformFactory, CompilerOptions, PlatformRef} from '@angular/core'; +import {coreDynamicPlatform, DirectiveResolver, ViewResolver, NgModuleResolver} from './index'; +import {MockViewResolver} from './testing/view_resolver_mock'; +import {MockDirectiveResolver} from './testing/directive_resolver_mock'; +import {MockNgModuleResolver} from './testing/ng_module_resolver_mock'; + + +/** + * Platform for dynamic tests + * + * @experimental + */ +export const coreDynamicTestingPlatform = + createPlatformFactory(coreDynamicPlatform, 'coreDynamicTesting', [{ + provide: CompilerOptions, + useValue: { + providers: [ + {provide: DirectiveResolver, useClass: MockDirectiveResolver}, + {provide: ViewResolver, useClass: MockViewResolver}, + {provide: NgModuleResolver, useClass: MockNgModuleResolver} + ] + }, + multi: true + }]); diff --git a/modules/@angular/compiler/testing/directive_resolver_mock.ts b/modules/@angular/compiler/testing/directive_resolver_mock.ts index 9a764f0e27..3a29439933 100644 --- a/modules/@angular/compiler/testing/directive_resolver_mock.ts +++ b/modules/@angular/compiler/testing/directive_resolver_mock.ts @@ -27,8 +27,11 @@ export class MockDirectiveResolver extends DirectiveResolver { private get _compiler(): Compiler { return this._injector.get(Compiler); } - resolve(type: Type): DirectiveMetadata { - var dm = super.resolve(type); + resolve(type: Type, throwIfNotFound = true): DirectiveMetadata { + var dm = super.resolve(type, throwIfNotFound); + if (!dm) { + return null; + } var providerOverrides = this._providerOverrides.get(type); var viewProviderOverrides = this.viewProviderOverrides.get(type); diff --git a/modules/@angular/compiler/testing/ng_module_resolver_mock.ts b/modules/@angular/compiler/testing/ng_module_resolver_mock.ts new file mode 100644 index 0000000000..fc270f6b4e --- /dev/null +++ b/modules/@angular/compiler/testing/ng_module_resolver_mock.ts @@ -0,0 +1,46 @@ +/** + * @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 {Compiler, Injectable, Injector, NgModuleMetadata, Type} from '@angular/core'; + +import {NgModuleResolver} from '../index'; +import {Map} from '../src/facade/collection'; + +@Injectable() +export class MockNgModuleResolver extends NgModuleResolver { + /** @internal */ + _ngModules = new Map(); + + constructor(private _injector: Injector) { super(); } + + private get _compiler(): Compiler { return this._injector.get(Compiler); } + + private _clearCacheFor(component: Type) { this._compiler.clearCacheFor(component); } + + /** + * Overrides the {@link NgModuleMetadata} for a module. + */ + setNgModule(type: Type, metadata: NgModuleMetadata): void { + this._ngModules.set(type, metadata); + this._clearCacheFor(type); + } + + /** + * Returns the {@link NgModuleMetadata} for a module: + * - Set the {@link NgModuleMetadata} to the overridden view when it exists or fallback to the + * default + * `NgModuleResolver`, see `setNgModule`. + */ + resolve(type: Type, throwIfNotFound = true): NgModuleMetadata { + var metadata = this._ngModules.get(type); + if (!metadata) { + metadata = super.resolve(type, throwIfNotFound); + } + return metadata; + } +} diff --git a/modules/@angular/compiler/testing/test_component_builder.ts b/modules/@angular/compiler/testing/test_component_builder.ts index 76fa0202fb..2a28454900 100644 --- a/modules/@angular/compiler/testing/test_component_builder.ts +++ b/modules/@angular/compiler/testing/test_component_builder.ts @@ -87,14 +87,16 @@ export class OverridingTestComponentBuilder extends TestComponentBuilder { return clone; } - createAsync(rootComponentType: ConcreteType): Promise> { + createAsync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): + Promise> { this._applyMetadataOverrides(); - return super.createAsync(rootComponentType); + return super.createAsync(rootComponentType, ngModule); } - createSync(rootComponentType: ConcreteType): ComponentFixture { + createSync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): + ComponentFixture { this._applyMetadataOverrides(); - return super.createSync(rootComponentType); + return super.createSync(rootComponentType, ngModule); } private _applyMetadataOverrides() { diff --git a/modules/@angular/compiler/testing/view_resolver_mock.ts b/modules/@angular/compiler/testing/view_resolver_mock.ts index 4b7b5090c1..7fe1ac6eb0 100644 --- a/modules/@angular/compiler/testing/view_resolver_mock.ts +++ b/modules/@angular/compiler/testing/view_resolver_mock.ts @@ -72,10 +72,13 @@ export class MockViewResolver extends ViewResolver { * - Override the directives, see `overrideViewDirective`. * - Override the @View definition, see `setInlineTemplate`. */ - resolve(component: Type): ViewMetadata { + resolve(component: Type, throwIfNotFound = true): ViewMetadata { var view = this._views.get(component); if (isBlank(view)) { - view = super.resolve(component); + view = super.resolve(component, throwIfNotFound); + if (!view) { + return null; + } } var directives: any[] /** TODO #9100 */ = []; diff --git a/modules/@angular/core/index.ts b/modules/@angular/core/index.ts index 59013b965a..20c0ea3e4e 100644 --- a/modules/@angular/core/index.ts +++ b/modules/@angular/core/index.ts @@ -23,8 +23,8 @@ export {DebugElement, DebugNode, asNativeElements, getDebugNode} from './src/deb export * from './src/testability/testability'; export * from './src/change_detection'; export * from './src/platform_directives_and_pipes'; -export * from './src/platform_common_providers'; -export {APPLICATION_COMMON_PROVIDERS} from './src/application_common_providers'; +export * from './src/platform_core_providers'; +export {APPLICATION_COMMON_PROVIDERS, ApplicationModule} from './src/application_module'; export {wtfCreateScope, wtfLeave, wtfStartTimeRange, wtfEndTimeRange, WtfScopeFn} from './src/profile/profile'; export {Type} from './src/facade/lang'; diff --git a/modules/@angular/core/private_export.ts b/modules/@angular/core/private_export.ts index f46baf03cd..16d776d45c 100644 --- a/modules/@angular/core/private_export.ts +++ b/modules/@angular/core/private_export.ts @@ -20,11 +20,11 @@ import * as console from './src/console'; import * as debug from './src/debug/debug_renderer'; import * as provider_util from './src/di/provider_util'; import * as reflective_provider from './src/di/reflective_provider'; -import * as app_module_factory from './src/linker/app_module_factory'; import * as component_factory_resolver from './src/linker/component_factory_resolver'; import * as component_resolver from './src/linker/component_resolver'; import * as debug_context from './src/linker/debug_context'; import * as element from './src/linker/element'; +import * as ng_module_factory from './src/linker/ng_module_factory'; import * as template_ref from './src/linker/template_ref'; import * as view from './src/linker/view'; import * as view_type from './src/linker/view_type'; @@ -52,13 +52,12 @@ export declare namespace __core_private_types__ { export var LIFECYCLE_HOOKS_VALUES: typeof lifecycle_hooks.LIFECYCLE_HOOKS_VALUES; export type ReflectorReader = reflector_reader.ReflectorReader; export var ReflectorReader: typeof reflector_reader.ReflectorReader; - export var ReflectorComponentResolver: typeof component_resolver.ReflectorComponentResolver; export var CodegenComponentFactoryResolver: typeof component_factory_resolver.CodegenComponentFactoryResolver; export type AppElement = element.AppElement; export var AppElement: typeof element.AppElement; export var AppView: typeof view.AppView; - export var AppModuleInjector: typeof app_module_factory.AppModuleInjector; + export var NgModuleInjector: typeof ng_module_factory.NgModuleInjector; export type DebugAppView = view.DebugAppView; export var DebugAppView: typeof view.DebugAppView; export type ViewType = view_type.ViewType; @@ -136,12 +135,11 @@ export var __core_private__ = { LifecycleHooks: lifecycle_hooks.LifecycleHooks, LIFECYCLE_HOOKS_VALUES: lifecycle_hooks.LIFECYCLE_HOOKS_VALUES, ReflectorReader: reflector_reader.ReflectorReader, - ReflectorComponentResolver: component_resolver.ReflectorComponentResolver, CodegenComponentFactoryResolver: component_factory_resolver.CodegenComponentFactoryResolver, AppElement: element.AppElement, AppView: view.AppView, DebugAppView: view.DebugAppView, - AppModuleInjector: app_module_factory.AppModuleInjector, + NgModuleInjector: ng_module_factory.NgModuleInjector, ViewType: view_type.ViewType, MAX_INTERPOLATION_VALUES: view_utils.MAX_INTERPOLATION_VALUES, checkBinding: view_utils.checkBinding, diff --git a/modules/@angular/core/src/application_common_providers.ts b/modules/@angular/core/src/application_common_providers.ts deleted file mode 100644 index ccf8619759..0000000000 --- a/modules/@angular/core/src/application_common_providers.ts +++ /dev/null @@ -1,56 +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 {Type} from '../src/facade/lang'; - -import {APPLICATION_CORE_PROVIDERS} from './application_ref'; -import {APP_ID_RANDOM_PROVIDER} from './application_tokens'; -import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection'; -import {ComponentFactoryResolver} from './linker/component_factory_resolver'; -import {ComponentResolver, ReflectorComponentResolver} from './linker/component_resolver'; -import {DynamicComponentLoader, DynamicComponentLoader_} from './linker/dynamic_component_loader'; -import {ViewUtils} from './linker/view_utils'; - -let __unused: Type; // avoid unused import when Type union types are erased - -export function _componentFactoryResolverFactory() { - return ComponentFactoryResolver.NULL; -} - -export function _iterableDiffersFactory() { - return defaultIterableDiffers; -} - -export function _keyValueDiffersFactory() { - return defaultKeyValueDiffers; -} - -/** - * A default set of providers which should be included in any Angular - * application, regardless of the platform it runs onto. - * @stable - */ -export const APPLICATION_COMMON_PROVIDERS: Array = - /*@ts2dart_const*/[ - APPLICATION_CORE_PROVIDERS, - /* @ts2dart_Provider */ {provide: ComponentResolver, useClass: ReflectorComponentResolver}, - {provide: ComponentFactoryResolver, useFactory: _componentFactoryResolverFactory, deps: []}, - APP_ID_RANDOM_PROVIDER, - ViewUtils, - /* @ts2dart_Provider */ { - provide: IterableDiffers, - useFactory: _iterableDiffersFactory, - deps: [] - }, - /* @ts2dart_Provider */ { - provide: KeyValueDiffers, - useFactory: _keyValueDiffersFactory, - deps: [] - }, - /* @ts2dart_Provider */ {provide: DynamicComponentLoader, useClass: DynamicComponentLoader_}, - ]; diff --git a/modules/@angular/core/src/application_module.ts b/modules/@angular/core/src/application_module.ts new file mode 100644 index 0000000000..91caaa9d75 --- /dev/null +++ b/modules/@angular/core/src/application_module.ts @@ -0,0 +1,84 @@ +/** + * @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 {Type} from '../src/facade/lang'; + +import {ApplicationRef, ApplicationRef_, isDevMode} from './application_ref'; +import {APP_ID_RANDOM_PROVIDER} from './application_tokens'; +import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection'; +import {OptionalMetadata, SkipSelfMetadata} from './di'; +import {Compiler} from './linker/compiler'; +import {ComponentFactoryResolver} from './linker/component_factory_resolver'; +import {ComponentResolver} from './linker/component_resolver'; +import {DynamicComponentLoader, DynamicComponentLoader_} from './linker/dynamic_component_loader'; +import {ViewUtils} from './linker/view_utils'; +import {NgModule} from './metadata'; +import {NgZone} from './zone/ng_zone'; + +let __unused: Type; // avoid unused import when Type union types are erased + +export function _componentFactoryResolverFactory() { + return ComponentFactoryResolver.NULL; +} + +export function _iterableDiffersFactory() { + return defaultIterableDiffers; +} + +export function _keyValueDiffersFactory() { + return defaultKeyValueDiffers; +} + +export function createNgZone(parent: NgZone): NgZone { + // If an NgZone is already present in the parent injector, + // use that one. Creating the NgZone in the same injector as the + // application is dangerous as some services might get created before + // the NgZone has been created. + // We keep the NgZone factory in the application providers for + // backwards compatibility for now though. + if (parent) { + return parent; + } + return new NgZone({enableLongStackTrace: isDevMode()}); +} + +/** + * A default set of providers which should be included in any Angular + * application, regardless of the platform it runs onto. + * + * @deprecated Include `ApplicationModule` instead. + */ +export const APPLICATION_COMMON_PROVIDERS: Array = []; + +/** + * This module includes the providers of @angular/core that are needed + * to bootstrap components via `ApplicationRef`. + * + * @experimental + */ +@NgModule({ + providers: [ + { + provide: NgZone, + useFactory: createNgZone, + deps: [[new SkipSelfMetadata(), new OptionalMetadata(), NgZone]] + }, + ApplicationRef_, + {provide: ApplicationRef, useExisting: ApplicationRef_}, + Compiler, + {provide: ComponentResolver, useExisting: Compiler}, + {provide: ComponentFactoryResolver, useFactory: _componentFactoryResolverFactory}, + APP_ID_RANDOM_PROVIDER, + ViewUtils, + {provide: IterableDiffers, useFactory: _iterableDiffersFactory}, + {provide: KeyValueDiffers, useFactory: _keyValueDiffersFactory}, + {provide: DynamicComponentLoader, useClass: DynamicComponentLoader_}, + ] +}) +export class ApplicationModule { +} diff --git a/modules/@angular/core/src/application_ref.ts b/modules/@angular/core/src/application_ref.ts index 11e607cf3a..f46aaa8e10 100644 --- a/modules/@angular/core/src/application_ref.ts +++ b/modules/@angular/core/src/application_ref.ts @@ -14,35 +14,16 @@ import {ConcreteType, IS_DART, Type, isBlank, isPresent, isPromise} from '../src import {APP_INITIALIZER, PLATFORM_INITIALIZER} from './application_tokens'; import {ChangeDetectorRef} from './change_detection/change_detector_ref'; import {Console} from './console'; -import {Inject, Injectable, Injector, OpaqueToken, Optional, OptionalMetadata, ReflectiveInjector, SkipSelf, SkipSelfMetadata, forwardRef} from './di'; -import {AppModuleFactory, AppModuleRef} from './linker/app_module_factory'; +import {Inject, Injectable, Injector, OpaqueToken, Optional, ReflectiveInjector, SkipSelf, forwardRef} from './di'; import {Compiler, CompilerFactory, CompilerOptions} from './linker/compiler'; import {ComponentFactory, ComponentRef} from './linker/component_factory'; import {ComponentFactoryResolver} from './linker/component_factory_resolver'; import {ComponentResolver} from './linker/component_resolver'; +import {NgModuleFactory, NgModuleRef} from './linker/ng_module_factory'; import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile'; import {Testability, TestabilityRegistry} from './testability/testability'; import {NgZone, NgZoneError} from './zone/ng_zone'; - - -/** - * Create an Angular zone. - * @experimental - */ -export function createNgZone(parent: NgZone): NgZone { - // If an NgZone is already present in the parent injector, - // use that one. Creating the NgZone in the same injector as the - // application is dangerous as some services might get created before - // the NgZone has been created. - // We keep the NgZone factory in the application providers for - // backwards compatibility for now though. - if (parent) { - return parent; - } - return new NgZone({enableLongStackTrace: isDevMode()}); -} - var _devMode: boolean = true; var _runModeLocked: boolean = false; var _platform: PlatformRef; @@ -113,17 +94,30 @@ export function createPlatform(injector: Injector): PlatformRef { return _platform; } +/** + * Factory for a platform. + * + * @experimental + */ +export type PlatformFactory = (extraProviders?: any[]) => PlatformRef; + /** * Creates a fatory for a platform * * @experimental APIs related to application bootstrap are currently under review. */ -export function createPlatformFactory(name: string, providers: any[]): () => PlatformRef { +export function createPlatformFactory( + parentPlaformFactory: PlatformFactory, name: string, providers: any[] = []): PlatformFactory { const marker = new OpaqueToken(`Platform: ${name}`); - return () => { + return (extraProviders: any[] = []) => { if (!getPlatform()) { - createPlatform( - ReflectiveInjector.resolveAndCreate(providers.concat({provide: marker, useValue: true}))); + if (parentPlaformFactory) { + parentPlaformFactory( + providers.concat(extraProviders).concat({provide: marker, useValue: true})); + } else { + createPlatform(ReflectiveInjector.resolveAndCreate( + providers.concat(extraProviders).concat({provide: marker, useValue: true}))); + } } return assertPlatform(marker); }; @@ -168,7 +162,7 @@ export function getPlatform(): PlatformRef { } /** - * Creates an instance of an `@AppModule` for the given platform + * Creates an instance of an `@NgModule` for the given platform * for offline compilation. * * ## Simple Example @@ -176,8 +170,8 @@ export function getPlatform(): PlatformRef { * ```typescript * my_module.ts: * - * @AppModule({ - * modules: [BrowserModule] + * @NgModule({ + * imports: [BrowserModule] * }) * class MyModule {} * @@ -192,11 +186,11 @@ export function getPlatform(): PlatformRef { * @experimental APIs related to application bootstrap are currently under review. */ export function bootstrapModuleFactory( - moduleFactory: AppModuleFactory, platform: PlatformRef): AppModuleRef { + moduleFactory: NgModuleFactory, platform: PlatformRef): NgModuleRef { // Note: We need to create the NgZone _before_ we instantiate the module, // as instantiating the module creates some providers eagerly. // So we create a mini parent injector that just contains the new NgZone and - // pass that as parent to the AppModuleFactory. + // pass that as parent to the NgModuleFactory. const ngZone = new NgZone({enableLongStackTrace: isDevMode()}); const ngZoneInjector = ReflectiveInjector.resolveAndCreate([{provide: NgZone, useValue: ngZone}], platform.injector); @@ -204,13 +198,13 @@ export function bootstrapModuleFactory( } /** - * Creates an instance of an `@AppModule` for a given platform using the given runtime compiler. + * Creates an instance of an `@NgModule` for a given platform using the given runtime compiler. * * ## Simple Example * * ```typescript - * @AppModule({ - * modules: [BrowserModule] + * @NgModule({ + * imports: [BrowserModule] * }) * class MyModule {} * @@ -220,10 +214,11 @@ export function bootstrapModuleFactory( */ export function bootstrapModule( moduleType: ConcreteType, platform: PlatformRef, - compilerOptions: CompilerOptions = {}): Promise> { + compilerOptions: CompilerOptions | CompilerOptions[] = []): Promise> { const compilerFactory: CompilerFactory = platform.injector.get(CompilerFactory); - const compiler = compilerFactory.createCompiler(compilerOptions); - return compiler.compileAppModuleAsync(moduleType) + const compiler = compilerFactory.createCompiler( + compilerOptions instanceof Array ? compilerOptions : [compilerOptions]); + return compiler.compileModuleAsync(moduleType) .then((moduleFactory) => bootstrapModuleFactory(moduleFactory, platform)) .then((moduleRef) => { const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef); @@ -239,10 +234,7 @@ export function bootstrapModule( */ export function coreBootstrap( componentFactory: ComponentFactory, injector: Injector): ComponentRef { - let console = injector.get(Console); - console.warn('coreBootstrap is deprecated. Use bootstrapModuleFactory instead.'); - var appRef: ApplicationRef = injector.get(ApplicationRef); - return appRef.bootstrap(componentFactory); + throw new BaseException('coreBootstrap is deprecated. Use bootstrapModuleFactory instead.'); } /** @@ -254,15 +246,7 @@ export function coreBootstrap( */ export function coreLoadAndBootstrap( componentType: Type, injector: Injector): Promise> { - let console = injector.get(Console); - console.warn('coreLoadAndBootstrap is deprecated. Use bootstrapModule instead.'); - var appRef: ApplicationRef = injector.get(ApplicationRef); - return appRef.run(() => { - var componentResolver: ComponentResolver = injector.get(ComponentResolver); - return PromiseWrapper - .all([componentResolver.resolveComponent(componentType), appRef.waitForAsyncInitializers()]) - .then((arr) => appRef.bootstrap(arr[0])); - }); + throw new BaseException('coreLoadAndBootstrap is deprecated. Use bootstrapModule instead.'); } /** @@ -592,20 +576,3 @@ export class ApplicationRef_ extends ApplicationRef { get componentTypes(): Type[] { return this._rootComponentTypes; } } - -export const PLATFORM_CORE_PROVIDERS = - /*@ts2dart_const*/[ - PlatformRef_, - /*@ts2dart_const*/ ( - /* @ts2dart_Provider */ {provide: PlatformRef, useExisting: PlatformRef_}) - ]; - -export const APPLICATION_CORE_PROVIDERS = /*@ts2dart_const*/[ - /* @ts2dart_Provider */ { - provide: NgZone, - useFactory: createNgZone, - deps: [[new SkipSelfMetadata(), new OptionalMetadata(), NgZone]] - }, - ApplicationRef_, - /* @ts2dart_Provider */ {provide: ApplicationRef, useExisting: ApplicationRef_}, -]; diff --git a/modules/@angular/core/src/linker.ts b/modules/@angular/core/src/linker.ts index 345f916b41..0fb5b6b971 100644 --- a/modules/@angular/core/src/linker.ts +++ b/modules/@angular/core/src/linker.ts @@ -7,8 +7,6 @@ */ // Public API for compiler -export {AppModuleFactory, AppModuleRef} from './linker/app_module_factory'; -export {AppModuleFactoryLoader} from './linker/app_module_factory_loader'; export {Compiler, CompilerFactory, CompilerOptions, ComponentStillLoadingError} from './linker/compiler'; export {ComponentFactory, ComponentRef} from './linker/component_factory'; export {ComponentFactoryResolver, NoComponentFactoryError} from './linker/component_factory_resolver'; @@ -16,8 +14,10 @@ export {ComponentResolver} from './linker/component_resolver'; export {DynamicComponentLoader} from './linker/dynamic_component_loader'; export {ElementRef} from './linker/element_ref'; export {ExpressionChangedAfterItHasBeenCheckedException} from './linker/exceptions'; +export {NgModuleFactory, NgModuleRef} from './linker/ng_module_factory'; +export {NgModuleFactoryLoader} from './linker/ng_module_factory_loader'; export {QueryList} from './linker/query_list'; -export {SystemJsAppModuleLoader} from './linker/system_js_app_module_factory_loader'; +export {SystemJsNgModuleLoader} from './linker/system_js_ng_module_factory_loader'; export {SystemJsCmpFactoryResolver, SystemJsComponentResolver} from './linker/systemjs_component_resolver'; export {TemplateRef} from './linker/template_ref'; export {ViewContainerRef} from './linker/view_container_ref'; diff --git a/modules/@angular/core/src/linker/compiler.ts b/modules/@angular/core/src/linker/compiler.ts index 004f83a306..cea792a283 100644 --- a/modules/@angular/core/src/linker/compiler.ts +++ b/modules/@angular/core/src/linker/compiler.ts @@ -6,14 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injector} from '../di'; +import {Injector, OpaqueToken} from '../di'; import {BaseException} from '../facade/exceptions'; import {ConcreteType, Type, stringify} from '../facade/lang'; import {ViewEncapsulation} from '../metadata'; -import {AppModuleMetadata} from '../metadata/app_module'; +import {NgModuleMetadata} from '../metadata/ng_module'; -import {AppModuleFactory} from './app_module_factory'; import {ComponentFactory} from './component_factory'; +import {ComponentResolver} from './component_resolver'; +import {NgModuleFactory} from './ng_module_factory'; /** @@ -32,14 +33,16 @@ export class ComponentStillLoadingError extends BaseException { * to create {@link ComponentFactory}s, which * can later be used to create and render a Component instance. * - * Each `@AppModule` provides an own `Compiler` to its injector, - * that will use the directives/pipes of the app module for compilation + * Each `@NgModule` provides an own `Compiler` to its injector, + * that will use the directives/pipes of the ng module for compilation * of components. * @stable */ export class Compiler { /** * Returns the injector with which the compiler has been created. + * + * @internal */ get injector(): Injector { throw new BaseException(`Runtime compiler is not loaded. Tried to read the injector.`); @@ -48,7 +51,8 @@ export class Compiler { /** * Loads the template and styles of a component and returns the associated `ComponentFactory`. */ - compileComponentAsync(component: ConcreteType): Promise> { + compileComponentAsync(component: ConcreteType, ngModule: Type = null): + Promise> { throw new BaseException( `Runtime compiler is not loaded. Tried to compile ${stringify(component)}`); } @@ -56,23 +60,21 @@ export class Compiler { * Compiles the given component. All templates have to be either inline or compiled via * `compileComponentAsync` before. Otherwise throws a {@link ComponentStillLoadingError}. */ - compileComponentSync(component: ConcreteType): ComponentFactory { + compileComponentSync(component: ConcreteType, ngModule: Type = null): ComponentFactory { throw new BaseException( `Runtime compiler is not loaded. Tried to compile ${stringify(component)}`); } /** - * Compiles the given App Module. All templates of the components listed in `precompile` + * Compiles the given NgModule. All templates of the components listed in `precompile` * have to be either inline or compiled before via `compileComponentAsync` / - * `compileAppModuleAsync`. Otherwise throws a {@link ComponentStillLoadingError}. + * `compileModuleAsync`. Otherwise throws a {@link ComponentStillLoadingError}. */ - compileAppModuleSync(moduleType: ConcreteType, metadata: AppModuleMetadata = null): - AppModuleFactory { + compileModuleSync(moduleType: ConcreteType): NgModuleFactory { throw new BaseException( `Runtime compiler is not loaded. Tried to compile ${stringify(moduleType)}`); } - compileAppModuleAsync(moduleType: ConcreteType, metadata: AppModuleMetadata = null): - Promise> { + compileModuleAsync(moduleType: ConcreteType): Promise> { throw new BaseException( `Runtime compiler is not loaded. Tried to compile ${stringify(moduleType)}`); } @@ -83,7 +85,7 @@ export class Compiler { clearCache(): void {} /** - * Clears the cache for the given component/appModule. + * Clears the cache for the given component/ngModule. */ clearCacheFor(type: Type) {} } @@ -98,8 +100,14 @@ export type CompilerOptions = { useJit?: boolean, defaultEncapsulation?: ViewEncapsulation, providers?: any[], - deprecatedAppProviders?: any[] -} +}; + +/** + * Token to provide CompilerOptions in the platform injector. + * + * @experimental + */ +export const CompilerOptions = new OpaqueToken('compilerOptions'); /** * A factory for creating a Compiler @@ -107,44 +115,5 @@ export type CompilerOptions = { * @experimental */ export abstract class CompilerFactory { - static mergeOptions(defaultOptions: CompilerOptions = {}, newOptions: CompilerOptions = {}): - CompilerOptions { - return { - useDebug: _firstDefined(newOptions.useDebug, defaultOptions.useDebug), - useJit: _firstDefined(newOptions.useJit, defaultOptions.useJit), - defaultEncapsulation: - _firstDefined(newOptions.defaultEncapsulation, defaultOptions.defaultEncapsulation), - providers: _mergeArrays(defaultOptions.providers, newOptions.providers), - deprecatedAppProviders: - _mergeArrays(defaultOptions.deprecatedAppProviders, newOptions.deprecatedAppProviders) - }; - } - - withDefaults(options: CompilerOptions = {}): CompilerFactory { - return new _DefaultApplyingCompilerFactory(this, options); - } - abstract createCompiler(options?: CompilerOptions): Compiler; -} - -class _DefaultApplyingCompilerFactory extends CompilerFactory { - constructor(private _delegate: CompilerFactory, private _options: CompilerOptions) { super(); } - - createCompiler(options: CompilerOptions = {}): Compiler { - return this._delegate.createCompiler(CompilerFactory.mergeOptions(this._options, options)); - } -} - -function _firstDefined(...args: T[]): T { - for (var i = 0; i < args.length; i++) { - if (args[i] !== undefined) { - return args[i]; - } - } - return undefined; -} - -function _mergeArrays(...parts: any[][]): any[] { - let result: any[] = []; - parts.forEach((part) => result.push.apply(result, part)); - return result; + abstract createCompiler(options?: CompilerOptions[]): Compiler; } diff --git a/modules/@angular/core/src/linker/component_resolver.ts b/modules/@angular/core/src/linker/component_resolver.ts index 910efa46bf..e7e07fa380 100644 --- a/modules/@angular/core/src/linker/component_resolver.ts +++ b/modules/@angular/core/src/linker/component_resolver.ts @@ -6,12 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Console} from '../console'; -import {Injectable} from '../di/decorators'; -import {PromiseWrapper} from '../facade/async'; -import {BaseException} from '../facade/exceptions'; -import {Type, isBlank, isString, stringify} from '../facade/lang'; -import {reflector} from '../reflection/reflection'; +import {Type} from '../facade/lang'; import {ComponentFactory} from './component_factory'; /** @@ -19,43 +14,17 @@ import {ComponentFactory} from './component_factory'; * can later be used to create and render a Component instance. * * @deprecated Use {@link ComponentFactoryResolver} together with {@link - * AppModule}.precompile}/{@link Component}.precompile or + * NgModule}.precompile}/{@link Component}.precompile or * {@link ANALYZE_FOR_PRECOMPILE} provider for dynamic component creation. - * Use {@link AppModuleFactoryLoader} for lazy loading. + * Use {@link NgModuleFactoryLoader} for lazy loading. */ export abstract class ComponentResolver { static DynamicCompilationDeprecationMsg = - 'ComponentResolver is deprecated for dynamic compilation. Use ComponentFactoryResolver together with @AppModule/@Component.precompile or ANALYZE_FOR_PRECOMPILE provider instead.'; + 'ComponentResolver is deprecated for dynamic compilation. Use ComponentFactoryResolver together with @NgModule/@Component.precompile or ANALYZE_FOR_PRECOMPILE provider instead. For runtime compile only, you can also use Compiler.compileComponentSync/Async.'; static LazyLoadingDeprecationMsg = - 'ComponentResolver is deprecated for lazy loading. Use AppModuleFactoryLoader instead.'; + 'ComponentResolver is deprecated for lazy loading. Use NgModuleFactoryLoader instead.'; abstract resolveComponent(component: Type|string): Promise>; abstract clearCache(): void; } - -function _isComponentFactory(type: any): boolean { - return type instanceof ComponentFactory; -} - -@Injectable() -export class ReflectorComponentResolver extends ComponentResolver { - constructor(private _console: Console) { super(); } - resolveComponent(component: Type|string): Promise> { - if (isString(component)) { - return PromiseWrapper.reject( - new BaseException(`Cannot resolve component using '${component}'.`), null); - } - this._console.warn(ComponentResolver.DynamicCompilationDeprecationMsg); - - var metadatas = reflector.annotations(component); - var componentFactory = metadatas.find(_isComponentFactory); - - if (isBlank(componentFactory)) { - throw new BaseException(`No precompiled component ${stringify(component)} found`); - } - return PromiseWrapper.resolve(componentFactory); - } - - clearCache() {} -} diff --git a/modules/@angular/core/src/linker/dynamic_component_loader.ts b/modules/@angular/core/src/linker/dynamic_component_loader.ts index 7b58353461..4ac744d41f 100644 --- a/modules/@angular/core/src/linker/dynamic_component_loader.ts +++ b/modules/@angular/core/src/linker/dynamic_component_loader.ts @@ -8,13 +8,15 @@ import {Injectable, Injector, ReflectiveInjector, ResolvedReflectiveProvider} from '../di'; import {Type, isPresent} from '../facade/lang'; + +import {Compiler} from './compiler'; import {ComponentRef} from './component_factory'; -import {ComponentResolver} from './component_resolver'; import {ViewContainerRef} from './view_container_ref'; + /** - * Use ComponentResolver and ViewContainerRef directly. + * Use ComponentFactoryResolver and ViewContainerRef directly. * * @deprecated */ @@ -119,12 +121,12 @@ export abstract class DynamicComponentLoader { @Injectable() export class DynamicComponentLoader_ extends DynamicComponentLoader { - constructor(private _compiler: ComponentResolver) { super(); } + constructor(private _compiler: Compiler) { super(); } loadAsRoot( type: Type, overrideSelectorOrNode: string|any, injector: Injector, onDispose?: () => void, projectableNodes?: any[][]): Promise> { - return this._compiler.resolveComponent(type).then(componentFactory => { + return this._compiler.compileComponentAsync(type).then(componentFactory => { var componentRef = componentFactory.create( injector, projectableNodes, isPresent(overrideSelectorOrNode) ? overrideSelectorOrNode : componentFactory.selector); @@ -138,7 +140,7 @@ export class DynamicComponentLoader_ extends DynamicComponentLoader { loadNextToLocation( type: Type, location: ViewContainerRef, providers: ResolvedReflectiveProvider[] = null, projectableNodes: any[][] = null): Promise> { - return this._compiler.resolveComponent(type).then(componentFactory => { + return this._compiler.compileComponentAsync(type).then(componentFactory => { var contextInjector = location.parentInjector; var childInjector = isPresent(providers) && providers.length > 0 ? ReflectiveInjector.fromResolvedProviders(providers, contextInjector) : diff --git a/modules/@angular/core/src/linker/app_module_factory.ts b/modules/@angular/core/src/linker/ng_module_factory.ts similarity index 75% rename from modules/@angular/core/src/linker/app_module_factory.ts rename to modules/@angular/core/src/linker/ng_module_factory.ts index 9d933163a4..0e2921ee35 100644 --- a/modules/@angular/core/src/linker/app_module_factory.ts +++ b/modules/@angular/core/src/linker/ng_module_factory.ts @@ -14,15 +14,16 @@ import {CodegenComponentFactoryResolver, ComponentFactoryResolver} from './compo /** - * Represents an instance of an AppModule created via a {@link AppModuleFactory}. + * Represents an instance of an NgModule created via a {@link NgModuleFactory}. * - * `AppModuleRef` provides access to the AppModule Instance as well other objects related to this - * AppModule Instance. - * @stable + * `NgModuleRef` provides access to the NgModule Instance as well other objects related to this + * NgModule Instance. + * + * @experimental */ -export abstract class AppModuleRef { +export abstract class NgModuleRef { /** - * The injector that contains all of the providers of the AppModule. + * The injector that contains all of the providers of the NgModule. */ get injector(): Injector { return unimplemented(); } @@ -33,22 +34,22 @@ export abstract class AppModuleRef { get componentFactoryResolver(): ComponentFactoryResolver { return unimplemented(); } /** - * The AppModule instance. + * The NgModule instance. */ get instance(): T { return unimplemented(); } } /** - * @stable + * @experimental */ -export class AppModuleFactory { +export class NgModuleFactory { constructor( - private _injectorClass: {new (parentInjector: Injector): AppModuleInjector}, + private _injectorClass: {new (parentInjector: Injector): NgModuleInjector}, private _moduleype: ConcreteType) {} get moduleType(): ConcreteType { return this._moduleype; } - create(parentInjector: Injector = null): AppModuleRef { + create(parentInjector: Injector): NgModuleRef { if (!parentInjector) { parentInjector = Injector.NULL; } @@ -60,9 +61,9 @@ export class AppModuleFactory { const _UNDEFINED = new Object(); -export abstract class AppModuleInjector extends CodegenComponentFactoryResolver implements +export abstract class NgModuleInjector extends CodegenComponentFactoryResolver implements Injector, - AppModuleRef { + NgModuleRef { public instance: T; constructor(public parent: Injector, factories: ComponentFactory[]) { diff --git a/modules/@angular/core/src/linker/app_module_factory_loader.ts b/modules/@angular/core/src/linker/ng_module_factory_loader.ts similarity index 52% rename from modules/@angular/core/src/linker/app_module_factory_loader.ts rename to modules/@angular/core/src/linker/ng_module_factory_loader.ts index 30f506ceca..83e66a489d 100644 --- a/modules/@angular/core/src/linker/app_module_factory_loader.ts +++ b/modules/@angular/core/src/linker/ng_module_factory_loader.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModuleFactory} from './app_module_factory'; +import {NgModuleFactory} from './ng_module_factory'; /** - * Used to load app moduled factories. + * Used to load ng moduled factories. * @experimental */ -export abstract class AppModuleFactoryLoader { - abstract load(path: string): Promise>; -} \ No newline at end of file +export abstract class NgModuleFactoryLoader { + abstract load(path: string): Promise>; +} diff --git a/modules/@angular/core/src/linker/system_js_app_module_factory_loader.ts b/modules/@angular/core/src/linker/system_js_ng_module_factory_loader.ts similarity index 73% rename from modules/@angular/core/src/linker/system_js_app_module_factory_loader.ts rename to modules/@angular/core/src/linker/system_js_ng_module_factory_loader.ts index f070bc518b..28232be4cf 100644 --- a/modules/@angular/core/src/linker/system_js_app_module_factory_loader.ts +++ b/modules/@angular/core/src/linker/system_js_ng_module_factory_loader.ts @@ -10,9 +10,9 @@ import {Injectable, Optional} from '../di'; import {global} from '../facade/lang'; -import {AppModuleFactory} from './app_module_factory'; -import {AppModuleFactoryLoader} from './app_module_factory_loader'; import {Compiler} from './compiler'; +import {NgModuleFactory} from './ng_module_factory'; +import {NgModuleFactoryLoader} from './ng_module_factory_loader'; const _SEPARATOR = '#'; @@ -20,18 +20,18 @@ const FACTORY_MODULE_SUFFIX = '.ngfactory'; const FACTORY_CLASS_SUFFIX = 'NgFactory'; /** - * AppModuleFactoryLoader that uses SystemJS to load AppModuleFactory + * NgModuleFactoryLoader that uses SystemJS to load NgModuleFactory * @experimental */ @Injectable() -export class SystemJsAppModuleLoader implements AppModuleFactoryLoader { +export class SystemJsNgModuleLoader implements NgModuleFactoryLoader { constructor(@Optional() private _compiler: Compiler) {} - load(path: string): Promise> { + load(path: string): Promise> { return this._compiler ? this.loadAndCompile(path) : this.loadFactory(path); } - private loadAndCompile(path: string): Promise> { + private loadAndCompile(path: string): Promise> { let [module, exportName] = path.split(_SEPARATOR); if (exportName === undefined) exportName = 'default'; @@ -39,10 +39,10 @@ export class SystemJsAppModuleLoader implements AppModuleFactoryLoader { .System.import(module) .then((module: any) => module[exportName]) .then((type: any) => checkNotEmpty(type, module, exportName)) - .then((type: any) => this._compiler.compileAppModuleAsync(type)); + .then((type: any) => this._compiler.compileModuleAsync(type)); } - private loadFactory(path: string): Promise> { + private loadFactory(path: string): Promise> { let [module, exportName] = path.split(_SEPARATOR); if (exportName === undefined) exportName = 'default'; @@ -58,4 +58,4 @@ function checkNotEmpty(value: any, modulePath: string, exportName: string): any throw new Error(`Cannot find '${exportName}' in '${modulePath}'`); } return value; -} \ No newline at end of file +} diff --git a/modules/@angular/core/src/linker/systemjs_component_resolver.ts b/modules/@angular/core/src/linker/systemjs_component_resolver.ts index 6383520792..55edef4e5e 100644 --- a/modules/@angular/core/src/linker/systemjs_component_resolver.ts +++ b/modules/@angular/core/src/linker/systemjs_component_resolver.ts @@ -18,9 +18,9 @@ const _SEPARATOR = '#'; /** * Component resolver that can load components lazily * - * @deprecated Lazy loading of components is deprecated. Use {@link SystemJsAppModuleLoader} to lazy + * @deprecated Lazy loading of components is deprecated. Use {@link SystemJsNgModuleLoader} to lazy * load - * {@link AppModuleFactory}s instead. + * {@link NgModuleFactory}s instead. */ @Injectable() export class SystemJsComponentResolver implements ComponentResolver { @@ -53,9 +53,9 @@ const FACTORY_CLASS_SUFFIX = 'NgFactory'; /** * Component resolver that can load component factories lazily * - * @deprecated Lazy loading of components is deprecated. Use {@link SystemJsAppModuleLoader} + * @deprecated Lazy loading of components is deprecated. Use {@link SystemJsNgModuleLoader} * to lazy - * load {@link AppModuleFactory}s instead. + * load {@link NgModuleFactory}s instead. */ @Injectable() export class SystemJsCmpFactoryResolver implements ComponentResolver { diff --git a/modules/@angular/core/src/metadata.ts b/modules/@angular/core/src/metadata.ts index 1a6b59bd10..7b0f3ebe11 100644 --- a/modules/@angular/core/src/metadata.ts +++ b/modules/@angular/core/src/metadata.ts @@ -14,15 +14,15 @@ import {ChangeDetectionStrategy} from '../src/change_detection/change_detection'; import {AnimationEntryMetadata} from './animation/metadata'; -import {AppModuleMetadata} from './metadata/app_module'; import {AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di'; import {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata} from './metadata/directives'; +import {NgModuleMetadata} from './metadata/ng_module'; import {ViewEncapsulation, ViewMetadata} from './metadata/view'; -export {AppModuleMetadata} from './metadata/app_module'; export {ANALYZE_FOR_PRECOMPILE, AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di'; export {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata} from './metadata/directives'; export {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy, OnInit} from './metadata/lifecycle_hooks'; +export {NgModuleMetadata} from './metadata/ng_module'; export {ViewEncapsulation, ViewMetadata} from './metadata/view'; import {makeDecorator, makeParamDecorator, makePropDecorator, TypeDecorator,} from './util/decorators'; @@ -86,13 +86,13 @@ export interface ViewDecorator extends TypeDecorator { } /** - * Interface for the {@link AppModuleMetadata} decorator function. + * Interface for the {@link NgModuleMetadata} decorator function. * - * See {@link AppModuleMetadataFactory}. + * See {@link NgModuleMetadataFactory}. * * @stable */ -export interface AppModuleDecorator extends TypeDecorator {} +export interface NgModuleDecorator extends TypeDecorator {} /** @@ -490,25 +490,25 @@ export interface HostListenerMetadataFactory { } /** - * {@link AppModuleMetadata} factory for creating annotations, decorators or DSL. + * {@link NgModuleMetadata} factory for creating annotations, decorators or DSL. * - * @stable + * @experimental */ -export interface AppModuleMetadataFactory { - (obj: { +export interface NgModuleMetadataFactory { + (obj?: { providers?: any[], - directives?: Array, - pipes?: Array, - precompile?: Array, - modules?: Array, - }): AppModuleDecorator; - new (obj: { + declarations?: Array, + imports?: Array, + exports?: Array, + precompile?: Array + }): NgModuleDecorator; + new (obj?: { providers?: any[], - directives?: Array, - pipes?: Array, - precompile?: Array, - modules?: Array, - }): AppModuleMetadata; + declarations?: Array, + imports?: Array, + exports?: Array, + precompile?: Array + }): NgModuleMetadata; } // TODO(alexeagle): remove the duplication of this doc. It is copied from ComponentMetadata. @@ -1537,9 +1537,9 @@ export var HostBinding: HostBindingMetadataFactory = makePropDecorator(HostBindi export var HostListener: HostListenerMetadataFactory = makePropDecorator(HostListenerMetadata); /** - * Declares an app module. - * @stable + * Declares an ng module. + * @experimental * @Annotation */ -export var AppModule: AppModuleMetadataFactory = - makeDecorator(AppModuleMetadata); +export var NgModule: NgModuleMetadataFactory = + makeDecorator(NgModuleMetadata); diff --git a/modules/@angular/core/src/metadata/di.ts b/modules/@angular/core/src/metadata/di.ts index 58666a1ded..3fc60fefe5 100644 --- a/modules/@angular/core/src/metadata/di.ts +++ b/modules/@angular/core/src/metadata/di.ts @@ -13,13 +13,13 @@ import {StringWrapper, Type, isString, stringify} from '../facade/lang'; /** * This token can be used to create a virtual provider that will populate the - * `precompile` fields of components and app modules based on its `useValue`. + * `precompile` fields of components and ng modules based on its `useValue`. * All components that are referenced in the `useValue` value (either directly * or in a nested array or map) will be added to the `precompile` property. * * ### Example * The following example shows how the router can populate the `precompile` - * field of an AppModule based on the router configuration which refers + * field of an NgModule based on the router configuration which refers * to components. * * ```typescript @@ -37,7 +37,7 @@ import {StringWrapper, Type, isString, stringify} from '../facade/lang'; * {path: /teams', component: TeamsComp} * ]; * - * @AppModule({ + * @NgModule({ * providers: [provideRoutes(routes)] * }) * class ModuleWithRoutes {} diff --git a/modules/@angular/core/src/metadata/app_module.ts b/modules/@angular/core/src/metadata/ng_module.ts similarity index 56% rename from modules/@angular/core/src/metadata/app_module.ts rename to modules/@angular/core/src/metadata/ng_module.ts index c707f67558..a08e90104d 100644 --- a/modules/@angular/core/src/metadata/app_module.ts +++ b/modules/@angular/core/src/metadata/ng_module.ts @@ -10,10 +10,10 @@ import {InjectableMetadata} from '../di/metadata'; import {Type} from '../facade/lang'; /** - * Declares an Application Module. - * @stable + * Declares an Angular Module. + * @experimental */ -export class AppModuleMetadata extends InjectableMetadata { +export class NgModuleMetadata extends InjectableMetadata { /** * Defines the set of injectable objects that are available in the injector * of this module. @@ -29,7 +29,7 @@ export class AppModuleMetadata extends InjectableMetadata { * } * } * - * @AppModule({ + * @NgModule({ * providers: [ * Greeter * ] @@ -48,36 +48,52 @@ export class AppModuleMetadata extends InjectableMetadata { /** - * Specifies a list of directives that can be used within the template - * of any component that is part of this application module. + * Specifies a list of directives/pipes that belong to this module. * * ### Example * * ```javascript - * @AppModule({ - * directives: [NgFor] + * @NgModule({ + * declarations: [NgFor] * }) - * class MyAppModule { + * class CommonModule { * } * ``` */ - directives: Array; + declarations: Array; /** - * Specifies a list of pipes that can be used within the template - * of any component that is part of this application module. + * Specifies a list of modules whose exported directives/pipes + * should be available to templates in this module. * * ### Example * * ```javascript - * @AppModule({ - * pipes: [SomePipe] + * @NgModule({ + * imports: [CommonModule] * }) - * class MyAppModule { + * class MainModule { * } * ``` */ - pipes: Array; + imports: Array; + + /** + * Specifies a list of directives/pipes/module that can be used within the template + * of any component that is part of an angular module + * that imports this angular module. + * + * ### Example + * + * ```javascript + * @NgModule({ + * exports: [NgFor] + * }) + * class CommonModule { + * } + * ``` + */ + exports: Array; /** * Defines the components that should be precompiled as well when @@ -87,27 +103,18 @@ export class AppModuleMetadata extends InjectableMetadata { */ precompile: Array; - /** - * Defines modules that should be included into this module. - * The providers / directives / pipes / precompile entries will be added - * to this module. - * Just like the main module, the modules listed here are also eagerly - * created and accessible via DI. - */ - modules: Array; - - constructor({providers, directives, pipes, precompile, modules}: { + constructor({providers, declarations, imports, exports, precompile}: { providers?: any[], - directives?: Array, - pipes?: Array, - precompile?: Array, - modules?: Array + declarations?: Array, + imports?: Array, + exports?: Array, + precompile?: Array } = {}) { super(); this._providers = providers; - this.directives = directives; - this.pipes = pipes; + this.declarations = declarations; + this.imports = imports; + this.exports = exports; this.precompile = precompile; - this.modules = modules; } } diff --git a/modules/@angular/core/src/platform_common_providers.ts b/modules/@angular/core/src/platform_core_providers.ts similarity index 51% rename from modules/@angular/core/src/platform_common_providers.ts rename to modules/@angular/core/src/platform_core_providers.ts index 1915b0e027..4d01845e7f 100644 --- a/modules/@angular/core/src/platform_common_providers.ts +++ b/modules/@angular/core/src/platform_core_providers.ts @@ -8,7 +8,7 @@ import {Type} from '../src/facade/lang'; -import {PLATFORM_CORE_PROVIDERS} from './application_ref'; +import {PlatformRef, PlatformRef_, createPlatformFactory} from './application_ref'; import {Console} from './console'; import {Provider} from './di'; import {Reflector, reflector} from './reflection/reflection'; @@ -21,13 +21,22 @@ function _reflector(): Reflector { var __unused: Type; // prevent missing use Dart warning. +const _CORE_PLATFORM_PROVIDERS: Array = [ + PlatformRef_, {provide: PlatformRef, useExisting: PlatformRef_}, + {provide: Reflector, useFactory: _reflector, deps: []}, + {provide: ReflectorReader, useExisting: Reflector}, TestabilityRegistry, Console +]; + /** - * A default set of providers which should be included in any Angular platform. + * This platform has to be included in any other platform + * * @experimental */ -export const PLATFORM_COMMON_PROVIDERS: Array = /*@ts2dart_const*/[ - PLATFORM_CORE_PROVIDERS, - /*@ts2dart_Provider*/ {provide: Reflector, useFactory: _reflector, deps: []}, - /*@ts2dart_Provider*/ {provide: ReflectorReader, useExisting: Reflector}, TestabilityRegistry, - Console -]; +export const corePlatform = createPlatformFactory(null, 'core', _CORE_PLATFORM_PROVIDERS); + +/** + * A default set of providers which should be included in any Angular platform. + * + * @deprecated Create platforms via `createPlatformFactory(corePlatform, ...) instead! + */ +export const PLATFORM_COMMON_PROVIDERS = _CORE_PLATFORM_PROVIDERS; diff --git a/modules/@angular/core/src/platform_directives_and_pipes.ts b/modules/@angular/core/src/platform_directives_and_pipes.ts index 984c749923..7aca5cf9bb 100644 --- a/modules/@angular/core/src/platform_directives_and_pipes.ts +++ b/modules/@angular/core/src/platform_directives_and_pipes.ts @@ -34,7 +34,7 @@ import {OpaqueToken} from './di'; * ``` * * @deprecated Providing platform directives via a provider is deprecated. Provide platform - * directives via an {@link AppModule} instead. + * directives via an {@link NgModule} instead. */ export const PLATFORM_DIRECTIVES: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken('Platform Directives'); @@ -63,6 +63,6 @@ export const PLATFORM_DIRECTIVES: OpaqueToken = * ``` * * @deprecated Providing platform pipes via a provider is deprecated. Provide platform pipes via an - * {@link AppModule} instead. + * {@link NgModule} instead. */ -export const PLATFORM_PIPES: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken('Platform Pipes'); \ No newline at end of file +export const PLATFORM_PIPES: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken('Platform Pipes'); diff --git a/modules/@angular/core/test/application_ref_spec.ts b/modules/@angular/core/test/application_ref_spec.ts index dad14d0d57..d359ae91c3 100644 --- a/modules/@angular/core/test/application_ref_spec.ts +++ b/modules/@angular/core/test/application_ref_spec.ts @@ -8,8 +8,10 @@ import {AsyncTestCompleter, ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal'; import {SpyChangeDetectorRef} from './spies'; -import {ApplicationRef_, ApplicationRef, PLATFORM_CORE_PROVIDERS, APPLICATION_CORE_PROVIDERS} from '@angular/core/src/application_ref'; -import {Type, Injector, APP_INITIALIZER, Component, ReflectiveInjector, coreLoadAndBootstrap, PlatformRef, createPlatform, disposePlatform, ComponentResolver, ComponentFactoryResolver, ChangeDetectorRef} from '@angular/core'; +import {ConcreteType} from '../src/facade/lang'; +import {ApplicationRef_, ApplicationRef} from '@angular/core/src/application_ref'; +import {Type, NgModule, CompilerFactory, Injector, APP_INITIALIZER, Component, ReflectiveInjector, bootstrapModule, bootstrapModuleFactory, PlatformRef, disposePlatform, createPlatformFactory, ComponentResolver, ComponentFactoryResolver, ChangeDetectorRef, ApplicationModule} from '@angular/core'; +import {coreDynamicPlatform} from '@angular/compiler'; import {Console} from '@angular/core/src/console'; import {BaseException} from '../src/facade/exceptions'; import {PromiseWrapper, PromiseCompleter, TimerWrapper} from '../src/facade/async'; @@ -18,36 +20,48 @@ import {ExceptionHandler} from '../src/facade/exception_handler'; export function main() { describe('bootstrap', () => { - var platform: PlatformRef; + var defaultPlatform: PlatformRef; var errorLogger: _ArrayLogger; var someCompFactory: ComponentFactory; + var appProviders: any[]; beforeEach(() => { errorLogger = new _ArrayLogger(); disposePlatform(); - platform = createPlatform(ReflectiveInjector.resolveAndCreate(PLATFORM_CORE_PROVIDERS)); + defaultPlatform = createPlatformFactory(coreDynamicPlatform, 'test')(); someCompFactory = new _MockComponentFactory(new _MockComponentRef(ReflectiveInjector.resolveAndCreate([]))); + appProviders = [ + {provide: Console, useValue: new _MockConsole()}, + {provide: ExceptionHandler, useValue: new ExceptionHandler(errorLogger, false)}, + {provide: ComponentResolver, useValue: new _MockComponentResolver(someCompFactory)} + ]; }); afterEach(() => { disposePlatform(); }); - function createApplication(providers: any[]): ApplicationRef_ { - var appInjector = ReflectiveInjector.resolveAndCreate( - [ - APPLICATION_CORE_PROVIDERS, {provide: Console, useValue: new _MockConsole()}, - {provide: ExceptionHandler, useValue: new ExceptionHandler(errorLogger, false)}, - {provide: ComponentResolver, useValue: new _MockComponentResolver(someCompFactory)}, - {provide: ComponentFactoryResolver, useValue: ComponentFactoryResolver.NULL}, providers - ], - platform.injector); + function createModule(providers: any[] = []): ConcreteType { + @NgModule({providers: [appProviders, providers], imports: [ApplicationModule]}) + class MyModule { + } + + return MyModule; + } + + function createApplication( + providers: any[] = [], platform: PlatformRef = defaultPlatform): ApplicationRef_ { + const compilerFactory: CompilerFactory = platform.injector.get(CompilerFactory); + const compiler = compilerFactory.createCompiler(); + const appInjector = + bootstrapModuleFactory(compiler.compileModuleSync(createModule(providers)), platform) + .injector; return appInjector.get(ApplicationRef); } describe('ApplicationRef', () => { it('should throw when reentering tick', () => { var cdRef = new SpyChangeDetectorRef(); - var ref = createApplication([]); + var ref = createApplication(); try { ref.registerChangeDetector(cdRef); cdRef.spy('detectChanges').andCallFake(() => ref.tick()); @@ -59,14 +73,14 @@ export function main() { describe('run', () => { it('should rethrow errors even if the exceptionHandler is not rethrowing', () => { - var ref = createApplication([]); + var ref = createApplication(); expect(() => ref.run(() => { throw new BaseException('Test'); })).toThrowError('Test'); }); it('should return a promise with rejected errors even if the exceptionHandler is not rethrowing', inject( [AsyncTestCompleter, Injector], (async: AsyncTestCompleter, injector: Injector) => { - var ref = createApplication([]); + var ref = createApplication(); var promise = ref.run(() => PromiseWrapper.reject('Test', null)); PromiseWrapper.catchError(promise, (e) => { expect(e).toEqual('Test'); @@ -76,7 +90,7 @@ export function main() { }); }); - describe('coreLoadAndBootstrap', () => { + describe('bootstrapModule', () => { it('should wait for asynchronous app initializers', inject([AsyncTestCompleter, Injector], (async: AsyncTestCompleter, injector: Injector) => { let completer: PromiseCompleter = PromiseWrapper.completer(); @@ -85,16 +99,19 @@ export function main() { completer.resolve(true); initializerDone = true; }, 1); - var app = createApplication( - [{provide: APP_INITIALIZER, useValue: () => completer.promise, multi: true}]); - coreLoadAndBootstrap(MyComp6, app.injector).then(_ => { - expect(initializerDone).toBe(true); - async.done(); - }); + + bootstrapModule( + createModule( + [{provide: APP_INITIALIZER, useValue: () => completer.promise, multi: true}]), + defaultPlatform) + .then(_ => { + expect(initializerDone).toBe(true); + async.done(); + }); })); }); - describe('coreBootstrap', () => { + describe('ApplicationRef.bootstrap', () => { it('should throw if an APP_INITIIALIZER is not yet resolved', inject([Injector], (injector: Injector) => { var app = createApplication([{ diff --git a/modules/@angular/core/test/fake_async_spec.ts b/modules/@angular/core/test/fake_async_spec.ts index 9c1ec74eff..09b3231766 100644 --- a/modules/@angular/core/test/fake_async_spec.ts +++ b/modules/@angular/core/test/fake_async_spec.ts @@ -87,14 +87,13 @@ export function main() { expect(log.result()).toEqual('1; 2'); })); - // TODO(vicb): check why this doesn't work in JS - linked to open issues on GH ? - xit('should complain if the test throws an exception during async calls', () => { + it('should complain if the test throws an exception during async calls', () => { expect(() => { fakeAsync(() => { PromiseWrapper.resolve(null).then((_) => { throw new BaseException('async'); }); flushMicrotasks(); })(); - }).toThrowError('async'); + }).toThrowError('Uncaught (in promise): async'); }); it('should complain if a test throws an exception', () => { diff --git a/modules/@angular/core/test/linker/app_module_integration_spec.ts b/modules/@angular/core/test/linker/app_module_integration_spec.ts deleted file mode 100644 index 67d0f2b76e..0000000000 --- a/modules/@angular/core/test/linker/app_module_integration_spec.ts +++ /dev/null @@ -1,557 +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 {ANALYZE_FOR_PRECOMPILE, AppModule, AppModuleMetadata, Compiler, Component, ComponentFactoryResolver, ComponentRef, ComponentResolver, DebugElement, Directive, Host, Inject, Injectable, Injector, Input, OpaqueToken, Optional, Pipe, Provider, ReflectiveInjector, SelfMetadata, SkipSelf, SkipSelfMetadata, forwardRef, getDebugNode, provide} from '@angular/core'; -import {ComponentFixture, configureCompiler} from '@angular/core/testing'; -import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; -import {expect} from '@angular/platform-browser/testing/matchers'; - -import {BaseException} from '../../src/facade/exceptions'; -import {ConcreteType, IS_DART, Type, stringify} from '../../src/facade/lang'; - -class Engine {} - -class BrokenEngine { - constructor() { throw new BaseException('Broken Engine'); } -} - -class DashboardSoftware {} - -@Injectable() -class Dashboard { - constructor(software: DashboardSoftware) {} -} - -class TurboEngine extends Engine {} - -@Injectable() -class Car { - engine: Engine; - constructor(engine: Engine) { this.engine = engine; } -} - -@Injectable() -class CarWithOptionalEngine { - engine: Engine; - constructor(@Optional() engine: Engine) { this.engine = engine; } -} - -@Injectable() -class CarWithDashboard { - engine: Engine; - dashboard: Dashboard; - constructor(engine: Engine, dashboard: Dashboard) { - this.engine = engine; - this.dashboard = dashboard; - } -} - -@Injectable() -class SportsCar extends Car { - engine: Engine; - constructor(engine: Engine) { super(engine); } -} - -@Injectable() -class CarWithInject { - engine: Engine; - constructor(@Inject(TurboEngine) engine: Engine) { this.engine = engine; } -} - -@Injectable() -class CyclicEngine { - constructor(car: Car) {} -} - -class NoAnnotations { - constructor(secretDependency: any) {} -} - -function factoryFn(a: any) {} - -@Injectable() -class SomeService { -} - -@AppModule({}) -class SomeModule { -} - -@AppModule({providers: [{provide: 'someToken', useValue: 'someValue'}]}) -class ModuleWithProvider { -} - -@Component({selector: 'comp', template: ''}) -class SomeComp { -} - -@AppModule({precompile: [SomeComp]}) -class ModuleWithPrecompile { -} - -@AppModule({ - providers: - [{provide: ANALYZE_FOR_PRECOMPILE, multi: true, useValue: [{a: 'b', component: SomeComp}]}] -}) -class ModuleWithAnalyzePrecompileProvider { -} - -@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) -class SomeDirective { - @Input() - someDir: string; -} - -@Pipe({name: 'somePipe'}) -class SomePipe { - transform(value: string): any { return `transformed ${value}`; } -} - -@Component({selector: 'comp', template: `
`}) -class CompUsingModuleDirectiveAndPipe { -} - -@Component( - {selector: 'parent', template: ``, directives: [CompUsingModuleDirectiveAndPipe]}) -class ParentCompUsingModuleDirectiveAndPipe { -} - -@AppModule( - {directives: [SomeDirective], pipes: [SomePipe], precompile: [CompUsingModuleDirectiveAndPipe]}) -class ModuleWithDirectivesAndPipes { -} - -export function main() { - if (IS_DART) { - declareTests({useJit: false}); - } else { - describe('jit', () => { declareTests({useJit: true}); }); - - describe('no jit', () => { declareTests({useJit: false}); }); - } -} - -function declareTests({useJit}: {useJit: boolean}) { - describe('AppModule', () => { - var compiler: Compiler; - var injector: Injector; - - beforeEach(() => { configureCompiler({useJit: useJit}); }); - - beforeEach(inject([Compiler, Injector], (_compiler: Compiler, _injector: Injector) => { - compiler = _compiler; - injector = _injector; - })); - - describe('precompile', function() { - it('should resolve ComponentFactories', () => { - let appModule = compiler.compileAppModuleSync(ModuleWithPrecompile).create(); - expect(appModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) - .toBe(SomeComp); - expect(appModule.injector.get(ComponentFactoryResolver) - .resolveComponentFactory(SomeComp) - .componentType) - .toBe(SomeComp); - }); - - it('should resolve ComponentFactories via ANALYZE_FOR_PRECOMPILE', () => { - let appModule = compiler.compileAppModuleSync(ModuleWithAnalyzePrecompileProvider).create(); - expect(appModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) - .toBe(SomeComp); - expect(appModule.injector.get(ComponentFactoryResolver) - .resolveComponentFactory(SomeComp) - .componentType) - .toBe(SomeComp); - }); - - it('should resolve ComponentFactories for nested modules', () => { - let appModule = - compiler - .compileAppModuleSync( - SomeModule, new AppModuleMetadata({modules: [ModuleWithPrecompile]})) - .create(); - expect(appModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) - .toBe(SomeComp); - expect(appModule.injector.get(ComponentFactoryResolver) - .resolveComponentFactory(SomeComp) - .componentType) - .toBe(SomeComp); - }); - }); - - describe('directives and pipes', () => { - function createComp( - compType: ConcreteType, moduleType: ConcreteType, - moduleMeta: AppModuleMetadata = null): ComponentFixture { - let appModule = compiler.compileAppModuleSync(moduleType, moduleMeta).create(); - var cf = appModule.componentFactoryResolver.resolveComponentFactory(compType); - return new ComponentFixture(cf.create(injector), null, false); - } - - it('should support module directives and pipes', () => { - let compFixture = createComp(CompUsingModuleDirectiveAndPipe, ModuleWithDirectivesAndPipes); - compFixture.detectChanges(); - expect(compFixture.debugElement.children[0].properties['title']) - .toBe('transformed someValue'); - }); - - it('should support module directives and pipes for nested modules', () => { - let compFixture = createComp( - CompUsingModuleDirectiveAndPipe, SomeModule, - new AppModuleMetadata({modules: [ModuleWithDirectivesAndPipes]})); - compFixture.detectChanges(); - expect(compFixture.debugElement.children[0].properties['title']) - .toBe('transformed someValue'); - }); - - it('should support module directives and pipes in nested components', () => { - let compFixture = - createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule, new AppModuleMetadata({ - directives: [SomeDirective], - pipes: [SomePipe], - precompile: [ParentCompUsingModuleDirectiveAndPipe] - })); - compFixture.detectChanges(); - expect(compFixture.debugElement.children[0].children[0].properties['title']) - .toBe('transformed someValue'); - }); - - it('should provide a Compiler instance that uses the directives/pipes of the module', () => { - let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes).create(); - let boundCompiler: Compiler = appModule.injector.get(Compiler); - var cf = boundCompiler.compileComponentSync(CompUsingModuleDirectiveAndPipe); - let compFixture = new ComponentFixture(cf.create(injector), null, false); - compFixture.detectChanges(); - expect(compFixture.debugElement.children[0].properties['title']) - .toBe('transformed someValue'); - }); - - it('should provide a ComponentResolver instance that uses the directives/pipes of the module', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - - let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes).create(); - let boundCompiler: ComponentResolver = appModule.injector.get(ComponentResolver); - boundCompiler.resolveComponent(CompUsingModuleDirectiveAndPipe).then((cf) => { - let compFixture = new ComponentFixture(cf.create(injector), null, false); - compFixture.detectChanges(); - expect(compFixture.debugElement.children[0].properties['title']) - .toBe('transformed someValue'); - async.done(); - }); - })); - - it('should provide a ComponentResolver instance that delegates to the parent ComponentResolver for strings', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - let parentResolver: any = - jasmine.createSpyObj('resolver', ['resolveComponent', 'clearCache']); - let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes) - .create(ReflectiveInjector.resolveAndCreate( - [{provide: ComponentResolver, useValue: parentResolver}])); - parentResolver.resolveComponent.and.returnValue( - Promise.resolve('someFactoryFromParent')); - let boundCompiler: ComponentResolver = appModule.injector.get(ComponentResolver); - boundCompiler.resolveComponent('someString').then((result) => { - expect(parentResolver.resolveComponent).toHaveBeenCalledWith('someString'); - expect(result).toBe('someFactoryFromParent'); - async.done(); - }); - })); - }); - - describe('providers', function() { - function createInjector(providers: any[], parent: Injector = null): Injector { - return compiler - .compileAppModuleSync(SomeModule, new AppModuleMetadata({providers: providers})) - .create(parent) - .injector; - } - - it('should provide the module', - () => { expect(createInjector([]).get(SomeModule)).toBeAnInstanceOf(SomeModule); }); - - it('should instantiate a class without dependencies', () => { - var injector = createInjector([Engine]); - var engine = injector.get(Engine); - - expect(engine).toBeAnInstanceOf(Engine); - }); - - it('should resolve dependencies based on type information', () => { - var injector = createInjector([Engine, Car]); - var car = injector.get(Car); - - expect(car).toBeAnInstanceOf(Car); - expect(car.engine).toBeAnInstanceOf(Engine); - }); - - it('should resolve dependencies based on @Inject annotation', () => { - var injector = createInjector([TurboEngine, Engine, CarWithInject]); - var car = injector.get(CarWithInject); - - expect(car).toBeAnInstanceOf(CarWithInject); - expect(car.engine).toBeAnInstanceOf(TurboEngine); - }); - - it('should throw when no type and not @Inject (class case)', () => { - expect(() => createInjector([NoAnnotations])) - .toThrowError('Can\'t resolve all parameters for NoAnnotations: (?).'); - }); - - it('should throw when no type and not @Inject (factory case)', () => { - expect(() => createInjector([provide('someToken', {useFactory: factoryFn})])) - .toThrowError('Can\'t resolve all parameters for factoryFn: (?).'); - }); - - it('should cache instances', () => { - var injector = createInjector([Engine]); - - var e1 = injector.get(Engine); - var e2 = injector.get(Engine); - - expect(e1).toBe(e2); - }); - - it('should provide to a value', () => { - var injector = createInjector([provide(Engine, {useValue: 'fake engine'})]); - - var engine = injector.get(Engine); - expect(engine).toEqual('fake engine'); - }); - - it('should provide to a factory', () => { - function sportsCarFactory(e: Engine) { return new SportsCar(e); } - - var injector = - createInjector([Engine, provide(Car, {useFactory: sportsCarFactory, deps: [Engine]})]); - - var car = injector.get(Car); - expect(car).toBeAnInstanceOf(SportsCar); - expect(car.engine).toBeAnInstanceOf(Engine); - }); - - it('should supporting provider to null', () => { - var injector = createInjector([provide(Engine, {useValue: null})]); - var engine = injector.get(Engine); - expect(engine).toBeNull(); - }); - - it('should provide to an alias', () => { - var injector = createInjector([ - Engine, provide(SportsCar, {useClass: SportsCar}), - provide(Car, {useExisting: SportsCar}) - ]); - - var car = injector.get(Car); - var sportsCar = injector.get(SportsCar); - expect(car).toBeAnInstanceOf(SportsCar); - expect(car).toBe(sportsCar); - }); - - it('should support multiProviders', () => { - var injector = createInjector([ - Engine, provide(Car, {useClass: SportsCar, multi: true}), - provide(Car, {useClass: CarWithOptionalEngine, multi: true}) - ]); - - var cars = injector.get(Car); - expect(cars.length).toEqual(2); - expect(cars[0]).toBeAnInstanceOf(SportsCar); - expect(cars[1]).toBeAnInstanceOf(CarWithOptionalEngine); - }); - - it('should support multiProviders that are created using useExisting', () => { - var injector = createInjector( - [Engine, SportsCar, provide(Car, {useExisting: SportsCar, multi: true})]); - - var cars = injector.get(Car); - expect(cars.length).toEqual(1); - expect(cars[0]).toBe(injector.get(SportsCar)); - }); - - it('should throw when the aliased provider does not exist', () => { - var injector = createInjector([provide('car', {useExisting: SportsCar})]); - var e = `No provider for ${stringify(SportsCar)}!`; - expect(() => injector.get('car')).toThrowError(e); - }); - - it('should handle forwardRef in useExisting', () => { - var injector = createInjector([ - provide('originalEngine', {useClass: forwardRef(() => Engine)}), - provide('aliasedEngine', {useExisting: forwardRef(() => 'originalEngine')}) - ]); - expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine); - }); - - it('should support overriding factory dependencies', () => { - var injector = createInjector( - [Engine, provide(Car, {useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]})]); - - var car = injector.get(Car); - expect(car).toBeAnInstanceOf(SportsCar); - expect(car.engine).toBeAnInstanceOf(Engine); - }); - - it('should support optional dependencies', () => { - var injector = createInjector([CarWithOptionalEngine]); - - var car = injector.get(CarWithOptionalEngine); - expect(car.engine).toEqual(null); - }); - - it('should flatten passed-in providers', () => { - var injector = createInjector([[[Engine, Car]]]); - - var car = injector.get(Car); - expect(car).toBeAnInstanceOf(Car); - }); - - it('should use the last provider when there are multiple providers for same token', () => { - var injector = createInjector( - [provide(Engine, {useClass: Engine}), provide(Engine, {useClass: TurboEngine})]); - - expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine); - }); - - it('should use non-type tokens', () => { - var injector = createInjector([provide('token', {useValue: 'value'})]); - - expect(injector.get('token')).toEqual('value'); - }); - - it('should throw when given invalid providers', () => { - expect(() => createInjector(['blah'])) - .toThrowError( - 'Invalid provider - only instances of Provider and Type are allowed, got: blah'); - }); - - it('should provide itself', () => { - var parent = createInjector([]); - var child = createInjector([], parent); - - expect(child.get(Injector)).toBe(child); - }); - - it('should throw when no provider defined', () => { - var injector = createInjector([]); - expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!'); - }); - - it('should throw when trying to instantiate a cyclic dependency', () => { - expect(() => createInjector([Car, provide(Engine, {useClass: CyclicEngine})])) - .toThrowError(/Cannot instantiate cyclic dependency! Car/g); - }); - - it('should support null values', () => { - var injector = createInjector([provide('null', {useValue: null})]); - expect(injector.get('null')).toBe(null); - }); - - - describe('child', () => { - it('should load instances from parent injector', () => { - var parent = createInjector([Engine]); - var child = createInjector([], parent); - - var engineFromParent = parent.get(Engine); - var engineFromChild = child.get(Engine); - - expect(engineFromChild).toBe(engineFromParent); - }); - - it('should not use the child providers when resolving the dependencies of a parent provider', - () => { - var parent = createInjector([Car, Engine]); - var child = createInjector([provide(Engine, {useClass: TurboEngine})], parent); - - var carFromChild = child.get(Car); - expect(carFromChild.engine).toBeAnInstanceOf(Engine); - }); - - it('should create new instance in a child injector', () => { - var parent = createInjector([Engine]); - var child = createInjector([provide(Engine, {useClass: TurboEngine})], parent); - - var engineFromParent = parent.get(Engine); - var engineFromChild = child.get(Engine); - - expect(engineFromParent).not.toBe(engineFromChild); - expect(engineFromChild).toBeAnInstanceOf(TurboEngine); - }); - - }); - - describe('depedency resolution', () => { - describe('@Self()', () => { - it('should return a dependency from self', () => { - var inj = createInjector([ - Engine, - provide( - Car, - {useFactory: (e: Engine) => new Car(e), deps: [[Engine, new SelfMetadata()]]}) - ]); - - expect(inj.get(Car)).toBeAnInstanceOf(Car); - }); - - it('should throw when not requested provider on self', () => { - expect(() => createInjector([provide(Car, { - useFactory: (e: Engine) => new Car(e), - deps: [[Engine, new SelfMetadata()]] - })])) - .toThrowError(/No provider for Engine/g); - }); - }); - - describe('default', () => { - it('should not skip self', () => { - var parent = createInjector([Engine]); - var child = createInjector( - [ - provide(Engine, {useClass: TurboEngine}), - provide(Car, {useFactory: (e: Engine) => new Car(e), deps: [Engine]}) - ], - parent); - - expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine); - }); - }); - }); - - describe('nested modules', () => { - it('should merge the providers of nested modules', () => { - var injector = - compiler - .compileAppModuleSync(SomeModule, new AppModuleMetadata({ - providers: [{provide: 'a', useValue: 'aValue'}], - modules: [ModuleWithProvider] - })) - .create() - .injector; - expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule); - expect(injector.get(ModuleWithProvider)).toBeAnInstanceOf(ModuleWithProvider); - expect(injector.get('a')).toBe('aValue'); - expect(injector.get('someToken')).toBe('someValue'); - }); - - it('should override the providers of nested modules', () => { - var injector = compiler - .compileAppModuleSync( - SomeModule, new AppModuleMetadata({ - providers: [{provide: 'someToken', useValue: 'someNewValue'}], - modules: [ModuleWithProvider] - })) - .create() - .injector; - expect(injector.get('someToken')).toBe('someNewValue'); - - }); - }); - - }); - }); -} diff --git a/modules/@angular/core/test/linker/integration_spec.ts b/modules/@angular/core/test/linker/integration_spec.ts index 01a3186b1a..8f5a177dca 100644 --- a/modules/@angular/core/test/linker/integration_spec.ts +++ b/modules/@angular/core/test/linker/integration_spec.ts @@ -1775,17 +1775,6 @@ function declareTests({useJit}: {useJit: boolean}) { }); describe('logging property updates', () => { - beforeEach(() => { - configureCompiler({ - providers: [{ - provide: CompilerConfig, - // Note: we are testing the `genDebugInfo` flag here, so we - // need to set it explicitely! - useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit}) - }] - }); - }); - it('should reflect property values as attributes', inject( [TestComponentBuilder, AsyncTestCompleter], diff --git a/modules/@angular/core/test/linker/ng_module_integration_spec.ts b/modules/@angular/core/test/linker/ng_module_integration_spec.ts new file mode 100644 index 0000000000..2e10f50930 --- /dev/null +++ b/modules/@angular/core/test/linker/ng_module_integration_spec.ts @@ -0,0 +1,1038 @@ +/** + * @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 {LowerCasePipe, NgIf} from '@angular/common'; +import {CompilerConfig, NgModuleResolver, ViewResolver} from '@angular/compiler'; +import {MockNgModuleResolver, MockViewResolver} from '@angular/compiler/testing'; +import {ANALYZE_FOR_PRECOMPILE, Compiler, Component, ComponentFactoryResolver, ComponentRef, ComponentResolver, DebugElement, Directive, Host, HostBinding, Inject, Injectable, Injector, Input, NgModule, NgModuleMetadata, NgModuleRef, OpaqueToken, Optional, Pipe, Provider, ReflectiveInjector, SelfMetadata, SkipSelf, SkipSelfMetadata, ViewMetadata, forwardRef, getDebugNode, provide} from '@angular/core'; +import {Console} from '@angular/core/src/console'; +import {ComponentFixture, configureCompiler} from '@angular/core/testing'; +import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {expect} from '@angular/platform-browser/testing/matchers'; + +import {BaseException} from '../../src/facade/exceptions'; +import {ConcreteType, IS_DART, Type, stringify} from '../../src/facade/lang'; + +class Engine {} + +class BrokenEngine { + constructor() { throw new BaseException('Broken Engine'); } +} + +class DashboardSoftware {} + +@Injectable() +class Dashboard { + constructor(software: DashboardSoftware) {} +} + +class TurboEngine extends Engine {} + +@Injectable() +class Car { + engine: Engine; + constructor(engine: Engine) { this.engine = engine; } +} + +@Injectable() +class CarWithOptionalEngine { + engine: Engine; + constructor(@Optional() engine: Engine) { this.engine = engine; } +} + +@Injectable() +class CarWithDashboard { + engine: Engine; + dashboard: Dashboard; + constructor(engine: Engine, dashboard: Dashboard) { + this.engine = engine; + this.dashboard = dashboard; + } +} + +@Injectable() +class SportsCar extends Car { + engine: Engine; + constructor(engine: Engine) { super(engine); } +} + +@Injectable() +class CarWithInject { + engine: Engine; + constructor(@Inject(TurboEngine) engine: Engine) { this.engine = engine; } +} + +@Injectable() +class CyclicEngine { + constructor(car: Car) {} +} + +class NoAnnotations { + constructor(secretDependency: any) {} +} + +function factoryFn(a: any) {} + +@Component({selector: 'comp', template: ''}) +class SomeComp { +} + +@Directive({selector: '[someDir]'}) +class SomeDirective { + @HostBinding('title') @Input() + someDir: string; +} + +@Pipe({name: 'somePipe'}) +class SomePipe { + transform(value: string): any { return `transformed ${value}`; } +} + +@Component({selector: 'comp', template: `
`}) +class CompUsingModuleDirectiveAndPipe { +} + +class DummyConsole implements Console { + public warnings: string[] = []; + + log(message: string) {} + warn(message: string) { this.warnings.push(message); } +} + +export function main() { + if (IS_DART) { + declareTests({useJit: false}); + } else { + describe('jit', () => { declareTests({useJit: true}); }); + + describe('no jit', () => { declareTests({useJit: false}); }); + } +} + +function declareTests({useJit}: {useJit: boolean}) { + describe('NgModule', () => { + var compiler: Compiler; + var injector: Injector; + var console: DummyConsole; + + beforeEach(() => { + console = new DummyConsole(); + configureCompiler({useJit: useJit, providers: [{provide: Console, useValue: console}]}); + }); + + beforeEach( + inject([Compiler, Injector, ViewResolver], (_compiler: Compiler, _injector: Injector) => { + compiler = _compiler; + injector = _injector; + })); + + function createModule( + moduleType: ConcreteType, parentInjector: Injector = null): NgModuleRef { + return compiler.compileModuleSync(moduleType).create(parentInjector); + } + + function createComp( + compType: ConcreteType, moduleType: ConcreteType): ComponentFixture { + let ngModule = createModule(moduleType); + var cf = ngModule.componentFactoryResolver.resolveComponentFactory(compType); + return new ComponentFixture(cf.create(injector), null, false); + } + + describe('errors', () => { + it('should error when exporting a directive that was neither declared nor imported', () => { + @NgModule({exports: [SomeDirective]}) + class SomeModule { + } + + expect(() => createModule(SomeModule)) + .toThrowError( + `Can't export directive ${stringify(SomeDirective)} from ${stringify(SomeModule)} as it was neither declared nor imported!`); + }); + + it('should error when exporting a pipe that was neither declared nor imported', () => { + @NgModule({exports: [SomePipe]}) + class SomeModule { + } + + expect(() => createModule(SomeModule)) + .toThrowError( + `Can't export pipe ${stringify(SomePipe)} from ${stringify(SomeModule)} as it was neither declared nor imported!`); + }); + + it('should error when precompiling a component that was neither declared nor imported', () => { + @NgModule({precompile: [SomeComp]}) + class SomeModule { + } + + expect(() => createModule(SomeModule)) + .toThrowError( + `NgModule ${stringify(SomeModule)} uses ${stringify(SomeComp)} via "precompile" but it was neither declared nor imported!`); + }); + + it('should error if a directive is declared in more than 1 module', () => { + @NgModule({declarations: [SomeDirective]}) + class Module1 { + } + + @NgModule({declarations: [SomeDirective]}) + class Module2 { + } + + createModule(Module1); + + expect(() => createModule(Module2)) + .toThrowError( + `Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}!`); + }); + + it('should error if a directive is declared in more than 1 module also if the module declaring it is imported', + () => { + @NgModule({declarations: [SomeDirective], exports: [SomeDirective]}) + class Module1 { + } + + @NgModule({declarations: [SomeDirective], imports: [Module1]}) + class Module2 { + } + + expect(() => createModule(Module2)) + .toThrowError( + `Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}!`); + }); + + it('should error if a pipe is declared in more than 1 module', () => { + @NgModule({declarations: [SomePipe]}) + class Module1 { + } + + @NgModule({declarations: [SomePipe]}) + class Module2 { + } + + createModule(Module1); + + expect(() => createModule(Module2)) + .toThrowError( + `Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}!`); + }); + + it('should error if a pipe is declared in more than 1 module also if the module declaring it is imported', + () => { + @NgModule({declarations: [SomePipe], exports: [SomePipe]}) + class Module1 { + } + + @NgModule({declarations: [SomePipe], imports: [Module1]}) + class Module2 { + } + + expect(() => createModule(Module2)) + .toThrowError( + `Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}!`); + }); + + }); + + describe('precompile', function() { + it('should precompile ComponentFactories in root modules', () => { + @NgModule({declarations: [SomeComp], precompile: [SomeComp]}) + class SomeModule { + } + + const ngModule = createModule(SomeModule); + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) + .toBe(SomeComp); + expect(ngModule.injector.get(ComponentFactoryResolver) + .resolveComponentFactory(SomeComp) + .componentType) + .toBe(SomeComp); + }); + + it('should precompile ComponentFactories via ANALYZE_FOR_PRECOMPILE', () => { + @NgModule({ + declarations: [SomeComp], + providers: [{ + provide: ANALYZE_FOR_PRECOMPILE, + multi: true, + useValue: [{a: 'b', component: SomeComp}] + }] + }) + class SomeModule { + } + + const ngModule = createModule(SomeModule); + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) + .toBe(SomeComp); + expect(ngModule.injector.get(ComponentFactoryResolver) + .resolveComponentFactory(SomeComp) + .componentType) + .toBe(SomeComp); + }); + + it('should precompile ComponentFactories in imported modules', () => { + @NgModule({declarations: [SomeComp], precompile: [SomeComp]}) + class SomeImportedModule { + } + + @NgModule({imports: [SomeImportedModule]}) + class SomeModule { + } + + const ngModule = createModule(SomeModule); + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) + .toBe(SomeComp); + expect(ngModule.injector.get(ComponentFactoryResolver) + .resolveComponentFactory(SomeComp) + .componentType) + .toBe(SomeComp); + }); + + it('should precompile ComponentFactories if the component was imported', () => { + @NgModule({declarations: [SomeComp], exports: [SomeComp]}) + class SomeImportedModule { + } + + @NgModule({imports: [SomeImportedModule], precompile: [SomeComp]}) + class SomeModule { + } + + const ngModule = createModule(SomeModule); + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) + .toBe(SomeComp); + expect(ngModule.injector.get(ComponentFactoryResolver) + .resolveComponentFactory(SomeComp) + .componentType) + .toBe(SomeComp); + }); + + }); + + describe('directives and pipes', () => { + describe('declarations', () => { + it('should be supported in root modules', () => { + @NgModule({ + declarations: [CompUsingModuleDirectiveAndPipe, SomeDirective, SomePipe], + precompile: [CompUsingModuleDirectiveAndPipe] + }) + class SomeModule { + } + + const compFixture = createComp(CompUsingModuleDirectiveAndPipe, SomeModule); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].properties['title']) + .toBe('transformed someValue'); + }); + + it('should be supported in imported modules', () => { + @NgModule({ + declarations: [CompUsingModuleDirectiveAndPipe, SomeDirective, SomePipe], + precompile: [CompUsingModuleDirectiveAndPipe] + }) + class SomeImportedModule { + } + + @NgModule({imports: [SomeImportedModule]}) + class SomeModule { + } + + const compFixture = createComp(CompUsingModuleDirectiveAndPipe, SomeModule); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].properties['title']) + .toBe('transformed someValue'); + }); + + + it('should be supported in nested components', () => { + @Component({ + selector: 'parent', + template: '', + }) + class ParentCompUsingModuleDirectiveAndPipe { + } + + @NgModule({ + declarations: [ + ParentCompUsingModuleDirectiveAndPipe, CompUsingModuleDirectiveAndPipe, SomeDirective, + SomePipe + ], + precompile: [ParentCompUsingModuleDirectiveAndPipe] + }) + class SomeModule { + } + + const compFixture = createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].children[0].properties['title']) + .toBe('transformed someValue'); + }); + + it('should hoist @Component.directives/pipes into the module', () => { + @Component({ + selector: 'parent', + template: '', + directives: [CompUsingModuleDirectiveAndPipe, SomeDirective], + pipes: [SomePipe] + }) + class ParentCompUsingModuleDirectiveAndPipe { + } + + @NgModule({ + declarations: [ParentCompUsingModuleDirectiveAndPipe], + precompile: [ParentCompUsingModuleDirectiveAndPipe] + }) + class SomeModule { + } + + const compFixture = createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].children[0].properties['title']) + .toBe('transformed someValue'); + }); + + it('should allow to use directives/pipes via @Component.directives/pipes that were already imported from another module', + () => { + @Component({ + selector: 'parent', + template: '', + directives: [CompUsingModuleDirectiveAndPipe, SomeDirective], + pipes: [SomePipe] + }) + class ParentCompUsingModuleDirectiveAndPipe { + } + + @NgModule({ + declarations: [SomeDirective, SomePipe, CompUsingModuleDirectiveAndPipe], + exports: [SomeDirective, SomePipe, CompUsingModuleDirectiveAndPipe] + }) + class SomeImportedModule { + } + + @NgModule({ + declarations: [ParentCompUsingModuleDirectiveAndPipe], + imports: [SomeImportedModule], + precompile: [ParentCompUsingModuleDirectiveAndPipe] + }) + class SomeModule { + } + + const compFixture = createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].children[0].properties['title']) + .toBe('transformed someValue'); + }); + }); + + describe('import/export', () => { + + it('should support exported directives and pipes', () => { + @NgModule({declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]}) + class SomeImportedModule { + } + + @NgModule({ + declarations: [CompUsingModuleDirectiveAndPipe], + imports: [SomeImportedModule], + precompile: [CompUsingModuleDirectiveAndPipe] + }) + class SomeModule { + } + + + const compFixture = createComp(CompUsingModuleDirectiveAndPipe, SomeModule); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].properties['title']) + .toBe('transformed someValue'); + }); + + it('should support reexported modules', () => { + @NgModule({declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]}) + class SomeReexportedModule { + } + + @NgModule({exports: [SomeReexportedModule]}) + class SomeImportedModule { + } + + @NgModule({ + declarations: [CompUsingModuleDirectiveAndPipe], + imports: [SomeImportedModule], + precompile: [CompUsingModuleDirectiveAndPipe] + }) + class SomeModule { + } + + const compFixture = createComp(CompUsingModuleDirectiveAndPipe, SomeModule); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].properties['title']) + .toBe('transformed someValue'); + }); + + it('should support exporting individual directives of an imported module', () => { + @NgModule({declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]}) + class SomeReexportedModule { + } + + @NgModule({imports: [SomeReexportedModule], exports: [SomeDirective, SomePipe]}) + class SomeImportedModule { + } + + @NgModule({ + declarations: [CompUsingModuleDirectiveAndPipe], + imports: [SomeImportedModule], + precompile: [CompUsingModuleDirectiveAndPipe] + }) + class SomeModule { + } + + const compFixture = createComp(CompUsingModuleDirectiveAndPipe, SomeModule); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].properties['title']) + .toBe('transformed someValue'); + }); + + it('should not use non exported pipes of an imported module', () => { + @NgModule({ + declarations: [SomePipe], + }) + class SomeImportedModule { + } + + @NgModule({ + declarations: [CompUsingModuleDirectiveAndPipe], + imports: [SomeImportedModule], + precompile: [CompUsingModuleDirectiveAndPipe] + }) + class SomeModule { + } + + expect(() => createComp(SomeComp, SomeModule)) + .toThrowError(/The pipe 'somePipe' could not be found/); + }); + + it('should not use non exported directives of an imported module', () => { + @NgModule({ + declarations: [SomeDirective], + }) + class SomeImportedModule { + } + + @NgModule({ + declarations: [CompUsingModuleDirectiveAndPipe, SomePipe], + imports: [SomeImportedModule], + precompile: [CompUsingModuleDirectiveAndPipe] + }) + class SomeModule { + } + + expect(() => createComp(SomeComp, SomeModule)).toThrowError(/Can't bind to 'someDir'/); + }); + }); + }); + + describe('bound compiler', () => { + it('should provide a Compiler instance that uses the directives/pipes of the module', () => { + @NgModule({declarations: [SomeDirective, SomePipe]}) + class SomeModule { + } + + const ngModule = createModule(SomeModule); + const boundCompiler: Compiler = ngModule.injector.get(Compiler); + const cf = boundCompiler.compileComponentSync(CompUsingModuleDirectiveAndPipe); + const compFixture = new ComponentFixture(cf.create(injector), null, false); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].properties['title']) + .toBe('transformed someValue'); + + // compile again should produce the same result + expect(boundCompiler.compileComponentSync(CompUsingModuleDirectiveAndPipe)).toBe(cf); + }); + + it('should provide a ComponentResolver instance that uses the directives/pipes of the module', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + + @NgModule({declarations: [SomeDirective, SomePipe]}) + class SomeModule { + } + + const ngModule = createModule(SomeModule); + + const boundCompiler: ComponentResolver = ngModule.injector.get(ComponentResolver); + boundCompiler.resolveComponent(CompUsingModuleDirectiveAndPipe).then((cf) => { + const compFixture = new ComponentFixture(cf.create(injector), null, false); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].properties['title']) + .toBe('transformed someValue'); + async.done(); + }); + })); + + it('should provide a ComponentResolver instance that delegates to the parent ComponentResolver for strings', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + @NgModule() + class SomeModule { + } + + let parentResolver: any = + jasmine.createSpyObj('resolver', ['resolveComponent', 'clearCache']); + const ngModule = createModule( + SomeModule, ReflectiveInjector.resolveAndCreate( + [{provide: ComponentResolver, useValue: parentResolver}])); + parentResolver.resolveComponent.and.returnValue( + Promise.resolve('someFactoryFromParent')); + let boundCompiler: ComponentResolver = ngModule.injector.get(ComponentResolver); + boundCompiler.resolveComponent('someString').then((result) => { + expect(parentResolver.resolveComponent).toHaveBeenCalledWith('someString'); + expect(result).toBe('someFactoryFromParent'); + async.done(); + }); + })); + + }); + + describe('providers', function() { + let moduleType: any = null; + + + function createInjector(providers: any[], parent: Injector = null): Injector { + @NgModule({providers: providers}) + class SomeModule { + } + + moduleType = SomeModule; + + return createModule(SomeModule, parent).injector; + } + + it('should provide the module', + () => { expect(createInjector([]).get(moduleType)).toBeAnInstanceOf(moduleType); }); + + it('should instantiate a class without dependencies', () => { + var injector = createInjector([Engine]); + var engine = injector.get(Engine); + + expect(engine).toBeAnInstanceOf(Engine); + }); + + it('should resolve dependencies based on type information', () => { + var injector = createInjector([Engine, Car]); + var car = injector.get(Car); + + expect(car).toBeAnInstanceOf(Car); + expect(car.engine).toBeAnInstanceOf(Engine); + }); + + it('should resolve dependencies based on @Inject annotation', () => { + var injector = createInjector([TurboEngine, Engine, CarWithInject]); + var car = injector.get(CarWithInject); + + expect(car).toBeAnInstanceOf(CarWithInject); + expect(car.engine).toBeAnInstanceOf(TurboEngine); + }); + + it('should throw when no type and not @Inject (class case)', () => { + expect(() => createInjector([NoAnnotations])) + .toThrowError('Can\'t resolve all parameters for NoAnnotations: (?).'); + }); + + it('should throw when no type and not @Inject (factory case)', () => { + expect(() => createInjector([provide('someToken', {useFactory: factoryFn})])) + .toThrowError('Can\'t resolve all parameters for factoryFn: (?).'); + }); + + it('should cache instances', () => { + var injector = createInjector([Engine]); + + var e1 = injector.get(Engine); + var e2 = injector.get(Engine); + + expect(e1).toBe(e2); + }); + + it('should provide to a value', () => { + var injector = createInjector([provide(Engine, {useValue: 'fake engine'})]); + + var engine = injector.get(Engine); + expect(engine).toEqual('fake engine'); + }); + + it('should provide to a factory', () => { + function sportsCarFactory(e: Engine) { return new SportsCar(e); } + + var injector = + createInjector([Engine, provide(Car, {useFactory: sportsCarFactory, deps: [Engine]})]); + + var car = injector.get(Car); + expect(car).toBeAnInstanceOf(SportsCar); + expect(car.engine).toBeAnInstanceOf(Engine); + }); + + it('should supporting provider to null', () => { + var injector = createInjector([provide(Engine, {useValue: null})]); + var engine = injector.get(Engine); + expect(engine).toBeNull(); + }); + + it('should provide to an alias', () => { + var injector = createInjector([ + Engine, provide(SportsCar, {useClass: SportsCar}), + provide(Car, {useExisting: SportsCar}) + ]); + + var car = injector.get(Car); + var sportsCar = injector.get(SportsCar); + expect(car).toBeAnInstanceOf(SportsCar); + expect(car).toBe(sportsCar); + }); + + it('should support multiProviders', () => { + var injector = createInjector([ + Engine, provide(Car, {useClass: SportsCar, multi: true}), + provide(Car, {useClass: CarWithOptionalEngine, multi: true}) + ]); + + var cars = injector.get(Car); + expect(cars.length).toEqual(2); + expect(cars[0]).toBeAnInstanceOf(SportsCar); + expect(cars[1]).toBeAnInstanceOf(CarWithOptionalEngine); + }); + + it('should support multiProviders that are created using useExisting', () => { + var injector = createInjector( + [Engine, SportsCar, provide(Car, {useExisting: SportsCar, multi: true})]); + + var cars = injector.get(Car); + expect(cars.length).toEqual(1); + expect(cars[0]).toBe(injector.get(SportsCar)); + }); + + it('should throw when the aliased provider does not exist', () => { + var injector = createInjector([provide('car', {useExisting: SportsCar})]); + var e = `No provider for ${stringify(SportsCar)}!`; + expect(() => injector.get('car')).toThrowError(e); + }); + + it('should handle forwardRef in useExisting', () => { + var injector = createInjector([ + provide('originalEngine', {useClass: forwardRef(() => Engine)}), + provide('aliasedEngine', {useExisting: forwardRef(() => 'originalEngine')}) + ]); + expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine); + }); + + it('should support overriding factory dependencies', () => { + var injector = createInjector( + [Engine, provide(Car, {useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]})]); + + var car = injector.get(Car); + expect(car).toBeAnInstanceOf(SportsCar); + expect(car.engine).toBeAnInstanceOf(Engine); + }); + + it('should support optional dependencies', () => { + var injector = createInjector([CarWithOptionalEngine]); + + var car = injector.get(CarWithOptionalEngine); + expect(car.engine).toEqual(null); + }); + + it('should flatten passed-in providers', () => { + var injector = createInjector([[[Engine, Car]]]); + + var car = injector.get(Car); + expect(car).toBeAnInstanceOf(Car); + }); + + it('should use the last provider when there are multiple providers for same token', () => { + var injector = createInjector( + [provide(Engine, {useClass: Engine}), provide(Engine, {useClass: TurboEngine})]); + + expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine); + }); + + it('should use non-type tokens', () => { + var injector = createInjector([provide('token', {useValue: 'value'})]); + + expect(injector.get('token')).toEqual('value'); + }); + + it('should throw when given invalid providers', () => { + expect(() => createInjector(['blah'])) + .toThrowError( + 'Invalid provider - only instances of Provider and Type are allowed, got: blah'); + }); + + it('should provide itself', () => { + var parent = createInjector([]); + var child = createInjector([], parent); + + expect(child.get(Injector)).toBe(child); + }); + + it('should throw when no provider defined', () => { + var injector = createInjector([]); + expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!'); + }); + + it('should throw when trying to instantiate a cyclic dependency', () => { + expect(() => createInjector([Car, provide(Engine, {useClass: CyclicEngine})])) + .toThrowError(/Cannot instantiate cyclic dependency! Car/g); + }); + + it('should support null values', () => { + var injector = createInjector([provide('null', {useValue: null})]); + expect(injector.get('null')).toBe(null); + }); + + + describe('child', () => { + it('should load instances from parent injector', () => { + var parent = createInjector([Engine]); + var child = createInjector([], parent); + + var engineFromParent = parent.get(Engine); + var engineFromChild = child.get(Engine); + + expect(engineFromChild).toBe(engineFromParent); + }); + + it('should not use the child providers when resolving the dependencies of a parent provider', + () => { + var parent = createInjector([Car, Engine]); + var child = createInjector([provide(Engine, {useClass: TurboEngine})], parent); + + var carFromChild = child.get(Car); + expect(carFromChild.engine).toBeAnInstanceOf(Engine); + }); + + it('should create new instance in a child injector', () => { + var parent = createInjector([Engine]); + var child = createInjector([provide(Engine, {useClass: TurboEngine})], parent); + + var engineFromParent = parent.get(Engine); + var engineFromChild = child.get(Engine); + + expect(engineFromParent).not.toBe(engineFromChild); + expect(engineFromChild).toBeAnInstanceOf(TurboEngine); + }); + + }); + + describe('depedency resolution', () => { + describe('@Self()', () => { + it('should return a dependency from self', () => { + var inj = createInjector([ + Engine, + provide( + Car, + {useFactory: (e: Engine) => new Car(e), deps: [[Engine, new SelfMetadata()]]}) + ]); + + expect(inj.get(Car)).toBeAnInstanceOf(Car); + }); + + it('should throw when not requested provider on self', () => { + expect(() => createInjector([provide(Car, { + useFactory: (e: Engine) => new Car(e), + deps: [[Engine, new SelfMetadata()]] + })])) + .toThrowError(/No provider for Engine/g); + }); + }); + + describe('default', () => { + it('should not skip self', () => { + var parent = createInjector([Engine]); + var child = createInjector( + [ + provide(Engine, {useClass: TurboEngine}), + provide(Car, {useFactory: (e: Engine) => new Car(e), deps: [Engine]}) + ], + parent); + + expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine); + }); + }); + }); + + describe('imported and exported modules', () => { + it('should add the providers of imported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) + class ImportedModule { + } + + @NgModule({imports: [ImportedModule]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + + expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule); + expect(injector.get(ImportedModule)).toBeAnInstanceOf(ImportedModule); + expect(injector.get('token1')).toBe('imported'); + }); + + it('should overwrite the providers of imported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) + class ImportedModule { + } + + @NgModule( + {providers: [{provide: 'token1', useValue: 'direct'}], imports: [ImportedModule]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('direct'); + }); + + it('should overwrite the providers of imported modules on the second import level', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) + class ImportedModuleLevel2 { + } + + @NgModule({ + providers: [{provide: 'token1', useValue: 'direct'}], + imports: [ImportedModuleLevel2] + }) + class ImportedModuleLevel1 { + } + + @NgModule({imports: [ImportedModuleLevel1]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('direct'); + }); + + it('should add the providers of exported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'exported'}]}) + class ExportedValue { + } + + @NgModule({exports: [ExportedValue]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + + expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule); + expect(injector.get(ExportedValue)).toBeAnInstanceOf(ExportedValue); + expect(injector.get('token1')).toBe('exported'); + }); + + it('should overwrite the providers of exported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'exported'}]}) + class ExportedModule { + } + + @NgModule( + {providers: [{provide: 'token1', useValue: 'direct'}], exports: [ExportedModule]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('direct'); + }); + + it('should overwrite the providers of imported modules by following imported modules', + () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported1'}]}) + class ImportedModule1 { + } + + @NgModule({providers: [{provide: 'token1', useValue: 'imported2'}]}) + class ImportedModule2 { + } + + @NgModule({imports: [ImportedModule1, ImportedModule2]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('imported2'); + }); + + it('should overwrite the providers of exported modules by following exported modules', + () => { + @NgModule({providers: [{provide: 'token1', useValue: 'exported1'}]}) + class ExportedModule1 { + } + + @NgModule({providers: [{provide: 'token1', useValue: 'exported2'}]}) + class ExportedModule2 { + } + + @NgModule({exports: [ExportedModule1, ExportedModule2]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('exported2'); + }); + + it('should overwrite the providers of imported modules by exported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) + class ImportedModule { + } + + @NgModule({providers: [{provide: 'token1', useValue: 'exported'}]}) + class ExportedModule { + } + + @NgModule({imports: [ImportedModule], exports: [ExportedModule]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('exported'); + }); + + it('should not overwrite the providers if a module was already used on the same level', + () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported1'}]}) + class ImportedModule1 { + } + + @NgModule({providers: [{provide: 'token1', useValue: 'imported2'}]}) + class ImportedModule2 { + } + + @NgModule({imports: [ImportedModule1, ImportedModule2, ImportedModule1]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('imported2'); + }); + + it('should not overwrite the providers if a module was already used on a child level', + () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported1'}]}) + class ImportedModule1 { + } + + @NgModule({imports: [ImportedModule1]}) + class ImportedModule3 { + } + + @NgModule({providers: [{provide: 'token1', useValue: 'imported2'}]}) + class ImportedModule2 { + } + + @NgModule({imports: [ImportedModule3, ImportedModule2, ImportedModule1]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('imported2'); + }); + }); + }); + }); +} diff --git a/modules/@angular/core/test/linker/precompile_integration_spec.ts b/modules/@angular/core/test/linker/precompile_integration_spec.ts index e93692acbc..78851ae032 100644 --- a/modules/@angular/core/test/linker/precompile_integration_spec.ts +++ b/modules/@angular/core/test/linker/precompile_integration_spec.ts @@ -7,8 +7,9 @@ */ import {AsyncTestCompleter, beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; -import {TestComponentBuilder} from '@angular/core/testing'; -import {Component, ComponentFactoryResolver, NoComponentFactoryError, forwardRef, ANALYZE_FOR_PRECOMPILE} from '@angular/core'; +import {TestComponentBuilder, configureModule} from '@angular/core/testing'; +import {Component, ComponentFactoryResolver, NoComponentFactoryError, forwardRef, ANALYZE_FOR_PRECOMPILE, ViewMetadata} from '@angular/core'; +import {stringify} from '../../src/facade/lang'; export function main() { describe('jit', () => { declareTests({useJit: true}); }); @@ -17,19 +18,33 @@ export function main() { function declareTests({useJit}: {useJit: boolean}) { describe('@Component.precompile', function() { + beforeEach(() => { configureModule({declarations: [MainComp, ChildComp, NestedChildComp]}); }); + + it('should error if the component was not declared nor imported by the module', + inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + + @Component({selector: 'child', template: ''}) + class ChildComp { + } + + @Component({template: 'comp', precompile: [ChildComp]}) + class SomeComp { + } + + expect(() => tcb.createSync(SomeComp)) + .toThrowError( + `Component ${stringify(SomeComp)} in NgModule DynamicTestModule uses ${stringify(ChildComp)} via "precompile" but it was neither declared nor imported into the module!`); + })); + + it('should resolve ComponentFactories from the same component', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.createAsync(MainComp).then((compFixture) => { - let mainComp: MainComp = compFixture.componentInstance; - expect(compFixture.debugElement.injector.get(ComponentFactoryResolver)) - .toBe(mainComp.cfr); - var cf = mainComp.cfr.resolveComponentFactory(ChildComp); - expect(cf.componentType).toBe(ChildComp); - async.done(); - }); - })); + inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + const compFixture = tcb.createSync(MainComp); + let mainComp: MainComp = compFixture.componentInstance; + expect(compFixture.componentRef.injector.get(ComponentFactoryResolver)).toBe(mainComp.cfr); + var cf = mainComp.cfr.resolveComponentFactory(ChildComp); + expect(cf.componentType).toBe(ChildComp); + })); it('should resolve ComponentFactories via ANALYZE_FOR_PRECOMPILE', @@ -37,63 +52,52 @@ function declareTests({useJit}: {useJit: boolean}) { let compFixture = tcb.createSync(CompWithAnalyzePrecompileProvider); let mainComp: CompWithAnalyzePrecompileProvider = compFixture.componentInstance; let cfr: ComponentFactoryResolver = - compFixture.debugElement.injector.get(ComponentFactoryResolver); + compFixture.componentRef.injector.get(ComponentFactoryResolver); expect(cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp); expect(cfr.resolveComponentFactory(NestedChildComp).componentType).toBe(NestedChildComp); })); it('should be able to get a component form a parent component (view hiearchy)', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideTemplate(MainComp, '') - .createAsync(MainComp) - .then((compFixture) => { - let childCompEl = compFixture.debugElement.children[0]; - let childComp: ChildComp = childCompEl.componentInstance; - // declared on ChildComp directly - expect(childComp.cfr.resolveComponentFactory(NestedChildComp).componentType) - .toBe(NestedChildComp); - // inherited from MainComp - expect(childComp.cfr.resolveComponentFactory(ChildComp).componentType) - .toBe(ChildComp); - async.done(); - }); - })); + inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + const compFixture = + tcb.overrideView( + MainComp, + new ViewMetadata({template: '', directives: [ChildComp]})) + .createSync(MainComp); + let childCompEl = compFixture.debugElement.children[0]; + let childComp: ChildComp = childCompEl.componentInstance; + // declared on ChildComp directly + expect(childComp.cfr.resolveComponentFactory(NestedChildComp).componentType) + .toBe(NestedChildComp); + // inherited from MainComp + expect(childComp.cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp); + })); it('should not be able to get components from a parent component (content hierarchy)', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideTemplate(MainComp, '') - .overrideTemplate(ChildComp, '') - .createAsync(MainComp) - .then((compFixture) => { - let nestedChildCompEl = compFixture.debugElement.children[0].children[0]; - let nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance; - expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp).componentType) - .toBe(ChildComp); - expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp)) - .toThrow(new NoComponentFactoryError(NestedChildComp)); - async.done(); - }); - })); + inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + const compFixture = tcb.overrideView(MainComp, new ViewMetadata({ + template: '', + directives: [ChildComp, NestedChildComp] + })) + .overrideTemplate(ChildComp, '') + .createSync(MainComp); + let nestedChildCompEl = compFixture.debugElement.children[0].children[0]; + let nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance; + expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp).componentType) + .toBe(ChildComp); + expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp)) + .toThrow(new NoComponentFactoryError(NestedChildComp)); + })); }); } -var DIRECTIVES: any[] = [ - forwardRef(() => NestedChildComp), - forwardRef(() => ChildComp), - forwardRef(() => MainComp), -]; - -@Component({selector: 'nested', directives: DIRECTIVES, template: ''}) +@Component({selector: 'nested', template: ''}) class NestedChildComp { constructor(public cfr: ComponentFactoryResolver) {} } -@Component({selector: 'child', precompile: [NestedChildComp], directives: DIRECTIVES, template: ''}) +@Component({selector: 'child', precompile: [NestedChildComp], template: ''}) class ChildComp { constructor(public cfr: ComponentFactoryResolver) {} } @@ -101,7 +105,6 @@ class ChildComp { @Component({ selector: 'main', precompile: [ChildComp], - directives: DIRECTIVES, template: '', }) class MainComp { diff --git a/modules/@angular/core/test/linker/reflector_component_resolver_spec.ts b/modules/@angular/core/test/linker/reflector_component_resolver_spec.ts deleted file mode 100644 index f256f975cd..0000000000 --- a/modules/@angular/core/test/linker/reflector_component_resolver_spec.ts +++ /dev/null @@ -1,50 +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 {ComponentFactory} from '@angular/core/src/linker/component_factory'; -import {ComponentResolver, ReflectorComponentResolver} from '@angular/core/src/linker/component_resolver'; -import {ReflectionInfo, reflector} from '@angular/core/src/reflection/reflection'; -import {AsyncTestCompleter, afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; -import {Console} from '../../src/console'; - -class DummyConsole implements Console { - log(message: string) {} - warn(message: string) {} -} - -export function main() { - describe('Compiler', () => { - var someCompFactory: any /** TODO #9100 */; - var compiler: ComponentResolver; - - beforeEach(() => { - someCompFactory = new ComponentFactory(null, null, null); - reflector.registerType(SomeComponent, new ReflectionInfo([someCompFactory])); - compiler = new ReflectorComponentResolver(new DummyConsole()); - }); - - it('should read the template from an annotation', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - compiler.resolveComponent(SomeComponent).then((compFactory: ComponentFactory) => { - expect(compFactory).toBe(someCompFactory); - async.done(); - return null; - }); - })); - - it('should throw when given a string', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - compiler.resolveComponent('someString').catch((e) => { - expect(e.message).toContain('Cannot resolve component using \'someString\'.'); - async.done(); - }); - })); - }); -} - -class SomeComponent {} diff --git a/modules/@angular/core/test/linker/regression_integration_spec.ts b/modules/@angular/core/test/linker/regression_integration_spec.ts index 30f144ff38..4670a3f698 100644 --- a/modules/@angular/core/test/linker/regression_integration_spec.ts +++ b/modules/@angular/core/test/linker/regression_integration_spec.ts @@ -26,7 +26,7 @@ function declareTests({useJit}: {useJit: boolean}) { describe('platform pipes', () => { beforeEach(() => { configureCompiler({useJit: useJit}); - configureModule({pipes: [PlatformPipe]}); + configureModule({declarations: [PlatformPipe]}); }); it('should overwrite them by custom pipes', diff --git a/modules/@angular/core/testing/test_bed.ts b/modules/@angular/core/testing/test_bed.ts index 154507e7fc..2190dacc34 100644 --- a/modules/@angular/core/testing/test_bed.ts +++ b/modules/@angular/core/testing/test_bed.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModule, AppModuleFactory, AppModuleMetadata, AppModuleRef, Compiler, CompilerFactory, ComponentStillLoadingError, Injector, PlatformRef, Provider, ReflectiveInjector, Type, assertPlatform, createPlatform, getPlatform} from '../index'; +import {Compiler, CompilerFactory, CompilerOptions, ComponentStillLoadingError, Injector, NgModule, NgModuleFactory, NgModuleMetadata, NgModuleRef, PlatformRef, Provider, ReflectiveInjector, Type, assertPlatform, createPlatform, getPlatform} from '../index'; import {ListWrapper} from '../src/facade/collection'; import {BaseException} from '../src/facade/exceptions'; -import {FunctionWrapper, isPresent, stringify} from '../src/facade/lang'; +import {ConcreteType, FunctionWrapper, isPresent, stringify} from '../src/facade/lang'; import {AsyncTestCompleter} from './async_test_completer'; @@ -22,105 +22,87 @@ export class TestBed implements Injector { private _instantiated: boolean = false; private _compiler: Compiler = null; - private _moduleRef: AppModuleRef = null; - private _appModuleFactory: AppModuleFactory = null; + private _moduleRef: NgModuleRef = null; + private _ngModuleFactory: NgModuleFactory = null; - private _compilerProviders: Array = []; - private _compilerUseJit: boolean = true; + private _compilerOptions: CompilerOptions[] = []; private _providers: Array = []; - private _directives: Array = []; - private _pipes: Array = []; - private _modules: Array = []; + private _declarations: Array = []; + private _imports: Array = []; private _precompile: Array = []; reset() { this._compiler = null; this._moduleRef = null; - this._appModuleFactory = null; - this._compilerProviders = []; - this._compilerUseJit = true; + this._ngModuleFactory = null; + this._compilerOptions = []; this._providers = []; - this._directives = []; - this._pipes = []; - this._modules = []; + this._declarations = []; + this._imports = []; this._precompile = []; this._instantiated = false; } platform: PlatformRef = null; - appModule: Type = null; + ngModule: Type = null; configureCompiler(config: {providers?: any[], useJit?: boolean}) { if (this._instantiated) { throw new BaseException('Cannot add configuration after test injector is instantiated'); } - if (config.providers) { - this._compilerProviders = ListWrapper.concat(this._compilerProviders, config.providers); - } - if (config.useJit !== undefined) { - this._compilerUseJit = config.useJit; - } + this._compilerOptions.push(config); } - configureModule(moduleDef: { - providers?: any[], - directives?: any[], - pipes?: any[], - precompile?: any[], - modules?: any[] - }) { + configureModule( + moduleDef: {providers?: any[], declarations?: any[], imports?: any[], precompile?: any[]}) { if (this._instantiated) { throw new BaseException('Cannot add configuration after test injector is instantiated'); } if (moduleDef.providers) { this._providers = ListWrapper.concat(this._providers, moduleDef.providers); } - if (moduleDef.directives) { - this._directives = ListWrapper.concat(this._directives, moduleDef.directives); + if (moduleDef.declarations) { + this._declarations = ListWrapper.concat(this._declarations, moduleDef.declarations); } - if (moduleDef.pipes) { - this._pipes = ListWrapper.concat(this._pipes, moduleDef.pipes); + if (moduleDef.imports) { + this._imports = ListWrapper.concat(this._imports, moduleDef.imports); } if (moduleDef.precompile) { this._precompile = ListWrapper.concat(this._precompile, moduleDef.precompile); } - if (moduleDef.modules) { - this._modules = ListWrapper.concat(this._modules, moduleDef.modules); - } } - createAppModuleFactory(): Promise> { + createModuleFactory(): Promise> { if (this._instantiated) { throw new BaseException( - 'Cannot run precompilation when the test AppModule has already been instantiated. ' + + 'Cannot run precompilation when the test NgModule has already been instantiated. ' + 'Make sure you are not using `inject` before `doAsyncPrecompilation`.'); } - if (this._appModuleFactory) { - return Promise.resolve(this._appModuleFactory); + if (this._ngModuleFactory) { + return Promise.resolve(this._ngModuleFactory); } - let moduleMeta = this._createCompilerAndModuleMeta(); + const moduleType = this._createCompilerAndModule(); - return this._compiler.compileAppModuleAsync(_NoopModule, moduleMeta) - .then((appModuleFactory) => { - this._appModuleFactory = appModuleFactory; - return appModuleFactory; - }); + return this._compiler.compileModuleAsync(moduleType).then((ngModuleFactory) => { + this._ngModuleFactory = ngModuleFactory; + return ngModuleFactory; + }); } - initTestAppModule() { + initTestModule() { if (this._instantiated) { return; } - if (this._appModuleFactory) { - this._createFromModuleFactory(this._appModuleFactory); + if (this._ngModuleFactory) { + this._createFromModuleFactory(this._ngModuleFactory); } else { - let moduleMeta = this._createCompilerAndModuleMeta(); - this._createFromModuleFactory(this._compiler.compileAppModuleSync(_NoopModule, moduleMeta)); + let moduleType = this._createCompilerAndModule(); + this._createFromModuleFactory(this._compiler.compileModuleSync(moduleType)); } } @@ -131,31 +113,34 @@ export class TestBed implements Injector { if (this._instantiated) { return Promise.resolve(this); } - let moduleMeta = this._createCompilerAndModuleMeta(); - return this._compiler.compileAppModuleAsync(_NoopModule, moduleMeta) - .then((appModuleFactory) => this._createFromModuleFactory(appModuleFactory)); + let ngModule = this._createCompilerAndModule(); + return this._compiler.compileModuleAsync(ngModule).then( + (ngModuleFactory) => this._createFromModuleFactory(ngModuleFactory)); } - private _createCompilerAndModuleMeta(): AppModuleMetadata { + private _createCompilerAndModule(): ConcreteType { + const providers = this._providers.concat([{provide: TestBed, useValue: this}]); + const declarations = this._declarations; + const imports = [this.ngModule, this._imports]; + const precompile = this._precompile; + + @NgModule({ + providers: providers, + declarations: declarations, + imports: imports, + precompile: precompile + }) + class DynamicTestModule { + } + const compilerFactory: CompilerFactory = this.platform.injector.get(CompilerFactory); - this._compiler = compilerFactory.createCompiler({ - providers: this._compilerProviders, - useJit: this._compilerUseJit, - deprecatedAppProviders: this._providers - }); - const moduleMeta = new AppModuleMetadata({ - providers: this._providers.concat([{provide: TestBed, useValue: this}]), - modules: this._modules.concat([this.appModule]), - directives: this._directives, - pipes: this._pipes, - precompile: this._precompile - }); - - return moduleMeta; + this._compiler = + compilerFactory.createCompiler(this._compilerOptions.concat([{useDebug: true}])); + return DynamicTestModule; } - private _createFromModuleFactory(appModuleFactory: AppModuleFactory): Injector { - this._moduleRef = appModuleFactory.create(this.platform.injector); + private _createFromModuleFactory(ngModuleFactory: NgModuleFactory): Injector { + this._moduleRef = ngModuleFactory.create(this.platform.injector); this._instantiated = true; return this; } @@ -163,13 +148,13 @@ export class TestBed implements Injector { get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND) { if (!this._instantiated) { throw new BaseException( - 'Illegal state: The test bed\'s injector has not yet been created. Call initTestAppModule first!'); + 'Illegal state: The test bed\'s injector has not yet been created. Call initTestNgModule first!'); } if (token === TestBed) { return this; } - // Tests can inject things from the app module and from the compiler, - // but the app module can't inject things from the compiler and vice versa. + // Tests can inject things from the ng module and from the compiler, + // but the ng module can't inject things from the compiler and vice versa. let result = this._moduleRef.injector.get(token, UNDEFINED); return result === UNDEFINED ? this._compiler.injector.get(token, notFoundValue) : result; } @@ -177,7 +162,7 @@ export class TestBed implements Injector { execute(tokens: any[], fn: Function): any { if (!this._instantiated) { throw new BaseException( - 'Illegal state: The test bed\'s injector has not yet been created. Call initTestAppModule first!'); + 'Illegal state: The test bed\'s injector has not yet been created. Call initTestNgModule first!'); } var params = tokens.map(t => this.get(t)); return FunctionWrapper.apply(fn, params); @@ -219,20 +204,17 @@ export function getTestInjector() { export function setBaseTestProviders( platformProviders: Array, applicationProviders: Array) { - // Create a platform based on the Platform Providers. - var platformRef = createPlatform(ReflectiveInjector.resolveAndCreate(platformProviders)); - - // Create an AppModule based on the application providers. - @AppModule({providers: applicationProviders}) - class TestAppModule { + if (platformProviders.length === 1 && typeof platformProviders[0] === 'function') { + (platformProviders[0])(applicationProviders); + } else { + throw new Error( + `setBaseTestProviders is deprecated and only supports platformProviders that are predefined by Angular. Use 'initTestEnvironment' instead.`); } - - initTestEnvironment(TestAppModule, platformRef); } /** * Initialize the environment for testing with a compiler factory, a PlatformRef, and an - * application module. These are common to every test in the suite. + * angular module. These are common to every test in the suite. * * This may only be called once, to set up the common providers for the current test * suite on the current platform. If you absolutely need to change the providers, @@ -243,13 +225,15 @@ export function setBaseTestProviders( * * @experimental */ -export function initTestEnvironment(appModule: Type, platform: PlatformRef) { +export function initTestEnvironment(ngModule: Type, platform: PlatformRef): Injector { var testBed = getTestBed(); - if (testBed.platform || testBed.appModule) { + if (testBed.platform || testBed.ngModule) { throw new BaseException('Cannot set base providers because it has already been called'); } testBed.platform = platform; - testBed.appModule = appModule; + testBed.ngModule = ngModule; + + return testBed; } /** @@ -269,20 +253,20 @@ export function resetBaseTestProviders() { export function resetTestEnvironment() { var testBed = getTestBed(); testBed.platform = null; - testBed.appModule = null; + testBed.ngModule = null; testBed.reset(); } /** - * Run asynchronous precompilation for the test's AppModule. It is necessary to call this function - * if your test is using an AppModule which has precompiled components that require an asynchronous + * Run asynchronous precompilation for the test's NgModule. It is necessary to call this function + * if your test is using an NgModule which has precompiled components that require an asynchronous * call, such as an XHR. Should be called once before the test case. * * @experimental */ export function doAsyncPrecompilation(): Promise { let testBed = getTestBed(); - return testBed.createAppModuleFactory(); + return testBed.createModuleFactory(); } /** @@ -324,7 +308,7 @@ export function inject(tokens: any[], fn: Function): () => any { } else { return () => { try { - testBed.initTestAppModule(); + testBed.initTestModule(); } catch (e) { if (e instanceof ComponentStillLoadingError) { throw new Error( @@ -343,13 +327,9 @@ export function inject(tokens: any[], fn: Function): () => any { * @experimental */ export class InjectSetupWrapper { - constructor(private _moduleDef: () => { - providers?: any[], - directives?: any[], - pipes?: any[], - precompile?: any[], - modules?: any[] - }) {} + constructor( + private _moduleDef: + () => {providers?: any[], declarations?: any[], imports?: any[], precompile?: any[]}) {} private _addModule() { var moduleDef = this._moduleDef(); @@ -378,12 +358,9 @@ export function withProviders(providers: () => any) { */ export function withModule(moduleDef: () => { providers?: any[], - directives?: any[], - pipes?: any[], - precompile?: any[], - modules?: any[] + declarations?: any[], + imports?: any[], + precompile?: any[] }) { return new InjectSetupWrapper(moduleDef); } - -class _NoopModule {} diff --git a/modules/@angular/core/testing/test_component_builder.ts b/modules/@angular/core/testing/test_component_builder.ts index 81747adfc6..b715ea245c 100644 --- a/modules/@angular/core/testing/test_component_builder.ts +++ b/modules/@angular/core/testing/test_component_builder.ts @@ -119,25 +119,27 @@ export class TestComponentBuilder { /** * Builds and returns a ComponentFixture. */ - createAsync(rootComponentType: ConcreteType): Promise> { + createAsync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): + Promise> { let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false); let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null); let compiler: Compiler = this._injector.get(Compiler); let initComponent = () => { let promise: Promise> = - compiler.compileComponentAsync(rootComponentType); + compiler.compileComponentAsync(rootComponentType, ngModule); return promise.then(componentFactory => this.createFromFactory(ngZone, componentFactory)); }; return ngZone == null ? initComponent() : ngZone.run(initComponent); } - createFakeAsync(rootComponentType: ConcreteType): ComponentFixture { + createFakeAsync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): + ComponentFixture { let result: any /** TODO #9100 */; let error: any /** TODO #9100 */; PromiseWrapper.then( - this.createAsync(rootComponentType), (_result) => { result = _result; }, + this.createAsync(rootComponentType, ngModule), (_result) => { result = _result; }, (_error) => { error = _error; }); tick(); if (isPresent(error)) { @@ -146,14 +148,15 @@ export class TestComponentBuilder { return result; } - createSync(rootComponentType: ConcreteType): ComponentFixture { + createSync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): + ComponentFixture { let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false); let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null); let compiler: Compiler = this._injector.get(Compiler); let initComponent = () => { return this.createFromFactory( - ngZone, this._injector.get(Compiler).compileComponentSync(rootComponentType)); + ngZone, compiler.compileComponentSync(rootComponentType, ngModule)); }; return ngZone == null ? initComponent() : ngZone.run(initComponent); diff --git a/modules/@angular/core/testing/testing.ts b/modules/@angular/core/testing/testing.ts index de9f880ce2..ebc293820f 100644 --- a/modules/@angular/core/testing/testing.ts +++ b/modules/@angular/core/testing/testing.ts @@ -49,13 +49,9 @@ export function addProviders(providers: Array): void { * * @stable */ -export function configureModule(moduleDef: { - providers?: any[], - directives?: any[], - pipes?: any[], - precompile?: any[], - modules?: any[] -}): void { +export function configureModule( + moduleDef: {providers?: any[], declarations?: any[], imports?: any[], precompile?: any[]}): + void { if (!moduleDef) return; try { testBed.configureModule(moduleDef); diff --git a/modules/@angular/examples/core/ts/platform/platform.ts b/modules/@angular/examples/core/ts/platform/platform.ts index 1b64debbd5..8b9fd2b4c0 100644 --- a/modules/@angular/examples/core/ts/platform/platform.ts +++ b/modules/@angular/examples/core/ts/platform/platform.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, ReflectiveInjector, coreLoadAndBootstrap, createPlatform} from '@angular/core'; -import {BROWSER_APP_PROVIDERS, BROWSER_PLATFORM_PROVIDERS} from '@angular/platform-browser'; +import {Component, ReflectiveInjector, bootstrapModule, createPlatformFactory} from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import {browserDynamicPlatform} from '@angular/platform-browser-dynamic'; var appProviders: any[] = []; @@ -16,8 +17,6 @@ var appProviders: any[] = []; class MyApp { } -var platform = createPlatform(ReflectiveInjector.resolveAndCreate(BROWSER_PLATFORM_PROVIDERS)); -var appInjector = - ReflectiveInjector.resolveAndCreate([BROWSER_APP_PROVIDERS, appProviders], platform.injector); -coreLoadAndBootstrap(MyApp, appInjector); +var myPlatformFactory = createPlatformFactory(browserDynamicPlatform, 'myPlatform'); +bootstrapModule(MyApp, myPlatformFactory()); // #enddocregion diff --git a/modules/@angular/facade/src/lang.ts b/modules/@angular/facade/src/lang.ts index 1e5e49abdf..0b42441fd0 100644 --- a/modules/@angular/facade/src/lang.ts +++ b/modules/@angular/facade/src/lang.ts @@ -155,12 +155,12 @@ export function stringify(token: any): string { return '' + token; } - if (token.name) { - return token.name; - } if (token.overriddenName) { return token.overriddenName; } + if (token.name) { + return token.name; + } var res = token.toString(); var newLineIndex = res.indexOf('\n'); diff --git a/modules/@angular/forms/src/directives.ts b/modules/@angular/forms/src/directives.ts index 861d92893a..86f7e1caa3 100644 --- a/modules/@angular/forms/src/directives.ts +++ b/modules/@angular/forms/src/directives.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Type} from '@angular/core'; +import {NgModule, Type} from '@angular/core'; import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor'; import {DefaultValueAccessor} from './directives/default_value_accessor'; @@ -83,3 +83,10 @@ export const FORM_DIRECTIVES: Type[][] = export const REACTIVE_FORM_DIRECTIVES: Type[][] = /*@ts2dart_const*/[REACTIVE_DRIVEN_DIRECTIVES, SHARED_FORM_DIRECTIVES]; + +/** + * Internal module used for sharing directives between FormsModule and ReactiveFormsModule + */ +@NgModule({declarations: SHARED_FORM_DIRECTIVES, exports: SHARED_FORM_DIRECTIVES}) +export class InternalFormsSharedModule { +} diff --git a/modules/@angular/forms/src/form_providers.ts b/modules/@angular/forms/src/form_providers.ts index 834027c585..427b7ef9dc 100644 --- a/modules/@angular/forms/src/form_providers.ts +++ b/modules/@angular/forms/src/form_providers.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModule, PLATFORM_DIRECTIVES, Type} from '@angular/core'; +import {NgModule, PLATFORM_DIRECTIVES, Type} from '@angular/core'; -import {FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES} from './directives'; +import {FORM_DIRECTIVES, InternalFormsSharedModule, REACTIVE_DRIVEN_DIRECTIVES, REACTIVE_FORM_DIRECTIVES, SHARED_FORM_DIRECTIVES, TEMPLATE_DRIVEN_DIRECTIVES} from './directives'; import {RadioControlRegistry} from './directives/radio_control_value_accessor'; import {FormBuilder} from './form_builder'; @@ -28,18 +28,26 @@ export const REACTIVE_FORM_PROVIDERS: Type[] = /*@ts2dart_const*/[FormBuilder, RadioControlRegistry]; /** - * The app module for forms. + * The ng module for forms. * @experimental */ -@AppModule({providers: [FORM_PROVIDERS], directives: FORM_DIRECTIVES, pipes: []}) +@NgModule({ + declarations: TEMPLATE_DRIVEN_DIRECTIVES, + providers: [FORM_PROVIDERS], + exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES] +}) export class FormsModule { } /** - * The app module for reactive forms. + * The ng module for reactive forms. * @experimental */ -@AppModule({providers: [REACTIVE_FORM_PROVIDERS], directives: REACTIVE_FORM_DIRECTIVES, pipes: []}) +@NgModule({ + declarations: [REACTIVE_DRIVEN_DIRECTIVES], + providers: [REACTIVE_FORM_PROVIDERS], + exports: [InternalFormsSharedModule, REACTIVE_DRIVEN_DIRECTIVES] +}) export class ReactiveFormsModule { } @@ -57,4 +65,4 @@ export function provideForms(): any[] { return [ {provide: PLATFORM_DIRECTIVES, useValue: FORM_DIRECTIVES, multi: true}, REACTIVE_FORM_PROVIDERS ]; -} \ No newline at end of file +} diff --git a/modules/@angular/forms/test/reactive_integration_spec.ts b/modules/@angular/forms/test/reactive_integration_spec.ts index c63365fc30..aca8273d8b 100644 --- a/modules/@angular/forms/test/reactive_integration_spec.ts +++ b/modules/@angular/forms/test/reactive_integration_spec.ts @@ -22,7 +22,7 @@ import {PromiseWrapper} from '../src/facade/promise'; export function main() { describe('reactive forms integration tests', () => { - beforeEach(() => { configureModule({modules: [FormsModule, ReactiveFormsModule]}); }); + beforeEach(() => { configureModule({imports: [FormsModule, ReactiveFormsModule]}); }); it('should initialize DOM elements with the given form object', inject( diff --git a/modules/@angular/forms/test/template_integration_spec.ts b/modules/@angular/forms/test/template_integration_spec.ts index ac8533b768..75eec3e60d 100644 --- a/modules/@angular/forms/test/template_integration_spec.ts +++ b/modules/@angular/forms/test/template_integration_spec.ts @@ -20,7 +20,7 @@ import {ListWrapper} from '../src/facade/collection'; export function main() { describe('template-driven forms integration tests', () => { - beforeEach(() => { configureModule({modules: [FormsModule]}); }); + beforeEach(() => { configureModule({imports: [FormsModule]}); }); it('should support ngModel for single fields', fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { diff --git a/modules/@angular/platform-browser-dynamic/index.ts b/modules/@angular/platform-browser-dynamic/index.ts index ba825514b8..07f538f1d7 100644 --- a/modules/@angular/platform-browser-dynamic/index.ts +++ b/modules/@angular/platform-browser-dynamic/index.ts @@ -6,37 +6,24 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMMON_DIRECTIVES, COMMON_PIPES} from '@angular/common'; -import {COMPILER_PROVIDERS, CompilerConfig, XHR, RUNTIME_COMPILER_FACTORY,} from '@angular/compiler'; -import {AppModule, AppModuleRef, ApplicationRef, Compiler, ComponentRef, ComponentResolver, ExceptionHandler, PLATFORM_DIRECTIVES, PLATFORM_PIPES, ReflectiveInjector, Type, coreLoadAndBootstrap, bootstrapModule, bootstrapModuleFactory, isDevMode, OpaqueToken, PlatformRef, getPlatform, assertPlatform, createPlatform, PLATFORM_INITIALIZER, CompilerOptions, CompilerFactory, createPlatformFactory} from '@angular/core'; -import {BROWSER_APP_PROVIDERS, BrowserModule, WORKER_APP_APPLICATION_PROVIDERS, WORKER_SCRIPT, WORKER_UI_APPLICATION_PROVIDERS, browserPlatform, workerAppPlatform, workerUiPlatform, BROWSER_PLATFORM_PROVIDERS} from '@angular/platform-browser'; +import {XHR, analyzeAppProvidersForDeprecatedConfiguration, coreDynamicPlatform} from '@angular/compiler'; +import {ApplicationRef, Compiler, CompilerFactory, CompilerOptions, ComponentRef, ComponentResolver, ExceptionHandler, NgModule, NgModuleRef, OpaqueToken, PLATFORM_DIRECTIVES, PLATFORM_INITIALIZER, PLATFORM_PIPES, PlatformRef, ReflectiveInjector, Type, assertPlatform, bootstrapModule, bootstrapModuleFactory, createPlatform, createPlatformFactory, getPlatform, isDevMode} from '@angular/core'; +import {BROWSER_PLATFORM_PROVIDERS, BrowserModule, WORKER_APP_PLATFORM_PROVIDERS, WORKER_SCRIPT, WorkerAppModule, browserPlatform, workerAppPlatform, workerUiPlatform} from '@angular/platform-browser'; -import {Console, ReflectionCapabilities, reflector} from './core_private'; -import {getDOM, initDomAdapter} from './platform_browser_private'; +import {Console} from './core_private'; import {PromiseWrapper} from './src/facade/async'; import {ConcreteType, isPresent, stringify} from './src/facade/lang'; +import {INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS} from './src/platform_providers'; import {CachedXHR} from './src/xhr/xhr_cache'; import {XHRImpl} from './src/xhr/xhr_impl'; + + /** * @deprecated The compiler providers are already included in the {@link CompilerFactory} that is * contained the {@link browserDynamicPlatform}()`. */ -export const BROWSER_APP_COMPILER_PROVIDERS: Array = [ - COMPILER_PROVIDERS, { - provide: CompilerConfig, - useFactory: (platformDirectives: any[], platformPipes: any[]) => { - return new CompilerConfig({ - deprecatedPlatformDirectives: platformDirectives, - deprecatedPlatformPipes: platformPipes - }); - }, - deps: [PLATFORM_DIRECTIVES, PLATFORM_PIPES] - }, - {provide: XHR, useClass: XHRImpl}, - {provide: PLATFORM_DIRECTIVES, useValue: COMMON_DIRECTIVES, multi: true}, - {provide: PLATFORM_PIPES, useValue: COMMON_PIPES, multi: true} -]; +export const BROWSER_APP_COMPILER_PROVIDERS: Array = []; /** * @experimental @@ -44,34 +31,11 @@ export const BROWSER_APP_COMPILER_PROVIDERS: Array = [{provide: XHR, useClass: CachedXHR}]; -function initReflector() { - reflector.reflectionCapabilities = new ReflectionCapabilities(); -} - -/** - * CompilerFactory for the browser dynamic platform - * - * @experimental - */ -export const BROWSER_DYNAMIC_COMPILER_FACTORY = - RUNTIME_COMPILER_FACTORY.withDefaults({providers: [{provide: XHR, useClass: XHRImpl}]}); - -/** - * Providers for the browser dynamic platform - * - * @experimental - */ -export const BROWSER_DYNAMIC_PLATFORM_PROVIDERS: Array = [ - BROWSER_PLATFORM_PROVIDERS, - {provide: CompilerFactory, useValue: BROWSER_DYNAMIC_COMPILER_FACTORY}, - {provide: PLATFORM_INITIALIZER, useValue: initReflector, multi: true}, -]; - /** * @experimental API related to bootstrapping are still under review. */ -export const browserDynamicPlatform = - createPlatformFactory('browserDynamic', BROWSER_DYNAMIC_PLATFORM_PROVIDERS); +export const browserDynamicPlatform = createPlatformFactory( + coreDynamicPlatform, 'browserDynamic', INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS); /** * Bootstrapping for Angular applications. @@ -142,8 +106,9 @@ export const browserDynamicPlatform = * ## API (version 2) * - `appComponentType`: The root component which should act as the application. This is * a reference to a `Type` which is annotated with `@Component(...)`. - * - `providers`, `directives`, `pipes`, `modules`, `precompile`: Defines the properties + * - `providers`, `declarations`, `imports`, `precompile`: Defines the properties * of the dynamically created module that is used to bootstrap the module. + * - to configure the compiler, use the `compilerOptions` parameter. * * Returns a `Promise` of {@link ComponentRef}. * @@ -156,11 +121,10 @@ export function bootstrap( customProviders?: Array): Promise>; export function bootstrap( appComponentType: ConcreteType, - {providers, directives, pipes, modules, precompile, compilerOptions}?: { + {providers, imports, declarations, precompile, compilerOptions}?: { providers?: Array, - directives?: any[], - pipes?: any[], - modules?: any[], + declarations?: any[], + imports?: any[], precompile?: any[], compilerOptions?: CompilerOptions }): Promise>; @@ -168,112 +132,107 @@ export function bootstrap( appComponentType: ConcreteType, customProvidersOrDynamicModule?: Array| { providers: Array, - directives: any[], - pipes: any[], - modules: any[], + declarations?: any[], + imports: any[], precompile: any[], compilerOptions: CompilerOptions }): Promise> { let compilerOptions: CompilerOptions; - let compilerProviders: any = []; let providers: any[] = []; - let directives: any[] = []; - let pipes: any[] = []; - let modules: any[] = []; + let declarations: any[] = []; + let imports: any[] = []; let precompile: any[] = []; + let deprecationMessages: string[] = []; if (customProvidersOrDynamicModule instanceof Array) { providers = customProvidersOrDynamicModule; + const deprecatedConfiguration = analyzeAppProvidersForDeprecatedConfiguration(providers); + declarations = deprecatedConfiguration.moduleDeclarations.concat(declarations); + compilerOptions = deprecatedConfiguration.compilerOptions; + deprecationMessages = deprecatedConfiguration.deprecationMessages; } else if (customProvidersOrDynamicModule) { providers = normalizeArray(customProvidersOrDynamicModule.providers); - directives = normalizeArray(customProvidersOrDynamicModule.directives); - pipes = normalizeArray(customProvidersOrDynamicModule.pipes); - modules = normalizeArray(customProvidersOrDynamicModule.modules); + declarations = normalizeArray(customProvidersOrDynamicModule.declarations); + imports = normalizeArray(customProvidersOrDynamicModule.imports); precompile = normalizeArray(customProvidersOrDynamicModule.precompile); compilerOptions = customProvidersOrDynamicModule.compilerOptions; } - @AppModule({ + @NgModule({ providers: providers, - modules: modules.concat([BrowserModule]), - directives: directives, - pipes: pipes, + declarations: declarations.concat([appComponentType]), + imports: [BrowserModule, imports], precompile: precompile.concat([appComponentType]) }) class DynamicModule { } - return bootstrapModule( - DynamicModule, browserDynamicPlatform(), - CompilerFactory.mergeOptions(compilerOptions, {deprecatedAppProviders: providers})) + return bootstrapModule(DynamicModule, browserDynamicPlatform(), compilerOptions) .then((moduleRef) => { + const console = moduleRef.injector.get(Console); + deprecationMessages.forEach((msg) => console.warn(msg)); const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef); return appRef.bootstrap(appComponentType); }); } /** - * @deprecated Create an {@link AppModule} that includes the {@link WorkerUiModule} and use {@link - * bootstrapModule} - * with the {@link workerUiPlatform}() instead. + * Bootstraps the worker ui. + * + * @experimental */ export function bootstrapWorkerUi( workerScriptUri: string, - customProviders?: Array): Promise { - console.warn( - 'bootstrapWorkerUi is deprecated. Create an @AppModule that includes the `WorkerUiModule` and use `bootstrapModule` with the `workerUiPlatform()` instead.'); - var app = ReflectiveInjector.resolveAndCreate( - [ - WORKER_UI_APPLICATION_PROVIDERS, BROWSER_APP_COMPILER_PROVIDERS, - {provide: WORKER_SCRIPT, useValue: workerScriptUri}, - isPresent(customProviders) ? customProviders : [] - ], - workerUiPlatform().injector); - // Return a promise so that we keep the same semantics as Dart, - // and we might want to wait for the app side to come up - // in the future... - return PromiseWrapper.resolve(app.get(ApplicationRef)); + customProviders: Array = []): Promise { + // For now, just creates the worker ui platform... + return Promise.resolve(workerUiPlatform([{ + provide: WORKER_SCRIPT, + useValue: workerScriptUri, + }].concat(customProviders))); } /** - * @deprecated The compiler providers are already included in the {@link CompilerFactory} that is - * contained the {@link workerAppPlatform}(). + * @experimental API related to bootstrapping are still under review. */ -const WORKER_APP_COMPILER_PROVIDERS: Array = [ - COMPILER_PROVIDERS, { - provide: CompilerConfig, - useFactory: (platformDirectives: any[], platformPipes: any[]) => { - return new CompilerConfig({ - deprecatedPlatformDirectives: platformDirectives, - deprecatedPlatformPipes: platformPipes - }); - }, - deps: [PLATFORM_DIRECTIVES, PLATFORM_PIPES] - }, - {provide: XHR, useClass: XHRImpl}, - {provide: PLATFORM_DIRECTIVES, useValue: COMMON_DIRECTIVES, multi: true}, - {provide: PLATFORM_PIPES, useValue: COMMON_PIPES, multi: true} -]; - +export const workerAppDynamicPlatform = + createPlatformFactory(coreDynamicPlatform, 'workerAppDynamic', [{ + provide: CompilerOptions, + useValue: {providers: [{provide: XHR, useClass: XHRImpl}]}, + multi: true + }]); /** - * @deprecated Create an {@link AppModule} that includes the {@link WorkerAppModule} and use {@link + * @deprecated Create an {@link NgModule} that includes the {@link WorkerAppModule} and use {@link * bootstrapModule} - * with the {@link workerAppPlatform}() instead. + * with the {@link workerAppDynamicPlatform}() instead. */ -export function bootstrapWorkerApp( - appComponentType: Type, - customProviders?: Array): Promise> { +export function bootstrapWorkerApp( + appComponentType: ConcreteType, + customProviders?: Array): Promise> { console.warn( - 'bootstrapWorkerApp is deprecated. Create an @AppModule that includes the `WorkerAppModule` and use `bootstrapModule` with the `workerAppPlatform()` instead.'); - var appInjector = ReflectiveInjector.resolveAndCreate( - [ - WORKER_APP_APPLICATION_PROVIDERS, WORKER_APP_COMPILER_PROVIDERS, - isPresent(customProviders) ? customProviders : [] - ], - workerAppPlatform().injector); - return coreLoadAndBootstrap(appComponentType, appInjector); + 'bootstrapWorkerApp is deprecated. Create an @NgModule that includes the `WorkerAppModule` and use `bootstrapModule` with the `workerAppDynamicPlatform()` instead.'); + + const deprecatedConfiguration = analyzeAppProvidersForDeprecatedConfiguration(customProviders); + const declarations = [deprecatedConfiguration.moduleDeclarations.concat([appComponentType])]; + + @NgModule({ + providers: customProviders, + declarations: declarations, + imports: [WorkerAppModule], + precompile: [appComponentType] + }) + class DynamicModule { + } + + return bootstrapModule( + DynamicModule, workerAppDynamicPlatform(), deprecatedConfiguration.compilerOptions) + .then((moduleRef) => { + const console = moduleRef.injector.get(Console); + deprecatedConfiguration.deprecationMessages.forEach((msg) => console.warn(msg)); + const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef); + return appRef.bootstrap(appComponentType); + }); } function normalizeArray(arr: any[]): any[] { return arr ? arr : []; -} \ No newline at end of file +} diff --git a/modules/@angular/platform-browser-dynamic/platform_browser_private.ts b/modules/@angular/platform-browser-dynamic/platform_browser_private.ts index 5ae937569b..8b69a84aa4 100644 --- a/modules/@angular/platform-browser-dynamic/platform_browser_private.ts +++ b/modules/@angular/platform-browser-dynamic/platform_browser_private.ts @@ -8,5 +8,6 @@ import {__platform_browser_private__ as r, __platform_browser_private__ as t} from '@angular/platform-browser'; +export var INTERNAL_BROWSER_PLATFORM_PROVIDERS: typeof t.INTERNAL_BROWSER_PLATFORM_PROVIDERS = + r.INTERNAL_BROWSER_PLATFORM_PROVIDERS; export var getDOM: typeof t.getDOM = r.getDOM; -export var initDomAdapter: typeof t.initDomAdapter = r.initDomAdapter; diff --git a/modules/@angular/platform-browser-dynamic/src/platform_providers.ts b/modules/@angular/platform-browser-dynamic/src/platform_providers.ts new file mode 100644 index 0000000000..5106930c56 --- /dev/null +++ b/modules/@angular/platform-browser-dynamic/src/platform_providers.ts @@ -0,0 +1,23 @@ +/** + * @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 {XHR} from '@angular/compiler'; +import {CompilerOptions} from '@angular/core'; + +import {INTERNAL_BROWSER_PLATFORM_PROVIDERS} from '../platform_browser_private'; + +import {XHRImpl} from './xhr/xhr_impl'; + +export const INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS: any[] = [ + INTERNAL_BROWSER_PLATFORM_PROVIDERS, + { + provide: CompilerOptions, + useValue: {providers: [{provide: XHR, useClass: XHRImpl}]}, + multi: true + }, +]; diff --git a/modules/@angular/platform-browser-dynamic/testing.ts b/modules/@angular/platform-browser-dynamic/testing.ts index c7f0a8b82c..22f164a926 100644 --- a/modules/@angular/platform-browser-dynamic/testing.ts +++ b/modules/@angular/platform-browser-dynamic/testing.ts @@ -6,90 +6,70 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMMON_DIRECTIVES, COMMON_PIPES} from '@angular/common'; -import {CompilerConfig, DirectiveResolver, ViewResolver} from '@angular/compiler'; -import {MockDirectiveResolver, MockViewResolver, OverridingTestComponentBuilder} from '@angular/compiler/testing'; -import {AppModule, Compiler, CompilerFactory, PLATFORM_DIRECTIVES, PLATFORM_PIPES, PlatformRef, Provider, ReflectiveInjector, Type, createPlatformFactory} from '@angular/core'; -import {TestComponentBuilder, TestComponentRenderer} from '@angular/core/testing'; -import {BrowserTestModule, TEST_BROWSER_APPLICATION_PROVIDERS, TEST_BROWSER_PLATFORM_PROVIDERS} from '@angular/platform-browser/testing'; +import {CompilerConfig, DirectiveResolver, NgModuleResolver, ViewResolver, analyzeAppProvidersForDeprecatedConfiguration} from '@angular/compiler'; +import {OverridingTestComponentBuilder, coreDynamicTestingPlatform} from '@angular/compiler/testing'; +import {Compiler, CompilerFactory, CompilerOptions, NgModule, PlatformRef, Provider, ReflectiveInjector, Type, createPlatform, createPlatformFactory} from '@angular/core'; +import {TestComponentBuilder, TestComponentRenderer, initTestEnvironment} from '@angular/core/testing'; +import {BrowserTestingModule, browserTestingPlatform} from '@angular/platform-browser/testing'; -import {BROWSER_APP_COMPILER_PROVIDERS, BROWSER_DYNAMIC_COMPILER_FACTORY, BROWSER_DYNAMIC_PLATFORM_PROVIDERS} from './index'; +import {Console} from './core_private'; +import {browserDynamicPlatform} from './index'; +import {INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS} from './src/platform_providers'; import {DOMTestComponentRenderer} from './testing/dom_test_component_renderer'; export * from './private_export_testing' -/** - * CompilerFactory for browser dynamic test platform - * - * @experimental - */ -export const BROWSER_DYNAMIC_TEST_COMPILER_FACTORY = BROWSER_DYNAMIC_COMPILER_FACTORY.withDefaults({ - providers: [ - {provide: DirectiveResolver, useClass: MockDirectiveResolver}, - {provide: ViewResolver, useClass: MockViewResolver} - ] -}); - - -/** - * Providers for the browser dynamic platform - * - * @experimental - */ -const BROWSER_DYNAMIC_TEST_PLATFORM_PROVIDERS: Array = [ - TEST_BROWSER_PLATFORM_PROVIDERS, - BROWSER_DYNAMIC_PLATFORM_PROVIDERS, - {provide: CompilerFactory, useValue: BROWSER_DYNAMIC_TEST_COMPILER_FACTORY}, -]; - /** * @experimental API related to bootstrapping are still under review. */ -export const browserDynamicTestPlatform = - createPlatformFactory('browserDynamicTest', BROWSER_DYNAMIC_TEST_PLATFORM_PROVIDERS); +export const browserDynamicTestingPlatform = createPlatformFactory( + coreDynamicTestingPlatform, 'browserDynamicTesting', + INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS); /** - * AppModule for testing. + * NgModule for testing. * * @stable */ -@AppModule({ - modules: [BrowserTestModule], +@NgModule({ + exports: [BrowserTestingModule], providers: [ {provide: TestComponentBuilder, useClass: OverridingTestComponentBuilder}, {provide: TestComponentRenderer, useClass: DOMTestComponentRenderer}, ] }) -export class BrowserDynamicTestModule { +export class BrowserDynamicTestingModule { } -// Used only as a shim until TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS is deprecated. -const BROWSER_DYNAMIC_TEST_COMPILER_FACTORY_OLD = BROWSER_DYNAMIC_COMPILER_FACTORY.withDefaults({ - providers: [ - {provide: DirectiveResolver, useClass: MockDirectiveResolver}, - {provide: ViewResolver, useClass: MockViewResolver} - ], - deprecatedAppProviders: [ - {provide: PLATFORM_DIRECTIVES, useValue: COMMON_DIRECTIVES, multi: true}, - {provide: PLATFORM_PIPES, useValue: COMMON_PIPES, multi: true} - ] -}); +/** + * @deprecated Use initTestEnvironment with browserDynamicTestingPlatform instead. + */ +export const TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS: Array = + // Note: This is not a real provider but a hack to still support the deprecated + // `setBaseTestProviders` method! + [(appProviders: any[]) => { + const deprecatedConfiguration = analyzeAppProvidersForDeprecatedConfiguration(appProviders); + const platformRef = + createPlatformFactory(browserDynamicTestingPlatform, 'browserDynamicTestingDeprecated', [{ + provide: CompilerOptions, + useValue: deprecatedConfiguration.compilerOptions, + multi: true + }])(); + + @NgModule({ + exports: [BrowserDynamicTestingModule], + declarations: [deprecatedConfiguration.moduleDeclarations] + }) + class DynamicTestModule { + } + + const testInjector = initTestEnvironment(DynamicTestModule, platformRef); + const console: Console = testInjector.get(Console); + deprecatedConfiguration.deprecationMessages.forEach((msg) => console.warn(msg)); + }]; /** - * @deprecated Use initTestEnvironment with browserDynamicTestPlatform instead. + * @deprecated Use initTestEnvironment with BrowserDynamicTestingModule instead. */ -export const TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS: Array = [ - TEST_BROWSER_PLATFORM_PROVIDERS, - BROWSER_DYNAMIC_PLATFORM_PROVIDERS, - {provide: CompilerFactory, useValue: BROWSER_DYNAMIC_TEST_COMPILER_FACTORY_OLD}, -]; - - -/** - * @deprecated Use initTestEnvironment with BrowserDynamicTestModule instead. - */ -export const TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS: Array = [ - TEST_BROWSER_APPLICATION_PROVIDERS, - {provide: TestComponentBuilder, useClass: OverridingTestComponentBuilder}, - {provide: TestComponentRenderer, useClass: DOMTestComponentRenderer}, -]; +export const TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS: Array = + []; diff --git a/modules/@angular/platform-browser/private_export.ts b/modules/@angular/platform-browser/private_export.ts index 87d65a52bc..d250d72a28 100644 --- a/modules/@angular/platform-browser/private_export.ts +++ b/modules/@angular/platform-browser/private_export.ts @@ -42,5 +42,6 @@ export var __platform_browser_private__ = { SharedStylesHost: shared_styles_host.SharedStylesHost, ELEMENT_PROBE_PROVIDERS: ng_proble.ELEMENT_PROBE_PROVIDERS, DomEventsPlugin: dom_events.DomEventsPlugin, - initDomAdapter: browser.initDomAdapter + initDomAdapter: browser.initDomAdapter, + INTERNAL_BROWSER_PLATFORM_PROVIDERS: browser.INTERNAL_BROWSER_PLATFORM_PROVIDERS }; diff --git a/modules/@angular/platform-browser/src/browser.ts b/modules/@angular/platform-browser/src/browser.ts index 343048a174..561dd4ea3e 100644 --- a/modules/@angular/platform-browser/src/browser.ts +++ b/modules/@angular/platform-browser/src/browser.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMMON_DIRECTIVES, COMMON_PIPES, PlatformLocation} from '@angular/common'; -import {APPLICATION_COMMON_PROVIDERS, AppModule, AppModuleFactory, AppModuleRef, ExceptionHandler, NgZone, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, RootRenderer, SanitizationService, Testability, assertPlatform, createPlatform, createPlatformFactory, getPlatform, isDevMode} from '@angular/core'; +import {CommonModule, PlatformLocation} from '@angular/common'; +import {ApplicationModule, ExceptionHandler, NgModule, NgModuleFactory, NgModuleRef, NgZone, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, RootRenderer, SanitizationService, Testability, assertPlatform, corePlatform, createPlatform, createPlatformFactory, getPlatform, isDevMode} from '@angular/core'; import {wtfInit} from '../core_private'; import {AnimationDriver} from '../src/dom/animation_driver'; @@ -28,18 +28,21 @@ import {DomSharedStylesHost, SharedStylesHost} from './dom/shared_styles_host'; import {isBlank} from './facade/lang'; import {DomSanitizationService, DomSanitizationServiceImpl} from './security/dom_sanitization_service'; +export const INTERNAL_BROWSER_PLATFORM_PROVIDERS: Array = [ + {provide: PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true}, + {provide: PlatformLocation, useClass: BrowserPlatformLocation} +]; /** * A set of providers to initialize the Angular platform in a web browser. * * Used automatically by `bootstrap`, or can be passed to `platform`. * - * @experimental API related to bootstrapping are still under review. + * @deprecated Use `browserPlatform()` or create a custom platform factory via + * `createPlatformFactory(browserPlatform, ...)` */ -export const BROWSER_PLATFORM_PROVIDERS: Array = [ - PLATFORM_COMMON_PROVIDERS, {provide: PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true}, - {provide: PlatformLocation, useClass: BrowserPlatformLocation} -]; +export const BROWSER_PLATFORM_PROVIDERS: Array = + [PLATFORM_COMMON_PROVIDERS, INTERNAL_BROWSER_PLATFORM_PROVIDERS]; /** * @security Replacing built-in sanitization providers exposes the application to XSS risks. @@ -58,27 +61,18 @@ export const BROWSER_SANITIZATION_PROVIDERS: Array = [ * Used automatically by `bootstrap`, or can be passed to {@link PlatformRef * PlatformRef.application}. * - * @experimental API related to bootstrapping are still under review. + * @deprecated Create a module that includes `BrowserModule` instead. This is empty for backwards + * compatibility, + * as all of our bootstrap methods add a module implicitly, i.e. keeping this filled would add the + * providers 2x. */ -export const BROWSER_APP_PROVIDERS: Array = [ - APPLICATION_COMMON_PROVIDERS, BROWSER_SANITIZATION_PROVIDERS, - {provide: ExceptionHandler, useFactory: _exceptionHandler, deps: []}, - {provide: DOCUMENT, useFactory: _document, deps: []}, - {provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true}, - {provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true}, - {provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true}, - {provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig}, - {provide: DomRootRenderer, useClass: DomRootRenderer_}, - {provide: RootRenderer, useExisting: DomRootRenderer}, - {provide: SharedStylesHost, useExisting: DomSharedStylesHost}, - {provide: AnimationDriver, useFactory: _resolveDefaultAnimationDriver}, DomSharedStylesHost, - Testability, EventManager, ELEMENT_PROBE_PROVIDERS -]; +export const BROWSER_APP_PROVIDERS: Array = []; /** * @experimental API related to bootstrapping are still under review. */ -export const browserPlatform = createPlatformFactory('browser', BROWSER_PLATFORM_PROVIDERS); +export const browserPlatform = + createPlatformFactory(corePlatform, 'browser', INTERNAL_BROWSER_PLATFORM_PROVIDERS); export function initDomAdapter() { BrowserDomAdapter.makeCurrent(); @@ -102,16 +96,26 @@ export function _resolveDefaultAnimationDriver(): AnimationDriver { } /** - * The app module for the browser. + * The ng module for the browser. * * @experimental */ -@AppModule({ +@NgModule({ providers: [ - BROWSER_APP_PROVIDERS, + BROWSER_SANITIZATION_PROVIDERS, + {provide: ExceptionHandler, useFactory: _exceptionHandler, deps: []}, + {provide: DOCUMENT, useFactory: _document, deps: []}, + {provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true}, + {provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true}, + {provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true}, + {provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig}, + {provide: DomRootRenderer, useClass: DomRootRenderer_}, + {provide: RootRenderer, useExisting: DomRootRenderer}, + {provide: SharedStylesHost, useExisting: DomSharedStylesHost}, + {provide: AnimationDriver, useFactory: _resolveDefaultAnimationDriver}, DomSharedStylesHost, + Testability, EventManager, ELEMENT_PROBE_PROVIDERS ], - directives: COMMON_DIRECTIVES, - pipes: COMMON_PIPES + exports: [CommonModule, ApplicationModule] }) export class BrowserModule { } diff --git a/modules/@angular/platform-browser/src/web_workers/ui/location_providers.ts b/modules/@angular/platform-browser/src/web_workers/ui/location_providers.ts index 9b3534b77d..60414107b1 100644 --- a/modules/@angular/platform-browser/src/web_workers/ui/location_providers.ts +++ b/modules/@angular/platform-browser/src/web_workers/ui/location_providers.ts @@ -6,13 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {APP_INITIALIZER, Injector, NgZone} from '@angular/core'; +import {Injector, NgZone, PLATFORM_INITIALIZER} from '@angular/core'; import {BrowserPlatformLocation} from '../../browser/location/browser_platform_location'; import {MessageBasedPlatformLocation} from './platform_location'; + /** * A list of {@link Provider}s. To use the router in a Worker enabled application you must * include these providers when setting up the render thread. @@ -20,7 +21,7 @@ import {MessageBasedPlatformLocation} from './platform_location'; */ export const WORKER_UI_LOCATION_PROVIDERS = [ MessageBasedPlatformLocation, BrowserPlatformLocation, - {provide: APP_INITIALIZER, useFactory: initUiLocation, multi: true, deps: [Injector]} + {provide: PLATFORM_INITIALIZER, useFactory: initUiLocation, multi: true, deps: [Injector]} ]; function initUiLocation(injector: Injector): () => void { diff --git a/modules/@angular/platform-browser/src/worker_app.ts b/modules/@angular/platform-browser/src/worker_app.ts index 4f923625bf..537c9f22bb 100644 --- a/modules/@angular/platform-browser/src/worker_app.ts +++ b/modules/@angular/platform-browser/src/worker_app.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMMON_DIRECTIVES, COMMON_PIPES, FORM_PROVIDERS} from '@angular/common'; -import {APPLICATION_COMMON_PROVIDERS, APP_INITIALIZER, AppModule, ExceptionHandler, NgZone, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PlatformRef, ReflectiveInjector, RootRenderer, assertPlatform, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; +import {CommonModule, FORM_PROVIDERS} from '@angular/common'; +import {APP_INITIALIZER, ApplicationModule, ExceptionHandler, NgModule, NgZone, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PlatformRef, ReflectiveInjector, RootRenderer, assertPlatform, corePlatform, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; import {BROWSER_SANITIZATION_PROVIDERS} from './browser'; import {isBlank, print} from './facade/lang'; @@ -29,29 +29,24 @@ class PrintLogger { } /** - * @experimental + * @deprecated Use `workerAppPlatform()` or create a custom platform factory via + * `createPlatformFactory(workerAppPlatform, ...)` */ export const WORKER_APP_PLATFORM_PROVIDERS: Array = PLATFORM_COMMON_PROVIDERS; /** - * @experimental + * @deprecated Create a module that includes `WorkerAppModule` instead. This is empty for backwards + * compatibility, + * as all of our bootstrap methods add a module implicitly, i.e. keeping this filled would add the + * providers 2x. */ -export const WORKER_APP_APPLICATION_PROVIDERS: Array = [ - APPLICATION_COMMON_PROVIDERS, FORM_PROVIDERS, BROWSER_SANITIZATION_PROVIDERS, Serializer, - {provide: ClientMessageBrokerFactory, useClass: ClientMessageBrokerFactory_}, - {provide: ServiceMessageBrokerFactory, useClass: ServiceMessageBrokerFactory_}, - WebWorkerRootRenderer, {provide: RootRenderer, useExisting: WebWorkerRootRenderer}, - {provide: ON_WEB_WORKER, useValue: true}, RenderStore, - {provide: ExceptionHandler, useFactory: _exceptionHandler, deps: []}, - {provide: MessageBus, useFactory: createMessageBus, deps: [NgZone]}, - {provide: APP_INITIALIZER, useValue: setupWebWorker, multi: true} -]; +export const WORKER_APP_APPLICATION_PROVIDERS: Array = []; /** * @experimental */ -export const workerAppPlatform = createPlatformFactory('workerApp', WORKER_APP_PLATFORM_PROVIDERS); +export const workerAppPlatform = createPlatformFactory(corePlatform, 'workerApp'); function _exceptionHandler(): ExceptionHandler { return new ExceptionHandler(new PrintLogger()); @@ -77,14 +72,22 @@ function setupWebWorker(): void { } /** - * The app module for the worker app side. + * The ng module for the worker app side. * * @experimental */ -@AppModule({ - providers: WORKER_APP_APPLICATION_PROVIDERS, - directives: COMMON_DIRECTIVES, - pipes: COMMON_PIPES +@NgModule({ + providers: [ + FORM_PROVIDERS, BROWSER_SANITIZATION_PROVIDERS, Serializer, + {provide: ClientMessageBrokerFactory, useClass: ClientMessageBrokerFactory_}, + {provide: ServiceMessageBrokerFactory, useClass: ServiceMessageBrokerFactory_}, + WebWorkerRootRenderer, {provide: RootRenderer, useExisting: WebWorkerRootRenderer}, + {provide: ON_WEB_WORKER, useValue: true}, RenderStore, + {provide: ExceptionHandler, useFactory: _exceptionHandler, deps: []}, + {provide: MessageBus, useFactory: createMessageBus, deps: [NgZone]}, + {provide: APP_INITIALIZER, useValue: setupWebWorker, multi: true} + ], + exports: [CommonModule, ApplicationModule] }) export class WorkerAppModule { -} \ No newline at end of file +} diff --git a/modules/@angular/platform-browser/src/worker_render.ts b/modules/@angular/platform-browser/src/worker_render.ts index 7c544cd4bf..806c557d75 100644 --- a/modules/@angular/platform-browser/src/worker_render.ts +++ b/modules/@angular/platform-browser/src/worker_render.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {APPLICATION_COMMON_PROVIDERS, APP_INITIALIZER, AppModule, ExceptionHandler, Injectable, Injector, NgZone, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, RootRenderer, Testability, assertPlatform, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; +import {ExceptionHandler, Injectable, Injector, NgZone, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, RootRenderer, Testability, assertPlatform, corePlatform, createPlatform, createPlatformFactory, getPlatform, isDevMode} from '@angular/core'; import {wtfInit} from '../core_private'; @@ -34,6 +34,7 @@ import {ServiceMessageBrokerFactory, ServiceMessageBrokerFactory_} from './web_w import {MessageBasedRenderer} from './web_workers/ui/renderer'; + /** * Wrapper class that exposes the Worker * and underlying {@link MessageBus} for lower level message passing. @@ -70,16 +71,8 @@ export const WORKER_UI_STARTABLE_MESSAGING_SERVICE = /** * @experimental WebWorker support is currently experimental. */ -export const WORKER_UI_PLATFORM_PROVIDERS: Array = [ - PLATFORM_COMMON_PROVIDERS, - {provide: PLATFORM_INITIALIZER, useValue: initWebWorkerRenderPlatform, multi: true} -]; - -/** - * @experimental WebWorker support is currently experimental. - */ -export const WORKER_UI_APPLICATION_PROVIDERS: Array = [ - APPLICATION_COMMON_PROVIDERS, +export const _WORKER_UI_PLATFORM_PROVIDERS: Array = [ + {provide: NgZone, useFactory: createNgZone, deps: []}, MessageBasedRenderer, {provide: WORKER_UI_STARTABLE_MESSAGING_SERVICE, useExisting: MessageBasedRenderer, multi: true}, BROWSER_SANITIZATION_PROVIDERS, @@ -104,10 +97,27 @@ export const WORKER_UI_APPLICATION_PROVIDERS: Array = + [PLATFORM_COMMON_PROVIDERS, _WORKER_UI_PLATFORM_PROVIDERS]; + +/** + * @deprecated Worker UI only has a platform but no application + */ +export const WORKER_UI_APPLICATION_PROVIDERS: Array = []; + function initializeGenericWorkerRenderer(injector: Injector) { var bus = injector.get(MessageBus); let zone = injector.get(NgZone); @@ -122,27 +132,11 @@ function messageBusFactory(instance: WebWorkerInstance): MessageBus { return instance.bus; } -function initWebWorkerRenderPlatform(): void { - BrowserDomAdapter.makeCurrent(); - wtfInit(); - BrowserGetTestability.init(); -} - -/** - * @experimental WebWorker support is currently experimental. - */ -export const workerUiPlatform = createPlatformFactory('workerUi', WORKER_UI_PLATFORM_PROVIDERS); - -function _exceptionHandler(): ExceptionHandler { - return new ExceptionHandler(getDOM()); -} - -function _document(): any { - return getDOM().defaultDoc(); -} - -function initWebWorkerAppFn(injector: Injector): () => void { +function initWebWorkerRenderPlatform(injector: Injector): () => void { return () => { + BrowserDomAdapter.makeCurrent(); + wtfInit(); + BrowserGetTestability.init(); var scriptUri: string; try { scriptUri = injector.get(WORKER_SCRIPT); @@ -158,6 +152,24 @@ function initWebWorkerAppFn(injector: Injector): () => void { }; } +/** + * @experimental WebWorker support is currently experimental. + */ +export const workerUiPlatform = + createPlatformFactory(corePlatform, 'workerUi', _WORKER_UI_PLATFORM_PROVIDERS); + +function _exceptionHandler(): ExceptionHandler { + return new ExceptionHandler(getDOM()); +} + +function _document(): any { + return getDOM().defaultDoc(); +} + +function createNgZone(): NgZone { + return new NgZone({enableLongStackTrace: isDevMode()}); +} + /** * Spawns a new class and initializes the WebWorkerInstance */ @@ -175,14 +187,3 @@ function _resolveDefaultAnimationDriver(): AnimationDriver { // work with animations just yet... return AnimationDriver.NOOP; } - -/** - * The app module for the worker ui side. - * To use this, you need to create an own module that includes this module - * and provides the `WORKER_SCRIPT` token. - * - * @experimental - */ -@AppModule({providers: WORKER_UI_APPLICATION_PROVIDERS}) -export class WorkerUiModule { -} diff --git a/modules/@angular/platform-browser/test/browser/bootstrap_spec.ts b/modules/@angular/platform-browser/test/browser/bootstrap_spec.ts index 93fa5e342b..6e0f958c44 100644 --- a/modules/@angular/platform-browser/test/browser/bootstrap_spec.ts +++ b/modules/@angular/platform-browser/test/browser/bootstrap_spec.ts @@ -8,15 +8,15 @@ import {LowerCasePipe, NgIf} from '@angular/common'; import {XHR} from '@angular/compiler'; -import {APP_INITIALIZER, Component, Directive, ExceptionHandler, Inject, Input, OnDestroy, PLATFORM_DIRECTIVES, PLATFORM_INITIALIZER, PLATFORM_PIPES, Pipe, ReflectiveInjector, coreLoadAndBootstrap, createPlatform, provide} from '@angular/core'; +import {APP_INITIALIZER, Component, Directive, ExceptionHandler, Inject, Input, NgModule, OnDestroy, PLATFORM_DIRECTIVES, PLATFORM_INITIALIZER, PLATFORM_PIPES, Pipe, ReflectiveInjector, bootstrapModule, createPlatformFactory, provide} from '@angular/core'; import {ApplicationRef, disposePlatform} from '@angular/core/src/application_ref'; import {Console} from '@angular/core/src/console'; import {ComponentRef} from '@angular/core/src/linker/component_factory'; import {Testability, TestabilityRegistry} from '@angular/core/src/testability/testability'; import {ComponentFixture} from '@angular/core/testing'; import {AsyncTestCompleter, Log, afterEach, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it} from '@angular/core/testing/testing_internal'; -import {BROWSER_APP_PROVIDERS, BROWSER_PLATFORM_PROVIDERS} from '@angular/platform-browser'; -import {BROWSER_APP_COMPILER_PROVIDERS, bootstrap} from '@angular/platform-browser-dynamic'; +import {BrowserModule} from '@angular/platform-browser'; +import {bootstrap, browserDynamicPlatform} from '@angular/platform-browser-dynamic'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {DOCUMENT} from '@angular/platform-browser/src/dom/dom_tokens'; import {expect} from '@angular/platform-browser/testing/matchers'; @@ -223,16 +223,10 @@ export function main() { it('should unregister change detectors when components are disposed', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - var platform = - createPlatform(ReflectiveInjector.resolveAndCreate(BROWSER_PLATFORM_PROVIDERS)); - var app = ReflectiveInjector - .resolveAndCreate( - [BROWSER_APP_PROVIDERS, BROWSER_APP_COMPILER_PROVIDERS, testProviders], - platform.injector) - .get(ApplicationRef); - coreLoadAndBootstrap(HelloRootCmp, app.injector).then((ref) => { + bootstrap(HelloRootCmp, testProviders).then((ref) => { + const appRef = ref.injector.get(ApplicationRef); ref.destroy(); - expect(() => app.tick()).not.toThrow(); + expect(() => appRef.tick()).not.toThrow(); async.done(); }); })); @@ -258,24 +252,29 @@ export function main() { }); })); - it('should run platform initializers', inject([Log], (log: Log) => { - let p = createPlatform(ReflectiveInjector.resolveAndCreate([ - BROWSER_PLATFORM_PROVIDERS, + it('should run platform initializers', + inject([Log, AsyncTestCompleter], (log: Log, async: AsyncTestCompleter) => { + let p = createPlatformFactory(browserDynamicPlatform, 'someName', [ {provide: PLATFORM_INITIALIZER, useValue: log.fn('platform_init1'), multi: true}, {provide: PLATFORM_INITIALIZER, useValue: log.fn('platform_init2'), multi: true} - ])); + ])(); + + @NgModule({ + imports: [BrowserModule], + providers: [ + {provide: APP_INITIALIZER, useValue: log.fn('app_init1'), multi: true}, + {provide: APP_INITIALIZER, useValue: log.fn('app_init2'), multi: true} + ] + }) + class SomeModule { + } + expect(log.result()).toEqual('platform_init1; platform_init2'); log.clear(); - var a = ReflectiveInjector.resolveAndCreate( - [ - BROWSER_APP_PROVIDERS, - {provide: APP_INITIALIZER, useValue: log.fn('app_init1'), multi: true}, - {provide: APP_INITIALIZER, useValue: log.fn('app_init2'), multi: true} - ], - p.injector); - a.get(ApplicationRef); - - expect(log.result()).toEqual('app_init1; app_init2'); + bootstrapModule(SomeModule, p).then(() => { + expect(log.result()).toEqual('app_init1; app_init2'); + async.done(); + }); })); it('should register each application with the testability registry', @@ -305,7 +304,7 @@ export function main() { ])).then((compRef) => { expect(el).toHaveText('hello world!'); expect(compilerConsole.warnings).toEqual([ - 'Passing an instance of XHR to "bootstrap()" as provider is deprecated. Pass the provider via the new parameter "compilerOptions" of "bootstrap()" instead.' + 'Passing XHR as regular provider is deprecated. Pass the provider via "compilerOptions" instead.' ]); async.done(); }); @@ -325,10 +324,8 @@ export function main() { .toBe('transformed someValue'); expect(compilerConsole.warnings).toEqual([ - 'Passing PLATFORM_DIRECTIVES to "bootstrap()" as provider is deprecated. Use the new parameter "directives" of "bootstrap()" instead.', - 'Passing PLATFORM_PIPES to "bootstrap()" as provider is deprecated. Use the new parameter "pipes" of "bootstrap()" instead.', - `Providing platform directives via the PLATFORM_DIRECTIVES provider or the "CompilerConfig" is deprecated. Provide platform directives via an @AppModule instead. Directives: ${stringify(SomeDirective)}`, - `Providing platform pipes via the PLATFORM_PIPES provider or the "CompilerConfig" is deprecated. Provide platform pipes via an @AppModule instead. Pipes: ${stringify(SomePipe)}` + `The PLATFORM_DIRECTIVES provider and CompilerConfig.platformDirectives is deprecated. Add the directives to an NgModule instead! (Directives: ${stringify(SomeDirective)})`, + `The PLATFORM_PIPES provider and CompilerConfig.platformPipes is deprecated. Add the pipes to an NgModule instead! (Pipes: ${stringify(SomePipe)})` ]); async.done(); }); diff --git a/modules/@angular/platform-browser/test/testing_public_spec.ts b/modules/@angular/platform-browser/test/testing_public_spec.ts index 57f210a8d4..1af3ee6bae 100644 --- a/modules/@angular/platform-browser/test/testing_public_spec.ts +++ b/modules/@angular/platform-browser/test/testing_public_spec.ts @@ -8,7 +8,7 @@ import {NgIf} from '@angular/common'; import {CompilerConfig, XHR} from '@angular/compiler'; -import {AppModule, Component, ComponentFactoryResolver, Directive, Injectable, Input, Pipe, ViewMetadata, provide} from '@angular/core'; +import {Component, ComponentFactoryResolver, Directive, Injectable, Input, NgModule, Pipe, ViewMetadata, provide} from '@angular/core'; import {TestComponentBuilder, addProviders, async, configureCompiler, configureModule, doAsyncPrecompilation, fakeAsync, inject, tick, withModule, withProviders} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/matchers'; @@ -112,8 +112,8 @@ class SomePipe { class CompUsingModuleDirectiveAndPipe { } -@AppModule({}) -class SomeNestedModule { +@NgModule() +class SomeLibModule { } @Component({ @@ -233,10 +233,9 @@ export function main() { beforeEach(() => { moduleConfig = { providers: [FancyService], - directives: [SomeDirective], - pipes: [SomePipe], - precompile: [CompUsingModuleDirectiveAndPipe], - modules: [SomeNestedModule] + imports: [SomeLibModule], + declarations: [SomeDirective, SomePipe, CompUsingModuleDirectiveAndPipe], + precompile: [CompUsingModuleDirectiveAndPipe] }; }); @@ -256,9 +255,9 @@ export function main() { expect(el.children[0].properties['title']).toBe('transformed someValue'); })); - it('should use set up nested modules', - inject([SomeNestedModule], (nestedModule: SomeNestedModule) => { - expect(nestedModule).toBeAnInstanceOf(SomeNestedModule); + it('should use set up library modules', + inject([SomeLibModule], (libModule: SomeLibModule) => { + expect(libModule).toBeAnInstanceOf(SomeLibModule); })); it('should use set up precompile components', @@ -284,11 +283,10 @@ export function main() { expect(el.children[0].properties['title']).toBe('transformed someValue'); })); - it('should use set up nested modules', - withModule(() => moduleConfig) - .inject([SomeNestedModule], (nestedModule: SomeNestedModule) => { - expect(nestedModule).toBeAnInstanceOf(SomeNestedModule); - })); + it('should use set up library modules', + withModule(() => moduleConfig).inject([SomeLibModule], (libModule: SomeLibModule) => { + expect(libModule).toBeAnInstanceOf(SomeLibModule); + })); it('should use set up precompile components', withModule(() => moduleConfig) @@ -301,7 +299,7 @@ export function main() { describe('precompile components with template url', () => { beforeEach(async(() => { - configureModule({precompile: [CompWithUrlTemplate]}); + configureModule({declarations: [CompWithUrlTemplate], precompile: [CompWithUrlTemplate]}); doAsyncPrecompilation(); })); @@ -450,7 +448,12 @@ export function main() { expect( () => it('should fail', - withModule(() => { return {precompile: [CompWithUrlTemplate]}; }) + withModule(() => { + return { + declarations: [CompWithUrlTemplate], + precompile: [CompWithUrlTemplate] + }; + }) .inject( [ComponentFactoryResolver], (resolver: ComponentFactoryResolver) => { diff --git a/modules/@angular/platform-browser/test/web_workers/worker/renderer_integration_spec.ts b/modules/@angular/platform-browser/test/web_workers/worker/renderer_integration_spec.ts index e6b26be995..c8b393389f 100644 --- a/modules/@angular/platform-browser/test/web_workers/worker/renderer_integration_spec.ts +++ b/modules/@angular/platform-browser/test/web_workers/worker/renderer_integration_spec.ts @@ -23,8 +23,8 @@ import {MessageBasedRenderer} from '@angular/platform-browser/src/web_workers/ui import {createPairedMessageBuses, PairedMessageBuses} from '../shared/web_worker_test_util'; import {ServiceMessageBrokerFactory_} from '@angular/platform-browser/src/web_workers/shared/service_message_broker'; import {dispatchEvent} from '../../../../platform-browser/testing/browser_util'; -import {BrowserTestModule} from '@angular/platform-browser/testing'; -import {browserDynamicTestPlatform} from '@angular/platform-browser-dynamic/testing'; +import {BrowserTestingModule} from '@angular/platform-browser/testing'; +import {browserDynamicTestingPlatform} from '@angular/platform-browser-dynamic/testing'; export function main() { function createWebWorkerBrokerFactory( @@ -65,8 +65,8 @@ export function main() { beforeEach(() => { uiRenderStore = new RenderStore(); var testUiInjector = new TestBed(); - testUiInjector.platform = browserDynamicTestPlatform(); - testUiInjector.appModule = BrowserTestModule; + testUiInjector.platform = browserDynamicTestingPlatform(); + testUiInjector.ngModule = BrowserTestingModule; testUiInjector.configureModule({ providers: [ Serializer, {provide: RenderStore, useValue: uiRenderStore}, @@ -74,7 +74,7 @@ export function main() { {provide: RootRenderer, useExisting: DomRootRenderer} ] }); - testUiInjector.initTestAppModule(); + testUiInjector.initTestModule(); var uiSerializer = testUiInjector.get(Serializer); var domRootRenderer = testUiInjector.get(DomRootRenderer); workerRenderStore = new RenderStore(); diff --git a/modules/@angular/platform-browser/testing/browser.ts b/modules/@angular/platform-browser/testing/browser.ts index eb464072da..659d637e4f 100644 --- a/modules/@angular/platform-browser/testing/browser.ts +++ b/modules/@angular/platform-browser/testing/browser.ts @@ -7,9 +7,9 @@ */ import {LocationStrategy} from '@angular/common'; -import {APP_ID, AppModule, NgZone, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, assertPlatform, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; +import {APP_ID, NgModule, NgZone, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, assertPlatform, corePlatform, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; -import {BROWSER_APP_PROVIDERS, BrowserModule} from '../src/browser'; +import {BrowserModule} from '../src/browser'; import {BrowserDomAdapter} from '../src/browser/browser_adapter'; import {AnimationDriver} from '../src/dom/animation_driver'; import {ELEMENT_PROBE_PROVIDERS} from '../src/dom/debug/ng_probe'; @@ -25,45 +25,46 @@ function createNgZone(): NgZone { return new NgZone({enableLongStackTrace: true}); } +const _TEST_BROWSER_PLATFORM_PROVIDERS: Array = + [{provide: PLATFORM_INITIALIZER, useValue: initBrowserTests, multi: true}]; + /** * Providers for the browser test platform * - * @experimental + * @deprecated Use `browserTestingPlatform()` or create a custom platform factory via + * `createPlatformFactory(browserTestingPlatform, ...)` */ -export const TEST_BROWSER_PLATFORM_PROVIDERS: Array = [ - PLATFORM_COMMON_PROVIDERS, - {provide: PLATFORM_INITIALIZER, useValue: initBrowserTests, multi: true} -]; +export const TEST_BROWSER_PLATFORM_PROVIDERS: Array = + [PLATFORM_COMMON_PROVIDERS, _TEST_BROWSER_PLATFORM_PROVIDERS]; /** - * @deprecated Use initTestEnvironment with BrowserTestModule instead. + * @deprecated Use initTestEnvironment with BrowserTestModule instead. This is empty for backwards + * compatibility, + * as all of our bootstrap methods add a module implicitly, i.e. keeping this filled would add the + * providers 2x. */ -export const TEST_BROWSER_APPLICATION_PROVIDERS: Array = [ - BROWSER_APP_PROVIDERS, {provide: APP_ID, useValue: 'a'}, ELEMENT_PROBE_PROVIDERS, - {provide: NgZone, useFactory: createNgZone}, - {provide: AnimationDriver, useValue: AnimationDriver.NOOP} -]; +export const TEST_BROWSER_APPLICATION_PROVIDERS: Array = []; /** * Platform for testing * * @experimental API related to bootstrapping are still under review. */ -export const browserTestPlatform = - createPlatformFactory('browserTest', TEST_BROWSER_PLATFORM_PROVIDERS); +export const browserTestingPlatform = + createPlatformFactory(corePlatform, 'browserTesting', _TEST_BROWSER_PLATFORM_PROVIDERS); /** - * AppModule for testing. + * NgModule for testing. * - * @stable + * @experimental */ -@AppModule({ - modules: [BrowserModule], +@NgModule({ + exports: [BrowserModule], providers: [ {provide: APP_ID, useValue: 'a'}, ELEMENT_PROBE_PROVIDERS, {provide: NgZone, useFactory: createNgZone}, {provide: AnimationDriver, useValue: AnimationDriver.NOOP} ] }) -export class BrowserTestModule { +export class BrowserTestingModule { } diff --git a/modules/@angular/platform-server/core_private.ts b/modules/@angular/platform-server/core_private.ts index 9cc4f2dd68..98e8f67072 100644 --- a/modules/@angular/platform-server/core_private.ts +++ b/modules/@angular/platform-server/core_private.ts @@ -11,3 +11,4 @@ import {__core_private__ as r, __core_private_types__ as t} from '@angular/core' export var reflector: typeof t.reflector = r.reflector; export var ReflectionCapabilities: typeof t.ReflectionCapabilities = r.ReflectionCapabilities; export var wtfInit: typeof t.wtfInit = r.wtfInit; +export var Console: typeof t.Console = r.Console; \ No newline at end of file diff --git a/modules/@angular/platform-server/src/server.ts b/modules/@angular/platform-server/src/server.ts index 6bc7b4fbf3..82c5449ebb 100644 --- a/modules/@angular/platform-server/src/server.ts +++ b/modules/@angular/platform-server/src/server.ts @@ -7,11 +7,13 @@ */ import {PlatformLocation} from '@angular/common'; -import {CompilerFactory, ComponentRef, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, Type, assertPlatform, coreLoadAndBootstrap, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; -import {BROWSER_DYNAMIC_TEST_COMPILER_FACTORY} from '@angular/platform-browser-dynamic/testing'; +import {analyzeAppProvidersForDeprecatedConfiguration, coreDynamicPlatform} from '@angular/compiler'; +import {ApplicationRef, CompilerFactory, ComponentRef, NgModule, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, Type, assertPlatform, bootstrapModule, corePlatform, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; -import {ReflectionCapabilities, reflector, wtfInit} from '../core_private'; +import {Console, ReflectionCapabilities, reflector, wtfInit} from '../core_private'; +import {ConcreteType} from './facade/lang'; import {Parse5DomAdapter} from './parse5_adapter'; function notSupported(feature: string): Error { @@ -31,23 +33,21 @@ class ServerPlatformLocation extends PlatformLocation { back(): void { notSupported('back'); }; } -/** - * A set of providers to initialize the Angular platform in a server. - * - * Used automatically by `serverBootstrap`, or can be passed to `platform`. - * @experimental - */ -export const SERVER_PLATFORM_PROVIDERS: Array = [ - PLATFORM_COMMON_PROVIDERS, +export const INTERNAL_SERVER_PLATFORM_PROVIDERS: Array = [ {provide: PLATFORM_INITIALIZER, useValue: initParse5Adapter, multi: true}, {provide: PlatformLocation, useClass: ServerPlatformLocation}, ]; -const SERVER_DYNAMIC_PROVIDERS: any[] = [ - SERVER_PLATFORM_PROVIDERS, - {provide: CompilerFactory, useValue: BROWSER_DYNAMIC_TEST_COMPILER_FACTORY}, -]; +/** + * A set of providers to initialize the Angular platform in a server. + * + * Used automatically by `serverBootstrap`, or can be passed to `platform`. + * @deprecated Use `serverPlatform()` or create a custom platform factory via + * `createPlatformFactory(serverPlatform, ...)` + */ +export const SERVER_PLATFORM_PROVIDERS: Array = + [PLATFORM_COMMON_PROVIDERS, INTERNAL_SERVER_PLATFORM_PROVIDERS]; function initParse5Adapter() { Parse5DomAdapter.makeCurrent(); @@ -57,7 +57,8 @@ function initParse5Adapter() { /** * @experimental */ -export const serverPlatform = createPlatformFactory('server', SERVER_PLATFORM_PROVIDERS); +export const serverPlatform = + createPlatformFactory(corePlatform, 'server', INTERNAL_SERVER_PLATFORM_PROVIDERS); /** * The server platform that supports the runtime compiler. @@ -65,7 +66,7 @@ export const serverPlatform = createPlatformFactory('server', SERVER_PLATFORM_PR * @experimental */ export const serverDynamicPlatform = - createPlatformFactory('serverDynamic', SERVER_DYNAMIC_PROVIDERS); + createPlatformFactory(coreDynamicPlatform, 'serverDynamic', INTERNAL_SERVER_PLATFORM_PROVIDERS); /** * Used to bootstrap Angular in server environment (such as node). @@ -81,16 +82,35 @@ export const serverDynamicPlatform = * serverBootstrap(..., [BROWSER_APP_PROVIDERS, BROWSER_APP_COMPILER_PROVIDERS]) * ``` * - * @deprecated create an {@link AppModule} and use {@link bootstrapModule} with the {@link + * @deprecated create an {@link NgModule} and use {@link bootstrapModule} with the {@link * serverDynamicPlatform}() * instead. */ -export function serverBootstrap( - appComponentType: Type, - providers: Array): Promise> { +export function serverBootstrap( + appComponentType: ConcreteType, + customProviders: Array): Promise> { console.warn( - 'serverBootstrap is deprecated. Create an @AppModule and use `bootstrapModule` with the `serverDynamicPlatform()` instead.'); + 'serverBootstrap is deprecated. Create an @NgModule and use `bootstrapModule` with the `serverDynamicPlatform()` instead.'); reflector.reflectionCapabilities = new ReflectionCapabilities(); - var appInjector = ReflectiveInjector.resolveAndCreate(providers, serverPlatform().injector); - return coreLoadAndBootstrap(appComponentType, appInjector); + + const deprecatedConfiguration = analyzeAppProvidersForDeprecatedConfiguration(customProviders); + const declarations = [deprecatedConfiguration.moduleDeclarations.concat([appComponentType])]; + + @NgModule({ + providers: customProviders, + declarations: declarations, + imports: [BrowserModule], + precompile: [appComponentType] + }) + class DynamicModule { + } + + return bootstrapModule( + DynamicModule, serverDynamicPlatform(), deprecatedConfiguration.compilerOptions) + .then((moduleRef) => { + const console = moduleRef.injector.get(Console); + deprecatedConfiguration.deprecationMessages.forEach((msg) => console.warn(msg)); + const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef); + return appRef.bootstrap(appComponentType); + }); } diff --git a/modules/@angular/platform-server/testing/server.ts b/modules/@angular/platform-server/testing/server.ts index 0ce06c85e4..dfd3ccd81d 100644 --- a/modules/@angular/platform-server/testing/server.ts +++ b/modules/@angular/platform-server/testing/server.ts @@ -6,44 +6,67 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModule, CompilerFactory, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, assertPlatform, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; -import {BROWSER_DYNAMIC_TEST_COMPILER_FACTORY, BrowserDynamicTestModule, TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS} from '@angular/platform-browser-dynamic/testing'; +import {analyzeAppProvidersForDeprecatedConfiguration} from '@angular/compiler'; +import {coreDynamicTestingPlatform} from '@angular/compiler/testing'; +import {CompilerFactory, CompilerOptions, NgModule, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, assertPlatform, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; +import {initTestEnvironment} from '@angular/core/testing'; +import {BrowserDynamicTestingModule, TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS, browserDynamicTestingPlatform} from '@angular/platform-browser-dynamic/testing'; +import {Console} from '../core_private'; +import {serverPlatform} from '../index'; import {Parse5DomAdapter} from '../src/parse5_adapter'; - -function initServerTests() { - Parse5DomAdapter.makeCurrent(); -} - -/** - * @deprecated Use initTestEnvironment with serverTestPlatform instead. - */ -export const TEST_SERVER_PLATFORM_PROVIDERS: Array = - /*@ts2dart_const*/[ - PLATFORM_COMMON_PROVIDERS, - /*@ts2dart_Provider*/ {provide: PLATFORM_INITIALIZER, useValue: initServerTests, multi: true}, - {provide: CompilerFactory, useValue: BROWSER_DYNAMIC_TEST_COMPILER_FACTORY}, - ]; +import {INTERNAL_SERVER_PLATFORM_PROVIDERS} from '../src/server'; /** * Platform for testing * * @experimental API related to bootstrapping are still under review. */ -export const serverTestPlatform = - createPlatformFactory('serverTest', TEST_SERVER_PLATFORM_PROVIDERS); +export const serverTestingPlatform = createPlatformFactory( + coreDynamicTestingPlatform, 'serverTesting', INTERNAL_SERVER_PLATFORM_PROVIDERS); /** - * AppModule for testing. + * NgModule for testing. * - * @stable + * @experimental API related to bootstrapping are still under review. */ -@AppModule({modules: [BrowserDynamicTestModule]}) -export class ServerTestModule { +@NgModule({exports: [BrowserDynamicTestingModule]}) +export class ServerTestingModule { } /** - * @deprecated Use initTestEnvironment with ServerTestModule instead. + * Providers of the `serverTestingPlatform` to be used for creating own platform based on this. + * + * @deprecated Use `serverTestingPlatform()` or create a custom platform factory via + * `createPlatformFactory(serverTestingPlatform, ...)` */ -export const TEST_SERVER_APPLICATION_PROVIDERS: Array = - TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS; +export const TEST_SERVER_PLATFORM_PROVIDERS: Array = + // Note: This is not a real provider but a hack to still support the deprecated + // `setBaseTestProviders` method! + [(appProviders: any[]) => { + const deprecatedConfiguration = analyzeAppProvidersForDeprecatedConfiguration(appProviders); + const platformRef = createPlatformFactory(serverTestingPlatform, 'serverTestingDeprecated', [{ + provide: CompilerOptions, + useValue: deprecatedConfiguration.compilerOptions, + multi: true + }])(); + + @NgModule({ + exports: [ServerTestingModule], + declarations: [deprecatedConfiguration.moduleDeclarations] + }) + class DynamicTestModule { + } + + const testInjector = initTestEnvironment(DynamicTestModule, platformRef); + const console: Console = testInjector.get(Console); + deprecatedConfiguration.deprecationMessages.forEach((msg) => console.warn(msg)); + }]; + +/** + * @deprecated Use initTestEnvironment with ServerTestModule instead. This is empty for backwards + * compatibility, + * as all of our bootstrap methods add a module implicitly, i.e. keeping this filled would add the + * providers 2x. + */ +export const TEST_SERVER_APPLICATION_PROVIDERS: Array = []; diff --git a/modules/@angular/router/README.md b/modules/@angular/router/README.md index e3306408c3..1d722c2ff8 100644 --- a/modules/@angular/router/README.md +++ b/modules/@angular/router/README.md @@ -9,4 +9,17 @@ The Angular router is designed to solve these problems. Using the router, you ca Read the overview of the Router [here](http://victorsavkin.com/post/145672529346/angular-router). ## Guide -Read the dev guide [here](https://angular.io/docs/ts/latest/guide/router.html). \ No newline at end of file +Read the dev guide [here](https://angular.io/docs/ts/latest/guide/router.html). + +## Local development + +``` +# keep @angular/router fresh +$ ./scripts/karma.sh + +# keep @angular/core fresh +$ ../../../node_modules/.bin/tsc -p modules --emitDecoratorMetadata -w + +# start karma +$ ./scripts/karma.sh +``` diff --git a/modules/@angular/router/index.ts b/modules/@angular/router/index.ts index 069d3f8a3c..193864b841 100644 --- a/modules/@angular/router/index.ts +++ b/modules/@angular/router/index.ts @@ -14,7 +14,7 @@ export {RouterLinkActive} from './src/directives/router_link_active'; export {RouterOutlet} from './src/directives/router_outlet'; export {CanActivate, CanActivateChild, CanDeactivate, Resolve} from './src/interfaces'; export {Event, NavigationCancel, NavigationEnd, NavigationError, NavigationExtras, NavigationStart, Router, RoutesRecognized} from './src/router'; -export {ROUTER_DIRECTIVES, RouterModule} from './src/router_module'; +export {ROUTER_DIRECTIVES, RouterModule, RouterModuleWithoutProviders} from './src/router_module'; export {RouterOutletMap} from './src/router_outlet_map'; export {provideRouter} from './src/router_providers'; export {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot} from './src/router_state'; diff --git a/modules/@angular/router/karma-test-shim.js b/modules/@angular/router/karma-test-shim.js index 222a4d192a..ac87dabe79 100644 --- a/modules/@angular/router/karma-test-shim.js +++ b/modules/@angular/router/karma-test-shim.js @@ -73,8 +73,8 @@ Promise.all([ var testingBrowser = providers[1]; testing.initTestEnvironment( - testingBrowser.BrowserDynamicTestModule, - testingBrowser.browserDynamicTestPlatform()); + testingBrowser.BrowserDynamicTestingModule, + testingBrowser.browserDynamicTestingPlatform()); }).then(function() { // Finally, load all spec files. diff --git a/modules/@angular/router/src/common_router_providers.ts b/modules/@angular/router/src/common_router_providers.ts index 4b5300107b..290c8b2423 100644 --- a/modules/@angular/router/src/common_router_providers.ts +++ b/modules/@angular/router/src/common_router_providers.ts @@ -7,7 +7,7 @@ */ import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common'; -import {ANALYZE_FOR_PRECOMPILE, APP_INITIALIZER, AppModuleFactoryLoader, ApplicationRef, ComponentResolver, Injector, OpaqueToken, SystemJsAppModuleLoader} from '@angular/core'; +import {ANALYZE_FOR_PRECOMPILE, APP_INITIALIZER, ApplicationRef, ComponentResolver, Injector, NgModuleFactoryLoader, OpaqueToken, SystemJsNgModuleLoader} from '@angular/core'; import {Routes} from './config'; import {Router} from './router'; @@ -26,7 +26,7 @@ export interface ExtraOptions { enableTracing?: boolean; } export function setupRouter( ref: ApplicationRef, resolver: ComponentResolver, urlSerializer: UrlSerializer, outletMap: RouterOutletMap, location: Location, injector: Injector, - loader: AppModuleFactoryLoader, config: Routes, opts: ExtraOptions) { + loader: NgModuleFactoryLoader, config: Routes, opts: ExtraOptions) { if (ref.componentTypes.length == 0) { throw new Error('Bootstrap at least one component before injecting Router.'); } @@ -100,7 +100,7 @@ export function provideRouter(routes: Routes, config: ExtraOptions): any[] { useFactory: setupRouter, deps: [ ApplicationRef, ComponentResolver, UrlSerializer, RouterOutletMap, Location, Injector, - AppModuleFactoryLoader, ROUTES, ROUTER_CONFIGURATION + NgModuleFactoryLoader, ROUTES, ROUTER_CONFIGURATION ] }, @@ -108,7 +108,7 @@ export function provideRouter(routes: Routes, config: ExtraOptions): any[] { // Trigger initial navigation {provide: APP_INITIALIZER, multi: true, useFactory: setupRouterInitializer, deps: [Injector]}, - {provide: AppModuleFactoryLoader, useClass: SystemJsAppModuleLoader} + {provide: NgModuleFactoryLoader, useClass: SystemJsNgModuleLoader} ]; } @@ -118,7 +118,7 @@ export function provideRouter(routes: Routes, config: ExtraOptions): any[] { * ### Example * * ``` - * @AppModule({providers: [ + * @NgModule({providers: [ * provideRoutes([{path: 'home', component: Home}]) * ]}) * class LazyLoadedModule { @@ -141,7 +141,7 @@ export function provideRoutes(routes: Routes): any { * ### Example * * ``` - * @AppModule({providers: [ + * @NgModule({providers: [ * provideRouterOptions({enableTracing: true}) * ]}) * class LazyLoadedModule { diff --git a/modules/@angular/router/src/resolve.ts b/modules/@angular/router/src/resolve.ts index c47cf80767..80cdde8f37 100644 --- a/modules/@angular/router/src/resolve.ts +++ b/modules/@angular/router/src/resolve.ts @@ -41,8 +41,7 @@ function resolveNode( function resolveComponent( resolver: ComponentResolver, snapshot: ActivatedRouteSnapshot): Promise { - // TODO: vsavkin change to typeof snapshot.component === 'string' in beta2 - if (snapshot.component && snapshot._routeConfig) { + if (snapshot.component && snapshot._routeConfig && typeof snapshot.component === 'string') { return resolver.resolveComponent(snapshot.component); } else { return Promise.resolve(null); diff --git a/modules/@angular/router/src/router.ts b/modules/@angular/router/src/router.ts index 65592e1d16..1818802dbf 100644 --- a/modules/@angular/router/src/router.ts +++ b/modules/@angular/router/src/router.ts @@ -13,7 +13,7 @@ import 'rxjs/add/operator/reduce'; import 'rxjs/add/operator/every'; import {Location} from '@angular/common'; -import {AppModuleFactoryLoader, ComponentFactoryResolver, ComponentResolver, Injector, ReflectiveInjector, Type} from '@angular/core'; +import {ComponentFactoryResolver, ComponentResolver, Injector, NgModuleFactoryLoader, ReflectiveInjector, Type} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; import {Subscription} from 'rxjs/Subscription'; @@ -146,7 +146,7 @@ export class Router { constructor( private rootComponentType: Type, private resolver: ComponentResolver, private urlSerializer: UrlSerializer, private outletMap: RouterOutletMap, - private location: Location, private injector: Injector, loader: AppModuleFactoryLoader, + private location: Location, private injector: Injector, loader: NgModuleFactoryLoader, config: Routes) { this.resetConfig(config); this.routerEvents = new Subject(); diff --git a/modules/@angular/router/src/router_config_loader.ts b/modules/@angular/router/src/router_config_loader.ts index 632e8de43b..5f73d112a3 100644 --- a/modules/@angular/router/src/router_config_loader.ts +++ b/modules/@angular/router/src/router_config_loader.ts @@ -6,13 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModuleFactoryLoader, ComponentFactoryResolver, Injector, OpaqueToken} from '@angular/core'; +import {ComponentFactoryResolver, Injector, NgModuleFactoryLoader, OpaqueToken} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import {fromPromise} from 'rxjs/observable/fromPromise'; import {Route} from './config'; + /** * @deprecated use Routes */ @@ -26,7 +27,7 @@ export class LoadedRouterConfig { } export class RouterConfigLoader { - constructor(private loader: AppModuleFactoryLoader) {} + constructor(private loader: NgModuleFactoryLoader) {} load(parentInjector: Injector, path: string): Observable { return fromPromise(this.loader.load(path).then(r => { @@ -35,4 +36,4 @@ export class RouterConfigLoader { ref.injector.get(ROUTES), ref.injector, ref.componentFactoryResolver); })); } -} \ No newline at end of file +} diff --git a/modules/@angular/router/src/router_module.ts b/modules/@angular/router/src/router_module.ts index 259ea41574..10b288004b 100644 --- a/modules/@angular/router/src/router_module.ts +++ b/modules/@angular/router/src/router_module.ts @@ -7,7 +7,7 @@ */ import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common'; -import {AppModule, AppModuleFactoryLoader, ApplicationRef, ComponentResolver, Injector, OpaqueToken, SystemJsAppModuleLoader} from '@angular/core'; +import {ApplicationRef, ComponentResolver, Injector, NgModule, NgModuleFactoryLoader, OpaqueToken, SystemJsNgModuleLoader} from '@angular/core'; import {ROUTER_CONFIGURATION, rootRoute, setupRouter} from './common_router_providers'; import {RouterLink, RouterLinkWithHref} from './directives/router_link'; @@ -33,14 +33,35 @@ export const ROUTER_PROVIDERS: any[] = [ useFactory: setupRouter, deps: [ ApplicationRef, ComponentResolver, UrlSerializer, RouterOutletMap, Location, Injector, - AppModuleFactoryLoader, ROUTES, ROUTER_CONFIGURATION + NgModuleFactoryLoader, ROUTES, ROUTER_CONFIGURATION ] }, RouterOutletMap, {provide: ActivatedRoute, useFactory: rootRoute, deps: [Router]}, - {provide: AppModuleFactoryLoader, useClass: SystemJsAppModuleLoader}, + {provide: NgModuleFactoryLoader, useClass: SystemJsNgModuleLoader}, {provide: ROUTER_CONFIGURATION, useValue: {enableTracing: false}} ]; + +/** + * Router module to be used for lazy loaded parts. + * + * ### Example + * + * ``` + * @NgModule({ + * imports: [RouterModuleWithoutProviders] + * }) + * class TeamsModule {} + * ``` + * + * @experimental We will soon have a way for the `RouterModule` to be imported with and without a + * provider, + * and then this module will be removed. + */ +@NgModule({declarations: ROUTER_DIRECTIVES, exports: ROUTER_DIRECTIVES}) +export class RouterModuleWithoutProviders { +} + /** * Router module. * @@ -52,7 +73,7 @@ export const ROUTER_PROVIDERS: any[] = [ * * @experimental */ -@AppModule({directives: ROUTER_DIRECTIVES, providers: ROUTER_PROVIDERS}) +@NgModule({exports: [RouterModuleWithoutProviders], providers: ROUTER_PROVIDERS}) export class RouterModule { constructor(private injector: Injector) { setTimeout(() => { @@ -64,4 +85,4 @@ export class RouterModule { } }, 0); } -} \ No newline at end of file +} diff --git a/modules/@angular/router/test/router.spec.ts b/modules/@angular/router/test/router.spec.ts index 591540fa61..1343f5da97 100644 --- a/modules/@angular/router/test/router.spec.ts +++ b/modules/@angular/router/test/router.spec.ts @@ -9,21 +9,27 @@ import 'rxjs/add/operator/map'; import {Location} from '@angular/common'; -import {AppModule, AppModuleFactoryLoader, Component} from '@angular/core'; +import {Component, NgModule, NgModuleFactoryLoader} from '@angular/core'; import {ComponentFixture, TestComponentBuilder, addProviders, configureModule, fakeAsync, inject, tick} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/matchers'; import {Observable} from 'rxjs/Observable'; import {of } from 'rxjs/observable/of'; -import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanDeactivate, Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Params, ROUTER_DIRECTIVES, Resolve, Router, RouterStateSnapshot, RoutesRecognized, provideRoutes} from '../index'; -import {RouterTestingModule, SpyAppModuleFactoryLoader} from '../testing'; +import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanDeactivate, Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Params, ROUTER_DIRECTIVES, Resolve, Router, RouterModuleWithoutProviders, RouterStateSnapshot, RoutesRecognized, provideRoutes} from '../index'; +import {RouterTestingModule, SpyNgModuleFactoryLoader} from '../testing'; describe('Integration', () => { beforeEach(() => { configureModule({ - modules: [RouterTestingModule], + imports: [RouterTestingModule], providers: [provideRoutes( - [{path: '', component: BlankCmp}, {path: 'simple', component: SimpleCmp}])] + [{path: '', component: BlankCmp}, {path: 'simple', component: SimpleCmp}])], + declarations: [ + BlankCmp, SimpleCmp, TeamCmp, UserCmp, StringLinkCmp, DummyLinkCmp, AbsoluteLinkCmp, + RelativeLinkCmp, DummyLinkWithParentCmp, LinkWithQueryParamsAndFragment, CollectParamsCmp, + QueryParamsAndFragmentCmp, StringLinkButtonCmp, WrapperCmp, LinkInNgIf, + ComponentRecordingQueryParams, ComponentRecordingRoutePathAndUrl, RouteCmp + ] }); }); @@ -254,24 +260,6 @@ describe('Integration', () => { it('should not push query params into components that will be deactivated', fakeAsync( inject([Router, TestComponentBuilder], (router: Router, tcb: TestComponentBuilder) => { - @Component({template: ''}) - class ComponentRecordingQueryParams { - recordedQueryParams: any[] = []; - subscription: any; - constructor(r: Router) { - this.subscription = - r.routerState.queryParams.subscribe(r => this.recordedQueryParams.push(r)); - } - - ngOnDestroy() { this.subscription.unsubscribe(); } - } - - @Component({ - template: '', - precompile: [SimpleCmp, ComponentRecordingQueryParams] - }) - class RootCmp { - } router.resetConfig([ {path: '', component: ComponentRecordingQueryParams}, @@ -524,25 +512,10 @@ describe('Integration', () => { fakeAsync(inject( [Router, TestComponentBuilder, Location], (router: Router, tcb: TestComponentBuilder, location: Location) => { - @Component({selector: 'cmp', template: ''}) - class Cmp { - private path: any; - private url: any; - constructor(router: Router, route: ActivatedRoute) { - this.path = router.routerState.pathFromRoot(route); - this.url = router.url.toString(); - } - } + const fixture = createRoot(tcb, router, RootCmp); - @Component( - {selector: 'root', template: '', precompile: [Cmp]}) - class Root { - } - - const fixture = createRoot(tcb, router, Root); - - router.resetConfig([{path: 'cmp', component: Cmp}]); + router.resetConfig([{path: 'cmp', component: ComponentRecordingRoutePathAndUrl}]); router.navigateByUrl('/cmp'); advance(fixture); @@ -1330,9 +1303,9 @@ describe('Integration', () => { describe('lazy loading', () => { it('works', fakeAsync(inject( - [Router, TestComponentBuilder, Location, AppModuleFactoryLoader], + [Router, TestComponentBuilder, Location, NgModuleFactoryLoader], (router: Router, tcb: TestComponentBuilder, location: Location, - loader: SpyAppModuleFactoryLoader) => { + loader: SpyNgModuleFactoryLoader) => { @Component({ selector: 'lazy', template: 'lazy-loaded-parent []', @@ -1345,12 +1318,14 @@ describe('Integration', () => { class ChildLazyLoadedComponent { } - @AppModule({ + @NgModule({ + declarations: [ParentLazyLoadedComponent, ChildLazyLoadedComponent], providers: [provideRoutes([{ path: 'loaded', component: ParentLazyLoadedComponent, children: [{path: 'child', component: ChildLazyLoadedComponent}] }])], + imports: [RouterModuleWithoutProviders], precompile: [ParentLazyLoadedComponent, ChildLazyLoadedComponent] }) class LoadedModule { @@ -1373,9 +1348,9 @@ describe('Integration', () => { it('should use the injector of the lazily-loaded configuration', fakeAsync(inject( - [Router, TestComponentBuilder, Location, AppModuleFactoryLoader], + [Router, TestComponentBuilder, Location, NgModuleFactoryLoader], (router: Router, tcb: TestComponentBuilder, location: Location, - loader: SpyAppModuleFactoryLoader) => { + loader: SpyNgModuleFactoryLoader) => { class LazyLoadedService {} @Component({selector: 'lazy', template: 'lazy-loaded', directives: ROUTER_DIRECTIVES}) @@ -1383,8 +1358,10 @@ describe('Integration', () => { constructor(service: LazyLoadedService) {} } - @AppModule({ + @NgModule({ precompile: [LazyLoadedComponent], + declarations: [LazyLoadedComponent], + imports: [RouterModuleWithoutProviders], providers: [ LazyLoadedService, provideRoutes([{ path: '', @@ -1412,9 +1389,9 @@ describe('Integration', () => { it('error emit an error when cannot load a config', fakeAsync(inject( - [Router, TestComponentBuilder, Location, AppModuleFactoryLoader], + [Router, TestComponentBuilder, Location, NgModuleFactoryLoader], (router: Router, tcb: TestComponentBuilder, location: Location, - loader: SpyAppModuleFactoryLoader) => { + loader: SpyNgModuleFactoryLoader) => { loader.stubbedModules = {}; const fixture = createRoot(tcb, router, RootCmp); @@ -1609,6 +1586,29 @@ class DummyLinkWithParentCmp { constructor(route: ActivatedRoute) { this.exact = (route.snapshot.params).exact === 'true'; } } + +@Component({template: ''}) +class ComponentRecordingQueryParams { + recordedQueryParams: any[] = []; + subscription: any; + constructor(r: Router) { + this.subscription = r.routerState.queryParams.subscribe(r => this.recordedQueryParams.push(r)); + } + + ngOnDestroy() { this.subscription.unsubscribe(); } +} + +@Component({selector: 'cmp', template: ''}) +class ComponentRecordingRoutePathAndUrl { + private path: any; + private url: any; + + constructor(router: Router, route: ActivatedRoute) { + this.path = router.routerState.pathFromRoot(route); + this.url = router.url.toString(); + } +} + @Component({ selector: 'root-cmp', template: ``, @@ -1616,7 +1616,8 @@ class DummyLinkWithParentCmp { precompile: [ BlankCmp, SimpleCmp, TeamCmp, UserCmp, StringLinkCmp, DummyLinkCmp, AbsoluteLinkCmp, RelativeLinkCmp, DummyLinkWithParentCmp, LinkWithQueryParamsAndFragment, CollectParamsCmp, - QueryParamsAndFragmentCmp, StringLinkButtonCmp, WrapperCmp, LinkInNgIf + QueryParamsAndFragmentCmp, StringLinkButtonCmp, WrapperCmp, LinkInNgIf, + ComponentRecordingQueryParams, ComponentRecordingRoutePathAndUrl ] }) class RootCmp { @@ -1632,6 +1633,7 @@ class RootCmp { class RootCmpWithTwoOutlets { } + function advance(fixture: ComponentFixture): void { tick(); fixture.detectChanges(); diff --git a/modules/@angular/router/testing/router_testing_module.ts b/modules/@angular/router/testing/router_testing_module.ts index ba8df0c29e..fbc3e4847e 100644 --- a/modules/@angular/router/testing/router_testing_module.ts +++ b/modules/@angular/router/testing/router_testing_module.ts @@ -8,29 +8,29 @@ import {Location, LocationStrategy} from '@angular/common'; import {MockLocationStrategy, SpyLocation} from '@angular/common/testing'; -import {AppModule, AppModuleFactory, AppModuleFactoryLoader, Compiler, ComponentResolver, Injectable, Injector} from '@angular/core'; +import {Compiler, ComponentResolver, Injectable, Injector, NgModule, NgModuleFactory, NgModuleFactoryLoader} from '@angular/core'; import {Router, RouterOutletMap, Routes, UrlSerializer} from '../index'; import {ROUTES} from '../src/router_config_loader'; -import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from '../src/router_module'; +import {RouterModule} from '../src/router_module'; /** - * A spy for {@link AppModuleFactoryLoader} that allows tests to simulate the loading of app module + * A spy for {@link NgModuleFactoryLoader} that allows tests to simulate the loading of ng module * factories. * * @experimental */ @Injectable() -export class SpyAppModuleFactoryLoader implements AppModuleFactoryLoader { +export class SpyNgModuleFactoryLoader implements NgModuleFactoryLoader { public stubbedModules: {[path: string]: any} = {}; constructor(private compiler: Compiler) {} - load(path: string): Promise> { + load(path: string): Promise> { if (this.stubbedModules[path]) { - return this.compiler.compileAppModuleAsync(this.stubbedModules[path]); + return this.compiler.compileModuleAsync(this.stubbedModules[path]); } else { return Promise.reject(new Error(`Cannot find module ${path}`)); } @@ -39,20 +39,20 @@ export class SpyAppModuleFactoryLoader implements AppModuleFactoryLoader { function setupTestingRouter( resolver: ComponentResolver, urlSerializer: UrlSerializer, outletMap: RouterOutletMap, - location: Location, loader: AppModuleFactoryLoader, injector: Injector, routes: Routes) { + location: Location, loader: NgModuleFactoryLoader, injector: Injector, routes: Routes) { return new Router(null, resolver, urlSerializer, outletMap, location, injector, loader, routes); } /** * A module setting up the router that should be used for testing. - * It provides spy implementations of Location, LocationStrategy, and AppModuleFactoryLoader. + * It provides spy implementations of Location, LocationStrategy, and NgModuleFactoryLoader. * * # Example: * * ``` * beforeEach(() => { * configureModule({ - * modules: [RouterTestModule], + * modules: [RouterTestingModule], * providers: [provideRoutes( * [{path: '', component: BlankCmp}, {path: 'simple', component: SimpleCmp}])] * }); @@ -61,18 +61,17 @@ function setupTestingRouter( * * @experimental */ -@AppModule({ - directives: ROUTER_DIRECTIVES, +@NgModule({ + exports: [RouterModule], providers: [ - ROUTER_PROVIDERS, {provide: Location, useClass: SpyLocation}, {provide: LocationStrategy, useClass: MockLocationStrategy}, - {provide: AppModuleFactoryLoader, useClass: SpyAppModuleFactoryLoader}, + {provide: NgModuleFactoryLoader, useClass: SpyNgModuleFactoryLoader}, { provide: Router, useFactory: setupTestingRouter, deps: [ - ComponentResolver, UrlSerializer, RouterOutletMap, Location, AppModuleFactoryLoader, + ComponentResolver, UrlSerializer, RouterOutletMap, Location, NgModuleFactoryLoader, Injector, ROUTES ] }, diff --git a/modules/@angular/upgrade/src/upgrade_adapter.ts b/modules/@angular/upgrade/src/upgrade_adapter.ts index e460935d85..5997c24852 100644 --- a/modules/@angular/upgrade/src/upgrade_adapter.ts +++ b/modules/@angular/upgrade/src/upgrade_adapter.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AppModule, ApplicationRef, Compiler, CompilerFactory, ComponentFactory, ComponentResolver, Injector, NgZone, PlatformRef, Provider, ReflectiveInjector, Testability, Type, bootstrapModuleFactory, provide} from '@angular/core'; +import {ApplicationRef, Compiler, CompilerFactory, ComponentFactory, ComponentResolver, Injector, NgModule, NgZone, PlatformRef, Provider, ReflectiveInjector, Testability, Type, bootstrapModuleFactory, provide} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {browserDynamicPlatform} from '@angular/platform-browser-dynamic'; @@ -279,18 +279,19 @@ export class UpgradeAdapter { var upgrade = new UpgradeAdapterRef(); var ng1Injector: angular.IInjectorService = null; var platformRef: PlatformRef = browserDynamicPlatform(); - var compiler: Compiler = platformRef.injector.get(CompilerFactory).createCompiler(); var providers = [ {provide: NG1_INJECTOR, useFactory: () => ng1Injector}, {provide: NG1_COMPILE, useFactory: () => ng1Injector.get(NG1_COMPILE)}, this.providers ]; - @AppModule({providers: providers, modules: [BrowserModule]}) + @NgModule({providers: providers, imports: [BrowserModule]}) class DynamicModule { } - var moduleRef = - bootstrapModuleFactory(compiler.compileAppModuleSync(DynamicModule), platformRef); + const compilerFactory: CompilerFactory = platformRef.injector.get(CompilerFactory); + var moduleRef = bootstrapModuleFactory( + compilerFactory.createCompiler().compileModuleSync(DynamicModule), platformRef); + const boundCompiler: Compiler = moduleRef.injector.get(Compiler); var applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef); var injector: Injector = applicationRef.injector; var ngZone: NgZone = injector.get(NgZone); @@ -304,7 +305,7 @@ export class UpgradeAdapter { var ng1compilePromise: Promise = null; ng1Module.value(NG2_INJECTOR, injector) .value(NG2_ZONE, ngZone) - .value(NG2_COMPILER, compiler) + .value(NG2_COMPILER, boundCompiler) .value(NG2_COMPONENT_FACTORY_REF_MAP, componentFactoryRefMap) .config([ '$provide', '$injector', @@ -384,7 +385,7 @@ export class UpgradeAdapter { }); Promise.all([ng1BootstrapPromise, ng1compilePromise]) - .then(() => { return this.compileNg2Components(compiler, componentFactoryRefMap); }) + .then(() => { return this.compileNg2Components(boundCompiler, componentFactoryRefMap); }) .then(() => { ngZone.run(() => { if (rootScopePrototype) { diff --git a/modules/playground/README.md b/modules/playground/README.md index 8cbeb4ddb2..0f8e32d516 100644 --- a/modules/playground/README.md +++ b/modules/playground/README.md @@ -1,6 +1,6 @@ # How to run the examples locally $ cp -r ./modules/playground ./dist/all/ -$ tsc -p modules --emitDecoratorMetadata -w +$ ./node_modules/.bin/tsc -p modules --emitDecoratorMetadata -w $ gulp serve $ open http://localhost:8000/all/playground/src/hello_world/index.html?bundles=false diff --git a/modules/playground/src/routing/app/inbox-detail.ts b/modules/playground/src/routing/app/inbox-detail.ts index d0780b8d59..551a8ceafc 100644 --- a/modules/playground/src/routing/app/inbox-detail.ts +++ b/modules/playground/src/routing/app/inbox-detail.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, AppModule} from '@angular/core'; +import {Component, NgModule} from '@angular/core'; import {ROUTER_DIRECTIVES, ActivatedRoute, provideRoutes} from '@angular/router'; import {PromiseWrapper} from '@angular/core/src/facade/async'; import {InboxRecord, DbService} from './inbox-app'; @@ -24,7 +24,8 @@ export class InboxDetailCmp { } } -@AppModule({ +@NgModule({ + declarations: [InboxDetailCmp], providers: [provideRoutes([{path: ':id', component: InboxDetailCmp}])] }) -export default class InboxDetailModule {} \ No newline at end of file +export default class InboxDetailModule {} diff --git a/modules/playground/src/routing/index.ts b/modules/playground/src/routing/index.ts index be309f7ce5..4b4d84a289 100644 --- a/modules/playground/src/routing/index.ts +++ b/modules/playground/src/routing/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {InboxApp, ROUTER_CONFIG} from './app/inbox-app'; +import {InboxApp, InboxCmp, DraftsCmp, ROUTER_CONFIG} from './app/inbox-app'; import {bootstrap} from '@angular/platform-browser-dynamic'; import {HashLocationStrategy, LocationStrategy} from '@angular/common'; import {provideRoutes, RouterModule} from '@angular/router'; @@ -17,6 +17,7 @@ export function main() { provideRoutes(ROUTER_CONFIG), {provide: LocationStrategy, useClass: HashLocationStrategy} ], - modules: [RouterModule] + declarations: [InboxCmp, DraftsCmp], + imports: [RouterModule] }); -} \ No newline at end of file +} diff --git a/modules/playground/src/web_workers/message_broker/index.ts b/modules/playground/src/web_workers/message_broker/index.ts index 2f5ba2de63..fea792b146 100644 --- a/modules/playground/src/web_workers/message_broker/index.ts +++ b/modules/playground/src/web_workers/message_broker/index.ts @@ -6,17 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ -import {ApplicationRef} from '@angular/core'; +import {PlatformRef} from '@angular/core'; import {UiArguments, FnArg, PRIMITIVE, ClientMessageBrokerFactory} from '@angular/platform-browser'; import {bootstrapWorkerUi} from "@angular/platform-browser-dynamic"; const ECHO_CHANNEL = "ECHO"; export function main() { - bootstrapWorkerUi("loader.js").then((ref) => afterBootstrap(ref)); + bootstrapWorkerUi("loader.js").then(afterBootstrap); } -function afterBootstrap(ref: ApplicationRef) { +function afterBootstrap(ref: PlatformRef) { let brokerFactory: ClientMessageBrokerFactory = ref.injector.get(ClientMessageBrokerFactory); var broker = brokerFactory.createMessageBroker(ECHO_CHANNEL, false); diff --git a/modules/playground/src/web_workers/router/background_index.ts b/modules/playground/src/web_workers/router/background_index.ts index 176a7838d8..dc47e0135d 100644 --- a/modules/playground/src/web_workers/router/background_index.ts +++ b/modules/playground/src/web_workers/router/background_index.ts @@ -6,17 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {provideRouter} from '@angular/router'; -import {WORKER_APP_LOCATION_PROVIDERS} from '@angular/platform-browser'; -import {bootstrapWorkerApp} from '@angular/platform-browser-dynamic'; +import {workerAppDynamicPlatform} from '@angular/platform-browser-dynamic'; +import {bootstrapModule} from '@angular/core'; -import {HashLocationStrategy, LocationStrategy} from '@angular/common'; -import {App, ROUTES} from './index_common'; +import {AppModule} from './index_common'; export function main() { - bootstrapWorkerApp(App, [ - provideRouter(ROUTES), - WORKER_APP_LOCATION_PROVIDERS, - {provide: LocationStrategy, useClass: HashLocationStrategy} - ]); + bootstrapModule(AppModule, workerAppDynamicPlatform()); } diff --git a/modules/playground/src/web_workers/router/index_common.ts b/modules/playground/src/web_workers/router/index_common.ts index 271fd3f6a7..dd21fec18b 100644 --- a/modules/playground/src/web_workers/router/index_common.ts +++ b/modules/playground/src/web_workers/router/index_common.ts @@ -6,13 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component} from '@angular/core'; +import {Component, NgModule, ApplicationRef} from '@angular/core'; import {Start} from './components/start'; import {About} from './components/about'; import {Contact} from './components/contact'; -import {ROUTER_DIRECTIVES, Router} from '@angular/router'; +import {Router, RouterModule, provideRoutes} from '@angular/router'; +import {WorkerAppModule, WORKER_APP_LOCATION_PROVIDERS} from '@angular/platform-browser'; +import {HashLocationStrategy, LocationStrategy} from '@angular/common'; -@Component({selector: 'app', directives: [ROUTER_DIRECTIVES], templateUrl: 'app.html'}) +@Component({selector: 'app', templateUrl: 'app.html'}) export class App { constructor(router: Router) { // this should not be required once web worker bootstrap method can use modules @@ -24,4 +26,18 @@ export const ROUTES = [ {path: '', component: Start}, {path: 'contact', component: Contact}, {path: 'about', component: About} -]; \ No newline at end of file +]; + +@NgModule({ + imports: [WorkerAppModule, RouterModule], + providers: [provideRoutes(ROUTES), WORKER_APP_LOCATION_PROVIDERS, {provide: LocationStrategy, useClass: HashLocationStrategy}], + precompile: [App], + declarations: [App, Start, Contact, About] +}) +export class AppModule { + constructor(appRef: ApplicationRef) { + appRef.waitForAsyncInitializers().then( () => { + appRef.bootstrap(App); + }); + } +} diff --git a/scripts/ci-lite/offline_compiler_test.sh b/scripts/ci-lite/offline_compiler_test.sh index 5027d1e103..421a5a4ca1 100755 --- a/scripts/ci-lite/offline_compiler_test.sh +++ b/scripts/ci-lite/offline_compiler_test.sh @@ -3,7 +3,7 @@ set -ex -o pipefail # These ones can be `npm link`ed for fast development LINKABLE_PKGS=( - $(pwd)/dist/packages-dist/{common,core,compiler,compiler-cli,platform-{browser,server},platform-browser-dynamic} + $(pwd)/dist/packages-dist/{common,forms,core,compiler,compiler-cli,platform-{browser,server},platform-browser-dynamic} $(pwd)/dist/tools/@angular/tsc-wrapped ) PKGS=( diff --git a/test-main.js b/test-main.js index e77fb7dd23..03007ce42a 100644 --- a/test-main.js +++ b/test-main.js @@ -76,8 +76,8 @@ System.import('@angular/core/testing') return System.import('@angular/platform-browser-dynamic/testing') .then(function(browserTesting) { coreTesting.initTestEnvironment( - browserTesting.BrowserDynamicTestModule, - browserTesting.browserDynamicTestPlatform()); + browserTesting.BrowserDynamicTestingModule, + browserTesting.browserDynamicTestingPlatform()); }); }) .then(function() { diff --git a/tools/cjs-jasmine/test-cjs-main.ts b/tools/cjs-jasmine/test-cjs-main.ts index af511998ef..a710b4955b 100644 --- a/tools/cjs-jasmine/test-cjs-main.ts +++ b/tools/cjs-jasmine/test-cjs-main.ts @@ -2,4 +2,4 @@ var testingPlatformServer = require('../../all/@angular/platform-server/testing/ var testing = require('../../all/@angular/core/testing'); testing.initTestEnvironment( - testingPlatformServer.ServerTestModule, testingPlatformServer.serverTestPlatform()); + testingPlatformServer.ServerTestingModule, testingPlatformServer.serverTestingPlatform()); diff --git a/tools/public_api_guard/common/index.d.ts b/tools/public_api_guard/common/index.d.ts index 728bd35770..a90668e565 100644 --- a/tools/public_api_guard/common/index.d.ts +++ b/tools/public_api_guard/common/index.d.ts @@ -80,6 +80,10 @@ export declare const COMMON_DIRECTIVES: Type[][]; /** @experimental */ export declare const COMMON_PIPES: (typeof AsyncPipe | typeof SlicePipe | typeof ReplacePipe | typeof I18nPluralPipe | typeof I18nSelectPipe)[]; +/** @experimental */ +export declare class CommonModule { +} + /** @experimental */ export declare class Control extends AbstractControl { constructor(value?: any, validator?: ValidatorFn, asyncValidator?: AsyncValidatorFn); diff --git a/tools/public_api_guard/core/index.d.ts b/tools/public_api_guard/core/index.d.ts index f5ec7385bd..725dfb9415 100644 --- a/tools/public_api_guard/core/index.d.ts +++ b/tools/public_api_guard/core/index.d.ts @@ -124,11 +124,15 @@ export declare const APP_ID: any; /** @experimental */ export declare const APP_INITIALIZER: any; -/** @stable */ +/** @deprecated */ export declare const APPLICATION_COMMON_PROVIDERS: Array; +/** @experimental */ +export declare class ApplicationModule { +} + /** @experimental */ export declare abstract class ApplicationRef { componentTypes: Type[]; @@ -143,68 +147,6 @@ export declare abstract class ApplicationRef { abstract waitForAsyncInitializers(): Promise; } -/** @stable */ -export declare var AppModule: AppModuleMetadataFactory; - -/** @stable */ -export interface AppModuleDecorator extends TypeDecorator { -} - -/** @stable */ -export declare class AppModuleFactory { - moduleType: ConcreteType; - constructor(_injectorClass: { - new (parentInjector: Injector): AppModuleInjector; - }, _moduleype: ConcreteType); - create(parentInjector?: Injector): AppModuleRef; -} - -/** @experimental */ -export declare abstract class AppModuleFactoryLoader { - abstract load(path: string): Promise>; -} - -/** @stable */ -export declare class AppModuleMetadata extends InjectableMetadata { - directives: Array; - modules: Array; - pipes: Array; - precompile: Array; - providers: any[]; - constructor({providers, directives, pipes, precompile, modules}?: { - providers?: any[]; - directives?: Array; - pipes?: Array; - precompile?: Array; - modules?: Array; - }); -} - -/** @stable */ -export interface AppModuleMetadataFactory { - (obj: { - providers?: any[]; - directives?: Array; - pipes?: Array; - precompile?: Array; - modules?: Array; - }): AppModuleDecorator; - new (obj: { - providers?: any[]; - directives?: Array; - pipes?: Array; - precompile?: Array; - modules?: Array; - }): AppModuleMetadata; -} - -/** @stable */ -export declare abstract class AppModuleRef { - componentFactoryResolver: ComponentFactoryResolver; - injector: Injector; - instance: T; -} - /** @experimental */ export declare function asNativeElements(debugEls: DebugElement[]): any; @@ -259,10 +201,10 @@ export declare class Binding extends Provider { } /** @stable */ -export declare function bootstrapModule(moduleType: ConcreteType, platform: PlatformRef, compilerOptions?: CompilerOptions): Promise>; +export declare function bootstrapModule(moduleType: ConcreteType, platform: PlatformRef, compilerOptions?: CompilerOptions | CompilerOptions[]): Promise>; /** @experimental */ -export declare function bootstrapModuleFactory(moduleFactory: AppModuleFactory, platform: PlatformRef): AppModuleRef; +export declare function bootstrapModuleFactory(moduleFactory: NgModuleFactory, platform: PlatformRef): NgModuleRef; /** @stable */ export declare enum ChangeDetectionStrategy { @@ -301,30 +243,21 @@ export declare class CollectionChangeRecord { /** @stable */ export declare class Compiler { - injector: Injector; clearCache(): void; clearCacheFor(type: Type): void; - compileAppModuleAsync(moduleType: ConcreteType, metadata?: AppModuleMetadata): Promise>; - compileAppModuleSync(moduleType: ConcreteType, metadata?: AppModuleMetadata): AppModuleFactory; - compileComponentAsync(component: ConcreteType): Promise>; - compileComponentSync(component: ConcreteType): ComponentFactory; + compileComponentAsync(component: ConcreteType, ngModule?: Type): Promise>; + compileComponentSync(component: ConcreteType, ngModule?: Type): ComponentFactory; + compileModuleAsync(moduleType: ConcreteType): Promise>; + compileModuleSync(moduleType: ConcreteType): NgModuleFactory; } /** @experimental */ export declare abstract class CompilerFactory { - abstract createCompiler(options?: CompilerOptions): Compiler; - withDefaults(options?: CompilerOptions): CompilerFactory; - static mergeOptions(defaultOptions?: CompilerOptions, newOptions?: CompilerOptions): CompilerOptions; + abstract createCompiler(options?: CompilerOptions[]): Compiler; } /** @experimental */ -export declare type CompilerOptions = { - useDebug?: boolean; - useJit?: boolean; - defaultEncapsulation?: ViewEncapsulation; - providers?: any[]; - deprecatedAppProviders?: any[]; -}; +export declare const CompilerOptions: OpaqueToken; /** @stable */ export declare var Component: ComponentMetadataFactory; @@ -536,11 +469,14 @@ export declare function coreBootstrap(componentFactory: ComponentFactory, /** @deprecated */ export declare function coreLoadAndBootstrap(componentType: Type, injector: Injector): Promise>; +/** @experimental */ +export declare const corePlatform: (extraProviders?: any[]) => PlatformRef; + /** @experimental */ export declare function createPlatform(injector: Injector): PlatformRef; /** @experimental */ -export declare function createPlatformFactory(name: string, providers: any[]): () => PlatformRef; +export declare function createPlatformFactory(parentPlaformFactory: PlatformFactory, name: string, providers?: any[]): PlatformFactory; /** @stable */ export declare class CyclicDependencyError extends AbstractProviderError { @@ -927,6 +863,68 @@ export declare class KeyValueDiffers { /** @deprecated */ export declare function lockRunMode(): void; +/** @experimental */ +export declare var NgModule: NgModuleMetadataFactory; + +/** @stable */ +export interface NgModuleDecorator extends TypeDecorator { +} + +/** @experimental */ +export declare class NgModuleFactory { + moduleType: ConcreteType; + constructor(_injectorClass: { + new (parentInjector: Injector): NgModuleInjector; + }, _moduleype: ConcreteType); + create(parentInjector: Injector): NgModuleRef; +} + +/** @experimental */ +export declare abstract class NgModuleFactoryLoader { + abstract load(path: string): Promise>; +} + +/** @experimental */ +export declare class NgModuleMetadata extends InjectableMetadata { + declarations: Array; + exports: Array; + imports: Array; + precompile: Array; + providers: any[]; + constructor({providers, declarations, imports, exports, precompile}?: { + providers?: any[]; + declarations?: Array; + imports?: Array; + exports?: Array; + precompile?: Array; + }); +} + +/** @experimental */ +export interface NgModuleMetadataFactory { + (obj?: { + providers?: any[]; + declarations?: Array; + imports?: Array; + exports?: Array; + precompile?: Array; + }): NgModuleDecorator; + new (obj?: { + providers?: any[]; + declarations?: Array; + imports?: Array; + exports?: Array; + precompile?: Array; + }): NgModuleMetadata; +} + +/** @experimental */ +export declare abstract class NgModuleRef { + componentFactoryResolver: ComponentFactoryResolver; + injector: Injector; + instance: T; +} + /** @experimental */ export declare class NgZone { hasPendingMacrotasks: boolean; @@ -1058,8 +1056,8 @@ export interface PipeTransform { transform(value: any, ...args: any[]): any; } -/** @experimental */ -export declare const PLATFORM_COMMON_PROVIDERS: Array; +/** @deprecated */ +export declare const PLATFORM_COMMON_PROVIDERS: any[]; /** @deprecated */ export declare const PLATFORM_DIRECTIVES: OpaqueToken; @@ -1336,12 +1334,6 @@ export declare function style(tokens: string | { [key: string]: string | number; }>): AnimationStyleMetadata; -/** @experimental */ -export declare class SystemJsAppModuleLoader implements AppModuleFactoryLoader { - constructor(_compiler: Compiler); - load(path: string): Promise>; -} - /** @deprecated */ export declare class SystemJsCmpFactoryResolver implements ComponentResolver { constructor(_console: Console); @@ -1356,6 +1348,12 @@ export declare class SystemJsComponentResolver implements ComponentResolver { resolveComponent(componentType: string | Type): Promise>; } +/** @experimental */ +export declare class SystemJsNgModuleLoader implements NgModuleFactoryLoader { + constructor(_compiler: Compiler); + load(path: string): Promise>; +} + /** @stable */ export declare abstract class TemplateRef { elementRef: ElementRef; diff --git a/tools/public_api_guard/core/testing.d.ts b/tools/public_api_guard/core/testing.d.ts index 09dbcbcde3..95a6aaf5c4 100644 --- a/tools/public_api_guard/core/testing.d.ts +++ b/tools/public_api_guard/core/testing.d.ts @@ -37,10 +37,9 @@ export declare function configureCompiler(config: { /** @stable */ export declare function configureModule(moduleDef: { providers?: any[]; - directives?: any[]; - pipes?: any[]; + declarations?: any[]; + imports?: any[]; precompile?: any[]; - modules?: any[]; }): void; /** @experimental */ @@ -62,7 +61,7 @@ export declare function getTestBed(): TestBed; export declare function getTestInjector(): TestBed; /** @experimental */ -export declare function initTestEnvironment(appModule: Type, platform: PlatformRef): void; +export declare function initTestEnvironment(ngModule: Type, platform: PlatformRef): Injector; /** @stable */ export declare function inject(tokens: any[], fn: Function): () => any; @@ -71,10 +70,9 @@ export declare function inject(tokens: any[], fn: Function): () => any; export declare class InjectSetupWrapper { constructor(_moduleDef: () => { providers?: any[]; - directives?: any[]; - pipes?: any[]; + declarations?: any[]; + imports?: any[]; precompile?: any[]; - modules?: any[]; }); inject(tokens: any[], fn: Function): () => any; } @@ -90,7 +88,7 @@ export declare function setBaseTestProviders(platformProviders: Array>; + createModuleFactory(): Promise>; execute(tokens: any[], fn: Function): any; get(token: any, notFoundValue?: any): any; - initTestAppModule(): void; + initTestModule(): void; reset(): void; } @@ -114,10 +111,10 @@ export declare class TestBed implements Injector { export declare class TestComponentBuilder { protected _injector: Injector; constructor(_injector: Injector); - createAsync(rootComponentType: ConcreteType): Promise>; - createFakeAsync(rootComponentType: ConcreteType): ComponentFixture; + createAsync(rootComponentType: ConcreteType, ngModule?: ConcreteType): Promise>; + createFakeAsync(rootComponentType: ConcreteType, ngModule?: ConcreteType): ComponentFixture; protected createFromFactory(ngZone: NgZone, componentFactory: ComponentFactory): ComponentFixture; - createSync(rootComponentType: ConcreteType): ComponentFixture; + createSync(rootComponentType: ConcreteType, ngModule?: ConcreteType): ComponentFixture; overrideAnimations(componentType: Type, animations: AnimationEntryMetadata[]): TestComponentBuilder; overrideDirective(componentType: Type, from: Type, to: Type): TestComponentBuilder; overrideProviders(type: Type, providers: any[]): TestComponentBuilder; @@ -137,10 +134,9 @@ export declare function tick(millis?: number): void; /** @experimental */ export declare function withModule(moduleDef: () => { providers?: any[]; - directives?: any[]; - pipes?: any[]; + declarations?: any[]; + imports?: any[]; precompile?: any[]; - modules?: any[]; }): InjectSetupWrapper; /** @experimental */ diff --git a/tools/public_api_guard/platform-browser-dynamic/index.d.ts b/tools/public_api_guard/platform-browser-dynamic/index.d.ts index 03d689070b..eeadf90c2c 100644 --- a/tools/public_api_guard/platform-browser-dynamic/index.d.ts +++ b/tools/public_api_guard/platform-browser-dynamic/index.d.ts @@ -2,22 +2,19 @@ export declare function bootstrap(appComponentType: ConcreteType, customProviders?: Array): Promise>; /** @deprecated */ -export declare function bootstrapWorkerApp(appComponentType: Type, customProviders?: Array): Promise>; +export declare function bootstrapWorkerApp(appComponentType: ConcreteType, customProviders?: Array): Promise>; -/** @deprecated */ -export declare function bootstrapWorkerUi(workerScriptUri: string, customProviders?: Array): Promise; +/** @experimental */ +export declare function bootstrapWorkerUi(workerScriptUri: string, customProviders?: Array): Promise; /** @deprecated */ export declare const BROWSER_APP_COMPILER_PROVIDERS: Array; /** @experimental */ -export declare const BROWSER_DYNAMIC_COMPILER_FACTORY: CompilerFactory; - -/** @experimental */ -export declare const BROWSER_DYNAMIC_PLATFORM_PROVIDERS: Array; - -/** @experimental */ -export declare const browserDynamicPlatform: () => PlatformRef; +export declare const browserDynamicPlatform: (extraProviders?: any[]) => PlatformRef; /** @experimental */ export declare const CACHED_TEMPLATE_PROVIDER: Array; + +/** @experimental */ +export declare const workerAppDynamicPlatform: (extraProviders?: any[]) => PlatformRef; diff --git a/tools/public_api_guard/platform-browser-dynamic/testing.d.ts b/tools/public_api_guard/platform-browser-dynamic/testing.d.ts index 089eb2e93c..e014304869 100644 --- a/tools/public_api_guard/platform-browser-dynamic/testing.d.ts +++ b/tools/public_api_guard/platform-browser-dynamic/testing.d.ts @@ -1,12 +1,9 @@ -/** @experimental */ -export declare const BROWSER_DYNAMIC_TEST_COMPILER_FACTORY: CompilerFactory; - /** @stable */ -export declare class BrowserDynamicTestModule { +export declare class BrowserDynamicTestingModule { } /** @experimental */ -export declare const browserDynamicTestPlatform: () => PlatformRef; +export declare const browserDynamicTestingPlatform: (extraProviders?: any[]) => PlatformRef; /** @deprecated */ export declare const TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS: Array; diff --git a/tools/public_api_guard/platform-browser/index.d.ts b/tools/public_api_guard/platform-browser/index.d.ts index 3383391bfd..bb67ee79a0 100644 --- a/tools/public_api_guard/platform-browser/index.d.ts +++ b/tools/public_api_guard/platform-browser/index.d.ts @@ -1,13 +1,16 @@ +/** @experimental */ +export declare const _WORKER_UI_PLATFORM_PROVIDERS: Array; + /** @experimental */ export declare abstract class AnimationDriver { abstract animate(element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number, easing: string): AnimationPlayer; static NOOP: AnimationDriver; } -/** @experimental */ +/** @deprecated */ export declare const BROWSER_APP_PROVIDERS: Array; -/** @experimental */ +/** @deprecated */ export declare const BROWSER_PLATFORM_PROVIDERS: Array; /** @experimental */ @@ -18,7 +21,7 @@ export declare class BrowserModule { } /** @experimental */ -export declare const browserPlatform: () => PlatformRef; +export declare const browserPlatform: (extraProviders?: any[]) => PlatformRef; /** @stable */ export declare class BrowserPlatformLocation extends PlatformLocation { @@ -186,7 +189,7 @@ export declare class WebWorkerInstance { worker: Worker; } -/** @experimental */ +/** @deprecated */ export declare const WORKER_APP_APPLICATION_PROVIDERS: Array; /** @experimental */ @@ -200,13 +203,13 @@ export declare const WORKER_APP_LOCATION_PROVIDERS: ({ deps: (typeof PlatformLocation | typeof NgZone)[]; })[]; -/** @experimental */ +/** @deprecated */ export declare const WORKER_APP_PLATFORM_PROVIDERS: Array; /** @experimental */ export declare const WORKER_SCRIPT: OpaqueToken; -/** @experimental */ +/** @deprecated */ export declare const WORKER_UI_APPLICATION_PROVIDERS: Array; /** @experimental */ @@ -217,7 +220,7 @@ export declare const WORKER_UI_LOCATION_PROVIDERS: (typeof MessageBasedPlatformL deps: typeof Injector[]; })[]; -/** @experimental */ +/** @deprecated */ export declare const WORKER_UI_PLATFORM_PROVIDERS: Array; /** @experimental */ @@ -228,11 +231,7 @@ export declare class WorkerAppModule { } /** @experimental */ -export declare const workerAppPlatform: () => PlatformRef; +export declare const workerAppPlatform: (extraProviders?: any[]) => PlatformRef; /** @experimental */ -export declare class WorkerUiModule { -} - -/** @experimental */ -export declare const workerUiPlatform: () => PlatformRef; +export declare const workerUiPlatform: (extraProviders?: any[]) => PlatformRef; diff --git a/tools/public_api_guard/platform-browser/testing.d.ts b/tools/public_api_guard/platform-browser/testing.d.ts index b79557efa4..dbcf77ad21 100644 --- a/tools/public_api_guard/platform-browser/testing.d.ts +++ b/tools/public_api_guard/platform-browser/testing.d.ts @@ -1,12 +1,12 @@ -/** @stable */ -export declare class BrowserTestModule { +/** @experimental */ +export declare class BrowserTestingModule { } /** @experimental */ -export declare const browserTestPlatform: () => PlatformRef; +export declare const browserTestingPlatform: (extraProviders?: any[]) => PlatformRef; /** @deprecated */ export declare const TEST_BROWSER_APPLICATION_PROVIDERS: Array; -/** @experimental */ +/** @deprecated */ export declare const TEST_BROWSER_PLATFORM_PROVIDERS: Array; diff --git a/tools/public_api_guard/platform-server/index.d.ts b/tools/public_api_guard/platform-server/index.d.ts index 4f9be412ea..a8a6d5c59c 100644 --- a/tools/public_api_guard/platform-server/index.d.ts +++ b/tools/public_api_guard/platform-server/index.d.ts @@ -1,11 +1,11 @@ -/** @experimental */ +/** @deprecated */ export declare const SERVER_PLATFORM_PROVIDERS: Array; /** @deprecated */ -export declare function serverBootstrap(appComponentType: Type, providers: Array): Promise>; +export declare function serverBootstrap(appComponentType: ConcreteType, customProviders: Array): Promise>; /** @experimental */ -export declare const serverDynamicPlatform: () => PlatformRef; +export declare const serverDynamicPlatform: (extraProviders?: any[]) => PlatformRef; /** @experimental */ -export declare const serverPlatform: () => PlatformRef; +export declare const serverPlatform: (extraProviders?: any[]) => PlatformRef; diff --git a/tools/public_api_guard/platform-server/testing.d.ts b/tools/public_api_guard/platform-server/testing.d.ts index 951ae46320..6c956e992c 100644 --- a/tools/public_api_guard/platform-server/testing.d.ts +++ b/tools/public_api_guard/platform-server/testing.d.ts @@ -1,9 +1,9 @@ -/** @stable */ -export declare class ServerTestModule { +/** @experimental */ +export declare class ServerTestingModule { } /** @experimental */ -export declare const serverTestPlatform: () => PlatformRef; +export declare const serverTestingPlatform: (extraProviders?: any[]) => PlatformRef; /** @deprecated */ export declare const TEST_SERVER_APPLICATION_PROVIDERS: Array; diff --git a/tools/public_api_guard/router/index.d.ts b/tools/public_api_guard/router/index.d.ts index 1956a58005..66498cb7a1 100644 --- a/tools/public_api_guard/router/index.d.ts +++ b/tools/public_api_guard/router/index.d.ts @@ -135,7 +135,7 @@ export declare class Router { /** @experimental */ navigated: boolean; routerState: RouterState; url: string; - constructor(rootComponentType: Type, resolver: ComponentResolver, urlSerializer: UrlSerializer, outletMap: RouterOutletMap, location: Location, injector: Injector, loader: AppModuleFactoryLoader, config: Routes); + constructor(rootComponentType: Type, resolver: ComponentResolver, urlSerializer: UrlSerializer, outletMap: RouterOutletMap, location: Location, injector: Injector, loader: NgModuleFactoryLoader, config: Routes); createUrlTree(commands: any[], {relativeTo, queryParams, fragment, preserveQueryParams, preserveFragment}?: NavigationExtras): UrlTree; dispose(): void; initialNavigation(): void; @@ -203,6 +203,10 @@ export declare class RouterModule { constructor(injector: Injector); } +/** @experimental */ +export declare class RouterModuleWithoutProviders { +} + /** @stable */ export declare class RouterOutlet { activateEvents: EventEmitter;