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
@@ -34,9 +34,9 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT ex (#PCDATA)>
]>
<messagebundle>
<msg id="3772663375917578720">other-3rdP-component</msg>
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
<msg id="3492007542396725315">Welcome</msg>
<msg id="3772663375917578720">other-3rdP-component</msg>
</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">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
<source>other-3rdP-component</source>
<target/>
</trans-unit>
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
<source>translate me</source>
<target/>
@@ -58,6 +54,10 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<source>Welcome</source>
<target/>
</trans-unit>
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
<source>other-3rdP-component</source>
<target/>
</trans-unit>
</body>
</file>
</xliff>
@@ -52,12 +52,9 @@ function codeGenTest() {
const config = tsc.readConfiguration(project, basePath);
const hostContext = new NodeCompilerHostContext();
const delegateHost = ts.createCompilerHost(config.parsed.options, true);
const host: ts.CompilerHost = Object.assign({}, delegateHost, {
writeFile: (fileName: string, ...rest: any[]) => {
wroteFiles.push(fileName);
return delegateHost.writeFile.call(delegateHost, fileName, ...rest);
}
});
const host: ts.CompilerHost = Object.assign(
{}, delegateHost,
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
config.ngOptions.basePath = basePath;
@@ -27,9 +27,14 @@ function main() {
const basePath = path.resolve(__dirname, '..');
const project = path.resolve(basePath, 'tsconfig-build.json');
const readFiles: string[] = [];
const writtenFiles: {fileName: string, content: string}[] = [];
class AssertingHostContext extends NodeCompilerHostContext {
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));
return super.readFile(fileName);
}
@@ -45,16 +50,29 @@ function main() {
config.ngOptions.generateCodeForLibraries = false;
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) => {
console.log(`>>> codegen done, asserting read files`);
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.metadata.json$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
console.log(`>>> asserting written files`);
assertWrittenFile(writtenFiles, /^src\/module\.ngfactory\.ts$/, /class MainModuleInjector/);
console.log(`done, no errors.`);
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')}`);
}
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();
+1 -9
View File
@@ -21,9 +21,7 @@ import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
import {Console} from './private_import_core';
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
const GENERATED_META_FILES = /\.json$/;
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
const PREAMBLE = `/**
* @fileoverview This file is generated by the Angular 2 template compiler.
@@ -102,14 +100,8 @@ export class CodeGenerator {
debug: options.debug === true,
translations: transContent,
i18nFormat: cliOptions.i18nFormat,
locale: cliOptions.locale,
excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES :
GENERATED_FILES
locale: cliOptions.locale
});
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
}
}
export function excludeFilePattern(options: AngularCompilerOptions): RegExp {
return options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
}
@@ -16,6 +16,8 @@ const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
const DTS = /\.d\.ts$/;
const NODE_MODULES = '/node_modules/';
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 {
readResource(fileName: string): Promise<string>;
@@ -28,6 +30,7 @@ export class CompilerHost implements AotCompilerHost {
protected basePath: string;
private genDir: string;
private resolverCache = new Map<string, ModuleMetadata[]>();
protected resolveModuleNameHost: CompilerHostContext;
constructor(
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);
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.
getCanonicalFileName(fileName: string): string { return fileName; }
moduleNameToFileName(m: string, containingFile: string) {
moduleNameToFileName(m: string, containingFile: string): string|null {
if (!containingFile || !containingFile.length) {
if (m.indexOf('.') === 0) {
throw new Error('Resolution of relative paths requires a containing file.');
@@ -53,7 +75,8 @@ export class CompilerHost implements AotCompilerHost {
}
m = m.replace(EXT, '');
const resolved =
ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context)
ts.resolveModuleName(
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
.resolvedModule;
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); }
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 {
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 {
@@ -17,7 +17,6 @@ import * as compiler from '@angular/compiler';
import * as tsc from '@angular/tsc-wrapped';
import * as ts from 'typescript';
import {excludeFilePattern} from './codegen';
import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';
export class Extractor {
@@ -36,8 +35,7 @@ export class Extractor {
if (!ngCompilerHost)
ngCompilerHost =
new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
const {extractor: ngExtractor} = compiler.Extractor.create(
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
const {extractor: ngExtractor} = compiler.Extractor.create(ngCompilerHost);
return new Extractor(ngExtractor, ngCompilerHost, program);
}
}
@@ -13,7 +13,7 @@
* 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 * as ts from 'typescript';
@@ -111,7 +111,10 @@ export class NgTools_InternalApi_NG_2 {
new PathMappedCompilerHost(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);
return Object.keys(routeMap).reduce(
@@ -29,13 +29,17 @@ describe('compiler-cli', () => {
"types": [],
"outDir": "built",
"declaration": true,
"module": "es2015"
"module": "es2015",
"moduleResolution": "node"
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true
},
"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
+2
View File
@@ -32,7 +32,9 @@ export * from './src/aot/compiler_host';
export * from './src/aot/static_reflector';
export * from './src/aot/static_reflection_capabilities';
export * from './src/aot/static_symbol';
export * from './src/aot/static_symbol_resolver';
export * from './src/aot/summary_resolver';
export * from './src/summary_resolver';
export {JitCompiler} from './src/jit/compiler';
export * from './src/jit/compiler_factory';
export * from './src/url_resolver';
@@ -6,11 +6,10 @@
* 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 {StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
import {ParseError} from '../parse_util';
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
@@ -35,7 +34,7 @@ export class AnimationEntryParseResult {
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
}
@Injectable()
@CompilerInjectable()
export class AnimationParser {
constructor(private _schema: ElementSchemaRegistry) {}
+83 -68
View File
@@ -20,34 +20,34 @@ import {NgModuleCompiler} from '../ng_module_compiler';
import {OutputEmitter} from '../output/abstract_emitter';
import * as o from '../output/output_ast';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser';
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 {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol';
import {AotSummaryResolver} from './summary_resolver';
import {filterFileByPatterns} from './utils';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {serializeSummaries, summaryFileName} from './summary_serializer';
export class AotCompiler {
private _animationCompiler = new AnimationCompiler();
constructor(
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _dirWrapperCompiler: DirectiveWrapperCompiler,
private _host: AotCompilerHost, private _metadataResolver: CompileMetadataResolver,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _dirWrapperCompiler: DirectiveWrapperCompiler,
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 _staticReflector: StaticReflector, private _options: AotCompilerOptions) {}
private _symbolResolver: StaticSymbolResolver) {}
clearCache() { this._metadataResolver.clearCache(); }
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} =
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
return Promise
.all(ngModules.map(
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
@@ -56,27 +56,21 @@ export class AotCompiler {
const sourceModules = files.map(
file => this._compileSrcFile(
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
file.ngModules));
file.ngModules, file.injectables));
return ListWrapper.flatten(sourceModules);
});
}
private _compileSrcFile(
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[]): GeneratedFile[] {
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
injectables: StaticSymbol[]): GeneratedFile[] {
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
const statements: o.Statement[] = [];
const exportedVars: string[] = [];
const generatedFiles: GeneratedFile[] = [];
// write summary files
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));
generatedFiles.push(this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables));
// compile all ng modules
exportedVars.push(
@@ -121,6 +115,22 @@ export class AotCompiler {
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 {
const ngModule = this._metadataResolver.getNgModuleMetadata(ngModuleType);
const providers: CompileProviderMetadata[] = [];
@@ -142,7 +152,7 @@ export class AotCompiler {
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.reference = this._staticReflector.getStaticSymbol(
dep.placeholder.reference = this._symbolResolver.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
});
@@ -163,7 +173,7 @@ export class AotCompiler {
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string {
const hostMeta = createHostComponentMeta(
this._staticReflector.getStaticSymbol(
this._symbolResolver.getStaticSymbol(
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
compMeta);
const hostViewFactoryVar = this._compileComponent(
@@ -206,16 +216,16 @@ export class AotCompiler {
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
if (componentStyles) {
targetStatements.push(
..._resolveStyleStatements(this._staticReflector, componentStyles, fileSuffix));
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
}
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
targetStatements.push(..._resolveViewStatements(this._staticReflector, viewResult));
targetStatements.push(..._resolveViewStatements(this._symbolResolver, viewResult));
return viewResult.viewClassVar;
}
private _codgenStyles(
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile {
_resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix);
_resolveStyleStatements(this._symbolResolver, stylesCompileResult, fileSuffix);
return this._codegenSourceModule(
fileUrl, _stylesModuleUrl(
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
@@ -232,7 +242,7 @@ export class AotCompiler {
}
function _resolveViewStatements(
reflector: StaticReflector, compileResult: ViewCompileResult): o.Statement[] {
reflector: StaticSymbolResolver, compileResult: ViewCompileResult): o.Statement[] {
compileResult.dependencies.forEach((dep) => {
if (dep instanceof ViewClassDependency) {
const vfd = <ViewClassDependency>dep;
@@ -253,7 +263,7 @@ function _resolveViewStatements(
function _resolveStyleStatements(
reflector: StaticReflector, compileResult: CompiledStylesheet,
reflector: StaticSymbolResolver, compileResult: CompiledStylesheet,
fileSuffix: string): o.Statement[] {
compileResult.dependencies.forEach((dep) => {
dep.valuePlaceholder.reference = reflector.getStaticSymbol(
@@ -303,26 +313,27 @@ export interface NgAnalyzedModules {
srcUrl: string,
directives: StaticSymbol[],
pipes: StaticSymbol[],
ngModules: StaticSymbol[]
ngModules: StaticSymbol[],
injectables: StaticSymbol[]
}>;
symbolsMissingModule?: StaticSymbol[];
}
export interface NgAnalyzeModulesHost { isSourceFile(filePath: string): boolean; }
// Returns all the source files and a mapping from modules to directives
export function analyzeNgModules(
programStaticSymbols: StaticSymbol[],
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const {ngModules, symbolsMissingModule} =
_createNgModules(programStaticSymbols, options, metadataResolver);
return _analyzeNgModules(ngModules, symbolsMissingModule);
_createNgModules(programStaticSymbols, host, metadataResolver);
return _analyzeNgModules(programStaticSymbols, ngModules, symbolsMissingModule, metadataResolver);
}
export function analyzeAndValidateNgModules(
programStaticSymbols: StaticSymbol[],
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const result = analyzeNgModules(programStaticSymbols, options, metadataResolver);
const result = analyzeNgModules(programStaticSymbols, host, metadataResolver);
if (result.symbolsMissingModule && result.symbolsMissingModule.length) {
const messages = result.symbolsMissingModule.map(
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
@@ -332,16 +343,27 @@ export function analyzeAndValidateNgModules(
}
function _analyzeNgModules(
ngModuleMetas: CompileNgModuleMetadata[],
symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules {
programSymbols: StaticSymbol[], ngModuleMetas: CompileNgModuleMetadata[],
symbolsMissingModule: StaticSymbol[],
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
const ngModulesByFile = new Map<string, StaticSymbol[]>();
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
const ngPipesByFile = new Map<string, StaticSymbol[]>();
const ngInjectablesByFile = new Map<string, StaticSymbol[]>();
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:
// - a map from file to modules `ngModulesByFile`,
// - a map from file to directives `ngDirectivesByFile`,
@@ -369,17 +391,20 @@ function _analyzeNgModules(
});
});
const files:
{srcUrl: string,
directives: StaticSymbol[],
pipes: StaticSymbol[],
ngModules: StaticSymbol[]}[] = [];
const files: {
srcUrl: string,
directives: StaticSymbol[],
pipes: StaticSymbol[],
ngModules: StaticSymbol[],
injectables: StaticSymbol[]
}[] = [];
filePaths.forEach((srcUrl) => {
const directives = ngDirectivesByFile.get(srcUrl) || [];
const pipes = ngPipesByFile.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 {
@@ -392,29 +417,20 @@ function _analyzeNgModules(
}
export function extractProgramSymbols(
staticReflector: StaticReflector, files: string[],
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] {
staticSymbolResolver: StaticSymbolResolver, files: string[],
host: NgAnalyzeModulesHost): StaticSymbol[] {
const staticSymbols: StaticSymbol[] = [];
files.filter(fileName => filterFileByPatterns(fileName, options)).forEach(sourceFile => {
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
if (!moduleMetadata) {
console.error(`WARNING: no metadata found for ${sourceFile}`);
return;
}
const metadata = moduleMetadata['metadata'];
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;
files.filter(fileName => host.isSourceFile(fileName)).forEach(sourceFile => {
staticSymbolResolver.getSymbolsOf(sourceFile).forEach((symbol) => {
const resolvedSymbol = staticSymbolResolver.resolveSymbol(symbol);
const symbolMeta = resolvedSymbol.metadata;
if (symbolMeta) {
if (symbolMeta.__symbolic != 'error') {
// Ignore symbols that are only included to record error information.
staticSymbols.push(resolvedSymbol.symbol);
}
}
staticSymbols.push(staticReflector.getStaticSymbol(sourceFile, symbol));
}
});
});
return staticSymbols;
@@ -424,8 +440,7 @@ export function extractProgramSymbols(
// that all directives / pipes that are present in the program
// are also declared by a module.
function _createNgModules(
programStaticSymbols: StaticSymbol[],
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
metadataResolver: CompileMetadataResolver):
{ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} {
const ngModules = new Map<any, CompileNgModuleMetadata>();
@@ -433,7 +448,7 @@ function _createNgModules(
const ngModulePipesAndDirective = new Set<StaticSymbol>();
const addNgModule = (staticSymbol: any) => {
if (ngModules.has(staticSymbol) || !filterFileByPatterns(staticSymbol.filePath, options)) {
if (ngModules.has(staticSymbol) || !host.isSourceFile(staticSymbol.filePath)) {
return false;
}
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
@@ -34,8 +34,11 @@ import {AotCompilerHost} from './compiler_host';
import {AotCompilerOptions} from './compiler_options';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector} from './static_reflector';
import {StaticSymbolCache} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
import {AotSummaryResolver} from './summary_resolver';
/**
* 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 || '';
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);
const htmlParser = new I18NHtmlParser(new HtmlParser(), translations, options.i18nFormat);
const config = new CompilerConfig({
@@ -60,17 +66,16 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
const console = new Console();
const tmplParser =
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
const summaryResolver = new AotSummaryResolver(compilerHost, staticReflector, options);
const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
staticReflector);
// TODO(vicb): do not pass options.i18nFormat here
const compiler = new AotCompiler(
resolver, tmplParser, new StyleCompiler(urlResolver),
compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver),
new ViewCompiler(config, elementSchemaRegistry),
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
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};
}
@@ -8,16 +8,17 @@
import {ImportResolver} from '../output/path_util';
import {StaticReflectorHost} from './static_reflector';
import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolverHost} from './static_symbol_resolver';
import {AotSummaryResolverHost} from './summary_resolver';
import {AotSummarySerializerHost} from './summary_serializer';
/**
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
* services and from underlying file systems.
*/
export interface AotCompilerHost extends StaticReflectorHost, ImportResolver,
AotSummaryResolverHost {
export interface AotCompilerHost extends StaticSymbolResolverHost, ImportResolver,
AotSummaryResolverHost, AotSummarySerializerHost {
/**
* Loads a resource (e.g. html / css)
*/
@@ -11,6 +11,4 @@ export interface AotCompilerOptions {
locale?: string;
i18nFormat?: string;
translations?: string;
includeFilePattern?: RegExp;
excludeFilePattern?: RegExp;
}
@@ -8,6 +8,7 @@
import {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from '../private_import_core';
import {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol';
export class StaticAndDynamicReflectionCapabilities {
static install(staticDelegate: StaticReflector) {
@@ -42,7 +43,7 @@ export class StaticAndDynamicReflectionCapabilities {
method(name: string): MethodFn { return this.dynamicDelegate.method(name); }
importUri(type: any): string { return this.staticDelegate.importUri(type); }
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 {
if (isStaticType(enumIdentifier)) {
File diff suppressed because it is too large Load Diff
@@ -14,3 +14,23 @@
export class StaticSymbol {
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;
}
}
@@ -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);
}
}
}
@@ -5,13 +5,12 @@
* 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 {SummaryResolver} from '../summary_resolver';
import {CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
import {Summary, SummaryResolver} from '../summary_resolver';
import {GeneratedFile} from './generated_file';
import {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol';
import {filterFileByPatterns} from './utils';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol} from './static_symbol_resolver';
import {deserializeSummaries, summaryFileName} from './summary_serializer';
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
*/
loadSummary(filePath: string): string;
loadSummary(filePath: string): string /*|null*/;
/**
* Returns the output file path of a source file.
* E.g.
* `some_file.ts` -> `some_file.d.ts`
* Returns whether a file is a source file or not.
*/
getOutputFileName(sourceFilePath: string): string;
isSourceFile(sourceFilePath: string): boolean;
}
export interface AotSummaryResolverOptions {
includeFilePattern?: RegExp;
excludeFilePattern?: RegExp;
}
export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
private summaryCache = new Map<StaticSymbol, Summary<StaticSymbol>>();
private loadedFilePaths = new Set<string>();
export class AotSummaryResolver implements SummaryResolver {
private summaryCache: {[cacheKey: string]: CompileTypeSummary} = {};
constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {}
constructor(
private host: AotSummaryResolverHost, private staticReflector: StaticReflector,
private options: AotSummaryResolverOptions) {}
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 _assertNoMembers(symbol: StaticSymbol) {
if (symbol.members.length) {
throw new Error(
`Internal state: StaticSymbols in summaries can't have members! ${JSON.stringify(symbol)}`);
}
}
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 {
const filePath = staticSymbol.filePath;
const name = staticSymbol.name;
const cacheKey = this._cacheKey(staticSymbol);
if (!filterFileByPatterns(filePath, this.options)) {
let summary = this.summaryCache[cacheKey];
getSymbolsOf(filePath: string): StaticSymbol[] {
this._loadSummaryFile(filePath);
return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
}
private _loadSummaryFile(filePath: string) {
if (this.loadedFilePaths.has(filePath)) {
return;
}
this.loadedFilePaths.add(filePath);
if (!this.host.isSourceFile(filePath)) {
const summaryFilePath = summaryFileName(filePath);
if (!summary) {
try {
const jsonReviver = (key: string, value: any) => {
if (value && value['__symbolic__'] === 'symbol') {
// Note: We can't use staticReflector.findDeclaration here:
// Summary files can contain symbols of transitive compilation units
// (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;
}
let json: string;
try {
json = this.host.loadSummary(summaryFilePath);
} catch (e) {
console.error(`Error loading summary file ${summaryFilePath}`);
throw e;
}
if (!summary) {
throw new Error(
`Could not find the symbol ${name} in the summary file ${summaryFilePath}!`);
if (json) {
const readSummaries = deserializeSummaries(this.staticSymbolCache, json);
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`;
}
@@ -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);
}
}
}
@@ -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;
}
@@ -115,10 +115,10 @@ export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata
export interface CompileIdentifierMetadata { reference: any; }
export enum CompileSummaryKind {
Template,
Pipe,
Directive,
NgModule
NgModule,
Injectable
}
/**
@@ -126,9 +126,10 @@ export enum CompileSummaryKind {
* in other modules / components. However, this data is not enough to compile
* the directive / module itself.
*/
export interface CompileSummary { summaryKind: CompileSummaryKind; }
export interface CompileTypeSummary extends CompileSummary { type: CompileTypeMetadata; }
export interface CompileTypeSummary {
summaryKind: CompileSummaryKind
type: CompileTypeMetadata;
}
export interface CompileDiDependencyMetadata {
isAttribute?: boolean;
@@ -210,7 +211,7 @@ export class CompileStylesheetMetadata {
/**
* Summary Metadata regarding compilation of a template.
*/
export interface CompileTemplateSummary extends CompileSummary {
export interface CompileTemplateSummary {
animations: string[];
ngContentSelectors: string[];
encapsulation: ViewEncapsulation;
@@ -258,7 +259,6 @@ export class CompileTemplateMetadata {
toSummary(): CompileTemplateSummary {
return {
summaryKind: CompileSummaryKind.Template,
animations: this.animations.map(anim => anim.name),
ngContentSelectors: this.ngContentSelectors,
encapsulation: this.encapsulation
@@ -6,11 +6,12 @@
* 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 {CompilerConfig} from './config';
import {isBlank, isPresent, stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import * as html from './ml_parser/ast';
import {HtmlParser} from './ml_parser/html_parser';
import {InterpolationConfig} from './ml_parser/interpolation_config';
@@ -32,7 +33,7 @@ export interface PrenormalizedTemplateMetadata {
animations?: CompileAnimationEntryMetadata[];
}
@Injectable()
@CompilerInjectable()
export class DirectiveNormalizer {
private _resourceLoaderCache = new Map<string, Promise<string>>();
@@ -6,14 +6,16 @@
* 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 {stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core';
import {splitAtColon} from './util';
/*
* Resolve a `Type` for {@link Directive}.
*
@@ -21,7 +23,7 @@ import {splitAtColon} from './util';
*
* See {@link Compiler}
*/
@Injectable()
@CompilerInjectable()
export class DirectiveResolver {
constructor(private _reflector: ReflectorReader = reflector) {}
@@ -6,8 +6,6 @@
* 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 {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
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 {Parser} from './expression_parser/parser';
import {Identifiers, createIdentifier} from './identifiers';
import {CompilerInjectable} from './injectable';
import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config';
import {ClassBuilder, createClassStmt} from './output/class_builder';
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.
*/
@Injectable()
@CompilerInjectable()
export class DirectiveWrapperCompiler {
static dirWrapperClassName(id: CompileIdentifierMetadata) {
return `Wrapper_${identifierName(id)}`;
@@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
import * as chars from '../chars';
import {NumberWrapper, isPresent} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
export enum TokenType {
Character,
@@ -22,7 +22,7 @@ export enum TokenType {
const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
@Injectable()
@CompilerInjectable()
export class Lexer {
tokenize(text: string): Token[] {
const scanner = new _Scanner(text);
@@ -6,10 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
import * as chars from '../chars';
import {escapeRegExp, isBlank, isPresent} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
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';
@@ -31,7 +30,7 @@ function _createInterpolateRegExp(config: InterpolationConfig): RegExp {
return new RegExp(pattern, 'g');
}
@Injectable()
@CompilerInjectable()
export class Parser {
private errors: ParserError[] = [];
+16 -18
View File
@@ -14,7 +14,9 @@ import {ViewEncapsulation} from '@angular/core';
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
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 {CompileDirectiveMetadata} from '../compile_metadata';
import {CompilerConfig} from '../config';
@@ -33,16 +35,11 @@ import {createOfflineCompileUrlResolver} from '../url_resolver';
import {I18NHtmlParser} from './i18n_html_parser';
import {MessageBundle} from './message_bundle';
export interface ExtractorOptions {
includeFilePattern?: RegExp;
excludeFilePattern?: RegExp;
}
/**
* The host of the Extractor disconnects the implementation from TypeScript / other language
* 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)
*/
@@ -51,14 +48,13 @@ export interface ExtractorHost extends StaticReflectorHost, AotSummaryResolverHo
export class Extractor {
constructor(
private options: ExtractorOptions, public host: ExtractorHost,
private staticReflector: StaticReflector, private messageBundle: MessageBundle,
private metadataResolver: CompileMetadataResolver) {}
public host: ExtractorHost, private staticSymbolResolver: StaticSymbolResolver,
private messageBundle: MessageBundle, private metadataResolver: CompileMetadataResolver) {}
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} =
analyzeAndValidateNgModules(programSymbols, this.options, this.metadataResolver);
analyzeAndValidateNgModules(programSymbols, this.host, this.metadataResolver);
return Promise
.all(ngModules.map(
ngModule => this.metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
@@ -91,12 +87,14 @@ export class Extractor {
});
}
static create(host: ExtractorHost, options: ExtractorOptions):
{extractor: Extractor, staticReflector: StaticReflector} {
static create(host: ExtractorHost): {extractor: Extractor, staticReflector: StaticReflector} {
const htmlParser = new I18NHtmlParser(new HtmlParser());
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);
const config = new CompilerConfig({
@@ -111,13 +109,13 @@ export class Extractor {
const elementSchemaRegistry = new DomElementSchemaRegistry();
const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), new AotSummaryResolver(host, staticReflector, options),
elementSchemaRegistry, normalizer, staticReflector);
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
staticReflector);
// TODO(vicb): implicit tags & attributes
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};
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
* 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 {MessageBundle} from './message_bundle';
export {Serializer} from './serializers/serializer';
@@ -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;
}

Some files were not shown because too many files have changed in this diff Show More