feat(core): introduce `@AppModule`

Main part for #9726
Closes #9730
This commit is contained in:
Tobias Bosch 2016-06-28 09:54:42 -07:00
parent 1608d91728
commit 17e4cfc748
52 changed files with 2085 additions and 851 deletions

View File

@ -107,6 +107,9 @@ $ cp tools/@angular/tsc-wrapped/package.json dist/tools/@angular/tsc-wrapped
$ ./scripts/ci-lite/offline_compiler_test.sh $ ./scripts/ci-lite/offline_compiler_test.sh
# Keep a package fresh in watch mode # Keep a package fresh in watch mode
./node_modules/.bin/tsc -p modules/@angular/compiler/tsconfig-es5.json -w ./node_modules/.bin/tsc -p modules/@angular/compiler/tsconfig-es5.json -w
# Recompile @angular/core module (needs to use tsc-ext to keep the metadata)
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
node dist/tools/@angular/tsc-wrapped/src/main -p modules/@angular/core/tsconfig-es5.json
# Iterate on the test # Iterate on the test
cd /tmp/wherever/e2e_test.1464388257/ cd /tmp/wherever/e2e_test.1464388257/
./node_modules/.bin/ngc ./node_modules/.bin/ngc

View File

@ -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 {LowerCasePipe, NgIf} from '@angular/common';
import {AppModule, Component, ComponentFactoryResolver, Injectable} from '@angular/core';
@Injectable()
export class SomeService {
public prop = 'someValue';
}
@Injectable()
export class NestedService {
}
@Component({
selector: 'cmp',
template: `<div [title]="'HELLO' | lowercase"></div><div *ngIf="true"></div>`
})
export class SomeComp {
constructor() {}
}
@Component({selector: 'parent', template: `<cmp></cmp>`, directives: [SomeComp]})
export class ParentComp {
}
@AppModule({providers: [NestedService]})
export class NestedModule {
}
@AppModule({
directives: [NgIf],
pipes: [LowerCasePipe],
providers: [SomeService],
precompile: [SomeComp],
modules: [NestedModule]
})
export class SomeModule {
}
@AppModule({
directives: [NgIf],
pipes: [LowerCasePipe],
precompile: [ParentComp],
})
export class SomeModuleUsingParentComp {
}

View File

@ -0,0 +1,75 @@
/**
* @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 {ComponentFactoryResolver, DebugElement, ReflectiveInjector, getDebugNode, lockRunMode} from '@angular/core';
import {BROWSER_APP_PROVIDERS, By} from '@angular/platform-browser';
import {serverPlatform} from '@angular/platform-server';
import {NestedModule, NestedService, ParentComp, SomeComp, SomeModule, SomeService} from '../src/app_module';
import {SomeModuleNgFactory, SomeModuleUsingParentCompNgFactory} from '../src/app_module.ngfactory';
// Need to lock the mode explicitely as this test is not using Angular's testing framework.
lockRunMode();
describe('AppModule', () => {
it('should support providers', () => {
var moduleRef = SomeModuleNgFactory.create();
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', () => {
const appInjector =
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
var moduleRef = SomeModuleNgFactory.create(appInjector);
var cf = moduleRef.injector.get(ComponentFactoryResolver).resolveComponentFactory(SomeComp);
expect(cf.componentType).toBe(SomeComp);
var comp = cf.create(moduleRef.injector);
});
it('should support module directives and pipes', () => {
const appInjector =
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
var moduleRef = SomeModuleNgFactory.create(appInjector);
var cf = moduleRef.injector.get(ComponentFactoryResolver).resolveComponentFactory(SomeComp);
var comp = cf.create(moduleRef.injector);
var debugElement = <DebugElement>getDebugNode(comp.location.nativeElement);
// NgIf should work, is being used as module directive
expect(debugElement.children.length).toBe(1);
comp.changeDetectorRef.detectChanges();
expect(debugElement.children.length).toBe(2);
expect(debugElement.children[0].properties['title']).toBe('hello');
});
it('should support module directives and pipes on nested components', () => {
const appInjector =
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
var moduleRef = SomeModuleUsingParentCompNgFactory.create(appInjector);
var cf = moduleRef.injector.get(ComponentFactoryResolver).resolveComponentFactory(ParentComp);
var comp = cf.create(moduleRef.injector);
var debugElement = <DebugElement>getDebugNode(comp.location.nativeElement);
debugElement = debugElement.children[0];
// NgIf should work, is being used as module directive
expect(debugElement.children.length).toBe(1);
comp.changeDetectorRef.detectChanges();
expect(debugElement.children.length).toBe(2);
expect(debugElement.children[0].properties['title']).toBe('hello');
});
it('should support child moduless', () => {
var moduleRef = SomeModuleNgFactory.create();
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);
});
});

View File

@ -11,15 +11,15 @@
* Intended to be used in a build step. * Intended to be used in a build step.
*/ */
import * as compiler from '@angular/compiler'; import * as compiler from '@angular/compiler';
import {ViewEncapsulation, lockRunMode} from '@angular/core'; import {AppModuleMetadata, ComponentMetadata, ViewEncapsulation, lockRunMode} from '@angular/core';
import {AngularCompilerOptions} from '@angular/tsc-wrapped'; import {AngularCompilerOptions} from '@angular/tsc-wrapped';
import * as path from 'path'; import * as path from 'path';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './compiler_private'; import {AppModuleCompiler, CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './compiler_private';
import {ReflectorHost, ReflectorHostContext} from './reflector_host'; import {ReflectorHost, ReflectorHostContext} from './reflector_host';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities'; import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector} from './static_reflector'; import {StaticReflector, StaticSymbol} from './static_reflector';
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/; const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
@ -40,26 +40,9 @@ export class CodeGenerator {
lockRunMode(); lockRunMode();
} }
private generateSource(metadatas: compiler.CompileDirectiveMetadata[]) { private readFileMetadata(absSourcePath: string): FileMetadata {
const normalize = (metadata: compiler.CompileDirectiveMetadata) => {
const directiveType = metadata.type.runtime;
const directives = this.resolver.getViewDirectivesMetadata(directiveType);
return Promise.all(directives.map(d => this.compiler.normalizeDirectiveMetadata(d)))
.then(normalizedDirectives => {
const pipes = this.resolver.getViewPipesMetadata(directiveType);
return new compiler.NormalizedComponentWithViewDirectives(
metadata, normalizedDirectives, pipes);
});
};
return Promise.all(metadatas.map(normalize))
.then(
normalizedCompWithDirectives =>
this.compiler.compileTemplates(normalizedCompWithDirectives));
}
private readComponents(absSourcePath: string) {
const result: Promise<compiler.CompileDirectiveMetadata>[] = [];
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath); const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
const result: FileMetadata = {components: [], appModules: [], fileUrl: absSourcePath};
if (!moduleMetadata) { if (!moduleMetadata) {
console.log(`WARNING: no metadata found for ${absSourcePath}`); console.log(`WARNING: no metadata found for ${absSourcePath}`);
return result; return result;
@ -75,13 +58,14 @@ export class CodeGenerator {
continue; continue;
} }
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath); const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
let directive: compiler.CompileDirectiveMetadata; const annotations = this.staticReflector.annotations(staticType);
directive = this.resolver.maybeGetDirectiveMetadata(<any>staticType); annotations.forEach((annotation) => {
if (annotation instanceof AppModuleMetadata) {
if (!directive || !directive.isComponent) { result.appModules.push(staticType);
continue; } else if (annotation instanceof ComponentMetadata) {
} result.components.push(staticType);
result.push(this.compiler.normalizeDirectiveMetadata(directive)); }
});
} }
return result; return result;
} }
@ -102,30 +86,31 @@ export class CodeGenerator {
} }
codegen(): Promise<any> { codegen(): Promise<any> {
const generateOneFile = (absSourcePath: string) => let filePaths =
Promise.all(this.readComponents(absSourcePath)) this.program.getSourceFiles().map(sf => sf.fileName).filter(f => !GENERATED_FILES.test(f));
.then((metadatas: compiler.CompileDirectiveMetadata[]) => { let fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
if (!metadatas || !metadatas.length) { let appModules = fileMetas.reduce((appModules, fileMeta) => {
return; appModules.push(...fileMeta.appModules);
} return appModules;
return this.generateSource(metadatas); }, <StaticSymbol[]>[]);
}) let analyzedAppModules = this.compiler.analyzeModules(appModules);
.then(generatedModules => { return Promise
if (generatedModules) { .all(fileMetas.map(
generatedModules.forEach((generatedModule) => { (fileMeta) => this.compiler
const sourceFile = this.program.getSourceFile(absSourcePath); .compile(
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl); fileMeta.fileUrl, analyzedAppModules, fileMeta.components,
this.host.writeFile( fileMeta.appModules)
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]); .then((generatedModules) => {
}); generatedModules.forEach((generatedModule) => {
} const sourceFile = this.program.getSourceFile(fileMeta.fileUrl);
}) const emitPath =
.catch((e) => { console.error(e.stack); }); this.calculateEmitPath(generatedModule.moduleUrl);
var compPromises = this.program.getSourceFiles() this.host.writeFile(
.map(sf => sf.fileName) emitPath, PREAMBLE + generatedModule.source, false, () => {},
.filter(f => !GENERATED_FILES.test(f)) [sourceFile]);
.map(generateOneFile); });
return Promise.all(compPromises); })))
.catch((e) => { console.error(e.stack); });
} }
static create( static create(
@ -158,14 +143,20 @@ export class CodeGenerator {
const tmplParser = new TemplateParser( const tmplParser = new TemplateParser(
parser, new DomElementSchemaRegistry(), htmlParser, parser, new DomElementSchemaRegistry(), htmlParser,
/*console*/ null, []); /*console*/ null, []);
const offlineCompiler = new compiler.OfflineCompiler(
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
new TypeScriptEmitter(reflectorHost));
const resolver = new CompileMetadataResolver( const resolver = new CompileMetadataResolver(
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
new compiler.ViewResolver(staticReflector), config, staticReflector); new compiler.ViewResolver(staticReflector), config, staticReflector);
const offlineCompiler = new compiler.OfflineCompiler(
resolver, normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
new AppModuleCompiler(), new TypeScriptEmitter(reflectorHost));
return new CodeGenerator( return new CodeGenerator(
options, program, compilerHost, staticReflector, resolver, offlineCompiler, reflectorHost); options, program, compilerHost, staticReflector, resolver, offlineCompiler, reflectorHost);
} }
} }
interface FileMetadata {
fileUrl: string;
components: StaticSymbol[];
appModules: StaticSymbol[];
}

View File

@ -61,5 +61,8 @@ export var StyleCompiler: typeof _c.StyleCompiler = _c.StyleCompiler;
export type ViewCompiler = _c.ViewCompiler; export type ViewCompiler = _c.ViewCompiler;
export var ViewCompiler: typeof _c.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 TypeScriptEmitter = _c.TypeScriptEmitter; export type TypeScriptEmitter = _c.TypeScriptEmitter;
export var TypeScriptEmitter: typeof _c.TypeScriptEmitter = _c.TypeScriptEmitter; export var TypeScriptEmitter: typeof _c.TypeScriptEmitter = _c.TypeScriptEmitter;

View File

@ -22,7 +22,7 @@ import * as compiler from '@angular/compiler';
import {ViewEncapsulation, lockRunMode} from '@angular/core'; import {ViewEncapsulation, lockRunMode} from '@angular/core';
import {StaticReflector} from './static_reflector'; import {StaticReflector} from './static_reflector';
import {CompileMetadataResolver, HtmlParser, DirectiveNormalizer, Lexer, Parser, TemplateParser, DomElementSchemaRegistry, StyleCompiler, ViewCompiler, TypeScriptEmitter, MessageExtractor, removeDuplicates, ExtractionResult, Message, ParseError, serializeXmb,} from './compiler_private'; import {CompileMetadataResolver, HtmlParser, DirectiveNormalizer, Lexer, Parser, DomElementSchemaRegistry, TypeScriptEmitter, MessageExtractor, removeDuplicates, ExtractionResult, Message, ParseError, serializeXmb,} from './compiler_private';
import {ReflectorHost} from './reflector_host'; import {ReflectorHost} from './reflector_host';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities'; import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
@ -40,42 +40,27 @@ class Extractor {
constructor( constructor(
private _options: tsc.AngularCompilerOptions, private _program: ts.Program, private _options: tsc.AngularCompilerOptions, private _program: ts.Program,
public host: ts.CompilerHost, private staticReflector: StaticReflector, public host: ts.CompilerHost, private staticReflector: StaticReflector,
private _resolver: CompileMetadataResolver, private _compiler: compiler.OfflineCompiler, private _resolver: CompileMetadataResolver, private _normalizer: DirectiveNormalizer,
private _reflectorHost: ReflectorHost, private _extractor: MessageExtractor) { private _reflectorHost: ReflectorHost, private _extractor: MessageExtractor) {
lockRunMode(); lockRunMode();
} }
private _extractCmpMessages(metadatas: compiler.CompileDirectiveMetadata[]): private _extractCmpMessages(components: compiler.CompileDirectiveMetadata[]): ExtractionResult {
Promise<ExtractionResult> { if (!components || !components.length) {
if (!metadatas || !metadatas.length) {
return null; return null;
} }
const normalize = (metadata: compiler.CompileDirectiveMetadata) => { let messages: Message[] = [];
const directiveType = metadata.type.runtime; let errors: ParseError[] = [];
const directives = this._resolver.getViewDirectivesMetadata(directiveType); components.forEach(metadata => {
return Promise.all(directives.map(d => this._compiler.normalizeDirectiveMetadata(d))) let url = _dirPaths.get(metadata);
.then(normalizedDirectives => { let result = this._extractor.extract(metadata.template.template, url);
const pipes = this._resolver.getViewPipesMetadata(directiveType); errors = errors.concat(result.errors);
return new compiler.NormalizedComponentWithViewDirectives( messages = messages.concat(result.messages);
metadata, normalizedDirectives, pipes); });
});
};
return Promise.all(metadatas.map(normalize)) // Extraction Result might contain duplicate messages at this point
.then((cmps: compiler.NormalizedComponentWithViewDirectives[]) => { return new ExtractionResult(messages, errors);
let messages: Message[] = [];
let errors: ParseError[] = [];
cmps.forEach(cmp => {
let url = _dirPaths.get(cmp.component);
let result = this._extractor.extract(cmp.component.template.template, url);
errors = errors.concat(result.errors);
messages = messages.concat(result.messages);
});
// Extraction Result might contain duplicate messages at this point
return new ExtractionResult(messages, errors);
});
} }
private _readComponents(absSourcePath: string): Promise<compiler.CompileDirectiveMetadata>[] { private _readComponents(absSourcePath: string): Promise<compiler.CompileDirectiveMetadata>[] {
@ -96,7 +81,7 @@ class Extractor {
directive = this._resolver.maybeGetDirectiveMetadata(<any>staticType); directive = this._resolver.maybeGetDirectiveMetadata(<any>staticType);
if (directive && directive.isComponent) { if (directive && directive.isComponent) {
let promise = this._compiler.normalizeDirectiveMetadata(directive); let promise = this._normalizer.normalizeDirective(directive).asyncResult;
promise.then(md => _dirPaths.set(md, absSourcePath)); promise.then(md => _dirPaths.set(md, absSourcePath));
result.push(promise); result.push(promise);
} }
@ -165,12 +150,6 @@ class Extractor {
}); });
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config); const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
const parser = new Parser(new Lexer()); const parser = new Parser(new Lexer());
const tmplParser = new TemplateParser(
parser, new DomElementSchemaRegistry(), htmlParser,
/*console*/ null, []);
const offlineCompiler = new compiler.OfflineCompiler(
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
new TypeScriptEmitter(reflectorHost));
const resolver = new CompileMetadataResolver( const resolver = new CompileMetadataResolver(
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
new compiler.ViewResolver(staticReflector), config, staticReflector); new compiler.ViewResolver(staticReflector), config, staticReflector);
@ -179,7 +158,7 @@ class Extractor {
const extractor = new MessageExtractor(htmlParser, parser, [], {}); const extractor = new MessageExtractor(htmlParser, parser, [], {});
return new Extractor( return new Extractor(
options, program, compilerHost, staticReflector, resolver, offlineCompiler, reflectorHost, options, program, compilerHost, staticReflector, resolver, normalizer, reflectorHost,
extractor); extractor);
} }
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {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 {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 {ReflectorReader} from './core_private'; import {ReflectorReader} from './core_private';
@ -216,6 +216,8 @@ export class StaticReflector implements ReflectorReader {
this.host.findDeclaration(coreDecorators, 'Directive'), DirectiveMetadata); this.host.findDeclaration(coreDecorators, 'Directive'), DirectiveMetadata);
this.registerDecoratorOrConstructor( this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'Component'), ComponentMetadata); this.host.findDeclaration(coreDecorators, 'Component'), ComponentMetadata);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'AppModule'), AppModuleMetadata);
// Note: Some metadata classes can be used directly with Provider.deps. // Note: Some metadata classes can be used directly with Provider.deps.
this.registerDecoratorOrConstructor( this.registerDecoratorOrConstructor(

View File

@ -11,7 +11,7 @@
* @description * @description
* Starting point to import all compiler APIs. * 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, NormalizedComponentWithViewDirectives, OfflineCompiler, PipeResolver, RenderTypes, RuntimeCompiler, SourceModule, TEMPLATE_TRANSFORMS, UrlResolver, ViewResolver, XHR, createOfflineCompileUrlResolver} from './src/compiler'; export {COMPILER_PROVIDERS, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileFactoryMetadata, CompileIdentifierMetadata, CompileMetadataWithIdentifier, CompileMetadataWithType, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, CompilerConfig, DEFAULT_PACKAGE_URL_PROVIDER, DirectiveResolver, OfflineCompiler, PipeResolver, RenderTypes, RuntimeCompiler, SourceModule, TEMPLATE_TRANSFORMS, UrlResolver, ViewResolver, XHR, createOfflineCompileUrlResolver} from './src/compiler';
export {ElementSchemaRegistry} from './src/schema/element_schema_registry'; export {ElementSchemaRegistry} from './src/schema/element_schema_registry';
export * from './src/template_ast'; export * from './src/template_ast';

View File

@ -29,6 +29,7 @@ export var CodegenComponentFactoryResolver: typeof t.CodegenComponentFactoryReso
export var AppView: typeof t.AppView = r.AppView; export var AppView: typeof t.AppView = r.AppView;
export type DebugAppView<T> = t.DebugAppView<T>; export type DebugAppView<T> = t.DebugAppView<T>;
export var DebugAppView: typeof t.DebugAppView = r.DebugAppView; export var DebugAppView: typeof t.DebugAppView = r.DebugAppView;
export var AppModuleInjector: typeof t.AppModuleInjector = r.AppModuleInjector;
export type ViewType = t.ViewType; export type ViewType = t.ViewType;
export var ViewType: typeof t.ViewType = r.ViewType; export var ViewType: typeof t.ViewType = r.ViewType;
export var MAX_INTERPOLATION_VALUES: typeof t.MAX_INTERPOLATION_VALUES = r.MAX_INTERPOLATION_VALUES; export var MAX_INTERPOLATION_VALUES: typeof t.MAX_INTERPOLATION_VALUES = r.MAX_INTERPOLATION_VALUES;

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license * 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 directive_normalizer from './src/directive_normalizer';
import * as lexer from './src/expression_parser/lexer'; import * as lexer from './src/expression_parser/lexer';
import * as parser from './src/expression_parser/parser'; import * as parser from './src/expression_parser/parser';
@ -98,6 +99,9 @@ export var StyleCompiler = style_compiler.StyleCompiler;
export type ViewCompiler = view_compiler.ViewCompiler; export type ViewCompiler = view_compiler.ViewCompiler;
export var 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 TypeScriptEmitter = ts_emitter.TypeScriptEmitter; export type TypeScriptEmitter = ts_emitter.TypeScriptEmitter;
export var TypeScriptEmitter = ts_emitter.TypeScriptEmitter; export var TypeScriptEmitter = ts_emitter.TypeScriptEmitter;
} }

View File

@ -0,0 +1,215 @@
/**
* @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} from '@angular/core';
import {CompileAppModuleMetadata, CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileTokenMap, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata';
import {isBlank, isPresent} from './facade/lang';
import {Identifiers, identifierToken} from './identifiers';
import * as o from './output/output_ast';
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
import {AppModuleProviderParser} from './provider_parser';
import {ProviderAst, ProviderAstType} from './template_ast';
import {createDiTokenExpression} from './util';
export class ComponentFactoryDependency {
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export class AppModuleCompileResult {
constructor(
public statements: o.Statement[], public appModuleFactoryVar: 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}`;
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 id = new CompileIdentifierMetadata({name: precompileComp.name});
deps.push(new ComponentFactoryDependency(precompileComp, id));
return id;
});
var builder = new _InjectorBuilder(appModuleMeta, precompileComponents, sourceSpan);
var providerParser = new AppModuleProviderParser(appModuleMeta, 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)
.instantiate(
[o.variable(injectorClass.name), o.importExpr(appModuleMeta.type)],
o.importType(
Identifiers.AppModuleFactory, [o.importType(appModuleMeta.type)],
[o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final]);
return new AppModuleCompileResult(
[injectorClass, appModuleFactoryStmt], appModuleFactoryVar, deps);
}
}
class _InjectorBuilder {
private _instances = new CompileTokenMap<o.Expression>();
private _fields: o.ClassField[] = [];
private _createStmts: o.Statement[] = [];
private _getters: o.ClassGetter[] = [];
constructor(
private _appModuleMeta: CompileAppModuleMetadata,
private _precompileComponents: CompileIdentifierMetadata[],
private _sourceSpan: ParseSourceSpan) {}
addProvider(resolvedProvider: ProviderAst) {
var providerValueExpressions =
resolvedProvider.providers.map((provider) => this._getProviderValue(provider));
var propName = `_${resolvedProvider.token.name}_${this._instances.size}`;
var instance = this._createProviderProperty(
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
resolvedProvider.eager);
this._instances.add(resolvedProvider.token, instance);
}
build(): o.ClassStmt {
let getMethodStmts: o.Statement[] = this._instances.keys().map((token) => {
var providerExpr = this._instances.get(token);
return new o.IfStmt(
InjectMethodVars.token.identical(createDiTokenExpression(token)),
[new o.ReturnStatement(providerExpr)]);
});
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.ClassMethod(
'getInternal',
[
new o.FnParam(InjectMethodVars.token.name, o.DYNAMIC_TYPE),
new o.FnParam(InjectMethodVars.notFoundResult.name, o.DYNAMIC_TYPE)
],
getMethodStmts.concat([new o.ReturnStatement(InjectMethodVars.notFoundResult)]),
o.DYNAMIC_TYPE)
];
var ctor = new o.ClassMethod(
null, [new o.FnParam(InjectorProps.parent.name, o.importType(Identifiers.Injector))],
[o.SUPER_EXPR
.callFn([
o.variable(InjectorProps.parent.name),
o.literalArr(this._precompileComponents.map(
(precompiledComponent) => o.importExpr(precompiledComponent)))
])
.toStmt()]);
var injClassName = `${this._appModuleMeta.type.name}Injector`;
return new o.ClassStmt(
injClassName,
o.importExpr(Identifiers.AppModuleInjector, [o.importType(this._appModuleMeta.type)]),
this._fields, this._getters, ctor, methods);
}
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
var result: o.Expression;
if (isPresent(provider.useExisting)) {
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting}));
} else if (isPresent(provider.useFactory)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
var depsExpr = deps.map((dep) => this._getDependency(dep));
result = o.importExpr(provider.useFactory).callFn(depsExpr);
} else if (isPresent(provider.useClass)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
var depsExpr = deps.map((dep) => this._getDependency(dep));
result =
o.importExpr(provider.useClass).instantiate(depsExpr, o.importType(provider.useClass));
} else {
result = o.literal(provider.useValue);
}
return result;
}
private _createProviderProperty(
propName: string, provider: ProviderAst, providerValueExpressions: o.Expression[],
isMulti: boolean, isEager: boolean): o.Expression {
var resolvedProviderValueExpr: o.Expression;
var type: o.Type;
if (isMulti) {
resolvedProviderValueExpr = o.literalArr(providerValueExpressions);
type = new o.ArrayType(o.DYNAMIC_TYPE);
} else {
resolvedProviderValueExpr = providerValueExpressions[0];
type = providerValueExpressions[0].type;
}
if (isBlank(type)) {
type = o.DYNAMIC_TYPE;
}
if (isEager) {
this._fields.push(new o.ClassField(propName, type));
this._createStmts.push(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
} else {
var internalField = `_${propName}`;
this._fields.push(new o.ClassField(internalField, type));
// Note: Equals is important for JS so that it also checks the undefined case!
var getterStmts = [
new o.IfStmt(
o.THIS_EXPR.prop(internalField).isBlank(),
[o.THIS_EXPR.prop(internalField).set(resolvedProviderValueExpr).toStmt()]),
new o.ReturnStatement(o.THIS_EXPR.prop(internalField))
];
this._getters.push(new o.ClassGetter(propName, getterStmts, type));
}
return o.THIS_EXPR.prop(propName);
}
private _getDependency(dep: CompileDiDependencyMetadata): o.Expression {
var result: o.Expression = null;
if (dep.isValue) {
result = o.literal(dep.value);
}
if (!dep.isSkipSelf) {
if (dep.token &&
(dep.token.equalsTo(identifierToken(Identifiers.Injector)) ||
dep.token.equalsTo(identifierToken(Identifiers.ComponentFactoryResolver)))) {
result = o.THIS_EXPR;
}
if (isBlank(result)) {
result = this._instances.get(dep.token);
}
}
if (isBlank(result)) {
var args = [createDiTokenExpression(dep.token)];
if (dep.isOptional) {
args.push(o.NULL_EXPR);
}
result = InjectorProps.parent.callMethod('get', args);
}
return result;
}
}
class InjectorProps {
static parent = o.THIS_EXPR.prop('parent');
}
class InjectMethodVars {
static token = o.variable('token');
static notFoundResult = o.variable('notFoundResult');
}

View File

@ -11,13 +11,14 @@ import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {CHANGE_DETECTION_STRATEGY_VALUES, LIFECYCLE_HOOKS_VALUES, LifecycleHooks, VIEW_ENCAPSULATION_VALUES, reflector} from '../core_private'; import {CHANGE_DETECTION_STRATEGY_VALUES, LIFECYCLE_HOOKS_VALUES, LifecycleHooks, VIEW_ENCAPSULATION_VALUES, reflector} from '../core_private';
import {ListWrapper, StringMapWrapper} from '../src/facade/collection'; import {ListWrapper, StringMapWrapper} from '../src/facade/collection';
import {BaseException, unimplemented} from '../src/facade/exceptions'; import {BaseException, unimplemented} from '../src/facade/exceptions';
import {NumberWrapper, RegExpWrapper, Type, isArray, isBlank, isBoolean, isNumber, isPresent, isString, normalizeBlank, normalizeBool, serializeEnum} from '../src/facade/lang'; import {NumberWrapper, RegExpWrapper, Type, isArray, isBlank, isBoolean, isNumber, isPresent, isString, isStringMap, normalizeBlank, normalizeBool, serializeEnum} from '../src/facade/lang';
import {CssSelector} from './selector'; import {CssSelector} from './selector';
import {getUrlScheme} from './url_resolver'; import {getUrlScheme} from './url_resolver';
import {sanitizeIdentifier, splitAtColon} from './util'; import {sanitizeIdentifier, splitAtColon} from './util';
// group 2: "event" from "(event)" // group 2: "event" from "(event)"
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g; var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
@ -468,12 +469,14 @@ export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
export class CompileTokenMap<VALUE> { export class CompileTokenMap<VALUE> {
private _valueMap = new Map<any, VALUE>(); private _valueMap = new Map<any, VALUE>();
private _values: VALUE[] = []; private _values: VALUE[] = [];
private _tokens: CompileTokenMetadata[] = [];
add(token: CompileTokenMetadata, value: VALUE) { add(token: CompileTokenMetadata, value: VALUE) {
var existing = this.get(token); var existing = this.get(token);
if (isPresent(existing)) { if (isPresent(existing)) {
throw new BaseException(`Can only add to a TokenMap! Token: ${token.name}`); throw new BaseException(`Can only add to a TokenMap! Token: ${token.name}`);
} }
this._tokens.push(token);
this._values.push(value); this._values.push(value);
var rk = token.runtimeCacheKey; var rk = token.runtimeCacheKey;
if (isPresent(rk)) { if (isPresent(rk)) {
@ -496,6 +499,7 @@ export class CompileTokenMap<VALUE> {
} }
return result; return result;
} }
keys(): CompileTokenMetadata[] { return this._tokens; }
values(): VALUE[] { return this._values; } values(): VALUE[] { return this._values; }
get size(): number { return this._values.length; } get size(): number { return this._values.length; }
} }
@ -966,7 +970,61 @@ export class CompilePipeMetadata implements CompileMetadataWithType {
} }
} }
/**
* Metadata regarding compilation of a directive.
*/
export class CompileAppModuleMetadata implements CompileMetadataWithType {
type: CompileTypeMetadata;
providers: CompileProviderMetadata[];
directives: CompileTypeMetadata[];
pipes: CompileTypeMetadata[];
precompile: CompileTypeMetadata[];
modules: CompileTypeMetadata[];
constructor({type, providers, directives, pipes, precompile, modules}: {
type?: CompileTypeMetadata,
providers?: Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
directives?: CompileTypeMetadata[],
pipes?: CompileTypeMetadata[],
precompile?: CompileTypeMetadata[],
modules?: CompileTypeMetadata[]
} = {}) {
this.type = type;
this.directives = _normalizeArray(directives);
this.pipes = _normalizeArray(pipes);
this.providers = _normalizeArray(providers);
this.precompile = _normalizeArray(precompile);
this.modules = _normalizeArray(modules);
}
get identifier(): CompileIdentifierMetadata { return this.type; }
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)
});
}
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)
};
}
}
var _COMPILE_METADATA_FROM_JSON = { var _COMPILE_METADATA_FROM_JSON = {
'AppModule': CompileAppModuleMetadata.fromJson,
'Directive': CompileDirectiveMetadata.fromJson, 'Directive': CompileDirectiveMetadata.fromJson,
'Pipe': CompilePipeMetadata.fromJson, 'Pipe': CompilePipeMetadata.fromJson,
'Type': CompileTypeMetadata.fromJson, 'Type': CompileTypeMetadata.fromJson,
@ -1006,3 +1064,12 @@ function _objToJson(obj: any): string|{[key: string]: any} {
function _normalizeArray(obj: any[]): any[] { function _normalizeArray(obj: any[]): any[] {
return isPresent(obj) ? obj : []; return isPresent(obj) ? obj : [];
} }
export function isStaticSymbol(value: any): value is StaticSymbol {
return isStringMap(value) && isPresent(value['name']) && isPresent(value['filePath']);
}
export interface StaticSymbol {
name: string;
filePath: string;
}

View File

@ -27,6 +27,7 @@ import {DirectiveNormalizer} from './directive_normalizer';
import {CompileMetadataResolver} from './metadata_resolver'; import {CompileMetadataResolver} from './metadata_resolver';
import {StyleCompiler} from './style_compiler'; import {StyleCompiler} from './style_compiler';
import {ViewCompiler} from './view_compiler/view_compiler'; import {ViewCompiler} from './view_compiler/view_compiler';
import {AppModuleCompiler} from './app_module_compiler';
import {CompilerConfig} from './config'; import {CompilerConfig} from './config';
import {RuntimeCompiler} from './runtime_compiler'; import {RuntimeCompiler} from './runtime_compiler';
import {ElementSchemaRegistry} from './schema/element_schema_registry'; import {ElementSchemaRegistry} from './schema/element_schema_registry';
@ -44,13 +45,24 @@ import {PipeResolver} from './pipe_resolver';
*/ */
export const COMPILER_PROVIDERS: Array<any|Type|{[k: string]: any}|any[]> = export const COMPILER_PROVIDERS: Array<any|Type|{[k: string]: any}|any[]> =
/*@ts2dart_const*/[ /*@ts2dart_const*/[
Lexer, Parser, HtmlParser, TemplateParser, DirectiveNormalizer, CompileMetadataResolver, Lexer,
DEFAULT_PACKAGE_URL_PROVIDER, StyleCompiler, ViewCompiler, Parser,
HtmlParser,
TemplateParser,
DirectiveNormalizer,
CompileMetadataResolver,
DEFAULT_PACKAGE_URL_PROVIDER,
StyleCompiler,
ViewCompiler,
AppModuleCompiler,
/*@ts2dart_Provider*/ {provide: CompilerConfig, useValue: new CompilerConfig()}, /*@ts2dart_Provider*/ {provide: CompilerConfig, useValue: new CompilerConfig()},
RuntimeCompiler, RuntimeCompiler,
/*@ts2dart_Provider*/ {provide: ComponentResolver, useExisting: RuntimeCompiler}, /*@ts2dart_Provider*/ {provide: ComponentResolver, useExisting: RuntimeCompiler},
/*@ts2dart_Provider*/ {provide: Compiler, useExisting: RuntimeCompiler}, /*@ts2dart_Provider*/ {provide: Compiler, useExisting: RuntimeCompiler},
DomElementSchemaRegistry, DomElementSchemaRegistry,
/*@ts2dart_Provider*/ {provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry}, /*@ts2dart_Provider*/ {provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
UrlResolver, ViewResolver, DirectiveResolver, PipeResolver UrlResolver,
ViewResolver,
DirectiveResolver,
PipeResolver
]; ];

View File

@ -20,14 +20,9 @@ import {HtmlParser} from './html_parser';
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver'; import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
import {PreparsedElementType, preparseElement} from './template_preparser'; import {PreparsedElementType, preparseElement} from './template_preparser';
import {UrlResolver} from './url_resolver'; import {UrlResolver} from './url_resolver';
import {SyncAsyncResult} from './util';
import {XHR} from './xhr'; import {XHR} from './xhr';
export class NormalizeDirectiveResult {
constructor(
public syncResult: CompileDirectiveMetadata,
public asyncResult: Promise<CompileDirectiveMetadata>) {}
}
@Injectable() @Injectable()
export class DirectiveNormalizer { export class DirectiveNormalizer {
private _xhrCache = new Map<string, Promise<string>>(); private _xhrCache = new Map<string, Promise<string>>();
@ -56,10 +51,11 @@ export class DirectiveNormalizer {
return result; return result;
} }
normalizeDirective(directive: CompileDirectiveMetadata): NormalizeDirectiveResult { normalizeDirective(directive: CompileDirectiveMetadata):
SyncAsyncResult<CompileDirectiveMetadata> {
if (!directive.isComponent) { if (!directive.isComponent) {
// For non components there is nothing to be normalized yet. // For non components there is nothing to be normalized yet.
return new NormalizeDirectiveResult(directive, Promise.resolve(directive)); return new SyncAsyncResult(directive, Promise.resolve(directive));
} }
let normalizedTemplateSync: CompileTemplateMetadata = null; let normalizedTemplateSync: CompileTemplateMetadata = null;
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>; let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
@ -74,11 +70,10 @@ export class DirectiveNormalizer {
if (normalizedTemplateSync && normalizedTemplateSync.styleUrls.length === 0) { if (normalizedTemplateSync && normalizedTemplateSync.styleUrls.length === 0) {
// sync case // sync case
let normalizedDirective = _cloneDirectiveWithTemplate(directive, normalizedTemplateSync); let normalizedDirective = _cloneDirectiveWithTemplate(directive, normalizedTemplateSync);
return new NormalizeDirectiveResult( return new SyncAsyncResult(normalizedDirective, Promise.resolve(normalizedDirective));
normalizedDirective, Promise.resolve(normalizedDirective));
} else { } else {
// async case // async case
return new NormalizeDirectiveResult( return new SyncAsyncResult(
null, null,
normalizedTemplateAsync normalizedTemplateAsync
.then((normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate)) .then((normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate))

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactoryResolver, ElementRef, Injector, QueryList, RenderComponentType, Renderer, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; import {AppModuleFactory, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, QueryList, RenderComponentType, Renderer, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {AnimationGroupPlayer as AnimationGroupPlayer_, AnimationKeyframe as AnimationKeyframe_, AnimationSequencePlayer as AnimationSequencePlayer_, AnimationStyles as AnimationStyles_, AppElement, 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_, 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 {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata'; import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
import {assetUrl} from './util'; import {assetUrl} from './util';
@ -108,6 +108,21 @@ export class Identifiers {
moduleUrl: assetUrl('core', 'linker/component_factory_resolver'), moduleUrl: assetUrl('core', 'linker/component_factory_resolver'),
runtime: ComponentFactoryResolver runtime: ComponentFactoryResolver
}); });
static ComponentFactory = new CompileIdentifierMetadata({
name: 'ComponentFactory',
runtime: ComponentFactory,
moduleUrl: assetUrl('core', 'linker/component_factory')
});
static AppModuleFactory = new CompileIdentifierMetadata({
name: 'AppModuleFactory',
runtime: AppModuleFactory,
moduleUrl: assetUrl('core', 'linker/app_module_factory')
});
static AppModuleInjector = new CompileIdentifierMetadata({
name: 'AppModuleInjector',
runtime: AppModuleInjector,
moduleUrl: assetUrl('core', 'linker/app_module_factory')
});
static ValueUnwrapper = new CompileIdentifierMetadata( static ValueUnwrapper = new CompileIdentifierMetadata(
{name: 'ValueUnwrapper', moduleUrl: CD_MODULE_URL, runtime: impValueUnwrapper}); {name: 'ValueUnwrapper', moduleUrl: CD_MODULE_URL, runtime: impValueUnwrapper});
static Injector = new CompileIdentifierMetadata( static Injector = new CompileIdentifierMetadata(

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, AttributeMetadata, 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, AppModuleMetadata, AttributeMetadata, ComponentMetadata, HostMetadata, Inject, InjectMetadata, Injectable, Optional, OptionalMetadata, Provider, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewMetadata, ViewQueryMetadata, resolveForwardRef} from '@angular/core';
import {LIFECYCLE_HOOKS_VALUES, ReflectorReader, createProvider, isProviderLiteral, reflector} from '../core_private'; import {LIFECYCLE_HOOKS_VALUES, ReflectorReader, createProvider, isProviderLiteral, reflector} from '../core_private';
import {StringMapWrapper} from '../src/facade/collection'; import {StringMapWrapper} from '../src/facade/collection';
@ -27,6 +27,7 @@ import {ViewResolver} from './view_resolver';
export class CompileMetadataResolver { export class CompileMetadataResolver {
private _directiveCache = new Map<Type, cpl.CompileDirectiveMetadata>(); private _directiveCache = new Map<Type, cpl.CompileDirectiveMetadata>();
private _pipeCache = new Map<Type, cpl.CompilePipeMetadata>(); private _pipeCache = new Map<Type, cpl.CompilePipeMetadata>();
private _appModuleCache = new Map<Type, cpl.CompileAppModuleMetadata>();
private _anonymousTypes = new Map<Object, number>(); private _anonymousTypes = new Map<Object, number>();
private _anonymousTypeIndex = 0; private _anonymousTypeIndex = 0;
@ -49,14 +50,16 @@ export class CompileMetadataResolver {
return sanitizeIdentifier(identifier); return sanitizeIdentifier(identifier);
} }
clearCacheFor(compType: Type) { clearCacheFor(type: Type) {
this._directiveCache.delete(compType); this._directiveCache.delete(type);
this._pipeCache.delete(compType); this._pipeCache.delete(type);
this._appModuleCache.delete(type);
} }
clearCache() { clearCache() {
this._directiveCache.clear(); this._directiveCache.clear();
this._pipeCache.clear(); this._pipeCache.clear();
this._appModuleCache.clear();
} }
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata { getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
@ -102,6 +105,7 @@ export class CompileMetadataResolver {
} }
getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata { getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
directiveType = resolveForwardRef(directiveType);
var meta = this._directiveCache.get(directiveType); var meta = this._directiveCache.get(directiveType);
if (isBlank(meta)) { if (isBlank(meta)) {
var dirMeta = this._directiveResolver.resolve(directiveType); var dirMeta = this._directiveResolver.resolve(directiveType);
@ -176,6 +180,72 @@ export class CompileMetadataResolver {
return meta; 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;
moduleType = resolveForwardRef(moduleType);
var compileMeta = this._appModuleCache.get(moduleType);
if (isBlank(compileMeta) || !useCache) {
if (!meta) {
meta = this._reflector.annotations(moduleType)
.find((meta) => meta instanceof AppModuleMetadata);
}
if (!meta) {
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[] = [];
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);
});
}
compileMeta = new cpl.CompileAppModuleMetadata({
type: this.getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)),
providers: providers,
directives: directives,
pipes: pipes,
precompile: precompile,
modules: modules
});
if (useCache) {
this._appModuleCache.set(moduleType, compileMeta);
}
}
return compileMeta;
}
/** /**
* @param someType a symbol which may or may not be a directive type * @param someType a symbol which may or may not be a directive type
* @returns {cpl.CompileDirectiveMetadata} if possible, otherwise null. * @returns {cpl.CompileDirectiveMetadata} if possible, otherwise null.
@ -193,6 +263,7 @@ export class CompileMetadataResolver {
getTypeMetadata(type: Type, moduleUrl: string, dependencies: any[] = null): getTypeMetadata(type: Type, moduleUrl: string, dependencies: any[] = null):
cpl.CompileTypeMetadata { cpl.CompileTypeMetadata {
type = resolveForwardRef(type);
return new cpl.CompileTypeMetadata({ return new cpl.CompileTypeMetadata({
name: this.sanitizeTokenName(type), name: this.sanitizeTokenName(type),
moduleUrl: moduleUrl, moduleUrl: moduleUrl,
@ -203,6 +274,7 @@ export class CompileMetadataResolver {
getFactoryMetadata(factory: Function, moduleUrl: string, dependencies: any[] = null): getFactoryMetadata(factory: Function, moduleUrl: string, dependencies: any[] = null):
cpl.CompileFactoryMetadata { cpl.CompileFactoryMetadata {
factory = resolveForwardRef(factory);
return new cpl.CompileFactoryMetadata({ return new cpl.CompileFactoryMetadata({
name: this.sanitizeTokenName(factory), name: this.sanitizeTokenName(factory),
moduleUrl: moduleUrl, moduleUrl: moduleUrl,
@ -212,6 +284,7 @@ export class CompileMetadataResolver {
} }
getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata { getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata {
pipeType = resolveForwardRef(pipeType);
var meta = this._pipeCache.get(pipeType); var meta = this._pipeCache.get(pipeType);
if (isBlank(meta)) { if (isBlank(meta)) {
var pipeMeta = this._pipeResolver.resolve(pipeType); var pipeMeta = this._pipeResolver.resolve(pipeType);
@ -349,8 +422,11 @@ export class CompileMetadataResolver {
return this.getProviderMetadata(provider); return this.getProviderMetadata(provider);
} else if (isProviderLiteral(provider)) { } else if (isProviderLiteral(provider)) {
return this.getProviderMetadata(createProvider(provider)); return this.getProviderMetadata(createProvider(provider));
} else { } else if (isValidType(provider)) {
return this.getTypeMetadata(provider, staticTypeModuleUrl(provider)); return this.getTypeMetadata(provider, staticTypeModuleUrl(provider));
} else {
throw new BaseException(
`Invalid provider - only instances of Provider and Type are allowed, got: ${stringify(provider)}`);
} }
}); });
} }
@ -468,21 +544,17 @@ function verifyNonBlankProviders(
return providersTree; return providersTree;
} }
function isStaticType(value: any): boolean {
return isStringMap(value) && isPresent(value['name']) && isPresent(value['filePath']);
}
function isValidType(value: any): boolean { function isValidType(value: any): boolean {
return isStaticType(value) || (value instanceof Type); return cpl.isStaticSymbol(value) || (value instanceof Type);
} }
function staticTypeModuleUrl(value: any): string { function staticTypeModuleUrl(value: any): string {
return isStaticType(value) ? value['filePath'] : null; return cpl.isStaticSymbol(value) ? value.filePath : null;
} }
function componentModuleUrl( function componentModuleUrl(
reflector: ReflectorReader, type: any, cmpMetadata: ComponentMetadata): string { reflector: ReflectorReader, type: any, cmpMetadata: ComponentMetadata): string {
if (isStaticType(type)) { if (cpl.isStaticSymbol(type)) {
return staticTypeModuleUrl(type); return staticTypeModuleUrl(type);
} }
@ -503,9 +575,8 @@ function convertToCompileValue(value: any): any {
class _CompileValueConverter extends ValueTransformer { class _CompileValueConverter extends ValueTransformer {
visitOther(value: any, context: any): any { visitOther(value: any, context: any): any {
if (isStaticType(value)) { if (cpl.isStaticSymbol(value)) {
return new cpl.CompileIdentifierMetadata( return new cpl.CompileIdentifierMetadata({name: value.name, moduleUrl: value.filePath});
{name: value['name'], moduleUrl: staticTypeModuleUrl(value)});
} else { } else {
return new cpl.CompileIdentifierMetadata({runtime: value}); return new cpl.CompileIdentifierMetadata({runtime: value});
} }

View File

@ -6,12 +6,15 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ComponentFactory} from '@angular/core'; import {AppModuleMetadata, ComponentMetadata} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, createHostComponentMeta} from './compile_metadata'; import {AppModuleCompiler} from './app_module_compiler';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer'; import {DirectiveNormalizer} from './directive_normalizer';
import {ListWrapper} from './facade/collection'; import {ListWrapper} from './facade/collection';
import {BaseException} from './facade/exceptions'; import {BaseException} from './facade/exceptions';
import {Identifiers} from './identifiers';
import {CompileMetadataResolver} from './metadata_resolver';
import {OutputEmitter} from './output/abstract_emitter'; import {OutputEmitter} from './output/abstract_emitter';
import * as o from './output/output_ast'; import * as o from './output/output_ast';
import {CompiledStylesheet, StyleCompiler} from './style_compiler'; import {CompiledStylesheet, StyleCompiler} from './style_compiler';
@ -20,79 +23,164 @@ import {assetUrl} from './util';
import {ComponentFactoryDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler'; import {ComponentFactoryDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
import {XHR} from './xhr'; import {XHR} from './xhr';
var _COMPONENT_FACTORY_IDENTIFIER = new CompileIdentifierMetadata({
name: 'ComponentFactory',
runtime: ComponentFactory,
moduleUrl: assetUrl('core', 'linker/component_factory')
});
export class SourceModule { export class SourceModule {
constructor(public moduleUrl: string, public source: string) {} constructor(public moduleUrl: string, public source: string) {}
} }
export class StyleSheetSourceWithImports { export class AppModulesSummary {
constructor(public source: SourceModule, public importedUrls: string[]) {} private _compAppModule = new Map<string, StaticSymbol>();
} private _hashKey(type: StaticSymbol) { return `${type.filePath}#${type.name}`; }
export class NormalizedComponentWithViewDirectives { hasComponent(component: StaticSymbol): boolean {
constructor( return this._compAppModule.has(this._hashKey(component));
public component: CompileDirectiveMetadata, public directives: CompileDirectiveMetadata[],
public pipes: CompilePipeMetadata[]) {}
}
export class OfflineCompiler {
constructor(
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _outputEmitter: OutputEmitter) {}
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
Promise<CompileDirectiveMetadata> {
return this._directiveNormalizer.normalizeDirective(directive).asyncResult;
} }
compileTemplates(components: NormalizedComponentWithViewDirectives[]): SourceModule[] { addComponent(module: StaticSymbol, component: StaticSymbol) {
if (components.length === 0) { this._compAppModule.set(this._hashKey(component), module);
throw new BaseException('No components given'); }
}
var statements: o.DeclareVarStmt[] = [];
var exportedVars: string[] = [];
var moduleUrl = _ngfactoryModuleUrl(components[0].component.type);
var outputSourceModules: SourceModule[] = [];
components.forEach(componentWithDirs => {
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
_assertComponent(compMeta);
var fileSuffix = _splitLastSuffix(compMeta.type.moduleUrl)[1];
var stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
outputSourceModules.push(this._codgenStyles(compiledStyleSheet, fileSuffix));
});
var compViewFactoryVar = this._compileComponent( getModule(comp: StaticSymbol): StaticSymbol {
compMeta, componentWithDirs.directives, componentWithDirs.pipes, return this._compAppModule.get(this._hashKey(comp));
stylesCompileResults.componentStylesheet, fileSuffix, statements); }
exportedVars.push(compViewFactoryVar); }
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) {}
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); analyzeModules(appModules: StaticSymbol[]): AppModulesSummary {
var hostViewFactoryVar = let result = new AppModulesSummary();
this._compileComponent(hostMeta, [compMeta], [], null, fileSuffix, statements); appModules.forEach((appModule) => {
var compFactoryVar = _componentFactoryName(compMeta.type); let appModuleMeta = this._metadataResolver.getAppModuleMetadata(appModule);
statements.push( appModuleMeta.precompile.forEach(
o.variable(compFactoryVar) (precompileComp) =>
.set(o.importExpr(_COMPONENT_FACTORY_IDENTIFIER, [o.importType(compMeta.type)]) this._getTransitiveComponents(appModule, <any>precompileComp.runtime, result));
.instantiate(
[
o.literal(compMeta.selector), o.variable(hostViewFactoryVar),
o.importExpr(compMeta.type)
],
o.importType(
_COMPONENT_FACTORY_IDENTIFIER, [o.importType(compMeta.type)],
[o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final]));
exportedVars.push(compFactoryVar);
}); });
outputSourceModules.unshift(this._codegenSourceModule(moduleUrl, statements, exportedVars)); return result;
return outputSourceModules; }
private _getTransitiveComponents(
appModule: StaticSymbol, component: StaticSymbol,
target: AppModulesSummary = new AppModulesSummary()): AppModulesSummary {
var compMeta = this._metadataResolver.getDirectiveMetadata(<any>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(<any>component).forEach((dirMeta) => {
this._getTransitiveComponents(appModule, <any>dirMeta.type.runtime);
});
compMeta.precompile.forEach((precompileComp) => {
this._getTransitiveComponents(appModule, <any>precompileComp.type.runtime);
});
return target;
}
clearCache() {
this._directiveNormalizer.clearCache();
this._metadataResolver.clearCache();
}
compile(
moduleUrl: string, appModulesSummary: AppModulesSummary, components: StaticSymbol[],
appModules: StaticSymbol[]): Promise<SourceModule[]> {
let fileSuffix = _splitLastSuffix(moduleUrl)[1];
let statements: o.Statement[] = [];
let exportedVars: string[] = [];
let outputSourceModules: SourceModule[] = [];
// compile app modules
exportedVars.push(
...appModules.map((appModule) => this._compileAppModule(appModule, 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)));
}
return Promise
.all([
this._metadataResolver.getDirectiveMetadata(<any>compType), ...appModuleDirectives,
...this._metadataResolver.getViewDirectivesMetadata(<any>compType)
].map(dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
.then((normalizedCompWithDirectives) => {
let compMeta = normalizedCompWithDirectives[0];
let dirMetas = normalizedCompWithDirectives.slice(1);
_assertComponent(compMeta);
// compile styles
let 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));
});
}))
.then(() => {
if (statements.length > 0) {
outputSourceModules.unshift(this._codegenSourceModule(
_ngfactoryModuleUrl(moduleUrl), statements, exportedVars));
}
return outputSourceModules;
});
}
private _compileAppModule(appModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
let appModuleMeta = this._metadataResolver.getAppModuleMetadata(appModuleType);
let appCompileResult = this._appModuleCompiler.compile(appModuleMeta);
appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.name = _componentFactoryName(dep.comp);
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
});
targetStatements.push(...appCompileResult.statements);
return appCompileResult.appModuleFactoryVar;
}
private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string {
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
var hostViewFactoryVar =
this._compileComponent(hostMeta, [compMeta], [], null, fileSuffix, targetStatements);
var compFactoryVar = _componentFactoryName(compMeta.type);
targetStatements.push(
o.variable(compFactoryVar)
.set(o.importExpr(Identifiers.ComponentFactory, [o.importType(compMeta.type)])
.instantiate(
[
o.literal(compMeta.selector), o.variable(hostViewFactoryVar),
o.importExpr(compMeta.type)
],
o.importType(
Identifiers.ComponentFactory, [o.importType(compMeta.type)],
[o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final]));
return compFactoryVar;
} }
private _compileComponent( private _compileComponent(
@ -130,11 +218,11 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
compileResult.dependencies.forEach((dep) => { compileResult.dependencies.forEach((dep) => {
if (dep instanceof ViewFactoryDependency) { if (dep instanceof ViewFactoryDependency) {
let vfd = <ViewFactoryDependency>dep; let vfd = <ViewFactoryDependency>dep;
vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp); vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp.moduleUrl);
} else if (dep instanceof ComponentFactoryDependency) { } else if (dep instanceof ComponentFactoryDependency) {
let cfd = <ComponentFactoryDependency>dep; let cfd = <ComponentFactoryDependency>dep;
cfd.placeholder.name = _componentFactoryName(cfd.comp); cfd.placeholder.name = _componentFactoryName(cfd.comp);
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp); cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
} }
}); });
return compileResult.statements; return compileResult.statements;
@ -149,8 +237,8 @@ function _resolveStyleStatements(
return compileResult.statements; return compileResult.statements;
} }
function _ngfactoryModuleUrl(comp: CompileIdentifierMetadata): string { function _ngfactoryModuleUrl(compUrl: string): string {
var urlWithSuffix = _splitLastSuffix(comp.moduleUrl); var urlWithSuffix = _splitLastSuffix(compUrl);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`; return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
} }

View File

@ -1,86 +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 {AppElement, AppView, DebugAppView} from '../../core_private';
import {BaseException} from '../facade/exceptions';
import {isPresent} from '../facade/lang';
import {DynamicInstance, InstanceFactory} from './output_interpreter';
export class InterpretiveAppViewInstanceFactory implements InstanceFactory {
createInstance(
superClass: any, clazz: any, args: any[], props: Map<string, any>,
getters: Map<string, Function>, methods: Map<string, Function>): any {
if (superClass === AppView) {
// We are always using DebugAppView as parent.
// However, in prod mode we generate a constructor call that does
// not have the argument for the debugNodeInfos.
args = args.concat([null]);
return new _InterpretiveAppView(args, props, getters, methods);
} else if (superClass === DebugAppView) {
return new _InterpretiveAppView(args, props, getters, methods);
}
throw new BaseException(`Can't instantiate class ${superClass} in interpretative mode`);
}
}
class _InterpretiveAppView extends DebugAppView<any> implements DynamicInstance {
constructor(
args: any[], public props: Map<string, any>, public getters: Map<string, Function>,
public methods: Map<string, Function>) {
super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
}
createInternal(rootSelector: string|any): AppElement {
var m = this.methods.get('createInternal');
if (isPresent(m)) {
return m(rootSelector);
} else {
return super.createInternal(rootSelector);
}
}
injectorGetInternal(token: any, nodeIndex: number, notFoundResult: any): any {
var m = this.methods.get('injectorGetInternal');
if (isPresent(m)) {
return m(token, nodeIndex, notFoundResult);
} else {
return super.injectorGet(token, nodeIndex, notFoundResult);
}
}
detachInternal(): void {
var m = this.methods.get('detachInternal');
if (isPresent(m)) {
return m();
} else {
return super.detachInternal();
}
}
destroyInternal(): void {
var m = this.methods.get('destroyInternal');
if (isPresent(m)) {
return m();
} else {
return super.destroyInternal();
}
}
dirtyParentQueriesInternal(): void {
var m = this.methods.get('dirtyParentQueriesInternal');
if (isPresent(m)) {
return m();
} else {
return super.dirtyParentQueriesInternal();
}
}
detectChangesInternal(throwOnChange: boolean): void {
var m = this.methods.get('detectChangesInternal');
if (isPresent(m)) {
return m(throwOnChange);
} else {
return super.detectChangesInternal(throwOnChange);
}
}
}

View File

@ -7,7 +7,11 @@
*/ */
import {CompileIdentifierMetadata} from '../compile_metadata'; import {CompileIdentifierMetadata} from '../compile_metadata';
import {StringMapWrapper} from '../facade/collection';
import {BaseException} from '../facade/exceptions';
import {isBlank, isPresent, isString} from '../facade/lang'; import {isBlank, isPresent, isString} from '../facade/lang';
import {ValueTransformer, visitValue} from '../util';
//// Types //// Types
@ -875,10 +879,6 @@ export function importType(
return isPresent(id) ? new ExternalType(id, typeParams, typeModifiers) : null; return isPresent(id) ? new ExternalType(id, typeParams, typeModifiers) : null;
} }
export function literal(value: any, type: Type = null): LiteralExpr {
return new LiteralExpr(value, type);
}
export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr { export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr {
return new LiteralArrayExpr(values, type); return new LiteralArrayExpr(values, type);
} }
@ -895,3 +895,30 @@ export function not(expr: Expression): NotExpr {
export function fn(params: FnParam[], body: Statement[], type: Type = null): FunctionExpr { export function fn(params: FnParam[], body: Statement[], type: Type = null): FunctionExpr {
return new FunctionExpr(params, body, type); return new FunctionExpr(params, body, type);
} }
export function literal(value: any, type: Type = null): Expression {
return visitValue(value, new _ValueOutputAstTransformer(), type);
}
class _ValueOutputAstTransformer implements ValueTransformer {
visitArray(arr: any[], type: Type): Expression {
return literalArr(arr.map(value => visitValue(value, this, null)), type);
}
visitStringMap(map: {[key: string]: any}, type: MapType): Expression {
var entries: Array<string|Expression>[] = [];
StringMapWrapper.forEach(map, (value: any, key: string) => {
entries.push([key, visitValue(value, this, null)]);
});
return literalMap(entries, type);
}
visitPrimitive(value: any, type: Type): Expression { return new LiteralExpr(value, type); }
visitOther(value: any, type: Type): Expression {
if (value instanceof CompileIdentifierMetadata) {
return importExpr(value);
} else if (value instanceof Expression) {
return value;
} else {
throw new BaseException(`Illegal state: Don't now how to compile value ${value}`);
}
}
}

View File

@ -6,49 +6,23 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {reflector} from '../../core_private';
import {ObservableWrapper} from '../facade/async'; import {ObservableWrapper} from '../facade/async';
import {ListWrapper} from '../facade/collection'; import {ListWrapper} from '../facade/collection';
import {BaseException, unimplemented} from '../facade/exceptions'; import {BaseException, unimplemented} from '../facade/exceptions';
import {FunctionWrapper, IS_DART, isPresent} from '../facade/lang'; import {IS_DART, isPresent} from '../facade/lang';
import {debugOutputAstAsDart} from './dart_emitter'; import {debugOutputAstAsDart} from './dart_emitter';
import * as o from './output_ast'; import * as o from './output_ast';
import {debugOutputAstAsTypeScript} from './ts_emitter'; import {debugOutputAstAsTypeScript} from './ts_emitter';
export function interpretStatements( export function interpretStatements(statements: o.Statement[], resultVar: string): any {
statements: o.Statement[], resultVar: string, instanceFactory: InstanceFactory): any {
var stmtsWithReturn = statements.concat([new o.ReturnStatement(o.variable(resultVar))]); var stmtsWithReturn = statements.concat([new o.ReturnStatement(o.variable(resultVar))]);
var ctx = new _ExecutionContext( var ctx = new _ExecutionContext(null, null, null, new Map<string, any>());
null, null, null, null, new Map<string, any>(), new Map<string, any>(),
new Map<string, Function>(), new Map<string, Function>(), instanceFactory);
var visitor = new StatementInterpreter(); var visitor = new StatementInterpreter();
var result = visitor.visitAllStatements(stmtsWithReturn, ctx); var result = visitor.visitAllStatements(stmtsWithReturn, ctx);
return isPresent(result) ? result.value : null; return isPresent(result) ? result.value : null;
} }
export interface InstanceFactory {
createInstance(
superClass: any, clazz: any, constructorArgs: any[], props: Map<string, any>,
getters: Map<string, Function>, methods: Map<string, Function>): DynamicInstance;
}
export abstract class DynamicInstance {
get props(): Map<string, any> { return unimplemented(); }
get getters(): Map<string, Function> { return unimplemented(); }
get methods(): Map<string, any> { return unimplemented(); }
get clazz(): any { return unimplemented(); }
}
function isDynamicInstance(instance: any): any {
if (IS_DART) {
return instance instanceof DynamicInstance;
} else {
return isPresent(instance) && isPresent(instance.props) && isPresent(instance.getters) &&
isPresent(instance.methods);
}
}
function _executeFunctionStatements( function _executeFunctionStatements(
varNames: string[], varValues: any[], statements: o.Statement[], ctx: _ExecutionContext, varNames: string[], varValues: any[], statements: o.Statement[], ctx: _ExecutionContext,
visitor: StatementInterpreter): any { visitor: StatementInterpreter): any {
@ -62,15 +36,11 @@ function _executeFunctionStatements(
class _ExecutionContext { class _ExecutionContext {
constructor( constructor(
public parent: _ExecutionContext, public superClass: any, public superInstance: any, public parent: _ExecutionContext, public instance: any, public className: string,
public className: string, public vars: Map<string, any>, public props: Map<string, any>, public vars: Map<string, any>) {}
public getters: Map<string, Function>, public methods: Map<string, Function>,
public instanceFactory: InstanceFactory) {}
createChildWihtLocalVars(): _ExecutionContext { createChildWihtLocalVars(): _ExecutionContext {
return new _ExecutionContext( return new _ExecutionContext(this, this.instance, this.className, new Map<string, any>());
this, this.superClass, this.superInstance, this.className, new Map<string, any>(),
this.props, this.getters, this.methods, this.instanceFactory);
} }
} }
@ -78,38 +48,44 @@ class ReturnValue {
constructor(public value: any) {} constructor(public value: any) {}
} }
class _DynamicClass { function createDynamicClass(
constructor( _classStmt: o.ClassStmt, _ctx: _ExecutionContext, _visitor: StatementInterpreter): Function {
private _classStmt: o.ClassStmt, private _ctx: _ExecutionContext, let propertyDescriptors: {[key: string]: any} = {};
private _visitor: StatementInterpreter) {}
instantiate(args: any[]): DynamicInstance { _classStmt.getters.forEach((getter: o.ClassGetter) => {
var props = new Map<string, any>(); // Note: use `function` instead of arrow function to capture `this`
var getters = new Map<string, Function>(); propertyDescriptors[getter.name] = {
var methods = new Map<string, Function>(); configurable: false,
var superClass = this._classStmt.parent.visitExpression(this._visitor, this._ctx); get: function() {
var instanceCtx = new _ExecutionContext( let instanceCtx = new _ExecutionContext(_ctx, this, _classStmt.name, _ctx.vars);
this._ctx, superClass, null, this._classStmt.name, this._ctx.vars, props, getters, methods, return _executeFunctionStatements([], [], getter.body, instanceCtx, _visitor);
this._ctx.instanceFactory); }
};
});
_classStmt.methods.forEach(function(method: o.ClassMethod) {
var paramNames = method.params.map(param => param.name);
// Note: use `function` instead of arrow function to capture `this`
propertyDescriptors[method.name] = {
writable: false,
configurable: false,
value: function(...args: any[]) {
let instanceCtx = new _ExecutionContext(_ctx, this, _classStmt.name, _ctx.vars);
return _executeFunctionStatements(paramNames, args, method.body, instanceCtx, _visitor);
}
};
});
this._classStmt.fields.forEach((field: o.ClassField) => { props.set(field.name, null); }); var ctorParamNames = _classStmt.constructorMethod.params.map(param => param.name);
this._classStmt.getters.forEach((getter: o.ClassGetter) => { // Note: use `function` instead of arrow function to capture `this`
getters.set( var ctor = function(...args: any[]) {
getter.name, let instanceCtx = new _ExecutionContext(_ctx, this, _classStmt.name, _ctx.vars);
() => _executeFunctionStatements([], [], getter.body, instanceCtx, this._visitor)); _classStmt.fields.forEach((field) => { this[field.name] = undefined; });
});
this._classStmt.methods.forEach((method: o.ClassMethod) => {
var paramNames = method.params.map(param => param.name);
methods.set(method.name, _declareFn(paramNames, method.body, instanceCtx, this._visitor));
});
var ctorParamNames = this._classStmt.constructorMethod.params.map(param => param.name);
_executeFunctionStatements( _executeFunctionStatements(
ctorParamNames, args, this._classStmt.constructorMethod.body, instanceCtx, this._visitor); ctorParamNames, args, _classStmt.constructorMethod.body, instanceCtx, _visitor);
return instanceCtx.superInstance; };
} var superClass = _classStmt.parent.visitExpression(_visitor, _ctx);
ctor.prototype = Object.create(superClass.prototype, propertyDescriptors);
debugAst(): string { return this._visitor.debugAst(this._classStmt); } return ctor;
} }
class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
@ -138,8 +114,9 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
if (isPresent(ast.builtin)) { if (isPresent(ast.builtin)) {
switch (ast.builtin) { switch (ast.builtin) {
case o.BuiltinVar.Super: case o.BuiltinVar.Super:
return ctx.instance.__proto__;
case o.BuiltinVar.This: case o.BuiltinVar.This:
return ctx.superInstance; return ctx.instance;
case o.BuiltinVar.CatchError: case o.BuiltinVar.CatchError:
varName = CATCH_ERROR_VAR; varName = CATCH_ERROR_VAR;
break; break;
@ -169,23 +146,14 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
visitWritePropExpr(expr: o.WritePropExpr, ctx: _ExecutionContext): any { visitWritePropExpr(expr: o.WritePropExpr, ctx: _ExecutionContext): any {
var receiver = expr.receiver.visitExpression(this, ctx); var receiver = expr.receiver.visitExpression(this, ctx);
var value = expr.value.visitExpression(this, ctx); var value = expr.value.visitExpression(this, ctx);
if (isDynamicInstance(receiver)) { receiver[expr.name] = value;
var di = <DynamicInstance>receiver;
if (di.props.has(expr.name)) {
di.props.set(expr.name, value);
} else {
reflector.setter(expr.name)(receiver, value);
}
} else {
reflector.setter(expr.name)(receiver, value);
}
return value; return value;
} }
visitInvokeMethodExpr(expr: o.InvokeMethodExpr, ctx: _ExecutionContext): any { visitInvokeMethodExpr(expr: o.InvokeMethodExpr, ctx: _ExecutionContext): any {
var receiver = expr.receiver.visitExpression(this, ctx); var receiver = expr.receiver.visitExpression(this, ctx);
var args = this.visitAllExpressions(expr.args, ctx); var args = this.visitAllExpressions(expr.args, ctx);
var result: any /** TODO #9100 */; var result: any;
if (isPresent(expr.builtin)) { if (isPresent(expr.builtin)) {
switch (expr.builtin) { switch (expr.builtin) {
case o.BuiltinMethod.ConcatArray: case o.BuiltinMethod.ConcatArray:
@ -204,15 +172,8 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
default: default:
throw new BaseException(`Unknown builtin method ${expr.builtin}`); throw new BaseException(`Unknown builtin method ${expr.builtin}`);
} }
} else if (isDynamicInstance(receiver)) {
var di = <DynamicInstance>receiver;
if (di.methods.has(expr.name)) {
result = FunctionWrapper.apply(di.methods.get(expr.name), args);
} else {
result = reflector.method(expr.name)(receiver, args);
}
} else { } else {
result = reflector.method(expr.name)(receiver, args); result = receiver[expr.name].apply(receiver, args);
} }
return result; return result;
} }
@ -220,20 +181,18 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
var args = this.visitAllExpressions(stmt.args, ctx); var args = this.visitAllExpressions(stmt.args, ctx);
var fnExpr = stmt.fn; var fnExpr = stmt.fn;
if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) { if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) {
ctx.superInstance = ctx.instanceFactory.createInstance( ctx.instance.constructor.prototype.constructor.apply(ctx.instance, args);
ctx.superClass, ctx.className, args, ctx.props, ctx.getters, ctx.methods);
ctx.parent.superInstance = ctx.superInstance;
return null; return null;
} else { } else {
var fn = stmt.fn.visitExpression(this, ctx); var fn = stmt.fn.visitExpression(this, ctx);
return FunctionWrapper.apply(fn, args); return fn.apply(null, args);
} }
} }
visitReturnStmt(stmt: o.ReturnStatement, ctx: _ExecutionContext): any { visitReturnStmt(stmt: o.ReturnStatement, ctx: _ExecutionContext): any {
return new ReturnValue(stmt.value.visitExpression(this, ctx)); return new ReturnValue(stmt.value.visitExpression(this, ctx));
} }
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: _ExecutionContext): any { visitDeclareClassStmt(stmt: o.ClassStmt, ctx: _ExecutionContext): any {
var clazz = new _DynamicClass(stmt, ctx, this); var clazz = createDynamicClass(stmt, ctx, this);
ctx.vars.set(stmt.name, clazz); ctx.vars.set(stmt.name, clazz);
return null; return null;
} }
@ -266,11 +225,7 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: _ExecutionContext): any { visitInstantiateExpr(ast: o.InstantiateExpr, ctx: _ExecutionContext): any {
var args = this.visitAllExpressions(ast.args, ctx); var args = this.visitAllExpressions(ast.args, ctx);
var clazz = ast.classExpr.visitExpression(this, ctx); var clazz = ast.classExpr.visitExpression(this, ctx);
if (clazz instanceof _DynamicClass) { return new clazz(...args);
return clazz.instantiate(args);
} else {
return FunctionWrapper.apply(reflector.factory(clazz), args);
}
} }
visitLiteralExpr(ast: o.LiteralExpr, ctx: _ExecutionContext): any { return ast.value; } visitLiteralExpr(ast: o.LiteralExpr, ctx: _ExecutionContext): any { return ast.value; }
visitExternalExpr(ast: o.ExternalExpr, ctx: _ExecutionContext): any { return ast.value.runtime; } visitExternalExpr(ast: o.ExternalExpr, ctx: _ExecutionContext): any { return ast.value.runtime; }
@ -337,22 +292,9 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
} }
} }
visitReadPropExpr(ast: o.ReadPropExpr, ctx: _ExecutionContext): any { visitReadPropExpr(ast: o.ReadPropExpr, ctx: _ExecutionContext): any {
var result: any /** TODO #9100 */; var result: any;
var receiver = ast.receiver.visitExpression(this, ctx); var receiver = ast.receiver.visitExpression(this, ctx);
if (isDynamicInstance(receiver)) { result = receiver[ast.name];
var di = <DynamicInstance>receiver;
if (di.props.has(ast.name)) {
result = di.props.get(ast.name);
} else if (di.getters.has(ast.name)) {
result = di.getters.get(ast.name)();
} else if (di.methods.has(ast.name)) {
result = di.methods.get(ast.name);
} else {
result = reflector.getter(ast.name)(receiver);
}
} else {
result = reflector.getter(ast.name)(receiver);
}
return result; return result;
} }
visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: _ExecutionContext): any { visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: _ExecutionContext): any {
@ -366,7 +308,7 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: _ExecutionContext): any { visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: _ExecutionContext): any {
var result = {}; var result = {};
ast.entries.forEach( ast.entries.forEach(
(entry) => (result as any /** TODO #9100 */)[<string>entry[0]] = (entry) => (result as any)[<string>entry[0]] =
(<o.Expression>entry[1]).visitExpression(this, ctx)); (<o.Expression>entry[1]).visitExpression(this, ctx));
return result; return result;
} }
@ -390,61 +332,7 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
function _declareFn( function _declareFn(
varNames: string[], statements: o.Statement[], ctx: _ExecutionContext, varNames: string[], statements: o.Statement[], ctx: _ExecutionContext,
visitor: StatementInterpreter): Function { visitor: StatementInterpreter): Function {
switch (varNames.length) { return (...args: any[]) => _executeFunctionStatements(varNames, args, statements, ctx, visitor);
case 0:
return () => _executeFunctionStatements(varNames, [], statements, ctx, visitor);
case 1:
return (d0: any /** TODO #9100 */) =>
_executeFunctionStatements(varNames, [d0], statements, ctx, visitor);
case 2:
return (d0: any /** TODO #9100 */, d1: any /** TODO #9100 */) =>
_executeFunctionStatements(varNames, [d0, d1], statements, ctx, visitor);
case 3:
return (d0: any /** TODO #9100 */, d1: any /** TODO #9100 */, d2: any /** TODO #9100 */) =>
_executeFunctionStatements(varNames, [d0, d1, d2], statements, ctx, visitor);
case 4:
return (d0: any /** TODO #9100 */, d1: any /** TODO #9100 */, d2: any /** TODO #9100 */,
d3: any /** TODO #9100 */) =>
_executeFunctionStatements(varNames, [d0, d1, d2, d3], statements, ctx, visitor);
case 5:
return (d0: any /** TODO #9100 */, d1: any /** TODO #9100 */, d2: any /** TODO #9100 */,
d3: any /** TODO #9100 */, d4: any /** TODO #9100 */) =>
_executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4], statements, ctx, visitor);
case 6:
return (d0: any /** TODO #9100 */, d1: any /** TODO #9100 */, d2: any /** TODO #9100 */,
d3: any /** TODO #9100 */, d4: any /** TODO #9100 */, d5: any /** TODO #9100 */) =>
_executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5], statements, ctx, visitor);
case 7:
return (d0: any /** TODO #9100 */, d1: any /** TODO #9100 */, d2: any /** TODO #9100 */,
d3: any /** TODO #9100 */, d4: any /** TODO #9100 */, d5: any /** TODO #9100 */,
d6: any /** TODO #9100 */) =>
_executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5, d6], statements, ctx, visitor);
case 8:
return (d0: any /** TODO #9100 */, d1: any /** TODO #9100 */, d2: any /** TODO #9100 */,
d3: any /** TODO #9100 */, d4: any /** TODO #9100 */, d5: any /** TODO #9100 */,
d6: any /** TODO #9100 */, d7: any /** TODO #9100 */) =>
_executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5, d6, d7], statements, ctx, visitor);
case 9:
return (d0: any /** TODO #9100 */, d1: any /** TODO #9100 */, d2: any /** TODO #9100 */,
d3: any /** TODO #9100 */, d4: any /** TODO #9100 */, d5: any /** TODO #9100 */,
d6: any /** TODO #9100 */, d7: any /** TODO #9100 */, d8: any /** TODO #9100 */) =>
_executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5, d6, d7, d8], statements, ctx, visitor);
case 10:
return (d0: any /** TODO #9100 */, d1: any /** TODO #9100 */, d2: any /** TODO #9100 */,
d3: any /** TODO #9100 */, d4: any /** TODO #9100 */, d5: any /** TODO #9100 */,
d6: any /** TODO #9100 */, d7: any /** TODO #9100 */, d8: any /** TODO #9100 */,
d9: any /** TODO #9100 */) =>
_executeFunctionStatements(
varNames, [d0, d1, d2, d3, d4, d5, d6, d7, d8, d9], statements, ctx, visitor);
default:
throw new BaseException(
'Declaring functions with more than 10 arguments is not supported right now');
}
} }
var CATCH_ERROR_VAR = 'error'; var CATCH_ERROR_VAR = 'error';

View File

@ -5,13 +5,16 @@
* Use of this source code is governed by an MIT-style license that can be * Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {isPresent} from './facade/lang';
export class ParseLocation { export class ParseLocation {
constructor( constructor(
public file: ParseSourceFile, public offset: number, public line: number, public file: ParseSourceFile, public offset: number, public line: number,
public col: number) {} public col: number) {}
toString(): string { return `${this.file.url}@${this.line}:${this.col}`; } toString(): string {
return isPresent(this.offset) ? `${this.file.url}@${this.line}:${this.col}` : this.file.url;
}
} }
export class ParseSourceFile { export class ParseSourceFile {
@ -39,38 +42,41 @@ export abstract class ParseError {
toString(): string { toString(): string {
var source = this.span.start.file.content; var source = this.span.start.file.content;
var ctxStart = this.span.start.offset; var ctxStart = this.span.start.offset;
if (ctxStart > source.length - 1) { var contextStr = '';
ctxStart = source.length - 1; if (isPresent(ctxStart)) {
} if (ctxStart > source.length - 1) {
var ctxEnd = ctxStart; ctxStart = source.length - 1;
var ctxLen = 0; }
var ctxLines = 0; var ctxEnd = ctxStart;
var ctxLen = 0;
var ctxLines = 0;
while (ctxLen < 100 && ctxStart > 0) { while (ctxLen < 100 && ctxStart > 0) {
ctxStart--; ctxStart--;
ctxLen++; ctxLen++;
if (source[ctxStart] == '\n') { if (source[ctxStart] == '\n') {
if (++ctxLines == 3) { if (++ctxLines == 3) {
break; break;
}
} }
} }
}
ctxLen = 0; ctxLen = 0;
ctxLines = 0; ctxLines = 0;
while (ctxLen < 100 && ctxEnd < source.length - 1) { while (ctxLen < 100 && ctxEnd < source.length - 1) {
ctxEnd++; ctxEnd++;
ctxLen++; ctxLen++;
if (source[ctxEnd] == '\n') { if (source[ctxEnd] == '\n') {
if (++ctxLines == 3) { if (++ctxLines == 3) {
break; break;
}
} }
} }
let context = source.substring(ctxStart, this.span.start.offset) + '[ERROR ->]' +
source.substring(this.span.start.offset, ctxEnd + 1);
contextStr = ` ("${context}")`;
} }
return `${this.msg}${contextStr}: ${this.span.start}`;
let context = source.substring(ctxStart, this.span.start.offset) + '[ERROR ->]' +
source.substring(this.span.start.offset, ctxEnd + 1);
return `${this.msg} ("${context}"): ${this.span.start}`;
} }
} }

View File

@ -7,9 +7,10 @@
*/ */
import {ListWrapper} from '../src/facade/collection'; import {ListWrapper} from '../src/facade/collection';
import {BaseException} from '../src/facade/exceptions';
import {isArray, isBlank, isPresent, normalizeBlank} from '../src/facade/lang'; import {isArray, isBlank, isPresent, normalizeBlank} from '../src/facade/lang';
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMap, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata'; import {CompileAppModuleMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMap, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata';
import {Identifiers, identifierToken} from './identifiers'; import {Identifiers, identifierToken} from './identifiers';
import {ParseError, ParseSourceSpan} from './parse_util'; import {ParseError, ParseSourceSpan} from './parse_util';
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst, VariableAst} from './template_ast'; import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst, VariableAst} from './template_ast';
@ -270,6 +271,115 @@ export class ProviderElementContext {
} }
} }
export class AppModuleProviderParser {
private _transformedProviders = new CompileTokenMap<ProviderAst>();
private _seenProviders = new CompileTokenMap<boolean>();
private _unparsedProviders: any[] = [];
private _allProviders: CompileTokenMap<ProviderAst>;
private _errors: ProviderError[] = [];
constructor(appModule: CompileAppModuleMetadata, sourceSpan: ParseSourceSpan) {
this._allProviders = new CompileTokenMap<ProviderAst>();
[appModule.type].concat(appModule.modules).forEach((appModuleType: CompileTypeMetadata) => {
var appModuleProvider = new CompileProviderMetadata(
{token: new CompileTokenMetadata({identifier: appModuleType}), useClass: appModuleType});
_resolveProviders(
[appModuleProvider], ProviderAstType.PublicService, true, sourceSpan, this._errors,
this._allProviders);
});
_resolveProviders(
_normalizeProviders(appModule.providers, sourceSpan, this._errors),
ProviderAstType.PublicService, false, sourceSpan, this._errors, this._allProviders);
}
parse(): ProviderAst[] {
this._allProviders.values().forEach(
(provider) => { this._getOrCreateLocalProvider(provider.token, provider.eager); });
if (this._errors.length > 0) {
var errorString = this._errors.join('\n');
throw new BaseException(`Provider parse errors:\n${errorString}`);
}
return this._transformedProviders.values();
}
private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst {
var resolvedProvider = this._allProviders.get(token);
if (isBlank(resolvedProvider)) {
return null;
}
var transformedProviderAst = this._transformedProviders.get(token);
if (isPresent(transformedProviderAst)) {
return transformedProviderAst;
}
if (isPresent(this._seenProviders.get(token))) {
this._errors.push(new ProviderError(
`Cannot instantiate cyclic dependency! ${token.name}`, resolvedProvider.sourceSpan));
return null;
}
this._seenProviders.add(token, true);
var transformedProviders = resolvedProvider.providers.map((provider) => {
var transformedUseValue = provider.useValue;
var transformedUseExisting = provider.useExisting;
var transformedDeps: CompileDiDependencyMetadata[];
if (isPresent(provider.useExisting)) {
var existingDiDep = this._getDependency(
new CompileDiDependencyMetadata({token: provider.useExisting}), eager,
resolvedProvider.sourceSpan);
if (isPresent(existingDiDep.token)) {
transformedUseExisting = existingDiDep.token;
} else {
transformedUseExisting = null;
transformedUseValue = existingDiDep.value;
}
} else if (isPresent(provider.useFactory)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
} else if (isPresent(provider.useClass)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
}
return _transformProvider(provider, {
useExisting: transformedUseExisting,
useValue: transformedUseValue,
deps: transformedDeps
});
});
transformedProviderAst =
_transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders});
this._transformedProviders.add(token, transformedProviderAst);
return transformedProviderAst;
}
private _getDependency(
dep: CompileDiDependencyMetadata, eager: boolean = null,
requestorSourceSpan: ParseSourceSpan): CompileDiDependencyMetadata {
var foundLocal = false;
if (!dep.isSkipSelf && isPresent(dep.token)) {
// access the injector
if (dep.token.equalsTo(identifierToken(Identifiers.Injector)) ||
dep.token.equalsTo(identifierToken(Identifiers.ComponentFactoryResolver))) {
foundLocal = true;
// access providers
} else if (isPresent(this._getOrCreateLocalProvider(dep.token, eager))) {
foundLocal = true;
}
}
var result: CompileDiDependencyMetadata = dep;
if (dep.isSelf && !foundLocal) {
if (dep.isOptional) {
result = new CompileDiDependencyMetadata({isValue: true, value: null});
} else {
this._errors.push(
new ProviderError(`No provider for ${dep.token.name}`, requestorSourceSpan));
}
}
return result;
}
}
function _transformProvider( function _transformProvider(
provider: CompileProviderMetadata, provider: CompileProviderMetadata,
{useExisting, useValue, deps}: {useExisting, useValue, deps}:

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Compiler, ComponentFactory, ComponentResolver, Injectable} from '@angular/core'; import {AppModuleFactory, AppModuleMetadata, Compiler, ComponentFactory, ComponentResolver, Injectable} from '@angular/core';
import {BaseException} from '../src/facade/exceptions'; import {BaseException} from '../src/facade/exceptions';
import {ConcreteType, IS_DART, Type, isBlank, isString, stringify} from '../src/facade/lang'; import {ConcreteType, IS_DART, Type, isBlank, isString, stringify} from '../src/facade/lang';
@ -17,14 +17,15 @@ import {createHostComponentMeta, CompileDirectiveMetadata, CompilePipeMetadata,
import {TemplateAst,} from './template_ast'; import {TemplateAst,} from './template_ast';
import {StyleCompiler, StylesCompileDependency, CompiledStylesheet} from './style_compiler'; import {StyleCompiler, StylesCompileDependency, CompiledStylesheet} from './style_compiler';
import {ViewCompiler, ViewCompileResult, ViewFactoryDependency, ComponentFactoryDependency} from './view_compiler/view_compiler'; import {ViewCompiler, ViewCompileResult, ViewFactoryDependency, ComponentFactoryDependency} from './view_compiler/view_compiler';
import {AppModuleCompiler} from './app_module_compiler';
import {TemplateParser} from './template_parser'; import {TemplateParser} from './template_parser';
import {DirectiveNormalizer, NormalizeDirectiveResult} from './directive_normalizer'; import {DirectiveNormalizer} from './directive_normalizer';
import {CompileMetadataResolver} from './metadata_resolver'; import {CompileMetadataResolver} from './metadata_resolver';
import {CompilerConfig} from './config'; import {CompilerConfig} from './config';
import * as ir from './output/output_ast'; import * as ir from './output/output_ast';
import {jitStatements} from './output/output_jit'; import {jitStatements} from './output/output_jit';
import {interpretStatements} from './output/output_interpreter'; import {interpretStatements} from './output/output_interpreter';
import {InterpretiveAppViewInstanceFactory} from './output/interpretive_view'; import {SyncAsyncResult} from './util';
/** /**
* An internal module of the Angular compiler that begins with component types, * An internal module of the Angular compiler that begins with component types,
@ -37,14 +38,15 @@ import {InterpretiveAppViewInstanceFactory} from './output/interpretive_view';
*/ */
@Injectable() @Injectable()
export class RuntimeCompiler implements ComponentResolver, Compiler { export class RuntimeCompiler implements ComponentResolver, Compiler {
private _compiledTemplateCache = new Map<any, CompiledTemplate>(); private _compiledTemplateCache = new Map<Type, CompiledTemplate>();
private _compiledHostTemplateCache = new Map<any, CompiledTemplate>(); private _compiledHostTemplateCache = new Map<Type, CompiledTemplate>();
private _compiledAppModuleCache = new Map<Type, AppModuleFactory<any>>();
constructor( constructor(
private _metadataResolver: CompileMetadataResolver, private _metadataResolver: CompileMetadataResolver,
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _genConfig: CompilerConfig) {} private _appModuleCompiler: AppModuleCompiler, private _genConfig: CompilerConfig) {}
resolveComponent(component: Type|string): Promise<ComponentFactory<any>> { resolveComponent(component: Type|string): Promise<ComponentFactory<any>> {
if (isString(component)) { if (isString(component)) {
@ -54,39 +56,101 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
return this.compileComponentAsync(<ConcreteType<any>>component); return this.compileComponentAsync(<ConcreteType<any>>component);
} }
compileComponentAsync<T>(compType: ConcreteType<T>): Promise<ComponentFactory<T>> { compileAppModuleSync<T>(moduleType: ConcreteType<T>, metadata: AppModuleMetadata = null):
var templates = this._getTransitiveCompiledTemplates(compType, true); AppModuleFactory<T> {
return this._compileAppModule(moduleType, true, metadata).syncResult;
}
compileAppModuleAsync<T>(moduleType: ConcreteType<T>, metadata: AppModuleMetadata = null):
Promise<AppModuleFactory<T>> {
return this._compileAppModule(moduleType, false, metadata).asyncResult;
}
private _compileAppModule<T>(
moduleType: ConcreteType<T>, isSync: boolean,
metadata: AppModuleMetadata = null): SyncAsyncResult<AppModuleFactory<T>> {
// 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<any>[] = [];
if (!appModuleFactory || !useCache) {
var compileModuleMeta = this._metadataResolver.getAppModuleMetadata(moduleType, metadata);
var compileResult = this._appModuleCompiler.compile(compileModuleMeta);
compileResult.dependencies.forEach((dep) => {
let compileResult = this._compileComponent(
dep.comp.runtime, isSync,
compileModuleMeta.directives.map(compileType => <any>compileType.runtime),
compileModuleMeta.pipes.map(compileType => <any>compileType.runtime));
dep.placeholder.runtime = compileResult.syncResult;
componentCompilePromises.push(compileResult.asyncResult);
dep.placeholder.name = `compFactory_${dep.comp.name}`;
});
if (IS_DART || !this._genConfig.useJit) {
appModuleFactory =
interpretStatements(compileResult.statements, compileResult.appModuleFactoryVar);
} else {
appModuleFactory = jitStatements(
`${compileModuleMeta.type.name}.ngfactory.js`, compileResult.statements,
compileResult.appModuleFactoryVar);
}
if (useCache) {
this._compiledAppModuleCache.set(moduleType, appModuleFactory);
}
}
return new SyncAsyncResult(
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;
}
compileComponentSync<T>(compType: ConcreteType<T>, {moduleDirectives = [], modulePipes = []}: {
moduleDirectives?: ConcreteType<any>[],
modulePipes?: ConcreteType<any>[]
} = {}): ComponentFactory<T> {
return this._compileComponent(compType, true, moduleDirectives, modulePipes).syncResult;
}
private _compileComponent<T>(
compType: ConcreteType<T>, isSync: boolean, moduleDirectives: ConcreteType<any>[],
modulePipes: ConcreteType<any>[]): SyncAsyncResult<ComponentFactory<T>> {
var templates =
this._getTransitiveCompiledTemplates(compType, true, moduleDirectives, modulePipes);
var loadingPromises: Promise<any>[] = []; var loadingPromises: Promise<any>[] = [];
templates.forEach((template) => { templates.forEach((template) => {
if (template.loading) { if (template.loading) {
loadingPromises.push(template.loading); if (isSync) {
throw new BaseException(
`Can't compile synchronously as ${template.compType.name} is still being loaded!`);
} else {
loadingPromises.push(template.loading);
}
} }
}); });
return Promise.all(loadingPromises).then(() => { let compile = () => { templates.forEach((template) => { this._compileTemplate(template); }); };
templates.forEach((template) => { this._compileTemplate(template); }); if (isSync) {
return this._getCompiledHostTemplate(compType).proxyComponentFactory; compile();
}); }
let result = this._compiledHostTemplateCache.get(compType).proxyComponentFactory;
return new SyncAsyncResult(result, Promise.all(loadingPromises).then(() => {
compile();
return result;
}));
} }
compileComponentSync<T>(compType: ConcreteType<T>): ComponentFactory<T> { clearCacheFor(type: Type) {
var templates = this._getTransitiveCompiledTemplates(compType, true); this._compiledAppModuleCache.delete(type);
templates.forEach((template) => { this._metadataResolver.clearCacheFor(type);
if (template.loading) { this._compiledHostTemplateCache.delete(type);
throw new BaseException( var compiledTemplate = this._compiledTemplateCache.get(type);
`Can't compile synchronously as ${template.compType.name} is still being loaded!`);
}
});
templates.forEach((template) => { this._compileTemplate(template); });
return this._getCompiledHostTemplate(compType).proxyComponentFactory;
}
clearCacheFor(compType: Type) {
this._metadataResolver.clearCacheFor(compType);
this._compiledHostTemplateCache.delete(compType);
var compiledTemplate = this._compiledTemplateCache.get(compType);
if (compiledTemplate) { if (compiledTemplate) {
this._templateNormalizer.clearCacheFor(compiledTemplate.normalizedCompMeta); this._templateNormalizer.clearCacheFor(compiledTemplate.normalizedCompMeta);
this._compiledTemplateCache.delete(compType); this._compiledTemplateCache.delete(type);
} }
} }
@ -95,9 +159,10 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
this._compiledTemplateCache.clear(); this._compiledTemplateCache.clear();
this._compiledHostTemplateCache.clear(); this._compiledHostTemplateCache.clear();
this._templateNormalizer.clearCache(); this._templateNormalizer.clearCache();
this._compiledAppModuleCache.clear();
} }
private _getCompiledHostTemplate(type: Type): CompiledTemplate { private _createCompiledHostTemplate(type: Type): CompiledTemplate {
var compiledTemplate = this._compiledHostTemplateCache.get(type); var compiledTemplate = this._compiledHostTemplateCache.get(type);
if (isBlank(compiledTemplate)) { if (isBlank(compiledTemplate)) {
var compMeta = this._metadataResolver.getDirectiveMetadata(type); var compMeta = this._metadataResolver.getDirectiveMetadata(type);
@ -111,12 +176,16 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
return compiledTemplate; return compiledTemplate;
} }
private _getCompiledTemplate(type: Type): CompiledTemplate { private _createCompiledTemplate(
type: Type, moduleDirectives: ConcreteType<any>[],
modulePipes: ConcreteType<any>[]): CompiledTemplate {
var compiledTemplate = this._compiledTemplateCache.get(type); var compiledTemplate = this._compiledTemplateCache.get(type);
if (isBlank(compiledTemplate)) { if (isBlank(compiledTemplate)) {
var compMeta = this._metadataResolver.getDirectiveMetadata(type); var compMeta = this._metadataResolver.getDirectiveMetadata(type);
assertComponent(compMeta); assertComponent(compMeta);
var viewDirectives: CompileDirectiveMetadata[] = []; var viewDirectives: CompileDirectiveMetadata[] = [];
moduleDirectives.forEach(
(type) => viewDirectives.push(this._metadataResolver.getDirectiveMetadata(type)));
var viewComponentTypes: Type[] = []; var viewComponentTypes: Type[] = [];
this._metadataResolver.getViewDirectivesMetadata(type).forEach(dirOrComp => { this._metadataResolver.getViewDirectivesMetadata(type).forEach(dirOrComp => {
if (dirOrComp.isComponent) { if (dirOrComp.isComponent) {
@ -126,7 +195,10 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
} }
}); });
var precompileComponentTypes = compMeta.precompile.map((typeMeta) => typeMeta.runtime); var precompileComponentTypes = compMeta.precompile.map((typeMeta) => typeMeta.runtime);
var pipes = this._metadataResolver.getViewPipesMetadata(type); var pipes = [
...modulePipes.map((type) => this._metadataResolver.getPipeMetadata(type)),
...this._metadataResolver.getViewPipesMetadata(type)
];
compiledTemplate = new CompiledTemplate( compiledTemplate = new CompiledTemplate(
false, compMeta.selector, compMeta.type, viewDirectives, viewComponentTypes, false, compMeta.selector, compMeta.type, viewDirectives, viewComponentTypes,
precompileComponentTypes, pipes, this._templateNormalizer.normalizeDirective(compMeta)); precompileComponentTypes, pipes, this._templateNormalizer.normalizeDirective(compMeta));
@ -136,16 +208,20 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
} }
private _getTransitiveCompiledTemplates( private _getTransitiveCompiledTemplates(
compType: Type, isHost: boolean, compType: Type, isHost: boolean, moduleDirectives: ConcreteType<any>[],
modulePipes: ConcreteType<any>[],
target: Set<CompiledTemplate> = new Set<CompiledTemplate>()): Set<CompiledTemplate> { target: Set<CompiledTemplate> = new Set<CompiledTemplate>()): Set<CompiledTemplate> {
var template = var template = isHost ? this._createCompiledHostTemplate(compType) :
isHost ? this._getCompiledHostTemplate(compType) : this._getCompiledTemplate(compType); this._createCompiledTemplate(compType, moduleDirectives, modulePipes);
if (!target.has(template)) { if (!target.has(template)) {
target.add(template); target.add(template);
template.viewComponentTypes.forEach( template.viewComponentTypes.forEach((compType) => {
(compType) => { this._getTransitiveCompiledTemplates(compType, false, target); }); this._getTransitiveCompiledTemplates(
template.precompileHostComponentTypes.forEach( compType, false, moduleDirectives, modulePipes, target);
(compType) => { this._getTransitiveCompiledTemplates(compType, true, target); }); });
template.precompileHostComponentTypes.forEach((compType) => {
this._getTransitiveCompiledTemplates(compType, true, moduleDirectives, modulePipes, target);
});
} }
return target; return target;
} }
@ -162,7 +238,7 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
this._resolveStylesCompileResult( this._resolveStylesCompileResult(
stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl); stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl);
var viewCompMetas = template.viewComponentTypes.map( var viewCompMetas = template.viewComponentTypes.map(
(compType) => this._getCompiledTemplate(compType).normalizedCompMeta); (compType) => this._compiledTemplateCache.get(compType).normalizedCompMeta);
var parsedTemplate = this._templateParser.parse( var parsedTemplate = this._templateParser.parse(
compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas), compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas),
template.viewPipes, compMeta.type.name); template.viewPipes, compMeta.type.name);
@ -173,12 +249,12 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
let depTemplate: CompiledTemplate; let depTemplate: CompiledTemplate;
if (dep instanceof ViewFactoryDependency) { if (dep instanceof ViewFactoryDependency) {
let vfd = <ViewFactoryDependency>dep; let vfd = <ViewFactoryDependency>dep;
depTemplate = this._getCompiledTemplate(vfd.comp.runtime); depTemplate = this._compiledTemplateCache.get(vfd.comp.runtime);
vfd.placeholder.runtime = depTemplate.proxyViewFactory; vfd.placeholder.runtime = depTemplate.proxyViewFactory;
vfd.placeholder.name = `viewFactory_${vfd.comp.name}`; vfd.placeholder.name = `viewFactory_${vfd.comp.name}`;
} else if (dep instanceof ComponentFactoryDependency) { } else if (dep instanceof ComponentFactoryDependency) {
let cfd = <ComponentFactoryDependency>dep; let cfd = <ComponentFactoryDependency>dep;
depTemplate = this._getCompiledHostTemplate(cfd.comp.runtime); depTemplate = this._compiledHostTemplateCache.get(cfd.comp.runtime);
cfd.placeholder.runtime = depTemplate.proxyComponentFactory; cfd.placeholder.runtime = depTemplate.proxyComponentFactory;
cfd.placeholder.name = `compFactory_${cfd.comp.name}`; cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
} }
@ -188,11 +264,10 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
stylesCompileResult.componentStylesheet.statements.concat(compileResult.statements); stylesCompileResult.componentStylesheet.statements.concat(compileResult.statements);
var factory: any; var factory: any;
if (IS_DART || !this._genConfig.useJit) { if (IS_DART || !this._genConfig.useJit) {
factory = interpretStatements( factory = interpretStatements(statements, compileResult.viewFactoryVar);
statements, compileResult.viewFactoryVar, new InterpretiveAppViewInstanceFactory());
} else { } else {
factory = jitStatements( factory = jitStatements(
`${template.compType.name}.template.js`, statements, compileResult.viewFactoryVar); `${template.compType.name}.ngfactory.js`, statements, compileResult.viewFactoryVar);
} }
template.compiled(factory); template.compiled(factory);
} }
@ -213,8 +288,7 @@ export class RuntimeCompiler implements ComponentResolver, Compiler {
externalStylesheetsByModuleUrl: Map<string, CompiledStylesheet>): string[] { externalStylesheetsByModuleUrl: Map<string, CompiledStylesheet>): string[] {
this._resolveStylesCompileResult(result, externalStylesheetsByModuleUrl); this._resolveStylesCompileResult(result, externalStylesheetsByModuleUrl);
if (IS_DART || !this._genConfig.useJit) { if (IS_DART || !this._genConfig.useJit) {
return interpretStatements( return interpretStatements(result.statements, result.stylesVar);
result.statements, result.stylesVar, new InterpretiveAppViewInstanceFactory());
} else { } else {
return jitStatements(`${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar); return jitStatements(`${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar);
} }
@ -234,7 +308,7 @@ class CompiledTemplate {
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata, public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
public viewDirectives: CompileDirectiveMetadata[], public viewComponentTypes: Type[], public viewDirectives: CompileDirectiveMetadata[], public viewComponentTypes: Type[],
public precompileHostComponentTypes: Type[], public viewPipes: CompilePipeMetadata[], public precompileHostComponentTypes: Type[], public viewPipes: CompilePipeMetadata[],
private _normalizeResult: NormalizeDirectiveResult) { _normalizeResult: SyncAsyncResult<CompileDirectiveMetadata>) {
this.proxyViewFactory = (...args: any[]) => this._viewFactory.apply(null, args); this.proxyViewFactory = (...args: any[]) => this._viewFactory.apply(null, args);
this.proxyComponentFactory = isHost ? this.proxyComponentFactory = isHost ?
new ComponentFactory<any>(selector, this.proxyViewFactory, compType.runtime) : new ComponentFactory<any>(selector, this.proxyViewFactory, compType.runtime) :

View File

@ -6,8 +6,10 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompileTokenMetadata} from './compile_metadata';
import {StringMapWrapper} from './facade/collection'; import {StringMapWrapper} from './facade/collection';
import {IS_DART, StringWrapper, isArray, isBlank, isPrimitive, isStrictStringMap} from './facade/lang'; import {IS_DART, StringWrapper, isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
import * as o from './output/output_ast';
export var MODULE_SUFFIX = IS_DART ? '.dart' : ''; export var MODULE_SUFFIX = IS_DART ? '.dart' : '';
@ -80,3 +82,22 @@ export function assetUrl(pkg: string, path: string = null, type: string = 'src')
} }
} }
} }
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
if (isPresent(token.value)) {
return o.literal(token.value);
} else if (token.identifierIsInstance) {
return o.importExpr(token.identifier)
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
} else {
return o.importExpr(token.identifier);
}
}
export class SyncAsyncResult<T> {
constructor(public syncResult: T, public asyncResult: Promise<T> = null) {
if (!asyncResult) {
asyncResult = Promise.resolve(syncResult);
}
}
}

View File

@ -18,10 +18,10 @@ import {CompileView} from './compile_view';
import {InjectMethodVars} from './constants'; import {InjectMethodVars} from './constants';
import {CompileTokenMap, CompileDirectiveMetadata, CompileTokenMetadata, CompileQueryMetadata, CompileProviderMetadata, CompileDiDependencyMetadata, CompileIdentifierMetadata,} from '../compile_metadata'; import {CompileTokenMap, CompileDirectiveMetadata, CompileTokenMetadata, CompileQueryMetadata, CompileProviderMetadata, CompileDiDependencyMetadata, CompileIdentifierMetadata,} from '../compile_metadata';
import {getPropertyInView, createDiTokenExpression, injectFromViewParentInjector} from './util'; import {getPropertyInView, injectFromViewParentInjector} from './util';
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query'; import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
import {CompileMethod} from './compile_method'; import {CompileMethod} from './compile_method';
import {ValueTransformer, visitValue} from '../util'; import {createDiTokenExpression} from '../util';
export class CompileNode { export class CompileNode {
constructor( constructor(
@ -165,7 +165,7 @@ export class CompileElement extends CompileNode {
return o.importExpr(provider.useClass) return o.importExpr(provider.useClass)
.instantiate(depsExpr, o.importType(provider.useClass)); .instantiate(depsExpr, o.importType(provider.useClass));
} else { } else {
return _convertValueToOutputAst(provider.useValue); return o.literal(provider.useValue);
} }
}); });
var propName = `_${resolvedProvider.token.name}_${this.nodeIndex}_${this._instances.size}`; var propName = `_${resolvedProvider.token.name}_${this.nodeIndex}_${this._instances.size}`;
@ -432,30 +432,3 @@ class _QueryWithRead {
this.read = isPresent(query.meta.read) ? query.meta.read : match; this.read = isPresent(query.meta.read) ? query.meta.read : match;
} }
} }
function _convertValueToOutputAst(value: any): o.Expression {
return visitValue(value, new _ValueOutputAstTransformer(), null);
}
class _ValueOutputAstTransformer extends ValueTransformer {
visitArray(arr: any[], context: any): o.Expression {
return o.literalArr(arr.map(value => visitValue(value, this, context)));
}
visitStringMap(map: {[key: string]: any}, context: any): o.Expression {
var entries: Array<string|o.Expression>[] = [];
StringMapWrapper.forEach(map, (value: any, key: string) => {
entries.push([key, visitValue(value, this, context)]);
});
return o.literalMap(entries);
}
visitPrimitive(value: any, context: any): o.Expression { return o.literal(value); }
visitOther(value: any, context: any): o.Expression {
if (value instanceof CompileIdentifierMetadata) {
return o.importExpr(value);
} else if (value instanceof o.Expression) {
return value;
} else {
throw new BaseException(`Illegal state: Don't now how to compile value ${value}`);
}
}
}

View File

@ -14,6 +14,7 @@ import {ListWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {Identifiers} from '../identifiers'; import {Identifiers} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {createDiTokenExpression} from '../util';
import {CompileBinding} from './compile_binding'; import {CompileBinding} from './compile_binding';
import {CompileElement, CompileNode} from './compile_element'; import {CompileElement, CompileNode} from './compile_element';
@ -22,7 +23,7 @@ import {CompilePipe} from './compile_pipe';
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query'; import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
import {EventHandlerVars} from './constants'; import {EventHandlerVars} from './constants';
import {NameResolver} from './expression_converter'; import {NameResolver} from './expression_converter';
import {createDiTokenExpression, createPureProxy, getPropertyInView, getViewFactoryName, injectFromViewParentInjector} from './util'; import {createPureProxy, getPropertyInView, getViewFactoryName, injectFromViewParentInjector} from './util';
export class CompileView implements NameResolver { export class CompileView implements NameResolver {
public viewType: ViewType; public viewType: ViewType;

View File

@ -13,6 +13,7 @@ import * as o from '../output/output_ast';
import {CompileTokenMetadata, CompileDirectiveMetadata,} from '../compile_metadata'; import {CompileTokenMetadata, CompileDirectiveMetadata,} from '../compile_metadata';
import {CompileView} from './compile_view'; import {CompileView} from './compile_view';
import {Identifiers} from '../identifiers'; import {Identifiers} from '../identifiers';
import {createDiTokenExpression} from '../util';
export function getPropertyInView( export function getPropertyInView(
property: o.Expression, callingView: CompileView, definedView: CompileView): o.Expression { property: o.Expression, callingView: CompileView, definedView: CompileView): o.Expression {
@ -55,18 +56,6 @@ export function getViewFactoryName(
return `viewFactory_${component.type.name}${embeddedTemplateIndex}`; return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
} }
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
if (isPresent(token.value)) {
return o.literal(token.value);
} else if (token.identifierIsInstance) {
return o.importExpr(token.identifier)
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
} else {
return o.importExpr(token.identifier);
}
}
export function createFlatArray(expressions: o.Expression[]): o.Expression { export function createFlatArray(expressions: o.Expression[]): o.Expression {
var lastNonArrayExpressions: any[] /** TODO #9100 */ = []; var lastNonArrayExpressions: any[] /** TODO #9100 */ = [];
var result: o.Expression = o.literalArr([]); var result: o.Expression = o.literalArr([]);

View File

@ -16,11 +16,12 @@ import {StringWrapper, isPresent} from '../facade/lang';
import {Identifiers, identifierToken} from '../identifiers'; import {Identifiers, identifierToken} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ProviderAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_ast'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ProviderAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_ast';
import {createDiTokenExpression} from '../util';
import {CompileElement, CompileNode} from './compile_element'; import {CompileElement, CompileNode} from './compile_element';
import {CompileView} from './compile_view'; import {CompileView} from './compile_view';
import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants'; import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants';
import {createDiTokenExpression, createFlatArray, getViewFactoryName} from './util'; import {createFlatArray, getViewFactoryName} from './util';
const IMPLICIT_TEMPLATE_VAR = '\$implicit'; const IMPLICIT_TEMPLATE_VAR = '\$implicit';
const CLASS_ATTR = 'class'; const CLASS_ATTR = 'class';

View File

@ -1,35 +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
*/
// ATTENTION: This file will be overwritten with generated code by main()
import {DartEmitter} from '@angular/compiler/src/output/dart_emitter';
import {DartImportGenerator} from '@angular/compiler/src/output/dart_imports';
import * as o from '@angular/compiler/src/output/output_ast';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {ComponentFactory} from '@angular/core/src/linker/component_factory';
import {IS_DART, print} from '../src/facade/lang';
import {compAMetadata, compileComp} from './offline_compiler_util';
import {CompA, SimpleJsImportGenerator} from './offline_compiler_util';
export const CompANgFactory: ComponentFactory<CompA> = null;
export function emit(): Promise<string> {
var emitter = IS_DART ? new DartEmitter(new DartImportGenerator()) :
new TypeScriptEmitter(new SimpleJsImportGenerator());
return compileComp(emitter, compAMetadata);
}
// Generator
export function main(args: string[]) {
emit().then((source) => {
// debug: console.error(source);
print(source);
});
}

View File

@ -1,31 +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
*/
// ATTENTION: This file will be overwritten with generated code by main()
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import {ComponentFactory} from '@angular/core/src/linker/component_factory';
import {print} from '../src/facade/lang';
import {compAMetadata, compileComp} from './offline_compiler_util';
import {CompA, SimpleJsImportGenerator} from './offline_compiler_util';
export const CompANgFactory: ComponentFactory<CompA> = null;
export function emit() {
var emitter = new JavaScriptEmitter(new SimpleJsImportGenerator());
return compileComp(emitter, compAMetadata);
}
// Generator
export function main(args: string[]) {
emit().then((source) => {
// debug: console.error(source);
print(source);
});
}

View File

@ -1,10 +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
*/
export const styles = /*@ts2dart_const*/[`.greenStyle[_ngcontent-a-1] { color: green; }`];

View File

@ -1,65 +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 {ddescribe, describe, xdescribe, it, iit, xit, expect, beforeEach, afterEach, inject, beforeEachProviders,} from '@angular/core/testing/testing_internal';
import {IS_DART} from '../src/facade/lang';
import {Injector} from '@angular/core';
import {ComponentFactory} from '@angular/core/src/linker/component_factory';
import * as typed from './offline_compiler_codegen_typed';
import * as untyped from './offline_compiler_codegen_untyped';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {SharedStylesHost} from '@angular/platform-browser/src/dom/shared_styles_host';
import {CompA} from './offline_compiler_util';
export function main() {
var typedComponentFactory = typed.CompANgFactory;
var untypedComponentFactory = untyped.CompANgFactory;
var fixtures: TestFixture[] = [];
if (IS_DART || !getDOM().supportsDOMEvents()) {
// Our generator only works on node.js and Dart...
fixtures.push(new TestFixture(typedComponentFactory, 'typed'));
}
if (!IS_DART) {
// Our generator only works on node.js and Dart...
if (!getDOM().supportsDOMEvents()) {
fixtures.push(new TestFixture(untypedComponentFactory, 'untyped'));
}
}
describe('OfflineCompiler', () => {
var injector: Injector;
var sharedStylesHost: SharedStylesHost;
beforeEach(inject(
[Injector, SharedStylesHost],
(_injector: Injector, _sharedStylesHost: SharedStylesHost) => {
injector = _injector;
sharedStylesHost = _sharedStylesHost;
}));
fixtures.forEach((fixture) => {
describe(`${fixture.name}`, () => {
it('should compile components', () => {
var hostEl = fixture.compFactory.create(injector);
expect(hostEl.instance).toBeAnInstanceOf(CompA);
var styles = sharedStylesHost.getAllStyles();
expect(styles[0]).toContain('.redStyle[_ngcontent');
expect(styles[1]).toContain('.greenStyle[_ngcontent');
});
});
});
});
}
class TestFixture {
constructor(public compFactory: ComponentFactory<CompA>, public name: string) {}
}

View File

@ -1,94 +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 {CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata';
import {CompilerConfig} from '@angular/compiler/src/config';
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
import {Parser} from '@angular/compiler/src/expression_parser/parser';
import {HtmlParser} from '@angular/compiler/src/html_parser';
import {NormalizedComponentWithViewDirectives, OfflineCompiler, SourceModule} from '@angular/compiler/src/offline_compiler';
import {OutputEmitter} from '@angular/compiler/src/output/abstract_emitter';
import {ImportGenerator} from '@angular/compiler/src/output/path_util';
import {StyleCompiler} from '@angular/compiler/src/style_compiler';
import {TemplateParser} from '@angular/compiler/src/template_parser';
import {createOfflineCompileUrlResolver} from '@angular/compiler/src/url_resolver';
import {MODULE_SUFFIX} from '@angular/compiler/src/util';
import {ViewCompiler} from '@angular/compiler/src/view_compiler/view_compiler';
import {XHR} from '@angular/compiler/src/xhr';
import {Console} from '../core_private';
import {IS_DART, isPresent, print} from '../src/facade/lang';
import {MockSchemaRegistry} from '../testing/schema_registry_mock';
export class CompA { user: string; }
var THIS_MODULE_PATH = `asset:@angular/lib/compiler/test`;
var THIS_MODULE_URL = `${THIS_MODULE_PATH}/offline_compiler_util${MODULE_SUFFIX}`;
export var compAMetadata = CompileDirectiveMetadata.create({
isComponent: true,
selector: 'comp-a',
type: new CompileTypeMetadata(
{name: 'CompA', moduleUrl: THIS_MODULE_URL, runtime: CompA, diDeps: []}),
template: new CompileTemplateMetadata({
templateUrl: './offline_compiler_compa.html',
styles: ['.redStyle { color: red; }'],
styleUrls: ['./offline_compiler_compa.css']
})
});
function _createOfflineCompiler(xhr: XHR, emitter: OutputEmitter): OfflineCompiler {
var urlResolver = createOfflineCompileUrlResolver();
var htmlParser = new HtmlParser();
var config = new CompilerConfig({genDebugInfo: true, useJit: true});
var normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
return new OfflineCompiler(
normalizer,
new TemplateParser(
new Parser(new Lexer()), new MockSchemaRegistry({}, {}), htmlParser, new Console(), []),
new StyleCompiler(urlResolver), new ViewCompiler(config), emitter);
}
export function compileComp(
emitter: OutputEmitter, comp: CompileDirectiveMetadata): Promise<string> {
var xhr = new MockXhr();
xhr.setResult(`${THIS_MODULE_PATH}/offline_compiler_compa.html`, '');
xhr.setResult(`${THIS_MODULE_PATH}/offline_compiler_compa.css`, '');
var compiler = _createOfflineCompiler(xhr, emitter);
var result = compiler.normalizeDirectiveMetadata(comp).then((normComp) => {
return compiler
.compileTemplates([new NormalizedComponentWithViewDirectives(normComp, [], [])])[0]
.source;
});
return result;
}
export class SimpleJsImportGenerator implements ImportGenerator {
getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
var importedAssetUrl = ImportGenerator.parseAssetUrl(importedUrlStr);
if (isPresent(importedAssetUrl)) {
return `${importedAssetUrl.packageName}/${importedAssetUrl.modulePath}`;
} else {
return importedUrlStr;
}
}
}
class MockXhr implements XHR {
results: {[key: string]: string} = {};
setResult(url: string, content: string) { this.results[url] = content; }
get(url: string): Promise<string> {
if (url in this.results) {
return Promise.resolve(this.results[url]);
}
return Promise.reject<any>(`Unknown url ${url}`);
}
}

View File

@ -12,7 +12,7 @@ import {isBlank} from '../../src/facade/lang';
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter'; import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
import {SimpleJsImportGenerator} from '../offline_compiler_util'; import {SimpleJsImportGenerator} from './output_emitter_util';
var someModuleUrl = 'asset:somePackage/lib/somePath'; var someModuleUrl = 'asset:somePackage/lib/somePath';
var anotherModuleUrl = 'asset:somePackage/lib/someOtherPath'; var anotherModuleUrl = 'asset:somePackage/lib/someOtherPath';

View File

@ -15,9 +15,8 @@ import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {unimplemented} from '../../src/facade/exceptions'; import {unimplemented} from '../../src/facade/exceptions';
import {IS_DART, print} from '../../src/facade/lang'; import {IS_DART, print} from '../../src/facade/lang';
import {assetUrl} from '../../src/util'; import {assetUrl} from '../../src/util';
import {SimpleJsImportGenerator} from '../offline_compiler_util';
import {codegenExportsVars, codegenStmts} from './output_emitter_util'; import {SimpleJsImportGenerator, codegenExportsVars, codegenStmts} from './output_emitter_util';
export function getExpressions(): any { export function getExpressions(): any {
return unimplemented(); return unimplemented();

View File

@ -12,9 +12,8 @@ import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import {unimplemented} from '../../src/facade/exceptions'; import {unimplemented} from '../../src/facade/exceptions';
import {print} from '../../src/facade/lang'; import {print} from '../../src/facade/lang';
import {assetUrl} from '../../src/util'; import {assetUrl} from '../../src/util';
import {SimpleJsImportGenerator} from '../offline_compiler_util';
import {codegenExportsVars, codegenStmts} from './output_emitter_util'; import {SimpleJsImportGenerator, codegenExportsVars, codegenStmts} from './output_emitter_util';
export function getExpressions(): any { export function getExpressions(): any {
return unimplemented(); return unimplemented();

View File

@ -14,7 +14,7 @@ import * as typed from './output_emitter_codegen_typed';
import * as untyped from './output_emitter_codegen_untyped'; import * as untyped from './output_emitter_codegen_untyped';
import {jitStatements} from '@angular/compiler/src/output/output_jit'; import {jitStatements} from '@angular/compiler/src/output/output_jit';
import {interpretStatements} from '@angular/compiler/src/output/output_interpreter'; import {interpretStatements} from '@angular/compiler/src/output/output_interpreter';
import {codegenStmts, ExternalClass, DynamicClassInstanceFactory} from './output_emitter_util'; import {codegenStmts, ExternalClass} from './output_emitter_util';
import {EventEmitter} from '@angular/core'; import {EventEmitter} from '@angular/core';
import {ViewType} from '@angular/core/src/linker/view_type'; import {ViewType} from '@angular/core/src/linker/view_type';
import {BaseException} from '@angular/core'; import {BaseException} from '@angular/core';
@ -23,8 +23,7 @@ import {browserDetection} from '@angular/platform-browser/testing/browser_util'
export function main() { export function main() {
var outputDefs: any[] /** TODO #9100 */ = []; outputDefs.push({ var outputDefs: any[] /** TODO #9100 */ = []; outputDefs.push({
'getExpressions': () => interpretStatements( 'getExpressions': () => interpretStatements(codegenStmts, 'getExpressions'),
codegenStmts, 'getExpressions', new DynamicClassInstanceFactory()),
'name': 'interpreted' 'name': 'interpreted'
}); });
if (IS_DART || !getDOM().supportsDOMEvents()) { if (IS_DART || !getDOM().supportsDOMEvents()) {

View File

@ -8,7 +8,7 @@
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
import {DynamicInstance, InstanceFactory} from '@angular/compiler/src/output/output_interpreter'; import {ImportGenerator} from '@angular/compiler/src/output/path_util';
import {assetUrl} from '@angular/compiler/src/util'; import {assetUrl} from '@angular/compiler/src/util';
import {EventEmitter} from '@angular/core'; import {EventEmitter} from '@angular/core';
import {ViewType} from '@angular/core/src/linker/view_type'; import {ViewType} from '@angular/core/src/linker/view_type';
@ -253,22 +253,13 @@ function createOperatorFn(op: o.BinaryOperator) {
o.DYNAMIC_TYPE); o.DYNAMIC_TYPE);
} }
export class DynamicClassInstanceFactory implements InstanceFactory { export class SimpleJsImportGenerator implements ImportGenerator {
createInstance( getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
superClass: any, clazz: any, args: any[], props: Map<string, any>, var importedAssetUrl = ImportGenerator.parseAssetUrl(importedUrlStr);
getters: Map<string, Function>, methods: Map<string, Function>): any { if (importedAssetUrl) {
if (superClass === ExternalClass) { return `${importedAssetUrl.packageName}/${importedAssetUrl.modulePath}`;
return new _InterpretiveDynamicClass(args, clazz, props, getters, methods); } else {
return importedUrlStr;
} }
throw new BaseException(`Can't instantiate class ${superClass} in interpretative mode`);
} }
} }
class _InterpretiveDynamicClass extends ExternalClass implements DynamicInstance {
constructor(
args: any[], public clazz: any, public props: Map<string, any>,
public getters: Map<string, Function>, public methods: Map<string, Function>) {
super(args[0]);
}
childMethod(a: any /** TODO #9100 */) { return this.methods.get('childMethod')(a); }
}

View File

@ -12,7 +12,7 @@ import {isBlank} from '../../src/facade/lang';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter'; import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
import {SimpleJsImportGenerator} from '../offline_compiler_util'; import {SimpleJsImportGenerator} from './output_emitter_util';
var someModuleUrl = 'asset:somePackage/lib/somePath'; var someModuleUrl = 'asset:somePackage/lib/somePath';
var anotherModuleUrl = 'asset:somePackage/lib/someOtherPath'; var anotherModuleUrl = 'asset:somePackage/lib/someOtherPath';

View File

@ -1,5 +1,5 @@
import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal';
import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector} from '@angular/core'; import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector, AppModule, AppModuleMetadata, AppModuleFactory} from '@angular/core';
import {ConcreteType, stringify} from '../src/facade/lang'; import {ConcreteType, stringify} from '../src/facade/lang';
import {fakeAsync, tick, TestComponentBuilder, ComponentFixture} from '@angular/core/testing'; import {fakeAsync, tick, TestComponentBuilder, ComponentFixture} from '@angular/core/testing';
import {XHR, ViewResolver} from '@angular/compiler'; import {XHR, ViewResolver} from '@angular/compiler';
@ -19,6 +19,10 @@ class SomeComp {
class SomeCompWithUrlTemplate { class SomeCompWithUrlTemplate {
} }
@AppModule({})
class SomeModule {
}
export function main() { export function main() {
describe('RuntimeCompiler', () => { describe('RuntimeCompiler', () => {
let compiler: Compiler; let compiler: Compiler;
@ -104,5 +108,51 @@ export function main() {
expect(compFixture.nativeElement).toHaveText('hello'); expect(compFixture.nativeElement).toHaveText('hello');
})); }));
}); });
describe('compileAppModuleAsync', () => {
it('should allow to use templateUrl components', fakeAsync(() => {
xhr.spy('get').andCallFake(() => Promise.resolve('hello'));
let appModuleFactory: AppModuleFactory<any>;
compiler
.compileAppModuleAsync(
SomeModule, new AppModuleMetadata({precompile: [SomeCompWithUrlTemplate]}))
.then((f) => appModuleFactory = f);
tick();
expect(appModuleFactory.moduleType).toBe(SomeModule);
}));
});
describe('compileAppModuleSync', () => {
it('should throw when using a templateUrl that has not been compiled before', () => {
xhr.spy('get').andCallFake(() => Promise.resolve(''));
expect(
() => compiler.compileAppModuleSync(
SomeModule, new AppModuleMetadata({precompile: [SomeCompWithUrlTemplate]})))
.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',
() => {
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]})))
.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(() => {
xhr.spy('get').andCallFake(() => Promise.resolve('hello'));
tcb.createFakeAsync(SomeCompWithUrlTemplate);
let appModuleFactory = compiler.compileAppModuleSync(
SomeModule, new AppModuleMetadata({precompile: [SomeCompWithUrlTemplate]}));
expect(appModuleFactory).toBeTruthy();
}));
});
}); });
} }

View File

@ -21,6 +21,7 @@ import * as console from './src/console';
import * as debug from './src/debug/debug_renderer'; import * as debug from './src/debug/debug_renderer';
import * as provider_util from './src/di/provider_util'; import * as provider_util from './src/di/provider_util';
import * as reflective_provider from './src/di/reflective_provider'; 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_factory_resolver from './src/linker/component_factory_resolver';
import * as component_resolver from './src/linker/component_resolver'; import * as component_resolver from './src/linker/component_resolver';
import * as debug_context from './src/linker/debug_context'; import * as debug_context from './src/linker/debug_context';
@ -58,6 +59,7 @@ export declare namespace __core_private_types__ {
export type AppElement = element.AppElement; export type AppElement = element.AppElement;
export var AppElement: typeof element.AppElement; export var AppElement: typeof element.AppElement;
export var AppView: typeof view.AppView; export var AppView: typeof view.AppView;
export var AppModuleInjector: typeof app_module_factory.AppModuleInjector;
export type DebugAppView<T> = view.DebugAppView<T>; export type DebugAppView<T> = view.DebugAppView<T>;
export var DebugAppView: typeof view.DebugAppView; export var DebugAppView: typeof view.DebugAppView;
export type ViewType = view_type.ViewType; export type ViewType = view_type.ViewType;
@ -148,6 +150,7 @@ export var __core_private__ = {
AppElement: element.AppElement, AppElement: element.AppElement,
AppView: view.AppView, AppView: view.AppView,
DebugAppView: view.DebugAppView, DebugAppView: view.DebugAppView,
AppModuleInjector: app_module_factory.AppModuleInjector,
ViewType: view_type.ViewType, ViewType: view_type.ViewType,
MAX_INTERPOLATION_VALUES: view_utils.MAX_INTERPOLATION_VALUES, MAX_INTERPOLATION_VALUES: view_utils.MAX_INTERPOLATION_VALUES,
checkBinding: view_utils.checkBinding, checkBinding: view_utils.checkBinding,

View File

@ -6,16 +6,27 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {unimplemented} from '../facade/exceptions'; import {BaseException, unimplemented} from '../facade/exceptions';
import {stringify} from '../facade/lang';
const _THROW_IF_NOT_FOUND = /*@ts2dart_const*/ new Object(); const _THROW_IF_NOT_FOUND = /*@ts2dart_const*/ new Object();
export const THROW_IF_NOT_FOUND = /*@ts2dart_const*/ _THROW_IF_NOT_FOUND; export const THROW_IF_NOT_FOUND = /*@ts2dart_const*/ _THROW_IF_NOT_FOUND;
class _NullInjector implements Injector {
get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
if (notFoundValue === _THROW_IF_NOT_FOUND) {
throw new BaseException(`No provider for ${stringify(token)}!`);
}
return notFoundValue;
}
}
/** /**
* @stable * @stable
*/ */
export abstract class Injector { export abstract class Injector {
static THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND; static THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
static NULL: Injector = new _NullInjector();
/** /**
* Retrieves an instance from the injector based on the provided token. * Retrieves an instance from the injector based on the provided token.

View File

@ -7,6 +7,7 @@
*/ */
// Public API for compiler // Public API for compiler
export {AppModuleFactory, AppModuleRef} from './linker/app_module_factory';
export {Compiler} from './linker/compiler'; export {Compiler} from './linker/compiler';
export {ComponentFactory, ComponentRef} from './linker/component_factory'; export {ComponentFactory, ComponentRef} from './linker/component_factory';
export {ComponentFactoryResolver, NoComponentFactoryError} from './linker/component_factory_resolver'; export {ComponentFactoryResolver, NoComponentFactoryError} from './linker/component_factory_resolver';

View File

@ -0,0 +1,90 @@
/**
* @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 {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
import {unimplemented} from '../facade/exceptions';
import {ConcreteType} from '../facade/lang';
import {ComponentFactory} from './component_factory';
import {CodegenComponentFactoryResolver, ComponentFactoryResolver} from './component_factory_resolver';
/**
* Represents an instance of an AppModule created via a {@link AppModuleFactory}.
*
* `AppModuleRef` provides access to the AppModule Instance as well other objects related to this
* AppModule Instance.
* @stable
*/
export abstract class AppModuleRef<T> {
/**
* The injector that contains all of the providers of the AppModule.
*/
get injector(): Injector { return unimplemented(); }
/**
* The ComponentFactoryResolver to get hold of the ComponentFactories
* delcared in the `precompile` property of the module.
*/
get componentFactoryResolver(): ComponentFactoryResolver { return unimplemented(); }
/**
* The AppModule instance.
*/
get instance(): T { return unimplemented(); }
}
/**
* @stable
*/
export class AppModuleFactory<T> {
constructor(
private _injectorClass: {new (parentInjector: Injector): AppModuleInjector<T>},
private _moduleype: ConcreteType<T>) {}
get moduleType(): ConcreteType<T> { return this._moduleype; }
create(parentInjector: Injector = null): AppModuleRef<T> {
if (!parentInjector) {
parentInjector = Injector.NULL;
}
var instance = new this._injectorClass(parentInjector);
instance.create();
return instance;
}
}
const _UNDEFINED = new Object();
export abstract class AppModuleInjector<T> extends CodegenComponentFactoryResolver implements
Injector,
AppModuleRef<T> {
public instance: T;
constructor(public parent: Injector, factories: ComponentFactory<any>[]) {
super(factories, parent.get(ComponentFactoryResolver, ComponentFactoryResolver.NULL));
}
create() { this.instance = this.createInternal(); }
abstract createInternal(): T;
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
if (token === Injector || token === ComponentFactoryResolver) {
return this;
}
var result = this.getInternal(token, _UNDEFINED);
return result === _UNDEFINED ? this.parent.get(token, notFoundValue) : result;
}
abstract getInternal(token: any, notFoundValue: any): any;
get injector(): Injector { return this; }
get componentFactoryResolver(): ComponentFactoryResolver { return this; }
}

View File

@ -8,7 +8,9 @@
import {BaseException} from '../facade/exceptions'; import {BaseException} from '../facade/exceptions';
import {ConcreteType, Type, stringify} from '../facade/lang'; import {ConcreteType, Type, stringify} from '../facade/lang';
import {AppModuleMetadata} from '../metadata/app_module';
import {AppModuleFactory} from './app_module_factory';
import {ComponentFactory} from './component_factory'; import {ComponentFactory} from './component_factory';
@ -22,7 +24,10 @@ export class Compiler {
/** /**
* Loads the template and styles of a component and returns the associated `ComponentFactory`. * Loads the template and styles of a component and returns the associated `ComponentFactory`.
*/ */
compileComponentAsync<T>(component: ConcreteType<T>): Promise<ComponentFactory<T>> { compileComponentAsync<T>(component: ConcreteType<T>, {moduleDirectives = [], modulePipes = []}: {
moduleDirectives?: ConcreteType<any>[],
modulePipes?: ConcreteType<any>[]
} = {}): Promise<ComponentFactory<T>> {
throw new BaseException( throw new BaseException(
`Runtime compiler is not loaded. Tried to compile ${stringify(component)}`); `Runtime compiler is not loaded. Tried to compile ${stringify(component)}`);
} }
@ -30,16 +35,37 @@ export class Compiler {
* Compiles the given component. All templates have to be either inline or compiled via * Compiles the given component. All templates have to be either inline or compiled via
* `compileComponentAsync` before. * `compileComponentAsync` before.
*/ */
compileComponentSync<T>(component: ConcreteType<T>): ComponentFactory<T> { compileComponentSync<T>(component: ConcreteType<T>, {moduleDirectives = [], modulePipes = []}: {
moduleDirectives?: ConcreteType<any>[],
modulePipes?: ConcreteType<any>[]
} = {}): ComponentFactory<T> {
throw new BaseException( throw new BaseException(
`Runtime compiler is not loaded. Tried to compile ${stringify(component)}`); `Runtime compiler is not loaded. Tried to compile ${stringify(component)}`);
} }
/**
* Compiles the given App Module. All templates of the components listed in `precompile`
* have to be either inline or compiled before via `compileComponentAsync` /
* `compileAppModuleAsync`.
*/
compileAppModuleSync<T>(moduleType: ConcreteType<T>, metadata: AppModuleMetadata = null):
AppModuleFactory<T> {
throw new BaseException(
`Runtime compiler is not loaded. Tried to compile ${stringify(moduleType)}`);
}
compileAppModuleAsync<T>(moduleType: ConcreteType<T>, metadata: AppModuleMetadata = null):
Promise<AppModuleFactory<T>> {
throw new BaseException(
`Runtime compiler is not loaded. Tried to compile ${stringify(moduleType)}`);
}
/** /**
* Clears all caches * Clears all caches
*/ */
clearCache(): void {} clearCache(): void {}
/** /**
* Clears the cache for the given component. * Clears the cache for the given component/appModule.
*/ */
clearCacheFor(compType: Type) {} clearCacheFor(type: Type) {}
} }

View File

@ -14,10 +14,12 @@
import {ChangeDetectionStrategy} from '../src/change_detection/change_detection'; import {ChangeDetectionStrategy} from '../src/change_detection/change_detection';
import {AnimationEntryMetadata} from './animation/metadata'; import {AnimationEntryMetadata} from './animation/metadata';
import {AppModuleMetadata} from './metadata/app_module';
import {AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di'; import {AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di';
import {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata} from './metadata/directives'; import {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata} from './metadata/directives';
import {ViewEncapsulation, ViewMetadata} from './metadata/view'; import {ViewEncapsulation, ViewMetadata} from './metadata/view';
export {AppModuleMetadata} from './metadata/app_module';
export {AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di'; export {AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di';
export {ComponentMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata} from './metadata/directives'; 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 {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy, OnInit} from './metadata/lifecycle_hooks';
@ -83,6 +85,16 @@ export interface ViewDecorator extends TypeDecorator {
}): ViewDecorator; }): ViewDecorator;
} }
/**
* Interface for the {@link AppModuleMetadata} decorator function.
*
* See {@link AppModuleMetadataFactory}.
*
* @stable
*/
export interface AppModuleDecorator extends TypeDecorator {}
/** /**
* {@link DirectiveMetadata} factory for creating annotations, decorators or DSL. * {@link DirectiveMetadata} factory for creating annotations, decorators or DSL.
* *
@ -477,6 +489,28 @@ export interface HostListenerMetadataFactory {
new (eventName: string, args?: string[]): any; new (eventName: string, args?: string[]): any;
} }
/**
* {@link AppModuleMetadata} factory for creating annotations, decorators or DSL.
*
* @stable
*/
export interface AppModuleMetadataFactory {
(obj: {
providers?: any[],
directives?: Array<Type|any[]>,
pipes?: Array<Type|any[]>,
precompile?: Array<Type|any[]>,
modules?: Array<Type|any[]>,
}): AppModuleDecorator;
new (obj: {
providers?: any[],
directives?: Array<Type|any[]>,
pipes?: Array<Type|any[]>,
precompile?: Array<Type|any[]>,
modules?: Array<Type|any[]>,
}): AppModuleMetadata;
}
// TODO(alexeagle): remove the duplication of this doc. It is copied from ComponentMetadata. // TODO(alexeagle): remove the duplication of this doc. It is copied from ComponentMetadata.
/** /**
* Declare reusable UI building blocks for an application. * Declare reusable UI building blocks for an application.
@ -1499,3 +1533,11 @@ export var HostBinding: HostBindingMetadataFactory = makePropDecorator(HostBindi
* @Annotation * @Annotation
*/ */
export var HostListener: HostListenerMetadataFactory = makePropDecorator(HostListenerMetadata); export var HostListener: HostListenerMetadataFactory = makePropDecorator(HostListenerMetadata);
/**
* Declares an app module.
* @stable
* @Annotation
*/
export var AppModule: AppModuleMetadataFactory =
<AppModuleMetadataFactory>makeDecorator(AppModuleMetadata);

View File

@ -0,0 +1,113 @@
/**
* @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 {InjectableMetadata} from '../di/metadata';
import {Type} from '../facade/lang';
/**
* Declares an Application Module.
* @stable
*/
export class AppModuleMetadata extends InjectableMetadata {
/**
* Defines the set of injectable objects that are available in the injector
* of this module.
*
* ## Simple Example
*
* Here is an example of a class that can be injected:
*
* ```
* class Greeter {
* greet(name:string) {
* return 'Hello ' + name + '!';
* }
* }
*
* @AppModule({
* providers: [
* Greeter
* ]
* })
* class HelloWorld {
* greeter:Greeter;
*
* constructor(greeter:Greeter) {
* this.greeter = greeter;
* }
* }
* ```
*/
get providers(): any[] { return this._providers; }
private _providers: any[];
/**
* Specifies a list of directives that can be used within the template
* of any component that is part of this application module.
*
* ### Example
*
* ```javascript
* @AppModule({
* directives: [NgFor]
* })
* class MyAppModule {
* }
* ```
*/
directives: Array<Type|any[]>;
/**
* Specifies a list of pipes that can be used within the template
* of any component that is part of this application module.
*
* ### Example
*
* ```javascript
* @AppModule({
* pipes: [SomePipe]
* })
* class MyAppModule {
* }
* ```
*/
pipes: Array<Type|any[]>;
/**
* Defines the components that should be precompiled as well when
* this component is defined. For each components listed here,
* Angular will create a {@link ComponentFactory ComponentFactory} and store it in the
* {@link ComponentFactoryResolver ComponentFactoryResolver}.
*/
precompile: Array<Type|any[]>;
/**
* 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<Type|any[]>;
constructor({providers, directives, pipes, precompile, modules}: {
providers?: any[],
directives?: Array<Type|any[]>,
pipes?: Array<Type|any[]>,
precompile?: Array<Type|any[]>,
modules?: Array<Type|any[]>
} = {}) {
super();
this._providers = providers;
this.directives = directives;
this.pipes = pipes;
this.precompile = precompile;
this.modules = modules;
}
}

View File

@ -0,0 +1,27 @@
/**
* @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 {Injector} from '@angular/core';
import {beforeEach, ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal';
import {IS_DART} from '../../src/facade/lang';
export function main() {
describe('Injector.NULL', () => {
it('should throw if no arg is given', () => {expect(() => Injector.NULL.get('someToken'))
.toThrowError('No provider for someToken!')});
it('should throw if THROW_IF_NOT_FOUND is given',
() => {expect(() => Injector.NULL.get('someToken', Injector.THROW_IF_NOT_FOUND))
.toThrowError('No provider for someToken!')});
it('should return the default value',
() => { expect(Injector.NULL.get('someToken', 'notFound')).toEqual('notFound'); });
});
}

View File

@ -0,0 +1,478 @@
import {LowerCasePipe, NgIf} from '@angular/common';
import {CompilerConfig} from '@angular/compiler';
import {AppModule, AppModuleMetadata, Compiler, Component, ComponentFactoryResolver, ComponentRef, DebugElement, Host, Inject, Injectable, Injector, OpaqueToken, Optional, Provider, SelfMetadata, SkipSelf, SkipSelfMetadata, forwardRef, getDebugNode, provide} from '@angular/core';
import {ComponentFixture} from '@angular/core/testing';
import {beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
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 {
}
@Component({
selector: 'comp',
template: `<div [title]="'HELLO' | lowercase"></div><div *ngIf="true"></div>`
})
class CompUsingModuleDirectiveAndPipe {
}
@Component(
{selector: 'parent', template: `<comp></comp>`, directives: [CompUsingModuleDirectiveAndPipe]})
class ParentCompUsingModuleDirectiveAndPipe {
}
@AppModule(
{directives: [NgIf], pipes: [LowerCasePipe], 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(inject([Compiler, Injector], (_compiler: Compiler, _injector: Injector) => {
compiler = _compiler;
injector = _injector;
}));
beforeEachProviders(() => [{
provide: CompilerConfig,
useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit})
}]);
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 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<T>(
compType: ConcreteType<T>, moduleType: ConcreteType<any>,
moduleMeta: AppModuleMetadata = null): ComponentFixture<T> {
let appModule = compiler.compileAppModuleSync(moduleType, moduleMeta).create();
var cf = appModule.componentFactoryResolver.resolveComponentFactory(compType);
return new ComponentFixture(cf.create(injector), null, false);
}
function checkNgIfAndLowerCasePipe(compFixture: ComponentFixture<any>, el: DebugElement) {
// Test that ngIf works
expect(el.children.length).toBe(1);
compFixture.detectChanges();
expect(el.children.length).toBe(2);
// Test that lowercase pipe works
expect(el.children[0].properties['title']).toBe('hello');
}
it('should support module directives and pipes', () => {
let compFixture = createComp(CompUsingModuleDirectiveAndPipe, ModuleWithDirectivesAndPipes);
checkNgIfAndLowerCasePipe(compFixture, compFixture.debugElement);
});
it('should support module directives and pipes for nested modules', () => {
let compFixture = createComp(
CompUsingModuleDirectiveAndPipe, SomeModule,
new AppModuleMetadata({modules: [ModuleWithDirectivesAndPipes]}));
checkNgIfAndLowerCasePipe(compFixture, compFixture.debugElement);
});
it('should support module directives and pipes in nested components', () => {
let compFixture =
createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule, new AppModuleMetadata({
directives: [NgIf],
pipes: [LowerCasePipe],
precompile: [ParentCompUsingModuleDirectiveAndPipe]
}));
checkNgIfAndLowerCasePipe(compFixture, compFixture.debugElement.children[0]);
});
});
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: <any>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(<any>['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');
});
});
});
});
}

View File

@ -21,8 +21,6 @@
"files": [ "files": [
"../../node_modules/@types/node/index.d.ts", "../../node_modules/@types/node/index.d.ts",
"../../dist/all/@angular/compiler/test/output/output_emitter_codegen_typed.ts", "../../dist/all/@angular/compiler/test/output/output_emitter_codegen_typed.ts",
"../../dist/all/@angular/compiler/test/output/output_emitter_codegen_untyped.ts", "../../dist/all/@angular/compiler/test/output/output_emitter_codegen_untyped.ts"
"../../dist/all/@angular/compiler/test/offline_compiler_codegen_untyped.ts",
"../../dist/all/@angular/compiler/test/offline_compiler_codegen_typed.ts"
] ]
} }

View File

@ -138,6 +138,63 @@ export declare abstract class ApplicationRef {
abstract waitForAsyncInitializers(): Promise<any>; abstract waitForAsyncInitializers(): Promise<any>;
} }
/** @stable */
export declare var AppModule: AppModuleMetadataFactory;
/** @stable */
export interface AppModuleDecorator extends TypeDecorator {
}
/** @stable */
export declare class AppModuleFactory<T> {
moduleType: ConcreteType<T>;
constructor(_injectorClass: {
new (parentInjector: Injector): AppModuleInjector<T>;
}, _moduleype: ConcreteType<T>);
create(parentInjector?: Injector): AppModuleRef<T>;
}
/** @stable */
export declare class AppModuleMetadata extends InjectableMetadata {
directives: Array<Type | any[]>;
modules: Array<Type | any[]>;
pipes: Array<Type | any[]>;
precompile: Array<Type | any[]>;
providers: any[];
constructor({providers, directives, pipes, precompile, modules}?: {
providers?: any[];
directives?: Array<Type | any[]>;
pipes?: Array<Type | any[]>;
precompile?: Array<Type | any[]>;
modules?: Array<Type | any[]>;
});
}
/** @stable */
export interface AppModuleMetadataFactory {
(obj: {
providers?: any[];
directives?: Array<Type | any[]>;
pipes?: Array<Type | any[]>;
precompile?: Array<Type | any[]>;
modules?: Array<Type | any[]>;
}): AppModuleDecorator;
new (obj: {
providers?: any[];
directives?: Array<Type | any[]>;
pipes?: Array<Type | any[]>;
precompile?: Array<Type | any[]>;
modules?: Array<Type | any[]>;
}): AppModuleMetadata;
}
/** @stable */
export declare abstract class AppModuleRef<T> {
componentFactoryResolver: ComponentFactoryResolver;
injector: Injector;
instance: T;
}
/** @experimental */ /** @experimental */
export declare function asNativeElements(debugEls: DebugElement[]): any; export declare function asNativeElements(debugEls: DebugElement[]): any;
@ -229,9 +286,17 @@ export declare class CollectionChangeRecord {
/** @stable */ /** @stable */
export declare class Compiler { export declare class Compiler {
clearCache(): void; clearCache(): void;
clearCacheFor(compType: Type): void; clearCacheFor(type: Type): void;
compileComponentAsync<T>(component: ConcreteType<T>): Promise<ComponentFactory<T>>; compileAppModuleAsync<T>(moduleType: ConcreteType<T>, metadata?: AppModuleMetadata): Promise<AppModuleFactory<T>>;
compileComponentSync<T>(component: ConcreteType<T>): ComponentFactory<T>; compileAppModuleSync<T>(moduleType: ConcreteType<T>, metadata?: AppModuleMetadata): AppModuleFactory<T>;
compileComponentAsync<T>(component: ConcreteType<T>, {moduleDirectives, modulePipes}?: {
moduleDirectives?: ConcreteType<any>[];
modulePipes?: ConcreteType<any>[];
}): Promise<ComponentFactory<T>>;
compileComponentSync<T>(component: ConcreteType<T>, {moduleDirectives, modulePipes}?: {
moduleDirectives?: ConcreteType<any>[];
modulePipes?: ConcreteType<any>[];
}): ComponentFactory<T>;
} }
/** @stable */ /** @stable */
@ -733,6 +798,7 @@ export interface InjectMetadataFactory {
/** @stable */ /** @stable */
export declare abstract class Injector { export declare abstract class Injector {
get(token: any, notFoundValue?: any): any; get(token: any, notFoundValue?: any): any;
static NULL: Injector;
static THROW_IF_NOT_FOUND: Object; static THROW_IF_NOT_FOUND: Object;
} }

View File

@ -6,10 +6,8 @@ import {TSC, TscWatch, reportError} from './tsc_watch';
export * from './tsc_watch'; export * from './tsc_watch';
import 'reflect-metadata'; import 'reflect-metadata';
const OFFLINE_COMPILE = [ const OFFLINE_COMPILE =
'output/output_emitter_codegen_untyped', 'output/output_emitter_codegen_typed', ['output/output_emitter_codegen_untyped', 'output/output_emitter_codegen_typed'];
'offline_compiler_codegen_untyped', 'offline_compiler_codegen_typed'
];
function processOutputEmitterCodeGen(): Promise<number> { function processOutputEmitterCodeGen(): Promise<number> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {