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:
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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[] = [];
|
||||
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user