refactor(compiler): store metadata of top level symbols also in summaries (#13289)

This allows a build using summaries to not need .metadata.json files at all
any more.

Part of #12787
This commit is contained in:
Tobias Bosch 2016-12-15 09:12:40 -08:00 committed by Igor Minar
parent 01ca2db6ae
commit 33910ddfc9
52 changed files with 1702 additions and 1011 deletions

View File

@ -34,9 +34,9 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT ex (#PCDATA)> <!ELEMENT ex (#PCDATA)>
]> ]>
<messagebundle> <messagebundle>
<msg id="3772663375917578720">other-3rdP-component</msg>
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg> <msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
<msg id="3492007542396725315">Welcome</msg> <msg id="3492007542396725315">Welcome</msg>
<msg id="3772663375917578720">other-3rdP-component</msg>
</messagebundle> </messagebundle>
`; `;
@ -44,10 +44,6 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template"> <file source-language="en" datatype="plaintext" original="ng2.template">
<body> <body>
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
<source>other-3rdP-component</source>
<target/>
</trans-unit>
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html"> <trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
<source>translate me</source> <source>translate me</source>
<target/> <target/>
@ -58,6 +54,10 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<source>Welcome</source> <source>Welcome</source>
<target/> <target/>
</trans-unit> </trans-unit>
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
<source>other-3rdP-component</source>
<target/>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@ -52,12 +52,9 @@ function codeGenTest() {
const config = tsc.readConfiguration(project, basePath); const config = tsc.readConfiguration(project, basePath);
const hostContext = new NodeCompilerHostContext(); const hostContext = new NodeCompilerHostContext();
const delegateHost = ts.createCompilerHost(config.parsed.options, true); const delegateHost = ts.createCompilerHost(config.parsed.options, true);
const host: ts.CompilerHost = Object.assign({}, delegateHost, { const host: ts.CompilerHost = Object.assign(
writeFile: (fileName: string, ...rest: any[]) => { {}, delegateHost,
wroteFiles.push(fileName); {writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
return delegateHost.writeFile.call(delegateHost, fileName, ...rest);
}
});
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host); const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
config.ngOptions.basePath = basePath; config.ngOptions.basePath = basePath;

View File

@ -27,9 +27,14 @@ function main() {
const basePath = path.resolve(__dirname, '..'); const basePath = path.resolve(__dirname, '..');
const project = path.resolve(basePath, 'tsconfig-build.json'); const project = path.resolve(basePath, 'tsconfig-build.json');
const readFiles: string[] = []; const readFiles: string[] = [];
const writtenFiles: {fileName: string, content: string}[] = [];
class AssertingHostContext extends NodeCompilerHostContext { class AssertingHostContext extends NodeCompilerHostContext {
readFile(fileName: string): string { readFile(fileName: string): string {
if (/.*\/node_modules\/.*/.test(fileName) && !/.*ngsummary\.json$/.test(fileName)) {
// Only allow to read summaries from node_modules
return null;
}
readFiles.push(path.relative(basePath, fileName)); readFiles.push(path.relative(basePath, fileName));
return super.readFile(fileName); return super.readFile(fileName);
} }
@ -45,16 +50,29 @@ function main() {
config.ngOptions.generateCodeForLibraries = false; config.ngOptions.generateCodeForLibraries = false;
console.log(`>>> running codegen for ${project}`); console.log(`>>> running codegen for ${project}`);
codegen(config, (host) => new AssertingHostContext()) codegen(
config,
(host) => {
host.writeFile = (fileName: string, content: string) => {
fileName = path.relative(basePath, fileName);
writtenFiles.push({fileName, content});
};
return new AssertingHostContext();
})
.then((exitCode: any) => { .then((exitCode: any) => {
console.log(`>>> codegen done, asserting read files`); console.log(`>>> codegen done, asserting read files`);
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/); assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.metadata.json$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/); assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/); assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/); assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
assertSomeFileMatch(readFiles, /^src\/.*\.html$/); assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
assertSomeFileMatch(readFiles, /^src\/.*\.css$/); assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
console.log(`>>> asserting written files`);
assertWrittenFile(writtenFiles, /^src\/module\.ngfactory\.ts$/, /class MainModuleInjector/);
console.log(`done, no errors.`); console.log(`done, no errors.`);
process.exit(exitCode); process.exit(exitCode);
}) })
@ -97,4 +115,11 @@ function assertNoFileMatch(fileNames: string[], pattern: RegExp) {
`Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`); `Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`);
} }
function assertWrittenFile(
files: {fileName: string, content: string}[], filePattern: RegExp, contentPattern: RegExp) {
assert(
files.some(file => filePattern.test(file.fileName) && contentPattern.test(file.content)),
`Expected some written files for ${filePattern} and content ${contentPattern}`);
}
main(); main();

View File

@ -21,9 +21,7 @@ import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './
import {PathMappedCompilerHost} from './path_mapped_compiler_host'; import {PathMappedCompilerHost} from './path_mapped_compiler_host';
import {Console} from './private_import_core'; import {Console} from './private_import_core';
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
const GENERATED_META_FILES = /\.json$/; const GENERATED_META_FILES = /\.json$/;
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
const PREAMBLE = `/** const PREAMBLE = `/**
* @fileoverview This file is generated by the Angular 2 template compiler. * @fileoverview This file is generated by the Angular 2 template compiler.
@ -102,14 +100,8 @@ export class CodeGenerator {
debug: options.debug === true, debug: options.debug === true,
translations: transContent, translations: transContent,
i18nFormat: cliOptions.i18nFormat, i18nFormat: cliOptions.i18nFormat,
locale: cliOptions.locale, locale: cliOptions.locale
excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES :
GENERATED_FILES
}); });
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost); return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
} }
} }
export function excludeFilePattern(options: AngularCompilerOptions): RegExp {
return options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
}

View File

@ -16,6 +16,8 @@ const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
const DTS = /\.d\.ts$/; const DTS = /\.d\.ts$/;
const NODE_MODULES = '/node_modules/'; const NODE_MODULES = '/node_modules/';
const IS_GENERATED = /\.(ngfactory|ngstyle)$/; const IS_GENERATED = /\.(ngfactory|ngstyle)$/;
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
export interface CompilerHostContext extends ts.ModuleResolutionHost { export interface CompilerHostContext extends ts.ModuleResolutionHost {
readResource(fileName: string): Promise<string>; readResource(fileName: string): Promise<string>;
@ -28,6 +30,7 @@ export class CompilerHost implements AotCompilerHost {
protected basePath: string; protected basePath: string;
private genDir: string; private genDir: string;
private resolverCache = new Map<string, ModuleMetadata[]>(); private resolverCache = new Map<string, ModuleMetadata[]>();
protected resolveModuleNameHost: CompilerHostContext;
constructor( constructor(
protected program: ts.Program, protected options: AngularCompilerOptions, protected program: ts.Program, protected options: AngularCompilerOptions,
@ -38,12 +41,31 @@ export class CompilerHost implements AotCompilerHost {
const genPath: string = path.relative(this.basePath, this.genDir); const genPath: string = path.relative(this.basePath, this.genDir);
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..'); this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
this.resolveModuleNameHost = Object.create(this.context);
// When calling ts.resolveModuleName,
// additional allow checks for .d.ts files to be done based on
// checks for .ngsummary.json files,
// so that our codegen depends on fewer inputs and requires to be called
// less often.
// This is needed as we use ts.resolveModuleName in reflector_host
// and it should be able to resolve summary file names.
this.resolveModuleNameHost.fileExists = (fileName: string): boolean => {
if (this.context.fileExists(fileName)) {
return true;
}
if (DTS.test(fileName)) {
const base = fileName.substring(0, fileName.length - 5);
return this.context.fileExists(base + '.ngsummary.json');
}
return false;
};
} }
// We use absolute paths on disk as canonical. // We use absolute paths on disk as canonical.
getCanonicalFileName(fileName: string): string { return fileName; } getCanonicalFileName(fileName: string): string { return fileName; }
moduleNameToFileName(m: string, containingFile: string) { moduleNameToFileName(m: string, containingFile: string): string|null {
if (!containingFile || !containingFile.length) { if (!containingFile || !containingFile.length) {
if (m.indexOf('.') === 0) { if (m.indexOf('.') === 0) {
throw new Error('Resolution of relative paths requires a containing file.'); throw new Error('Resolution of relative paths requires a containing file.');
@ -53,7 +75,8 @@ export class CompilerHost implements AotCompilerHost {
} }
m = m.replace(EXT, ''); m = m.replace(EXT, '');
const resolved = const resolved =
ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context) ts.resolveModuleName(
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
.resolvedModule; .resolvedModule;
return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null; return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
}; };
@ -213,11 +236,21 @@ export class CompilerHost implements AotCompilerHost {
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); } loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
loadSummary(filePath: string): string { return this.context.readFile(filePath); } loadSummary(filePath: string): string|null {
if (this.context.fileExists(filePath)) {
return this.context.readFile(filePath);
}
}
getOutputFileName(sourceFilePath: string): string { getOutputFileName(sourceFilePath: string): string {
return sourceFilePath.replace(EXT, '') + '.d.ts'; return sourceFilePath.replace(EXT, '') + '.d.ts';
} }
isSourceFile(filePath: string): boolean {
const excludeRegex =
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
return !excludeRegex.test(filePath);
}
} }
export class CompilerHostContextAdapter { export class CompilerHostContextAdapter {

View File

@ -17,7 +17,6 @@ import * as compiler from '@angular/compiler';
import * as tsc from '@angular/tsc-wrapped'; import * as tsc from '@angular/tsc-wrapped';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {excludeFilePattern} from './codegen';
import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host'; import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';
export class Extractor { export class Extractor {
@ -36,8 +35,7 @@ export class Extractor {
if (!ngCompilerHost) if (!ngCompilerHost)
ngCompilerHost = ngCompilerHost =
new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost)); new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
const {extractor: ngExtractor} = compiler.Extractor.create( const {extractor: ngExtractor} = compiler.Extractor.create(ngCompilerHost);
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
return new Extractor(ngExtractor, ngCompilerHost, program); return new Extractor(ngExtractor, ngCompilerHost, program);
} }
} }

View File

@ -13,7 +13,7 @@
* something else. * something else.
*/ */
import {AotCompilerHost, StaticReflector} from '@angular/compiler'; import {AotCompilerHost, AotSummaryResolver, StaticReflector, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped'; import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
import * as ts from 'typescript'; import * as ts from 'typescript';
@ -111,7 +111,10 @@ export class NgTools_InternalApi_NG_2 {
new PathMappedCompilerHost(program, angularCompilerOptions, moduleResolutionHost) : new PathMappedCompilerHost(program, angularCompilerOptions, moduleResolutionHost) :
new CompilerHost(program, angularCompilerOptions, moduleResolutionHost); new CompilerHost(program, angularCompilerOptions, moduleResolutionHost);
const staticReflector = new StaticReflector(ngCompilerHost); const symbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(ngCompilerHost, symbolCache);
const symbolResolver = new StaticSymbolResolver(ngCompilerHost, symbolCache, summaryResolver);
const staticReflector = new StaticReflector(symbolResolver);
const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector); const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector);
return Object.keys(routeMap).reduce( return Object.keys(routeMap).reduce(

View File

@ -29,13 +29,17 @@ describe('compiler-cli', () => {
"types": [], "types": [],
"outDir": "built", "outDir": "built",
"declaration": true, "declaration": true,
"module": "es2015" "module": "es2015",
"moduleResolution": "node"
}, },
"angularCompilerOptions": { "angularCompilerOptions": {
"annotateForClosureCompiler": true "annotateForClosureCompiler": true
}, },
"files": ["test.ts"] "files": ["test.ts"]
}`); }`);
const nodeModulesPath = path.resolve(basePath, 'node_modules');
fs.mkdirSync(nodeModulesPath);
fs.symlinkSync(path.resolve(__dirname, '..', '..'), path.resolve(nodeModulesPath, '@angular'));
}); });
// Restore reflector since AoT compiler will update it with a new static reflector // Restore reflector since AoT compiler will update it with a new static reflector

View File

@ -32,7 +32,9 @@ export * from './src/aot/compiler_host';
export * from './src/aot/static_reflector'; export * from './src/aot/static_reflector';
export * from './src/aot/static_reflection_capabilities'; export * from './src/aot/static_reflection_capabilities';
export * from './src/aot/static_symbol'; export * from './src/aot/static_symbol';
export * from './src/aot/static_symbol_resolver';
export * from './src/aot/summary_resolver'; export * from './src/aot/summary_resolver';
export * from './src/summary_resolver';
export {JitCompiler} from './src/jit/compiler'; export {JitCompiler} from './src/jit/compiler';
export * from './src/jit/compiler_factory'; export * from './src/jit/compiler_factory';
export * from './src/url_resolver'; export * from './src/url_resolver';

View File

@ -6,11 +6,10 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from '@angular/core';
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata, identifierName} from '../compile_metadata'; import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata, identifierName} from '../compile_metadata';
import {StringMapWrapper} from '../facade/collection'; import {StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
import {ParseError} from '../parse_util'; import {ParseError} from '../parse_util';
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core'; import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
import {ElementSchemaRegistry} from '../schema/element_schema_registry'; import {ElementSchemaRegistry} from '../schema/element_schema_registry';
@ -35,7 +34,7 @@ export class AnimationEntryParseResult {
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {} constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
} }
@Injectable() @CompilerInjectable()
export class AnimationParser { export class AnimationParser {
constructor(private _schema: ElementSchemaRegistry) {} constructor(private _schema: ElementSchemaRegistry) {}

View File

@ -20,34 +20,34 @@ import {NgModuleCompiler} from '../ng_module_compiler';
import {OutputEmitter} from '../output/abstract_emitter'; import {OutputEmitter} from '../output/abstract_emitter';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler'; import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
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, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
import {AotCompilerOptions} from './compiler_options'; import {AotCompilerHost} from './compiler_host';
import {GeneratedFile} from './generated_file'; import {GeneratedFile} from './generated_file';
import {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol'; import {StaticSymbol} from './static_symbol';
import {AotSummaryResolver} from './summary_resolver'; import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {filterFileByPatterns} from './utils'; import {serializeSummaries, summaryFileName} from './summary_serializer';
export class AotCompiler { export class AotCompiler {
private _animationCompiler = new AnimationCompiler(); private _animationCompiler = new AnimationCompiler();
constructor( constructor(
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser, private _host: AotCompilerHost, private _metadataResolver: CompileMetadataResolver,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _dirWrapperCompiler: DirectiveWrapperCompiler, private _viewCompiler: ViewCompiler, private _dirWrapperCompiler: DirectiveWrapperCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter, private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
private _summaryResolver: AotSummaryResolver, private _localeId: string, private _summaryResolver: SummaryResolver<StaticSymbol>, private _localeId: string,
private _translationFormat: string, private _animationParser: AnimationParser, private _translationFormat: string, private _animationParser: AnimationParser,
private _staticReflector: StaticReflector, private _options: AotCompilerOptions) {} private _symbolResolver: StaticSymbolResolver) {}
clearCache() { this._metadataResolver.clearCache(); } clearCache() { this._metadataResolver.clearCache(); }
compileAll(rootFiles: string[]): Promise<GeneratedFile[]> { compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options); const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
const {ngModuleByPipeOrDirective, files, ngModules} = const {ngModuleByPipeOrDirective, files, ngModules} =
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver); analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
return Promise return Promise
.all(ngModules.map( .all(ngModules.map(
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata( ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
@ -56,27 +56,21 @@ export class AotCompiler {
const sourceModules = files.map( const sourceModules = files.map(
file => this._compileSrcFile( file => this._compileSrcFile(
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes, file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
file.ngModules)); file.ngModules, file.injectables));
return ListWrapper.flatten(sourceModules); return ListWrapper.flatten(sourceModules);
}); });
} }
private _compileSrcFile( private _compileSrcFile(
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>, srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], pipes: StaticSymbol[], directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
ngModules: 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[] = [];
// write summary files generatedFiles.push(this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables));
const summaries: CompileTypeSummary[] = [
...ngModules.map(ref => this._metadataResolver.getNgModuleSummary(ref)),
...directives.map(ref => this._metadataResolver.getDirectiveSummary(ref)),
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref))
];
generatedFiles.push(this._summaryResolver.serializeSummaries(srcFileUrl, summaries));
// compile all ng modules // compile all ng modules
exportedVars.push( exportedVars.push(
@ -121,6 +115,22 @@ export class AotCompiler {
return generatedFiles; return generatedFiles;
} }
private _createSummary(
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile {
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
.map(symbol => this._symbolResolver.resolveSymbol(symbol));
const typeSummaries = [
...ngModules.map(ref => this._metadataResolver.getNgModuleSummary(ref)),
...directives.map(ref => this._metadataResolver.getDirectiveSummary(ref)),
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)),
...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref))
];
const json = serializeSummaries(
this._host, this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json);
}
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string { private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
const ngModule = this._metadataResolver.getNgModuleMetadata(ngModuleType); const ngModule = this._metadataResolver.getNgModuleMetadata(ngModuleType);
const providers: CompileProviderMetadata[] = []; const providers: CompileProviderMetadata[] = [];
@ -142,7 +152,7 @@ export class AotCompiler {
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers); const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
appCompileResult.dependencies.forEach((dep) => { appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.reference = this._staticReflector.getStaticSymbol( dep.placeholder.reference = this._symbolResolver.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp)); _ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
}); });
@ -163,7 +173,7 @@ export class AotCompiler {
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string, compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string { targetStatements: o.Statement[]): string {
const hostMeta = createHostComponentMeta( const hostMeta = createHostComponentMeta(
this._staticReflector.getStaticSymbol( this._symbolResolver.getStaticSymbol(
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`), identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
compMeta); compMeta);
const hostViewFactoryVar = this._compileComponent( const hostViewFactoryVar = this._compileComponent(
@ -206,16 +216,16 @@ export class AotCompiler {
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations); compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
if (componentStyles) { if (componentStyles) {
targetStatements.push( targetStatements.push(
..._resolveStyleStatements(this._staticReflector, 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._staticReflector, viewResult)); targetStatements.push(..._resolveViewStatements(this._symbolResolver, viewResult));
return viewResult.viewClassVar; return viewResult.viewClassVar;
} }
private _codgenStyles( private _codgenStyles(
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile { fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile {
_resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix); _resolveStyleStatements(this._symbolResolver, stylesCompileResult, fileSuffix);
return this._codegenSourceModule( return this._codegenSourceModule(
fileUrl, _stylesModuleUrl( fileUrl, _stylesModuleUrl(
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix), stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
@ -232,7 +242,7 @@ export class AotCompiler {
} }
function _resolveViewStatements( function _resolveViewStatements(
reflector: StaticReflector, compileResult: ViewCompileResult): o.Statement[] { reflector: StaticSymbolResolver, compileResult: ViewCompileResult): o.Statement[] {
compileResult.dependencies.forEach((dep) => { compileResult.dependencies.forEach((dep) => {
if (dep instanceof ViewClassDependency) { if (dep instanceof ViewClassDependency) {
const vfd = <ViewClassDependency>dep; const vfd = <ViewClassDependency>dep;
@ -253,7 +263,7 @@ function _resolveViewStatements(
function _resolveStyleStatements( function _resolveStyleStatements(
reflector: StaticReflector, compileResult: CompiledStylesheet, reflector: StaticSymbolResolver, compileResult: CompiledStylesheet,
fileSuffix: string): o.Statement[] { fileSuffix: string): o.Statement[] {
compileResult.dependencies.forEach((dep) => { compileResult.dependencies.forEach((dep) => {
dep.valuePlaceholder.reference = reflector.getStaticSymbol( dep.valuePlaceholder.reference = reflector.getStaticSymbol(
@ -303,26 +313,27 @@ export interface NgAnalyzedModules {
srcUrl: string, srcUrl: string,
directives: StaticSymbol[], directives: StaticSymbol[],
pipes: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[] ngModules: StaticSymbol[],
injectables: StaticSymbol[]
}>; }>;
symbolsMissingModule?: StaticSymbol[]; symbolsMissingModule?: StaticSymbol[];
} }
export interface NgAnalyzeModulesHost { isSourceFile(filePath: string): boolean; }
// Returns all the source files and a mapping from modules to directives // Returns all the source files and a mapping from modules to directives
export function analyzeNgModules( export function analyzeNgModules(
programStaticSymbols: StaticSymbol[], programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
metadataResolver: CompileMetadataResolver): NgAnalyzedModules { metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const {ngModules, symbolsMissingModule} = const {ngModules, symbolsMissingModule} =
_createNgModules(programStaticSymbols, options, metadataResolver); _createNgModules(programStaticSymbols, host, metadataResolver);
return _analyzeNgModules(ngModules, symbolsMissingModule); return _analyzeNgModules(programStaticSymbols, ngModules, symbolsMissingModule, metadataResolver);
} }
export function analyzeAndValidateNgModules( export function analyzeAndValidateNgModules(
programStaticSymbols: StaticSymbol[], programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
metadataResolver: CompileMetadataResolver): NgAnalyzedModules { metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const result = analyzeNgModules(programStaticSymbols, options, metadataResolver); const result = analyzeNgModules(programStaticSymbols, host, metadataResolver);
if (result.symbolsMissingModule && result.symbolsMissingModule.length) { if (result.symbolsMissingModule && result.symbolsMissingModule.length) {
const messages = result.symbolsMissingModule.map( const messages = result.symbolsMissingModule.map(
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`); s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
@ -332,16 +343,27 @@ export function analyzeAndValidateNgModules(
} }
function _analyzeNgModules( function _analyzeNgModules(
ngModuleMetas: CompileNgModuleMetadata[], programSymbols: StaticSymbol[], ngModuleMetas: CompileNgModuleMetadata[],
symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules { symbolsMissingModule: StaticSymbol[],
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>(); const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule)); ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>(); const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
const ngModulesByFile = new Map<string, StaticSymbol[]>(); const ngModulesByFile = new Map<string, StaticSymbol[]>();
const ngDirectivesByFile = new Map<string, StaticSymbol[]>(); const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
const ngPipesByFile = new Map<string, StaticSymbol[]>(); const ngPipesByFile = new Map<string, StaticSymbol[]>();
const ngInjectablesByFile = new Map<string, StaticSymbol[]>();
const filePaths = new Set<string>(); const filePaths = new Set<string>();
// Make sure we produce an analyzed file for each input file
programSymbols.forEach((symbol) => {
const filePath = symbol.filePath;
filePaths.add(filePath);
if (metadataResolver.isInjectable(symbol)) {
ngInjectablesByFile.set(filePath, (ngInjectablesByFile.get(filePath) || []).concat(symbol));
}
});
// Looping over all modules to construct: // Looping over all modules to construct:
// - a map from file to modules `ngModulesByFile`, // - a map from file to modules `ngModulesByFile`,
// - a map from file to directives `ngDirectivesByFile`, // - a map from file to directives `ngDirectivesByFile`,
@ -369,17 +391,20 @@ function _analyzeNgModules(
}); });
}); });
const files: const files: {
{srcUrl: string, srcUrl: string,
directives: StaticSymbol[], directives: StaticSymbol[],
pipes: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[]}[] = []; ngModules: StaticSymbol[],
injectables: StaticSymbol[]
}[] = [];
filePaths.forEach((srcUrl) => { filePaths.forEach((srcUrl) => {
const directives = ngDirectivesByFile.get(srcUrl) || []; const directives = ngDirectivesByFile.get(srcUrl) || [];
const pipes = ngPipesByFile.get(srcUrl) || []; const pipes = ngPipesByFile.get(srcUrl) || [];
const ngModules = ngModulesByFile.get(srcUrl) || []; const ngModules = ngModulesByFile.get(srcUrl) || [];
files.push({srcUrl, directives, pipes, ngModules}); const injectables = ngInjectablesByFile.get(srcUrl) || [];
files.push({srcUrl, directives, pipes, ngModules, injectables});
}); });
return { return {
@ -392,29 +417,20 @@ function _analyzeNgModules(
} }
export function extractProgramSymbols( export function extractProgramSymbols(
staticReflector: StaticReflector, files: string[], staticSymbolResolver: StaticSymbolResolver, files: string[],
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] { host: NgAnalyzeModulesHost): StaticSymbol[] {
const staticSymbols: StaticSymbol[] = []; const staticSymbols: StaticSymbol[] = [];
files.filter(fileName => filterFileByPatterns(fileName, options)).forEach(sourceFile => { files.filter(fileName => host.isSourceFile(fileName)).forEach(sourceFile => {
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile); staticSymbolResolver.getSymbolsOf(sourceFile).forEach((symbol) => {
if (!moduleMetadata) { const resolvedSymbol = staticSymbolResolver.resolveSymbol(symbol);
console.error(`WARNING: no metadata found for ${sourceFile}`); const symbolMeta = resolvedSymbol.metadata;
return; if (symbolMeta) {
} if (symbolMeta.__symbolic != 'error') {
// Ignore symbols that are only included to record error information.
const metadata = moduleMetadata['metadata']; staticSymbols.push(resolvedSymbol.symbol);
}
if (!metadata) {
return;
}
for (const symbol of Object.keys(metadata)) {
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
// Ignore symbols that are only included to record error information.
continue;
} }
staticSymbols.push(staticReflector.getStaticSymbol(sourceFile, symbol)); });
}
}); });
return staticSymbols; return staticSymbols;
@ -424,8 +440,7 @@ export function extractProgramSymbols(
// that all directives / pipes that are present in the program // that all directives / pipes that are present in the program
// are also declared by a module. // are also declared by a module.
function _createNgModules( function _createNgModules(
programStaticSymbols: StaticSymbol[], programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
metadataResolver: CompileMetadataResolver): metadataResolver: CompileMetadataResolver):
{ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} { {ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} {
const ngModules = new Map<any, CompileNgModuleMetadata>(); const ngModules = new Map<any, CompileNgModuleMetadata>();
@ -433,7 +448,7 @@ function _createNgModules(
const ngModulePipesAndDirective = new Set<StaticSymbol>(); const ngModulePipesAndDirective = new Set<StaticSymbol>();
const addNgModule = (staticSymbol: any) => { const addNgModule = (staticSymbol: any) => {
if (ngModules.has(staticSymbol) || !filterFileByPatterns(staticSymbol.filePath, options)) { if (ngModules.has(staticSymbol) || !host.isSourceFile(staticSymbol.filePath)) {
return false; return false;
} }
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false); const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);

View File

@ -34,8 +34,11 @@ 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 {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.
*/ */
@ -44,7 +47,10 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
let translations: string = options.translations || ''; let translations: string = options.translations || '';
const urlResolver = createOfflineCompileUrlResolver(); const urlResolver = createOfflineCompileUrlResolver();
const staticReflector = new StaticReflector(compilerHost); const symbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache);
const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver);
const staticReflector = new StaticReflector(symbolResolver);
StaticAndDynamicReflectionCapabilities.install(staticReflector); StaticAndDynamicReflectionCapabilities.install(staticReflector);
const htmlParser = new I18NHtmlParser(new HtmlParser(), translations, options.i18nFormat); const htmlParser = new I18NHtmlParser(new HtmlParser(), translations, options.i18nFormat);
const config = new CompilerConfig({ const config = new CompilerConfig({
@ -60,17 +66,16 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
const console = new Console(); const console = new Console();
const tmplParser = const tmplParser =
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []); new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
const summaryResolver = new AotSummaryResolver(compilerHost, staticReflector, options);
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); staticReflector);
// TODO(vicb): do not pass options.i18nFormat here // TODO(vicb): do not pass options.i18nFormat here
const compiler = new AotCompiler( const compiler = new AotCompiler(
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(compilerHost), summaryResolver, options.locale,
options.i18nFormat, new AnimationParser(elementSchemaRegistry), staticReflector, options); options.i18nFormat, new AnimationParser(elementSchemaRegistry), symbolResolver);
return {compiler, reflector: staticReflector}; return {compiler, reflector: staticReflector};
} }

View File

@ -8,16 +8,17 @@
import {ImportResolver} from '../output/path_util'; import {ImportResolver} from '../output/path_util';
import {StaticReflectorHost} from './static_reflector';
import {StaticSymbol} from './static_symbol'; import {StaticSymbol} from './static_symbol';
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 StaticReflectorHost, ImportResolver, export interface AotCompilerHost extends StaticSymbolResolverHost, ImportResolver,
AotSummaryResolverHost { AotSummaryResolverHost, AotSummarySerializerHost {
/** /**
* Loads a resource (e.g. html / css) * Loads a resource (e.g. html / css)
*/ */

View File

@ -11,6 +11,4 @@ export interface AotCompilerOptions {
locale?: string; locale?: string;
i18nFormat?: string; i18nFormat?: string;
translations?: string; translations?: string;
includeFilePattern?: RegExp;
excludeFilePattern?: RegExp;
} }

View File

@ -8,6 +8,7 @@
import {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from '../private_import_core'; import {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from '../private_import_core';
import {StaticReflector} from './static_reflector'; import {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol';
export class StaticAndDynamicReflectionCapabilities { export class StaticAndDynamicReflectionCapabilities {
static install(staticDelegate: StaticReflector) { static install(staticDelegate: StaticReflector) {
@ -42,7 +43,7 @@ export class StaticAndDynamicReflectionCapabilities {
method(name: string): MethodFn { return this.dynamicDelegate.method(name); } method(name: string): MethodFn { return this.dynamicDelegate.method(name); }
importUri(type: any): string { return this.staticDelegate.importUri(type); } importUri(type: any): string { return this.staticDelegate.importUri(type); }
resolveIdentifier(name: string, moduleUrl: string, runtime: any) { resolveIdentifier(name: string, moduleUrl: string, runtime: any) {
return this.staticDelegate.resolveIdentifier(name, moduleUrl, runtime); return this.staticDelegate.resolveIdentifier(name, moduleUrl);
} }
resolveEnum(enumIdentifier: any, name: string): any { resolveEnum(enumIdentifier: any, name: string): any {
if (isStaticType(enumIdentifier)) { if (isStaticType(enumIdentifier)) {

View File

@ -7,10 +7,12 @@
*/ */
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {ReflectorReader} from '../private_import_core';
import {StaticSymbol} from './static_symbol';
const SUPPORTED_SCHEMA_VERSION = 3; import {ReflectorReader} from '../private_import_core';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
const ANGULAR_IMPORT_LOCATIONS = { const ANGULAR_IMPORT_LOCATIONS = {
coreDecorators: '@angular/core/src/metadata', coreDecorators: '@angular/core/src/metadata',
diDecorators: '@angular/core/src/di/metadata', diDecorators: '@angular/core/src/di/metadata',
@ -22,66 +24,20 @@ const ANGULAR_IMPORT_LOCATIONS = {
const HIDDEN_KEY = /^\$.*\$$/; const HIDDEN_KEY = /^\$.*\$$/;
/**
* The host of the StaticReflector disconnects the implementation from TypeScript / other language
* services and from underlying file systems.
*/
export interface StaticReflectorHost {
/**
* Return a ModuleMetadata for the given module.
* Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
* produced and the module has exported variables or classes with decorators. Module metadata can
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
*
* @param modulePath is a string identifier for a module as an absolute path.
* @returns the metadata for the given module.
*/
getMetadataFor(modulePath: string): {[key: string]: any}[];
/**
* Converts a module name that is used in an `import` to a file path.
* I.e.
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
*/
moduleNameToFileName(moduleName: string, containingFile: string): string;
}
/**
* A cache of static symbol used by the StaticReflector to return the same symbol for the
* same symbol values.
*/
export class StaticSymbolCache {
private cache = new Map<string, StaticSymbol>();
get(declarationFile: string, name: string, members?: string[]): StaticSymbol {
const memberSuffix = members ? `.${ members.join('.')}` : '';
const key = `"${declarationFile}".${name}${memberSuffix}`;
let result = this.cache.get(key);
if (!result) {
result = new StaticSymbol(declarationFile, name, members);
this.cache.set(key, result);
}
return result;
}
}
/** /**
* A static reflector implements enough of the Reflector API that is necessary to compile * A static reflector implements enough of the Reflector API that is necessary to compile
* templates statically. * templates statically.
*/ */
export class StaticReflector implements ReflectorReader { export class StaticReflector implements ReflectorReader {
private declarationCache = new Map<string, StaticSymbol>();
private annotationCache = new Map<StaticSymbol, any[]>(); private annotationCache = new Map<StaticSymbol, any[]>();
private propertyCache = new Map<StaticSymbol, {[key: string]: any[]}>(); private propertyCache = new Map<StaticSymbol, {[key: string]: any[]}>();
private parameterCache = new Map<StaticSymbol, any[]>(); private parameterCache = new Map<StaticSymbol, any[]>();
private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>(); private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
private metadataCache = new Map<string, {[key: string]: any}>();
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>(); private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
private opaqueToken: StaticSymbol; private opaqueToken: StaticSymbol;
constructor( constructor(
private host: StaticReflectorHost, private symbolResolver: StaticSymbolResolver,
private staticSymbolCache: StaticSymbolCache = new StaticSymbolCache(),
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [], knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [], knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [],
private errorRecorder?: (error: any, fileName: string) => void) { private errorRecorder?: (error: any, fileName: string) => void) {
@ -94,12 +50,26 @@ export class StaticReflector implements ReflectorReader {
} }
importUri(typeOrFunc: StaticSymbol): string { importUri(typeOrFunc: StaticSymbol): string {
const staticSymbol = this.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, ''); const staticSymbol = this.findSymbolDeclaration(typeOrFunc);
return staticSymbol ? staticSymbol.filePath : null; return staticSymbol ? staticSymbol.filePath : null;
} }
resolveIdentifier(name: string, moduleUrl: string, runtime: any): any { resolveIdentifier(name: string, moduleUrl: string): StaticSymbol {
return this.findDeclaration(moduleUrl, name, ''); return this.findDeclaration(moduleUrl, name);
}
findDeclaration(moduleUrl: string, name: string, containingFile?: string): StaticSymbol {
return this.findSymbolDeclaration(
this.symbolResolver.getSymbolByModule(moduleUrl, name, containingFile));
}
findSymbolDeclaration(symbol: StaticSymbol): StaticSymbol {
const resolvedSymbol = this.symbolResolver.resolveSymbol(symbol);
if (resolvedSymbol && resolvedSymbol.metadata instanceof StaticSymbol) {
return this.findSymbolDeclaration(resolvedSymbol.metadata);
} else {
return symbol;
}
} }
resolveEnum(enumIdentifier: any, name: string): any { resolveEnum(enumIdentifier: any, name: string): any {
@ -128,7 +98,7 @@ export class StaticReflector implements ReflectorReader {
public propMetadata(type: StaticSymbol): {[key: string]: any[]} { public propMetadata(type: StaticSymbol): {[key: string]: any[]} {
let propMetadata = this.propertyCache.get(type); let propMetadata = this.propertyCache.get(type);
if (!propMetadata) { if (!propMetadata) {
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 parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
@ -203,7 +173,7 @@ export class StaticReflector implements ReflectorReader {
private _methodNames(type: any): {[key: string]: boolean} { private _methodNames(type: any): {[key: string]: boolean} {
let methodNames = this.methodCache.get(type); let methodNames = this.methodCache.get(type);
if (!methodNames) { if (!methodNames) {
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 parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
@ -306,7 +276,7 @@ export class StaticReflector implements ReflectorReader {
* @param name the name of the type. * @param name the name of the type.
*/ */
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol { getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
return this.staticSymbolCache.get(declarationFile, name, members); return this.symbolResolver.getStaticSymbol(declarationFile, name, members);
} }
private reportError(error: Error, context: StaticSymbol, path?: string) { private reportError(error: Error, context: StaticSymbol, path?: string) {
@ -317,96 +287,6 @@ export class StaticReflector implements ReflectorReader {
} }
} }
private resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
const resolveModule = (moduleName: string): string => {
const resolvedModulePath = this.host.moduleNameToFileName(moduleName, filePath);
if (!resolvedModulePath) {
this.reportError(
new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`),
null, filePath);
}
return resolvedModulePath;
};
const cacheKey = `${filePath}|${symbolName}`;
let staticSymbol = this.declarationCache.get(cacheKey);
if (staticSymbol) {
return staticSymbol;
}
const metadata = this.getModuleMetadata(filePath);
if (metadata) {
// If we have metadata for the symbol, this is the original exporting location.
if (metadata['metadata'][symbolName]) {
staticSymbol = this.getStaticSymbol(filePath, symbolName);
}
// If no, try to find the symbol in one of the re-export location
if (!staticSymbol && metadata['exports']) {
// Try and find the symbol in the list of explicitly re-exported symbols.
for (const moduleExport of metadata['exports']) {
if (moduleExport.export) {
const exportSymbol = moduleExport.export.find((symbol: any) => {
if (typeof symbol === 'string') {
return symbol == symbolName;
} else {
return symbol.as == symbolName;
}
});
if (exportSymbol) {
let symName = symbolName;
if (typeof exportSymbol !== 'string') {
symName = exportSymbol.name;
}
const resolvedModule = resolveModule(moduleExport.from);
if (resolvedModule) {
staticSymbol =
this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
break;
}
}
}
}
if (!staticSymbol) {
// Try to find the symbol via export * directives.
for (const moduleExport of metadata['exports']) {
if (!moduleExport.export) {
const resolvedModule = resolveModule(moduleExport.from);
if (resolvedModule) {
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
if (candidateSymbol) {
staticSymbol = candidateSymbol;
break;
}
}
}
}
}
}
}
this.declarationCache.set(cacheKey, staticSymbol);
return staticSymbol;
}
findDeclaration(module: string, symbolName: string, containingFile?: string): StaticSymbol {
try {
const filePath = this.host.moduleNameToFileName(module, containingFile);
let symbol: StaticSymbol;
if (!filePath) {
// If the file cannot be found the module is probably referencing a declared module
// for which there is no disambiguating file and we also don't need to track
// re-exports. Just use the module name.
symbol = this.getStaticSymbol(module, symbolName);
} else {
symbol = this.resolveExportedSymbol(filePath, symbolName) ||
this.getStaticSymbol(filePath, symbolName);
}
return symbol;
} catch (e) {
console.error(`can't resolve module ${module} from ${containingFile}`);
throw e;
}
}
/** @internal */ /** @internal */
public simplify(context: StaticSymbol, value: any): any { public simplify(context: StaticSymbol, value: any): any {
const self = this; const self = this;
@ -414,93 +294,41 @@ export class StaticReflector implements ReflectorReader {
const calling = new Map<StaticSymbol, boolean>(); const calling = new Map<StaticSymbol, boolean>();
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any { function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
function resolveReference(context: StaticSymbol, expression: any): StaticSymbol {
let staticSymbol: StaticSymbol;
if (expression['module']) {
staticSymbol =
self.findDeclaration(expression['module'], expression['name'], context.filePath);
} else {
staticSymbol = self.getStaticSymbol(context.filePath, expression['name']);
}
return staticSymbol;
}
function resolveReferenceValue(staticSymbol: StaticSymbol): any { function resolveReferenceValue(staticSymbol: StaticSymbol): any {
const moduleMetadata = self.getModuleMetadata(staticSymbol.filePath); const resolvedSymbol = self.symbolResolver.resolveSymbol(staticSymbol);
const declarationValue = return resolvedSymbol ? resolvedSymbol.metadata : null;
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
return declarationValue;
} }
function isOpaqueToken(context: StaticSymbol, value: any): boolean { function simplifyCall(functionSymbol: StaticSymbol, targetFunction: any, args: any[]) {
if (value && value.__symbolic === 'new' && value.expression) { if (targetFunction && targetFunction['__symbolic'] == 'function') {
const target = value.expression; if (calling.get(functionSymbol)) {
if (target.__symbolic == 'reference') { throw new Error('Recursion not supported');
return sameSymbol(resolveReference(context, target), self.opaqueToken);
} }
} calling.set(functionSymbol, true);
return false; try {
} const value = targetFunction['value'];
if (value && (depth != 0 || value.__symbolic != 'error')) {
function simplifyCall(expression: any) { const parameters: string[] = targetFunction['parameters'];
let callContext: {[name: string]: string}|undefined = undefined; const defaults: any[] = targetFunction.defaults;
if (expression['__symbolic'] == 'call') { if (defaults && defaults.length > args.length) {
const target = expression['expression']; args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
let functionSymbol: StaticSymbol;
let targetFunction: any;
if (target) {
switch (target.__symbolic) {
case 'reference':
// Find the function to call.
callContext = {name: target.name};
functionSymbol = resolveReference(context, target);
targetFunction = resolveReferenceValue(functionSymbol);
break;
case 'select':
// Find the static method to call
if (target.expression.__symbolic == 'reference') {
functionSymbol = resolveReference(context, target.expression);
const classData = resolveReferenceValue(functionSymbol);
if (classData && classData.statics) {
targetFunction = classData.statics[target.member];
}
}
break;
}
}
if (targetFunction && targetFunction['__symbolic'] == 'function') {
if (calling.get(functionSymbol)) {
throw new Error('Recursion not supported');
}
calling.set(functionSymbol, true);
try {
const value = targetFunction['value'];
if (value && (depth != 0 || value.__symbolic != 'error')) {
// Determine the arguments
const args: any[] =
(expression['arguments'] || []).map((arg: any) => simplify(arg));
const parameters: string[] = targetFunction['parameters'];
const defaults: any[] = targetFunction.defaults;
if (defaults && defaults.length > args.length) {
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
}
const functionScope = BindingScope.build();
for (let i = 0; i < parameters.length; i++) {
functionScope.define(parameters[i], args[i]);
}
const oldScope = scope;
let result: any;
try {
scope = functionScope.done();
result = simplifyInContext(functionSymbol, value, depth + 1);
} finally {
scope = oldScope;
}
return result;
} }
} finally { const functionScope = BindingScope.build();
calling.delete(functionSymbol); for (let i = 0; i < parameters.length; i++) {
functionScope.define(parameters[i], args[i]);
}
const oldScope = scope;
let result: any;
try {
scope = functionScope.done();
result = simplifyInContext(functionSymbol, value, depth + 1);
} finally {
scope = oldScope;
}
return result;
} }
} finally {
calling.delete(functionSymbol);
} }
} }
@ -511,7 +339,7 @@ export class StaticReflector implements ReflectorReader {
return {__symbolic: 'ignore'}; return {__symbolic: 'ignore'};
} }
return simplify( return simplify(
{__symbolic: 'error', message: 'Function call not supported', context: callContext}); {__symbolic: 'error', message: 'Function call not supported', context: functionSymbol});
} }
function simplify(expression: any): any { function simplify(expression: any): any {
@ -540,7 +368,18 @@ export class StaticReflector implements ReflectorReader {
return result; return result;
} }
if (expression instanceof StaticSymbol) { if (expression instanceof StaticSymbol) {
return expression; // Stop simplification at builtin symbols
if (expression === self.opaqueToken || self.conversionMap.has(expression)) {
return expression;
} else {
const staticSymbol = expression;
const declarationValue = resolveReferenceValue(staticSymbol);
if (declarationValue) {
return simplifyInContext(staticSymbol, declarationValue, depth + 1);
} else {
return staticSymbol;
}
}
} }
if (expression) { if (expression) {
if (expression['__symbolic']) { if (expression['__symbolic']) {
@ -618,50 +457,33 @@ export class StaticReflector implements ReflectorReader {
if (indexTarget && isPrimitive(index)) return indexTarget[index]; if (indexTarget && isPrimitive(index)) return indexTarget[index];
return null; return null;
case 'select': case 'select':
const member = expression['member'];
let selectContext = context; let selectContext = context;
let selectTarget = simplify(expression['expression']); let selectTarget = simplify(expression['expression']);
if (selectTarget instanceof StaticSymbol) { if (selectTarget instanceof StaticSymbol) {
// Access to a static instance variable const members = selectTarget.members.concat(member);
const member: string = expression['member'];
const members = selectTarget.members ?
(selectTarget.members as string[]).concat(member) :
[member];
const declarationValue = resolveReferenceValue(selectTarget);
selectContext = selectContext =
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members); self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
if (declarationValue && declarationValue.statics) { const declarationValue = resolveReferenceValue(selectContext);
selectTarget = declarationValue.statics; if (declarationValue) {
return simplifyInContext(selectContext, declarationValue, depth + 1);
} else { } else {
return selectContext; return selectContext;
} }
} }
const member = simplifyInContext(selectContext, expression['member'], depth + 1);
if (selectTarget && isPrimitive(member)) if (selectTarget && isPrimitive(member))
return simplifyInContext(selectContext, selectTarget[member], depth + 1); return simplifyInContext(selectContext, selectTarget[member], depth + 1);
return null; return null;
case 'reference': case 'reference':
if (!expression['name']) { // Note: This only has to deal with variable references,
return context; // as symbol references have been converted into StaticSymbols already
// in the StaticSymbolResolver!
const name: string = expression['name'];
const localValue = scope.resolve(name);
if (localValue != BindingScope.missing) {
return localValue;
} }
if (!expression.module) { break;
const name: string = expression['name'];
const localValue = scope.resolve(name);
if (localValue != BindingScope.missing) {
return localValue;
}
}
staticSymbol = resolveReference(context, expression);
let result: any = staticSymbol;
let declarationValue = resolveReferenceValue(result);
if (declarationValue) {
if (isOpaqueToken(staticSymbol, declarationValue)) {
// If the referenced symbol is initalized by a new OpaqueToken we can keep the
// reference to the symbol.
return staticSymbol;
}
result = simplifyInContext(staticSymbol, declarationValue, depth + 1);
}
return result;
case 'class': case 'class':
return context; return context;
case 'function': case 'function':
@ -669,26 +491,26 @@ export class StaticReflector implements ReflectorReader {
case 'new': case 'new':
case 'call': case 'call':
// Determine if the function is a built-in conversion // Determine if the function is a built-in conversion
let target = expression['expression']; staticSymbol = simplifyInContext(context, expression['expression'], depth + 1);
if (target['module']) { if (staticSymbol instanceof StaticSymbol) {
staticSymbol = if (staticSymbol === self.opaqueToken) {
self.findDeclaration(target['module'], target['name'], context.filePath); // if somebody calls new OpaqueToken, don't create an OpaqueToken,
} else { // but rather return the symbol to which the OpaqueToken is assigned to.
staticSymbol = self.getStaticSymbol(context.filePath, target['name']); return context;
} }
let converter = self.conversionMap.get(staticSymbol); const argExpressions: any[] = expression['arguments'] || [];
if (converter) { const args =
let args: any[] = expression['arguments']; argExpressions.map(arg => simplifyInContext(context, arg, depth + 1));
if (!args) { let converter = self.conversionMap.get(staticSymbol);
args = []; if (converter) {
return converter(context, args);
} else {
// Determine if the function is one we can simplify.
const targetFunction = resolveReferenceValue(staticSymbol);
return simplifyCall(staticSymbol, targetFunction, args);
} }
return converter(
context, args.map(arg => simplifyInContext(context, arg, depth + 1)));
} }
break;
// Determine if the function is one we can simplify.
return simplifyCall(expression);
case 'error': case 'error':
let message = produceErrorMessage(expression); let message = produceErrorMessage(expression);
if (expression['line']) { if (expression['line']) {
@ -709,7 +531,9 @@ export class StaticReflector implements ReflectorReader {
try { try {
return simplify(value); return simplify(value);
} catch (e) { } catch (e) {
const message = `${e.message}, resolving symbol ${context.name} in ${context.filePath}`; const members = context.members.length ? `.${context.members.join('.')}` : '';
const message =
`${e.message}, resolving symbol ${context.name}${members} in ${context.filePath}`;
if (e.fileName) { if (e.fileName) {
throw positionalError(message, e.fileName, e.line, e.column); throw positionalError(message, e.fileName, e.line, e.column);
} }
@ -733,40 +557,10 @@ export class StaticReflector implements ReflectorReader {
return result; return result;
} }
/**
* @param module an absolute path to a module file.
*/
public getModuleMetadata(module: string): {[key: string]: any} {
let moduleMetadata = this.metadataCache.get(module);
if (!moduleMetadata) {
const moduleMetadatas = this.host.getMetadataFor(module);
if (moduleMetadatas) {
let maxVersion = -1;
moduleMetadatas.forEach((md) => {
if (md['version'] > maxVersion) {
maxVersion = md['version'];
moduleMetadata = md;
}
});
}
if (!moduleMetadata) {
moduleMetadata =
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
}
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
const errorMessage = moduleMetadata['version'] == 2 ?
`Unsupported metadata version ${moduleMetadata['version']} for module ${module}. This module should be compiled with a newer version of ngc` :
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`;
this.reportError(new Error(errorMessage), null);
}
this.metadataCache.set(module, moduleMetadata);
}
return moduleMetadata;
}
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} { private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
const moduleMetadata = this.getModuleMetadata(type.filePath); const resolvedSymbol = this.symbolResolver.resolveSymbol(type);
return moduleMetadata['metadata'][type.name] || {__symbolic: 'class'}; return resolvedSymbol && resolvedSymbol.metadata ? resolvedSymbol.metadata :
{__symbolic: 'class'};
} }
} }
@ -858,7 +652,7 @@ class PopulatedScope extends BindingScope {
} }
function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean { function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
return a === b || (a.name == b.name && a.filePath == b.filePath); return a === b;
} }
function shouldIgnore(value: any): boolean { function shouldIgnore(value: any): boolean {

View File

@ -14,3 +14,23 @@
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[]) {}
} }
/**
* A cache of static symbol used by the StaticReflector to return the same symbol for the
* same symbol values.
*/
export class StaticSymbolCache {
private cache = new Map<string, StaticSymbol>();
get(declarationFile: string, name: string, members?: string[]): StaticSymbol {
members = members || [];
const memberSuffix = members.length ? `.${ members.join('.')}` : '';
const key = `"${declarationFile}".${name}${memberSuffix}`;
let result = this.cache.get(key);
if (!result) {
result = new StaticSymbol(declarationFile, name, members);
this.cache.set(key, result);
}
return result;
}
}

View File

@ -0,0 +1,289 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {SummaryResolver} from '../summary_resolver';
import {ValueTransformer, visitValue} from '../util';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
export class ResolvedStaticSymbol {
constructor(public symbol: StaticSymbol, public metadata: any) {}
}
/**
* The host of the SymbolResolverHost disconnects the implementation from TypeScript / other
* language
* services and from underlying file systems.
*/
export interface StaticSymbolResolverHost {
/**
* Return a ModuleMetadata for the given module.
* Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
* produced and the module has exported variables or classes with decorators. Module metadata can
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
*
* @param modulePath is a string identifier for a module as an absolute path.
* @returns the metadata for the given module.
*/
getMetadataFor(modulePath: string): {[key: string]: any}[];
/**
* Converts a module name that is used in an `import` to a file path.
* I.e.
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
*/
moduleNameToFileName(moduleName: string, containingFile: string): string /*|null*/;
}
const SUPPORTED_SCHEMA_VERSION = 3;
/**
* This class is responsible for loading metadata per symbol,
* and normalizing references between symbols.
*/
export class StaticSymbolResolver {
private metadataCache = new Map<string, {[key: string]: any}>();
private resolvedSymbols = new Map<StaticSymbol, ResolvedStaticSymbol>();
private resolvedFilePaths = new Set<string>();
constructor(
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
private summaryResolver: SummaryResolver<StaticSymbol>,
private errorRecorder?: (error: any, fileName: string) => void) {}
resolveSymbol(staticSymbol: StaticSymbol): ResolvedStaticSymbol {
if (staticSymbol.members.length > 0) {
return this._resolveSymbolMembers(staticSymbol);
}
let result = this._resolveSymbolFromSummary(staticSymbol);
if (!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;
}
private _resolveSymbolMembers(staticSymbol: StaticSymbol): ResolvedStaticSymbol {
const members = staticSymbol.members;
const baseResolvedSymbol =
this.resolveSymbol(this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name));
if (!baseResolvedSymbol) {
return null;
}
const baseMetadata = baseResolvedSymbol.metadata;
if (baseMetadata instanceof StaticSymbol) {
return new ResolvedStaticSymbol(
staticSymbol, this.getStaticSymbol(baseMetadata.filePath, baseMetadata.name, members));
} else if (baseMetadata && baseMetadata.__symbolic === 'class') {
if (baseMetadata.statics && members.length === 1) {
return new ResolvedStaticSymbol(staticSymbol, baseMetadata.statics[members[0]]);
}
} else {
let value = baseMetadata;
for (var i = 0; i < members.length && value; i++) {
value = value[members[i]];
}
return new ResolvedStaticSymbol(staticSymbol, value);
}
return null;
}
private _resolveSymbolFromSummary(staticSymbol: StaticSymbol): ResolvedStaticSymbol {
const summary = this.summaryResolver.resolveSummary(staticSymbol);
return summary ? new ResolvedStaticSymbol(staticSymbol, summary.metadata) : null;
}
/**
* getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
* All types passed to the StaticResolver should be pseudo-types returned by this method.
*
* @param declarationFile the absolute path of the file where the symbol is declared
* @param name the name of the type.
*/
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
return this.staticSymbolCache.get(declarationFile, name, members);
}
getSymbolsOf(filePath: string): StaticSymbol[] {
// 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.
let symbols = new Set<StaticSymbol>(this.summaryResolver.getSymbolsOf(filePath));
this._createSymbolsOf(filePath);
this.resolvedSymbols.forEach((resolvedSymbol) => {
if (resolvedSymbol.symbol.filePath === filePath) {
symbols.add(resolvedSymbol.symbol);
}
});
return Array.from(symbols);
}
private _createSymbolsOf(filePath: string) {
if (this.resolvedFilePaths.has(filePath)) {
return;
}
this.resolvedFilePaths.add(filePath);
const resolvedSymbols: ResolvedStaticSymbol[] = [];
const metadata = this.getModuleMetadata(filePath);
if (metadata['metadata']) {
// handle direct declarations of the symbol
Object.keys(metadata['metadata']).forEach((symbolName) => {
const symbolMeta = metadata['metadata'][symbolName];
resolvedSymbols.push(
this.createResolvedSymbol(this.getStaticSymbol(filePath, symbolName), symbolMeta));
});
}
// handle the symbols in one of the re-export location
if (metadata['exports']) {
for (const moduleExport of metadata['exports']) {
// handle the symbols in the list of explicitly re-exported symbols.
if (moduleExport.export) {
moduleExport.export.forEach((exportSymbol: any) => {
let symbolName: string;
if (typeof exportSymbol === 'string') {
symbolName = exportSymbol;
} else {
symbolName = exportSymbol.as;
}
let symName = symbolName;
if (typeof exportSymbol !== 'string') {
symName = exportSymbol.name;
}
const resolvedModule = this.resolveModule(moduleExport.from, filePath);
if (resolvedModule) {
const targetSymbol = this.getStaticSymbol(resolvedModule, symName);
const sourceSymbol = this.getStaticSymbol(filePath, symbolName);
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
}
});
} else {
// handle the symbols via export * directives.
const resolvedModule = this.resolveModule(moduleExport.from, filePath);
if (resolvedModule) {
const nestedExports = this.getSymbolsOf(resolvedModule);
nestedExports.forEach((targetSymbol) => {
const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name);
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
});
}
}
}
}
resolvedSymbols.forEach(
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
}
private createResolvedSymbol(sourceSymbol: StaticSymbol, metadata: any): ResolvedStaticSymbol {
const self = this;
class ReferenceTransformer extends ValueTransformer {
visitStringMap(map: {[key: string]: any}, functionParams: string[]): any {
const symbolic = map['__symbolic'];
if (symbolic === 'function') {
const oldLen = functionParams.length;
functionParams.push(...(map['parameters'] || []));
const result = super.visitStringMap(map, functionParams);
functionParams.length = oldLen;
return result;
} else if (symbolic === 'reference') {
const module = map['module'];
const name = map['name'];
if (!name) {
return null;
}
let filePath: string;
if (module) {
filePath = self.resolveModule(module, sourceSymbol.filePath);
if (!filePath) {
return {
__symbolic: 'error',
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);
} else {
// reference to a function parameter
return {__symbolic: 'reference', name: name};
}
} else {
return super.visitStringMap(map, functionParams);
}
}
}
const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
}
private reportError(error: Error, context: StaticSymbol, path?: string) {
if (this.errorRecorder) {
this.errorRecorder(error, (context && context.filePath) || path);
} else {
throw error;
}
}
/**
* @param module an absolute path to a module file.
*/
private getModuleMetadata(module: string): {[key: string]: any} {
let moduleMetadata = this.metadataCache.get(module);
if (!moduleMetadata) {
const moduleMetadatas = this.host.getMetadataFor(module);
if (moduleMetadatas) {
let maxVersion = -1;
moduleMetadatas.forEach((md) => {
if (md['version'] > maxVersion) {
maxVersion = md['version'];
moduleMetadata = md;
}
});
}
if (!moduleMetadata) {
moduleMetadata =
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
}
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
const errorMessage = moduleMetadata['version'] == 2 ?
`Unsupported metadata version ${moduleMetadata['version']} for module ${module}. This module should be compiled with a newer version of ngc` :
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`;
this.reportError(new Error(errorMessage), null);
}
this.metadataCache.set(module, moduleMetadata);
}
return moduleMetadata;
}
getSymbolByModule(module: string, symbolName: string, containingFile?: string): StaticSymbol {
const filePath = this.resolveModule(module, containingFile);
if (!filePath) {
throw new Error(`Could not resolve module ${module} relative to ${containingFile}`);
}
return this.getStaticSymbol(filePath, symbolName);
}
private resolveModule(module: string, containingFile: string): string {
try {
return this.host.moduleNameToFileName(module, containingFile);
} catch (e) {
console.error(`Could not resolve module '${module}' relative to file ${containingFile}`);
this.reportError(new e, null, containingFile);
}
}
}

View File

@ -5,13 +5,12 @@
* Use of this source code is governed by an MIT-style license that can be * Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleSummary, CompilePipeSummary, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, identifierModuleUrl, identifierName} from '../compile_metadata'; import {CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
import {SummaryResolver} from '../summary_resolver'; import {Summary, SummaryResolver} from '../summary_resolver';
import {GeneratedFile} from './generated_file'; import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {StaticReflector} from './static_reflector'; import {ResolvedStaticSymbol} from './static_symbol_resolver';
import {StaticSymbol} from './static_symbol'; import {deserializeSummaries, summaryFileName} from './summary_serializer';
import {filterFileByPatterns} from './utils';
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/; const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
@ -19,106 +18,60 @@ export interface AotSummaryResolverHost {
/** /**
* Loads an NgModule/Directive/Pipe summary file * Loads an NgModule/Directive/Pipe summary file
*/ */
loadSummary(filePath: string): string; loadSummary(filePath: string): string /*|null*/;
/** /**
* Returns the output file path of a source file. * Returns whether a file is a source file or not.
* E.g.
* `some_file.ts` -> `some_file.d.ts`
*/ */
getOutputFileName(sourceFilePath: string): string; isSourceFile(sourceFilePath: string): boolean;
} }
export interface AotSummaryResolverOptions { export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
includeFilePattern?: RegExp; private summaryCache = new Map<StaticSymbol, Summary<StaticSymbol>>();
excludeFilePattern?: RegExp; private loadedFilePaths = new Set<string>();
}
export class AotSummaryResolver implements SummaryResolver { constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {}
private summaryCache: {[cacheKey: string]: CompileTypeSummary} = {};
constructor( private _assertNoMembers(symbol: StaticSymbol) {
private host: AotSummaryResolverHost, private staticReflector: StaticReflector, if (symbol.members.length) {
private options: AotSummaryResolverOptions) {} throw new Error(
`Internal state: StaticSymbols in summaries can't have members! ${JSON.stringify(symbol)}`);
serializeSummaries(srcFileUrl: string, summaries: CompileTypeSummary[]): GeneratedFile { }
const jsonReplacer = (key: string, value: any) => {
if (value instanceof StaticSymbol) {
// We convert the source filenames into output filenames,
// as the generated summary file will be used when the current
// compilation unit is used as a library
return {
'__symbolic__': 'symbol',
'name': value.name,
'path': this.host.getOutputFileName(value.filePath),
'members': value.members
};
}
return value;
};
const allSummaries = summaries.slice();
summaries.forEach((summary) => {
if (summary.summaryKind === CompileSummaryKind.NgModule) {
const moduleMeta = <CompileNgModuleSummary>summary;
moduleMeta.exportedDirectives.concat(moduleMeta.exportedPipes).forEach((id) => {
if (!filterFileByPatterns(id.reference.filePath, this.options)) {
allSummaries.push(this.resolveSummary(id.reference));
}
});
}
});
return new GeneratedFile(
srcFileUrl, summaryFileName(srcFileUrl), JSON.stringify(allSummaries, jsonReplacer));
} }
private _cacheKey(symbol: StaticSymbol) { return `${symbol.filePath}|${symbol.name}`; } resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> {
this._assertNoMembers(staticSymbol);
let summary = this.summaryCache.get(staticSymbol);
if (!summary) {
this._loadSummaryFile(staticSymbol.filePath);
summary = this.summaryCache.get(staticSymbol);
}
return summary;
}
resolveSummary(staticSymbol: StaticSymbol): any { getSymbolsOf(filePath: string): StaticSymbol[] {
const filePath = staticSymbol.filePath; this._loadSummaryFile(filePath);
const name = staticSymbol.name; return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
const cacheKey = this._cacheKey(staticSymbol); }
if (!filterFileByPatterns(filePath, this.options)) {
let summary = this.summaryCache[cacheKey]; private _loadSummaryFile(filePath: string) {
if (this.loadedFilePaths.has(filePath)) {
return;
}
this.loadedFilePaths.add(filePath);
if (!this.host.isSourceFile(filePath)) {
const summaryFilePath = summaryFileName(filePath); const summaryFilePath = summaryFileName(filePath);
if (!summary) { let json: string;
try { try {
const jsonReviver = (key: string, value: any) => { json = this.host.loadSummary(summaryFilePath);
if (value && value['__symbolic__'] === 'symbol') { } catch (e) {
// Note: We can't use staticReflector.findDeclaration here: console.error(`Error loading summary file ${summaryFilePath}`);
// Summary files can contain symbols of transitive compilation units throw e;
// (via the providers), and findDeclaration needs .metadata.json / .d.ts files,
// but we don't want to depend on these for transitive dependencies.
return this.staticReflector.getStaticSymbol(
value['path'], value['name'], value['members']);
} else {
return value;
}
};
const readSummaries: CompileTypeSummary[] =
JSON.parse(this.host.loadSummary(summaryFilePath), jsonReviver);
readSummaries.forEach((summary) => {
const filePath = summary.type.reference.filePath;
this.summaryCache[this._cacheKey(summary.type.reference)] = summary;
});
summary = this.summaryCache[cacheKey];
} catch (e) {
console.error(`Error loading summary file ${summaryFilePath}`);
throw e;
}
} }
if (!summary) { if (json) {
throw new Error( const readSummaries = deserializeSummaries(this.staticSymbolCache, json);
`Could not find the symbol ${name} in the summary file ${summaryFilePath}!`); readSummaries.forEach((summary) => { this.summaryCache.set(summary.symbol, summary); });
} }
return summary;
} else {
return null;
} }
} }
} }
function summaryFileName(fileName: string): string {
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
return `${fileNameWithoutSuffix}.ngsummary.json`;
}

View File

@ -0,0 +1,183 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleSummary, CompilePipeSummary, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
import {Summary, SummaryResolver} from '../summary_resolver';
import {ValueTransformer, visitValue} from '../util';
import {GeneratedFile} from './generated_file';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
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(
host: AotSummarySerializerHost, summaryResolver: SummaryResolver<StaticSymbol>,
symbolResolver: StaticSymbolResolver,
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string {
const serializer = new Serializer(host);
// for symbols, we use everything except for the class metadata itself
// (we keep the statics though), as the class metadata is contained in the
// CompileTypeSummary.
symbols.forEach(
(resolvedSymbol) => serializer.addOrMergeSummary(
{symbol: resolvedSymbol.symbol, metadata: resolvedSymbol.metadata}));
// Add summaries that are referenced by the given symbols (transitively)
// Note: the serializer.symbols array might be growing while
// we execute the loop!
for (let processedIndex = 0; processedIndex < serializer.symbols.length; processedIndex++) {
const symbol = serializer.symbols[processedIndex];
if (!host.isSourceFile(symbol.filePath)) {
let summary = summaryResolver.resolveSummary(symbol);
if (!summary) {
// some symbols might originate from a plain typescript library
// that just exported .d.ts and .metadata.json files, i.e. where no summary
// files were created.
const resolvedSymbol = symbolResolver.resolveSymbol(symbol);
if (resolvedSymbol) {
summary = {symbol: resolvedSymbol.symbol, metadata: resolvedSymbol.metadata};
}
}
if (summary) {
serializer.addOrMergeSummary(summary);
}
}
}
// Add type summaries.
// Note: We don't add the summaries of all referenced symbols as for the ResolvedSymbols,
// as the type summaries already contain the transitive data that they require
// (in a minimal way).
types.forEach((typeSummary) => {
serializer.addOrMergeSummary(
{symbol: typeSummary.type.reference, metadata: {__symbolic: 'class'}, type: typeSummary});
if (typeSummary.summaryKind === CompileSummaryKind.NgModule) {
const ngModuleSummary = <CompileNgModuleSummary>typeSummary;
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
const symbol: StaticSymbol = id.reference;
if (!host.isSourceFile(symbol.filePath)) {
serializer.addOrMergeSummary(summaryResolver.resolveSummary(symbol));
}
});
}
});
return serializer.serialize();
}
export function deserializeSummaries(
symbolCache: StaticSymbolCache, json: string): Summary<StaticSymbol>[] {
const deserializer = new Deserializer(symbolCache);
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 {
symbols: StaticSymbol[] = [];
private indexBySymbol = new Map<StaticSymbol, number>();
// This now contains a `__symbol: number` in the place of
// StaticSymbols, but otherwise has the same shape as the original objects.
private processedSummaryBySymbol = new Map<StaticSymbol, any>();
private processedSummaries: any[] = [];
constructor(private host: AotSummarySerializerHost) { super(); }
addOrMergeSummary(summary: Summary<StaticSymbol>) {
let symbolMeta = summary.metadata;
if (symbolMeta && symbolMeta.__symbolic === 'class') {
// For classes, we only keep their statics, but not the metadata
// of the class itself as that has been captured already via other summaries
// (e.g. DirectiveSummary, ...).
symbolMeta = {__symbolic: 'class', statics: symbolMeta.statics};
}
let processedSummary = this.processedSummaryBySymbol.get(summary.symbol);
if (!processedSummary) {
processedSummary = this.processValue({symbol: summary.symbol});
this.processedSummaries.push(processedSummary);
this.processedSummaryBySymbol.set(summary.symbol, processedSummary);
}
// Note: == by purpose to compare with undefined!
if (processedSummary.metadata == null && symbolMeta != null) {
processedSummary.metadata = this.processValue(symbolMeta);
}
// Note: == by purpose to compare with undefined!
if (processedSummary.type == null && summary.type != null) {
processedSummary.type = this.processValue(summary.type);
}
}
serialize(): string {
return JSON.stringify({
summaries: this.processedSummaries,
symbols: this.symbols.map((symbol, index) => {
return {
__symbol: index,
name: symbol.name,
// We convert the source filenames tinto output filenames,
// as the generated summary file will be used when teh current
// compilation unit is used as a library
filePath: this.host.getOutputFileName(symbol.filePath)
};
})
});
}
private processValue(value: any): any { return visitValue(value, this, null); }
visitOther(value: any, context: any): any {
if (value instanceof StaticSymbol) {
let index = this.indexBySymbol.get(value);
// Note: == by purpose to compare with undefined!
if (index == null) {
index = this.indexBySymbol.size;
this.indexBySymbol.set(value, index);
this.symbols.push(value);
}
return {__symbol: index};
}
}
}
class Deserializer extends ValueTransformer {
private symbols: StaticSymbol[];
constructor(private symbolCache: StaticSymbolCache) { super(); }
deserialize(json: string): Summary<StaticSymbol>[] {
const data: {summaries: any[], symbols: any[]} = JSON.parse(json);
this.symbols = data.symbols.map(
serializedSymbol => this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name));
return visitValue(data.summaries, this, null);
}
visitStringMap(map: {[key: string]: any}, context: any): any {
if ('__symbol' in map) {
return this.symbols[map['__symbol']];
} else {
return super.visitStringMap(map, context);
}
}
}

View File

@ -1,19 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export function filterFileByPatterns(
fileName: string, options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}) {
let match = true;
if (options.includeFilePattern) {
match = match && !!options.includeFilePattern.exec(fileName);
}
if (options.excludeFilePattern) {
match = match && !options.excludeFilePattern.exec(fileName);
}
return match;
}

View File

@ -115,10 +115,10 @@ export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata
export interface CompileIdentifierMetadata { reference: any; } export interface CompileIdentifierMetadata { reference: any; }
export enum CompileSummaryKind { export enum CompileSummaryKind {
Template,
Pipe, Pipe,
Directive, Directive,
NgModule NgModule,
Injectable
} }
/** /**
@ -126,9 +126,10 @@ export enum CompileSummaryKind {
* in other modules / components. However, this data is not enough to compile * in other modules / components. However, this data is not enough to compile
* the directive / module itself. * the directive / module itself.
*/ */
export interface CompileSummary { summaryKind: CompileSummaryKind; } export interface CompileTypeSummary {
summaryKind: CompileSummaryKind
export interface CompileTypeSummary extends CompileSummary { type: CompileTypeMetadata; } type: CompileTypeMetadata;
}
export interface CompileDiDependencyMetadata { export interface CompileDiDependencyMetadata {
isAttribute?: boolean; isAttribute?: boolean;
@ -210,7 +211,7 @@ export class CompileStylesheetMetadata {
/** /**
* Summary Metadata regarding compilation of a template. * Summary Metadata regarding compilation of a template.
*/ */
export interface CompileTemplateSummary extends CompileSummary { export interface CompileTemplateSummary {
animations: string[]; animations: string[];
ngContentSelectors: string[]; ngContentSelectors: string[];
encapsulation: ViewEncapsulation; encapsulation: ViewEncapsulation;
@ -258,7 +259,6 @@ export class CompileTemplateMetadata {
toSummary(): CompileTemplateSummary { toSummary(): CompileTemplateSummary {
return { return {
summaryKind: CompileSummaryKind.Template,
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

View File

@ -6,11 +6,12 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Component, Injectable, ViewEncapsulation} from '@angular/core'; import {Component, ViewEncapsulation} from '@angular/core';
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata'; import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
import {CompilerConfig} from './config'; import {CompilerConfig} from './config';
import {isBlank, isPresent, stringify} from './facade/lang'; import {isBlank, isPresent, stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import * as html from './ml_parser/ast'; import * as html from './ml_parser/ast';
import {HtmlParser} from './ml_parser/html_parser'; import {HtmlParser} from './ml_parser/html_parser';
import {InterpolationConfig} from './ml_parser/interpolation_config'; import {InterpolationConfig} from './ml_parser/interpolation_config';
@ -32,7 +33,7 @@ export interface PrenormalizedTemplateMetadata {
animations?: CompileAnimationEntryMetadata[]; animations?: CompileAnimationEntryMetadata[];
} }
@Injectable() @CompilerInjectable()
export class DirectiveNormalizer { export class DirectiveNormalizer {
private _resourceLoaderCache = new Map<string, Promise<string>>(); private _resourceLoaderCache = new Map<string, Promise<string>>();

View File

@ -6,14 +6,16 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core'; import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
import {ListWrapper, StringMapWrapper} from './facade/collection'; import {ListWrapper, StringMapWrapper} from './facade/collection';
import {stringify} from './facade/lang'; import {stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core'; import {ReflectorReader, reflector} from './private_import_core';
import {splitAtColon} from './util'; import {splitAtColon} from './util';
/* /*
* Resolve a `Type` for {@link Directive}. * Resolve a `Type` for {@link Directive}.
* *
@ -21,7 +23,7 @@ import {splitAtColon} from './util';
* *
* See {@link Compiler} * See {@link Compiler}
*/ */
@Injectable() @CompilerInjectable()
export class DirectiveResolver { export class DirectiveResolver {
constructor(private _reflector: ReflectorReader = reflector) {} constructor(private _reflector: ReflectorReader = reflector) {}

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 '@angular/core';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata'; import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, 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';
@ -15,6 +13,7 @@ import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
import {CompilerConfig} from './config'; import {CompilerConfig} from './config';
import {Parser} from './expression_parser/parser'; import {Parser} from './expression_parser/parser';
import {Identifiers, createIdentifier} from './identifiers'; import {Identifiers, createIdentifier} from './identifiers';
import {CompilerInjectable} from './injectable';
import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config'; import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config';
import {ClassBuilder, createClassStmt} from './output/class_builder'; import {ClassBuilder, createClassStmt} from './output/class_builder';
import * as o from './output/output_ast'; import * as o from './output/output_ast';
@ -51,7 +50,7 @@ const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap
* *
* So far, only `@Input` and the lifecycle hooks have been implemented. * So far, only `@Input` and the lifecycle hooks have been implemented.
*/ */
@Injectable() @CompilerInjectable()
export class DirectiveWrapperCompiler { export class DirectiveWrapperCompiler {
static dirWrapperClassName(id: CompileIdentifierMetadata) { static dirWrapperClassName(id: CompileIdentifierMetadata) {
return `Wrapper_${identifierName(id)}`; return `Wrapper_${identifierName(id)}`;

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from '@angular/core';
import * as chars from '../chars'; import * as chars from '../chars';
import {NumberWrapper, isPresent} from '../facade/lang'; import {NumberWrapper, isPresent} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
export enum TokenType { export enum TokenType {
Character, Character,
@ -22,7 +22,7 @@ export enum TokenType {
const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this']; const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
@Injectable() @CompilerInjectable()
export class Lexer { export class Lexer {
tokenize(text: string): Token[] { tokenize(text: string): Token[] {
const scanner = new _Scanner(text); const scanner = new _Scanner(text);

View File

@ -6,10 +6,9 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from '@angular/core';
import * as chars from '../chars'; import * as chars from '../chars';
import {escapeRegExp, isBlank, isPresent} from '../facade/lang'; import {escapeRegExp, isBlank, isPresent} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast'; import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
@ -31,7 +30,7 @@ function _createInterpolateRegExp(config: InterpolationConfig): RegExp {
return new RegExp(pattern, 'g'); return new RegExp(pattern, 'g');
} }
@Injectable() @CompilerInjectable()
export class Parser { export class Parser {
private errors: ParserError[] = []; private errors: ParserError[] = [];

View File

@ -14,7 +14,9 @@ import {ViewEncapsulation} from '@angular/core';
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler'; import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities'; import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
import {StaticReflector, StaticReflectorHost} from '../aot/static_reflector'; import {StaticReflector} from '../aot/static_reflector';
import {StaticSymbolCache} from '../aot/static_symbol';
import {StaticSymbolResolver, StaticSymbolResolverHost} from '../aot/static_symbol_resolver';
import {AotSummaryResolver, AotSummaryResolverHost} from '../aot/summary_resolver'; import {AotSummaryResolver, AotSummaryResolverHost} from '../aot/summary_resolver';
import {CompileDirectiveMetadata} from '../compile_metadata'; import {CompileDirectiveMetadata} from '../compile_metadata';
import {CompilerConfig} from '../config'; import {CompilerConfig} from '../config';
@ -33,16 +35,11 @@ import {createOfflineCompileUrlResolver} from '../url_resolver';
import {I18NHtmlParser} from './i18n_html_parser'; import {I18NHtmlParser} from './i18n_html_parser';
import {MessageBundle} from './message_bundle'; import {MessageBundle} from './message_bundle';
export interface ExtractorOptions {
includeFilePattern?: RegExp;
excludeFilePattern?: RegExp;
}
/** /**
* The host of the Extractor disconnects the implementation from TypeScript / other language * The host of the Extractor disconnects the implementation from TypeScript / other language
* services and from underlying file systems. * services and from underlying file systems.
*/ */
export interface ExtractorHost extends StaticReflectorHost, AotSummaryResolverHost { export interface ExtractorHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
/** /**
* Loads a resource (e.g. html / css) * Loads a resource (e.g. html / css)
*/ */
@ -51,14 +48,13 @@ export interface ExtractorHost extends StaticReflectorHost, AotSummaryResolverHo
export class Extractor { export class Extractor {
constructor( constructor(
private options: ExtractorOptions, public host: ExtractorHost, public host: ExtractorHost, private staticSymbolResolver: StaticSymbolResolver,
private staticReflector: StaticReflector, private messageBundle: MessageBundle, private messageBundle: MessageBundle, private metadataResolver: CompileMetadataResolver) {}
private metadataResolver: CompileMetadataResolver) {}
extract(rootFiles: string[]): Promise<MessageBundle> { extract(rootFiles: string[]): Promise<MessageBundle> {
const programSymbols = extractProgramSymbols(this.staticReflector, rootFiles, this.options); const programSymbols = extractProgramSymbols(this.staticSymbolResolver, rootFiles, this.host);
const {ngModuleByPipeOrDirective, files, ngModules} = const {ngModuleByPipeOrDirective, files, ngModules} =
analyzeAndValidateNgModules(programSymbols, this.options, this.metadataResolver); analyzeAndValidateNgModules(programSymbols, this.host, this.metadataResolver);
return Promise return Promise
.all(ngModules.map( .all(ngModules.map(
ngModule => this.metadataResolver.loadNgModuleDirectiveAndPipeMetadata( ngModule => this.metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
@ -91,12 +87,14 @@ export class Extractor {
}); });
} }
static create(host: ExtractorHost, options: ExtractorOptions): static create(host: ExtractorHost): {extractor: Extractor, staticReflector: StaticReflector} {
{extractor: Extractor, staticReflector: StaticReflector} {
const htmlParser = new I18NHtmlParser(new HtmlParser()); const htmlParser = new I18NHtmlParser(new HtmlParser());
const urlResolver = createOfflineCompileUrlResolver(); const urlResolver = createOfflineCompileUrlResolver();
const staticReflector = new StaticReflector(host); const symbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(host, symbolCache);
const staticSymbolResolver = new StaticSymbolResolver(host, symbolCache, summaryResolver);
const staticReflector = new StaticReflector(staticSymbolResolver);
StaticAndDynamicReflectionCapabilities.install(staticReflector); StaticAndDynamicReflectionCapabilities.install(staticReflector);
const config = new CompilerConfig({ const config = new CompilerConfig({
@ -111,13 +109,13 @@ export class Extractor {
const elementSchemaRegistry = new DomElementSchemaRegistry(); const elementSchemaRegistry = new DomElementSchemaRegistry();
const resolver = new CompileMetadataResolver( const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector), new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), new AotSummaryResolver(host, staticReflector, options), new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
elementSchemaRegistry, normalizer, staticReflector); staticReflector);
// TODO(vicb): implicit tags & attributes // TODO(vicb): implicit tags & attributes
const messageBundle = new MessageBundle(htmlParser, [], {}); const messageBundle = new MessageBundle(htmlParser, [], {});
const extractor = new Extractor(options, host, staticReflector, messageBundle, resolver); const extractor = new Extractor(host, staticSymbolResolver, messageBundle, resolver);
return {extractor, staticReflector}; return {extractor, staticReflector};
} }
} }

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
*/ */
export {Extractor, ExtractorHost, ExtractorOptions} from './extractor'; export {Extractor, ExtractorHost} from './extractor';
export {I18NHtmlParser} from './i18n_html_parser'; export {I18NHtmlParser} from './i18n_html_parser';
export {MessageBundle} from './message_bundle'; export {MessageBundle} from './message_bundle';
export {Serializer} from './serializers/serializer'; export {Serializer} from './serializers/serializer';

View File

@ -0,0 +1,16 @@
/**
* @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
*/
/**
* A replacement for @Injectable to be used in the compiler, so that
* we don't try to evaluate the metadata in the compiler during AoT.
* This decorator is enough to make the compiler work with the ReflectiveInjector though.
*/
export function CompilerInjectable(): (data: any) => any {
return (x) => x;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core'; import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} 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';
@ -15,6 +15,7 @@ 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';
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
import {CompileMetadataResolver} from '../metadata_resolver'; import {CompileMetadataResolver} from '../metadata_resolver';
import {NgModuleCompiler} from '../ng_module_compiler'; import {NgModuleCompiler} from '../ng_module_compiler';
import * as ir from '../output/output_ast'; import * as ir from '../output/output_ast';
@ -36,7 +37,7 @@ import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDepende
* from a trusted source. Attacker-controlled data introduced by a template could expose your * from a trusted source. Attacker-controlled data introduced by a template could expose your
* application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security). * application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security).
*/ */
@Injectable() @CompilerInjectable()
export class JitCompiler implements Compiler { export class JitCompiler implements Compiler {
private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>(); private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>(); private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core'; import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
import {AnimationParser} from '../animation/animation_parser'; import {AnimationParser} from '../animation/animation_parser';
import {CompilerConfig} from '../config'; import {CompilerConfig} from '../config';
@ -16,6 +16,7 @@ import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {Lexer} from '../expression_parser/lexer'; import {Lexer} from '../expression_parser/lexer';
import {Parser} from '../expression_parser/parser'; import {Parser} from '../expression_parser/parser';
import * as i18n from '../i18n/index'; import * as i18n from '../i18n/index';
import {CompilerInjectable} from '../injectable';
import {CompileMetadataResolver} from '../metadata_resolver'; import {CompileMetadataResolver} from '../metadata_resolver';
import {HtmlParser} from '../ml_parser/html_parser'; import {HtmlParser} from '../ml_parser/html_parser';
import {NgModuleCompiler} from '../ng_module_compiler'; import {NgModuleCompiler} from '../ng_module_compiler';
@ -83,7 +84,7 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
]; ];
@Injectable() @CompilerInjectable()
export class JitCompilerFactory implements CompilerFactory { export class JitCompilerFactory implements CompilerFactory {
private _defaultOptions: CompilerOptions[]; private _defaultOptions: CompilerOptions[];
constructor(@Inject(COMPILER_OPTIONS) defaultOptions: CompilerOptions[]) { constructor(@Inject(COMPILER_OPTIONS) defaultOptions: CompilerOptions[]) {

View File

@ -16,6 +16,7 @@ import {DirectiveResolver} from './directive_resolver';
import {ListWrapper, StringMapWrapper} from './facade/collection'; import {ListWrapper, StringMapWrapper} from './facade/collection';
import {isBlank, isPresent, stringify} from './facade/lang'; import {isBlank, isPresent, stringify} from './facade/lang';
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers'; import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {CompilerInjectable} from './injectable';
import {hasLifecycleHook} from './lifecycle_reflector'; import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver'; import {NgModuleResolver} from './ng_module_resolver';
import {PipeResolver} from './pipe_resolver'; import {PipeResolver} from './pipe_resolver';
@ -35,7 +36,7 @@ export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
// But we want to report errors even when the async work is // But we want to report errors even when the async work is
// not required to check that the user would have been able // not required to check that the user would have been able
// to wait correctly. // to wait correctly.
@Injectable() @CompilerInjectable()
export class CompileMetadataResolver { export class CompileMetadataResolver {
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>();
@ -45,7 +46,7 @@ export class CompileMetadataResolver {
constructor( constructor(
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver, private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver, private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver<any>,
private _schemaRegistry: ElementSchemaRegistry, private _schemaRegistry: ElementSchemaRegistry,
private _directiveNormalizer: DirectiveNormalizer, private _directiveNormalizer: DirectiveNormalizer,
private _reflector: ReflectorReader = reflector, private _reflector: ReflectorReader = reflector,
@ -128,12 +129,13 @@ export class CompileMetadataResolver {
} }
private _loadSummary(type: any, kind: cpl.CompileSummaryKind): cpl.CompileTypeSummary { private _loadSummary(type: any, kind: cpl.CompileSummaryKind): cpl.CompileTypeSummary {
let summary = this._summaryCache.get(type); let typeSummary = this._summaryCache.get(type);
if (!summary) { if (!typeSummary) {
summary = this._summaryResolver.resolveSummary(type); const summary = this._summaryResolver.resolveSummary(type);
this._summaryCache.set(type, summary); typeSummary = summary ? summary.type : null;
this._summaryCache.set(type, typeSummary);
} }
return summary && summary.summaryKind === kind ? summary : null; return typeSummary && typeSummary.summaryKind === kind ? typeSummary : null;
} }
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> { private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> {
@ -237,7 +239,7 @@ export class CompileMetadataResolver {
if (dirMeta.viewProviders) { if (dirMeta.viewProviders) {
viewProviders = this._getProvidersMetadata( viewProviders = this._getProvidersMetadata(
dirMeta.viewProviders, entryComponentMetadata, dirMeta.viewProviders, entryComponentMetadata,
`viewProviders for "${stringify(directiveType)}"`, [], directiveType); `viewProviders for "${stringifyType(directiveType)}"`, [], directiveType);
} }
if (dirMeta.entryComponents) { if (dirMeta.entryComponents) {
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents) entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
@ -251,7 +253,7 @@ export class CompileMetadataResolver {
// Directive // Directive
if (!selector) { if (!selector) {
this._reportError( this._reportError(
new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`), new Error(`Directive ${stringifyType(directiveType)} has no selector, please add it!`),
directiveType); directiveType);
selector = 'error'; selector = 'error';
} }
@ -260,8 +262,8 @@ export class CompileMetadataResolver {
let providers: cpl.CompileProviderMetadata[] = []; let providers: cpl.CompileProviderMetadata[] = [];
if (isPresent(dirMeta.providers)) { if (isPresent(dirMeta.providers)) {
providers = this._getProvidersMetadata( providers = this._getProvidersMetadata(
dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`, dirMeta.providers, entryComponentMetadata,
[], directiveType); `providers for "${stringifyType(directiveType)}"`, [], directiveType);
} }
let queries: cpl.CompileQueryMetadata[] = []; let queries: cpl.CompileQueryMetadata[] = [];
let viewQueries: cpl.CompileQueryMetadata[] = []; let viewQueries: cpl.CompileQueryMetadata[] = [];
@ -298,7 +300,7 @@ export class CompileMetadataResolver {
if (!dirMeta) { if (!dirMeta) {
this._reportError( this._reportError(
new Error( new Error(
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringify(directiveType)}.`), `Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`),
directiveType); directiveType);
} }
return dirMeta; return dirMeta;
@ -310,7 +312,7 @@ export class CompileMetadataResolver {
if (!dirSummary) { if (!dirSummary) {
this._reportError( this._reportError(
new Error( new Error(
`Illegal state: Could not load the summary for directive ${stringify(dirType)}.`), `Illegal state: Could not load the summary for directive ${stringifyType(dirType)}.`),
dirType); dirType);
} }
return dirSummary; return dirSummary;
@ -383,7 +385,8 @@ export class CompileMetadataResolver {
if (moduleWithProviders.providers) { if (moduleWithProviders.providers) {
providers.push(...this._getProvidersMetadata( providers.push(...this._getProvidersMetadata(
moduleWithProviders.providers, entryComponents, moduleWithProviders.providers, entryComponents,
`provider for the NgModule '${stringify(importedModuleType)}'`, [], importedType)); `provider for the NgModule '${stringifyType(importedModuleType)}'`, [],
importedType));
} }
} }
@ -392,7 +395,7 @@ export class CompileMetadataResolver {
if (!importedModuleSummary) { if (!importedModuleSummary) {
this._reportError( this._reportError(
new Error( new Error(
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`), `Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
} }
@ -400,7 +403,7 @@ export class CompileMetadataResolver {
} else { } else {
this._reportError( this._reportError(
new Error( new Error(
`Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`), `Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
} }
@ -412,7 +415,7 @@ export class CompileMetadataResolver {
if (!isValidType(exportedType)) { if (!isValidType(exportedType)) {
this._reportError( this._reportError(
new Error( new Error(
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`), `Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
} }
@ -433,7 +436,7 @@ export class CompileMetadataResolver {
if (!isValidType(declaredType)) { if (!isValidType(declaredType)) {
this._reportError( this._reportError(
new Error( new Error(
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`), `Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
} }
@ -450,7 +453,7 @@ export class CompileMetadataResolver {
} else { } else {
this._reportError( this._reportError(
new Error( new Error(
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`), `Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
} }
@ -469,7 +472,7 @@ export class CompileMetadataResolver {
} else { } else {
this._reportError( this._reportError(
new Error( new Error(
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringify(exportedId.reference)} from ${stringify(moduleType)} as it was neither declared nor imported!`), `Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`),
moduleType); moduleType);
} }
}); });
@ -478,13 +481,13 @@ export class CompileMetadataResolver {
// so that they overwrite any other provider we already added. // so that they overwrite any other provider we already added.
if (meta.providers) { if (meta.providers) {
providers.push(...this._getProvidersMetadata( providers.push(...this._getProvidersMetadata(
meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`, meta.providers, entryComponents,
[], moduleType)); `provider for the NgModule '${stringifyType(moduleType)}'`, [], moduleType));
} }
if (meta.entryComponents) { if (meta.entryComponents) {
entryComponents.push( entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
...flattenAndDedupeArray(meta.entryComponents).map(type => this._getTypeMetadata(type))); .map(type => this._getIdentifierMetadata(type)));
} }
if (meta.bootstrap) { if (meta.bootstrap) {
@ -492,11 +495,11 @@ export class CompileMetadataResolver {
if (!isValidType(type)) { if (!isValidType(type)) {
this._reportError( this._reportError(
new Error( new Error(
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`), `Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
} }
bootstrapComponents.push(this._getTypeMetadata(type)); bootstrapComponents.push(this._getIdentifierMetadata(type));
}); });
} }
@ -555,9 +558,9 @@ export class CompileMetadataResolver {
if (oldModule && oldModule !== moduleType) { if (oldModule && oldModule !== moduleType) {
this._reportError( this._reportError(
new Error( new Error(
`Type ${stringify(type)} is part of the declarations of 2 modules: ${stringify(oldModule)} and ${stringify(moduleType)}! ` + `Type ${stringifyType(type)} is part of the declarations of 2 modules: ${stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` +
`Please consider moving ${stringify(type)} to a higher module that imports ${stringify(oldModule)} and ${stringify(moduleType)}. ` + `Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` +
`You can also create a new NgModule that exports and includes ${stringify(type)} then import that NgModule in ${stringify(oldModule)} and ${stringify(moduleType)}.`), `You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`),
moduleType); moduleType);
} }
this._ngModuleOfTypes.set(type, moduleType); this._ngModuleOfTypes.set(type, moduleType);
@ -606,6 +609,26 @@ export class CompileMetadataResolver {
return {reference: type}; return {reference: type};
} }
isInjectable(type: any): boolean {
const annotations = this._reflector.annotations(type);
// Note: We need an exact check here as @Component / @Directive / ... inherit
// from @CompilerInjectable!
return annotations.some(ann => ann.constructor === Injectable);
}
getInjectableSummary(type: any): cpl.CompileTypeSummary {
return {summaryKind: cpl.CompileSummaryKind.Injectable, type: this._getTypeMetadata(type)};
}
private _getInjectableMetadata(type: Type<any>, dependencies: any[] = null):
cpl.CompileTypeMetadata {
const typeSummary = this._loadSummary(type, cpl.CompileSummaryKind.Injectable);
if (typeSummary) {
return typeSummary.type;
}
return this._getTypeMetadata(type, dependencies);
}
private _getTypeMetadata(type: Type<any>, dependencies: any[] = null): cpl.CompileTypeMetadata { private _getTypeMetadata(type: Type<any>, dependencies: any[] = null): cpl.CompileTypeMetadata {
const identifier = this._getIdentifierMetadata(type); const identifier = this._getIdentifierMetadata(type);
return { return {
@ -631,7 +654,7 @@ export class CompileMetadataResolver {
if (!pipeMeta) { if (!pipeMeta) {
this._reportError( this._reportError(
new Error( new Error(
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`), `Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`),
pipeType); pipeType);
} }
return pipeMeta; return pipeMeta;
@ -642,7 +665,8 @@ export class CompileMetadataResolver {
<cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe); <cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe);
if (!pipeSummary) { if (!pipeSummary) {
this._reportError( this._reportError(
new Error(`Illegal state: Could not load the summary for pipe ${stringify(pipeType)}.`), new Error(
`Illegal state: Could not load the summary for pipe ${stringifyType(pipeType)}.`),
pipeType); pipeType);
} }
return pipeSummary; return pipeSummary;
@ -722,9 +746,10 @@ export class CompileMetadataResolver {
if (hasUnknownDeps) { if (hasUnknownDeps) {
const depsTokens = const depsTokens =
dependenciesMetadata.map((dep) => dep ? stringify(dep.token) : '?').join(', '); dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', ');
this._reportError( this._reportError(
new Error(`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`), new Error(
`Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`),
typeOrFunc); typeOrFunc);
} }
@ -761,9 +786,9 @@ export class CompileMetadataResolver {
(<string[]>providers.reduce( (<string[]>providers.reduce(
(soFar: string[], seenProvider: any, seenProviderIdx: number) => { (soFar: string[], seenProvider: any, seenProviderIdx: number) => {
if (seenProviderIdx < providerIdx) { if (seenProviderIdx < providerIdx) {
soFar.push(`${stringify(seenProvider)}`); soFar.push(`${stringifyType(seenProvider)}`);
} else if (seenProviderIdx == providerIdx) { } else if (seenProviderIdx == providerIdx) {
soFar.push(`?${stringify(seenProvider)}?`); soFar.push(`?${stringifyType(seenProvider)}?`);
} else if (seenProviderIdx == providerIdx + 1) { } else if (seenProviderIdx == providerIdx + 1) {
soFar.push('...'); soFar.push('...');
} }
@ -805,7 +830,8 @@ export class CompileMetadataResolver {
extractIdentifiers(provider.useValue, collectedIdentifiers); extractIdentifiers(provider.useValue, collectedIdentifiers);
collectedIdentifiers.forEach((identifier) => { collectedIdentifiers.forEach((identifier) => {
if (this._directiveResolver.isDirective(identifier.reference)) { if (this._directiveResolver.isDirective(identifier.reference) ||
this._loadSummary(identifier.reference, cpl.CompileSummaryKind.Directive)) {
components.push(identifier); components.push(identifier);
} }
}); });
@ -819,7 +845,7 @@ export class CompileMetadataResolver {
let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token); let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token);
if (provider.useClass) { if (provider.useClass) {
compileTypeMetadata = this._getTypeMetadata(provider.useClass, provider.dependencies); compileTypeMetadata = this._getInjectableMetadata(provider.useClass, provider.dependencies);
compileDeps = compileTypeMetadata.diDeps; compileDeps = compileTypeMetadata.diDeps;
if (provider.token === provider.useClass) { if (provider.token === provider.useClass) {
// use the compileTypeMetadata as it contains information about lifecycleHooks... // use the compileTypeMetadata as it contains information about lifecycleHooks...
@ -868,7 +894,7 @@ export class CompileMetadataResolver {
if (!q.selector) { if (!q.selector) {
this._reportError( this._reportError(
new Error( new Error(
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`), `Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`),
typeOrFunc); typeOrFunc);
} }
selectors = [this._getTokenMetadata(q.selector)]; selectors = [this._getTokenMetadata(q.selector)];
@ -936,7 +962,7 @@ export function componentModuleUrl(
return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`; return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`;
} else if (moduleId !== null && moduleId !== void 0) { } else if (moduleId !== null && moduleId !== void 0) {
throw new Error( throw new Error(
`moduleId should be a string in "${stringify(type)}". See https://goo.gl/wIDDiL for more information.\n` + `moduleId should be a string in "${stringifyType(type)}". See https://goo.gl/wIDDiL for more information.\n` +
`If you're using Webpack you should inline the template and the styles, see https://goo.gl/X2J8zc.`); `If you're using Webpack you should inline the template and the styles, see https://goo.gl/X2J8zc.`);
} }
@ -952,3 +978,11 @@ class _CompileValueConverter extends ValueTransformer {
targetIdentifiers.push({reference: value}); targetIdentifiers.push({reference: value});
} }
} }
function stringifyType(type: any): string {
if (type instanceof StaticSymbol) {
return `${type.name} in ${type.filePath}`;
} else {
return stringify(type);
}
}

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 {Injectable} from '@angular/core'; import {CompilerInjectable} from '../injectable';
import {getHtmlTagDefinition} from './html_tags'; import {getHtmlTagDefinition} from './html_tags';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './interpolation_config'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './interpolation_config';
@ -14,7 +14,7 @@ import {ParseTreeResult, Parser} from './parser';
export {ParseTreeResult, TreeError} from './parser'; export {ParseTreeResult, TreeError} from './parser';
@Injectable() @CompilerInjectable()
export class HtmlParser extends Parser { export class HtmlParser extends Parser {
constructor() { super(getHtmlTagDefinition); } constructor() { super(getHtmlTagDefinition); }

View File

@ -6,12 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from '@angular/core';
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata'; import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
import {createDiTokenExpression} from './compiler_util/identifier_util'; import {createDiTokenExpression} from './compiler_util/identifier_util';
import {isPresent} from './facade/lang'; import {isPresent} from './facade/lang';
import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from './identifiers'; import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from './identifiers';
import {CompilerInjectable} from './injectable';
import {ClassBuilder, createClassStmt} from './output/class_builder'; import {ClassBuilder, createClassStmt} from './output/class_builder';
import * as o from './output/output_ast'; import * as o from './output/output_ast';
import {convertValueToOutputAst} from './output/value_util'; import {convertValueToOutputAst} from './output/value_util';
@ -31,7 +30,7 @@ export class NgModuleCompileResult {
public dependencies: ComponentFactoryDependency[]) {} public dependencies: ComponentFactoryDependency[]) {}
} }
@Injectable() @CompilerInjectable()
export class NgModuleCompiler { export class NgModuleCompiler {
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]): compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
NgModuleCompileResult { NgModuleCompileResult {

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable, NgModule, Type} from '@angular/core'; import {NgModule, Type} from '@angular/core';
import {ListWrapper} from './facade/collection'; import {ListWrapper} from './facade/collection';
import {isPresent, stringify} from './facade/lang'; import {isPresent, stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core'; import {ReflectorReader, reflector} from './private_import_core';
function _isNgModuleMetadata(obj: any): obj is NgModule { function _isNgModuleMetadata(obj: any): obj is NgModule {
@ -19,7 +20,7 @@ function _isNgModuleMetadata(obj: any): obj is NgModule {
/** /**
* Resolves types to {@link NgModule}. * Resolves types to {@link NgModule}.
*/ */
@Injectable() @CompilerInjectable()
export class NgModuleResolver { export class NgModuleResolver {
constructor(private _reflector: ReflectorReader = reflector) {} constructor(private _reflector: ReflectorReader = reflector) {}

View File

@ -14,5 +14,6 @@ export abstract class ImportResolver {
* Converts a file path to a module name that can be used as an `import. * 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`. * I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
*/ */
abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string; abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
/*|null*/;
} }

View File

@ -335,7 +335,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
} }
ctx.print(`${prefix}.`); ctx.print(`${prefix}.`);
} }
if (value.reference && value.reference.members) { if (value.reference && value.reference.members && value.reference.members.length) {
ctx.print(value.reference.name); ctx.print(value.reference.name);
ctx.print('.'); ctx.print('.');
ctx.print(value.reference.members.join('.')); ctx.print(value.reference.members.join('.'));

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable, Pipe, Type, resolveForwardRef} from '@angular/core'; import {Pipe, Type, resolveForwardRef} from '@angular/core';
import {ListWrapper} from './facade/collection'; import {ListWrapper} from './facade/collection';
import {isPresent, stringify} from './facade/lang'; import {isPresent, stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core'; import {ReflectorReader, reflector} from './private_import_core';
function _isPipeMetadata(type: any): boolean { function _isPipeMetadata(type: any): boolean {
@ -23,7 +24,7 @@ function _isPipeMetadata(type: any): boolean {
* *
* See {@link Compiler} * See {@link Compiler}
*/ */
@Injectable() @CompilerInjectable()
export class PipeResolver { export class PipeResolver {
constructor(private _reflector: ReflectorReader = reflector) {} constructor(private _reflector: ReflectorReader = reflector) {}

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AUTO_STYLE, CUSTOM_ELEMENTS_SCHEMA, Injectable, NO_ERRORS_SCHEMA, SchemaMetadata, SecurityContext} from '@angular/core'; import {AUTO_STYLE, CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata, SecurityContext} from '@angular/core';
import {CompilerInjectable} from '../injectable';
import {dashCaseToCamelCase} from '../util'; import {dashCaseToCamelCase} from '../util';
@ -238,7 +239,7 @@ const _ATTR_TO_PROP: {[name: string]: string} = {
'tabindex': 'tabIndex', 'tabindex': 'tabIndex',
}; };
@Injectable() @CompilerInjectable()
export class DomElementSchemaRegistry extends ElementSchemaRegistry { export class DomElementSchemaRegistry extends ElementSchemaRegistry {
private _schema: {[element: string]: {[property: string]: string}} = {}; private _schema: {[element: string]: {[property: string]: string}} = {};

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 {Injectable, ViewEncapsulation} from '@angular/core'; import {ViewEncapsulation} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileStylesheetMetadata, identifierModuleUrl, identifierName} from './compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileStylesheetMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
import {CompilerInjectable} from './injectable';
import * as o from './output/output_ast'; import * as o from './output/output_ast';
import {ShadowCss} from './shadow_css'; import {ShadowCss} from './shadow_css';
import {UrlResolver} from './url_resolver'; import {UrlResolver} from './url_resolver';
@ -36,7 +37,7 @@ export class CompiledStylesheet {
public meta: CompileStylesheetMetadata) {} public meta: CompileStylesheetMetadata) {}
} }
@Injectable() @CompilerInjectable()
export class StyleCompiler { export class StyleCompiler {
private _shadowCss: ShadowCss = new ShadowCss(); private _shadowCss: ShadowCss = new ShadowCss();

View File

@ -5,10 +5,17 @@
* Use of this source code is governed by an MIT-style license that can be * Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from '@angular/core';
import {CompileTypeSummary} from './compile_metadata'; import {CompileTypeSummary} from './compile_metadata';
import {CompilerInjectable} from './injectable';
@Injectable() export interface Summary<T> {
export class SummaryResolver { symbol: T;
resolveSummary(reference: any): CompileTypeSummary { return null; } metadata: any;
type?: CompileTypeSummary;
}
@CompilerInjectable()
export class SummaryResolver<T> {
resolveSummary(reference: T): Summary<T> { return null; };
getSymbolsOf(filePath: string): T[] { return []; }
} }

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata} from '@angular/core'; import {Inject, OpaqueToken, Optional, SchemaMetadata} from '@angular/core';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
import {Parser} from '../expression_parser/parser'; import {Parser} from '../expression_parser/parser';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {I18NHtmlParser} from '../i18n/i18n_html_parser'; import {I18NHtmlParser} from '../i18n/i18n_html_parser';
import {Identifiers, createIdentifierToken, identifierToken} from '../identifiers'; import {Identifiers, createIdentifierToken, identifierToken} from '../identifiers';
import {CompilerInjectable} from '../injectable';
import * as html from '../ml_parser/ast'; import * as html from '../ml_parser/ast';
import {ParseTreeResult} from '../ml_parser/html_parser'; import {ParseTreeResult} from '../ml_parser/html_parser';
import {expandNodes} from '../ml_parser/icu_ast_expander'; import {expandNodes} from '../ml_parser/icu_ast_expander';
@ -79,7 +79,7 @@ export class TemplateParseResult {
constructor(public templateAst?: TemplateAst[], public errors?: ParseError[]) {} constructor(public templateAst?: TemplateAst[], public errors?: ParseError[]) {}
} }
@Injectable() @CompilerInjectable()
export class TemplateParser { export class TemplateParser {
constructor( constructor(
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry, private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,

View File

@ -6,9 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Inject, Injectable, PACKAGE_ROOT_URL} from '@angular/core'; import {Inject, PACKAGE_ROOT_URL} from '@angular/core';
import {isBlank, isPresent} from './facade/lang'; import {isBlank, isPresent} from './facade/lang';
import {CompilerInjectable} from './injectable';
/** /**
* Create a {@link UrlResolver} with no package prefix. * Create a {@link UrlResolver} with no package prefix.
@ -45,7 +47,7 @@ export var DEFAULT_PACKAGE_URL_PROVIDER = {
* Attacker-controlled data introduced by a template could expose your * Attacker-controlled data introduced by a template could expose your
* application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security). * application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security).
*/ */
@Injectable() @CompilerInjectable()
export class UrlResolver { export class UrlResolver {
constructor(@Inject(PACKAGE_ROOT_URL) private _packagePrefix: string = null) {} constructor(@Inject(PACKAGE_ROOT_URL) private _packagePrefix: string = null) {}

View File

@ -6,11 +6,10 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from '@angular/core';
import {AnimationEntryCompileResult} from '../animation/animation_compiler'; import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompilePipeSummary} from '../compile_metadata'; import {CompileDirectiveMetadata, CompilePipeSummary} from '../compile_metadata';
import {CompilerConfig} from '../config'; import {CompilerConfig} from '../config';
import {CompilerInjectable} from '../injectable';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {ElementSchemaRegistry} from '../schema/element_schema_registry'; import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {TemplateAst} from '../template_parser/template_ast'; import {TemplateAst} from '../template_parser/template_ast';
@ -30,7 +29,7 @@ export class ViewCompileResult {
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {} Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
} }
@Injectable() @CompilerInjectable()
export class ViewCompiler { export class ViewCompiler {
constructor(private _genConfig: CompilerConfig, private _schemaRegistry: ElementSchemaRegistry) {} constructor(private _genConfig: CompilerConfig, private _schemaRegistry: ElementSchemaRegistry) {}

View File

@ -6,26 +6,25 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler'; import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler';
import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {MetadataCollector} from '@angular/tsc-wrapped';
import * as ts from 'typescript';
import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';
// This matches .ts files but not .d.ts files.
const TS_EXT = /(^.|(?!\.d)..)\.ts$/;
describe('StaticReflector', () => { describe('StaticReflector', () => {
const noContext = new StaticSymbol('', ''); let noContext: StaticSymbol;
let host: StaticReflectorHost; let host: StaticSymbolResolverHost;
let symbolResolver: StaticSymbolResolver;
let reflector: StaticReflector; let reflector: StaticReflector;
function init( function init(
testData: {[key: string]: any} = DEFAULT_TEST_DATA, testData: {[key: string]: any} = DEFAULT_TEST_DATA,
decorators: {name: string, filePath: string, ctor: any}[] = []) { decorators: {name: string, filePath: string, ctor: any}[] = []) {
host = new MockStaticReflectorHost(testData); const symbolCache = new StaticSymbolCache();
reflector = new StaticReflector(host, undefined, decorators); host = new MockStaticSymbolResolverHost(testData);
symbolResolver = new StaticSymbolResolver(host, symbolCache, new MockSummaryResolver([]));
reflector = new StaticReflector(symbolResolver, decorators);
noContext = reflector.getStaticSymbol('', '');
} }
beforeEach(() => init()); beforeEach(() => init());
@ -77,24 +76,22 @@ describe('StaticReflector', () => {
])]); ])]);
}); });
it('should throw an exception for unsupported metadata versions', () => {
expect(() => reflector.findDeclaration('src/version-error', 'e'))
.toThrow(new Error(
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 3'));
});
it('should throw an exception for version 2 metadata', () => {
expect(() => reflector.findDeclaration('src/version-2-error', 'e'))
.toThrowError(
'Unsupported metadata version 2 for module /tmp/src/version-2-error.d.ts. This module should be compiled with a newer version of ngc');
});
it('should get and empty annotation list for an unknown class', () => { it('should get and empty annotation list for an unknown class', () => {
const UnknownClass = reflector.findDeclaration('src/app/app.component', 'UnknownClass'); const UnknownClass = reflector.findDeclaration('src/app/app.component', 'UnknownClass');
const annotations = reflector.annotations(UnknownClass); const annotations = reflector.annotations(UnknownClass);
expect(annotations).toEqual([]); expect(annotations).toEqual([]);
}); });
it('should get and empty annotation list for a symbol with null value', () => {
init({
'/tmp/test.ts': `
export var x = null;
`
});
const annotations = reflector.annotations(reflector.getStaticSymbol('/tmp/test.ts', 'x'));
expect(annotations).toEqual([]);
});
it('should get propMetadata for HeroDetailComponent', () => { it('should get propMetadata for HeroDetailComponent', () => {
const HeroDetailComponent = const HeroDetailComponent =
reflector.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent'); reflector.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
@ -129,7 +126,7 @@ describe('StaticReflector', () => {
}); });
it('should simplify a static symbol into itself', () => { it('should simplify a static symbol into itself', () => {
const staticSymbol = new StaticSymbol('', ''); const staticSymbol = reflector.getStaticSymbol('', '');
expect(simplify(noContext, staticSymbol)).toBe(staticSymbol); expect(simplify(noContext, staticSymbol)).toBe(staticSymbol);
}); });
@ -306,49 +303,43 @@ describe('StaticReflector', () => {
expect(simplify(noContext, expr)).toBe(2); expect(simplify(noContext, expr)).toBe(2);
}); });
it('should simplify a module reference', () => { it('should simplify a file reference', () => {
expect(simplify( expect(simplify(
new StaticSymbol('/src/cases', ''), reflector.getStaticSymbol('/src/cases', ''),
({__symbolic: 'reference', module: './extern', name: 's'}))) reflector.getStaticSymbol('/src/extern.d.ts', 's')))
.toEqual('s'); .toEqual('s');
}); });
it('should not simplify a module reference without a name', () => {
const staticSymbol = new StaticSymbol('/src/cases', '');
expect(simplify(staticSymbol, ({__symbolic: 'reference', module: './extern', name: ''})))
.toEqual(staticSymbol);
});
it('should simplify a non existing reference as a static symbol', () => { it('should simplify a non existing reference as a static symbol', () => {
expect(simplify( expect(simplify(
new StaticSymbol('/src/cases', ''), reflector.getStaticSymbol('/src/cases', ''),
({__symbolic: 'reference', module: './extern', name: 'nonExisting'}))) reflector.getStaticSymbol('/src/extern.d.ts', 'nonExisting')))
.toEqual(reflector.getStaticSymbol('/src/extern.d.ts', 'nonExisting')); .toEqual(reflector.getStaticSymbol('/src/extern.d.ts', 'nonExisting'));
}); });
it('should simplify a function reference as a static symbol', () => { it('should simplify a function reference as a static symbol', () => {
expect(simplify( expect(simplify(
new StaticSymbol('/src/cases', 'myFunction'), reflector.getStaticSymbol('/src/cases', 'myFunction'),
({__symbolic: 'function', parameters: ['a'], value: []}))) ({__symbolic: 'function', parameters: ['a'], value: []})))
.toEqual(reflector.getStaticSymbol('/src/cases', 'myFunction')); .toEqual(reflector.getStaticSymbol('/src/cases', 'myFunction'));
}); });
it('should simplify values initialized with a function call', () => { it('should simplify values initialized with a function call', () => {
expect(simplify(new StaticSymbol('/tmp/src/function-reference.ts', ''), { expect(simplify(
__symbolic: 'reference', reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
name: 'one' reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'one')))
})).toEqual(['some-value']); .toEqual(['some-value']);
expect(simplify(new StaticSymbol('/tmp/src/function-reference.ts', ''), { expect(simplify(
__symbolic: 'reference', reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
name: 'three' reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'three')))
})).toEqual(3); .toEqual(3);
}); });
it('should error on direct recursive calls', () => { it('should error on direct recursive calls', () => {
expect( expect(
() => simplify( () => simplify(
new StaticSymbol('/tmp/src/function-reference.ts', ''), reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
{__symbolic: 'reference', name: 'recursion'})) reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'recursion')))
.toThrow(new Error( .toThrow(new Error(
'Recursion not supported, resolving symbol recursive in /tmp/src/function-recursive.d.ts, resolving symbol recursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts')); 'Recursion not supported, resolving symbol recursive in /tmp/src/function-recursive.d.ts, resolving symbol recursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
}); });
@ -362,7 +353,8 @@ describe('StaticReflector', () => {
expect(moduleMetadata).toBeDefined(); expect(moduleMetadata).toBeDefined();
const classData: any = moduleMetadata['InvalidMetadata']; const classData: any = moduleMetadata['InvalidMetadata'];
expect(classData).toBeDefined(); expect(classData).toBeDefined();
simplify(new StaticSymbol('/tmp/src/invalid-metadata.ts', ''), classData.decorators[0]); simplify(
reflector.getStaticSymbol('/tmp/src/invalid-metadata.ts', ''), classData.decorators[0]);
} catch (e) { } catch (e) {
expect(e.fileName).toBe('/tmp/src/invalid-metadata.ts'); expect(e.fileName).toBe('/tmp/src/invalid-metadata.ts');
threw = true; threw = true;
@ -373,48 +365,17 @@ describe('StaticReflector', () => {
it('should error on indirect recursive calls', () => { it('should error on indirect recursive calls', () => {
expect( expect(
() => simplify( () => simplify(
new StaticSymbol('/tmp/src/function-reference.ts', ''), reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
{__symbolic: 'reference', name: 'indirectRecursion'})) reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'indirectRecursion')))
.toThrow(new Error( .toThrow(new Error(
'Recursion not supported, resolving symbol indirectRecursion2 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion1 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts')); 'Recursion not supported, resolving symbol indirectRecursion2 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion1 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
}); });
it('should simplify a spread expression', () => { it('should simplify a spread expression', () => {
expect(simplify(new StaticSymbol('/tmp/src/spread.ts', ''), { expect(simplify(
__symbolic: 'reference', reflector.getStaticSymbol('/tmp/src/spread.ts', ''),
name: 'spread' reflector.getStaticSymbol('/tmp/src/spread.ts', 'spread')))
})).toEqual([0, 1, 2, 3, 4, 5]); .toEqual([0, 1, 2, 3, 4, 5]);
});
it('should be able to get metadata from a ts file', () => {
const metadata = reflector.getModuleMetadata('/tmp/src/custom-decorator-reference.ts');
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
metadata: {
Foo: {
__symbolic: 'class',
decorators: [{
__symbolic: 'call',
expression:
{__symbolic: 'reference', module: './custom-decorator', name: 'CustomDecorator'}
}],
members: {
foo: [{
__symbolic: 'property',
decorators: [{
__symbolic: 'call',
expression: {
__symbolic: 'reference',
module: './custom-decorator',
name: 'CustomDecorator'
}
}]
}]
}
}
}
});
}); });
it('should be able to get metadata for a class containing a custom decorator', () => { it('should be able to get metadata for a class containing a custom decorator', () => {
@ -488,62 +449,6 @@ describe('StaticReflector', () => {
expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod'); expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod');
}); });
it('should be able to produce a symbol for an exported symbol', () => {
expect(reflector.findDeclaration('@angular/router', 'foo', 'main.ts')).toBeDefined();
});
it('should be able to produce a symbol for values space only reference', () => {
expect(reflector.findDeclaration('@angular/router/src/providers', 'foo', 'main.ts'))
.toBeDefined();
});
it('should be produce the same symbol if asked twice', () => {
const foo1 = reflector.getStaticSymbol('main.ts', 'foo');
const foo2 = reflector.getStaticSymbol('main.ts', 'foo');
expect(foo1).toBe(foo2);
});
it('should be able to produce a symbol for a module with no file',
() => { expect(reflector.getStaticSymbol('angularjs', 'SomeAngularSymbol')).toBeDefined(); });
it('should be able to trace a named export', () => {
const symbol = reflector.findDeclaration('./reexport/reexport', 'One', '/tmp/src/main.ts');
expect(symbol.name).toEqual('One');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
});
it('should be able to trace a renamed export', () => {
const symbol = reflector.findDeclaration('./reexport/reexport', 'Four', '/tmp/src/main.ts');
expect(symbol.name).toEqual('Three');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
});
it('should be able to trace an export * export', () => {
const symbol = reflector.findDeclaration('./reexport/reexport', 'Five', '/tmp/src/main.ts');
expect(symbol.name).toEqual('Five');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin5.d.ts');
});
it('should be able to trace a multi-level re-export', () => {
const symbol = reflector.findDeclaration('./reexport/reexport', 'Thirty', '/tmp/src/main.ts');
expect(symbol.name).toEqual('Thirty');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts');
});
it('should cache tracing a named export', () => {
const moduleNameToFileNameSpy = spyOn(host, 'moduleNameToFileName').and.callThrough();
const getMetadataForSpy = spyOn(host, 'getMetadataFor').and.callThrough();
reflector.findDeclaration('./reexport/reexport', 'One', '/tmp/src/main.ts');
moduleNameToFileNameSpy.calls.reset();
getMetadataForSpy.calls.reset();
const symbol = reflector.findDeclaration('./reexport/reexport', 'One', '/tmp/src/main.ts');
expect(moduleNameToFileNameSpy.calls.count()).toBe(1);
expect(getMetadataForSpy.calls.count()).toBe(0);
expect(symbol.name).toEqual('One');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
});
describe('inheritance', () => { describe('inheritance', () => {
class ClassDecorator { class ClassDecorator {
constructor(public value: any) {} constructor(public value: any) {}
@ -706,78 +611,6 @@ describe('StaticReflector', () => {
}); });
export class MockStaticReflectorHost implements StaticReflectorHost {
private collector = new MetadataCollector();
constructor(private data: {[key: string]: any}) {}
// In tests, assume that symbols are not re-exported
moduleNameToFileName(modulePath: string, containingFile?: string): string {
function splitPath(path: string): string[] { return path.split(/\/|\\/g); }
function resolvePath(pathParts: string[]): string {
const result: string[] = [];
pathParts.forEach((part, index) => {
switch (part) {
case '':
case '.':
if (index > 0) return;
break;
case '..':
if (index > 0 && result.length != 0) result.pop();
return;
}
result.push(part);
});
return result.join('/');
}
function pathTo(from: string, to: string): string {
let result = to;
if (to.startsWith('.')) {
const fromParts = splitPath(from);
fromParts.pop(); // remove the file name.
const toParts = splitPath(to);
result = resolvePath(fromParts.concat(toParts));
}
return result;
}
if (modulePath.indexOf('.') === 0) {
const baseName = pathTo(containingFile, modulePath);
const tsName = baseName + '.ts';
if (this._getMetadataFor(tsName)) {
return tsName;
}
return baseName + '.d.ts';
}
return '/tmp/' + modulePath + '.d.ts';
}
getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); }
private _getMetadataFor(moduleId: string): any {
if (this.data[moduleId] && moduleId.match(TS_EXT)) {
const text = this.data[moduleId];
if (typeof text === 'string') {
const sf = ts.createSourceFile(
moduleId, this.data[moduleId], ts.ScriptTarget.ES5, /* setParentNodes */ true);
const diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
if (diagnostics && diagnostics.length) {
throw Error(`Error encountered during parse of file ${moduleId}`);
}
return [this.collector.getMetadata(sf)];
}
}
const result = this.data[moduleId];
if (result) {
return Array.isArray(result) ? result : [result];
} else {
return null;
}
}
}
const DEFAULT_TEST_DATA: {[key: string]: any} = { const DEFAULT_TEST_DATA: {[key: string]: any} = {
'/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{ '/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{
'__symbolic': 'module', '__symbolic': 'module',
@ -1008,8 +841,6 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
} }
}, },
'/src/extern.d.ts': {'__symbolic': 'module', 'version': 3, metadata: {s: 's'}}, '/src/extern.d.ts': {'__symbolic': 'module', 'version': 3, metadata: {s: 's'}},
'/tmp/src/version-error.d.ts': {'__symbolic': 'module', 'version': 100, metadata: {e: 's'}},
'/tmp/src/version-2-error.d.ts': {'__symbolic': 'module', 'version': 2, metadata: {e: 's'}},
'/tmp/src/error-reporting.d.ts': { '/tmp/src/error-reporting.d.ts': {
__symbolic: 'module', __symbolic: 'module',
version: 3, version: 3,
@ -1343,47 +1174,4 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
@Input f: Forward; @Input f: Forward;
} }
`, `,
'/tmp/src/reexport/reexport.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {},
exports: [
{from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]},
{from: './src/origin5'}, {from: './src/reexport2'}
]
},
'/tmp/src/reexport/src/origin1.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {
One: {__symbolic: 'class'},
Two: {__symbolic: 'class'},
Three: {__symbolic: 'class'},
},
},
'/tmp/src/reexport/src/origin5.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {
Five: {__symbolic: 'class'},
},
},
'/tmp/src/reexport/src/origin30.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {
Thirty: {__symbolic: 'class'},
},
},
'/tmp/src/reexport/src/originNone.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {},
},
'/tmp/src/reexport/src/reexport2.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {},
exports: [{from: './originNone'}, {from: './origin30'}]
}
}; };

View File

@ -0,0 +1,372 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, Summary, SummaryResolver} from '@angular/compiler';
import {MetadataCollector} from '@angular/tsc-wrapped';
import * as ts from 'typescript';
// This matches .ts files but not .d.ts files.
const TS_EXT = /(^.|(?!\.d)..)\.ts$/;
describe('StaticSymbolResolver', () => {
const noContext = new StaticSymbol('', '');
let host: StaticSymbolResolverHost;
let symbolResolver: StaticSymbolResolver;
let symbolCache: StaticSymbolCache;
beforeEach(() => { symbolCache = new StaticSymbolCache(); });
function init(
testData: {[key: string]: any} = DEFAULT_TEST_DATA, summaries: Summary<StaticSymbol>[] = []) {
host = new MockStaticSymbolResolverHost(testData);
symbolResolver =
new StaticSymbolResolver(host, symbolCache, new MockSummaryResolver(summaries));
}
beforeEach(() => init());
it('should throw an exception for unsupported metadata versions', () => {
expect(
() => symbolResolver.resolveSymbol(
symbolResolver.getSymbolByModule('src/version-error', 'e')))
.toThrow(new Error(
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 3'));
});
it('should throw an exception for version 2 metadata', () => {
expect(
() => symbolResolver.resolveSymbol(
symbolResolver.getSymbolByModule('src/version-2-error', 'e')))
.toThrowError(
'Unsupported metadata version 2 for module /tmp/src/version-2-error.d.ts. This module should be compiled with a newer version of ngc');
});
it('should be produce the same symbol if asked twice', () => {
const foo1 = symbolResolver.getStaticSymbol('main.ts', 'foo');
const foo2 = symbolResolver.getStaticSymbol('main.ts', 'foo');
expect(foo1).toBe(foo2);
});
it('should be able to produce a symbol for a module with no file', () => {
expect(symbolResolver.getStaticSymbol('angularjs', 'SomeAngularSymbol')).toBeDefined();
});
it('should be able to split the metadata per symbol', () => {
init({
'/tmp/src/test.ts': `
export var a = 1;
export var b = 2;
`
});
expect(symbolResolver.resolveSymbol(symbolResolver.getStaticSymbol('/tmp/src/test.ts', 'a'))
.metadata)
.toBe(1);
expect(symbolResolver.resolveSymbol(symbolResolver.getStaticSymbol('/tmp/src/test.ts', 'b'))
.metadata)
.toBe(2);
});
it('should be able to resolve static symbols with members', () => {
init({
'/tmp/src/test.ts': `
export {exportedObj} from './export';
export var obj = {a: 1};
export class SomeClass {
static someField = 2;
}
`,
'/tmp/src/export.ts': `
export var exportedObj = {};
`,
});
expect(symbolResolver
.resolveSymbol(symbolResolver.getStaticSymbol('/tmp/src/test.ts', 'obj', ['a']))
.metadata)
.toBe(1);
expect(symbolResolver
.resolveSymbol(
symbolResolver.getStaticSymbol('/tmp/src/test.ts', 'SomeClass', ['someField']))
.metadata)
.toBe(2);
expect(symbolResolver
.resolveSymbol(symbolResolver.getStaticSymbol(
'/tmp/src/test.ts', 'exportedObj', ['someMember']))
.metadata)
.toBe(symbolResolver.getStaticSymbol('/tmp/src/export.ts', 'exportedObj', ['someMember']));
});
it('should use summaries in resolveSymbol and prefer them over regular metadata', () => {
const someSymbol = symbolCache.get('/test.ts', 'a');
init({'/test.ts': 'export var a = 2'}, [{symbol: someSymbol, metadata: 1}]);
expect(symbolResolver.resolveSymbol(someSymbol).metadata).toBe(1);
});
it('should be able to get all exported symbols of a file', () => {
expect(symbolResolver.getSymbolsOf('/tmp/src/reexport/src/origin1.d.ts')).toEqual([
symbolResolver.getStaticSymbol('/tmp/src/reexport/src/origin1.d.ts', 'One'),
symbolResolver.getStaticSymbol('/tmp/src/reexport/src/origin1.d.ts', 'Two'),
symbolResolver.getStaticSymbol('/tmp/src/reexport/src/origin1.d.ts', 'Three'),
]);
});
it('should be able to get all reexported symbols of a file', () => {
expect(symbolResolver.getSymbolsOf('/tmp/src/reexport/reexport.d.ts')).toEqual([
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'One'),
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'Two'),
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'Four'),
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'Five'),
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'Thirty')
]);
});
it('should merge the exported symbols of a file with the exported symbols of its summary', () => {
const someSymbol = symbolCache.get('/test.ts', 'a');
init(
{'/test.ts': 'export var b = 2'},
[{symbol: symbolCache.get('/test.ts', 'a'), metadata: 1}]);
expect(symbolResolver.getSymbolsOf('/test.ts')).toEqual([
symbolCache.get('/test.ts', 'a'), symbolCache.get('/test.ts', 'b')
]);
});
it('should replace references by StaticSymbols', () => {
init({
'/test.ts': `
import {b, y} from './test2';
export var a = b;
export var x = [y];
export function simpleFn(fnArg) {
return [a, y, fnArg];
}
`,
'/test2.ts': `
export var b;
export var y;
`
});
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'a')).metadata)
.toEqual(symbolCache.get('/test2.ts', 'b'));
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'x')).metadata).toEqual([
symbolCache.get('/test2.ts', 'y')
]);
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'simpleFn')).metadata).toEqual({
__symbolic: 'function',
parameters: ['fnArg'],
value: [
symbolCache.get('/test.ts', 'a'), symbolCache.get('/test2.ts', 'y'),
Object({__symbolic: 'reference', name: 'fnArg'})
]
});
});
it('should ignore module references without a name', () => {
init({
'/test.ts': `
import Default from './test2';
export {Default};
`
});
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'Default')).metadata)
.toBeFalsy();
});
it('should be able to trace a named export', () => {
const symbol = symbolResolver
.resolveSymbol(symbolResolver.getSymbolByModule(
'./reexport/reexport', 'One', '/tmp/src/main.ts'))
.metadata;
expect(symbol.name).toEqual('One');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
});
it('should be able to trace a renamed export', () => {
const symbol = symbolResolver
.resolveSymbol(symbolResolver.getSymbolByModule(
'./reexport/reexport', 'Four', '/tmp/src/main.ts'))
.metadata;
expect(symbol.name).toEqual('Three');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
});
it('should be able to trace an export * export', () => {
const symbol = symbolResolver
.resolveSymbol(symbolResolver.getSymbolByModule(
'./reexport/reexport', 'Five', '/tmp/src/main.ts'))
.metadata;
expect(symbol.name).toEqual('Five');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin5.d.ts');
});
it('should be able to trace a multi-level re-export', () => {
const symbol1 = symbolResolver
.resolveSymbol(symbolResolver.getSymbolByModule(
'./reexport/reexport', 'Thirty', '/tmp/src/main.ts'))
.metadata;
expect(symbol1.name).toEqual('Thirty');
expect(symbol1.filePath).toEqual('/tmp/src/reexport/src/reexport2.d.ts');
const symbol2 = symbolResolver.resolveSymbol(symbol1).metadata;
expect(symbol2.name).toEqual('Thirty');
expect(symbol2.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts');
});
it('should cache tracing a named export', () => {
const moduleNameToFileNameSpy = spyOn(host, 'moduleNameToFileName').and.callThrough();
const getMetadataForSpy = spyOn(host, 'getMetadataFor').and.callThrough();
symbolResolver.resolveSymbol(
symbolResolver.getSymbolByModule('./reexport/reexport', 'One', '/tmp/src/main.ts'));
moduleNameToFileNameSpy.calls.reset();
getMetadataForSpy.calls.reset();
const symbol = symbolResolver
.resolveSymbol(symbolResolver.getSymbolByModule(
'./reexport/reexport', 'One', '/tmp/src/main.ts'))
.metadata;
expect(moduleNameToFileNameSpy.calls.count()).toBe(1);
expect(getMetadataForSpy.calls.count()).toBe(0);
expect(symbol.name).toEqual('One');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
});
});
export class MockSummaryResolver implements SummaryResolver<StaticSymbol> {
constructor(private summaries: Summary<StaticSymbol>[] = []) {}
resolveSummary(reference: StaticSymbol): Summary<StaticSymbol> {
return this.summaries.find(summary => summary.symbol === reference);
};
getSymbolsOf(filePath: string): StaticSymbol[] {
return this.summaries.filter(summary => summary.symbol.filePath === filePath)
.map(summary => summary.symbol);
}
}
export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost {
private collector = new MetadataCollector();
constructor(private data: {[key: string]: any}) {}
// In tests, assume that symbols are not re-exported
moduleNameToFileName(modulePath: string, containingFile?: string): string {
function splitPath(path: string): string[] { return path.split(/\/|\\/g); }
function resolvePath(pathParts: string[]): string {
const result: string[] = [];
pathParts.forEach((part, index) => {
switch (part) {
case '':
case '.':
if (index > 0) return;
break;
case '..':
if (index > 0 && result.length != 0) result.pop();
return;
}
result.push(part);
});
return result.join('/');
}
function pathTo(from: string, to: string): string {
let result = to;
if (to.startsWith('.')) {
const fromParts = splitPath(from);
fromParts.pop(); // remove the file name.
const toParts = splitPath(to);
result = resolvePath(fromParts.concat(toParts));
}
return result;
}
if (modulePath.indexOf('.') === 0) {
const baseName = pathTo(containingFile, modulePath);
const tsName = baseName + '.ts';
if (this._getMetadataFor(tsName)) {
return tsName;
}
return baseName + '.d.ts';
}
return '/tmp/' + modulePath + '.d.ts';
}
getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); }
private _getMetadataFor(filePath: string): any {
if (this.data[filePath] && filePath.match(TS_EXT)) {
const text = this.data[filePath];
if (typeof text === 'string') {
const sf = ts.createSourceFile(
filePath, this.data[filePath], ts.ScriptTarget.ES5, /* setParentNodes */ true);
const diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
if (diagnostics && diagnostics.length) {
throw Error(`Error encountered during parse of file ${filePath}`);
}
return [this.collector.getMetadata(sf)];
}
}
const result = this.data[filePath];
if (result) {
return Array.isArray(result) ? result : [result];
} else {
return null;
}
}
}
const DEFAULT_TEST_DATA: {[key: string]: any} = {
'/tmp/src/version-error.d.ts': {'__symbolic': 'module', 'version': 100, metadata: {e: 's'}},
'/tmp/src/version-2-error.d.ts': {'__symbolic': 'module', 'version': 2, metadata: {e: 's'}},
'/tmp/src/reexport/reexport.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {},
exports: [
{from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]},
{from: './src/origin5'}, {from: './src/reexport2'}
]
},
'/tmp/src/reexport/src/origin1.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {
One: {__symbolic: 'class'},
Two: {__symbolic: 'class'},
Three: {__symbolic: 'class'},
},
},
'/tmp/src/reexport/src/origin5.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {
Five: {__symbolic: 'class'},
},
},
'/tmp/src/reexport/src/origin30.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {
Thirty: {__symbolic: 'class'},
},
},
'/tmp/src/reexport/src/originNone.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {},
},
'/tmp/src/reexport/src/reexport2.d.ts': {
__symbolic: 'module',
version: 3,
metadata: {},
exports: [{from: './originNone'}, {from: './origin30'}]
}
};

View File

@ -6,128 +6,68 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AotSummaryResolver, AotSummaryResolverHost, CompileNgModuleSummary, CompileSummaryKind, CompileTypeSummary, StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler'; import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, CompileTypeSummary, ResolvedStaticSymbol, StaticSymbol, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
import {AotSummarySerializerHost} 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 {MockStaticReflectorHost} from './static_reflector_spec'; import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';
const EXT = /\.ts$|.d.ts$/; const EXT = /\.ts$|.d.ts$/;
export function main() { export function main() {
describe('AotSummaryResolver', () => { describe('AotSummaryResolver', () => {
let resolver: AotSummaryResolver; let summaryResolver: AotSummaryResolver;
let staticReflector: StaticReflector; let symbolCache: StaticSymbolCache;
let host: MockAotSummaryResolverHost;
beforeEach(() => { symbolCache = new StaticSymbolCache(); });
function init(summaries: {[filePath: string]: string} = {}) { function init(summaries: {[filePath: string]: string} = {}) {
// Note: We don't give the static reflector metadata files, host = new MockAotSummaryResolverHost(summaries);
// so that we can test that we can deserialize summary files summaryResolver = new AotSummaryResolver(host, symbolCache);
// without reading metadata files. This is important
// as summary files can contain references to files of transitive compilation
// dependencies, and we don't want to read their metadata files.
staticReflector = new StaticReflector(new MockStaticReflectorHost({}));
const host = new MockAotSummaryResolverHost(summaries);
resolver = new AotSummaryResolver(host, staticReflector, {excludeFilePattern: /\.d\.ts$/});
} }
it('should add .ngsummary.json to the filename', () => { function serialize(symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string {
init(); // Note: Don't use the top level host / summaryResolver as they might not be created yet
expect(resolver.serializeSummaries('a.ts', []).genFileUrl).toBe('a.ngsummary.json'); const mockSummaryResolver = new MockSummaryResolver([]);
expect(resolver.serializeSummaries('a.d.ts', []).genFileUrl).toBe('a.ngsummary.json'); const symbolResolver = new StaticSymbolResolver(
expect(resolver.serializeSummaries('a.js', []).genFileUrl).toBe('a.ngsummary.json'); new MockStaticSymbolResolverHost({}), symbolCache, mockSummaryResolver);
return serializeSummaries(
new MockAotSummarySerializerHost(), mockSummaryResolver, symbolResolver, symbols, types);
}
it('should load serialized summary files', () => {
const asymbol = symbolCache.get('/a.d.ts', 'a');
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
expect(summaryResolver.resolveSummary(asymbol)).toEqual({symbol: asymbol, metadata: 1});
}); });
it('should serialize various data correctly', () => { it('should not load summaries for source files', () => {
init(); init({});
const serializedData = resolver.serializeSummaries( spyOn(host, 'loadSummary').and.callThrough();
'/tmp/some_pipe.ts', [<any>{
summaryKind: CompileSummaryKind.Pipe,
type: {
reference: staticReflector.getStaticSymbol('/tmp/some_pipe.ts', 'SomePipe'),
},
aNumber: 1,
aString: 'hello',
anArray: [1, 2],
aStaticSymbol:
staticReflector.getStaticSymbol('/tmp/some_symbol.ts', 'someName', ['someMember'])
}]);
// Note: this creates a new staticReflector! expect(summaryResolver.resolveSummary(symbolCache.get('/a.ts', 'a'))).toBeFalsy();
init({[serializedData.genFileUrl]: serializedData.source}); expect(host.loadSummary).not.toHaveBeenCalled();
const deserialized = resolver.resolveSummary(
staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomePipe'));
expect(deserialized.aNumber).toBe(1);
expect(deserialized.aString).toBe('hello');
expect(deserialized.anArray).toEqual([1, 2]);
expect(deserialized.aStaticSymbol instanceof StaticSymbol).toBe(true);
// Note: change from .ts to .d.ts is expected
expect(deserialized.aStaticSymbol)
.toEqual(
staticReflector.getStaticSymbol('/tmp/some_symbol.d.ts', 'someName', ['someMember']));
}); });
it('should store reexports in the same file', () => { it('should cache summaries', () => {
init(); const asymbol = symbolCache.get('/a.d.ts', 'a');
const reexportedData = resolver.serializeSummaries( init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
'/tmp/some_pipe.ts', [{ expect(summaryResolver.resolveSummary(asymbol)).toBe(summaryResolver.resolveSummary(asymbol));
summaryKind: CompileSummaryKind.Pipe,
type: {
reference: staticReflector.getStaticSymbol('/tmp/some_pipe.ts', 'SomeReexportedPipe'),
diDeps: [],
lifecycleHooks: []
},
}]);
init({[reexportedData.genFileUrl]: reexportedData.source});
const serializedData = resolver.serializeSummaries('/tmp/some_module.ts', [
<CompileNgModuleSummary>{
summaryKind: CompileSummaryKind.NgModule,
type: {
reference: staticReflector.getStaticSymbol('/tmp/some_module.ts', 'SomeModule'),
diDeps: [],
lifecycleHooks: []
},
exportedPipes: [{
reference: staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomeReexportedPipe')
}],
exportedDirectives: [],
providers: [],
entryComponents: [],
modules: []
}
]);
init({[serializedData.genFileUrl]: serializedData.source});
resolver.resolveSummary(
staticReflector.getStaticSymbol('/tmp/some_module.d.ts', 'SomeModule'));
expect(resolver.resolveSummary(
staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomeReexportedPipe')))
.toEqual({
summaryKind: CompileSummaryKind.Pipe,
type: {
reference:
staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomeReexportedPipe'),
diDeps: [],
lifecycleHooks: []
},
});
}); });
it('should return all sumbols in a summary', () => {
const asymbol = symbolCache.get('/a.d.ts', 'a');
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
expect(summaryResolver.getSymbolsOf('/a.d.ts')).toEqual([asymbol]);
});
}); });
} }
class MockAotSummaryResolverHost implements AotSummaryResolverHost {
constructor(private summaries: {[fileName: string]: string}) {}
loadSummary(filePath: string): string {
const result = this.summaries[filePath];
if (!result) {
throw new Error(`Could not find summary for ${filePath}`);
}
return result;
}
export class MockAotSummarySerializerHost implements AotSummarySerializerHost {
fileNameToModuleName(fileName: string): string { fileNameToModuleName(fileName: string): string {
return './' + path.basename(fileName).replace(EXT, ''); return './' + path.basename(fileName).replace(EXT, '');
} }
@ -135,4 +75,13 @@ class MockAotSummaryResolverHost implements AotSummaryResolverHost {
getOutputFileName(sourceFileName: string): string { getOutputFileName(sourceFileName: string): string {
return sourceFileName.replace(EXT, '') + '.d.ts'; return sourceFileName.replace(EXT, '') + '.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]; }
} }

View File

@ -0,0 +1,199 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler';
import {AotSummarySerializerHost, deserializeSummaries, serializeSummaries, summaryFileName} from '@angular/compiler/src/aot/summary_serializer';
import {MockStaticSymbolResolverHost} from './static_symbol_resolver_spec';
import {MockAotSummaryResolverHost} from './summary_resolver_spec';
export function main() {
describe('summary serializer', () => {
let summaryResolver: AotSummaryResolver;
let symbolResolver: StaticSymbolResolver;
let symbolCache: StaticSymbolCache;
let host: MockAotSummaryResolverHost;
beforeEach(() => { symbolCache = new StaticSymbolCache(); });
function init(
summaries: {[filePath: string]: string} = {}, metadata: {[key: string]: any} = {}) {
host = new MockAotSummaryResolverHost(summaries);
summaryResolver = new AotSummaryResolver(host, symbolCache);
symbolResolver = new StaticSymbolResolver(
new MockStaticSymbolResolverHost(metadata), symbolCache, summaryResolver);
}
describe('summaryFileName', () => {
it('should add .ngsummary.json to the filename', () => {
init();
expect(summaryFileName('a.ts')).toBe('a.ngsummary.json');
expect(summaryFileName('a.d.ts')).toBe('a.ngsummary.json');
expect(summaryFileName('a.js')).toBe('a.ngsummary.json');
});
});
it('should serialize various data correctly', () => {
init();
const serializedData = serializeSummaries(
host, summaryResolver, symbolResolver,
[
{
symbol: symbolCache.get('/tmp/some_values.ts', 'Values'),
metadata: {
aNumber: 1,
aString: 'hello',
anArray: [1, 2],
aStaticSymbol: symbolCache.get('/tmp/some_symbol.ts', 'someName')
}
},
{
symbol: symbolCache.get('/tmp/some_service.ts', 'SomeService'),
metadata: {
__symbolic: 'class',
members: {'aMethod': {__symbolic: 'function'}},
statics: {aStatic: true}
}
}
],
[<any>{
summaryKind: CompileSummaryKind.Injectable,
type: {
reference: symbolCache.get('/tmp/some_service.ts', 'SomeService'),
},
}]);
const summaries = deserializeSummaries(symbolCache, serializedData);
expect(summaries.length).toBe(2);
// Note: change from .ts to .d.ts is expected
expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/some_values.d.ts', 'Values'));
expect(summaries[0].metadata).toEqual({
aNumber: 1,
aString: 'hello',
anArray: [1, 2],
aStaticSymbol: symbolCache.get('/tmp/some_symbol.d.ts', 'someName')
});
expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService'));
// serialization should only keep the statics...
expect(summaries[1].metadata).toEqual({__symbolic: 'class', statics: {aStatic: true}});
expect(summaries[1].type.type.reference)
.toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService'));
});
it('should automatically add exported directives / pipes of NgModules that are not source files',
() => {
init({});
const externalSerialized = serializeSummaries(host, summaryResolver, symbolResolver, [], [
<any>{
summaryKind: CompileSummaryKind.Pipe,
type: {
reference: symbolCache.get('/tmp/external.ts', 'SomeExternalPipe'),
}
},
<any>{
summaryKind: CompileSummaryKind.Directive,
type: {
reference: symbolCache.get('/tmp/external.ts', 'SomeExternalDir'),
}
}
]);
init({
'/tmp/external.ngsummary.json': externalSerialized,
});
const serialized = serializeSummaries(
host, summaryResolver, symbolResolver, [], [<any>{
summaryKind: CompileSummaryKind.NgModule,
type: {reference: symbolCache.get('/tmp/some_module.ts', 'SomeModule')},
exportedPipes: [
{reference: symbolCache.get('/tmp/some_pipe.ts', 'SomePipe')},
{reference: symbolCache.get('/tmp/external.d.ts', 'SomeExternalPipe')}
],
exportedDirectives: [
{reference: symbolCache.get('/tmp/some_dir.ts', 'SomeDir')},
{reference: symbolCache.get('/tmp/external.d.ts', 'SomeExternalDir')}
]
}]);
const summaries = deserializeSummaries(symbolCache, serialized);
expect(summaries.length).toBe(3);
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[2].symbol)
.toBe(symbolCache.get('/tmp/external.d.ts', 'SomeExternalPipe'));
});
it('should automatically add the metadata of referenced symbols that are not in the soure files',
() => {
const externalSerialized = serializeSummaries(
host, summaryResolver, symbolResolver,
[
{
symbol: symbolCache.get('/tmp/external.ts', 'PROVIDERS'),
metadata: [symbolCache.get('/tmp/external_svc.ts', 'SomeService')]
},
{
symbol: symbolCache.get('/tmp/external_svc.ts', 'SomeService'),
metadata: {__symbolic: 'class'}
}
],
[<any>{
summaryKind: CompileSummaryKind.Injectable,
type: {
reference: symbolCache.get('/tmp/external_svc.ts', 'SomeService'),
}
}]);
init(
{
'/tmp/external.ngsummary.json': externalSerialized,
},
{
'/tmp/local.ts': `
export var local = 'a';
`,
'/tmp/non_summary.d.ts':
{__symbolic: 'module', version: 3, metadata: {'external': 'b'}}
});
const serialized = serializeSummaries(
host, summaryResolver, symbolResolver, [{
symbol: symbolCache.get('/tmp/test.ts', 'main'),
metadata: {
local: symbolCache.get('/tmp/local.ts', 'local'),
external: symbolCache.get('/tmp/external.d.ts', 'PROVIDERS'),
externalNonSummary: symbolCache.get('/tmp/non_summary.d.ts', 'external')
}
}],
[]);
const summaries = deserializeSummaries(symbolCache, serialized);
// Note: local should not show up!
expect(summaries.length).toBe(4);
expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/test.d.ts', 'main'));
expect(summaries[0].metadata).toEqual({
local: symbolCache.get('/tmp/local.d.ts', 'local'),
external: symbolCache.get('/tmp/external.d.ts', 'PROVIDERS'),
externalNonSummary: symbolCache.get('/tmp/non_summary.d.ts', 'external')
});
expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/external.d.ts', 'PROVIDERS'));
expect(summaries[1].metadata).toEqual([symbolCache.get(
'/tmp/external_svc.d.ts', 'SomeService')]);
// there was no summary for non_summary, but it should have
// been serialized as well.
expect(summaries[2].symbol).toBe(symbolCache.get('/tmp/non_summary.d.ts', 'external'));
expect(summaries[2].metadata).toEqual('b');
// SomService is a transitive dep, but sould have been serialized as well.
expect(summaries[3].symbol).toBe(symbolCache.get('/tmp/external_svc.d.ts', 'SomeService'));
expect(summaries[3].type.type.reference)
.toBe(symbolCache.get('/tmp/external_svc.d.ts', 'SomeService'));
});
});
}

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 {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler'; import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler';
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';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter'; import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
@ -14,6 +14,8 @@ import {convertValueToOutputAst} from '@angular/compiler/src/output/value_util';
import {MetadataCollector, isClassMetadata, isMetadataSymbolicCallExpression} from '@angular/tsc-wrapped'; import {MetadataCollector, isClassMetadata, isMetadataSymbolicCallExpression} from '@angular/tsc-wrapped';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {MockSummaryResolver} from '../aot/static_symbol_resolver_spec';
describe('TypeScriptEmitter (node only)', () => { describe('TypeScriptEmitter (node only)', () => {
it('should quote identifiers quoted in the source', () => { it('should quote identifiers quoted in the source', () => {
const sourceText = ` const sourceText = `
@ -27,7 +29,10 @@ describe('TypeScriptEmitter (node only)', () => {
const source = ts.createSourceFile('test.ts', sourceText, ts.ScriptTarget.Latest); const source = ts.createSourceFile('test.ts', sourceText, ts.ScriptTarget.Latest);
const collector = new MetadataCollector({quotedNames: true}); const collector = new MetadataCollector({quotedNames: true});
const stubHost = new StubReflectorHost(); const stubHost = new StubReflectorHost();
const reflector = new StaticReflector(stubHost); const symbolCache = new StaticSymbolCache();
const symbolResolver =
new StaticSymbolResolver(stubHost, symbolCache, new MockSummaryResolver());
const reflector = new StaticReflector(symbolResolver);
// Get the metadata from the above source // Get the metadata from the above source
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source);
@ -62,9 +67,9 @@ describe('TypeScriptEmitter (node only)', () => {
}); });
}); });
class StubReflectorHost implements StaticReflectorHost { class StubReflectorHost implements StaticSymbolResolverHost {
getMetadataFor(modulePath: string): {[key: string]: any}[] { return []; } getMetadataFor(modulePath: string): {[key: string]: any}[] { return []; }
moduleNameToFileName(moduleName: string, containingFile: string): string { return ''; } moduleNameToFileName(moduleName: string, containingFile: string): string { return 'somePath'; }
} }
class StubImportResolver extends ImportResolver { class StubImportResolver extends ImportResolver {

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 {OpaqueToken} from '../di'; import {Injectable, OpaqueToken} from '../di';
import {BaseError} from '../facade/errors'; import {BaseError} from '../facade/errors';
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
import {ViewEncapsulation} from '../metadata'; import {ViewEncapsulation} from '../metadata';
@ -54,6 +54,7 @@ function _throwError() {
* of components. * of components.
* @stable * @stable
*/ */
@Injectable()
export class Compiler { export class Compiler {
/** /**
* Compiles the given NgModule and all of its components. All templates of the components listed * Compiles the given NgModule and all of its components. All templates of the components listed

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, CompilerConfig, StaticReflector, StaticSymbol, StaticSymbolCache, componentModuleUrl, createOfflineCompileUrlResolver} from '@angular/compiler'; import {AotSummaryResolver, CompileDirectiveMetadata, CompilerConfig, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, componentModuleUrl, createOfflineCompileUrlResolver} from '@angular/compiler';
import {NgAnalyzedModules, analyzeNgModules, extractProgramSymbols} from '@angular/compiler/src/aot/compiler'; import {NgAnalyzedModules, analyzeNgModules, extractProgramSymbols} from '@angular/compiler/src/aot/compiler';
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer'; import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
import {DirectiveResolver} from '@angular/compiler/src/directive_resolver'; import {DirectiveResolver} from '@angular/compiler/src/directive_resolver';
@ -30,6 +30,7 @@ import {ReflectorHost} from './reflector_host';
import {BuiltinType, CompletionKind, Declaration, DeclarationError, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types'; import {BuiltinType, CompletionKind, Declaration, DeclarationError, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types';
/** /**
* Create a `LanguageServiceHost` * Create a `LanguageServiceHost`
*/ */
@ -75,6 +76,7 @@ export class DummyResourceLoader extends ResourceLoader {
export class TypeScriptServiceHost implements LanguageServiceHost { export class TypeScriptServiceHost implements LanguageServiceHost {
private _resolver: CompileMetadataResolver; private _resolver: CompileMetadataResolver;
private _staticSymbolCache = new StaticSymbolCache(); private _staticSymbolCache = new StaticSymbolCache();
private _staticSymbolResolver: StaticSymbolResolver;
private _reflector: StaticReflector; private _reflector: StaticReflector;
private _reflectorHost: ReflectorHost; private _reflectorHost: ReflectorHost;
private _checker: ts.TypeChecker; private _checker: ts.TypeChecker;
@ -159,10 +161,13 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
private ensureAnalyzedModules(): NgAnalyzedModules { private ensureAnalyzedModules(): NgAnalyzedModules {
let analyzedModules = this.analyzedModules; let analyzedModules = this.analyzedModules;
if (!analyzedModules) { if (!analyzedModules) {
const analyzeHost = {isSourceFile(filePath: string) { return true; }};
const programSymbols = extractProgramSymbols( const programSymbols = extractProgramSymbols(
this.reflector, this.program.getSourceFiles().map(sf => sf.fileName), {}); this.staticSymbolResolver, this.program.getSourceFiles().map(sf => sf.fileName),
analyzeHost);
analyzedModules = this.analyzedModules = analyzeNgModules(programSymbols, {}, this.resolver); analyzedModules = this.analyzedModules =
analyzeNgModules(programSymbols, analyzeHost, this.resolver);
} }
return analyzedModules; return analyzedModules;
} }
@ -224,6 +229,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (this.modulesOutOfDate) { if (this.modulesOutOfDate) {
this.analyzedModules = null; this.analyzedModules = null;
this._reflector = null; this._reflector = null;
this._staticSymbolResolver = null;
this.templateReferences = null; this.templateReferences = null;
this.fileToComponent = null; this.fileToComponent = null;
this.ensureAnalyzedModules(); this.ensureAnalyzedModules();
@ -385,12 +391,27 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
errors.push(error); errors.push(error);
} }
private get staticSymbolResolver(): StaticSymbolResolver {
let result = this._staticSymbolResolver;
if (!result) {
const summaryResolver = new AotSummaryResolver(
{
loadSummary(filePath: string) { return null; },
isSourceFile(sourceFilePath: string) { return true; }
},
this._staticSymbolCache);
result = this._staticSymbolResolver = new StaticSymbolResolver(
this.reflectorHost, this._staticSymbolCache, summaryResolver,
(e, filePath) => this.collectError(e, filePath));
}
return result;
}
private get reflector(): StaticReflector { private get reflector(): StaticReflector {
let result = this._reflector; let result = this._reflector;
if (!result) { if (!result) {
result = this._reflector = new StaticReflector( result = this._reflector = new StaticReflector(
this.reflectorHost, this._staticSymbolCache, [], [], this.staticSymbolResolver, [], [], (e, filePath) => this.collectError(e, filePath));
(e, filePath) => this.collectError(e, filePath));
} }
return result; return result;
} }