refactor(core): change module semantics

This contains major changes to the compiler, bootstrap of the platforms
and test environment initialization.

Main part of #10043
Closes #10164

BREAKING CHANGE:
- Semantics and name of `@AppModule` (now `@NgModule`) changed quite a bit.
  This is actually not breaking as `@AppModules` were not part of rc.4.
  We will have detailed docs on `@NgModule` separately.
- `coreLoadAndBootstrap` and `coreBootstrap` can't be used any more (without migration support).
  Use `bootstrapModule` / `bootstrapModuleFactory` instead.
- All Components listed in routes have to be part of the `declarations` of an NgModule.
  Either directly on the bootstrap module / lazy loaded module, or in an NgModule imported by them.
This commit is contained in:
Tobias Bosch
2016-07-18 03:50:31 -07:00
parent ca16fc29a6
commit 46b212706b
129 changed files with 3580 additions and 3366 deletions

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AppModuleCompiler} from './app_module_compiler';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer';
import {ListWrapper} from './facade/collection';
import {BaseException} from './facade/exceptions';
import {Identifiers} from './identifiers';
import {CompileMetadataResolver} from './metadata_resolver';
import {NgModuleCompiler} from './ng_module_compiler';
import {OutputEmitter} from './output/abstract_emitter';
import * as o from './output/output_ast';
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
@ -23,61 +23,29 @@ export class SourceModule {
constructor(public moduleUrl: string, public source: string) {}
}
export class AppModulesSummary {
private _compAppModule = new Map<string, StaticSymbol>();
private _hashKey(type: StaticSymbol) { return `${type.filePath}#${type.name}`; }
hasComponent(component: StaticSymbol): boolean {
return this._compAppModule.has(this._hashKey(component));
}
addComponent(module: StaticSymbol, component: StaticSymbol) {
this._compAppModule.set(this._hashKey(component), module);
}
getModule(comp: StaticSymbol): StaticSymbol {
return this._compAppModule.get(this._hashKey(comp));
}
export class NgModulesSummary {
constructor(public ngModuleByComponent: Map<StaticSymbol, CompileNgModuleMetadata>) {}
}
export class OfflineCompiler {
constructor(
private _metadataResolver: CompileMetadataResolver,
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _appModuleCompiler: AppModuleCompiler, private _outputEmitter: OutputEmitter) {}
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter) {}
analyzeModules(appModules: StaticSymbol[]): AppModulesSummary {
let result = new AppModulesSummary();
appModules.forEach((appModule) => {
let appModuleMeta = this._metadataResolver.getAppModuleMetadata(appModule);
appModuleMeta.precompile.forEach(
(precompileComp) =>
this._getTransitiveComponents(appModule, <any>precompileComp.runtime, result));
});
return result;
}
analyzeModules(ngModules: StaticSymbol[]): NgModulesSummary {
const ngModuleByComponent = new Map<StaticSymbol, CompileNgModuleMetadata>();
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);
ngModules.forEach((ngModule) => {
const ngModuleMeta = this._metadataResolver.getNgModuleMetadata(<any>ngModule);
ngModuleMeta.declaredDirectives.forEach((dirMeta) => {
if (dirMeta.isComponent) {
ngModuleByComponent.set(dirMeta.type.runtime, ngModuleMeta);
}
});
});
compMeta.precompile.forEach((precompileComp) => {
this._getTransitiveComponents(appModule, <any>precompileComp.type.runtime);
});
return target;
return new NgModulesSummary(ngModuleByComponent);
}
clearCache() {
@ -86,55 +54,45 @@ export class OfflineCompiler {
}
compile(
moduleUrl: string, appModulesSummary: AppModulesSummary, components: StaticSymbol[],
appModules: StaticSymbol[]): Promise<SourceModule[]> {
moduleUrl: string, ngModulesSummary: NgModulesSummary, components: StaticSymbol[],
ngModules: StaticSymbol[]): Promise<SourceModule[]> {
let fileSuffix = _splitLastSuffix(moduleUrl)[1];
let statements: o.Statement[] = [];
let exportedVars: string[] = [];
let outputSourceModules: SourceModule[] = [];
// compile app modules
// compile all ng modules
exportedVars.push(
...appModules.map((appModule) => this._compileAppModule(appModule, statements)));
...ngModules.map((ngModuleType) => this._compileModule(ngModuleType, statements)));
// compile components
return Promise
.all(components.map((compType) => {
let appModule = appModulesSummary.getModule(compType);
let appModuleDirectives: CompileDirectiveMetadata[] = [];
let appModulePipes: CompilePipeMetadata[] = [];
if (appModule) {
let appModuleMeta = this._metadataResolver.getAppModuleMetadata(appModule);
appModuleDirectives.push(...appModuleMeta.directives.map(
type => this._metadataResolver.getDirectiveMetadata(type.runtime)));
appModulePipes.push(...appModuleMeta.pipes.map(
type => this._metadataResolver.getPipeMetadata(type.runtime)));
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>compType);
let ngModule = ngModulesSummary.ngModuleByComponent.get(compType);
if (!ngModule) {
throw new BaseException(
`Cannot determine the module for component ${compMeta.type.name}!`);
}
return Promise
.all([
this._metadataResolver.getDirectiveMetadata(<any>compType), ...appModuleDirectives,
...this._metadataResolver.getViewDirectivesMetadata(<any>compType)
].map(dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
.all([compMeta, ...ngModule.transitiveModule.directives].map(
dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
.then((normalizedCompWithDirectives) => {
let compMeta = normalizedCompWithDirectives[0];
let dirMetas = normalizedCompWithDirectives.slice(1);
const compMeta = normalizedCompWithDirectives[0];
const dirMetas = normalizedCompWithDirectives.slice(1);
_assertComponent(compMeta);
// compile styles
let stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
outputSourceModules.push(this._codgenStyles(compiledStyleSheet, fileSuffix));
});
// compile components
exportedVars.push(this._compileComponentFactory(compMeta, fileSuffix, statements));
let pipeMetas = [
...appModulePipes,
...this._metadataResolver.getViewPipesMetadata(compMeta.type.runtime)
];
exportedVars.push(this._compileComponent(
compMeta, dirMetas, pipeMetas, stylesCompileResults.componentStylesheet,
fileSuffix, statements));
compMeta, dirMetas, ngModule.transitiveModule.pipes,
stylesCompileResults.componentStylesheet, fileSuffix, statements));
});
}))
.then(() => {
@ -146,21 +104,21 @@ export class OfflineCompiler {
});
}
private _compileAppModule(appModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
let appModuleMeta = this._metadataResolver.getAppModuleMetadata(appModuleType);
let appCompileResult = this._appModuleCompiler.compile(appModuleMeta);
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
const ngModule = this._metadataResolver.getNgModuleMetadata(<any>ngModuleType);
let appCompileResult = this._ngModuleCompiler.compile(ngModule, []);
appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.name = _componentFactoryName(dep.comp);
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
});
targetStatements.push(...appCompileResult.statements);
return appCompileResult.appModuleFactoryVar;
return appCompileResult.ngModuleFactoryVar;
}
private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string {
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
var hostMeta = createHostComponentMeta(compMeta);
var hostViewFactoryVar =
this._compileComponent(hostMeta, [compMeta], [], null, fileSuffix, targetStatements);
var compFactoryVar = _componentFactoryName(compMeta.type);