feat(browser): use AppModules for bootstrap in the browser

This introduces the `BrowserModule` to be used for long form
bootstrap and offline compile bootstrap:

```
@AppModule({
  modules: [BrowserModule],
  precompile: [MainComponent],
  providers: […], // additional providers
  directives: […], // additional platform directives
  pipes: […] // additional platform pipes
})
class MyModule {
  constructor(appRef: ApplicationRef) {
    appRef.bootstrap(MainComponent);
  }
}

// offline compile
import {bootstrapModuleFactory} from ‘@angular/platform-browser’;
bootstrapModuleFactory(MyModuleNgFactory);

// runtime compile long form
import {bootstrapModule} from ‘@angular/platform-browser-dynamic’;
bootstrapModule(MyModule);
```

The short form, `bootstrap(...)`, can now creates a module on the fly,
given `directives`, `pipes, `providers`, `precompile` and `modules`
properties.

Related changes:
- make `SanitizationService`, `SecurityContext` public in `@angular/core` so that the offline compiler can resolve the token
- move `AnimationDriver` to `platform-browser` and make it
  public so that the offline compiler can resolve the token

BREAKING CHANGES:
- short form bootstrap does no longer allow
  to inject compiler internals (i.e. everything 
  from `@angular/compiler). Inject `Compiler` instead.
  To provide custom providers for the compiler,
  create a custom compiler via `browserCompiler({providers: [...]})`
  and pass that into the `bootstrap` method.
This commit is contained in:
Tobias Bosch
2016-06-30 13:07:17 -07:00
parent 74b45dfbf8
commit 3f55aa609f
71 changed files with 793 additions and 406 deletions

View File

@ -38,6 +38,7 @@ 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';
/**
* A set of providers that provide `RuntimeCompiler` and its dependencies to use for
@ -45,6 +46,9 @@ import {PipeResolver} from './pipe_resolver';
*/
export const COMPILER_PROVIDERS: Array<any|Type|{[k: string]: any}|any[]> =
/*@ts2dart_const*/[
{provide: Reflector, useValue: reflector},
{provide: ReflectorReader, useExisting: Reflector},
Console,
Lexer,
Parser,
HtmlParser,

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AppModuleFactory, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, QueryList, RenderComponentType, Renderer, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {AppModuleFactory, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, 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_, SecurityContext, StaticNodeDebugInfo, TemplateRef_, 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, uninitialized} from '../core_private';
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_, 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, uninitialized} from '../core_private';
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
import {assetUrl} from './util';

View File

@ -196,29 +196,11 @@ export class CompileMetadataResolver {
throw new BaseException(
`Could not compile '${stringify(moduleType)}' because it is not an AppModule.`);
}
let providers: any[] = [];
if (meta.providers) {
providers.push(...this.getProvidersMetadata(meta.providers));
}
let directives: cpl.CompileTypeMetadata[] = [];
if (meta.directives) {
directives.push(...flattenArray(meta.directives)
.map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type))));
}
let pipes: cpl.CompileTypeMetadata[] = [];
if (meta.pipes) {
pipes.push(...flattenArray(meta.pipes)
.map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type))));
}
let precompile: cpl.CompileTypeMetadata[] = [];
if (meta.precompile) {
precompile.push(...flattenArray(meta.precompile)
.map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type))));
}
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);
@ -231,6 +213,22 @@ export class CompileMetadataResolver {
});
}
if (meta.providers) {
providers.push(...this.getProvidersMetadata(meta.providers));
}
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({
type: this.getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)),
providers: providers,

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AppModuleFactory, AppModuleMetadata, Compiler, ComponentFactory, ComponentResolver, Injectable} from '@angular/core';
import {AppModuleFactory, AppModuleMetadata, Compiler, ComponentFactory, ComponentResolver, Injectable, Provider} from '@angular/core';
import {BaseException} from '../src/facade/exceptions';
import {ConcreteType, IS_DART, Type, isBlank, isString, stringify} from '../src/facade/lang';
@ -76,6 +76,14 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
let componentCompilePromises: Promise<any>[] = [];
if (!appModuleFactory || !useCache) {
var compileModuleMeta = this._metadataResolver.getAppModuleMetadata(moduleType, metadata);
let boundCompiler = new BoundCompiler(
this, compileModuleMeta.directives.map(dir => dir.type.runtime),
compileModuleMeta.pipes.map((pipe) => pipe.type.runtime));
// Always provide a bound Compiler / ComponentResolver
compileModuleMeta.providers.push(this._metadataResolver.getProviderMetadata(
new Provider(Compiler, {useValue: boundCompiler})));
compileModuleMeta.providers.push(this._metadataResolver.getProviderMetadata(
new Provider(ComponentResolver, {useExisting: Compiler})));
var compileResult = this._appModuleCompiler.compile(compileModuleMeta);
compileResult.dependencies.forEach((dep) => {
let compileResult = this._compileComponent(
@ -102,21 +110,18 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
appModuleFactory, Promise.all(componentCompilePromises).then(() => appModuleFactory));
}
compileComponentAsync<T>(compType: ConcreteType<T>, {moduleDirectives = [], modulePipes = []}: {
moduleDirectives?: ConcreteType<any>[],
modulePipes?: ConcreteType<any>[]
} = {}): Promise<ComponentFactory<T>> {
return this._compileComponent(compType, false, moduleDirectives, modulePipes).asyncResult;
compileComponentAsync<T>(compType: ConcreteType<T>): Promise<ComponentFactory<T>> {
return this._compileComponent(compType, false, [], []).asyncResult;
}
compileComponentSync<T>(compType: ConcreteType<T>, {moduleDirectives = [], modulePipes = []}: {
moduleDirectives?: ConcreteType<any>[],
modulePipes?: ConcreteType<any>[]
} = {}): ComponentFactory<T> {
return this._compileComponent(compType, true, moduleDirectives, modulePipes).syncResult;
compileComponentSync<T>(compType: ConcreteType<T>): ComponentFactory<T> {
return this._compileComponent(compType, true, [], []).syncResult;
}
private _compileComponent<T>(
/**
* @internal
*/
_compileComponent<T>(
compType: ConcreteType<T>, isSync: boolean, moduleDirectives: ConcreteType<any>[],
modulePipes: ConcreteType<any>[]): SyncAsyncResult<ComponentFactory<T>> {
var templates =
@ -343,3 +348,50 @@ function assertComponent(meta: CompileDirectiveMetadata) {
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
}
}
/**
* A wrapper around `Compiler` and `ComponentResolver` that
* provides default patform directives / pipes.
*/
class BoundCompiler implements Compiler, ComponentResolver {
constructor(
private _delegate: RuntimeCompiler, private _directives: any[], private _pipes: any[]) {}
resolveComponent(component: Type|string): Promise<ComponentFactory<any>> {
if (isString(component)) {
return PromiseWrapper.reject(
new BaseException(`Cannot resolve component using '${component}'.`), null);
}
return this.compileComponentAsync(<ConcreteType<any>>component);
}
compileComponentAsync<T>(compType: ConcreteType<T>): Promise<ComponentFactory<T>> {
return this._delegate._compileComponent(compType, false, this._directives, this._pipes)
.asyncResult;
}
compileComponentSync<T>(compType: ConcreteType<T>): ComponentFactory<T> {
return this._delegate._compileComponent(compType, true, this._directives, this._pipes)
.syncResult;
}
compileAppModuleSync<T>(moduleType: ConcreteType<T>, metadata: AppModuleMetadata = null):
AppModuleFactory<T> {
return this._delegate.compileAppModuleSync(moduleType, metadata);
}
compileAppModuleAsync<T>(moduleType: ConcreteType<T>, metadata: AppModuleMetadata = null):
Promise<AppModuleFactory<T>> {
return this._delegate.compileAppModuleAsync(moduleType, metadata);
}
/**
* Clears all caches
*/
clearCache(): void { this._delegate.clearCache(); }
/**
* Clears the cache for the given component/appModule.
*/
clearCacheFor(type: Type) { this._delegate.clearCacheFor(type); }
}

View File

@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
import {Injectable, SecurityContext} from '@angular/core';
import {SecurityContext} from '../../core_private';
import {StringMapWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {SecurityContext} from '../../core_private';
import {SecurityContext} from '@angular/core';
// =================================================================================================
// =================================================================================================

View File

@ -12,7 +12,7 @@ import {AST} from './expression_parser/ast';
import {CompileDirectiveMetadata, CompileTokenMetadata, CompileProviderMetadata,} from './compile_metadata';
import {ParseSourceSpan} from './parse_util';
import {SecurityContext} from '../core_private';
import {SecurityContext} from '@angular/core';
/**
* An Abstract Syntax Tree node representing part of a parsed Angular template.

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, OpaqueToken, Optional} from '@angular/core';
import {Inject, Injectable, OpaqueToken, Optional, SecurityContext} from '@angular/core';
import {Console, MAX_INTERPOLATION_VALUES, SecurityContext} from '../core_private';
import {Console, MAX_INTERPOLATION_VALUES} from '../core_private';
import {ListWrapper, StringMapWrapper, SetWrapper,} from '../src/facade/collection';
import {RegExpWrapper, isPresent, StringWrapper, isBlank, isArray} from '../src/facade/lang';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, SecurityContext, isDefaultChangeDetectionStrategy} from '../../core_private';
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../../core_private';
import * as cdAst from '../expression_parser/ast';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers} from '../identifiers';
@ -25,7 +25,7 @@ import {camelCaseToDashCase} from '../util';
import {convertCdExpressionToIr} from './expression_converter';
import {CompileBinding} from './compile_binding';
import {BaseException} from '@angular/core';
import {BaseException, SecurityContext} from '@angular/core';
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {