feat(compiler): generate proper reexports in `.ngfactory.ts` files to not need transitive deps for compiling `.ngfactory.ts` files. (#13524)

Note: This checks the constructors of `@Injectable` classes more strictly.
E.g this will fail now as the constructor argument has no `@Inject` nor is
the type of the argument a DI token.

```
@Injectable()
class MyService {
  constructor(dep: string) {}
}
```

Last part of #12787
Closes #12787
This commit is contained in:
Tobias Bosch 2016-12-27 09:36:47 -08:00 committed by Hans
parent 697690349f
commit 9c697030e6
37 changed files with 995 additions and 410 deletions

View File

@ -10,7 +10,7 @@ import {SchemaMetadata} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler'; import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser'; import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, componentFactoryName, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
import {DirectiveNormalizer} from '../directive_normalizer'; import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler'; import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {ListWrapper} from '../facade/collection'; import {ListWrapper} from '../facade/collection';
@ -22,13 +22,14 @@ import * as o from '../output/output_ast';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler'; import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver'; import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser'; import {TemplateParser} from '../template_parser/template_parser';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler'; import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
import {AotCompilerHost} from './compiler_host'; import {AotCompilerHost} from './compiler_host';
import {GeneratedFile} from './generated_file'; import {GeneratedFile} from './generated_file';
import {StaticSymbol} from './static_symbol'; import {StaticSymbol} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver'; import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {serializeSummaries, summaryFileName} from './summary_serializer'; import {serializeSummaries} from './summary_serializer';
import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName} from './util';
export class AotCompiler { export class AotCompiler {
private _animationCompiler = new AnimationCompiler(); private _animationCompiler = new AnimationCompiler();
@ -65,12 +66,13 @@ export class AotCompiler {
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>, srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[], directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
injectables: StaticSymbol[]): GeneratedFile[] { injectables: StaticSymbol[]): GeneratedFile[] {
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1]; const fileSuffix = splitTypescriptSuffix(srcFileUrl)[1];
const statements: o.Statement[] = []; const statements: o.Statement[] = [];
const exportedVars: string[] = []; const exportedVars: string[] = [];
const generatedFiles: GeneratedFile[] = []; const generatedFiles: GeneratedFile[] = [];
generatedFiles.push(this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables)); generatedFiles.push(this._createSummary(
srcFileUrl, directives, pipes, ngModules, injectables, statements, exportedVars));
// compile all ng modules // compile all ng modules
exportedVars.push( exportedVars.push(
@ -109,7 +111,7 @@ export class AotCompiler {
}); });
if (statements.length > 0) { if (statements.length > 0) {
const srcModule = this._codegenSourceModule( const srcModule = this._codegenSourceModule(
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars); srcFileUrl, ngfactoryFilePath(srcFileUrl), statements, exportedVars);
generatedFiles.unshift(srcModule); generatedFiles.unshift(srcModule);
} }
return generatedFiles; return generatedFiles;
@ -117,7 +119,8 @@ export class AotCompiler {
private _createSummary( private _createSummary(
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[], srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile { ngModules: StaticSymbol[], injectables: StaticSymbol[], targetStatements: o.Statement[],
targetExportedVars: string[]): GeneratedFile {
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl) const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
.map(symbol => this._symbolResolver.resolveSymbol(symbol)); .map(symbol => this._symbolResolver.resolveSymbol(symbol));
const typeSummaries = [ const typeSummaries = [
@ -126,8 +129,13 @@ export class AotCompiler {
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)), ...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)),
...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref)) ...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref))
]; ];
const json = serializeSummaries( const {json, exportAs} = serializeSummaries(
this._host, this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries); this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
exportAs.forEach((entry) => {
targetStatements.push(
o.variable(entry.exportAs).set(o.importExpr({reference: entry.symbol})).toDeclStmt());
targetExportedVars.push(entry.exportAs);
});
return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json); return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json);
} }
@ -150,12 +158,6 @@ export class AotCompiler {
} }
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers); const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.reference = this._symbolResolver.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
});
targetStatements.push(...appCompileResult.statements); targetStatements.push(...appCompileResult.statements);
return appCompileResult.ngModuleFactoryVar; return appCompileResult.ngModuleFactoryVar;
} }
@ -172,13 +174,12 @@ export class AotCompiler {
private _compileComponentFactory( private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string, compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string { targetStatements: o.Statement[]): string {
const hostType = this._metadataResolver.getHostComponentType(compMeta.type.reference);
const hostMeta = createHostComponentMeta( const hostMeta = createHostComponentMeta(
this._symbolResolver.getStaticSymbol( hostType, compMeta, this._metadataResolver.getHostComponentViewClass(hostType));
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
compMeta);
const hostViewFactoryVar = this._compileComponent( const hostViewFactoryVar = this._compileComponent(
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements); hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
const compFactoryVar = _componentFactoryName(compMeta.type); const compFactoryVar = componentFactoryName(compMeta.type.reference);
targetStatements.push( targetStatements.push(
o.variable(compFactoryVar) o.variable(compFactoryVar)
.set(o.importExpr( .set(o.importExpr(
@ -219,7 +220,7 @@ export class AotCompiler {
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix)); ..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
} }
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements)); compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
targetStatements.push(..._resolveViewStatements(this._symbolResolver, viewResult)); targetStatements.push(...viewResult.statements);
return viewResult.viewClassVar; return viewResult.viewClassVar;
} }
@ -241,27 +242,6 @@ export class AotCompiler {
} }
} }
function _resolveViewStatements(
reflector: StaticSymbolResolver, compileResult: ViewCompileResult): o.Statement[] {
compileResult.dependencies.forEach((dep) => {
if (dep instanceof ViewClassDependency) {
const vfd = <ViewClassDependency>dep;
vfd.placeholder.reference =
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(vfd.comp)), dep.name);
} else if (dep instanceof ComponentFactoryDependency) {
const cfd = <ComponentFactoryDependency>dep;
cfd.placeholder.reference = reflector.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp));
} else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.reference =
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(dwd.dir)), dwd.name);
}
});
return compileResult.statements;
}
function _resolveStyleStatements( function _resolveStyleStatements(
reflector: StaticSymbolResolver, compileResult: CompiledStylesheet, reflector: StaticSymbolResolver, compileResult: CompiledStylesheet,
fileSuffix: string): o.Statement[] { fileSuffix: string): o.Statement[] {
@ -272,15 +252,6 @@ function _resolveStyleStatements(
return compileResult.statements; return compileResult.statements;
} }
function _ngfactoryModuleUrl(dirUrl: string): string {
const urlWithSuffix = _splitTypescriptSuffix(dirUrl);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}
function _componentFactoryName(comp: CompileIdentifierMetadata): string {
return `${identifierName(comp)}NgFactory`;
}
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string { function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`; return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`;
} }
@ -292,20 +263,6 @@ function _assertComponent(meta: CompileDirectiveMetadata) {
} }
} }
function _splitTypescriptSuffix(path: string): string[] {
if (path.endsWith('.d.ts')) {
return [path.slice(0, -5), '.ts'];
}
const lastDot = path.lastIndexOf('.');
if (lastDot !== -1) {
return [path.substring(0, lastDot), path.substring(lastDot)];
}
return [path, ''];
}
export interface NgAnalyzedModules { export interface NgAnalyzedModules {
ngModules: CompileNgModuleMetadata[]; ngModules: CompileNgModuleMetadata[];
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>; ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;

View File

@ -34,11 +34,12 @@ import {AotCompilerHost} from './compiler_host';
import {AotCompilerOptions} from './compiler_options'; import {AotCompilerOptions} from './compiler_options';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities'; import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector} from './static_reflector'; import {StaticReflector} from './static_reflector';
import {StaticSymbolCache} from './static_symbol'; import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver'; import {StaticSymbolResolver} from './static_symbol_resolver';
import {AotSummaryResolver} from './summary_resolver'; import {AotSummaryResolver} from './summary_resolver';
/** /**
* Creates a new AotCompiler based on options and a host. * Creates a new AotCompiler based on options and a host.
*/ */
@ -69,13 +70,19 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
const resolver = new CompileMetadataResolver( const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector), new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer, new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
staticReflector); symbolCache, staticReflector);
// TODO(vicb): do not pass options.i18nFormat here // TODO(vicb): do not pass options.i18nFormat here
const importResolver = {
getImportAs: (symbol: StaticSymbol) => symbolResolver.getImportAs(symbol),
fileNameToModuleName: (fileName: string, containingFilePath: string) =>
compilerHost.fileNameToModuleName(fileName, containingFilePath)
};
const compiler = new AotCompiler( const compiler = new AotCompiler(
compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver), compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver),
new ViewCompiler(config, elementSchemaRegistry), new ViewCompiler(config, elementSchemaRegistry),
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console), new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale, new NgModuleCompiler(), new TypeScriptEmitter(importResolver), summaryResolver,
options.i18nFormat, new AnimationParser(elementSchemaRegistry), symbolResolver); options.locale, options.i18nFormat, new AnimationParser(elementSchemaRegistry),
symbolResolver);
return {compiler, reflector: staticReflector}; return {compiler, reflector: staticReflector};
} }

View File

@ -6,19 +6,24 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ImportResolver} from '../output/path_util';
import {StaticSymbol} from './static_symbol'; import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolverHost} from './static_symbol_resolver'; import {StaticSymbolResolverHost} from './static_symbol_resolver';
import {AotSummaryResolverHost} from './summary_resolver'; import {AotSummaryResolverHost} from './summary_resolver';
import {AotSummarySerializerHost} from './summary_serializer';
/** /**
* The host of the AotCompiler disconnects the implementation from TypeScript / other language * The host of the AotCompiler disconnects the implementation from TypeScript / other language
* services and from underlying file systems. * services and from underlying file systems.
*/ */
export interface AotCompilerHost extends StaticSymbolResolverHost, ImportResolver, export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
AotSummaryResolverHost, AotSummarySerializerHost { /**
* Converts a file path to a module name that can be used as an `import.
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
*
* See ImportResolver.
*/
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
/*|null*/;
/** /**
* Loads a resource (e.g. html / css) * Loads a resource (e.g. html / css)
*/ */

View File

@ -83,8 +83,11 @@ export class StaticReflector implements ReflectorReader {
annotations = []; annotations = [];
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type);
if (classMetadata['extends']) { if (classMetadata['extends']) {
const parentAnnotations = this.annotations(this.simplify(type, classMetadata['extends'])); const parentType = this.simplify(type, classMetadata['extends']);
annotations.push(...parentAnnotations); if (parentType instanceof StaticSymbol) {
const parentAnnotations = this.annotations(parentType);
annotations.push(...parentAnnotations);
}
} }
if (classMetadata['decorators']) { if (classMetadata['decorators']) {
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']); const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
@ -101,10 +104,13 @@ export class StaticReflector implements ReflectorReader {
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type);
propMetadata = {}; propMetadata = {};
if (classMetadata['extends']) { if (classMetadata['extends']) {
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends'])); const parentType = this.simplify(type, classMetadata['extends']);
Object.keys(parentPropMetadata).forEach((parentProp) => { if (parentType instanceof StaticSymbol) {
propMetadata[parentProp] = parentPropMetadata[parentProp]; const parentPropMetadata = this.propMetadata(parentType);
}); Object.keys(parentPropMetadata).forEach((parentProp) => {
propMetadata[parentProp] = parentPropMetadata[parentProp];
});
}
} }
const members = classMetadata['members'] || {}; const members = classMetadata['members'] || {};
@ -156,7 +162,10 @@ export class StaticReflector implements ReflectorReader {
parameters.push(nestedResult); parameters.push(nestedResult);
}); });
} else if (classMetadata['extends']) { } else if (classMetadata['extends']) {
parameters = this.parameters(this.simplify(type, classMetadata['extends'])); const parentType = this.simplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) {
parameters = this.parameters(parentType);
}
} }
if (!parameters) { if (!parameters) {
parameters = []; parameters = [];
@ -176,10 +185,13 @@ export class StaticReflector implements ReflectorReader {
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type);
methodNames = {}; methodNames = {};
if (classMetadata['extends']) { if (classMetadata['extends']) {
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends'])); const parentType = this.simplify(type, classMetadata['extends']);
Object.keys(parentMethodNames).forEach((parentProp) => { if (parentType instanceof StaticSymbol) {
methodNames[parentProp] = parentMethodNames[parentProp]; const parentMethodNames = this._methodNames(parentType);
}); Object.keys(parentMethodNames).forEach((parentProp) => {
methodNames[parentProp] = parentMethodNames[parentProp];
});
}
} }
const members = classMetadata['members'] || {}; const members = classMetadata['members'] || {};

View File

@ -12,7 +12,14 @@
* This token is unique for a filePath and name and can be used as a hash table key. * This token is unique for a filePath and name and can be used as a hash table key.
*/ */
export class StaticSymbol { export class StaticSymbol {
constructor(public filePath: string, public name: string, public members?: string[]) {} constructor(public filePath: string, public name: string, public members: string[]) {}
assertNoMembers() {
if (this.members.length) {
throw new Error(
`Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`);
}
}
} }
/** /**

View File

@ -45,11 +45,18 @@ const SUPPORTED_SCHEMA_VERSION = 3;
/** /**
* This class is responsible for loading metadata per symbol, * This class is responsible for loading metadata per symbol,
* and normalizing references between symbols. * and normalizing references between symbols.
*
* Internally, it only uses symbols without members,
* and deduces the values for symbols with members based
* on these symbols.
*/ */
export class StaticSymbolResolver { export class StaticSymbolResolver {
private metadataCache = new Map<string, {[key: string]: any}>(); private metadataCache = new Map<string, {[key: string]: any}>();
// Note: this will only contain StaticSymbols without members!
private resolvedSymbols = new Map<StaticSymbol, ResolvedStaticSymbol>(); private resolvedSymbols = new Map<StaticSymbol, ResolvedStaticSymbol>();
private resolvedFilePaths = new Set<string>(); private resolvedFilePaths = new Set<string>();
// Note: this will only contain StaticSymbols without members!
private importAs = new Map<StaticSymbol, StaticSymbol>();
constructor( constructor(
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache, private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
@ -60,13 +67,33 @@ export class StaticSymbolResolver {
if (staticSymbol.members.length > 0) { if (staticSymbol.members.length > 0) {
return this._resolveSymbolMembers(staticSymbol); return this._resolveSymbolMembers(staticSymbol);
} }
let result = this._resolveSymbolFromSummary(staticSymbol); let result = this.resolvedSymbols.get(staticSymbol);
if (result) {
return result;
}
result = this._resolveSymbolFromSummary(staticSymbol);
if (result) {
return result;
}
// Note: Some users use libraries that were not compiled with ngc, i.e. they don't
// have summaries, only .d.ts files. So we always need to check both, the summary
// and metadata.
this._createSymbolsOf(staticSymbol.filePath);
result = this.resolvedSymbols.get(staticSymbol);
return result;
}
getImportAs(staticSymbol: StaticSymbol): StaticSymbol {
if (staticSymbol.members.length) {
const baseSymbol = this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name);
const baseImportAs = this.getImportAs(baseSymbol);
return baseImportAs ?
this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) :
null;
}
let result = this.summaryResolver.getImportAs(staticSymbol);
if (!result) { if (!result) {
// Note: Some users use libraries that were not compiled with ngc, i.e. they don't result = this.importAs.get(staticSymbol);
// have summaries, only .d.ts files. So we always need to check both, the summary
// and metadata.
this._createSymbolsOf(staticSymbol.filePath);
result = this.resolvedSymbols.get(staticSymbol);
} }
return result; return result;
} }
@ -135,10 +162,13 @@ export class StaticSymbolResolver {
const metadata = this.getModuleMetadata(filePath); const metadata = this.getModuleMetadata(filePath);
if (metadata['metadata']) { if (metadata['metadata']) {
// handle direct declarations of the symbol // handle direct declarations of the symbol
Object.keys(metadata['metadata']).forEach((symbolName) => { const topLevelSymbolNames =
const symbolMeta = metadata['metadata'][symbolName]; new Set<string>(Object.keys(metadata['metadata']).map(unescapeIdentifier));
resolvedSymbols.push( Object.keys(metadata['metadata']).forEach((metadataKey) => {
this.createResolvedSymbol(this.getStaticSymbol(filePath, symbolName), symbolMeta)); const symbolMeta = metadata['metadata'][metadataKey];
resolvedSymbols.push(this.createResolvedSymbol(
this.getStaticSymbol(filePath, unescapeIdentifier(metadataKey)), topLevelSymbolNames,
symbolMeta));
}); });
} }
@ -154,15 +184,16 @@ export class StaticSymbolResolver {
} else { } else {
symbolName = exportSymbol.as; symbolName = exportSymbol.as;
} }
symbolName = unescapeIdentifier(symbolName);
let symName = symbolName; let symName = symbolName;
if (typeof exportSymbol !== 'string') { if (typeof exportSymbol !== 'string') {
symName = exportSymbol.name; symName = unescapeIdentifier(exportSymbol.name);
} }
const resolvedModule = this.resolveModule(moduleExport.from, filePath); const resolvedModule = this.resolveModule(moduleExport.from, filePath);
if (resolvedModule) { if (resolvedModule) {
const targetSymbol = this.getStaticSymbol(resolvedModule, symName); const targetSymbol = this.getStaticSymbol(resolvedModule, symName);
const sourceSymbol = this.getStaticSymbol(filePath, symbolName); const sourceSymbol = this.getStaticSymbol(filePath, symbolName);
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol)); resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
} }
}); });
} else { } else {
@ -172,7 +203,7 @@ export class StaticSymbolResolver {
const nestedExports = this.getSymbolsOf(resolvedModule); const nestedExports = this.getSymbolsOf(resolvedModule);
nestedExports.forEach((targetSymbol) => { nestedExports.forEach((targetSymbol) => {
const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name); const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name);
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol)); resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
}); });
} }
} }
@ -182,7 +213,9 @@ export class StaticSymbolResolver {
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol)); (resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
} }
private createResolvedSymbol(sourceSymbol: StaticSymbol, metadata: any): ResolvedStaticSymbol { private createResolvedSymbol(
sourceSymbol: StaticSymbol, topLevelSymbolNames: Set<string>,
metadata: any): ResolvedStaticSymbol {
const self = this; const self = this;
class ReferenceTransformer extends ValueTransformer { class ReferenceTransformer extends ValueTransformer {
@ -196,7 +229,7 @@ export class StaticSymbolResolver {
return result; return result;
} else if (symbolic === 'reference') { } else if (symbolic === 'reference') {
const module = map['module']; const module = map['module'];
const name = map['name']; const name = map['name'] ? unescapeIdentifier(map['name']) : map['name'];
if (!name) { if (!name) {
return null; return null;
} }
@ -209,28 +242,43 @@ export class StaticSymbolResolver {
message: `Could not resolve ${module} relative to ${sourceSymbol.filePath}.` message: `Could not resolve ${module} relative to ${sourceSymbol.filePath}.`
}; };
} }
} else {
const isFunctionParam = functionParams.indexOf(name) >= 0;
if (!isFunctionParam) {
filePath = sourceSymbol.filePath;
}
}
if (filePath) {
return self.getStaticSymbol(filePath, name); return self.getStaticSymbol(filePath, name);
} else { } else if (functionParams.indexOf(name) >= 0) {
// reference to a function parameter // reference to a function parameter
return {__symbolic: 'reference', name: name}; return {__symbolic: 'reference', name: name};
} else {
if (topLevelSymbolNames.has(name)) {
return self.getStaticSymbol(sourceSymbol.filePath, name);
}
// ambient value
null;
} }
} else { } else {
return super.visitStringMap(map, functionParams); return super.visitStringMap(map, functionParams);
} }
} }
} }
const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []); const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
if (transformedMeta instanceof StaticSymbol) {
return this.createExport(sourceSymbol, transformedMeta);
}
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta); return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
} }
private createExport(sourceSymbol: StaticSymbol, targetSymbol: StaticSymbol):
ResolvedStaticSymbol {
sourceSymbol.assertNoMembers();
targetSymbol.assertNoMembers();
if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath)) {
// This case is for an ng library importing symbols from a plain ts library
// transitively.
// Note: We rely on the fact that we discover symbols in the direction
// from source files to library files
this.importAs.set(targetSymbol, this.getImportAs(sourceSymbol) || sourceSymbol);
}
return new ResolvedStaticSymbol(sourceSymbol, targetSymbol);
}
private reportError(error: Error, context: StaticSymbol, path?: string) { private reportError(error: Error, context: StaticSymbol, path?: string) {
if (this.errorRecorder) { if (this.errorRecorder) {
this.errorRecorder(error, (context && context.filePath) || path); this.errorRecorder(error, (context && context.filePath) || path);
@ -287,3 +335,11 @@ export class StaticSymbolResolver {
} }
} }
} }
const UNDERSCORE_CHAR_CODE = 95;
// Remove extra underscore from escaped identifier.
// See https://github.com/Microsoft/TypeScript/blob/master/src/compiler/utilities.ts
export function unescapeIdentifier(identifier: string): string {
return identifier.startsWith('___') ? identifier.substr(1) : identifier;
}

View File

@ -10,9 +10,8 @@ import {Summary, SummaryResolver} from '../summary_resolver';
import {StaticSymbol, StaticSymbolCache} from './static_symbol'; import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol} from './static_symbol_resolver'; import {ResolvedStaticSymbol} from './static_symbol_resolver';
import {deserializeSummaries, summaryFileName} from './summary_serializer'; import {deserializeSummaries} from './summary_serializer';
import {ngfactoryFilePath, stripNgFactory, summaryFileName} from './util';
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export interface AotSummaryResolverHost { export interface AotSummaryResolverHost {
/** /**
@ -24,23 +23,34 @@ export interface AotSummaryResolverHost {
* Returns whether a file is a source file or not. * Returns whether a file is a source file or not.
*/ */
isSourceFile(sourceFilePath: string): boolean; isSourceFile(sourceFilePath: string): boolean;
/**
* Returns the output file path of a source file.
* E.g.
* `some_file.ts` -> `some_file.d.ts`
*/
getOutputFileName(sourceFilePath: string): string;
} }
export class AotSummaryResolver implements SummaryResolver<StaticSymbol> { export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
// Note: this will only contain StaticSymbols without members!
private summaryCache = new Map<StaticSymbol, Summary<StaticSymbol>>(); private summaryCache = new Map<StaticSymbol, Summary<StaticSymbol>>();
private loadedFilePaths = new Set<string>(); private loadedFilePaths = new Set<string>();
// Note: this will only contain StaticSymbols without members!
private importAs = new Map<StaticSymbol, StaticSymbol>();
constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {} constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {}
private _assertNoMembers(symbol: StaticSymbol) { isLibraryFile(filePath: string): boolean {
if (symbol.members.length) { // Note: We need to strip the .ngfactory. file path,
throw new Error( // so this method also works for generated files
`Internal state: StaticSymbols in summaries can't have members! ${JSON.stringify(symbol)}`); // (for which host.isSourceFile will always return false).
} return !this.host.isSourceFile(stripNgFactory(filePath));
} }
getLibraryFileName(filePath: string) { return this.host.getOutputFileName(filePath); }
resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> { resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> {
this._assertNoMembers(staticSymbol); staticSymbol.assertNoMembers();
let summary = this.summaryCache.get(staticSymbol); let summary = this.summaryCache.get(staticSymbol);
if (!summary) { if (!summary) {
this._loadSummaryFile(staticSymbol.filePath); this._loadSummaryFile(staticSymbol.filePath);
@ -54,12 +64,17 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath); return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
} }
getImportAs(staticSymbol: StaticSymbol): StaticSymbol {
staticSymbol.assertNoMembers();
return this.importAs.get(staticSymbol);
}
private _loadSummaryFile(filePath: string) { private _loadSummaryFile(filePath: string) {
if (this.loadedFilePaths.has(filePath)) { if (this.loadedFilePaths.has(filePath)) {
return; return;
} }
this.loadedFilePaths.add(filePath); this.loadedFilePaths.add(filePath);
if (!this.host.isSourceFile(filePath)) { if (this.isLibraryFile(filePath)) {
const summaryFilePath = summaryFileName(filePath); const summaryFilePath = summaryFileName(filePath);
let json: string; let json: string;
try { try {
@ -69,8 +84,13 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
throw e; throw e;
} }
if (json) { if (json) {
const readSummaries = deserializeSummaries(this.staticSymbolCache, json); const {summaries, importAs} = deserializeSummaries(this.staticSymbolCache, json);
readSummaries.forEach((summary) => { this.summaryCache.set(summary.symbol, summary); }); summaries.forEach((summary) => this.summaryCache.set(summary.symbol, summary));
importAs.forEach((importAs) => {
this.importAs.set(
importAs.symbol,
this.staticSymbolCache.get(ngfactoryFilePath(filePath), importAs.importAs));
});
} }
} }
} }

View File

@ -15,25 +15,11 @@ import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolv
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/; const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export interface AotSummarySerializerHost {
/**
* Returns the output file path of a source file.
* E.g.
* `some_file.ts` -> `some_file.d.ts`
*/
getOutputFileName(sourceFilePath: string): string;
/**
* Returns whether a file is a source file or not.
*/
isSourceFile(sourceFilePath: string): boolean;
}
export function serializeSummaries( export function serializeSummaries(
host: AotSummarySerializerHost, summaryResolver: SummaryResolver<StaticSymbol>, summaryResolver: SummaryResolver<StaticSymbol>, symbolResolver: StaticSymbolResolver,
symbolResolver: StaticSymbolResolver, symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]):
{json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string { const serializer = new Serializer(symbolResolver, summaryResolver);
const serializer = new Serializer(host);
// for symbols, we use everything except for the class metadata itself // for symbols, we use everything except for the class metadata itself
// (we keep the statics though), as the class metadata is contained in the // (we keep the statics though), as the class metadata is contained in the
@ -46,7 +32,7 @@ export function serializeSummaries(
// we execute the loop! // we execute the loop!
for (let processedIndex = 0; processedIndex < serializer.symbols.length; processedIndex++) { for (let processedIndex = 0; processedIndex < serializer.symbols.length; processedIndex++) {
const symbol = serializer.symbols[processedIndex]; const symbol = serializer.symbols[processedIndex];
if (!host.isSourceFile(symbol.filePath)) { if (summaryResolver.isLibraryFile(symbol.filePath)) {
let summary = summaryResolver.resolveSummary(symbol); let summary = summaryResolver.resolveSummary(symbol);
if (!summary) { if (!summary) {
// some symbols might originate from a plain typescript library // some symbols might originate from a plain typescript library
@ -74,8 +60,11 @@ export function serializeSummaries(
const ngModuleSummary = <CompileNgModuleSummary>typeSummary; const ngModuleSummary = <CompileNgModuleSummary>typeSummary;
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => { ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
const symbol: StaticSymbol = id.reference; const symbol: StaticSymbol = id.reference;
if (!host.isSourceFile(symbol.filePath)) { if (summaryResolver.isLibraryFile(symbol.filePath)) {
serializer.addOrMergeSummary(summaryResolver.resolveSummary(symbol)); const summary = summaryResolver.resolveSummary(symbol);
if (summary) {
serializer.addOrMergeSummary(summary);
}
} }
}); });
} }
@ -83,18 +72,14 @@ export function serializeSummaries(
return serializer.serialize(); return serializer.serialize();
} }
export function deserializeSummaries( export function deserializeSummaries(symbolCache: StaticSymbolCache, json: string):
symbolCache: StaticSymbolCache, json: string): Summary<StaticSymbol>[] { {summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: string}[]} {
const deserializer = new Deserializer(symbolCache); const deserializer = new Deserializer(symbolCache);
return deserializer.deserialize(json); return deserializer.deserialize(json);
} }
export function summaryFileName(fileName: string): string {
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
return `${fileNameWithoutSuffix}.ngsummary.json`;
}
class Serializer extends ValueTransformer { class Serializer extends ValueTransformer {
// Note: This only contains symbols without members.
symbols: StaticSymbol[] = []; symbols: StaticSymbol[] = [];
private indexBySymbol = new Map<StaticSymbol, number>(); private indexBySymbol = new Map<StaticSymbol, number>();
// This now contains a `__symbol: number` in the place of // This now contains a `__symbol: number` in the place of
@ -102,7 +87,11 @@ class Serializer extends ValueTransformer {
private processedSummaryBySymbol = new Map<StaticSymbol, any>(); private processedSummaryBySymbol = new Map<StaticSymbol, any>();
private processedSummaries: any[] = []; private processedSummaries: any[] = [];
constructor(private host: AotSummarySerializerHost) { super(); } constructor(
private symbolResolver: StaticSymbolResolver,
private summaryResolver: SummaryResolver<StaticSymbol>) {
super();
}
addOrMergeSummary(summary: Summary<StaticSymbol>) { addOrMergeSummary(summary: Summary<StaticSymbol>) {
let symbolMeta = summary.metadata; let symbolMeta = summary.metadata;
@ -129,34 +118,44 @@ class Serializer extends ValueTransformer {
} }
} }
serialize(): string { serialize(): {json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
return JSON.stringify({ const exportAs: {symbol: StaticSymbol, exportAs: string}[] = [];
const json = JSON.stringify({
summaries: this.processedSummaries, summaries: this.processedSummaries,
symbols: this.symbols.map((symbol, index) => { symbols: this.symbols.map((symbol, index) => {
symbol.assertNoMembers();
let importAs: string;
if (this.summaryResolver.isLibraryFile(symbol.filePath)) {
importAs = `${symbol.name}_${index}`;
exportAs.push({symbol, exportAs: importAs});
}
return { return {
__symbol: index, __symbol: index,
name: symbol.name, name: symbol.name,
// We convert the source filenames tinto output filenames, // We convert the source filenames tinto output filenames,
// as the generated summary file will be used when teh current // as the generated summary file will be used when teh current
// compilation unit is used as a library // compilation unit is used as a library
filePath: this.host.getOutputFileName(symbol.filePath) filePath: this.summaryResolver.getLibraryFileName(symbol.filePath),
importAs: importAs
}; };
}) })
}); });
return {json, exportAs};
} }
private processValue(value: any): any { return visitValue(value, this, null); } private processValue(value: any): any { return visitValue(value, this, null); }
visitOther(value: any, context: any): any { visitOther(value: any, context: any): any {
if (value instanceof StaticSymbol) { if (value instanceof StaticSymbol) {
let index = this.indexBySymbol.get(value); const baseSymbol = this.symbolResolver.getStaticSymbol(value.filePath, value.name);
let index = this.indexBySymbol.get(baseSymbol);
// Note: == by purpose to compare with undefined! // Note: == by purpose to compare with undefined!
if (index == null) { if (index == null) {
index = this.indexBySymbol.size; index = this.indexBySymbol.size;
this.indexBySymbol.set(value, index); this.indexBySymbol.set(baseSymbol, index);
this.symbols.push(value); this.symbols.push(baseSymbol);
} }
return {__symbol: index}; return {__symbol: index, members: value.members};
} }
} }
} }
@ -166,16 +165,28 @@ class Deserializer extends ValueTransformer {
constructor(private symbolCache: StaticSymbolCache) { super(); } constructor(private symbolCache: StaticSymbolCache) { super(); }
deserialize(json: string): Summary<StaticSymbol>[] { deserialize(json: string):
{summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: string}[]} {
const data: {summaries: any[], symbols: any[]} = JSON.parse(json); const data: {summaries: any[], symbols: any[]} = JSON.parse(json);
this.symbols = data.symbols.map( const importAs: {symbol: StaticSymbol, importAs: string}[] = [];
serializedSymbol => this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name)); this.symbols = [];
return visitValue(data.summaries, this, null); data.symbols.forEach((serializedSymbol) => {
const symbol = this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name);
this.symbols.push(symbol);
if (serializedSymbol.importAs) {
importAs.push({symbol: symbol, importAs: serializedSymbol.importAs});
}
});
const summaries = visitValue(data.summaries, this, null);
return {summaries, importAs};
} }
visitStringMap(map: {[key: string]: any}, context: any): any { visitStringMap(map: {[key: string]: any}, context: any): any {
if ('__symbol' in map) { if ('__symbol' in map) {
return this.symbols[map['__symbol']]; const baseSymbol = this.symbols[map['__symbol']];
const members = map['members'];
return members.length ? this.symbolCache.get(baseSymbol.filePath, baseSymbol.name, members) :
baseSymbol;
} else { } else {
return super.visitStringMap(map, context); return super.visitStringMap(map, context);
} }

View File

@ -0,0 +1,37 @@
/**
* @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
*/
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export function ngfactoryFilePath(filePath: string): string {
const urlWithSuffix = splitTypescriptSuffix(filePath);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}
export function stripNgFactory(filePath: string): string {
return filePath.replace(/\.ngfactory\./, '.');
}
export function splitTypescriptSuffix(path: string): string[] {
if (path.endsWith('.d.ts')) {
return [path.slice(0, -5), '.ts'];
}
const lastDot = path.lastIndexOf('.');
if (lastDot !== -1) {
return [path.substring(0, lastDot), path.substring(lastDot)];
}
return [path, ''];
}
export function summaryFileName(fileName: string): string {
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
return `${fileNameWithoutSuffix}.ngsummary.json`;
}

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 {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core'; import {ChangeDetectionStrategy, ComponentFactory, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {StaticSymbol} from './aot/static_symbol'; import {StaticSymbol} from './aot/static_symbol';
import {ListWrapper} from './facade/collection'; import {ListWrapper} from './facade/collection';
@ -112,6 +112,24 @@ export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata
return reflector.importUri(ref); return reflector.importUri(ref);
} }
export function viewClassName(compType: any, embeddedTemplateIndex: number): string {
return `View_${identifierName({reference: compType})}_${embeddedTemplateIndex}`;
}
export function hostViewClassName(compType: any): string {
return `HostView_${identifierName({reference: compType})}`;
}
export function dirWrapperClassName(dirType: any) {
return `Wrapper_${identifierName({reference: dirType})}`;
}
export function componentFactoryName(compType: any): string {
return `${identifierName({reference: compType})}NgFactory`;
}
export interface ProxyClass { setDelegate(delegate: any): void; }
export interface CompileIdentifierMetadata { reference: any; } export interface CompileIdentifierMetadata { reference: any; }
export enum CompileSummaryKind { export enum CompileSummaryKind {
@ -241,7 +259,7 @@ export class CompileTemplateMetadata {
externalStylesheets?: CompileStylesheetMetadata[], externalStylesheets?: CompileStylesheetMetadata[],
ngContentSelectors?: string[], ngContentSelectors?: string[],
animations?: CompileAnimationEntryMetadata[], animations?: CompileAnimationEntryMetadata[],
interpolation?: [string, string] interpolation?: [string, string],
} = {}) { } = {}) {
this.encapsulation = encapsulation; this.encapsulation = encapsulation;
this.template = template; this.template = template;
@ -261,11 +279,16 @@ export class CompileTemplateMetadata {
return { return {
animations: this.animations.map(anim => anim.name), animations: this.animations.map(anim => anim.name),
ngContentSelectors: this.ngContentSelectors, ngContentSelectors: this.ngContentSelectors,
encapsulation: this.encapsulation encapsulation: this.encapsulation,
}; };
} }
} }
export interface CompileEntryComponentMetadata {
componentType: any;
componentFactory: StaticSymbol|ComponentFactory<any>;
}
// Note: This should only use interfaces as nested data types // Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON! // as we need to be able to serialize this from/to JSON!
export interface CompileDirectiveSummary extends CompileTypeSummary { export interface CompileDirectiveSummary extends CompileTypeSummary {
@ -281,9 +304,12 @@ export interface CompileDirectiveSummary extends CompileTypeSummary {
providers: CompileProviderMetadata[]; providers: CompileProviderMetadata[];
viewProviders: CompileProviderMetadata[]; viewProviders: CompileProviderMetadata[];
queries: CompileQueryMetadata[]; queries: CompileQueryMetadata[];
entryComponents: CompileIdentifierMetadata[]; entryComponents: CompileEntryComponentMetadata[];
changeDetection: ChangeDetectionStrategy; changeDetection: ChangeDetectionStrategy;
template: CompileTemplateSummary; template: CompileTemplateSummary;
wrapperType: StaticSymbol|ProxyClass;
componentViewType: StaticSymbol|ProxyClass;
componentFactory: StaticSymbol|ComponentFactory<any>;
} }
/** /**
@ -292,7 +318,8 @@ export interface CompileDirectiveSummary extends CompileTypeSummary {
export class CompileDirectiveMetadata { export class CompileDirectiveMetadata {
static create( static create(
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, {isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
providers, viewProviders, queries, viewQueries, entryComponents, template}: { providers, viewProviders, queries, viewQueries, entryComponents, template, wrapperType,
componentViewType, componentFactory}: {
isHost?: boolean, isHost?: boolean,
type?: CompileTypeMetadata, type?: CompileTypeMetadata,
isComponent?: boolean, isComponent?: boolean,
@ -306,8 +333,11 @@ export class CompileDirectiveMetadata {
viewProviders?: CompileProviderMetadata[], viewProviders?: CompileProviderMetadata[],
queries?: CompileQueryMetadata[], queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileIdentifierMetadata[], entryComponents?: CompileEntryComponentMetadata[],
template?: CompileTemplateMetadata template?: CompileTemplateMetadata,
wrapperType?: StaticSymbol|ProxyClass,
componentViewType?: StaticSymbol|ProxyClass,
componentFactory?: StaticSymbol|ComponentFactory<any>,
} = {}): CompileDirectiveMetadata { } = {}): CompileDirectiveMetadata {
const hostListeners: {[key: string]: string} = {}; const hostListeners: {[key: string]: string} = {};
const hostProperties: {[key: string]: string} = {}; const hostProperties: {[key: string]: string} = {};
@ -359,6 +389,9 @@ export class CompileDirectiveMetadata {
viewQueries, viewQueries,
entryComponents, entryComponents,
template, template,
wrapperType,
componentViewType,
componentFactory,
}); });
} }
isHost: boolean; isHost: boolean;
@ -376,32 +409,39 @@ export class CompileDirectiveMetadata {
viewProviders: CompileProviderMetadata[]; viewProviders: CompileProviderMetadata[];
queries: CompileQueryMetadata[]; queries: CompileQueryMetadata[];
viewQueries: CompileQueryMetadata[]; viewQueries: CompileQueryMetadata[];
entryComponents: CompileIdentifierMetadata[]; entryComponents: CompileEntryComponentMetadata[];
template: CompileTemplateMetadata; template: CompileTemplateMetadata;
constructor( wrapperType: StaticSymbol|ProxyClass;
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, componentViewType: StaticSymbol|ProxyClass;
hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries, componentFactory: StaticSymbol|ComponentFactory<any>;
viewQueries, entryComponents, template}: {
isHost?: boolean, constructor({isHost, type, isComponent, selector, exportAs,
type?: CompileTypeMetadata, changeDetection, inputs, outputs, hostListeners, hostProperties,
isComponent?: boolean, hostAttributes, providers, viewProviders, queries, viewQueries,
selector?: string, entryComponents, template, wrapperType, componentViewType, componentFactory}: {
exportAs?: string, isHost?: boolean,
changeDetection?: ChangeDetectionStrategy, type?: CompileTypeMetadata,
inputs?: {[key: string]: string}, isComponent?: boolean,
outputs?: {[key: string]: string}, selector?: string,
hostListeners?: {[key: string]: string}, exportAs?: string,
hostProperties?: {[key: string]: string}, changeDetection?: ChangeDetectionStrategy,
hostAttributes?: {[key: string]: string}, inputs?: {[key: string]: string},
providers?: CompileProviderMetadata[], outputs?: {[key: string]: string},
viewProviders?: CompileProviderMetadata[], hostListeners?: {[key: string]: string},
queries?: CompileQueryMetadata[], hostProperties?: {[key: string]: string},
viewQueries?: CompileQueryMetadata[], hostAttributes?: {[key: string]: string},
entryComponents?: CompileIdentifierMetadata[], providers?: CompileProviderMetadata[],
template?: CompileTemplateMetadata, viewProviders?: CompileProviderMetadata[],
} = {}) { queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileEntryComponentMetadata[],
template?: CompileTemplateMetadata,
wrapperType?: StaticSymbol|ProxyClass,
componentViewType?: StaticSymbol|ProxyClass,
componentFactory?: StaticSymbol|ComponentFactory<any>,
} = {}) {
this.isHost = !!isHost; this.isHost = !!isHost;
this.type = type; this.type = type;
this.isComponent = isComponent; this.isComponent = isComponent;
@ -418,8 +458,11 @@ export class CompileDirectiveMetadata {
this.queries = _normalizeArray(queries); this.queries = _normalizeArray(queries);
this.viewQueries = _normalizeArray(viewQueries); this.viewQueries = _normalizeArray(viewQueries);
this.entryComponents = _normalizeArray(entryComponents); this.entryComponents = _normalizeArray(entryComponents);
this.template = template; this.template = template;
this.wrapperType = wrapperType;
this.componentViewType = componentViewType;
this.componentFactory = componentFactory;
} }
toSummary(): CompileDirectiveSummary { toSummary(): CompileDirectiveSummary {
@ -439,7 +482,10 @@ export class CompileDirectiveMetadata {
queries: this.queries, queries: this.queries,
entryComponents: this.entryComponents, entryComponents: this.entryComponents,
changeDetection: this.changeDetection, changeDetection: this.changeDetection,
template: this.template && this.template.toSummary() template: this.template && this.template.toSummary(),
wrapperType: this.wrapperType,
componentViewType: this.componentViewType,
componentFactory: this.componentFactory
}; };
} }
} }
@ -448,11 +494,12 @@ export class CompileDirectiveMetadata {
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector. * Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
*/ */
export function createHostComponentMeta( export function createHostComponentMeta(
typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata { hostTypeReference: any, compMeta: CompileDirectiveMetadata,
hostViewType: StaticSymbol | ProxyClass): CompileDirectiveMetadata {
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate(); const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
return CompileDirectiveMetadata.create({ return CompileDirectiveMetadata.create({
isHost: true, isHost: true,
type: {reference: typeReference, diDeps: [], lifecycleHooks: []}, type: {reference: hostTypeReference, diDeps: [], lifecycleHooks: []},
template: new CompileTemplateMetadata({ template: new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
template: template, template: template,
@ -471,7 +518,8 @@ export function createHostComponentMeta(
providers: [], providers: [],
viewProviders: [], viewProviders: [],
queries: [], queries: [],
viewQueries: [] viewQueries: [],
componentViewType: hostViewType
}); });
} }
@ -517,7 +565,7 @@ export interface CompileNgModuleSummary extends CompileTypeSummary {
exportedPipes: CompileIdentifierMetadata[]; exportedPipes: CompileIdentifierMetadata[];
// Note: This is transitive. // Note: This is transitive.
entryComponents: CompileIdentifierMetadata[]; entryComponents: CompileEntryComponentMetadata[];
// Note: This is transitive. // Note: This is transitive.
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[]; providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[];
// Note: This is transitive. // Note: This is transitive.
@ -534,7 +582,7 @@ export class CompileNgModuleMetadata {
declaredPipes: CompileIdentifierMetadata[]; declaredPipes: CompileIdentifierMetadata[];
exportedPipes: CompileIdentifierMetadata[]; exportedPipes: CompileIdentifierMetadata[];
entryComponents: CompileIdentifierMetadata[]; entryComponents: CompileEntryComponentMetadata[];
bootstrapComponents: CompileIdentifierMetadata[]; bootstrapComponents: CompileIdentifierMetadata[];
providers: CompileProviderMetadata[]; providers: CompileProviderMetadata[];
@ -555,7 +603,7 @@ export class CompileNgModuleMetadata {
exportedDirectives?: CompileIdentifierMetadata[], exportedDirectives?: CompileIdentifierMetadata[],
declaredPipes?: CompileIdentifierMetadata[], declaredPipes?: CompileIdentifierMetadata[],
exportedPipes?: CompileIdentifierMetadata[], exportedPipes?: CompileIdentifierMetadata[],
entryComponents?: CompileIdentifierMetadata[], entryComponents?: CompileEntryComponentMetadata[],
bootstrapComponents?: CompileIdentifierMetadata[], bootstrapComponents?: CompileIdentifierMetadata[],
importedModules?: CompileNgModuleSummary[], importedModules?: CompileNgModuleSummary[],
exportedModules?: CompileNgModuleSummary[], exportedModules?: CompileNgModuleSummary[],
@ -603,7 +651,7 @@ export class TransitiveCompileNgModuleMetadata {
modulesSet = new Set<any>(); modulesSet = new Set<any>();
modules: CompileTypeMetadata[] = []; modules: CompileTypeMetadata[] = [];
entryComponentsSet = new Set<any>(); entryComponentsSet = new Set<any>();
entryComponents: CompileIdentifierMetadata[] = []; entryComponents: CompileEntryComponentMetadata[] = [];
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = []; providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
@ -641,10 +689,10 @@ export class TransitiveCompileNgModuleMetadata {
this.modules.push(id); this.modules.push(id);
} }
} }
addEntryComponent(id: CompileIdentifierMetadata) { addEntryComponent(ec: CompileEntryComponentMetadata) {
if (!this.entryComponentsSet.has(id.reference)) { if (!this.entryComponentsSet.has(ec.componentType)) {
this.entryComponentsSet.add(id.reference); this.entryComponentsSet.add(ec.componentType);
this.entryComponents.push(id); this.entryComponents.push(ec);
} }
} }
} }

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 {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata'; import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, dirWrapperClassName, identifierModuleUrl, identifierName} from './compile_metadata';
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util'; import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter'; import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util'; import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
@ -52,10 +52,6 @@ const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap
*/ */
@CompilerInjectable() @CompilerInjectable()
export class DirectiveWrapperCompiler { export class DirectiveWrapperCompiler {
static dirWrapperClassName(id: CompileIdentifierMetadata) {
return `Wrapper_${identifierName(id)}`;
}
constructor( constructor(
private compilerConfig: CompilerConfig, private _exprParser: Parser, private compilerConfig: CompilerConfig, private _exprParser: Parser,
private _schemaRegistry: ElementSchemaRegistry, private _console: Console) {} private _schemaRegistry: ElementSchemaRegistry, private _console: Console) {}
@ -149,7 +145,7 @@ class DirectiveWrapperBuilder implements ClassBuilder {
.toStmt()); .toStmt());
return createClassStmt({ return createClassStmt({
name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type), name: dirWrapperClassName(this.dirMeta.type.reference),
ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)), ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
builders: [{fields, ctorStmts, methods}, this] builders: [{fields, ctorStmts, methods}, this]
}); });

View File

@ -109,7 +109,7 @@ export class Extractor {
const resolver = new CompileMetadataResolver( const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector), new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer, new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
staticReflector); symbolCache, staticReflector);
// TODO(vicb): implicit tags & attributes // TODO(vicb): implicit tags & attributes
const messageBundle = new MessageBundle(htmlParser, [], {}); const messageBundle = new MessageBundle(htmlParser, [], {});

View File

@ -10,7 +10,7 @@ import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgMo
import {AnimationCompiler} from '../animation/animation_compiler'; import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser'; import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta, identifierName} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName} from '../compile_metadata';
import {CompilerConfig} from '../config'; import {CompilerConfig} from '../config';
import {DirectiveNormalizer} from '../directive_normalizer'; import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler'; import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
@ -21,10 +21,11 @@ import {NgModuleCompiler} from '../ng_module_compiler';
import * as ir from '../output/output_ast'; import * as ir from '../output/output_ast';
import {interpretStatements} from '../output/output_interpreter'; import {interpretStatements} from '../output/output_interpreter';
import {jitStatements} from '../output/output_jit'; import {jitStatements} from '../output/output_jit';
import {view_utils} from '../private_import_core';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler'; import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {TemplateParser} from '../template_parser/template_parser'; import {TemplateParser} from '../template_parser/template_parser';
import {SyncAsyncResult} from '../util'; import {SyncAsyncResult} from '../util';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompiler} from '../view_compiler/view_compiler'; import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompiler} from '../view_compiler/view_compiler';
@ -130,10 +131,6 @@ export class JitCompiler implements Compiler {
const extraProviders = [this._metadataResolver.getProviderMetadata(new ProviderMeta( const extraProviders = [this._metadataResolver.getProviderMetadata(new ProviderMeta(
Compiler, {useFactory: () => new ModuleBoundCompiler(this, moduleMeta.type.reference)}))]; Compiler, {useFactory: () => new ModuleBoundCompiler(this, moduleMeta.type.reference)}))];
const compileResult = this._ngModuleCompiler.compile(moduleMeta, extraProviders); const compileResult = this._ngModuleCompiler.compile(moduleMeta, extraProviders);
compileResult.dependencies.forEach((dep) => {
dep.placeholder.reference =
this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory;
});
if (!this._compilerConfig.useJit) { if (!this._compilerConfig.useJit) {
ngModuleFactory = ngModuleFactory =
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar); interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
@ -168,7 +165,7 @@ export class JitCompiler implements Compiler {
const template = const template =
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta); this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
templates.add(template); templates.add(template);
allComponentFactories.push(template.proxyComponentFactory); allComponentFactories.push(<ComponentFactory<any>>dirMeta.componentFactory);
} }
} }
}); });
@ -180,15 +177,16 @@ export class JitCompiler implements Compiler {
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference); const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
if (dirMeta.isComponent) { if (dirMeta.isComponent) {
dirMeta.entryComponents.forEach((entryComponentType) => { dirMeta.entryComponents.forEach((entryComponentType) => {
const moduleMeta = moduleByDirective.get(entryComponentType.reference); const moduleMeta = moduleByDirective.get(entryComponentType.componentType);
templates.add( templates.add(
this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta)); this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
}); });
} }
}); });
localModuleMeta.entryComponents.forEach((entryComponentType) => { localModuleMeta.entryComponents.forEach((entryComponentType) => {
const moduleMeta = moduleByDirective.get(entryComponentType.reference); const moduleMeta = moduleByDirective.get(entryComponentType.componentType);
templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta)); templates.add(
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
}); });
}); });
templates.forEach((template) => this._compileTemplate(template)); templates.forEach((template) => this._compileTemplate(template));
@ -222,12 +220,12 @@ export class JitCompiler implements Compiler {
const compMeta = this._metadataResolver.getDirectiveMetadata(compType); const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
assertComponent(compMeta); assertComponent(compMeta);
const HostClass = function HostClass() {}; const componentFactory = <ComponentFactory<any>>compMeta.componentFactory;
(<any>HostClass).overriddenName = `${identifierName(compMeta.type)}_Host`; const hostClass = this._metadataResolver.getHostComponentType(compType);
const hostMeta = createHostComponentMeta(
const hostMeta = createHostComponentMeta(HostClass, compMeta); hostClass, compMeta, <any>view_utils.getComponentFactoryViewClass(componentFactory));
compiledTemplate = new CompiledTemplate( compiledTemplate =
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]); new CompiledTemplate(true, compMeta.type, hostMeta, ngModule, [compMeta.type]);
this._compiledHostTemplateCache.set(compType, compiledTemplate); this._compiledHostTemplateCache.set(compType, compiledTemplate);
} }
return compiledTemplate; return compiledTemplate;
@ -239,8 +237,7 @@ export class JitCompiler implements Compiler {
if (!compiledTemplate) { if (!compiledTemplate) {
assertComponent(compMeta); assertComponent(compMeta);
compiledTemplate = new CompiledTemplate( compiledTemplate = new CompiledTemplate(
false, compMeta.selector, compMeta.type, compMeta, ngModule, false, compMeta.type, compMeta, ngModule, ngModule.transitiveModule.directives);
ngModule.transitiveModule.directives);
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate); this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
} }
return compiledTemplate; return compiledTemplate;
@ -277,6 +274,7 @@ export class JitCompiler implements Compiler {
`/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`, `/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`,
statements, compileResult.dirWrapperClassVar); statements, compileResult.dirWrapperClassVar);
} }
(<ProxyClass>dirMeta.wrapperType).setDelegate(directiveWrapperClass);
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass); this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
} }
@ -304,21 +302,6 @@ export class JitCompiler implements Compiler {
const compileResult = this._viewCompiler.compileComponent( const compileResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar), compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
pipes, compiledAnimations); pipes, compiledAnimations);
compileResult.dependencies.forEach((dep) => {
let depTemplate: CompiledTemplate;
if (dep instanceof ViewClassDependency) {
const vfd = <ViewClassDependency>dep;
depTemplate = this._assertComponentKnown(vfd.comp.reference, false);
vfd.placeholder.reference = depTemplate.proxyViewClass;
} else if (dep instanceof ComponentFactoryDependency) {
const cfd = <ComponentFactoryDependency>dep;
depTemplate = this._assertComponentKnown(cfd.comp.reference, true);
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
} else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
}
});
const statements = stylesCompileResult.componentStylesheet.statements const statements = stylesCompileResult.componentStylesheet.statements
.concat(...compiledAnimations.map(ca => ca.statements)) .concat(...compiledAnimations.map(ca => ca.statements))
.concat(compileResult.statements); .concat(compileResult.statements);
@ -358,30 +341,18 @@ export class JitCompiler implements Compiler {
class CompiledTemplate { class CompiledTemplate {
private _viewClass: Function = null; private _viewClass: Function = null;
proxyViewClass: Type<any>;
proxyComponentFactory: ComponentFactory<any>;
isCompiled = false; isCompiled = false;
constructor( constructor(
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata, public isHost: boolean, public compType: CompileIdentifierMetadata,
public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata, public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata,
public directives: CompileIdentifierMetadata[]) { public directives: CompileIdentifierMetadata[]) {
const self = this; const self = this;
this.proxyViewClass = <any>function() {
if (!self._viewClass) {
throw new Error(
`Illegal state: CompiledTemplate for ${stringify(self.compType)} is not compiled yet!`);
}
return self._viewClass.apply(this, arguments);
};
this.proxyComponentFactory = isHost ?
new ComponentFactory<any>(selector, this.proxyViewClass, compType.reference) :
null;
} }
compiled(viewClass: Function) { compiled(viewClass: Function) {
this._viewClass = viewClass; this._viewClass = viewClass;
this.proxyViewClass.prototype = viewClass.prototype; (<ProxyClass>this.compMeta.componentViewType).setDelegate(viewClass);
this.isCompiled = true; this.isCompiled = true;
} }
} }

View File

@ -6,9 +6,10 @@
* 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, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core'; import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
import {StaticSymbol} from './aot/static_symbol'; import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
import {ngfactoryFilePath} from './aot/util';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata'; import * as cpl from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer'; import {DirectiveNormalizer} from './directive_normalizer';
@ -38,6 +39,8 @@ export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
// to wait correctly. // to wait correctly.
@CompilerInjectable() @CompilerInjectable()
export class CompileMetadataResolver { export class CompileMetadataResolver {
private _nonNormalizedDirectiveCache =
new Map<Type<any>, {annotation: Directive, metadata: cpl.CompileDirectiveMetadata}>();
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>(); private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
private _summaryCache = new Map<Type<any>, cpl.CompileTypeSummary>(); private _summaryCache = new Map<Type<any>, cpl.CompileTypeSummary>();
private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>(); private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>();
@ -49,12 +52,14 @@ export class CompileMetadataResolver {
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver<any>, private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver<any>,
private _schemaRegistry: ElementSchemaRegistry, private _schemaRegistry: ElementSchemaRegistry,
private _directiveNormalizer: DirectiveNormalizer, private _directiveNormalizer: DirectiveNormalizer,
@Optional() private _staticSymbolCache: StaticSymbolCache,
private _reflector: ReflectorReader = reflector, private _reflector: ReflectorReader = reflector,
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {} @Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
clearCacheFor(type: Type<any>) { clearCacheFor(type: Type<any>) {
const dirMeta = this._directiveCache.get(type); const dirMeta = this._directiveCache.get(type);
this._directiveCache.delete(type); this._directiveCache.delete(type);
this._nonNormalizedDirectiveCache.delete(type);
this._summaryCache.delete(type); this._summaryCache.delete(type);
this._pipeCache.delete(type); this._pipeCache.delete(type);
this._ngModuleOfTypes.delete(type); this._ngModuleOfTypes.delete(type);
@ -67,6 +72,7 @@ export class CompileMetadataResolver {
clearCache() { clearCache() {
this._directiveCache.clear(); this._directiveCache.clear();
this._nonNormalizedDirectiveCache.clear();
this._summaryCache.clear(); this._summaryCache.clear();
this._pipeCache.clear(); this._pipeCache.clear();
this._ngModuleCache.clear(); this._ngModuleCache.clear();
@ -74,6 +80,66 @@ export class CompileMetadataResolver {
this._directiveNormalizer.clearCache(); this._directiveNormalizer.clearCache();
} }
private _createProxyClass(baseType: any, name: string): cpl.ProxyClass {
let delegate: any = null;
const proxyClass: cpl.ProxyClass = <any>function() {
if (!delegate) {
throw new Error(
`Illegal state: Class ${name} for type ${stringify(baseType)} is not compiled yet!`);
}
return delegate.apply(this, arguments);
};
proxyClass.setDelegate = (d) => {
delegate = d;
(<any>proxyClass).prototype = d.prototype;
};
// Make stringify work correctly
(<any>proxyClass).overriddenName = name;
return proxyClass;
}
private getGeneratedClass(dirType: any, name: string): StaticSymbol|cpl.ProxyClass {
if (dirType instanceof StaticSymbol) {
return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), name);
} else {
return this._createProxyClass(dirType, name);
}
}
private getDirectiveWrapperClass(dirType: any): StaticSymbol|cpl.ProxyClass {
return this.getGeneratedClass(dirType, cpl.dirWrapperClassName(dirType));
}
private getComponentViewClass(dirType: any): StaticSymbol|cpl.ProxyClass {
return this.getGeneratedClass(dirType, cpl.viewClassName(dirType, 0));
}
getHostComponentViewClass(dirType: any): StaticSymbol|cpl.ProxyClass {
return this.getGeneratedClass(dirType, cpl.hostViewClassName(dirType));
}
getHostComponentType(dirType: any): StaticSymbol|Type<any> {
const name = `${cpl.identifierName({reference: dirType})}_Host`;
if (dirType instanceof StaticSymbol) {
return this._staticSymbolCache.get(dirType.filePath, name);
} else {
const HostClass = <any>function HostClass() {};
HostClass.overriddenName = name;
return HostClass;
}
}
private getComponentFactory(selector: string, dirType: any): StaticSymbol|ComponentFactory<any> {
if (dirType instanceof StaticSymbol) {
return this._staticSymbolCache.get(
ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType));
} else {
const hostView = this.getHostComponentViewClass(dirType);
return new ComponentFactory(selector, <any>hostView, dirType);
}
}
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata { getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def)); const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def));
return new cpl.CompileAnimationEntryMetadata(entry.name, defs); return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
@ -162,6 +228,9 @@ export class CompileMetadataResolver {
queries: metadata.queries, queries: metadata.queries,
viewQueries: metadata.viewQueries, viewQueries: metadata.viewQueries,
entryComponents: metadata.entryComponents, entryComponents: metadata.entryComponents,
wrapperType: metadata.wrapperType,
componentViewType: metadata.componentViewType,
componentFactory: metadata.componentFactory,
template: templateMetadata template: templateMetadata
}); });
this._directiveCache.set(directiveType, normalizedDirMeta); this._directiveCache.set(directiveType, normalizedDirMeta);
@ -201,7 +270,14 @@ export class CompileMetadataResolver {
getNonNormalizedDirectiveMetadata(directiveType: any): getNonNormalizedDirectiveMetadata(directiveType: any):
{annotation: Directive, metadata: cpl.CompileDirectiveMetadata} { {annotation: Directive, metadata: cpl.CompileDirectiveMetadata} {
directiveType = resolveForwardRef(directiveType); directiveType = resolveForwardRef(directiveType);
const dirMeta = this._directiveResolver.resolve(directiveType); if (!directiveType) {
return null;
}
let cacheEntry = this._nonNormalizedDirectiveCache.get(directiveType);
if (cacheEntry) {
return cacheEntry;
}
const dirMeta = this._directiveResolver.resolve(directiveType, false);
if (!dirMeta) { if (!dirMeta) {
return null; return null;
} }
@ -230,7 +306,7 @@ export class CompileMetadataResolver {
let changeDetectionStrategy: ChangeDetectionStrategy = null; let changeDetectionStrategy: ChangeDetectionStrategy = null;
let viewProviders: cpl.CompileProviderMetadata[] = []; let viewProviders: cpl.CompileProviderMetadata[] = [];
let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = []; let entryComponentMetadata: cpl.CompileEntryComponentMetadata[] = [];
let selector = dirMeta.selector; let selector = dirMeta.selector;
if (dirMeta instanceof Component) { if (dirMeta instanceof Component) {
@ -243,7 +319,7 @@ export class CompileMetadataResolver {
} }
if (dirMeta.entryComponents) { if (dirMeta.entryComponents) {
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents) entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
.map((type) => this._getIdentifierMetadata(type)) .map((type) => this._getEntryComponentMetadata(type))
.concat(entryComponentMetadata); .concat(entryComponentMetadata);
} }
if (!selector) { if (!selector) {
@ -287,9 +363,17 @@ export class CompileMetadataResolver {
viewProviders: viewProviders, viewProviders: viewProviders,
queries: queries, queries: queries,
viewQueries: viewQueries, viewQueries: viewQueries,
entryComponents: entryComponentMetadata entryComponents: entryComponentMetadata,
wrapperType: this.getDirectiveWrapperClass(directiveType),
componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) :
undefined,
componentFactory: nonNormalizedTemplateMetadata ?
this.getComponentFactory(selector, directiveType) :
undefined
}); });
return {metadata, annotation: dirMeta}; cacheEntry = {metadata, annotation: dirMeta};
this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry);
return cacheEntry;
} }
/** /**
@ -371,7 +455,7 @@ export class CompileMetadataResolver {
const importedModules: cpl.CompileNgModuleSummary[] = []; const importedModules: cpl.CompileNgModuleSummary[] = [];
const exportedModules: cpl.CompileNgModuleSummary[] = []; const exportedModules: cpl.CompileNgModuleSummary[] = [];
const providers: cpl.CompileProviderMetadata[] = []; const providers: cpl.CompileProviderMetadata[] = [];
const entryComponents: cpl.CompileIdentifierMetadata[] = []; const entryComponents: cpl.CompileEntryComponentMetadata[] = [];
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = []; const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
const schemas: SchemaMetadata[] = []; const schemas: SchemaMetadata[] = [];
@ -488,7 +572,7 @@ export class CompileMetadataResolver {
if (meta.entryComponents) { if (meta.entryComponents) {
entryComponents.push(...flattenAndDedupeArray(meta.entryComponents) entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
.map(type => this._getIdentifierMetadata(type))); .map(type => this._getEntryComponentMetadata(type)));
} }
if (meta.bootstrap) { if (meta.bootstrap) {
@ -504,7 +588,8 @@ export class CompileMetadataResolver {
}); });
} }
entryComponents.push(...bootstrapComponents); entryComponents.push(
...bootstrapComponents.map(type => this._getEntryComponentMetadata(type.reference)));
if (meta.schemas) { if (meta.schemas) {
schemas.push(...flattenAndDedupeArray(meta.schemas)); schemas.push(...flattenAndDedupeArray(meta.schemas));
@ -769,7 +854,7 @@ export class CompileMetadataResolver {
} }
private _getProvidersMetadata( private _getProvidersMetadata(
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[], providers: Provider[], targetEntryComponents: cpl.CompileEntryComponentMetadata[],
debugInfo?: string, compileProviders: cpl.CompileProviderMetadata[] = [], debugInfo?: string, compileProviders: cpl.CompileProviderMetadata[] = [],
type?: any): cpl.CompileProviderMetadata[] { type?: any): cpl.CompileProviderMetadata[] {
providers.forEach((provider: any, providerIdx: number) => { providers.forEach((provider: any, providerIdx: number) => {
@ -813,8 +898,8 @@ export class CompileMetadataResolver {
} }
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any): private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any):
cpl.CompileIdentifierMetadata[] { cpl.CompileEntryComponentMetadata[] {
const components: cpl.CompileIdentifierMetadata[] = []; const components: cpl.CompileEntryComponentMetadata[] = [];
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = []; const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
if (provider.useFactory || provider.useExisting || provider.useClass) { if (provider.useFactory || provider.useExisting || provider.useClass) {
@ -832,14 +917,27 @@ export class CompileMetadataResolver {
extractIdentifiers(provider.useValue, collectedIdentifiers); extractIdentifiers(provider.useValue, collectedIdentifiers);
collectedIdentifiers.forEach((identifier) => { collectedIdentifiers.forEach((identifier) => {
if (this._directiveResolver.isDirective(identifier.reference) || const entry = this._getEntryComponentMetadata(identifier.reference);
this._loadSummary(identifier.reference, cpl.CompileSummaryKind.Directive)) { if (entry) {
components.push(identifier); components.push(entry);
} }
}); });
return components; return components;
} }
private _getEntryComponentMetadata(dirType: any): cpl.CompileEntryComponentMetadata {
const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType);
if (dirMeta) {
return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory};
} else {
const dirSummary =
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
if (dirSummary) {
return {componentType: dirType, componentFactory: dirSummary.componentFactory};
}
}
}
getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata { getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata {
let compileDeps: cpl.CompileDiDependencyMetadata[]; let compileDeps: cpl.CompileDiDependencyMetadata[];
let compileTypeMetadata: cpl.CompileTypeMetadata = null; let compileTypeMetadata: cpl.CompileTypeMetadata = null;

View File

@ -19,9 +19,12 @@ import {LifecycleHooks} from './private_import_core';
import {NgModuleProviderAnalyzer} from './provider_analyzer'; import {NgModuleProviderAnalyzer} from './provider_analyzer';
import {ProviderAst} from './template_parser/template_ast'; import {ProviderAst} from './template_parser/template_ast';
/**
* This is currently not read, but will probably be used in the future.
* We keep it as we already pass it through all the rigth places...
*/
export class ComponentFactoryDependency { export class ComponentFactoryDependency {
constructor( constructor(public compType: any) {}
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
} }
export class NgModuleCompileResult { export class NgModuleCompileResult {
@ -46,13 +49,12 @@ export class NgModuleCompiler {
const bootstrapComponentFactories: CompileIdentifierMetadata[] = []; const bootstrapComponentFactories: CompileIdentifierMetadata[] = [];
const entryComponentFactories = const entryComponentFactories =
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => { ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
const id: CompileIdentifierMetadata = {reference: null};
if (ngModuleMeta.bootstrapComponents.some( if (ngModuleMeta.bootstrapComponents.some(
(id) => id.reference === entryComponent.reference)) { (id) => id.reference === entryComponent.componentType)) {
bootstrapComponentFactories.push(id); bootstrapComponentFactories.push({reference: entryComponent.componentFactory});
} }
deps.push(new ComponentFactoryDependency(entryComponent, id)); deps.push(new ComponentFactoryDependency(entryComponent.componentType));
return id; return {reference: entryComponent.componentFactory};
}); });
const builder = new _InjectorBuilder( const builder = new _InjectorBuilder(
ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan); ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan);

View File

@ -7,7 +7,8 @@
*/ */
import {identifierModuleUrl, identifierName} from '../compile_metadata'; import {StaticSymbol} from '../aot/static_symbol';
import {CompileIdentifierMetadata} from '../compile_metadata';
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter'; import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
@ -16,17 +17,17 @@ import * as o from './output_ast';
import {ImportResolver} from './path_util'; import {ImportResolver} from './path_util';
export class JavaScriptEmitter implements OutputEmitter { export class JavaScriptEmitter implements OutputEmitter {
constructor(private _importGenerator: ImportResolver) {} constructor(private _importResolver: ImportResolver) {}
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string { emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new JsEmitterVisitor(moduleUrl); const converter = new JsEmitterVisitor(genFilePath, this._importResolver);
const ctx = EmitterVisitorContext.createRoot(exportedVars); const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx); converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = []; const srcParts: string[] = [];
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => { converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
// Note: can't write the real word for import as it screws up system.js auto detection... // Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push( srcParts.push(
`var ${prefix} = req` + `var ${prefix} = req` +
`uire('${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}');`); `uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`);
}); });
srcParts.push(ctx.toSource()); srcParts.push(ctx.toSource());
return srcParts.join('\n'); return srcParts.join('\n');
@ -36,20 +37,23 @@ export class JavaScriptEmitter implements OutputEmitter {
class JsEmitterVisitor extends AbstractJsEmitterVisitor { class JsEmitterVisitor extends AbstractJsEmitterVisitor {
importsWithPrefixes = new Map<string, string>(); importsWithPrefixes = new Map<string, string>();
constructor(private _moduleUrl: string) { super(); } constructor(private _genFilePath: string, private _importResolver: ImportResolver) { super(); }
private _resolveStaticSymbol(value: CompileIdentifierMetadata): StaticSymbol {
const reference = value.reference;
if (!(reference instanceof StaticSymbol)) {
throw new Error(`Internal error: unknown identifier ${JSON.stringify(value)}`);
}
return this._importResolver.getImportAs(reference) || reference;
}
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any { visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
const name = identifierName(ast.value); const {name, filePath} = this._resolveStaticSymbol(ast.value);
const moduleUrl = identifierModuleUrl(ast.value); if (filePath != this._genFilePath) {
if (isBlank(name)) { let prefix = this.importsWithPrefixes.get(filePath);
console.error('>>>', ast.value);
throw new Error(`Internal error: unknown identifier ${ast.value}`);
}
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
let prefix = this.importsWithPrefixes.get(moduleUrl);
if (isBlank(prefix)) { if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`; prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(moduleUrl, prefix); this.importsWithPrefixes.set(filePath, prefix);
} }
ctx.print(`${prefix}.`); ctx.print(`${prefix}.`);
} }

View File

@ -6,6 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {StaticSymbol} from '../aot/static_symbol';
/** /**
* Interface that defines how import statements should be generated. * Interface that defines how import statements should be generated.
*/ */
@ -16,4 +18,10 @@ export abstract class ImportResolver {
*/ */
abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
/*|null*/; /*|null*/;
/**
* Converts the given StaticSymbol into another StaticSymbol that should be used
* to generate the import from.
*/
abstract getImportAs(symbol: StaticSymbol): StaticSymbol /*|null*/;
} }

View File

@ -7,18 +7,22 @@
*/ */
import {CompileIdentifierMetadata, identifierModuleUrl, identifierName} from '../compile_metadata'; import {StaticSymbol} from '../aot/static_symbol';
import {CompileIdentifierMetadata} from '../compile_metadata';
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter'; import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
import * as o from './output_ast'; import * as o from './output_ast';
import {ImportResolver} from './path_util'; import {ImportResolver} from './path_util';
const _debugModuleUrl = '/debug/lib'; const _debugFilePath = '/debug/lib';
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]): export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]):
string { string {
const converter = new _TsEmitterVisitor(_debugModuleUrl); const converter = new _TsEmitterVisitor(_debugFilePath, {
fileNameToModuleName(filePath: string, containingFilePath: string) { return filePath; },
getImportAs(symbol: StaticSymbol) { return null; }
});
const ctx = EmitterVisitorContext.createRoot([]); const ctx = EmitterVisitorContext.createRoot([]);
const asts: any[] = Array.isArray(ast) ? ast : [ast]; const asts: any[] = Array.isArray(ast) ? ast : [ast];
@ -37,17 +41,23 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T
} }
export class TypeScriptEmitter implements OutputEmitter { export class TypeScriptEmitter implements OutputEmitter {
constructor(private _importGenerator: ImportResolver) {} constructor(private _importResolver: ImportResolver) {}
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string { emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new _TsEmitterVisitor(moduleUrl); const converter = new _TsEmitterVisitor(genFilePath, this._importResolver);
const ctx = EmitterVisitorContext.createRoot(exportedVars); const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx); converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = []; const srcParts: string[] = [];
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => { converter.reexports.forEach((reexports, exportedFilePath) => {
const reexportsCode =
reexports.map(reexport => `${reexport.name} as ${reexport.as}`).join(',');
srcParts.push(
`export {${reexportsCode}} from '${this._importResolver.fileNameToModuleName(exportedFilePath, genFilePath)}';`);
});
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
// Note: can't write the real word for import as it screws up system.js auto detection... // Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push( srcParts.push(
`imp` + `imp` +
`ort * as ${prefix} from '${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}';`); `ort * as ${prefix} from '${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}';`);
}); });
srcParts.push(ctx.toSource()); srcParts.push(ctx.toSource());
return srcParts.join('\n'); return srcParts.join('\n');
@ -55,9 +65,12 @@ export class TypeScriptEmitter implements OutputEmitter {
} }
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor { class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
constructor(private _moduleUrl: string) { super(false); } constructor(private _genFilePath: string, private _importResolver: ImportResolver) {
super(false);
}
importsWithPrefixes = new Map<string, string>(); importsWithPrefixes = new Map<string, string>();
reexports = new Map<string, {name: string, as: string}[]>();
visitType(t: o.Type, ctx: EmitterVisitorContext, defaultType: string = 'any') { visitType(t: o.Type, ctx: EmitterVisitorContext, defaultType: string = 'any') {
if (isPresent(t)) { if (isPresent(t)) {
@ -98,6 +111,19 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
} }
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any { visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
if (ctx.isExportedVar(stmt.name) && stmt.value instanceof o.ExternalExpr && !stmt.type) {
// check for a reexport
const {name, filePath, members} = this._resolveStaticSymbol(stmt.value.value);
if (members.length === 0 && filePath !== this._genFilePath) {
let reexports = this.reexports.get(filePath);
if (!reexports) {
reexports = [];
this.reexports.set(filePath, reexports);
}
reexports.push({name, as: stmt.name});
return null;
}
}
if (ctx.isExportedVar(stmt.name)) { if (ctx.isExportedVar(stmt.name)) {
ctx.print(`export `); ctx.print(`export `);
} }
@ -320,25 +346,29 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
}, params, ctx, ','); }, params, ctx, ',');
} }
private _resolveStaticSymbol(value: CompileIdentifierMetadata): StaticSymbol {
const reference = value.reference;
if (!(reference instanceof StaticSymbol)) {
throw new Error(`Internal error: unknown identifier ${JSON.stringify(value)}`);
}
return this._importResolver.getImportAs(reference) || reference;
}
private _visitIdentifier( private _visitIdentifier(
value: CompileIdentifierMetadata, typeParams: o.Type[], ctx: EmitterVisitorContext): void { value: CompileIdentifierMetadata, typeParams: o.Type[], ctx: EmitterVisitorContext): void {
const name = identifierName(value); const {name, filePath, members} = this._resolveStaticSymbol(value);
const moduleUrl = identifierModuleUrl(value); if (filePath != this._genFilePath) {
if (isBlank(name)) { let prefix = this.importsWithPrefixes.get(filePath);
throw new Error(`Internal error: unknown identifier ${value}`);
}
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
let prefix = this.importsWithPrefixes.get(moduleUrl);
if (isBlank(prefix)) { if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`; prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(moduleUrl, prefix); this.importsWithPrefixes.set(filePath, prefix);
} }
ctx.print(`${prefix}.`); ctx.print(`${prefix}.`);
} }
if (value.reference && value.reference.members && value.reference.members.length) { if (members.length) {
ctx.print(value.reference.name); ctx.print(name);
ctx.print('.'); ctx.print('.');
ctx.print(value.reference.members.join('.')); ctx.print(members.join('.'));
} else { } else {
ctx.print(name); ctx.print(name);
} }

View File

@ -16,6 +16,9 @@ export interface Summary<T> {
@CompilerInjectable() @CompilerInjectable()
export class SummaryResolver<T> { export class SummaryResolver<T> {
isLibraryFile(fileName: string): boolean { return false; };
getLibraryFileName(fileName: string): string { return null; }
resolveSummary(reference: T): Summary<T> { return null; }; resolveSummary(reference: T): Summary<T> { return null; };
getSymbolsOf(filePath: string): T[] { return []; } getSymbolsOf(filePath: string): T[] { return []; }
getImportAs(reference: T): T { return reference; }
} }

View File

@ -97,12 +97,11 @@ export class CompileElement extends CompileNode {
} }
private _createComponentFactoryResolver() { private _createComponentFactoryResolver() {
const entryComponents = const entryComponents = this.component.entryComponents.map((entryComponent) => {
this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => { this.view.targetDependencies.push(
const id: CompileIdentifierMetadata = {reference: null}; new ComponentFactoryDependency(entryComponent.componentType));
this.view.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id)); return {reference: entryComponent.componentFactory};
return id; });
});
if (!entryComponents || entryComponents.length === 0) { if (!entryComponents || entryComponents.length === 0) {
return; return;
} }
@ -179,11 +178,11 @@ export class CompileElement extends CompileNode {
const depsExpr = const depsExpr =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep)); deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
if (isDirectiveWrapper) { if (isDirectiveWrapper) {
const directiveWrapperIdentifier: CompileIdentifierMetadata = {reference: null}; const dirMeta =
this.view.targetDependencies.push(new DirectiveWrapperDependency( this._directives.find(dir => dir.type.reference === provider.useClass.reference);
provider.useClass, DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass), this.view.targetDependencies.push(
directiveWrapperIdentifier)); new DirectiveWrapperDependency(dirMeta.type.reference));
return DirectiveWrapperExpressions.create(directiveWrapperIdentifier, depsExpr); return DirectiveWrapperExpressions.create({reference: dirMeta.wrapperType}, depsExpr);
} else { } else {
return o.importExpr(provider.useClass) return o.importExpr(provider.useClass)
.instantiate(depsExpr, o.importType(provider.useClass)); .instantiate(depsExpr, o.importType(provider.useClass));

View File

@ -7,7 +7,7 @@
*/ */
import {AnimationEntryCompileResult} from '../animation/animation_compiler'; import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeSummary, tokenName} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeSummary, tokenName, viewClassName} from '../compile_metadata';
import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter'; import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter';
import {createPureProxy} from '../compiler_util/identifier_util'; import {createPureProxy} from '../compiler_util/identifier_util';
import {CompilerConfig} from '../config'; import {CompilerConfig} from '../config';
@ -20,8 +20,8 @@ import {CompileElement, CompileNode} from './compile_element';
import {CompileMethod} from './compile_method'; import {CompileMethod} from './compile_method';
import {CompilePipe} from './compile_pipe'; import {CompilePipe} from './compile_pipe';
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query'; import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps'; import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps';
import {getPropertyInView, getViewClassName} from './util'; import {getPropertyInView} from './util';
export enum CompileViewRootNodeType { export enum CompileViewRootNodeType {
Node, Node,
@ -87,7 +87,7 @@ export class CompileView implements NameResolver {
public animations: AnimationEntryCompileResult[], public viewIndex: number, public animations: AnimationEntryCompileResult[], public viewIndex: number,
public declarationElement: CompileElement, public templateVariableBindings: string[][], public declarationElement: CompileElement, public templateVariableBindings: string[][],
public targetDependencies: public targetDependencies:
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) { Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
this.createMethod = new CompileMethod(this); this.createMethod = new CompileMethod(this);
this.animationBindingsMethod = new CompileMethod(this); this.animationBindingsMethod = new CompileMethod(this);
this.injectorGetMethod = new CompileMethod(this); this.injectorGetMethod = new CompileMethod(this);
@ -103,7 +103,7 @@ export class CompileView implements NameResolver {
this.detachMethod = new CompileMethod(this); this.detachMethod = new CompileMethod(this);
this.viewType = getViewType(component, viewIndex); this.viewType = getViewType(component, viewIndex);
this.className = getViewClassName(component, viewIndex); this.className = viewClassName(component.type.reference, viewIndex);
this.classType = o.expressionType(o.variable(this.className)); this.classType = o.expressionType(o.variable(this.className));
this.classExpr = o.variable(this.className); this.classExpr = o.variable(this.className);
if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) { if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {

View File

@ -8,19 +8,26 @@
import {CompileIdentifierMetadata} from '../compile_metadata'; import {CompileIdentifierMetadata} from '../compile_metadata';
export class ViewClassDependency { /**
constructor( * This is currently not read, but will probably be used in the future.
public comp: CompileIdentifierMetadata, public name: string, * We keep it as we already pass it through all the rigth places...
public placeholder: CompileIdentifierMetadata) {} */
export class ComponentViewDependency {
constructor(public compType: any) {}
} }
/**
* This is currently not read, but will probably be used in the future.
* We keep it as we already pass it through all the rigth places...
*/
export class ComponentFactoryDependency { export class ComponentFactoryDependency {
constructor( constructor(public compType: any) {}
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
} }
/**
* This is currently not read, but will probably be used in the future.
* We keep it as we already pass it through all the rigth places...
*/
export class DirectiveWrapperDependency { export class DirectiveWrapperDependency {
constructor( constructor(public dirType: any) {}
public dir: CompileIdentifierMetadata, public name: string,
public placeholder: CompileIdentifierMetadata) {}
} }

View File

@ -71,12 +71,6 @@ export function injectFromViewParentInjector(
return viewExpr.callMethod('injectorGet', args); return viewExpr.callMethod('injectorGet', args);
} }
export function getViewClassName(
component: CompileDirectiveSummary | CompileDirectiveMetadata,
embeddedTemplateIndex: number): string {
return `View_${identifierName(component.type)}${embeddedTemplateIndex}`;
}
export function getHandleEventMethodName(elementIndex: number): string { export function getHandleEventMethodName(elementIndex: number): string {
return `handleEvent_${elementIndex}`; return `handleEvent_${elementIndex}`;
} }

View File

@ -8,7 +8,7 @@
import {ViewEncapsulation} from '@angular/core'; import {ViewEncapsulation} from '@angular/core';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, viewClassName} from '../compile_metadata';
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter'; import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util'; import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
@ -22,8 +22,7 @@ import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventA
import {CompileElement, CompileNode} from './compile_element'; import {CompileElement, CompileNode} from './compile_element';
import {CompileView, CompileViewRootNode, CompileViewRootNodeType} from './compile_view'; import {CompileView, CompileViewRootNode, CompileViewRootNodeType} from './compile_view';
import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants'; import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps'; import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps';
import {getViewClassName} from './util';
const IMPLICIT_TEMPLATE_VAR = '\$implicit'; const IMPLICIT_TEMPLATE_VAR = '\$implicit';
const CLASS_ATTR = 'class'; const CLASS_ATTR = 'class';
@ -36,7 +35,8 @@ const rootSelectorVar = o.variable('rootSelector');
export function buildView( export function buildView(
view: CompileView, template: TemplateAst[], view: CompileView, template: TemplateAst[],
targetDependencies: targetDependencies:
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>): number { Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>):
number {
const builderVisitor = new ViewBuilderVisitor(view, targetDependencies); const builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
const parentEl = const parentEl =
view.declarationElement.isNull() ? view.declarationElement : view.declarationElement.parent; view.declarationElement.isNull() ? view.declarationElement : view.declarationElement.parent;
@ -63,7 +63,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
constructor( constructor(
public view: CompileView, public view: CompileView,
public targetDependencies: public targetDependencies:
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {} Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; } private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
@ -214,9 +214,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
this.view.nodes.push(compileElement); this.view.nodes.push(compileElement);
let compViewExpr: o.ReadPropExpr = null; let compViewExpr: o.ReadPropExpr = null;
if (isPresent(component)) { if (isPresent(component)) {
const nestedComponentIdentifier: CompileIdentifierMetadata = {reference: null}; this.targetDependencies.push(new ComponentViewDependency(component.type.reference));
this.targetDependencies.push(new ViewClassDependency(
component.type, getViewClassName(component, 0), nestedComponentIdentifier));
compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: ` compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: `
this.view.fields.push(new o.ClassField( this.view.fields.push(new o.ClassField(
@ -226,7 +224,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
compileElement.setComponentView(compViewExpr); compileElement.setComponentView(compViewExpr);
this.view.createMethod.addStmt( this.view.createMethod.addStmt(
compViewExpr compViewExpr
.set(o.importExpr(nestedComponentIdentifier).instantiate([ .set(o.importExpr({reference: component.componentViewType}).instantiate([
ViewProperties.viewUtils, o.THIS_EXPR, o.literal(nodeIndex), renderNode ViewProperties.viewUtils, o.THIS_EXPR, o.literal(nodeIndex), renderNode
])) ]))
.toStmt()); .toStmt());

View File

@ -16,17 +16,17 @@ import {TemplateAst} from '../template_parser/template_ast';
import {CompileElement} from './compile_element'; import {CompileElement} from './compile_element';
import {CompileView} from './compile_view'; import {CompileView} from './compile_view';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps'; import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps';
import {bindView} from './view_binder'; import {bindView} from './view_binder';
import {buildView, finishView} from './view_builder'; import {buildView, finishView} from './view_builder';
export {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps'; export {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps';
export class ViewCompileResult { export class ViewCompileResult {
constructor( constructor(
public statements: o.Statement[], public viewClassVar: string, public statements: o.Statement[], public viewClassVar: string,
public dependencies: public dependencies:
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {} Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
} }
@CompilerInjectable() @CompilerInjectable()
@ -38,7 +38,7 @@ export class ViewCompiler {
pipes: CompilePipeSummary[], pipes: CompilePipeSummary[],
compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult { compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult {
const dependencies: const dependencies:
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency> = []; Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency> = [];
const view = new CompileView( const view = new CompileView(
component, this._genConfig, pipes, styles, compiledAnimations, 0, component, this._genConfig, pipes, styles, compiledAnimations, 0,
CompileElement.createNull(), [], dependencies); CompileElement.createNull(), [], dependencies);

View File

@ -487,6 +487,8 @@ describe('StaticReflector', () => {
export class Child extends Parent {} export class Child extends Parent {}
export class ChildNoDecorators extends Parent {} export class ChildNoDecorators extends Parent {}
export class ChildInvalidParent extends a.InvalidParent {}
` `
}); });
@ -500,6 +502,10 @@ describe('StaticReflector', () => {
expect( expect(
reflector.annotations(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildNoDecorators'))) reflector.annotations(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildNoDecorators')))
.toEqual([new ClassDecorator('parent')]); .toEqual([new ClassDecorator('parent')]);
expect(reflector.annotations(
reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent')))
.toEqual([]);
}); });
it('should inherit parameters', () => { it('should inherit parameters', () => {
@ -520,6 +526,8 @@ describe('StaticReflector', () => {
export class ChildWithCtor extends Parent { export class ChildWithCtor extends Parent {
constructor(@ParamDecorator('c') c: C) {} constructor(@ParamDecorator('c') c: C) {}
} }
export class ChildInvalidParent extends a.InvalidParent {}
` `
}); });
@ -537,6 +545,10 @@ describe('StaticReflector', () => {
expect(reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildWithCtor'))) expect(reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildWithCtor')))
.toEqual([[reflector.getStaticSymbol('/tmp/src/main.ts', 'C'), new ParamDecorator('c')]]); .toEqual([[reflector.getStaticSymbol('/tmp/src/main.ts', 'C'), new ParamDecorator('c')]]);
expect(
reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent')))
.toEqual([]);
}); });
it('should inherit property metadata', () => { it('should inherit property metadata', () => {
@ -561,6 +573,8 @@ describe('StaticReflector', () => {
@PropDecorator('c') @PropDecorator('c')
c: C; c: C;
} }
export class ChildInvalidParent extends a.InvalidParent {}
` `
}); });
@ -577,6 +591,10 @@ describe('StaticReflector', () => {
'b': [new PropDecorator('b1'), new PropDecorator('b2')], 'b': [new PropDecorator('b1'), new PropDecorator('b2')],
'c': [new PropDecorator('c')] 'c': [new PropDecorator('c')]
}); });
expect(reflector.propMetadata(
reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent')))
.toEqual({});
}); });
it('should inherit lifecycle hooks', () => { it('should inherit lifecycle hooks', () => {
@ -591,6 +609,8 @@ describe('StaticReflector', () => {
hook2() {} hook2() {}
hook3() {} hook3() {}
} }
export class ChildInvalidParent extends a.InvalidParent {}
` `
}); });
@ -606,6 +626,10 @@ describe('StaticReflector', () => {
expect(hooks(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child'), [ expect(hooks(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child'), [
'hook1', 'hook2', 'hook3' 'hook1', 'hook2', 'hook3'
])).toEqual([true, true, true]); ])).toEqual([true, true, true]);
expect(hooks(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent'), [
'hook1', 'hook2', 'hook3'
])).toEqual([false, false, false]);
}); });
}); });

View File

@ -16,7 +16,7 @@ import * as ts from 'typescript';
const TS_EXT = /(^.|(?!\.d)..)\.ts$/; const TS_EXT = /(^.|(?!\.d)..)\.ts$/;
describe('StaticSymbolResolver', () => { describe('StaticSymbolResolver', () => {
const noContext = new StaticSymbol('', ''); const noContext = new StaticSymbol('', '', []);
let host: StaticSymbolResolverHost; let host: StaticSymbolResolverHost;
let symbolResolver: StaticSymbolResolver; let symbolResolver: StaticSymbolResolver;
let symbolCache: StaticSymbolCache; let symbolCache: StaticSymbolCache;
@ -24,10 +24,11 @@ describe('StaticSymbolResolver', () => {
beforeEach(() => { symbolCache = new StaticSymbolCache(); }); beforeEach(() => { symbolCache = new StaticSymbolCache(); });
function init( function init(
testData: {[key: string]: any} = DEFAULT_TEST_DATA, summaries: Summary<StaticSymbol>[] = []) { testData: {[key: string]: any} = DEFAULT_TEST_DATA, summaries: Summary<StaticSymbol>[] = [],
summaryImportAs: {symbol: StaticSymbol, importAs: StaticSymbol}[] = []) {
host = new MockStaticSymbolResolverHost(testData); host = new MockStaticSymbolResolverHost(testData);
symbolResolver = symbolResolver = new StaticSymbolResolver(
new StaticSymbolResolver(host, symbolCache, new MockSummaryResolver(summaries)); host, symbolCache, new MockSummaryResolver(summaries, summaryImportAs));
} }
beforeEach(() => init()); beforeEach(() => init());
@ -137,6 +138,73 @@ describe('StaticSymbolResolver', () => {
]); ]);
}); });
describe('importAs', () => {
it('should calculate importAs relationship for non source files without summaries', () => {
init(
{
'/test.d.ts': [{
'__symbolic': 'module',
'version': 3,
'metadata': {
'a': {'__symbolic': 'reference', 'name': 'b', 'module': './test2'},
}
}],
'/test2.d.ts': [{
'__symbolic': 'module',
'version': 3,
'metadata': {
'b': {'__symbolic': 'reference', 'name': 'c', 'module': './test3'},
}
}]
},
[]);
symbolResolver.getSymbolsOf('/test.d.ts');
symbolResolver.getSymbolsOf('/test2.d.ts');
expect(symbolResolver.getImportAs(symbolCache.get('/test2.d.ts', 'b')))
.toBe(symbolCache.get('/test.d.ts', 'a'));
expect(symbolResolver.getImportAs(symbolCache.get('/test3.d.ts', 'c')))
.toBe(symbolCache.get('/test.d.ts', 'a'));
});
it('should calculate importAs relationship for non source files with summaries', () => {
init(
{
'/test.ts': `
export {a} from './test2';
`
},
[], [{
symbol: symbolCache.get('/test2.d.ts', 'a'),
importAs: symbolCache.get('/test3.d.ts', 'b')
}]);
symbolResolver.getSymbolsOf('/test.ts');
expect(symbolResolver.getImportAs(symbolCache.get('/test2.d.ts', 'a')))
.toBe(symbolCache.get('/test3.d.ts', 'b'));
});
it('should calculate importAs for symbols with members based on importAs for symbols without',
() => {
init(
{
'/test.ts': `
export {a} from './test2';
`
},
[], [{
symbol: symbolCache.get('/test2.d.ts', 'a'),
importAs: symbolCache.get('/test3.d.ts', 'b')
}]);
symbolResolver.getSymbolsOf('/test.ts');
expect(symbolResolver.getImportAs(symbolCache.get('/test2.d.ts', 'a', ['someMember'])))
.toBe(symbolCache.get('/test3.d.ts', 'b', ['someMember']));
});
});
it('should replace references by StaticSymbols', () => { it('should replace references by StaticSymbols', () => {
init({ init({
'/test.ts': ` '/test.ts': `
@ -180,6 +248,42 @@ describe('StaticSymbolResolver', () => {
.toBeFalsy(); .toBeFalsy();
}); });
it('should fill references to ambient symbols with undefined', () => {
init({
'/test.ts': `
export var y = 1;
export var z = [window, z];
`
});
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'z')).metadata).toEqual([
undefined, symbolCache.get('/test.ts', 'z')
]);
});
it('should allow to use symbols with __', () => {
init({
'/test.ts': `
export {__a__ as __b__} from './test2';
import {__c__} from './test2';
export var __x__ = 1;
export var __y__ = __c__;
`
});
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', '__x__')).metadata).toBe(1);
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', '__y__')).metadata)
.toBe(symbolCache.get('/test2.d.ts', '__c__'));
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', '__b__')).metadata)
.toBe(symbolCache.get('/test2.d.ts', '__a__'));
expect(symbolResolver.getSymbolsOf('/test.ts')).toEqual([
symbolCache.get('/test.ts', '__x__'), symbolCache.get('/test.ts', '__y__'),
symbolCache.get('/test.ts', '__b__')
]);
});
it('should be able to trace a named export', () => { it('should be able to trace a named export', () => {
const symbol = symbolResolver const symbol = symbolResolver
.resolveSymbol(symbolResolver.getSymbolByModule( .resolveSymbol(symbolResolver.getSymbolByModule(
@ -240,7 +344,10 @@ describe('StaticSymbolResolver', () => {
}); });
export class MockSummaryResolver implements SummaryResolver<StaticSymbol> { export class MockSummaryResolver implements SummaryResolver<StaticSymbol> {
constructor(private summaries: Summary<StaticSymbol>[] = []) {} constructor(private summaries: Summary<StaticSymbol>[] = [], private importAs: {
symbol: StaticSymbol,
importAs: StaticSymbol
}[] = []) {}
resolveSummary(reference: StaticSymbol): Summary<StaticSymbol> { resolveSummary(reference: StaticSymbol): Summary<StaticSymbol> {
return this.summaries.find(summary => summary.symbol === reference); return this.summaries.find(summary => summary.symbol === reference);
@ -249,6 +356,13 @@ export class MockSummaryResolver implements SummaryResolver<StaticSymbol> {
return this.summaries.filter(summary => summary.symbol.filePath === filePath) return this.summaries.filter(summary => summary.symbol.filePath === filePath)
.map(summary => summary.symbol); .map(summary => summary.symbol);
} }
getImportAs(symbol: StaticSymbol): StaticSymbol {
const entry = this.importAs.find(entry => entry.symbol === symbol);
return entry ? entry.importAs : undefined;
}
isLibraryFile(filePath: string): boolean { return filePath.endsWith('.d.ts'); }
getLibraryFileName(filePath: string): string { return filePath.replace(/(\.d)?\.ts$/, '.d.ts'); }
} }
export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost { export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost {

View File

@ -7,12 +7,12 @@
*/ */
import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, CompileTypeSummary, ResolvedStaticSymbol, StaticSymbol, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler'; import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, CompileTypeSummary, ResolvedStaticSymbol, StaticSymbol, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
import {AotSummarySerializerHost, deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer'; import {deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer';
import * as path from 'path'; import * as path from 'path';
import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec'; import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';
const EXT = /\.ts$|.d.ts$/; const EXT = /(\.d)?\.ts$/;
export function main() { export function main() {
describe('AotSummaryResolver', () => { describe('AotSummaryResolver', () => {
@ -32,8 +32,7 @@ export function main() {
const mockSummaryResolver = new MockSummaryResolver([]); const mockSummaryResolver = new MockSummaryResolver([]);
const symbolResolver = new StaticSymbolResolver( const symbolResolver = new StaticSymbolResolver(
new MockStaticSymbolResolverHost({}), symbolCache, mockSummaryResolver); new MockStaticSymbolResolverHost({}), symbolCache, mockSummaryResolver);
return serializeSummaries( return serializeSummaries(mockSummaryResolver, symbolResolver, symbols, types).json;
new MockAotSummarySerializerHost(), mockSummaryResolver, symbolResolver, symbols, types);
} }
it('should load serialized summary files', () => { it('should load serialized summary files', () => {
@ -56,17 +55,48 @@ export function main() {
expect(summaryResolver.resolveSummary(asymbol)).toBe(summaryResolver.resolveSummary(asymbol)); expect(summaryResolver.resolveSummary(asymbol)).toBe(summaryResolver.resolveSummary(asymbol));
}); });
it('should return all sumbols in a summary', () => { it('should return all symbols in a summary', () => {
const asymbol = symbolCache.get('/a.d.ts', 'a'); const asymbol = symbolCache.get('/a.d.ts', 'a');
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])}); init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
expect(summaryResolver.getSymbolsOf('/a.d.ts')).toEqual([asymbol]); expect(summaryResolver.getSymbolsOf('/a.d.ts')).toEqual([asymbol]);
});
it('should fill importAs for deep symbols', () => {
const libSymbol = symbolCache.get('/lib.d.ts', 'Lib');
const srcSymbol = symbolCache.get('/src.ts', 'Src');
init({
'/src.ngsummary.json':
serialize([{symbol: srcSymbol, metadata: 1}, {symbol: libSymbol, metadata: 2}], [])
});
summaryResolver.getSymbolsOf('/src.d.ts');
expect(summaryResolver.getImportAs(symbolCache.get('/src.d.ts', 'Src'))).toBeFalsy();
expect(summaryResolver.getImportAs(libSymbol))
.toBe(symbolCache.get('/src.ngfactory.ts', 'Lib_1'));
});
describe('isLibraryFile', () => {
it('should use host.isSourceFile to calculate the result', () => {
init();
expect(summaryResolver.isLibraryFile('someFile.ts')).toBe(false);
expect(summaryResolver.isLibraryFile('someFile.d.ts')).toBe(true);
});
it('should calculate the result for generated files based on the result for non generated files',
() => {
init();
spyOn(host, 'isSourceFile').and.callThrough();
expect(summaryResolver.isLibraryFile('someFile.ngfactory.ts')).toBe(false);
expect(host.isSourceFile).toHaveBeenCalledWith('someFile.ts');
});
}); });
}); });
} }
export class MockAotSummarySerializerHost implements AotSummarySerializerHost { export class MockAotSummaryResolverHost implements AotSummaryResolverHost {
constructor(private summaries: {[fileName: string]: string}) {}
fileNameToModuleName(fileName: string): string { fileNameToModuleName(fileName: string): string {
return './' + path.basename(fileName).replace(EXT, ''); return './' + path.basename(fileName).replace(EXT, '');
} }
@ -76,11 +106,6 @@ export class MockAotSummarySerializerHost implements AotSummarySerializerHost {
} }
isSourceFile(filePath: string) { return !filePath.endsWith('.d.ts'); } isSourceFile(filePath: string) { return !filePath.endsWith('.d.ts'); }
}
export class MockAotSummaryResolverHost extends MockAotSummarySerializerHost implements
AotSummaryResolverHost {
constructor(private summaries: {[fileName: string]: string}) { super(); }
loadSummary(filePath: string): string { return this.summaries[filePath]; } loadSummary(filePath: string): string { return this.summaries[filePath]; }
} }

View File

@ -7,7 +7,8 @@
*/ */
import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler'; import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler';
import {AotSummarySerializerHost, deserializeSummaries, serializeSummaries, summaryFileName} from '@angular/compiler/src/aot/summary_serializer'; import {deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer';
import {summaryFileName} from '@angular/compiler/src/aot/util';
import {MockStaticSymbolResolverHost} from './static_symbol_resolver_spec'; import {MockStaticSymbolResolverHost} from './static_symbol_resolver_spec';
import {MockAotSummaryResolverHost} from './summary_resolver_spec'; import {MockAotSummaryResolverHost} from './summary_resolver_spec';
@ -42,7 +43,7 @@ export function main() {
it('should serialize various data correctly', () => { it('should serialize various data correctly', () => {
init(); init();
const serializedData = serializeSummaries( const serializedData = serializeSummaries(
host, summaryResolver, symbolResolver, summaryResolver, symbolResolver,
[ [
{ {
symbol: symbolCache.get('/tmp/some_values.ts', 'Values'), symbol: symbolCache.get('/tmp/some_values.ts', 'Values'),
@ -50,7 +51,9 @@ export function main() {
aNumber: 1, aNumber: 1,
aString: 'hello', aString: 'hello',
anArray: [1, 2], anArray: [1, 2],
aStaticSymbol: symbolCache.get('/tmp/some_symbol.ts', 'someName') aStaticSymbol: symbolCache.get('/tmp/some_symbol.ts', 'someName'),
aStaticSymbolWithMembers:
symbolCache.get('/tmp/some_symbol.ts', 'someName', ['someMember']),
} }
}, },
{ {
@ -66,11 +69,11 @@ export function main() {
summaryKind: CompileSummaryKind.Injectable, summaryKind: CompileSummaryKind.Injectable,
type: { type: {
reference: symbolCache.get('/tmp/some_service.ts', 'SomeService'), reference: symbolCache.get('/tmp/some_service.ts', 'SomeService'),
}, }
}]); }]);
const summaries = deserializeSummaries(symbolCache, serializedData); const summaries = deserializeSummaries(symbolCache, serializedData.json).summaries;
expect(summaries.length).toBe(2); expect(summaries.length).toBe(2);
// Note: change from .ts to .d.ts is expected // Note: change from .ts to .d.ts is expected
@ -79,7 +82,9 @@ export function main() {
aNumber: 1, aNumber: 1,
aString: 'hello', aString: 'hello',
anArray: [1, 2], anArray: [1, 2],
aStaticSymbol: symbolCache.get('/tmp/some_symbol.d.ts', 'someName') aStaticSymbol: symbolCache.get('/tmp/some_symbol.d.ts', 'someName'),
aStaticSymbolWithMembers:
symbolCache.get('/tmp/some_symbol.d.ts', 'someName', ['someMember'])
}); });
expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService')); expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService'));
@ -91,8 +96,8 @@ export function main() {
it('should automatically add exported directives / pipes of NgModules that are not source files', it('should automatically add exported directives / pipes of NgModules that are not source files',
() => { () => {
init({}); init();
const externalSerialized = serializeSummaries(host, summaryResolver, symbolResolver, [], [ const externalSerialized = serializeSummaries(summaryResolver, symbolResolver, [], [
<any>{ <any>{
summaryKind: CompileSummaryKind.Pipe, summaryKind: CompileSummaryKind.Pipe,
type: { type: {
@ -107,11 +112,11 @@ export function main() {
} }
]); ]);
init({ init({
'/tmp/external.ngsummary.json': externalSerialized, '/tmp/external.ngsummary.json': externalSerialized.json,
}); });
const serialized = serializeSummaries( const serialized = serializeSummaries(
host, summaryResolver, symbolResolver, [], [<any>{ summaryResolver, symbolResolver, [], [<any>{
summaryKind: CompileSummaryKind.NgModule, summaryKind: CompileSummaryKind.NgModule,
type: {reference: symbolCache.get('/tmp/some_module.ts', 'SomeModule')}, type: {reference: symbolCache.get('/tmp/some_module.ts', 'SomeModule')},
exportedPipes: [ exportedPipes: [
@ -124,7 +129,7 @@ export function main() {
] ]
}]); }]);
const summaries = deserializeSummaries(symbolCache, serialized); const summaries = deserializeSummaries(symbolCache, serialized.json).summaries;
expect(summaries.length).toBe(3); expect(summaries.length).toBe(3);
expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/some_module.d.ts', 'SomeModule')); expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/some_module.d.ts', 'SomeModule'));
expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/external.d.ts', 'SomeExternalDir')); expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/external.d.ts', 'SomeExternalDir'));
@ -134,8 +139,9 @@ export function main() {
it('should automatically add the metadata of referenced symbols that are not in the soure files', it('should automatically add the metadata of referenced symbols that are not in the soure files',
() => { () => {
init();
const externalSerialized = serializeSummaries( const externalSerialized = serializeSummaries(
host, summaryResolver, symbolResolver, summaryResolver, symbolResolver,
[ [
{ {
symbol: symbolCache.get('/tmp/external.ts', 'PROVIDERS'), symbol: symbolCache.get('/tmp/external.ts', 'PROVIDERS'),
@ -154,7 +160,7 @@ export function main() {
}]); }]);
init( init(
{ {
'/tmp/external.ngsummary.json': externalSerialized, '/tmp/external.ngsummary.json': externalSerialized.json,
}, },
{ {
'/tmp/local.ts': ` '/tmp/local.ts': `
@ -164,7 +170,7 @@ export function main() {
{__symbolic: 'module', version: 3, metadata: {'external': 'b'}} {__symbolic: 'module', version: 3, metadata: {'external': 'b'}}
}); });
const serialized = serializeSummaries( const serialized = serializeSummaries(
host, summaryResolver, symbolResolver, [{ summaryResolver, symbolResolver, [{
symbol: symbolCache.get('/tmp/test.ts', 'main'), symbol: symbolCache.get('/tmp/test.ts', 'main'),
metadata: { metadata: {
local: symbolCache.get('/tmp/local.ts', 'local'), local: symbolCache.get('/tmp/local.ts', 'local'),
@ -174,7 +180,7 @@ export function main() {
}], }],
[]); []);
const summaries = deserializeSummaries(symbolCache, serialized); const summaries = deserializeSummaries(symbolCache, serialized.json).summaries;
// Note: local should not show up! // Note: local should not show up!
expect(summaries.length).toBe(4); expect(summaries.length).toBe(4);
expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/test.d.ts', 'main')); expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/test.d.ts', 'main'));
@ -195,5 +201,28 @@ export function main() {
expect(summaries[3].type.type.reference) expect(summaries[3].type.type.reference)
.toBe(symbolCache.get('/tmp/external_svc.d.ts', 'SomeService')); .toBe(symbolCache.get('/tmp/external_svc.d.ts', 'SomeService'));
}); });
it('should create "importAs" names for non source symbols', () => {
init();
const serialized = serializeSummaries(
summaryResolver, symbolResolver, [{
symbol: symbolCache.get('/tmp/test.ts', 'main'),
metadata: [
symbolCache.get('/tmp/external.d.ts', 'lib'),
symbolCache.get('/tmp/external.d.ts', 'lib', ['someMember']),
]
}],
[]);
// Note: no entry for the symbol with members!
expect(serialized.exportAs).toEqual([
{symbol: symbolCache.get('/tmp/external.d.ts', 'lib'), exportAs: 'lib_1'}
]);
const deserialized = deserializeSummaries(symbolCache, serialized.json);
// Note: no entry for the symbol with members!
expect(deserialized.importAs).toEqual([
{symbol: symbolCache.get('/tmp/external.d.ts', 'lib'), importAs: 'lib_1'}
]);
});
}); });
} }

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 {StaticSymbol} from '@angular/compiler/src/aot/static_symbol';
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter'; import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
@ -15,16 +16,17 @@ const someModuleUrl = 'somePackage/somePath';
const anotherModuleUrl = 'somePackage/someOtherPath'; const anotherModuleUrl = 'somePackage/someOtherPath';
const sameModuleIdentifier: CompileIdentifierMetadata = { const sameModuleIdentifier: CompileIdentifierMetadata = {
reference: {name: 'someLocalId', filePath: someModuleUrl} reference: new StaticSymbol(someModuleUrl, 'someLocalId', [])
}; };
const externalModuleIdentifier: CompileIdentifierMetadata = { const externalModuleIdentifier: CompileIdentifierMetadata = {
reference: {name: 'someExternalId', filePath: anotherModuleUrl} reference: new StaticSymbol(anotherModuleUrl, 'someExternalId', [])
}; };
class SimpleJsImportGenerator implements ImportResolver { class SimpleJsImportGenerator implements ImportResolver {
fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string { fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
return importedUrlStr; return importedUrlStr;
} }
getImportAs(symbol: StaticSymbol): StaticSymbol { return null; }
} }
export function main() { export function main() {
@ -33,11 +35,13 @@ export function main() {
// - declaring fields // - declaring fields
describe('JavaScriptEmitter', () => { describe('JavaScriptEmitter', () => {
let importResolver: ImportResolver;
let emitter: JavaScriptEmitter; let emitter: JavaScriptEmitter;
let someVar: o.ReadVarExpr; let someVar: o.ReadVarExpr;
beforeEach(() => { beforeEach(() => {
emitter = new JavaScriptEmitter(new SimpleJsImportGenerator()); importResolver = new SimpleJsImportGenerator();
emitter = new JavaScriptEmitter(importResolver);
someVar = o.variable('someVar'); someVar = o.variable('someVar');
}); });
@ -124,6 +128,16 @@ export function main() {
].join('\n')); ].join('\n'));
}); });
it('should support `importAs` for external identifiers', () => {
spyOn(importResolver, 'getImportAs')
.and.returnValue(new StaticSymbol('somePackage/importAsModule', 'importAsName', []));
expect(emitStmt(o.importExpr(externalModuleIdentifier).toStmt())).toEqual([
`var import0 = re` +
`quire('somePackage/importAsModule');`,
`import0.importAsName;`
].join('\n'));
});
it('should support operators', () => { it('should support operators', () => {
const lhs = o.variable('lhs'); const lhs = o.variable('lhs');
const rhs = o.variable('rhs'); const rhs = o.variable('rhs');

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 {StaticSymbol} from '@angular/compiler/src/aot/static_symbol';
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import {assetUrl, createIdentifier} from '@angular/compiler/src/identifiers'; import {assetUrl, createIdentifier} from '@angular/compiler/src/identifiers';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
@ -262,4 +263,5 @@ export class SimpleJsImportGenerator implements ImportResolver {
fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string { fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
return importedUrlStr; return importedUrlStr;
} }
getImportAs(symbol: StaticSymbol): StaticSymbol { return null; }
} }

View File

@ -74,4 +74,5 @@ class StubReflectorHost implements StaticSymbolResolverHost {
class StubImportResolver extends ImportResolver { class StubImportResolver extends ImportResolver {
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string { return ''; } fileNameToModuleName(importedFilePath: string, containingFilePath: string): string { return ''; }
getImportAs(symbol: StaticSymbol): StaticSymbol { return null; }
} }

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 {StaticSymbol} from '@angular/compiler/src/aot/static_symbol';
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 {ImportResolver} from '@angular/compiler/src/output/path_util'; import {ImportResolver} from '@angular/compiler/src/output/path_util';
@ -15,38 +16,42 @@ const someModuleUrl = 'somePackage/somePath';
const anotherModuleUrl = 'somePackage/someOtherPath'; const anotherModuleUrl = 'somePackage/someOtherPath';
const sameModuleIdentifier: CompileIdentifierMetadata = { const sameModuleIdentifier: CompileIdentifierMetadata = {
reference: {name: 'someLocalId', filePath: someModuleUrl} reference: new StaticSymbol(someModuleUrl, 'someLocalId', [])
}; };
const externalModuleIdentifier: CompileIdentifierMetadata = { const externalModuleIdentifier: CompileIdentifierMetadata = {
reference: {name: 'someExternalId', filePath: anotherModuleUrl} reference: new StaticSymbol(anotherModuleUrl, 'someExternalId', [])
}; };
class SimpleJsImportGenerator implements ImportResolver { class SimpleJsImportGenerator implements ImportResolver {
fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string { fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
return importedUrlStr; return importedUrlStr;
} }
getImportAs(symbol: StaticSymbol): StaticSymbol { return null; }
} }
export function main() { export function main() {
// Note supported features of our OutputAsti n TS: // Not supported features of our OutputAst in TS:
// - real `const` like in Dart // - real `const` like in Dart
// - final fields // - final fields
describe('TypeScriptEmitter', () => { describe('TypeScriptEmitter', () => {
let importResolver: ImportResolver;
let emitter: TypeScriptEmitter; let emitter: TypeScriptEmitter;
let someVar: o.ReadVarExpr; let someVar: o.ReadVarExpr;
beforeEach(() => { beforeEach(() => {
emitter = new TypeScriptEmitter(new SimpleJsImportGenerator()); importResolver = new SimpleJsImportGenerator();
emitter = new TypeScriptEmitter(importResolver);
someVar = o.variable('someVar'); someVar = o.variable('someVar');
}); });
function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string { function emitStmt(stmt: o.Statement | o.Statement[], exportedVars: string[] = null): string {
if (!exportedVars) { if (!exportedVars) {
exportedVars = []; exportedVars = [];
} }
return emitter.emitStatements(someModuleUrl, [stmt], exportedVars); const stmts = Array.isArray(stmt) ? stmt : [stmt];
return emitter.emitStatements(someModuleUrl, stmts, exportedVars);
} }
it('should declare variables', () => { it('should declare variables', () => {
@ -59,6 +64,79 @@ export function main() {
.toEqual(`var someVar:number = 1;`); .toEqual(`var someVar:number = 1;`);
}); });
describe('declare variables with ExternExpressions as values', () => {
it('should create no reexport if the identifier is in the same module', () => {
// identifier is in the same module -> no reexport
expect(emitStmt(someVar.set(o.importExpr(sameModuleIdentifier)).toDeclStmt(), ['someVar']))
.toEqual('export var someVar:any = someLocalId;');
});
it('should create no reexport if the variable is not exported', () => {
expect(emitStmt(someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt())).toEqual([
`import * as import0 from 'somePackage/someOtherPath';`,
`var someVar:any = import0.someExternalId;`
].join('\n'));
});
it('should create no reexport if the variable is typed', () => {
expect(emitStmt(
someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt(o.DYNAMIC_TYPE),
['someVar']))
.toEqual([
`import * as import0 from 'somePackage/someOtherPath';`,
`export var someVar:any = import0.someExternalId;`
].join('\n'));
});
it('should create no reexport if the identifier has members', () => {
const externalModuleIdentifierWithMembers: CompileIdentifierMetadata = {
reference: new StaticSymbol(anotherModuleUrl, 'someExternalId', ['a'])
};
expect(emitStmt(
someVar.set(o.importExpr(externalModuleIdentifierWithMembers)).toDeclStmt(),
['someVar']))
.toEqual([
`import * as import0 from 'somePackage/someOtherPath';`,
`export var someVar:any = import0.someExternalId.a;`
].join('\n'));
});
it('should create a reexport', () => {
expect(
emitStmt(someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt(), ['someVar']))
.toEqual([
`export {someExternalId as someVar} from 'somePackage/someOtherPath';`, ``
].join('\n'));
});
it('should create multiple reexports from the same file', () => {
const someVar2 = o.variable('someVar2');
const externalModuleIdentifier2: CompileIdentifierMetadata = {
reference: new StaticSymbol(anotherModuleUrl, 'someExternalId2', [])
};
expect(emitStmt(
[
someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt(),
someVar2.set(o.importExpr(externalModuleIdentifier2)).toDeclStmt()
],
['someVar', 'someVar2']))
.toEqual([
`export {someExternalId as someVar,someExternalId2 as someVar2} from 'somePackage/someOtherPath';`,
``
].join('\n'));
});
it('should use `importAs` for reexports', () => {
spyOn(importResolver, 'getImportAs')
.and.returnValue(new StaticSymbol('somePackage/importAsModule', 'importAsName', []));
expect(
emitStmt(someVar.set(o.importExpr(externalModuleIdentifier)).toDeclStmt(), ['someVar']))
.toEqual([
`export {importAsName as someVar} from 'somePackage/importAsModule';`, ``
].join('\n'));
});
});
it('should read and write variables', () => { it('should read and write variables', () => {
expect(emitStmt(someVar.toStmt())).toEqual(`someVar;`); expect(emitStmt(someVar.toStmt())).toEqual(`someVar;`);
expect(emitStmt(someVar.set(o.literal(1)).toStmt())).toEqual(`someVar = 1;`); expect(emitStmt(someVar.set(o.literal(1)).toStmt())).toEqual(`someVar = 1;`);
@ -134,6 +212,14 @@ export function main() {
].join('\n')); ].join('\n'));
}); });
it('should support `importAs` for external identifiers', () => {
spyOn(importResolver, 'getImportAs')
.and.returnValue(new StaticSymbol('somePackage/importAsModule', 'importAsName', []));
expect(emitStmt(o.importExpr(externalModuleIdentifier).toStmt())).toEqual([
`import * as import0 from 'somePackage/importAsModule';`, `import0.importAsName;`
].join('\n'));
});
it('should support operators', () => { it('should support operators', () => {
const lhs = o.variable('lhs'); const lhs = o.variable('lhs');
const rhs = o.variable('rhs'); const rhs = o.variable('rhs');
@ -332,6 +418,16 @@ export function main() {
].join('\n')); ].join('\n'));
}); });
it('should support `importAs` for external types', () => {
spyOn(importResolver, 'getImportAs')
.and.returnValue(new StaticSymbol('somePackage/importAsModule', 'importAsName', []));
const writeVarExpr = o.variable('a').set(o.NULL_EXPR);
expect(emitStmt(writeVarExpr.toDeclStmt(o.importType(externalModuleIdentifier)))).toEqual([
`import * as import0 from 'somePackage/importAsModule';`,
`var a:import0.importAsName = (null as any);`
].join('\n'));
});
it('should support expression types', () => { it('should support expression types', () => {
expect(emitStmt(o.variable('a') expect(emitStmt(o.variable('a')
.set(o.NULL_EXPR) .set(o.NULL_EXPR)

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from './metadata';
/** /**
* Creates a token that can be used in a DI Provider. * Creates a token that can be used in a DI Provider.
* *
@ -30,7 +28,6 @@ import {Injectable} from './metadata';
* error messages. * error messages.
* @stable * @stable
*/ */
@Injectable() // so that metadata is gathered for this class
export class OpaqueToken { export class OpaqueToken {
constructor(private _desc: string) {} constructor(private _desc: string) {}

View File

@ -95,9 +95,12 @@ const EMPTY_CONTEXT = new Object();
* @stable * @stable
*/ */
export class ComponentFactory<C> { export class ComponentFactory<C> {
/** @internal */
_viewClass: Type<AppView<any>>;
constructor( constructor(
public selector: string, private _viewClass: Type<AppView<any>>, public selector: string, _viewClass: Type<AppView<any>>, private _componentType: Type<any>) {
private _componentType: Type<any>) {} this._viewClass = _viewClass;
}
get componentType(): Type<any> { return this._componentType; } get componentType(): Type<any> { return this._componentType; }

View File

@ -14,9 +14,11 @@ import {isPresent, looseIdentical} from '../facade/lang';
import {ViewEncapsulation} from '../metadata/view'; import {ViewEncapsulation} from '../metadata/view';
import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api'; import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api';
import {Sanitizer} from '../security'; import {Sanitizer} from '../security';
import {Type} from '../type';
import {VERSION} from '../version'; import {VERSION} from '../version';
import {NgZone} from '../zone/ng_zone'; import {NgZone} from '../zone/ng_zone';
import {ComponentFactory} from './component_factory';
import {ExpressionChangedAfterItHasBeenCheckedError} from './errors'; import {ExpressionChangedAfterItHasBeenCheckedError} from './errors';
import {AppView} from './view'; import {AppView} from './view';
@ -652,3 +654,10 @@ export class InlineArrayDynamic<T> implements InlineArray<T> {
} }
export const EMPTY_INLINE_ARRAY: InlineArray<any> = new InlineArray0(); export const EMPTY_INLINE_ARRAY: InlineArray<any> = new InlineArray0();
/**
* This is a private API only used by the compiler to read the view class.
*/
export function getComponentFactoryViewClass(componentFactory: ComponentFactory<any>): Type<any> {
return componentFactory._viewClass;
}

View File

@ -122,7 +122,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
result = this._resolver = new CompileMetadataResolver( result = this._resolver = new CompileMetadataResolver(
moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(), moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(),
elementSchemaRegistry, directiveNormalizer, this.reflector, elementSchemaRegistry, directiveNormalizer, this._staticSymbolCache, this.reflector,
(error, type) => this.collectError(error, type && type.filePath)); (error, type) => this.collectError(error, type && type.filePath));
} }
return result; return result;
@ -397,7 +397,8 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
const summaryResolver = new AotSummaryResolver( const summaryResolver = new AotSummaryResolver(
{ {
loadSummary(filePath: string) { return null; }, loadSummary(filePath: string) { return null; },
isSourceFile(sourceFilePath: string) { return true; } isSourceFile(sourceFilePath: string) { return true; },
getOutputFileName(sourceFilePath: string) { return null; }
}, },
this._staticSymbolCache); this._staticSymbolCache);
result = this._staticSymbolResolver = new StaticSymbolResolver( result = this._staticSymbolResolver = new StaticSymbolResolver(