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:
parent
01ca2db6ae
commit
33910ddfc9
|
@ -34,9 +34,9 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<!ELEMENT ex (#PCDATA)>
|
<!ELEMENT ex (#PCDATA)>
|
||||||
]>
|
]>
|
||||||
<messagebundle>
|
<messagebundle>
|
||||||
<msg id="3772663375917578720">other-3rdP-component</msg>
|
|
||||||
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
|
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
|
||||||
<msg id="3492007542396725315">Welcome</msg>
|
<msg id="3492007542396725315">Welcome</msg>
|
||||||
|
<msg id="3772663375917578720">other-3rdP-component</msg>
|
||||||
</messagebundle>
|
</messagebundle>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -44,10 +44,6 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
|
|
||||||
<source>other-3rdP-component</source>
|
|
||||||
<target/>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
||||||
<source>translate me</source>
|
<source>translate me</source>
|
||||||
<target/>
|
<target/>
|
||||||
|
@ -58,6 +54,10 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<source>Welcome</source>
|
<source>Welcome</source>
|
||||||
<target/>
|
<target/>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
|
||||||
|
<source>other-3rdP-component</source>
|
||||||
|
<target/>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
|
@ -52,12 +52,9 @@ function codeGenTest() {
|
||||||
const config = tsc.readConfiguration(project, basePath);
|
const config = tsc.readConfiguration(project, basePath);
|
||||||
const hostContext = new NodeCompilerHostContext();
|
const hostContext = new NodeCompilerHostContext();
|
||||||
const delegateHost = ts.createCompilerHost(config.parsed.options, true);
|
const delegateHost = ts.createCompilerHost(config.parsed.options, true);
|
||||||
const host: ts.CompilerHost = Object.assign({}, delegateHost, {
|
const host: ts.CompilerHost = Object.assign(
|
||||||
writeFile: (fileName: string, ...rest: any[]) => {
|
{}, delegateHost,
|
||||||
wroteFiles.push(fileName);
|
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
|
||||||
return delegateHost.writeFile.call(delegateHost, fileName, ...rest);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
||||||
|
|
||||||
config.ngOptions.basePath = basePath;
|
config.ngOptions.basePath = basePath;
|
||||||
|
|
|
@ -27,9 +27,14 @@ function main() {
|
||||||
const basePath = path.resolve(__dirname, '..');
|
const basePath = path.resolve(__dirname, '..');
|
||||||
const project = path.resolve(basePath, 'tsconfig-build.json');
|
const project = path.resolve(basePath, 'tsconfig-build.json');
|
||||||
const readFiles: string[] = [];
|
const readFiles: string[] = [];
|
||||||
|
const writtenFiles: {fileName: string, content: string}[] = [];
|
||||||
|
|
||||||
class AssertingHostContext extends NodeCompilerHostContext {
|
class AssertingHostContext extends NodeCompilerHostContext {
|
||||||
readFile(fileName: string): string {
|
readFile(fileName: string): string {
|
||||||
|
if (/.*\/node_modules\/.*/.test(fileName) && !/.*ngsummary\.json$/.test(fileName)) {
|
||||||
|
// Only allow to read summaries from node_modules
|
||||||
|
return null;
|
||||||
|
}
|
||||||
readFiles.push(path.relative(basePath, fileName));
|
readFiles.push(path.relative(basePath, fileName));
|
||||||
return super.readFile(fileName);
|
return super.readFile(fileName);
|
||||||
}
|
}
|
||||||
|
@ -45,16 +50,29 @@ function main() {
|
||||||
config.ngOptions.generateCodeForLibraries = false;
|
config.ngOptions.generateCodeForLibraries = false;
|
||||||
|
|
||||||
console.log(`>>> running codegen for ${project}`);
|
console.log(`>>> running codegen for ${project}`);
|
||||||
codegen(config, (host) => new AssertingHostContext())
|
codegen(
|
||||||
|
config,
|
||||||
|
(host) => {
|
||||||
|
host.writeFile = (fileName: string, content: string) => {
|
||||||
|
fileName = path.relative(basePath, fileName);
|
||||||
|
writtenFiles.push({fileName, content});
|
||||||
|
};
|
||||||
|
return new AssertingHostContext();
|
||||||
|
})
|
||||||
.then((exitCode: any) => {
|
.then((exitCode: any) => {
|
||||||
console.log(`>>> codegen done, asserting read files`);
|
console.log(`>>> codegen done, asserting read files`);
|
||||||
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
|
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
|
||||||
|
assertNoFileMatch(readFiles, /^node_modules\/.*\.metadata.json$/);
|
||||||
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
|
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
|
||||||
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
|
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
|
||||||
|
|
||||||
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
|
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
|
||||||
assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
|
assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
|
||||||
assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
|
assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
|
||||||
|
|
||||||
|
console.log(`>>> asserting written files`);
|
||||||
|
assertWrittenFile(writtenFiles, /^src\/module\.ngfactory\.ts$/, /class MainModuleInjector/);
|
||||||
|
|
||||||
console.log(`done, no errors.`);
|
console.log(`done, no errors.`);
|
||||||
process.exit(exitCode);
|
process.exit(exitCode);
|
||||||
})
|
})
|
||||||
|
@ -97,4 +115,11 @@ function assertNoFileMatch(fileNames: string[], pattern: RegExp) {
|
||||||
`Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`);
|
`Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assertWrittenFile(
|
||||||
|
files: {fileName: string, content: string}[], filePattern: RegExp, contentPattern: RegExp) {
|
||||||
|
assert(
|
||||||
|
files.some(file => filePattern.test(file.fileName) && contentPattern.test(file.content)),
|
||||||
|
`Expected some written files for ${filePattern} and content ${contentPattern}`);
|
||||||
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
|
@ -21,9 +21,7 @@ import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './
|
||||||
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
||||||
import {Console} from './private_import_core';
|
import {Console} from './private_import_core';
|
||||||
|
|
||||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
|
||||||
const GENERATED_META_FILES = /\.json$/;
|
const GENERATED_META_FILES = /\.json$/;
|
||||||
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
|
||||||
|
|
||||||
const PREAMBLE = `/**
|
const PREAMBLE = `/**
|
||||||
* @fileoverview This file is generated by the Angular 2 template compiler.
|
* @fileoverview This file is generated by the Angular 2 template compiler.
|
||||||
|
@ -102,14 +100,8 @@ export class CodeGenerator {
|
||||||
debug: options.debug === true,
|
debug: options.debug === true,
|
||||||
translations: transContent,
|
translations: transContent,
|
||||||
i18nFormat: cliOptions.i18nFormat,
|
i18nFormat: cliOptions.i18nFormat,
|
||||||
locale: cliOptions.locale,
|
locale: cliOptions.locale
|
||||||
excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES :
|
|
||||||
GENERATED_FILES
|
|
||||||
});
|
});
|
||||||
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
|
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function excludeFilePattern(options: AngularCompilerOptions): RegExp {
|
|
||||||
return options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,6 +16,8 @@ const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
const DTS = /\.d\.ts$/;
|
const DTS = /\.d\.ts$/;
|
||||||
const NODE_MODULES = '/node_modules/';
|
const NODE_MODULES = '/node_modules/';
|
||||||
const IS_GENERATED = /\.(ngfactory|ngstyle)$/;
|
const IS_GENERATED = /\.(ngfactory|ngstyle)$/;
|
||||||
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
||||||
|
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
||||||
|
|
||||||
export interface CompilerHostContext extends ts.ModuleResolutionHost {
|
export interface CompilerHostContext extends ts.ModuleResolutionHost {
|
||||||
readResource(fileName: string): Promise<string>;
|
readResource(fileName: string): Promise<string>;
|
||||||
|
@ -28,6 +30,7 @@ export class CompilerHost implements AotCompilerHost {
|
||||||
protected basePath: string;
|
protected basePath: string;
|
||||||
private genDir: string;
|
private genDir: string;
|
||||||
private resolverCache = new Map<string, ModuleMetadata[]>();
|
private resolverCache = new Map<string, ModuleMetadata[]>();
|
||||||
|
protected resolveModuleNameHost: CompilerHostContext;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected program: ts.Program, protected options: AngularCompilerOptions,
|
protected program: ts.Program, protected options: AngularCompilerOptions,
|
||||||
|
@ -38,12 +41,31 @@ export class CompilerHost implements AotCompilerHost {
|
||||||
|
|
||||||
const genPath: string = path.relative(this.basePath, this.genDir);
|
const genPath: string = path.relative(this.basePath, this.genDir);
|
||||||
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
||||||
|
this.resolveModuleNameHost = Object.create(this.context);
|
||||||
|
|
||||||
|
// When calling ts.resolveModuleName,
|
||||||
|
// additional allow checks for .d.ts files to be done based on
|
||||||
|
// checks for .ngsummary.json files,
|
||||||
|
// so that our codegen depends on fewer inputs and requires to be called
|
||||||
|
// less often.
|
||||||
|
// This is needed as we use ts.resolveModuleName in reflector_host
|
||||||
|
// and it should be able to resolve summary file names.
|
||||||
|
this.resolveModuleNameHost.fileExists = (fileName: string): boolean => {
|
||||||
|
if (this.context.fileExists(fileName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (DTS.test(fileName)) {
|
||||||
|
const base = fileName.substring(0, fileName.length - 5);
|
||||||
|
return this.context.fileExists(base + '.ngsummary.json');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use absolute paths on disk as canonical.
|
// We use absolute paths on disk as canonical.
|
||||||
getCanonicalFileName(fileName: string): string { return fileName; }
|
getCanonicalFileName(fileName: string): string { return fileName; }
|
||||||
|
|
||||||
moduleNameToFileName(m: string, containingFile: string) {
|
moduleNameToFileName(m: string, containingFile: string): string|null {
|
||||||
if (!containingFile || !containingFile.length) {
|
if (!containingFile || !containingFile.length) {
|
||||||
if (m.indexOf('.') === 0) {
|
if (m.indexOf('.') === 0) {
|
||||||
throw new Error('Resolution of relative paths requires a containing file.');
|
throw new Error('Resolution of relative paths requires a containing file.');
|
||||||
|
@ -53,7 +75,8 @@ export class CompilerHost implements AotCompilerHost {
|
||||||
}
|
}
|
||||||
m = m.replace(EXT, '');
|
m = m.replace(EXT, '');
|
||||||
const resolved =
|
const resolved =
|
||||||
ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context)
|
ts.resolveModuleName(
|
||||||
|
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
|
||||||
.resolvedModule;
|
.resolvedModule;
|
||||||
return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
|
return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
|
||||||
};
|
};
|
||||||
|
@ -213,11 +236,21 @@ export class CompilerHost implements AotCompilerHost {
|
||||||
|
|
||||||
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
|
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
|
||||||
|
|
||||||
loadSummary(filePath: string): string { return this.context.readFile(filePath); }
|
loadSummary(filePath: string): string|null {
|
||||||
|
if (this.context.fileExists(filePath)) {
|
||||||
|
return this.context.readFile(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getOutputFileName(sourceFilePath: string): string {
|
getOutputFileName(sourceFilePath: string): string {
|
||||||
return sourceFilePath.replace(EXT, '') + '.d.ts';
|
return sourceFilePath.replace(EXT, '') + '.d.ts';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSourceFile(filePath: string): boolean {
|
||||||
|
const excludeRegex =
|
||||||
|
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
||||||
|
return !excludeRegex.test(filePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompilerHostContextAdapter {
|
export class CompilerHostContextAdapter {
|
||||||
|
|
|
@ -17,7 +17,6 @@ import * as compiler from '@angular/compiler';
|
||||||
import * as tsc from '@angular/tsc-wrapped';
|
import * as tsc from '@angular/tsc-wrapped';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {excludeFilePattern} from './codegen';
|
|
||||||
import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';
|
import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';
|
||||||
|
|
||||||
export class Extractor {
|
export class Extractor {
|
||||||
|
@ -36,8 +35,7 @@ export class Extractor {
|
||||||
if (!ngCompilerHost)
|
if (!ngCompilerHost)
|
||||||
ngCompilerHost =
|
ngCompilerHost =
|
||||||
new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
|
new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
|
||||||
const {extractor: ngExtractor} = compiler.Extractor.create(
|
const {extractor: ngExtractor} = compiler.Extractor.create(ngCompilerHost);
|
||||||
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
|
|
||||||
return new Extractor(ngExtractor, ngCompilerHost, program);
|
return new Extractor(ngExtractor, ngCompilerHost, program);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* something else.
|
* something else.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AotCompilerHost, StaticReflector} from '@angular/compiler';
|
import {AotCompilerHost, AotSummaryResolver, StaticReflector, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
|
||||||
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
@ -111,7 +111,10 @@ export class NgTools_InternalApi_NG_2 {
|
||||||
new PathMappedCompilerHost(program, angularCompilerOptions, moduleResolutionHost) :
|
new PathMappedCompilerHost(program, angularCompilerOptions, moduleResolutionHost) :
|
||||||
new CompilerHost(program, angularCompilerOptions, moduleResolutionHost);
|
new CompilerHost(program, angularCompilerOptions, moduleResolutionHost);
|
||||||
|
|
||||||
const staticReflector = new StaticReflector(ngCompilerHost);
|
const symbolCache = new StaticSymbolCache();
|
||||||
|
const summaryResolver = new AotSummaryResolver(ngCompilerHost, symbolCache);
|
||||||
|
const symbolResolver = new StaticSymbolResolver(ngCompilerHost, symbolCache, summaryResolver);
|
||||||
|
const staticReflector = new StaticReflector(symbolResolver);
|
||||||
const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector);
|
const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector);
|
||||||
|
|
||||||
return Object.keys(routeMap).reduce(
|
return Object.keys(routeMap).reduce(
|
||||||
|
|
|
@ -29,13 +29,17 @@ describe('compiler-cli', () => {
|
||||||
"types": [],
|
"types": [],
|
||||||
"outDir": "built",
|
"outDir": "built",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"module": "es2015"
|
"module": "es2015",
|
||||||
|
"moduleResolution": "node"
|
||||||
},
|
},
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true
|
"annotateForClosureCompiler": true
|
||||||
},
|
},
|
||||||
"files": ["test.ts"]
|
"files": ["test.ts"]
|
||||||
}`);
|
}`);
|
||||||
|
const nodeModulesPath = path.resolve(basePath, 'node_modules');
|
||||||
|
fs.mkdirSync(nodeModulesPath);
|
||||||
|
fs.symlinkSync(path.resolve(__dirname, '..', '..'), path.resolve(nodeModulesPath, '@angular'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Restore reflector since AoT compiler will update it with a new static reflector
|
// Restore reflector since AoT compiler will update it with a new static reflector
|
||||||
|
|
|
@ -32,7 +32,9 @@ export * from './src/aot/compiler_host';
|
||||||
export * from './src/aot/static_reflector';
|
export * from './src/aot/static_reflector';
|
||||||
export * from './src/aot/static_reflection_capabilities';
|
export * from './src/aot/static_reflection_capabilities';
|
||||||
export * from './src/aot/static_symbol';
|
export * from './src/aot/static_symbol';
|
||||||
|
export * from './src/aot/static_symbol_resolver';
|
||||||
export * from './src/aot/summary_resolver';
|
export * from './src/aot/summary_resolver';
|
||||||
|
export * from './src/summary_resolver';
|
||||||
export {JitCompiler} from './src/jit/compiler';
|
export {JitCompiler} from './src/jit/compiler';
|
||||||
export * from './src/jit/compiler_factory';
|
export * from './src/jit/compiler_factory';
|
||||||
export * from './src/url_resolver';
|
export * from './src/url_resolver';
|
||||||
|
|
|
@ -6,11 +6,10 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
|
||||||
|
|
||||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata, identifierName} from '../compile_metadata';
|
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata, identifierName} from '../compile_metadata';
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
import {StringMapWrapper} from '../facade/collection';
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
|
import {CompilerInjectable} from '../injectable';
|
||||||
import {ParseError} from '../parse_util';
|
import {ParseError} from '../parse_util';
|
||||||
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
||||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
|
@ -35,7 +34,7 @@ export class AnimationEntryParseResult {
|
||||||
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
|
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class AnimationParser {
|
export class AnimationParser {
|
||||||
constructor(private _schema: ElementSchemaRegistry) {}
|
constructor(private _schema: ElementSchemaRegistry) {}
|
||||||
|
|
||||||
|
|
|
@ -20,34 +20,34 @@ import {NgModuleCompiler} from '../ng_module_compiler';
|
||||||
import {OutputEmitter} from '../output/abstract_emitter';
|
import {OutputEmitter} from '../output/abstract_emitter';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||||
|
import {SummaryResolver} from '../summary_resolver';
|
||||||
import {TemplateParser} from '../template_parser/template_parser';
|
import {TemplateParser} from '../template_parser/template_parser';
|
||||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
|
||||||
import {AotCompilerOptions} from './compiler_options';
|
import {AotCompilerHost} from './compiler_host';
|
||||||
import {GeneratedFile} from './generated_file';
|
import {GeneratedFile} from './generated_file';
|
||||||
import {StaticReflector} from './static_reflector';
|
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
import {AotSummaryResolver} from './summary_resolver';
|
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
import {filterFileByPatterns} from './utils';
|
import {serializeSummaries, summaryFileName} from './summary_serializer';
|
||||||
|
|
||||||
export class AotCompiler {
|
export class AotCompiler {
|
||||||
private _animationCompiler = new AnimationCompiler();
|
private _animationCompiler = new AnimationCompiler();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
|
private _host: AotCompilerHost, private _metadataResolver: CompileMetadataResolver,
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
||||||
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
private _viewCompiler: ViewCompiler, private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
||||||
private _summaryResolver: AotSummaryResolver, private _localeId: string,
|
private _summaryResolver: SummaryResolver<StaticSymbol>, private _localeId: string,
|
||||||
private _translationFormat: string, private _animationParser: AnimationParser,
|
private _translationFormat: string, private _animationParser: AnimationParser,
|
||||||
private _staticReflector: StaticReflector, private _options: AotCompilerOptions) {}
|
private _symbolResolver: StaticSymbolResolver) {}
|
||||||
|
|
||||||
clearCache() { this._metadataResolver.clearCache(); }
|
clearCache() { this._metadataResolver.clearCache(); }
|
||||||
|
|
||||||
compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
|
compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
|
||||||
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options);
|
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
|
||||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||||
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
|
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
|
||||||
return Promise
|
return Promise
|
||||||
.all(ngModules.map(
|
.all(ngModules.map(
|
||||||
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||||
|
@ -56,27 +56,21 @@ export class AotCompiler {
|
||||||
const sourceModules = files.map(
|
const sourceModules = files.map(
|
||||||
file => this._compileSrcFile(
|
file => this._compileSrcFile(
|
||||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
|
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
|
||||||
file.ngModules));
|
file.ngModules, file.injectables));
|
||||||
return ListWrapper.flatten(sourceModules);
|
return ListWrapper.flatten(sourceModules);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileSrcFile(
|
private _compileSrcFile(
|
||||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
directives: StaticSymbol[], pipes: StaticSymbol[],
|
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
|
||||||
ngModules: StaticSymbol[]): GeneratedFile[] {
|
injectables: StaticSymbol[]): GeneratedFile[] {
|
||||||
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
|
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
|
||||||
const statements: o.Statement[] = [];
|
const statements: o.Statement[] = [];
|
||||||
const exportedVars: string[] = [];
|
const exportedVars: string[] = [];
|
||||||
const generatedFiles: GeneratedFile[] = [];
|
const generatedFiles: GeneratedFile[] = [];
|
||||||
|
|
||||||
// write summary files
|
generatedFiles.push(this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables));
|
||||||
const summaries: CompileTypeSummary[] = [
|
|
||||||
...ngModules.map(ref => this._metadataResolver.getNgModuleSummary(ref)),
|
|
||||||
...directives.map(ref => this._metadataResolver.getDirectiveSummary(ref)),
|
|
||||||
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref))
|
|
||||||
];
|
|
||||||
generatedFiles.push(this._summaryResolver.serializeSummaries(srcFileUrl, summaries));
|
|
||||||
|
|
||||||
// compile all ng modules
|
// compile all ng modules
|
||||||
exportedVars.push(
|
exportedVars.push(
|
||||||
|
@ -121,6 +115,22 @@ export class AotCompiler {
|
||||||
return generatedFiles;
|
return generatedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _createSummary(
|
||||||
|
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
|
||||||
|
ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile {
|
||||||
|
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
|
||||||
|
.map(symbol => this._symbolResolver.resolveSymbol(symbol));
|
||||||
|
const typeSummaries = [
|
||||||
|
...ngModules.map(ref => this._metadataResolver.getNgModuleSummary(ref)),
|
||||||
|
...directives.map(ref => this._metadataResolver.getDirectiveSummary(ref)),
|
||||||
|
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)),
|
||||||
|
...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref))
|
||||||
|
];
|
||||||
|
const json = serializeSummaries(
|
||||||
|
this._host, this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
|
||||||
|
return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json);
|
||||||
|
}
|
||||||
|
|
||||||
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
||||||
const ngModule = this._metadataResolver.getNgModuleMetadata(ngModuleType);
|
const ngModule = this._metadataResolver.getNgModuleMetadata(ngModuleType);
|
||||||
const providers: CompileProviderMetadata[] = [];
|
const providers: CompileProviderMetadata[] = [];
|
||||||
|
@ -142,7 +152,7 @@ export class AotCompiler {
|
||||||
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
||||||
|
|
||||||
appCompileResult.dependencies.forEach((dep) => {
|
appCompileResult.dependencies.forEach((dep) => {
|
||||||
dep.placeholder.reference = this._staticReflector.getStaticSymbol(
|
dep.placeholder.reference = this._symbolResolver.getStaticSymbol(
|
||||||
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
|
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -163,7 +173,7 @@ export class AotCompiler {
|
||||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
|
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
|
||||||
targetStatements: o.Statement[]): string {
|
targetStatements: o.Statement[]): string {
|
||||||
const hostMeta = createHostComponentMeta(
|
const hostMeta = createHostComponentMeta(
|
||||||
this._staticReflector.getStaticSymbol(
|
this._symbolResolver.getStaticSymbol(
|
||||||
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
|
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
|
||||||
compMeta);
|
compMeta);
|
||||||
const hostViewFactoryVar = this._compileComponent(
|
const hostViewFactoryVar = this._compileComponent(
|
||||||
|
@ -206,16 +216,16 @@ export class AotCompiler {
|
||||||
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
|
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
|
||||||
if (componentStyles) {
|
if (componentStyles) {
|
||||||
targetStatements.push(
|
targetStatements.push(
|
||||||
..._resolveStyleStatements(this._staticReflector, componentStyles, fileSuffix));
|
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
|
||||||
}
|
}
|
||||||
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
||||||
targetStatements.push(..._resolveViewStatements(this._staticReflector, viewResult));
|
targetStatements.push(..._resolveViewStatements(this._symbolResolver, viewResult));
|
||||||
return viewResult.viewClassVar;
|
return viewResult.viewClassVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _codgenStyles(
|
private _codgenStyles(
|
||||||
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile {
|
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile {
|
||||||
_resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix);
|
_resolveStyleStatements(this._symbolResolver, stylesCompileResult, fileSuffix);
|
||||||
return this._codegenSourceModule(
|
return this._codegenSourceModule(
|
||||||
fileUrl, _stylesModuleUrl(
|
fileUrl, _stylesModuleUrl(
|
||||||
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
|
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
|
||||||
|
@ -232,7 +242,7 @@ export class AotCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _resolveViewStatements(
|
function _resolveViewStatements(
|
||||||
reflector: StaticReflector, compileResult: ViewCompileResult): o.Statement[] {
|
reflector: StaticSymbolResolver, compileResult: ViewCompileResult): o.Statement[] {
|
||||||
compileResult.dependencies.forEach((dep) => {
|
compileResult.dependencies.forEach((dep) => {
|
||||||
if (dep instanceof ViewClassDependency) {
|
if (dep instanceof ViewClassDependency) {
|
||||||
const vfd = <ViewClassDependency>dep;
|
const vfd = <ViewClassDependency>dep;
|
||||||
|
@ -253,7 +263,7 @@ function _resolveViewStatements(
|
||||||
|
|
||||||
|
|
||||||
function _resolveStyleStatements(
|
function _resolveStyleStatements(
|
||||||
reflector: StaticReflector, compileResult: CompiledStylesheet,
|
reflector: StaticSymbolResolver, compileResult: CompiledStylesheet,
|
||||||
fileSuffix: string): o.Statement[] {
|
fileSuffix: string): o.Statement[] {
|
||||||
compileResult.dependencies.forEach((dep) => {
|
compileResult.dependencies.forEach((dep) => {
|
||||||
dep.valuePlaceholder.reference = reflector.getStaticSymbol(
|
dep.valuePlaceholder.reference = reflector.getStaticSymbol(
|
||||||
|
@ -303,26 +313,27 @@ export interface NgAnalyzedModules {
|
||||||
srcUrl: string,
|
srcUrl: string,
|
||||||
directives: StaticSymbol[],
|
directives: StaticSymbol[],
|
||||||
pipes: StaticSymbol[],
|
pipes: StaticSymbol[],
|
||||||
ngModules: StaticSymbol[]
|
ngModules: StaticSymbol[],
|
||||||
|
injectables: StaticSymbol[]
|
||||||
}>;
|
}>;
|
||||||
symbolsMissingModule?: StaticSymbol[];
|
symbolsMissingModule?: StaticSymbol[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NgAnalyzeModulesHost { isSourceFile(filePath: string): boolean; }
|
||||||
|
|
||||||
// Returns all the source files and a mapping from modules to directives
|
// Returns all the source files and a mapping from modules to directives
|
||||||
export function analyzeNgModules(
|
export function analyzeNgModules(
|
||||||
programStaticSymbols: StaticSymbol[],
|
programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
|
||||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
|
|
||||||
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
|
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
|
||||||
const {ngModules, symbolsMissingModule} =
|
const {ngModules, symbolsMissingModule} =
|
||||||
_createNgModules(programStaticSymbols, options, metadataResolver);
|
_createNgModules(programStaticSymbols, host, metadataResolver);
|
||||||
return _analyzeNgModules(ngModules, symbolsMissingModule);
|
return _analyzeNgModules(programStaticSymbols, ngModules, symbolsMissingModule, metadataResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function analyzeAndValidateNgModules(
|
export function analyzeAndValidateNgModules(
|
||||||
programStaticSymbols: StaticSymbol[],
|
programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
|
||||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
|
|
||||||
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
|
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
|
||||||
const result = analyzeNgModules(programStaticSymbols, options, metadataResolver);
|
const result = analyzeNgModules(programStaticSymbols, host, metadataResolver);
|
||||||
if (result.symbolsMissingModule && result.symbolsMissingModule.length) {
|
if (result.symbolsMissingModule && result.symbolsMissingModule.length) {
|
||||||
const messages = result.symbolsMissingModule.map(
|
const messages = result.symbolsMissingModule.map(
|
||||||
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
|
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
|
||||||
|
@ -332,16 +343,27 @@ export function analyzeAndValidateNgModules(
|
||||||
}
|
}
|
||||||
|
|
||||||
function _analyzeNgModules(
|
function _analyzeNgModules(
|
||||||
ngModuleMetas: CompileNgModuleMetadata[],
|
programSymbols: StaticSymbol[], ngModuleMetas: CompileNgModuleMetadata[],
|
||||||
symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules {
|
symbolsMissingModule: StaticSymbol[],
|
||||||
|
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
|
||||||
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
|
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
|
||||||
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
|
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
|
||||||
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||||
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
||||||
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
||||||
const ngPipesByFile = new Map<string, StaticSymbol[]>();
|
const ngPipesByFile = new Map<string, StaticSymbol[]>();
|
||||||
|
const ngInjectablesByFile = new Map<string, StaticSymbol[]>();
|
||||||
const filePaths = new Set<string>();
|
const filePaths = new Set<string>();
|
||||||
|
|
||||||
|
// Make sure we produce an analyzed file for each input file
|
||||||
|
programSymbols.forEach((symbol) => {
|
||||||
|
const filePath = symbol.filePath;
|
||||||
|
filePaths.add(filePath);
|
||||||
|
if (metadataResolver.isInjectable(symbol)) {
|
||||||
|
ngInjectablesByFile.set(filePath, (ngInjectablesByFile.get(filePath) || []).concat(symbol));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Looping over all modules to construct:
|
// Looping over all modules to construct:
|
||||||
// - a map from file to modules `ngModulesByFile`,
|
// - a map from file to modules `ngModulesByFile`,
|
||||||
// - a map from file to directives `ngDirectivesByFile`,
|
// - a map from file to directives `ngDirectivesByFile`,
|
||||||
|
@ -369,17 +391,20 @@ function _analyzeNgModules(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const files:
|
const files: {
|
||||||
{srcUrl: string,
|
srcUrl: string,
|
||||||
directives: StaticSymbol[],
|
directives: StaticSymbol[],
|
||||||
pipes: StaticSymbol[],
|
pipes: StaticSymbol[],
|
||||||
ngModules: StaticSymbol[]}[] = [];
|
ngModules: StaticSymbol[],
|
||||||
|
injectables: StaticSymbol[]
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
filePaths.forEach((srcUrl) => {
|
filePaths.forEach((srcUrl) => {
|
||||||
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
||||||
const pipes = ngPipesByFile.get(srcUrl) || [];
|
const pipes = ngPipesByFile.get(srcUrl) || [];
|
||||||
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
||||||
files.push({srcUrl, directives, pipes, ngModules});
|
const injectables = ngInjectablesByFile.get(srcUrl) || [];
|
||||||
|
files.push({srcUrl, directives, pipes, ngModules, injectables});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -392,29 +417,20 @@ function _analyzeNgModules(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractProgramSymbols(
|
export function extractProgramSymbols(
|
||||||
staticReflector: StaticReflector, files: string[],
|
staticSymbolResolver: StaticSymbolResolver, files: string[],
|
||||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] {
|
host: NgAnalyzeModulesHost): StaticSymbol[] {
|
||||||
const staticSymbols: StaticSymbol[] = [];
|
const staticSymbols: StaticSymbol[] = [];
|
||||||
files.filter(fileName => filterFileByPatterns(fileName, options)).forEach(sourceFile => {
|
files.filter(fileName => host.isSourceFile(fileName)).forEach(sourceFile => {
|
||||||
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
|
staticSymbolResolver.getSymbolsOf(sourceFile).forEach((symbol) => {
|
||||||
if (!moduleMetadata) {
|
const resolvedSymbol = staticSymbolResolver.resolveSymbol(symbol);
|
||||||
console.error(`WARNING: no metadata found for ${sourceFile}`);
|
const symbolMeta = resolvedSymbol.metadata;
|
||||||
return;
|
if (symbolMeta) {
|
||||||
}
|
if (symbolMeta.__symbolic != 'error') {
|
||||||
|
// Ignore symbols that are only included to record error information.
|
||||||
const metadata = moduleMetadata['metadata'];
|
staticSymbols.push(resolvedSymbol.symbol);
|
||||||
|
}
|
||||||
if (!metadata) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const symbol of Object.keys(metadata)) {
|
|
||||||
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
|
||||||
// Ignore symbols that are only included to record error information.
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
staticSymbols.push(staticReflector.getStaticSymbol(sourceFile, symbol));
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return staticSymbols;
|
return staticSymbols;
|
||||||
|
@ -424,8 +440,7 @@ export function extractProgramSymbols(
|
||||||
// that all directives / pipes that are present in the program
|
// that all directives / pipes that are present in the program
|
||||||
// are also declared by a module.
|
// are also declared by a module.
|
||||||
function _createNgModules(
|
function _createNgModules(
|
||||||
programStaticSymbols: StaticSymbol[],
|
programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
|
||||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
|
|
||||||
metadataResolver: CompileMetadataResolver):
|
metadataResolver: CompileMetadataResolver):
|
||||||
{ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} {
|
{ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} {
|
||||||
const ngModules = new Map<any, CompileNgModuleMetadata>();
|
const ngModules = new Map<any, CompileNgModuleMetadata>();
|
||||||
|
@ -433,7 +448,7 @@ function _createNgModules(
|
||||||
const ngModulePipesAndDirective = new Set<StaticSymbol>();
|
const ngModulePipesAndDirective = new Set<StaticSymbol>();
|
||||||
|
|
||||||
const addNgModule = (staticSymbol: any) => {
|
const addNgModule = (staticSymbol: any) => {
|
||||||
if (ngModules.has(staticSymbol) || !filterFileByPatterns(staticSymbol.filePath, options)) {
|
if (ngModules.has(staticSymbol) || !host.isSourceFile(staticSymbol.filePath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
|
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
|
||||||
|
|
|
@ -34,8 +34,11 @@ import {AotCompilerHost} from './compiler_host';
|
||||||
import {AotCompilerOptions} from './compiler_options';
|
import {AotCompilerOptions} from './compiler_options';
|
||||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||||
import {StaticReflector} from './static_reflector';
|
import {StaticReflector} from './static_reflector';
|
||||||
|
import {StaticSymbolCache} from './static_symbol';
|
||||||
|
import {StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
import {AotSummaryResolver} from './summary_resolver';
|
import {AotSummaryResolver} from './summary_resolver';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new AotCompiler based on options and a host.
|
* Creates a new AotCompiler based on options and a host.
|
||||||
*/
|
*/
|
||||||
|
@ -44,7 +47,10 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
||||||
let translations: string = options.translations || '';
|
let translations: string = options.translations || '';
|
||||||
|
|
||||||
const urlResolver = createOfflineCompileUrlResolver();
|
const urlResolver = createOfflineCompileUrlResolver();
|
||||||
const staticReflector = new StaticReflector(compilerHost);
|
const symbolCache = new StaticSymbolCache();
|
||||||
|
const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache);
|
||||||
|
const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver);
|
||||||
|
const staticReflector = new StaticReflector(symbolResolver);
|
||||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||||
const htmlParser = new I18NHtmlParser(new HtmlParser(), translations, options.i18nFormat);
|
const htmlParser = new I18NHtmlParser(new HtmlParser(), translations, options.i18nFormat);
|
||||||
const config = new CompilerConfig({
|
const config = new CompilerConfig({
|
||||||
|
@ -60,17 +66,16 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
||||||
const console = new Console();
|
const console = new Console();
|
||||||
const tmplParser =
|
const tmplParser =
|
||||||
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
|
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
|
||||||
const summaryResolver = new AotSummaryResolver(compilerHost, staticReflector, options);
|
|
||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||||
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||||
staticReflector);
|
staticReflector);
|
||||||
// TODO(vicb): do not pass options.i18nFormat here
|
// TODO(vicb): do not pass options.i18nFormat here
|
||||||
const compiler = new AotCompiler(
|
const compiler = new AotCompiler(
|
||||||
resolver, tmplParser, new StyleCompiler(urlResolver),
|
compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver),
|
||||||
new ViewCompiler(config, elementSchemaRegistry),
|
new ViewCompiler(config, elementSchemaRegistry),
|
||||||
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
|
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
|
||||||
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
|
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
|
||||||
options.i18nFormat, new AnimationParser(elementSchemaRegistry), staticReflector, options);
|
options.i18nFormat, new AnimationParser(elementSchemaRegistry), symbolResolver);
|
||||||
return {compiler, reflector: staticReflector};
|
return {compiler, reflector: staticReflector};
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,17 @@
|
||||||
|
|
||||||
import {ImportResolver} from '../output/path_util';
|
import {ImportResolver} from '../output/path_util';
|
||||||
|
|
||||||
import {StaticReflectorHost} from './static_reflector';
|
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
|
import {StaticSymbolResolverHost} from './static_symbol_resolver';
|
||||||
import {AotSummaryResolverHost} from './summary_resolver';
|
import {AotSummaryResolverHost} from './summary_resolver';
|
||||||
|
import {AotSummarySerializerHost} from './summary_serializer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
|
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
|
||||||
* services and from underlying file systems.
|
* services and from underlying file systems.
|
||||||
*/
|
*/
|
||||||
export interface AotCompilerHost extends StaticReflectorHost, ImportResolver,
|
export interface AotCompilerHost extends StaticSymbolResolverHost, ImportResolver,
|
||||||
AotSummaryResolverHost {
|
AotSummaryResolverHost, AotSummarySerializerHost {
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -11,6 +11,4 @@ export interface AotCompilerOptions {
|
||||||
locale?: string;
|
locale?: string;
|
||||||
i18nFormat?: string;
|
i18nFormat?: string;
|
||||||
translations?: string;
|
translations?: string;
|
||||||
includeFilePattern?: RegExp;
|
|
||||||
excludeFilePattern?: RegExp;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from '../private_import_core';
|
import {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from '../private_import_core';
|
||||||
import {StaticReflector} from './static_reflector';
|
import {StaticReflector} from './static_reflector';
|
||||||
|
import {StaticSymbol} from './static_symbol';
|
||||||
|
|
||||||
export class StaticAndDynamicReflectionCapabilities {
|
export class StaticAndDynamicReflectionCapabilities {
|
||||||
static install(staticDelegate: StaticReflector) {
|
static install(staticDelegate: StaticReflector) {
|
||||||
|
@ -42,7 +43,7 @@ export class StaticAndDynamicReflectionCapabilities {
|
||||||
method(name: string): MethodFn { return this.dynamicDelegate.method(name); }
|
method(name: string): MethodFn { return this.dynamicDelegate.method(name); }
|
||||||
importUri(type: any): string { return this.staticDelegate.importUri(type); }
|
importUri(type: any): string { return this.staticDelegate.importUri(type); }
|
||||||
resolveIdentifier(name: string, moduleUrl: string, runtime: any) {
|
resolveIdentifier(name: string, moduleUrl: string, runtime: any) {
|
||||||
return this.staticDelegate.resolveIdentifier(name, moduleUrl, runtime);
|
return this.staticDelegate.resolveIdentifier(name, moduleUrl);
|
||||||
}
|
}
|
||||||
resolveEnum(enumIdentifier: any, name: string): any {
|
resolveEnum(enumIdentifier: any, name: string): any {
|
||||||
if (isStaticType(enumIdentifier)) {
|
if (isStaticType(enumIdentifier)) {
|
||||||
|
|
|
@ -7,10 +7,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||||
import {ReflectorReader} from '../private_import_core';
|
|
||||||
import {StaticSymbol} from './static_symbol';
|
|
||||||
|
|
||||||
const SUPPORTED_SCHEMA_VERSION = 3;
|
import {ReflectorReader} from '../private_import_core';
|
||||||
|
|
||||||
|
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||||
|
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
|
|
||||||
const ANGULAR_IMPORT_LOCATIONS = {
|
const ANGULAR_IMPORT_LOCATIONS = {
|
||||||
coreDecorators: '@angular/core/src/metadata',
|
coreDecorators: '@angular/core/src/metadata',
|
||||||
diDecorators: '@angular/core/src/di/metadata',
|
diDecorators: '@angular/core/src/di/metadata',
|
||||||
|
@ -22,66 +24,20 @@ const ANGULAR_IMPORT_LOCATIONS = {
|
||||||
|
|
||||||
const HIDDEN_KEY = /^\$.*\$$/;
|
const HIDDEN_KEY = /^\$.*\$$/;
|
||||||
|
|
||||||
/**
|
|
||||||
* The host of the StaticReflector disconnects the implementation from TypeScript / other language
|
|
||||||
* services and from underlying file systems.
|
|
||||||
*/
|
|
||||||
export interface StaticReflectorHost {
|
|
||||||
/**
|
|
||||||
* Return a ModuleMetadata for the given module.
|
|
||||||
* Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
|
|
||||||
* produced and the module has exported variables or classes with decorators. Module metadata can
|
|
||||||
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
|
|
||||||
*
|
|
||||||
* @param modulePath is a string identifier for a module as an absolute path.
|
|
||||||
* @returns the metadata for the given module.
|
|
||||||
*/
|
|
||||||
getMetadataFor(modulePath: string): {[key: string]: any}[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a module name that is used in an `import` to a file path.
|
|
||||||
* I.e.
|
|
||||||
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
|
|
||||||
*/
|
|
||||||
moduleNameToFileName(moduleName: string, containingFile: string): string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A cache of static symbol used by the StaticReflector to return the same symbol for the
|
|
||||||
* same symbol values.
|
|
||||||
*/
|
|
||||||
export class StaticSymbolCache {
|
|
||||||
private cache = new Map<string, StaticSymbol>();
|
|
||||||
|
|
||||||
get(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
|
||||||
const memberSuffix = members ? `.${ members.join('.')}` : '';
|
|
||||||
const key = `"${declarationFile}".${name}${memberSuffix}`;
|
|
||||||
let result = this.cache.get(key);
|
|
||||||
if (!result) {
|
|
||||||
result = new StaticSymbol(declarationFile, name, members);
|
|
||||||
this.cache.set(key, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A static reflector implements enough of the Reflector API that is necessary to compile
|
* A static reflector implements enough of the Reflector API that is necessary to compile
|
||||||
* templates statically.
|
* templates statically.
|
||||||
*/
|
*/
|
||||||
export class StaticReflector implements ReflectorReader {
|
export class StaticReflector implements ReflectorReader {
|
||||||
private declarationCache = new Map<string, StaticSymbol>();
|
|
||||||
private annotationCache = new Map<StaticSymbol, any[]>();
|
private annotationCache = new Map<StaticSymbol, any[]>();
|
||||||
private propertyCache = new Map<StaticSymbol, {[key: string]: any[]}>();
|
private propertyCache = new Map<StaticSymbol, {[key: string]: any[]}>();
|
||||||
private parameterCache = new Map<StaticSymbol, any[]>();
|
private parameterCache = new Map<StaticSymbol, any[]>();
|
||||||
private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
|
private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
|
||||||
private metadataCache = new Map<string, {[key: string]: any}>();
|
|
||||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||||
private opaqueToken: StaticSymbol;
|
private opaqueToken: StaticSymbol;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private host: StaticReflectorHost,
|
private symbolResolver: StaticSymbolResolver,
|
||||||
private staticSymbolCache: StaticSymbolCache = new StaticSymbolCache(),
|
|
||||||
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
|
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
|
||||||
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [],
|
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [],
|
||||||
private errorRecorder?: (error: any, fileName: string) => void) {
|
private errorRecorder?: (error: any, fileName: string) => void) {
|
||||||
|
@ -94,12 +50,26 @@ export class StaticReflector implements ReflectorReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
importUri(typeOrFunc: StaticSymbol): string {
|
importUri(typeOrFunc: StaticSymbol): string {
|
||||||
const staticSymbol = this.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
const staticSymbol = this.findSymbolDeclaration(typeOrFunc);
|
||||||
return staticSymbol ? staticSymbol.filePath : null;
|
return staticSymbol ? staticSymbol.filePath : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveIdentifier(name: string, moduleUrl: string, runtime: any): any {
|
resolveIdentifier(name: string, moduleUrl: string): StaticSymbol {
|
||||||
return this.findDeclaration(moduleUrl, name, '');
|
return this.findDeclaration(moduleUrl, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
findDeclaration(moduleUrl: string, name: string, containingFile?: string): StaticSymbol {
|
||||||
|
return this.findSymbolDeclaration(
|
||||||
|
this.symbolResolver.getSymbolByModule(moduleUrl, name, containingFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
findSymbolDeclaration(symbol: StaticSymbol): StaticSymbol {
|
||||||
|
const resolvedSymbol = this.symbolResolver.resolveSymbol(symbol);
|
||||||
|
if (resolvedSymbol && resolvedSymbol.metadata instanceof StaticSymbol) {
|
||||||
|
return this.findSymbolDeclaration(resolvedSymbol.metadata);
|
||||||
|
} else {
|
||||||
|
return symbol;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveEnum(enumIdentifier: any, name: string): any {
|
resolveEnum(enumIdentifier: any, name: string): any {
|
||||||
|
@ -128,7 +98,7 @@ export class StaticReflector implements ReflectorReader {
|
||||||
public propMetadata(type: StaticSymbol): {[key: string]: any[]} {
|
public propMetadata(type: StaticSymbol): {[key: string]: any[]} {
|
||||||
let propMetadata = this.propertyCache.get(type);
|
let propMetadata = this.propertyCache.get(type);
|
||||||
if (!propMetadata) {
|
if (!propMetadata) {
|
||||||
const classMetadata = this.getTypeMetadata(type) || {};
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
propMetadata = {};
|
propMetadata = {};
|
||||||
if (classMetadata['extends']) {
|
if (classMetadata['extends']) {
|
||||||
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
|
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
|
||||||
|
@ -203,7 +173,7 @@ export class StaticReflector implements ReflectorReader {
|
||||||
private _methodNames(type: any): {[key: string]: boolean} {
|
private _methodNames(type: any): {[key: string]: boolean} {
|
||||||
let methodNames = this.methodCache.get(type);
|
let methodNames = this.methodCache.get(type);
|
||||||
if (!methodNames) {
|
if (!methodNames) {
|
||||||
const classMetadata = this.getTypeMetadata(type) || {};
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
methodNames = {};
|
methodNames = {};
|
||||||
if (classMetadata['extends']) {
|
if (classMetadata['extends']) {
|
||||||
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
|
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
|
||||||
|
@ -306,7 +276,7 @@ export class StaticReflector implements ReflectorReader {
|
||||||
* @param name the name of the type.
|
* @param name the name of the type.
|
||||||
*/
|
*/
|
||||||
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
||||||
return this.staticSymbolCache.get(declarationFile, name, members);
|
return this.symbolResolver.getStaticSymbol(declarationFile, name, members);
|
||||||
}
|
}
|
||||||
|
|
||||||
private reportError(error: Error, context: StaticSymbol, path?: string) {
|
private reportError(error: Error, context: StaticSymbol, path?: string) {
|
||||||
|
@ -317,96 +287,6 @@ export class StaticReflector implements ReflectorReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
|
|
||||||
const resolveModule = (moduleName: string): string => {
|
|
||||||
const resolvedModulePath = this.host.moduleNameToFileName(moduleName, filePath);
|
|
||||||
if (!resolvedModulePath) {
|
|
||||||
this.reportError(
|
|
||||||
new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`),
|
|
||||||
null, filePath);
|
|
||||||
}
|
|
||||||
return resolvedModulePath;
|
|
||||||
};
|
|
||||||
const cacheKey = `${filePath}|${symbolName}`;
|
|
||||||
let staticSymbol = this.declarationCache.get(cacheKey);
|
|
||||||
if (staticSymbol) {
|
|
||||||
return staticSymbol;
|
|
||||||
}
|
|
||||||
const metadata = this.getModuleMetadata(filePath);
|
|
||||||
if (metadata) {
|
|
||||||
// If we have metadata for the symbol, this is the original exporting location.
|
|
||||||
if (metadata['metadata'][symbolName]) {
|
|
||||||
staticSymbol = this.getStaticSymbol(filePath, symbolName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no, try to find the symbol in one of the re-export location
|
|
||||||
if (!staticSymbol && metadata['exports']) {
|
|
||||||
// Try and find the symbol in the list of explicitly re-exported symbols.
|
|
||||||
for (const moduleExport of metadata['exports']) {
|
|
||||||
if (moduleExport.export) {
|
|
||||||
const exportSymbol = moduleExport.export.find((symbol: any) => {
|
|
||||||
if (typeof symbol === 'string') {
|
|
||||||
return symbol == symbolName;
|
|
||||||
} else {
|
|
||||||
return symbol.as == symbolName;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (exportSymbol) {
|
|
||||||
let symName = symbolName;
|
|
||||||
if (typeof exportSymbol !== 'string') {
|
|
||||||
symName = exportSymbol.name;
|
|
||||||
}
|
|
||||||
const resolvedModule = resolveModule(moduleExport.from);
|
|
||||||
if (resolvedModule) {
|
|
||||||
staticSymbol =
|
|
||||||
this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!staticSymbol) {
|
|
||||||
// Try to find the symbol via export * directives.
|
|
||||||
for (const moduleExport of metadata['exports']) {
|
|
||||||
if (!moduleExport.export) {
|
|
||||||
const resolvedModule = resolveModule(moduleExport.from);
|
|
||||||
if (resolvedModule) {
|
|
||||||
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
|
|
||||||
if (candidateSymbol) {
|
|
||||||
staticSymbol = candidateSymbol;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.declarationCache.set(cacheKey, staticSymbol);
|
|
||||||
return staticSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
findDeclaration(module: string, symbolName: string, containingFile?: string): StaticSymbol {
|
|
||||||
try {
|
|
||||||
const filePath = this.host.moduleNameToFileName(module, containingFile);
|
|
||||||
let symbol: StaticSymbol;
|
|
||||||
if (!filePath) {
|
|
||||||
// If the file cannot be found the module is probably referencing a declared module
|
|
||||||
// for which there is no disambiguating file and we also don't need to track
|
|
||||||
// re-exports. Just use the module name.
|
|
||||||
symbol = this.getStaticSymbol(module, symbolName);
|
|
||||||
} else {
|
|
||||||
symbol = this.resolveExportedSymbol(filePath, symbolName) ||
|
|
||||||
this.getStaticSymbol(filePath, symbolName);
|
|
||||||
}
|
|
||||||
return symbol;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`can't resolve module ${module} from ${containingFile}`);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
public simplify(context: StaticSymbol, value: any): any {
|
public simplify(context: StaticSymbol, value: any): any {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
@ -414,93 +294,41 @@ export class StaticReflector implements ReflectorReader {
|
||||||
const calling = new Map<StaticSymbol, boolean>();
|
const calling = new Map<StaticSymbol, boolean>();
|
||||||
|
|
||||||
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
|
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
|
||||||
function resolveReference(context: StaticSymbol, expression: any): StaticSymbol {
|
|
||||||
let staticSymbol: StaticSymbol;
|
|
||||||
if (expression['module']) {
|
|
||||||
staticSymbol =
|
|
||||||
self.findDeclaration(expression['module'], expression['name'], context.filePath);
|
|
||||||
} else {
|
|
||||||
staticSymbol = self.getStaticSymbol(context.filePath, expression['name']);
|
|
||||||
}
|
|
||||||
return staticSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
||||||
const moduleMetadata = self.getModuleMetadata(staticSymbol.filePath);
|
const resolvedSymbol = self.symbolResolver.resolveSymbol(staticSymbol);
|
||||||
const declarationValue =
|
return resolvedSymbol ? resolvedSymbol.metadata : null;
|
||||||
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
|
||||||
return declarationValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOpaqueToken(context: StaticSymbol, value: any): boolean {
|
function simplifyCall(functionSymbol: StaticSymbol, targetFunction: any, args: any[]) {
|
||||||
if (value && value.__symbolic === 'new' && value.expression) {
|
if (targetFunction && targetFunction['__symbolic'] == 'function') {
|
||||||
const target = value.expression;
|
if (calling.get(functionSymbol)) {
|
||||||
if (target.__symbolic == 'reference') {
|
throw new Error('Recursion not supported');
|
||||||
return sameSymbol(resolveReference(context, target), self.opaqueToken);
|
|
||||||
}
|
}
|
||||||
}
|
calling.set(functionSymbol, true);
|
||||||
return false;
|
try {
|
||||||
}
|
const value = targetFunction['value'];
|
||||||
|
if (value && (depth != 0 || value.__symbolic != 'error')) {
|
||||||
function simplifyCall(expression: any) {
|
const parameters: string[] = targetFunction['parameters'];
|
||||||
let callContext: {[name: string]: string}|undefined = undefined;
|
const defaults: any[] = targetFunction.defaults;
|
||||||
if (expression['__symbolic'] == 'call') {
|
if (defaults && defaults.length > args.length) {
|
||||||
const target = expression['expression'];
|
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
|
||||||
let functionSymbol: StaticSymbol;
|
|
||||||
let targetFunction: any;
|
|
||||||
if (target) {
|
|
||||||
switch (target.__symbolic) {
|
|
||||||
case 'reference':
|
|
||||||
// Find the function to call.
|
|
||||||
callContext = {name: target.name};
|
|
||||||
functionSymbol = resolveReference(context, target);
|
|
||||||
targetFunction = resolveReferenceValue(functionSymbol);
|
|
||||||
break;
|
|
||||||
case 'select':
|
|
||||||
// Find the static method to call
|
|
||||||
if (target.expression.__symbolic == 'reference') {
|
|
||||||
functionSymbol = resolveReference(context, target.expression);
|
|
||||||
const classData = resolveReferenceValue(functionSymbol);
|
|
||||||
if (classData && classData.statics) {
|
|
||||||
targetFunction = classData.statics[target.member];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (targetFunction && targetFunction['__symbolic'] == 'function') {
|
|
||||||
if (calling.get(functionSymbol)) {
|
|
||||||
throw new Error('Recursion not supported');
|
|
||||||
}
|
|
||||||
calling.set(functionSymbol, true);
|
|
||||||
try {
|
|
||||||
const value = targetFunction['value'];
|
|
||||||
if (value && (depth != 0 || value.__symbolic != 'error')) {
|
|
||||||
// Determine the arguments
|
|
||||||
const args: any[] =
|
|
||||||
(expression['arguments'] || []).map((arg: any) => simplify(arg));
|
|
||||||
const parameters: string[] = targetFunction['parameters'];
|
|
||||||
const defaults: any[] = targetFunction.defaults;
|
|
||||||
if (defaults && defaults.length > args.length) {
|
|
||||||
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
|
|
||||||
}
|
|
||||||
const functionScope = BindingScope.build();
|
|
||||||
for (let i = 0; i < parameters.length; i++) {
|
|
||||||
functionScope.define(parameters[i], args[i]);
|
|
||||||
}
|
|
||||||
const oldScope = scope;
|
|
||||||
let result: any;
|
|
||||||
try {
|
|
||||||
scope = functionScope.done();
|
|
||||||
result = simplifyInContext(functionSymbol, value, depth + 1);
|
|
||||||
} finally {
|
|
||||||
scope = oldScope;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
} finally {
|
const functionScope = BindingScope.build();
|
||||||
calling.delete(functionSymbol);
|
for (let i = 0; i < parameters.length; i++) {
|
||||||
|
functionScope.define(parameters[i], args[i]);
|
||||||
|
}
|
||||||
|
const oldScope = scope;
|
||||||
|
let result: any;
|
||||||
|
try {
|
||||||
|
scope = functionScope.done();
|
||||||
|
result = simplifyInContext(functionSymbol, value, depth + 1);
|
||||||
|
} finally {
|
||||||
|
scope = oldScope;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
calling.delete(functionSymbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +339,7 @@ export class StaticReflector implements ReflectorReader {
|
||||||
return {__symbolic: 'ignore'};
|
return {__symbolic: 'ignore'};
|
||||||
}
|
}
|
||||||
return simplify(
|
return simplify(
|
||||||
{__symbolic: 'error', message: 'Function call not supported', context: callContext});
|
{__symbolic: 'error', message: 'Function call not supported', context: functionSymbol});
|
||||||
}
|
}
|
||||||
|
|
||||||
function simplify(expression: any): any {
|
function simplify(expression: any): any {
|
||||||
|
@ -540,7 +368,18 @@ export class StaticReflector implements ReflectorReader {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (expression instanceof StaticSymbol) {
|
if (expression instanceof StaticSymbol) {
|
||||||
return expression;
|
// Stop simplification at builtin symbols
|
||||||
|
if (expression === self.opaqueToken || self.conversionMap.has(expression)) {
|
||||||
|
return expression;
|
||||||
|
} else {
|
||||||
|
const staticSymbol = expression;
|
||||||
|
const declarationValue = resolveReferenceValue(staticSymbol);
|
||||||
|
if (declarationValue) {
|
||||||
|
return simplifyInContext(staticSymbol, declarationValue, depth + 1);
|
||||||
|
} else {
|
||||||
|
return staticSymbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (expression) {
|
if (expression) {
|
||||||
if (expression['__symbolic']) {
|
if (expression['__symbolic']) {
|
||||||
|
@ -618,50 +457,33 @@ export class StaticReflector implements ReflectorReader {
|
||||||
if (indexTarget && isPrimitive(index)) return indexTarget[index];
|
if (indexTarget && isPrimitive(index)) return indexTarget[index];
|
||||||
return null;
|
return null;
|
||||||
case 'select':
|
case 'select':
|
||||||
|
const member = expression['member'];
|
||||||
let selectContext = context;
|
let selectContext = context;
|
||||||
let selectTarget = simplify(expression['expression']);
|
let selectTarget = simplify(expression['expression']);
|
||||||
if (selectTarget instanceof StaticSymbol) {
|
if (selectTarget instanceof StaticSymbol) {
|
||||||
// Access to a static instance variable
|
const members = selectTarget.members.concat(member);
|
||||||
const member: string = expression['member'];
|
|
||||||
const members = selectTarget.members ?
|
|
||||||
(selectTarget.members as string[]).concat(member) :
|
|
||||||
[member];
|
|
||||||
const declarationValue = resolveReferenceValue(selectTarget);
|
|
||||||
selectContext =
|
selectContext =
|
||||||
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
||||||
if (declarationValue && declarationValue.statics) {
|
const declarationValue = resolveReferenceValue(selectContext);
|
||||||
selectTarget = declarationValue.statics;
|
if (declarationValue) {
|
||||||
|
return simplifyInContext(selectContext, declarationValue, depth + 1);
|
||||||
} else {
|
} else {
|
||||||
return selectContext;
|
return selectContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const member = simplifyInContext(selectContext, expression['member'], depth + 1);
|
|
||||||
if (selectTarget && isPrimitive(member))
|
if (selectTarget && isPrimitive(member))
|
||||||
return simplifyInContext(selectContext, selectTarget[member], depth + 1);
|
return simplifyInContext(selectContext, selectTarget[member], depth + 1);
|
||||||
return null;
|
return null;
|
||||||
case 'reference':
|
case 'reference':
|
||||||
if (!expression['name']) {
|
// Note: This only has to deal with variable references,
|
||||||
return context;
|
// as symbol references have been converted into StaticSymbols already
|
||||||
|
// in the StaticSymbolResolver!
|
||||||
|
const name: string = expression['name'];
|
||||||
|
const localValue = scope.resolve(name);
|
||||||
|
if (localValue != BindingScope.missing) {
|
||||||
|
return localValue;
|
||||||
}
|
}
|
||||||
if (!expression.module) {
|
break;
|
||||||
const name: string = expression['name'];
|
|
||||||
const localValue = scope.resolve(name);
|
|
||||||
if (localValue != BindingScope.missing) {
|
|
||||||
return localValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
staticSymbol = resolveReference(context, expression);
|
|
||||||
let result: any = staticSymbol;
|
|
||||||
let declarationValue = resolveReferenceValue(result);
|
|
||||||
if (declarationValue) {
|
|
||||||
if (isOpaqueToken(staticSymbol, declarationValue)) {
|
|
||||||
// If the referenced symbol is initalized by a new OpaqueToken we can keep the
|
|
||||||
// reference to the symbol.
|
|
||||||
return staticSymbol;
|
|
||||||
}
|
|
||||||
result = simplifyInContext(staticSymbol, declarationValue, depth + 1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
case 'class':
|
case 'class':
|
||||||
return context;
|
return context;
|
||||||
case 'function':
|
case 'function':
|
||||||
|
@ -669,26 +491,26 @@ export class StaticReflector implements ReflectorReader {
|
||||||
case 'new':
|
case 'new':
|
||||||
case 'call':
|
case 'call':
|
||||||
// Determine if the function is a built-in conversion
|
// Determine if the function is a built-in conversion
|
||||||
let target = expression['expression'];
|
staticSymbol = simplifyInContext(context, expression['expression'], depth + 1);
|
||||||
if (target['module']) {
|
if (staticSymbol instanceof StaticSymbol) {
|
||||||
staticSymbol =
|
if (staticSymbol === self.opaqueToken) {
|
||||||
self.findDeclaration(target['module'], target['name'], context.filePath);
|
// if somebody calls new OpaqueToken, don't create an OpaqueToken,
|
||||||
} else {
|
// but rather return the symbol to which the OpaqueToken is assigned to.
|
||||||
staticSymbol = self.getStaticSymbol(context.filePath, target['name']);
|
return context;
|
||||||
}
|
}
|
||||||
let converter = self.conversionMap.get(staticSymbol);
|
const argExpressions: any[] = expression['arguments'] || [];
|
||||||
if (converter) {
|
const args =
|
||||||
let args: any[] = expression['arguments'];
|
argExpressions.map(arg => simplifyInContext(context, arg, depth + 1));
|
||||||
if (!args) {
|
let converter = self.conversionMap.get(staticSymbol);
|
||||||
args = [];
|
if (converter) {
|
||||||
|
return converter(context, args);
|
||||||
|
} else {
|
||||||
|
// Determine if the function is one we can simplify.
|
||||||
|
const targetFunction = resolveReferenceValue(staticSymbol);
|
||||||
|
return simplifyCall(staticSymbol, targetFunction, args);
|
||||||
}
|
}
|
||||||
return converter(
|
|
||||||
context, args.map(arg => simplifyInContext(context, arg, depth + 1)));
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
// Determine if the function is one we can simplify.
|
|
||||||
return simplifyCall(expression);
|
|
||||||
|
|
||||||
case 'error':
|
case 'error':
|
||||||
let message = produceErrorMessage(expression);
|
let message = produceErrorMessage(expression);
|
||||||
if (expression['line']) {
|
if (expression['line']) {
|
||||||
|
@ -709,7 +531,9 @@ export class StaticReflector implements ReflectorReader {
|
||||||
try {
|
try {
|
||||||
return simplify(value);
|
return simplify(value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = `${e.message}, resolving symbol ${context.name} in ${context.filePath}`;
|
const members = context.members.length ? `.${context.members.join('.')}` : '';
|
||||||
|
const message =
|
||||||
|
`${e.message}, resolving symbol ${context.name}${members} in ${context.filePath}`;
|
||||||
if (e.fileName) {
|
if (e.fileName) {
|
||||||
throw positionalError(message, e.fileName, e.line, e.column);
|
throw positionalError(message, e.fileName, e.line, e.column);
|
||||||
}
|
}
|
||||||
|
@ -733,40 +557,10 @@ export class StaticReflector implements ReflectorReader {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param module an absolute path to a module file.
|
|
||||||
*/
|
|
||||||
public getModuleMetadata(module: string): {[key: string]: any} {
|
|
||||||
let moduleMetadata = this.metadataCache.get(module);
|
|
||||||
if (!moduleMetadata) {
|
|
||||||
const moduleMetadatas = this.host.getMetadataFor(module);
|
|
||||||
if (moduleMetadatas) {
|
|
||||||
let maxVersion = -1;
|
|
||||||
moduleMetadatas.forEach((md) => {
|
|
||||||
if (md['version'] > maxVersion) {
|
|
||||||
maxVersion = md['version'];
|
|
||||||
moduleMetadata = md;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!moduleMetadata) {
|
|
||||||
moduleMetadata =
|
|
||||||
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
|
|
||||||
}
|
|
||||||
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
|
||||||
const errorMessage = moduleMetadata['version'] == 2 ?
|
|
||||||
`Unsupported metadata version ${moduleMetadata['version']} for module ${module}. This module should be compiled with a newer version of ngc` :
|
|
||||||
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`;
|
|
||||||
this.reportError(new Error(errorMessage), null);
|
|
||||||
}
|
|
||||||
this.metadataCache.set(module, moduleMetadata);
|
|
||||||
}
|
|
||||||
return moduleMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
|
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||||
const moduleMetadata = this.getModuleMetadata(type.filePath);
|
const resolvedSymbol = this.symbolResolver.resolveSymbol(type);
|
||||||
return moduleMetadata['metadata'][type.name] || {__symbolic: 'class'};
|
return resolvedSymbol && resolvedSymbol.metadata ? resolvedSymbol.metadata :
|
||||||
|
{__symbolic: 'class'};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,7 +652,7 @@ class PopulatedScope extends BindingScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
|
function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
|
||||||
return a === b || (a.name == b.name && a.filePath == b.filePath);
|
return a === b;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldIgnore(value: any): boolean {
|
function shouldIgnore(value: any): boolean {
|
||||||
|
|
|
@ -14,3 +14,23 @@
|
||||||
export class StaticSymbol {
|
export class StaticSymbol {
|
||||||
constructor(public filePath: string, public name: string, public members?: string[]) {}
|
constructor(public filePath: string, public name: string, public members?: string[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of static symbol used by the StaticReflector to return the same symbol for the
|
||||||
|
* same symbol values.
|
||||||
|
*/
|
||||||
|
export class StaticSymbolCache {
|
||||||
|
private cache = new Map<string, StaticSymbol>();
|
||||||
|
|
||||||
|
get(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
||||||
|
members = members || [];
|
||||||
|
const memberSuffix = members.length ? `.${ members.join('.')}` : '';
|
||||||
|
const key = `"${declarationFile}".${name}${memberSuffix}`;
|
||||||
|
let result = this.cache.get(key);
|
||||||
|
if (!result) {
|
||||||
|
result = new StaticSymbol(declarationFile, name, members);
|
||||||
|
this.cache.set(key, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleSummary, CompilePipeSummary, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
|
import {CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
|
||||||
import {SummaryResolver} from '../summary_resolver';
|
import {Summary, SummaryResolver} from '../summary_resolver';
|
||||||
|
|
||||||
import {GeneratedFile} from './generated_file';
|
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||||
import {StaticReflector} from './static_reflector';
|
import {ResolvedStaticSymbol} from './static_symbol_resolver';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {deserializeSummaries, summaryFileName} from './summary_serializer';
|
||||||
import {filterFileByPatterns} from './utils';
|
|
||||||
|
|
||||||
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
|
|
||||||
|
@ -19,106 +18,60 @@ export interface AotSummaryResolverHost {
|
||||||
/**
|
/**
|
||||||
* Loads an NgModule/Directive/Pipe summary file
|
* Loads an NgModule/Directive/Pipe summary file
|
||||||
*/
|
*/
|
||||||
loadSummary(filePath: string): string;
|
loadSummary(filePath: string): string /*|null*/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the output file path of a source file.
|
* Returns whether a file is a source file or not.
|
||||||
* E.g.
|
|
||||||
* `some_file.ts` -> `some_file.d.ts`
|
|
||||||
*/
|
*/
|
||||||
getOutputFileName(sourceFilePath: string): string;
|
isSourceFile(sourceFilePath: string): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AotSummaryResolverOptions {
|
export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
|
||||||
includeFilePattern?: RegExp;
|
private summaryCache = new Map<StaticSymbol, Summary<StaticSymbol>>();
|
||||||
excludeFilePattern?: RegExp;
|
private loadedFilePaths = new Set<string>();
|
||||||
}
|
|
||||||
|
|
||||||
export class AotSummaryResolver implements SummaryResolver {
|
constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {}
|
||||||
private summaryCache: {[cacheKey: string]: CompileTypeSummary} = {};
|
|
||||||
|
|
||||||
constructor(
|
private _assertNoMembers(symbol: StaticSymbol) {
|
||||||
private host: AotSummaryResolverHost, private staticReflector: StaticReflector,
|
if (symbol.members.length) {
|
||||||
private options: AotSummaryResolverOptions) {}
|
throw new Error(
|
||||||
|
`Internal state: StaticSymbols in summaries can't have members! ${JSON.stringify(symbol)}`);
|
||||||
serializeSummaries(srcFileUrl: string, summaries: CompileTypeSummary[]): GeneratedFile {
|
}
|
||||||
const jsonReplacer = (key: string, value: any) => {
|
|
||||||
if (value instanceof StaticSymbol) {
|
|
||||||
// We convert the source filenames into output filenames,
|
|
||||||
// as the generated summary file will be used when the current
|
|
||||||
// compilation unit is used as a library
|
|
||||||
return {
|
|
||||||
'__symbolic__': 'symbol',
|
|
||||||
'name': value.name,
|
|
||||||
'path': this.host.getOutputFileName(value.filePath),
|
|
||||||
'members': value.members
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
const allSummaries = summaries.slice();
|
|
||||||
summaries.forEach((summary) => {
|
|
||||||
if (summary.summaryKind === CompileSummaryKind.NgModule) {
|
|
||||||
const moduleMeta = <CompileNgModuleSummary>summary;
|
|
||||||
moduleMeta.exportedDirectives.concat(moduleMeta.exportedPipes).forEach((id) => {
|
|
||||||
if (!filterFileByPatterns(id.reference.filePath, this.options)) {
|
|
||||||
allSummaries.push(this.resolveSummary(id.reference));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return new GeneratedFile(
|
|
||||||
srcFileUrl, summaryFileName(srcFileUrl), JSON.stringify(allSummaries, jsonReplacer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _cacheKey(symbol: StaticSymbol) { return `${symbol.filePath}|${symbol.name}`; }
|
resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> {
|
||||||
|
this._assertNoMembers(staticSymbol);
|
||||||
|
let summary = this.summaryCache.get(staticSymbol);
|
||||||
|
if (!summary) {
|
||||||
|
this._loadSummaryFile(staticSymbol.filePath);
|
||||||
|
summary = this.summaryCache.get(staticSymbol);
|
||||||
|
}
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
resolveSummary(staticSymbol: StaticSymbol): any {
|
getSymbolsOf(filePath: string): StaticSymbol[] {
|
||||||
const filePath = staticSymbol.filePath;
|
this._loadSummaryFile(filePath);
|
||||||
const name = staticSymbol.name;
|
return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
|
||||||
const cacheKey = this._cacheKey(staticSymbol);
|
}
|
||||||
if (!filterFileByPatterns(filePath, this.options)) {
|
|
||||||
let summary = this.summaryCache[cacheKey];
|
private _loadSummaryFile(filePath: string) {
|
||||||
|
if (this.loadedFilePaths.has(filePath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loadedFilePaths.add(filePath);
|
||||||
|
if (!this.host.isSourceFile(filePath)) {
|
||||||
const summaryFilePath = summaryFileName(filePath);
|
const summaryFilePath = summaryFileName(filePath);
|
||||||
if (!summary) {
|
let json: string;
|
||||||
try {
|
try {
|
||||||
const jsonReviver = (key: string, value: any) => {
|
json = this.host.loadSummary(summaryFilePath);
|
||||||
if (value && value['__symbolic__'] === 'symbol') {
|
} catch (e) {
|
||||||
// Note: We can't use staticReflector.findDeclaration here:
|
console.error(`Error loading summary file ${summaryFilePath}`);
|
||||||
// Summary files can contain symbols of transitive compilation units
|
throw e;
|
||||||
// (via the providers), and findDeclaration needs .metadata.json / .d.ts files,
|
|
||||||
// but we don't want to depend on these for transitive dependencies.
|
|
||||||
return this.staticReflector.getStaticSymbol(
|
|
||||||
value['path'], value['name'], value['members']);
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const readSummaries: CompileTypeSummary[] =
|
|
||||||
JSON.parse(this.host.loadSummary(summaryFilePath), jsonReviver);
|
|
||||||
readSummaries.forEach((summary) => {
|
|
||||||
const filePath = summary.type.reference.filePath;
|
|
||||||
this.summaryCache[this._cacheKey(summary.type.reference)] = summary;
|
|
||||||
});
|
|
||||||
summary = this.summaryCache[cacheKey];
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Error loading summary file ${summaryFilePath}`);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!summary) {
|
if (json) {
|
||||||
throw new Error(
|
const readSummaries = deserializeSummaries(this.staticSymbolCache, json);
|
||||||
`Could not find the symbol ${name} in the summary file ${summaryFilePath}!`);
|
readSummaries.forEach((summary) => { this.summaryCache.set(summary.symbol, summary); });
|
||||||
}
|
}
|
||||||
return summary;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function summaryFileName(fileName: string): string {
|
|
||||||
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
|
|
||||||
return `${fileNameWithoutSuffix}.ngsummary.json`;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 interface CompileIdentifierMetadata { reference: any; }
|
||||||
|
|
||||||
export enum CompileSummaryKind {
|
export enum CompileSummaryKind {
|
||||||
Template,
|
|
||||||
Pipe,
|
Pipe,
|
||||||
Directive,
|
Directive,
|
||||||
NgModule
|
NgModule,
|
||||||
|
Injectable
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,9 +126,10 @@ export enum CompileSummaryKind {
|
||||||
* in other modules / components. However, this data is not enough to compile
|
* in other modules / components. However, this data is not enough to compile
|
||||||
* the directive / module itself.
|
* the directive / module itself.
|
||||||
*/
|
*/
|
||||||
export interface CompileSummary { summaryKind: CompileSummaryKind; }
|
export interface CompileTypeSummary {
|
||||||
|
summaryKind: CompileSummaryKind
|
||||||
export interface CompileTypeSummary extends CompileSummary { type: CompileTypeMetadata; }
|
type: CompileTypeMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CompileDiDependencyMetadata {
|
export interface CompileDiDependencyMetadata {
|
||||||
isAttribute?: boolean;
|
isAttribute?: boolean;
|
||||||
|
@ -210,7 +211,7 @@ export class CompileStylesheetMetadata {
|
||||||
/**
|
/**
|
||||||
* Summary Metadata regarding compilation of a template.
|
* Summary Metadata regarding compilation of a template.
|
||||||
*/
|
*/
|
||||||
export interface CompileTemplateSummary extends CompileSummary {
|
export interface CompileTemplateSummary {
|
||||||
animations: string[];
|
animations: string[];
|
||||||
ngContentSelectors: string[];
|
ngContentSelectors: string[];
|
||||||
encapsulation: ViewEncapsulation;
|
encapsulation: ViewEncapsulation;
|
||||||
|
@ -258,7 +259,6 @@ export class CompileTemplateMetadata {
|
||||||
|
|
||||||
toSummary(): CompileTemplateSummary {
|
toSummary(): CompileTemplateSummary {
|
||||||
return {
|
return {
|
||||||
summaryKind: CompileSummaryKind.Template,
|
|
||||||
animations: this.animations.map(anim => anim.name),
|
animations: this.animations.map(anim => anim.name),
|
||||||
ngContentSelectors: this.ngContentSelectors,
|
ngContentSelectors: this.ngContentSelectors,
|
||||||
encapsulation: this.encapsulation
|
encapsulation: this.encapsulation
|
||||||
|
|
|
@ -6,11 +6,12 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component, Injectable, ViewEncapsulation} from '@angular/core';
|
import {Component, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
|
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {isBlank, isPresent, stringify} from './facade/lang';
|
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
import * as html from './ml_parser/ast';
|
import * as html from './ml_parser/ast';
|
||||||
import {HtmlParser} from './ml_parser/html_parser';
|
import {HtmlParser} from './ml_parser/html_parser';
|
||||||
import {InterpolationConfig} from './ml_parser/interpolation_config';
|
import {InterpolationConfig} from './ml_parser/interpolation_config';
|
||||||
|
@ -32,7 +33,7 @@ export interface PrenormalizedTemplateMetadata {
|
||||||
animations?: CompileAnimationEntryMetadata[];
|
animations?: CompileAnimationEntryMetadata[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class DirectiveNormalizer {
|
export class DirectiveNormalizer {
|
||||||
private _resourceLoaderCache = new Map<string, Promise<string>>();
|
private _resourceLoaderCache = new Map<string, Promise<string>>();
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,16 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
|
import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper, StringMapWrapper} from './facade/collection';
|
import {ListWrapper, StringMapWrapper} from './facade/collection';
|
||||||
import {stringify} from './facade/lang';
|
import {stringify} from './facade/lang';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
import {ReflectorReader, reflector} from './private_import_core';
|
import {ReflectorReader, reflector} from './private_import_core';
|
||||||
import {splitAtColon} from './util';
|
import {splitAtColon} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Resolve a `Type` for {@link Directive}.
|
* Resolve a `Type` for {@link Directive}.
|
||||||
*
|
*
|
||||||
|
@ -21,7 +23,7 @@ import {splitAtColon} from './util';
|
||||||
*
|
*
|
||||||
* See {@link Compiler}
|
* See {@link Compiler}
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class DirectiveResolver {
|
export class DirectiveResolver {
|
||||||
constructor(private _reflector: ReflectorReader = reflector) {}
|
constructor(private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
||||||
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
|
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
|
||||||
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
||||||
|
@ -15,6 +13,7 @@ import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {Parser} from './expression_parser/parser';
|
import {Parser} from './expression_parser/parser';
|
||||||
import {Identifiers, createIdentifier} from './identifiers';
|
import {Identifiers, createIdentifier} from './identifiers';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config';
|
||||||
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
|
@ -51,7 +50,7 @@ const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap
|
||||||
*
|
*
|
||||||
* So far, only `@Input` and the lifecycle hooks have been implemented.
|
* So far, only `@Input` and the lifecycle hooks have been implemented.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class DirectiveWrapperCompiler {
|
export class DirectiveWrapperCompiler {
|
||||||
static dirWrapperClassName(id: CompileIdentifierMetadata) {
|
static dirWrapperClassName(id: CompileIdentifierMetadata) {
|
||||||
return `Wrapper_${identifierName(id)}`;
|
return `Wrapper_${identifierName(id)}`;
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
|
||||||
import * as chars from '../chars';
|
import * as chars from '../chars';
|
||||||
import {NumberWrapper, isPresent} from '../facade/lang';
|
import {NumberWrapper, isPresent} from '../facade/lang';
|
||||||
|
import {CompilerInjectable} from '../injectable';
|
||||||
|
|
||||||
export enum TokenType {
|
export enum TokenType {
|
||||||
Character,
|
Character,
|
||||||
|
@ -22,7 +22,7 @@ export enum TokenType {
|
||||||
|
|
||||||
const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
|
const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class Lexer {
|
export class Lexer {
|
||||||
tokenize(text: string): Token[] {
|
tokenize(text: string): Token[] {
|
||||||
const scanner = new _Scanner(text);
|
const scanner = new _Scanner(text);
|
||||||
|
|
|
@ -6,10 +6,9 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
|
||||||
|
|
||||||
import * as chars from '../chars';
|
import * as chars from '../chars';
|
||||||
import {escapeRegExp, isBlank, isPresent} from '../facade/lang';
|
import {escapeRegExp, isBlank, isPresent} from '../facade/lang';
|
||||||
|
import {CompilerInjectable} from '../injectable';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||||
|
|
||||||
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
|
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
|
||||||
|
@ -31,7 +30,7 @@ function _createInterpolateRegExp(config: InterpolationConfig): RegExp {
|
||||||
return new RegExp(pattern, 'g');
|
return new RegExp(pattern, 'g');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class Parser {
|
export class Parser {
|
||||||
private errors: ParserError[] = [];
|
private errors: ParserError[] = [];
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,9 @@ import {ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
|
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
|
||||||
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
|
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
|
||||||
import {StaticReflector, StaticReflectorHost} from '../aot/static_reflector';
|
import {StaticReflector} from '../aot/static_reflector';
|
||||||
|
import {StaticSymbolCache} from '../aot/static_symbol';
|
||||||
|
import {StaticSymbolResolver, StaticSymbolResolverHost} from '../aot/static_symbol_resolver';
|
||||||
import {AotSummaryResolver, AotSummaryResolverHost} from '../aot/summary_resolver';
|
import {AotSummaryResolver, AotSummaryResolverHost} from '../aot/summary_resolver';
|
||||||
import {CompileDirectiveMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata} from '../compile_metadata';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
|
@ -33,16 +35,11 @@ import {createOfflineCompileUrlResolver} from '../url_resolver';
|
||||||
import {I18NHtmlParser} from './i18n_html_parser';
|
import {I18NHtmlParser} from './i18n_html_parser';
|
||||||
import {MessageBundle} from './message_bundle';
|
import {MessageBundle} from './message_bundle';
|
||||||
|
|
||||||
export interface ExtractorOptions {
|
|
||||||
includeFilePattern?: RegExp;
|
|
||||||
excludeFilePattern?: RegExp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The host of the Extractor disconnects the implementation from TypeScript / other language
|
* The host of the Extractor disconnects the implementation from TypeScript / other language
|
||||||
* services and from underlying file systems.
|
* services and from underlying file systems.
|
||||||
*/
|
*/
|
||||||
export interface ExtractorHost extends StaticReflectorHost, AotSummaryResolverHost {
|
export interface ExtractorHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
|
@ -51,14 +48,13 @@ export interface ExtractorHost extends StaticReflectorHost, AotSummaryResolverHo
|
||||||
|
|
||||||
export class Extractor {
|
export class Extractor {
|
||||||
constructor(
|
constructor(
|
||||||
private options: ExtractorOptions, public host: ExtractorHost,
|
public host: ExtractorHost, private staticSymbolResolver: StaticSymbolResolver,
|
||||||
private staticReflector: StaticReflector, private messageBundle: MessageBundle,
|
private messageBundle: MessageBundle, private metadataResolver: CompileMetadataResolver) {}
|
||||||
private metadataResolver: CompileMetadataResolver) {}
|
|
||||||
|
|
||||||
extract(rootFiles: string[]): Promise<MessageBundle> {
|
extract(rootFiles: string[]): Promise<MessageBundle> {
|
||||||
const programSymbols = extractProgramSymbols(this.staticReflector, rootFiles, this.options);
|
const programSymbols = extractProgramSymbols(this.staticSymbolResolver, rootFiles, this.host);
|
||||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||||
analyzeAndValidateNgModules(programSymbols, this.options, this.metadataResolver);
|
analyzeAndValidateNgModules(programSymbols, this.host, this.metadataResolver);
|
||||||
return Promise
|
return Promise
|
||||||
.all(ngModules.map(
|
.all(ngModules.map(
|
||||||
ngModule => this.metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
ngModule => this.metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||||
|
@ -91,12 +87,14 @@ export class Extractor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(host: ExtractorHost, options: ExtractorOptions):
|
static create(host: ExtractorHost): {extractor: Extractor, staticReflector: StaticReflector} {
|
||||||
{extractor: Extractor, staticReflector: StaticReflector} {
|
|
||||||
const htmlParser = new I18NHtmlParser(new HtmlParser());
|
const htmlParser = new I18NHtmlParser(new HtmlParser());
|
||||||
|
|
||||||
const urlResolver = createOfflineCompileUrlResolver();
|
const urlResolver = createOfflineCompileUrlResolver();
|
||||||
const staticReflector = new StaticReflector(host);
|
const symbolCache = new StaticSymbolCache();
|
||||||
|
const summaryResolver = new AotSummaryResolver(host, symbolCache);
|
||||||
|
const staticSymbolResolver = new StaticSymbolResolver(host, symbolCache, summaryResolver);
|
||||||
|
const staticReflector = new StaticReflector(staticSymbolResolver);
|
||||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||||
|
|
||||||
const config = new CompilerConfig({
|
const config = new CompilerConfig({
|
||||||
|
@ -111,13 +109,13 @@ export class Extractor {
|
||||||
const elementSchemaRegistry = new DomElementSchemaRegistry();
|
const elementSchemaRegistry = new DomElementSchemaRegistry();
|
||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||||
new PipeResolver(staticReflector), new AotSummaryResolver(host, staticReflector, options),
|
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||||
elementSchemaRegistry, normalizer, staticReflector);
|
staticReflector);
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
// TODO(vicb): implicit tags & attributes
|
||||||
const messageBundle = new MessageBundle(htmlParser, [], {});
|
const messageBundle = new MessageBundle(htmlParser, [], {});
|
||||||
|
|
||||||
const extractor = new Extractor(options, host, staticReflector, messageBundle, resolver);
|
const extractor = new Extractor(host, staticSymbolResolver, messageBundle, resolver);
|
||||||
return {extractor, staticReflector};
|
return {extractor, staticReflector};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {Extractor, ExtractorHost, ExtractorOptions} from './extractor';
|
export {Extractor, ExtractorHost} from './extractor';
|
||||||
export {I18NHtmlParser} from './i18n_html_parser';
|
export {I18NHtmlParser} from './i18n_html_parser';
|
||||||
export {MessageBundle} from './message_bundle';
|
export {MessageBundle} from './message_bundle';
|
||||||
export {Serializer} from './serializers/serializer';
|
export {Serializer} from './serializers/serializer';
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
|
import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
|
||||||
|
|
||||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||||
import {AnimationParser} from '../animation/animation_parser';
|
import {AnimationParser} from '../animation/animation_parser';
|
||||||
|
@ -15,6 +15,7 @@ import {CompilerConfig} from '../config';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||||
import {stringify} from '../facade/lang';
|
import {stringify} from '../facade/lang';
|
||||||
|
import {CompilerInjectable} from '../injectable';
|
||||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||||
import * as ir from '../output/output_ast';
|
import * as ir from '../output/output_ast';
|
||||||
|
@ -36,7 +37,7 @@ import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDepende
|
||||||
* from a trusted source. Attacker-controlled data introduced by a template could expose your
|
* from a trusted source. Attacker-controlled data introduced by a template could expose your
|
||||||
* application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security).
|
* application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security).
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class JitCompiler implements Compiler {
|
export class JitCompiler implements Compiler {
|
||||||
private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||||
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||||
|
|
||||||
import {AnimationParser} from '../animation/animation_parser';
|
import {AnimationParser} from '../animation/animation_parser';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
|
@ -16,6 +16,7 @@ import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||||
import {Lexer} from '../expression_parser/lexer';
|
import {Lexer} from '../expression_parser/lexer';
|
||||||
import {Parser} from '../expression_parser/parser';
|
import {Parser} from '../expression_parser/parser';
|
||||||
import * as i18n from '../i18n/index';
|
import * as i18n from '../i18n/index';
|
||||||
|
import {CompilerInjectable} from '../injectable';
|
||||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||||
import {HtmlParser} from '../ml_parser/html_parser';
|
import {HtmlParser} from '../ml_parser/html_parser';
|
||||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||||
|
@ -83,7 +84,7 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class JitCompilerFactory implements CompilerFactory {
|
export class JitCompilerFactory implements CompilerFactory {
|
||||||
private _defaultOptions: CompilerOptions[];
|
private _defaultOptions: CompilerOptions[];
|
||||||
constructor(@Inject(COMPILER_OPTIONS) defaultOptions: CompilerOptions[]) {
|
constructor(@Inject(COMPILER_OPTIONS) defaultOptions: CompilerOptions[]) {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {DirectiveResolver} from './directive_resolver';
|
||||||
import {ListWrapper, StringMapWrapper} from './facade/collection';
|
import {ListWrapper, StringMapWrapper} from './facade/collection';
|
||||||
import {isBlank, isPresent, stringify} from './facade/lang';
|
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||||
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
|
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
import {hasLifecycleHook} from './lifecycle_reflector';
|
import {hasLifecycleHook} from './lifecycle_reflector';
|
||||||
import {NgModuleResolver} from './ng_module_resolver';
|
import {NgModuleResolver} from './ng_module_resolver';
|
||||||
import {PipeResolver} from './pipe_resolver';
|
import {PipeResolver} from './pipe_resolver';
|
||||||
|
@ -35,7 +36,7 @@ export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
|
||||||
// But we want to report errors even when the async work is
|
// But we want to report errors even when the async work is
|
||||||
// not required to check that the user would have been able
|
// not required to check that the user would have been able
|
||||||
// to wait correctly.
|
// to wait correctly.
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class CompileMetadataResolver {
|
export class CompileMetadataResolver {
|
||||||
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
|
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
|
||||||
private _summaryCache = new Map<Type<any>, cpl.CompileTypeSummary>();
|
private _summaryCache = new Map<Type<any>, cpl.CompileTypeSummary>();
|
||||||
|
@ -45,7 +46,7 @@ export class CompileMetadataResolver {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
|
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
|
||||||
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver,
|
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver<any>,
|
||||||
private _schemaRegistry: ElementSchemaRegistry,
|
private _schemaRegistry: ElementSchemaRegistry,
|
||||||
private _directiveNormalizer: DirectiveNormalizer,
|
private _directiveNormalizer: DirectiveNormalizer,
|
||||||
private _reflector: ReflectorReader = reflector,
|
private _reflector: ReflectorReader = reflector,
|
||||||
|
@ -128,12 +129,13 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadSummary(type: any, kind: cpl.CompileSummaryKind): cpl.CompileTypeSummary {
|
private _loadSummary(type: any, kind: cpl.CompileSummaryKind): cpl.CompileTypeSummary {
|
||||||
let summary = this._summaryCache.get(type);
|
let typeSummary = this._summaryCache.get(type);
|
||||||
if (!summary) {
|
if (!typeSummary) {
|
||||||
summary = this._summaryResolver.resolveSummary(type);
|
const summary = this._summaryResolver.resolveSummary(type);
|
||||||
this._summaryCache.set(type, summary);
|
typeSummary = summary ? summary.type : null;
|
||||||
|
this._summaryCache.set(type, typeSummary);
|
||||||
}
|
}
|
||||||
return summary && summary.summaryKind === kind ? summary : null;
|
return typeSummary && typeSummary.summaryKind === kind ? typeSummary : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> {
|
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> {
|
||||||
|
@ -237,7 +239,7 @@ export class CompileMetadataResolver {
|
||||||
if (dirMeta.viewProviders) {
|
if (dirMeta.viewProviders) {
|
||||||
viewProviders = this._getProvidersMetadata(
|
viewProviders = this._getProvidersMetadata(
|
||||||
dirMeta.viewProviders, entryComponentMetadata,
|
dirMeta.viewProviders, entryComponentMetadata,
|
||||||
`viewProviders for "${stringify(directiveType)}"`, [], directiveType);
|
`viewProviders for "${stringifyType(directiveType)}"`, [], directiveType);
|
||||||
}
|
}
|
||||||
if (dirMeta.entryComponents) {
|
if (dirMeta.entryComponents) {
|
||||||
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
|
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
|
||||||
|
@ -251,7 +253,7 @@ export class CompileMetadataResolver {
|
||||||
// Directive
|
// Directive
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`),
|
new Error(`Directive ${stringifyType(directiveType)} has no selector, please add it!`),
|
||||||
directiveType);
|
directiveType);
|
||||||
selector = 'error';
|
selector = 'error';
|
||||||
}
|
}
|
||||||
|
@ -260,8 +262,8 @@ export class CompileMetadataResolver {
|
||||||
let providers: cpl.CompileProviderMetadata[] = [];
|
let providers: cpl.CompileProviderMetadata[] = [];
|
||||||
if (isPresent(dirMeta.providers)) {
|
if (isPresent(dirMeta.providers)) {
|
||||||
providers = this._getProvidersMetadata(
|
providers = this._getProvidersMetadata(
|
||||||
dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`,
|
dirMeta.providers, entryComponentMetadata,
|
||||||
[], directiveType);
|
`providers for "${stringifyType(directiveType)}"`, [], directiveType);
|
||||||
}
|
}
|
||||||
let queries: cpl.CompileQueryMetadata[] = [];
|
let queries: cpl.CompileQueryMetadata[] = [];
|
||||||
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
||||||
|
@ -298,7 +300,7 @@ export class CompileMetadataResolver {
|
||||||
if (!dirMeta) {
|
if (!dirMeta) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringify(directiveType)}.`),
|
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`),
|
||||||
directiveType);
|
directiveType);
|
||||||
}
|
}
|
||||||
return dirMeta;
|
return dirMeta;
|
||||||
|
@ -310,7 +312,7 @@ export class CompileMetadataResolver {
|
||||||
if (!dirSummary) {
|
if (!dirSummary) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Illegal state: Could not load the summary for directive ${stringify(dirType)}.`),
|
`Illegal state: Could not load the summary for directive ${stringifyType(dirType)}.`),
|
||||||
dirType);
|
dirType);
|
||||||
}
|
}
|
||||||
return dirSummary;
|
return dirSummary;
|
||||||
|
@ -383,7 +385,8 @@ export class CompileMetadataResolver {
|
||||||
if (moduleWithProviders.providers) {
|
if (moduleWithProviders.providers) {
|
||||||
providers.push(...this._getProvidersMetadata(
|
providers.push(...this._getProvidersMetadata(
|
||||||
moduleWithProviders.providers, entryComponents,
|
moduleWithProviders.providers, entryComponents,
|
||||||
`provider for the NgModule '${stringify(importedModuleType)}'`, [], importedType));
|
`provider for the NgModule '${stringifyType(importedModuleType)}'`, [],
|
||||||
|
importedType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,7 +395,7 @@ export class CompileMetadataResolver {
|
||||||
if (!importedModuleSummary) {
|
if (!importedModuleSummary) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`),
|
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
|
||||||
moduleType);
|
moduleType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -400,7 +403,7 @@ export class CompileMetadataResolver {
|
||||||
} else {
|
} else {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`),
|
`Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
|
||||||
moduleType);
|
moduleType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -412,7 +415,7 @@ export class CompileMetadataResolver {
|
||||||
if (!isValidType(exportedType)) {
|
if (!isValidType(exportedType)) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`),
|
`Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`),
|
||||||
moduleType);
|
moduleType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -433,7 +436,7 @@ export class CompileMetadataResolver {
|
||||||
if (!isValidType(declaredType)) {
|
if (!isValidType(declaredType)) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`),
|
`Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
|
||||||
moduleType);
|
moduleType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -450,7 +453,7 @@ export class CompileMetadataResolver {
|
||||||
} else {
|
} else {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`),
|
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
|
||||||
moduleType);
|
moduleType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -469,7 +472,7 @@ export class CompileMetadataResolver {
|
||||||
} else {
|
} else {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringify(exportedId.reference)} from ${stringify(moduleType)} as it was neither declared nor imported!`),
|
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`),
|
||||||
moduleType);
|
moduleType);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -478,13 +481,13 @@ export class CompileMetadataResolver {
|
||||||
// so that they overwrite any other provider we already added.
|
// so that they overwrite any other provider we already added.
|
||||||
if (meta.providers) {
|
if (meta.providers) {
|
||||||
providers.push(...this._getProvidersMetadata(
|
providers.push(...this._getProvidersMetadata(
|
||||||
meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`,
|
meta.providers, entryComponents,
|
||||||
[], moduleType));
|
`provider for the NgModule '${stringifyType(moduleType)}'`, [], moduleType));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.entryComponents) {
|
if (meta.entryComponents) {
|
||||||
entryComponents.push(
|
entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
|
||||||
...flattenAndDedupeArray(meta.entryComponents).map(type => this._getTypeMetadata(type)));
|
.map(type => this._getIdentifierMetadata(type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.bootstrap) {
|
if (meta.bootstrap) {
|
||||||
|
@ -492,11 +495,11 @@ export class CompileMetadataResolver {
|
||||||
if (!isValidType(type)) {
|
if (!isValidType(type)) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`),
|
`Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`),
|
||||||
moduleType);
|
moduleType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bootstrapComponents.push(this._getTypeMetadata(type));
|
bootstrapComponents.push(this._getIdentifierMetadata(type));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,9 +558,9 @@ export class CompileMetadataResolver {
|
||||||
if (oldModule && oldModule !== moduleType) {
|
if (oldModule && oldModule !== moduleType) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Type ${stringify(type)} is part of the declarations of 2 modules: ${stringify(oldModule)} and ${stringify(moduleType)}! ` +
|
`Type ${stringifyType(type)} is part of the declarations of 2 modules: ${stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` +
|
||||||
`Please consider moving ${stringify(type)} to a higher module that imports ${stringify(oldModule)} and ${stringify(moduleType)}. ` +
|
`Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` +
|
||||||
`You can also create a new NgModule that exports and includes ${stringify(type)} then import that NgModule in ${stringify(oldModule)} and ${stringify(moduleType)}.`),
|
`You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`),
|
||||||
moduleType);
|
moduleType);
|
||||||
}
|
}
|
||||||
this._ngModuleOfTypes.set(type, moduleType);
|
this._ngModuleOfTypes.set(type, moduleType);
|
||||||
|
@ -606,6 +609,26 @@ export class CompileMetadataResolver {
|
||||||
return {reference: type};
|
return {reference: type};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isInjectable(type: any): boolean {
|
||||||
|
const annotations = this._reflector.annotations(type);
|
||||||
|
// Note: We need an exact check here as @Component / @Directive / ... inherit
|
||||||
|
// from @CompilerInjectable!
|
||||||
|
return annotations.some(ann => ann.constructor === Injectable);
|
||||||
|
}
|
||||||
|
|
||||||
|
getInjectableSummary(type: any): cpl.CompileTypeSummary {
|
||||||
|
return {summaryKind: cpl.CompileSummaryKind.Injectable, type: this._getTypeMetadata(type)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getInjectableMetadata(type: Type<any>, dependencies: any[] = null):
|
||||||
|
cpl.CompileTypeMetadata {
|
||||||
|
const typeSummary = this._loadSummary(type, cpl.CompileSummaryKind.Injectable);
|
||||||
|
if (typeSummary) {
|
||||||
|
return typeSummary.type;
|
||||||
|
}
|
||||||
|
return this._getTypeMetadata(type, dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
private _getTypeMetadata(type: Type<any>, dependencies: any[] = null): cpl.CompileTypeMetadata {
|
private _getTypeMetadata(type: Type<any>, dependencies: any[] = null): cpl.CompileTypeMetadata {
|
||||||
const identifier = this._getIdentifierMetadata(type);
|
const identifier = this._getIdentifierMetadata(type);
|
||||||
return {
|
return {
|
||||||
|
@ -631,7 +654,7 @@ export class CompileMetadataResolver {
|
||||||
if (!pipeMeta) {
|
if (!pipeMeta) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`),
|
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`),
|
||||||
pipeType);
|
pipeType);
|
||||||
}
|
}
|
||||||
return pipeMeta;
|
return pipeMeta;
|
||||||
|
@ -642,7 +665,8 @@ export class CompileMetadataResolver {
|
||||||
<cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe);
|
<cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe);
|
||||||
if (!pipeSummary) {
|
if (!pipeSummary) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(`Illegal state: Could not load the summary for pipe ${stringify(pipeType)}.`),
|
new Error(
|
||||||
|
`Illegal state: Could not load the summary for pipe ${stringifyType(pipeType)}.`),
|
||||||
pipeType);
|
pipeType);
|
||||||
}
|
}
|
||||||
return pipeSummary;
|
return pipeSummary;
|
||||||
|
@ -722,9 +746,10 @@ export class CompileMetadataResolver {
|
||||||
|
|
||||||
if (hasUnknownDeps) {
|
if (hasUnknownDeps) {
|
||||||
const depsTokens =
|
const depsTokens =
|
||||||
dependenciesMetadata.map((dep) => dep ? stringify(dep.token) : '?').join(', ');
|
dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', ');
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`),
|
new Error(
|
||||||
|
`Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`),
|
||||||
typeOrFunc);
|
typeOrFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -761,9 +786,9 @@ export class CompileMetadataResolver {
|
||||||
(<string[]>providers.reduce(
|
(<string[]>providers.reduce(
|
||||||
(soFar: string[], seenProvider: any, seenProviderIdx: number) => {
|
(soFar: string[], seenProvider: any, seenProviderIdx: number) => {
|
||||||
if (seenProviderIdx < providerIdx) {
|
if (seenProviderIdx < providerIdx) {
|
||||||
soFar.push(`${stringify(seenProvider)}`);
|
soFar.push(`${stringifyType(seenProvider)}`);
|
||||||
} else if (seenProviderIdx == providerIdx) {
|
} else if (seenProviderIdx == providerIdx) {
|
||||||
soFar.push(`?${stringify(seenProvider)}?`);
|
soFar.push(`?${stringifyType(seenProvider)}?`);
|
||||||
} else if (seenProviderIdx == providerIdx + 1) {
|
} else if (seenProviderIdx == providerIdx + 1) {
|
||||||
soFar.push('...');
|
soFar.push('...');
|
||||||
}
|
}
|
||||||
|
@ -805,7 +830,8 @@ export class CompileMetadataResolver {
|
||||||
|
|
||||||
extractIdentifiers(provider.useValue, collectedIdentifiers);
|
extractIdentifiers(provider.useValue, collectedIdentifiers);
|
||||||
collectedIdentifiers.forEach((identifier) => {
|
collectedIdentifiers.forEach((identifier) => {
|
||||||
if (this._directiveResolver.isDirective(identifier.reference)) {
|
if (this._directiveResolver.isDirective(identifier.reference) ||
|
||||||
|
this._loadSummary(identifier.reference, cpl.CompileSummaryKind.Directive)) {
|
||||||
components.push(identifier);
|
components.push(identifier);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -819,7 +845,7 @@ export class CompileMetadataResolver {
|
||||||
let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token);
|
let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token);
|
||||||
|
|
||||||
if (provider.useClass) {
|
if (provider.useClass) {
|
||||||
compileTypeMetadata = this._getTypeMetadata(provider.useClass, provider.dependencies);
|
compileTypeMetadata = this._getInjectableMetadata(provider.useClass, provider.dependencies);
|
||||||
compileDeps = compileTypeMetadata.diDeps;
|
compileDeps = compileTypeMetadata.diDeps;
|
||||||
if (provider.token === provider.useClass) {
|
if (provider.token === provider.useClass) {
|
||||||
// use the compileTypeMetadata as it contains information about lifecycleHooks...
|
// use the compileTypeMetadata as it contains information about lifecycleHooks...
|
||||||
|
@ -868,7 +894,7 @@ export class CompileMetadataResolver {
|
||||||
if (!q.selector) {
|
if (!q.selector) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new Error(
|
new Error(
|
||||||
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`),
|
`Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`),
|
||||||
typeOrFunc);
|
typeOrFunc);
|
||||||
}
|
}
|
||||||
selectors = [this._getTokenMetadata(q.selector)];
|
selectors = [this._getTokenMetadata(q.selector)];
|
||||||
|
@ -936,7 +962,7 @@ export function componentModuleUrl(
|
||||||
return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`;
|
return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`;
|
||||||
} else if (moduleId !== null && moduleId !== void 0) {
|
} else if (moduleId !== null && moduleId !== void 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`moduleId should be a string in "${stringify(type)}". See https://goo.gl/wIDDiL for more information.\n` +
|
`moduleId should be a string in "${stringifyType(type)}". See https://goo.gl/wIDDiL for more information.\n` +
|
||||||
`If you're using Webpack you should inline the template and the styles, see https://goo.gl/X2J8zc.`);
|
`If you're using Webpack you should inline the template and the styles, see https://goo.gl/X2J8zc.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -952,3 +978,11 @@ class _CompileValueConverter extends ValueTransformer {
|
||||||
targetIdentifiers.push({reference: value});
|
targetIdentifiers.push({reference: value});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stringifyType(type: any): string {
|
||||||
|
if (type instanceof StaticSymbol) {
|
||||||
|
return `${type.name} in ${type.filePath}`;
|
||||||
|
} else {
|
||||||
|
return stringify(type);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {CompilerInjectable} from '../injectable';
|
||||||
|
|
||||||
import {getHtmlTagDefinition} from './html_tags';
|
import {getHtmlTagDefinition} from './html_tags';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './interpolation_config';
|
||||||
|
@ -14,7 +14,7 @@ import {ParseTreeResult, Parser} from './parser';
|
||||||
|
|
||||||
export {ParseTreeResult, TreeError} from './parser';
|
export {ParseTreeResult, TreeError} from './parser';
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class HtmlParser extends Parser {
|
export class HtmlParser extends Parser {
|
||||||
constructor() { super(getHtmlTagDefinition); }
|
constructor() { super(getHtmlTagDefinition); }
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
|
||||||
|
|
||||||
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
|
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
|
||||||
import {createDiTokenExpression} from './compiler_util/identifier_util';
|
import {createDiTokenExpression} from './compiler_util/identifier_util';
|
||||||
import {isPresent} from './facade/lang';
|
import {isPresent} from './facade/lang';
|
||||||
import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from './identifiers';
|
import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from './identifiers';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
import {convertValueToOutputAst} from './output/value_util';
|
import {convertValueToOutputAst} from './output/value_util';
|
||||||
|
@ -31,7 +30,7 @@ export class NgModuleCompileResult {
|
||||||
public dependencies: ComponentFactoryDependency[]) {}
|
public dependencies: ComponentFactoryDependency[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class NgModuleCompiler {
|
export class NgModuleCompiler {
|
||||||
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
||||||
NgModuleCompileResult {
|
NgModuleCompileResult {
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable, NgModule, Type} from '@angular/core';
|
import {NgModule, Type} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper} from './facade/collection';
|
import {ListWrapper} from './facade/collection';
|
||||||
import {isPresent, stringify} from './facade/lang';
|
import {isPresent, stringify} from './facade/lang';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
import {ReflectorReader, reflector} from './private_import_core';
|
import {ReflectorReader, reflector} from './private_import_core';
|
||||||
|
|
||||||
function _isNgModuleMetadata(obj: any): obj is NgModule {
|
function _isNgModuleMetadata(obj: any): obj is NgModule {
|
||||||
|
@ -19,7 +20,7 @@ function _isNgModuleMetadata(obj: any): obj is NgModule {
|
||||||
/**
|
/**
|
||||||
* Resolves types to {@link NgModule}.
|
* Resolves types to {@link NgModule}.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class NgModuleResolver {
|
export class NgModuleResolver {
|
||||||
constructor(private _reflector: ReflectorReader = reflector) {}
|
constructor(private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
|
|
|
@ -14,5 +14,6 @@ export abstract class ImportResolver {
|
||||||
* Converts a file path to a module name that can be used as an `import.
|
* Converts a file path to a module name that can be used as an `import.
|
||||||
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
|
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
|
||||||
*/
|
*/
|
||||||
abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string;
|
abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
|
||||||
|
/*|null*/;
|
||||||
}
|
}
|
||||||
|
|
|
@ -335,7 +335,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
}
|
}
|
||||||
ctx.print(`${prefix}.`);
|
ctx.print(`${prefix}.`);
|
||||||
}
|
}
|
||||||
if (value.reference && value.reference.members) {
|
if (value.reference && value.reference.members && value.reference.members.length) {
|
||||||
ctx.print(value.reference.name);
|
ctx.print(value.reference.name);
|
||||||
ctx.print('.');
|
ctx.print('.');
|
||||||
ctx.print(value.reference.members.join('.'));
|
ctx.print(value.reference.members.join('.'));
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable, Pipe, Type, resolveForwardRef} from '@angular/core';
|
import {Pipe, Type, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper} from './facade/collection';
|
import {ListWrapper} from './facade/collection';
|
||||||
import {isPresent, stringify} from './facade/lang';
|
import {isPresent, stringify} from './facade/lang';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
import {ReflectorReader, reflector} from './private_import_core';
|
import {ReflectorReader, reflector} from './private_import_core';
|
||||||
|
|
||||||
function _isPipeMetadata(type: any): boolean {
|
function _isPipeMetadata(type: any): boolean {
|
||||||
|
@ -23,7 +24,7 @@ function _isPipeMetadata(type: any): boolean {
|
||||||
*
|
*
|
||||||
* See {@link Compiler}
|
* See {@link Compiler}
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class PipeResolver {
|
export class PipeResolver {
|
||||||
constructor(private _reflector: ReflectorReader = reflector) {}
|
constructor(private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AUTO_STYLE, CUSTOM_ELEMENTS_SCHEMA, Injectable, NO_ERRORS_SCHEMA, SchemaMetadata, SecurityContext} from '@angular/core';
|
import {AUTO_STYLE, CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata, SecurityContext} from '@angular/core';
|
||||||
|
import {CompilerInjectable} from '../injectable';
|
||||||
|
|
||||||
import {dashCaseToCamelCase} from '../util';
|
import {dashCaseToCamelCase} from '../util';
|
||||||
|
|
||||||
|
@ -238,7 +239,7 @@ const _ATTR_TO_PROP: {[name: string]: string} = {
|
||||||
'tabindex': 'tabIndex',
|
'tabindex': 'tabIndex',
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class DomElementSchemaRegistry extends ElementSchemaRegistry {
|
export class DomElementSchemaRegistry extends ElementSchemaRegistry {
|
||||||
private _schema: {[element: string]: {[property: string]: string}} = {};
|
private _schema: {[element: string]: {[property: string]: string}} = {};
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable, ViewEncapsulation} from '@angular/core';
|
import {ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileStylesheetMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileStylesheetMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
import {ShadowCss} from './shadow_css';
|
import {ShadowCss} from './shadow_css';
|
||||||
import {UrlResolver} from './url_resolver';
|
import {UrlResolver} from './url_resolver';
|
||||||
|
@ -36,7 +37,7 @@ export class CompiledStylesheet {
|
||||||
public meta: CompileStylesheetMetadata) {}
|
public meta: CompileStylesheetMetadata) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class StyleCompiler {
|
export class StyleCompiler {
|
||||||
private _shadowCss: ShadowCss = new ShadowCss();
|
private _shadowCss: ShadowCss = new ShadowCss();
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,17 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {Injectable} from '@angular/core';
|
|
||||||
import {CompileTypeSummary} from './compile_metadata';
|
import {CompileTypeSummary} from './compile_metadata';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
|
|
||||||
@Injectable()
|
export interface Summary<T> {
|
||||||
export class SummaryResolver {
|
symbol: T;
|
||||||
resolveSummary(reference: any): CompileTypeSummary { return null; }
|
metadata: any;
|
||||||
|
type?: CompileTypeSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
@CompilerInjectable()
|
||||||
|
export class SummaryResolver<T> {
|
||||||
|
resolveSummary(reference: T): Summary<T> { return null; };
|
||||||
|
getSymbolsOf(filePath: string): T[] { return []; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata} from '@angular/core';
|
import {Inject, OpaqueToken, Optional, SchemaMetadata} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
|
||||||
import {Parser} from '../expression_parser/parser';
|
import {Parser} from '../expression_parser/parser';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
|
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
|
||||||
import {Identifiers, createIdentifierToken, identifierToken} from '../identifiers';
|
import {Identifiers, createIdentifierToken, identifierToken} from '../identifiers';
|
||||||
|
import {CompilerInjectable} from '../injectable';
|
||||||
import * as html from '../ml_parser/ast';
|
import * as html from '../ml_parser/ast';
|
||||||
import {ParseTreeResult} from '../ml_parser/html_parser';
|
import {ParseTreeResult} from '../ml_parser/html_parser';
|
||||||
import {expandNodes} from '../ml_parser/icu_ast_expander';
|
import {expandNodes} from '../ml_parser/icu_ast_expander';
|
||||||
|
@ -79,7 +79,7 @@ export class TemplateParseResult {
|
||||||
constructor(public templateAst?: TemplateAst[], public errors?: ParseError[]) {}
|
constructor(public templateAst?: TemplateAst[], public errors?: ParseError[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class TemplateParser {
|
export class TemplateParser {
|
||||||
constructor(
|
constructor(
|
||||||
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
||||||
|
|
|
@ -6,9 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, Injectable, PACKAGE_ROOT_URL} from '@angular/core';
|
import {Inject, PACKAGE_ROOT_URL} from '@angular/core';
|
||||||
|
|
||||||
import {isBlank, isPresent} from './facade/lang';
|
import {isBlank, isPresent} from './facade/lang';
|
||||||
|
import {CompilerInjectable} from './injectable';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link UrlResolver} with no package prefix.
|
* Create a {@link UrlResolver} with no package prefix.
|
||||||
|
@ -45,7 +47,7 @@ export var DEFAULT_PACKAGE_URL_PROVIDER = {
|
||||||
* Attacker-controlled data introduced by a template could expose your
|
* Attacker-controlled data introduced by a template could expose your
|
||||||
* application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security).
|
* application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security).
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class UrlResolver {
|
export class UrlResolver {
|
||||||
constructor(@Inject(PACKAGE_ROOT_URL) private _packagePrefix: string = null) {}
|
constructor(@Inject(PACKAGE_ROOT_URL) private _packagePrefix: string = null) {}
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,10 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
|
||||||
|
|
||||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||||
import {CompileDirectiveMetadata, CompilePipeSummary} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompilePipeSummary} from '../compile_metadata';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
|
import {CompilerInjectable} from '../injectable';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
import {TemplateAst} from '../template_parser/template_ast';
|
import {TemplateAst} from '../template_parser/template_ast';
|
||||||
|
@ -30,7 +29,7 @@ export class ViewCompileResult {
|
||||||
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
|
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@CompilerInjectable()
|
||||||
export class ViewCompiler {
|
export class ViewCompiler {
|
||||||
constructor(private _genConfig: CompilerConfig, private _schemaRegistry: ElementSchemaRegistry) {}
|
constructor(private _genConfig: CompilerConfig, private _schemaRegistry: ElementSchemaRegistry) {}
|
||||||
|
|
||||||
|
|
|
@ -6,26 +6,25 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler';
|
import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler';
|
||||||
import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||||
import {MetadataCollector} from '@angular/tsc-wrapped';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
|
import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';
|
||||||
|
|
||||||
// This matches .ts files but not .d.ts files.
|
|
||||||
const TS_EXT = /(^.|(?!\.d)..)\.ts$/;
|
|
||||||
|
|
||||||
describe('StaticReflector', () => {
|
describe('StaticReflector', () => {
|
||||||
const noContext = new StaticSymbol('', '');
|
let noContext: StaticSymbol;
|
||||||
let host: StaticReflectorHost;
|
let host: StaticSymbolResolverHost;
|
||||||
|
let symbolResolver: StaticSymbolResolver;
|
||||||
let reflector: StaticReflector;
|
let reflector: StaticReflector;
|
||||||
|
|
||||||
function init(
|
function init(
|
||||||
testData: {[key: string]: any} = DEFAULT_TEST_DATA,
|
testData: {[key: string]: any} = DEFAULT_TEST_DATA,
|
||||||
decorators: {name: string, filePath: string, ctor: any}[] = []) {
|
decorators: {name: string, filePath: string, ctor: any}[] = []) {
|
||||||
host = new MockStaticReflectorHost(testData);
|
const symbolCache = new StaticSymbolCache();
|
||||||
reflector = new StaticReflector(host, undefined, decorators);
|
host = new MockStaticSymbolResolverHost(testData);
|
||||||
|
symbolResolver = new StaticSymbolResolver(host, symbolCache, new MockSummaryResolver([]));
|
||||||
|
reflector = new StaticReflector(symbolResolver, decorators);
|
||||||
|
noContext = reflector.getStaticSymbol('', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => init());
|
beforeEach(() => init());
|
||||||
|
@ -77,24 +76,22 @@ describe('StaticReflector', () => {
|
||||||
])]);
|
])]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an exception for unsupported metadata versions', () => {
|
|
||||||
expect(() => reflector.findDeclaration('src/version-error', 'e'))
|
|
||||||
.toThrow(new Error(
|
|
||||||
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 3'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an exception for version 2 metadata', () => {
|
|
||||||
expect(() => reflector.findDeclaration('src/version-2-error', 'e'))
|
|
||||||
.toThrowError(
|
|
||||||
'Unsupported metadata version 2 for module /tmp/src/version-2-error.d.ts. This module should be compiled with a newer version of ngc');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get and empty annotation list for an unknown class', () => {
|
it('should get and empty annotation list for an unknown class', () => {
|
||||||
const UnknownClass = reflector.findDeclaration('src/app/app.component', 'UnknownClass');
|
const UnknownClass = reflector.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||||
const annotations = reflector.annotations(UnknownClass);
|
const annotations = reflector.annotations(UnknownClass);
|
||||||
expect(annotations).toEqual([]);
|
expect(annotations).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should get and empty annotation list for a symbol with null value', () => {
|
||||||
|
init({
|
||||||
|
'/tmp/test.ts': `
|
||||||
|
export var x = null;
|
||||||
|
`
|
||||||
|
});
|
||||||
|
const annotations = reflector.annotations(reflector.getStaticSymbol('/tmp/test.ts', 'x'));
|
||||||
|
expect(annotations).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get propMetadata for HeroDetailComponent', () => {
|
it('should get propMetadata for HeroDetailComponent', () => {
|
||||||
const HeroDetailComponent =
|
const HeroDetailComponent =
|
||||||
reflector.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
reflector.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
||||||
|
@ -129,7 +126,7 @@ describe('StaticReflector', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should simplify a static symbol into itself', () => {
|
it('should simplify a static symbol into itself', () => {
|
||||||
const staticSymbol = new StaticSymbol('', '');
|
const staticSymbol = reflector.getStaticSymbol('', '');
|
||||||
expect(simplify(noContext, staticSymbol)).toBe(staticSymbol);
|
expect(simplify(noContext, staticSymbol)).toBe(staticSymbol);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -306,49 +303,43 @@ describe('StaticReflector', () => {
|
||||||
expect(simplify(noContext, expr)).toBe(2);
|
expect(simplify(noContext, expr)).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should simplify a module reference', () => {
|
it('should simplify a file reference', () => {
|
||||||
expect(simplify(
|
expect(simplify(
|
||||||
new StaticSymbol('/src/cases', ''),
|
reflector.getStaticSymbol('/src/cases', ''),
|
||||||
({__symbolic: 'reference', module: './extern', name: 's'})))
|
reflector.getStaticSymbol('/src/extern.d.ts', 's')))
|
||||||
.toEqual('s');
|
.toEqual('s');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not simplify a module reference without a name', () => {
|
|
||||||
const staticSymbol = new StaticSymbol('/src/cases', '');
|
|
||||||
expect(simplify(staticSymbol, ({__symbolic: 'reference', module: './extern', name: ''})))
|
|
||||||
.toEqual(staticSymbol);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should simplify a non existing reference as a static symbol', () => {
|
it('should simplify a non existing reference as a static symbol', () => {
|
||||||
expect(simplify(
|
expect(simplify(
|
||||||
new StaticSymbol('/src/cases', ''),
|
reflector.getStaticSymbol('/src/cases', ''),
|
||||||
({__symbolic: 'reference', module: './extern', name: 'nonExisting'})))
|
reflector.getStaticSymbol('/src/extern.d.ts', 'nonExisting')))
|
||||||
.toEqual(reflector.getStaticSymbol('/src/extern.d.ts', 'nonExisting'));
|
.toEqual(reflector.getStaticSymbol('/src/extern.d.ts', 'nonExisting'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should simplify a function reference as a static symbol', () => {
|
it('should simplify a function reference as a static symbol', () => {
|
||||||
expect(simplify(
|
expect(simplify(
|
||||||
new StaticSymbol('/src/cases', 'myFunction'),
|
reflector.getStaticSymbol('/src/cases', 'myFunction'),
|
||||||
({__symbolic: 'function', parameters: ['a'], value: []})))
|
({__symbolic: 'function', parameters: ['a'], value: []})))
|
||||||
.toEqual(reflector.getStaticSymbol('/src/cases', 'myFunction'));
|
.toEqual(reflector.getStaticSymbol('/src/cases', 'myFunction'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should simplify values initialized with a function call', () => {
|
it('should simplify values initialized with a function call', () => {
|
||||||
expect(simplify(new StaticSymbol('/tmp/src/function-reference.ts', ''), {
|
expect(simplify(
|
||||||
__symbolic: 'reference',
|
reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
|
||||||
name: 'one'
|
reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'one')))
|
||||||
})).toEqual(['some-value']);
|
.toEqual(['some-value']);
|
||||||
expect(simplify(new StaticSymbol('/tmp/src/function-reference.ts', ''), {
|
expect(simplify(
|
||||||
__symbolic: 'reference',
|
reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
|
||||||
name: 'three'
|
reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'three')))
|
||||||
})).toEqual(3);
|
.toEqual(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error on direct recursive calls', () => {
|
it('should error on direct recursive calls', () => {
|
||||||
expect(
|
expect(
|
||||||
() => simplify(
|
() => simplify(
|
||||||
new StaticSymbol('/tmp/src/function-reference.ts', ''),
|
reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
|
||||||
{__symbolic: 'reference', name: 'recursion'}))
|
reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'recursion')))
|
||||||
.toThrow(new Error(
|
.toThrow(new Error(
|
||||||
'Recursion not supported, resolving symbol recursive in /tmp/src/function-recursive.d.ts, resolving symbol recursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
|
'Recursion not supported, resolving symbol recursive in /tmp/src/function-recursive.d.ts, resolving symbol recursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
|
||||||
});
|
});
|
||||||
|
@ -362,7 +353,8 @@ describe('StaticReflector', () => {
|
||||||
expect(moduleMetadata).toBeDefined();
|
expect(moduleMetadata).toBeDefined();
|
||||||
const classData: any = moduleMetadata['InvalidMetadata'];
|
const classData: any = moduleMetadata['InvalidMetadata'];
|
||||||
expect(classData).toBeDefined();
|
expect(classData).toBeDefined();
|
||||||
simplify(new StaticSymbol('/tmp/src/invalid-metadata.ts', ''), classData.decorators[0]);
|
simplify(
|
||||||
|
reflector.getStaticSymbol('/tmp/src/invalid-metadata.ts', ''), classData.decorators[0]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e.fileName).toBe('/tmp/src/invalid-metadata.ts');
|
expect(e.fileName).toBe('/tmp/src/invalid-metadata.ts');
|
||||||
threw = true;
|
threw = true;
|
||||||
|
@ -373,48 +365,17 @@ describe('StaticReflector', () => {
|
||||||
it('should error on indirect recursive calls', () => {
|
it('should error on indirect recursive calls', () => {
|
||||||
expect(
|
expect(
|
||||||
() => simplify(
|
() => simplify(
|
||||||
new StaticSymbol('/tmp/src/function-reference.ts', ''),
|
reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
|
||||||
{__symbolic: 'reference', name: 'indirectRecursion'}))
|
reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'indirectRecursion')))
|
||||||
.toThrow(new Error(
|
.toThrow(new Error(
|
||||||
'Recursion not supported, resolving symbol indirectRecursion2 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion1 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
|
'Recursion not supported, resolving symbol indirectRecursion2 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion1 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should simplify a spread expression', () => {
|
it('should simplify a spread expression', () => {
|
||||||
expect(simplify(new StaticSymbol('/tmp/src/spread.ts', ''), {
|
expect(simplify(
|
||||||
__symbolic: 'reference',
|
reflector.getStaticSymbol('/tmp/src/spread.ts', ''),
|
||||||
name: 'spread'
|
reflector.getStaticSymbol('/tmp/src/spread.ts', 'spread')))
|
||||||
})).toEqual([0, 1, 2, 3, 4, 5]);
|
.toEqual([0, 1, 2, 3, 4, 5]);
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to get metadata from a ts file', () => {
|
|
||||||
const metadata = reflector.getModuleMetadata('/tmp/src/custom-decorator-reference.ts');
|
|
||||||
expect(metadata).toEqual({
|
|
||||||
__symbolic: 'module',
|
|
||||||
version: 3,
|
|
||||||
metadata: {
|
|
||||||
Foo: {
|
|
||||||
__symbolic: 'class',
|
|
||||||
decorators: [{
|
|
||||||
__symbolic: 'call',
|
|
||||||
expression:
|
|
||||||
{__symbolic: 'reference', module: './custom-decorator', name: 'CustomDecorator'}
|
|
||||||
}],
|
|
||||||
members: {
|
|
||||||
foo: [{
|
|
||||||
__symbolic: 'property',
|
|
||||||
decorators: [{
|
|
||||||
__symbolic: 'call',
|
|
||||||
expression: {
|
|
||||||
__symbolic: 'reference',
|
|
||||||
module: './custom-decorator',
|
|
||||||
name: 'CustomDecorator'
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to get metadata for a class containing a custom decorator', () => {
|
it('should be able to get metadata for a class containing a custom decorator', () => {
|
||||||
|
@ -488,62 +449,6 @@ describe('StaticReflector', () => {
|
||||||
expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod');
|
expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to produce a symbol for an exported symbol', () => {
|
|
||||||
expect(reflector.findDeclaration('@angular/router', 'foo', 'main.ts')).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to produce a symbol for values space only reference', () => {
|
|
||||||
expect(reflector.findDeclaration('@angular/router/src/providers', 'foo', 'main.ts'))
|
|
||||||
.toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be produce the same symbol if asked twice', () => {
|
|
||||||
const foo1 = reflector.getStaticSymbol('main.ts', 'foo');
|
|
||||||
const foo2 = reflector.getStaticSymbol('main.ts', 'foo');
|
|
||||||
expect(foo1).toBe(foo2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to produce a symbol for a module with no file',
|
|
||||||
() => { expect(reflector.getStaticSymbol('angularjs', 'SomeAngularSymbol')).toBeDefined(); });
|
|
||||||
|
|
||||||
it('should be able to trace a named export', () => {
|
|
||||||
const symbol = reflector.findDeclaration('./reexport/reexport', 'One', '/tmp/src/main.ts');
|
|
||||||
expect(symbol.name).toEqual('One');
|
|
||||||
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to trace a renamed export', () => {
|
|
||||||
const symbol = reflector.findDeclaration('./reexport/reexport', 'Four', '/tmp/src/main.ts');
|
|
||||||
expect(symbol.name).toEqual('Three');
|
|
||||||
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to trace an export * export', () => {
|
|
||||||
const symbol = reflector.findDeclaration('./reexport/reexport', 'Five', '/tmp/src/main.ts');
|
|
||||||
expect(symbol.name).toEqual('Five');
|
|
||||||
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin5.d.ts');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to trace a multi-level re-export', () => {
|
|
||||||
const symbol = reflector.findDeclaration('./reexport/reexport', 'Thirty', '/tmp/src/main.ts');
|
|
||||||
expect(symbol.name).toEqual('Thirty');
|
|
||||||
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should cache tracing a named export', () => {
|
|
||||||
const moduleNameToFileNameSpy = spyOn(host, 'moduleNameToFileName').and.callThrough();
|
|
||||||
const getMetadataForSpy = spyOn(host, 'getMetadataFor').and.callThrough();
|
|
||||||
reflector.findDeclaration('./reexport/reexport', 'One', '/tmp/src/main.ts');
|
|
||||||
moduleNameToFileNameSpy.calls.reset();
|
|
||||||
getMetadataForSpy.calls.reset();
|
|
||||||
|
|
||||||
const symbol = reflector.findDeclaration('./reexport/reexport', 'One', '/tmp/src/main.ts');
|
|
||||||
expect(moduleNameToFileNameSpy.calls.count()).toBe(1);
|
|
||||||
expect(getMetadataForSpy.calls.count()).toBe(0);
|
|
||||||
expect(symbol.name).toEqual('One');
|
|
||||||
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('inheritance', () => {
|
describe('inheritance', () => {
|
||||||
class ClassDecorator {
|
class ClassDecorator {
|
||||||
constructor(public value: any) {}
|
constructor(public value: any) {}
|
||||||
|
@ -706,78 +611,6 @@ describe('StaticReflector', () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export class MockStaticReflectorHost implements StaticReflectorHost {
|
|
||||||
private collector = new MetadataCollector();
|
|
||||||
|
|
||||||
constructor(private data: {[key: string]: any}) {}
|
|
||||||
|
|
||||||
// In tests, assume that symbols are not re-exported
|
|
||||||
moduleNameToFileName(modulePath: string, containingFile?: string): string {
|
|
||||||
function splitPath(path: string): string[] { return path.split(/\/|\\/g); }
|
|
||||||
|
|
||||||
function resolvePath(pathParts: string[]): string {
|
|
||||||
const result: string[] = [];
|
|
||||||
pathParts.forEach((part, index) => {
|
|
||||||
switch (part) {
|
|
||||||
case '':
|
|
||||||
case '.':
|
|
||||||
if (index > 0) return;
|
|
||||||
break;
|
|
||||||
case '..':
|
|
||||||
if (index > 0 && result.length != 0) result.pop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
result.push(part);
|
|
||||||
});
|
|
||||||
return result.join('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
function pathTo(from: string, to: string): string {
|
|
||||||
let result = to;
|
|
||||||
if (to.startsWith('.')) {
|
|
||||||
const fromParts = splitPath(from);
|
|
||||||
fromParts.pop(); // remove the file name.
|
|
||||||
const toParts = splitPath(to);
|
|
||||||
result = resolvePath(fromParts.concat(toParts));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modulePath.indexOf('.') === 0) {
|
|
||||||
const baseName = pathTo(containingFile, modulePath);
|
|
||||||
const tsName = baseName + '.ts';
|
|
||||||
if (this._getMetadataFor(tsName)) {
|
|
||||||
return tsName;
|
|
||||||
}
|
|
||||||
return baseName + '.d.ts';
|
|
||||||
}
|
|
||||||
return '/tmp/' + modulePath + '.d.ts';
|
|
||||||
}
|
|
||||||
|
|
||||||
getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); }
|
|
||||||
|
|
||||||
private _getMetadataFor(moduleId: string): any {
|
|
||||||
if (this.data[moduleId] && moduleId.match(TS_EXT)) {
|
|
||||||
const text = this.data[moduleId];
|
|
||||||
if (typeof text === 'string') {
|
|
||||||
const sf = ts.createSourceFile(
|
|
||||||
moduleId, this.data[moduleId], ts.ScriptTarget.ES5, /* setParentNodes */ true);
|
|
||||||
const diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
|
|
||||||
if (diagnostics && diagnostics.length) {
|
|
||||||
throw Error(`Error encountered during parse of file ${moduleId}`);
|
|
||||||
}
|
|
||||||
return [this.collector.getMetadata(sf)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const result = this.data[moduleId];
|
|
||||||
if (result) {
|
|
||||||
return Array.isArray(result) ? result : [result];
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_TEST_DATA: {[key: string]: any} = {
|
const DEFAULT_TEST_DATA: {[key: string]: any} = {
|
||||||
'/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{
|
'/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{
|
||||||
'__symbolic': 'module',
|
'__symbolic': 'module',
|
||||||
|
@ -1008,8 +841,6 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'/src/extern.d.ts': {'__symbolic': 'module', 'version': 3, metadata: {s: 's'}},
|
'/src/extern.d.ts': {'__symbolic': 'module', 'version': 3, metadata: {s: 's'}},
|
||||||
'/tmp/src/version-error.d.ts': {'__symbolic': 'module', 'version': 100, metadata: {e: 's'}},
|
|
||||||
'/tmp/src/version-2-error.d.ts': {'__symbolic': 'module', 'version': 2, metadata: {e: 's'}},
|
|
||||||
'/tmp/src/error-reporting.d.ts': {
|
'/tmp/src/error-reporting.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 3,
|
version: 3,
|
||||||
|
@ -1343,47 +1174,4 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
|
||||||
@Input f: Forward;
|
@Input f: Forward;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
'/tmp/src/reexport/reexport.d.ts': {
|
|
||||||
__symbolic: 'module',
|
|
||||||
version: 3,
|
|
||||||
metadata: {},
|
|
||||||
exports: [
|
|
||||||
{from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]},
|
|
||||||
{from: './src/origin5'}, {from: './src/reexport2'}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'/tmp/src/reexport/src/origin1.d.ts': {
|
|
||||||
__symbolic: 'module',
|
|
||||||
version: 3,
|
|
||||||
metadata: {
|
|
||||||
One: {__symbolic: 'class'},
|
|
||||||
Two: {__symbolic: 'class'},
|
|
||||||
Three: {__symbolic: 'class'},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'/tmp/src/reexport/src/origin5.d.ts': {
|
|
||||||
__symbolic: 'module',
|
|
||||||
version: 3,
|
|
||||||
metadata: {
|
|
||||||
Five: {__symbolic: 'class'},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'/tmp/src/reexport/src/origin30.d.ts': {
|
|
||||||
__symbolic: 'module',
|
|
||||||
version: 3,
|
|
||||||
metadata: {
|
|
||||||
Thirty: {__symbolic: 'class'},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'/tmp/src/reexport/src/originNone.d.ts': {
|
|
||||||
__symbolic: 'module',
|
|
||||||
version: 3,
|
|
||||||
metadata: {},
|
|
||||||
},
|
|
||||||
'/tmp/src/reexport/src/reexport2.d.ts': {
|
|
||||||
__symbolic: 'module',
|
|
||||||
version: 3,
|
|
||||||
metadata: {},
|
|
||||||
exports: [{from: './originNone'}, {from: './origin30'}]
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,372 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, Summary, SummaryResolver} from '@angular/compiler';
|
||||||
|
import {MetadataCollector} from '@angular/tsc-wrapped';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This matches .ts files but not .d.ts files.
|
||||||
|
const TS_EXT = /(^.|(?!\.d)..)\.ts$/;
|
||||||
|
|
||||||
|
describe('StaticSymbolResolver', () => {
|
||||||
|
const noContext = new StaticSymbol('', '');
|
||||||
|
let host: StaticSymbolResolverHost;
|
||||||
|
let symbolResolver: StaticSymbolResolver;
|
||||||
|
let symbolCache: StaticSymbolCache;
|
||||||
|
|
||||||
|
beforeEach(() => { symbolCache = new StaticSymbolCache(); });
|
||||||
|
|
||||||
|
function init(
|
||||||
|
testData: {[key: string]: any} = DEFAULT_TEST_DATA, summaries: Summary<StaticSymbol>[] = []) {
|
||||||
|
host = new MockStaticSymbolResolverHost(testData);
|
||||||
|
symbolResolver =
|
||||||
|
new StaticSymbolResolver(host, symbolCache, new MockSummaryResolver(summaries));
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => init());
|
||||||
|
|
||||||
|
it('should throw an exception for unsupported metadata versions', () => {
|
||||||
|
expect(
|
||||||
|
() => symbolResolver.resolveSymbol(
|
||||||
|
symbolResolver.getSymbolByModule('src/version-error', 'e')))
|
||||||
|
.toThrow(new Error(
|
||||||
|
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 3'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an exception for version 2 metadata', () => {
|
||||||
|
expect(
|
||||||
|
() => symbolResolver.resolveSymbol(
|
||||||
|
symbolResolver.getSymbolByModule('src/version-2-error', 'e')))
|
||||||
|
.toThrowError(
|
||||||
|
'Unsupported metadata version 2 for module /tmp/src/version-2-error.d.ts. This module should be compiled with a newer version of ngc');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be produce the same symbol if asked twice', () => {
|
||||||
|
const foo1 = symbolResolver.getStaticSymbol('main.ts', 'foo');
|
||||||
|
const foo2 = symbolResolver.getStaticSymbol('main.ts', 'foo');
|
||||||
|
expect(foo1).toBe(foo2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to produce a symbol for a module with no file', () => {
|
||||||
|
expect(symbolResolver.getStaticSymbol('angularjs', 'SomeAngularSymbol')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to split the metadata per symbol', () => {
|
||||||
|
init({
|
||||||
|
'/tmp/src/test.ts': `
|
||||||
|
export var a = 1;
|
||||||
|
export var b = 2;
|
||||||
|
`
|
||||||
|
});
|
||||||
|
expect(symbolResolver.resolveSymbol(symbolResolver.getStaticSymbol('/tmp/src/test.ts', 'a'))
|
||||||
|
.metadata)
|
||||||
|
.toBe(1);
|
||||||
|
expect(symbolResolver.resolveSymbol(symbolResolver.getStaticSymbol('/tmp/src/test.ts', 'b'))
|
||||||
|
.metadata)
|
||||||
|
.toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to resolve static symbols with members', () => {
|
||||||
|
init({
|
||||||
|
'/tmp/src/test.ts': `
|
||||||
|
export {exportedObj} from './export';
|
||||||
|
|
||||||
|
export var obj = {a: 1};
|
||||||
|
export class SomeClass {
|
||||||
|
static someField = 2;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'/tmp/src/export.ts': `
|
||||||
|
export var exportedObj = {};
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(symbolResolver
|
||||||
|
.resolveSymbol(symbolResolver.getStaticSymbol('/tmp/src/test.ts', 'obj', ['a']))
|
||||||
|
.metadata)
|
||||||
|
.toBe(1);
|
||||||
|
expect(symbolResolver
|
||||||
|
.resolveSymbol(
|
||||||
|
symbolResolver.getStaticSymbol('/tmp/src/test.ts', 'SomeClass', ['someField']))
|
||||||
|
.metadata)
|
||||||
|
.toBe(2);
|
||||||
|
expect(symbolResolver
|
||||||
|
.resolveSymbol(symbolResolver.getStaticSymbol(
|
||||||
|
'/tmp/src/test.ts', 'exportedObj', ['someMember']))
|
||||||
|
.metadata)
|
||||||
|
.toBe(symbolResolver.getStaticSymbol('/tmp/src/export.ts', 'exportedObj', ['someMember']));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use summaries in resolveSymbol and prefer them over regular metadata', () => {
|
||||||
|
const someSymbol = symbolCache.get('/test.ts', 'a');
|
||||||
|
init({'/test.ts': 'export var a = 2'}, [{symbol: someSymbol, metadata: 1}]);
|
||||||
|
expect(symbolResolver.resolveSymbol(someSymbol).metadata).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to get all exported symbols of a file', () => {
|
||||||
|
expect(symbolResolver.getSymbolsOf('/tmp/src/reexport/src/origin1.d.ts')).toEqual([
|
||||||
|
symbolResolver.getStaticSymbol('/tmp/src/reexport/src/origin1.d.ts', 'One'),
|
||||||
|
symbolResolver.getStaticSymbol('/tmp/src/reexport/src/origin1.d.ts', 'Two'),
|
||||||
|
symbolResolver.getStaticSymbol('/tmp/src/reexport/src/origin1.d.ts', 'Three'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to get all reexported symbols of a file', () => {
|
||||||
|
expect(symbolResolver.getSymbolsOf('/tmp/src/reexport/reexport.d.ts')).toEqual([
|
||||||
|
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'One'),
|
||||||
|
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'Two'),
|
||||||
|
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'Four'),
|
||||||
|
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'Five'),
|
||||||
|
symbolResolver.getStaticSymbol('/tmp/src/reexport/reexport.d.ts', 'Thirty')
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge the exported symbols of a file with the exported symbols of its summary', () => {
|
||||||
|
const someSymbol = symbolCache.get('/test.ts', 'a');
|
||||||
|
init(
|
||||||
|
{'/test.ts': 'export var b = 2'},
|
||||||
|
[{symbol: symbolCache.get('/test.ts', 'a'), metadata: 1}]);
|
||||||
|
expect(symbolResolver.getSymbolsOf('/test.ts')).toEqual([
|
||||||
|
symbolCache.get('/test.ts', 'a'), symbolCache.get('/test.ts', 'b')
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should replace references by StaticSymbols', () => {
|
||||||
|
init({
|
||||||
|
'/test.ts': `
|
||||||
|
import {b, y} from './test2';
|
||||||
|
export var a = b;
|
||||||
|
export var x = [y];
|
||||||
|
|
||||||
|
export function simpleFn(fnArg) {
|
||||||
|
return [a, y, fnArg];
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'/test2.ts': `
|
||||||
|
export var b;
|
||||||
|
export var y;
|
||||||
|
`
|
||||||
|
});
|
||||||
|
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'a')).metadata)
|
||||||
|
.toEqual(symbolCache.get('/test2.ts', 'b'));
|
||||||
|
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'x')).metadata).toEqual([
|
||||||
|
symbolCache.get('/test2.ts', 'y')
|
||||||
|
]);
|
||||||
|
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'simpleFn')).metadata).toEqual({
|
||||||
|
__symbolic: 'function',
|
||||||
|
parameters: ['fnArg'],
|
||||||
|
value: [
|
||||||
|
symbolCache.get('/test.ts', 'a'), symbolCache.get('/test2.ts', 'y'),
|
||||||
|
Object({__symbolic: 'reference', name: 'fnArg'})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore module references without a name', () => {
|
||||||
|
init({
|
||||||
|
'/test.ts': `
|
||||||
|
import Default from './test2';
|
||||||
|
export {Default};
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.ts', 'Default')).metadata)
|
||||||
|
.toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to trace a named export', () => {
|
||||||
|
const symbol = symbolResolver
|
||||||
|
.resolveSymbol(symbolResolver.getSymbolByModule(
|
||||||
|
'./reexport/reexport', 'One', '/tmp/src/main.ts'))
|
||||||
|
.metadata;
|
||||||
|
expect(symbol.name).toEqual('One');
|
||||||
|
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to trace a renamed export', () => {
|
||||||
|
const symbol = symbolResolver
|
||||||
|
.resolveSymbol(symbolResolver.getSymbolByModule(
|
||||||
|
'./reexport/reexport', 'Four', '/tmp/src/main.ts'))
|
||||||
|
.metadata;
|
||||||
|
expect(symbol.name).toEqual('Three');
|
||||||
|
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to trace an export * export', () => {
|
||||||
|
const symbol = symbolResolver
|
||||||
|
.resolveSymbol(symbolResolver.getSymbolByModule(
|
||||||
|
'./reexport/reexport', 'Five', '/tmp/src/main.ts'))
|
||||||
|
.metadata;
|
||||||
|
expect(symbol.name).toEqual('Five');
|
||||||
|
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin5.d.ts');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to trace a multi-level re-export', () => {
|
||||||
|
const symbol1 = symbolResolver
|
||||||
|
.resolveSymbol(symbolResolver.getSymbolByModule(
|
||||||
|
'./reexport/reexport', 'Thirty', '/tmp/src/main.ts'))
|
||||||
|
.metadata;
|
||||||
|
expect(symbol1.name).toEqual('Thirty');
|
||||||
|
expect(symbol1.filePath).toEqual('/tmp/src/reexport/src/reexport2.d.ts');
|
||||||
|
const symbol2 = symbolResolver.resolveSymbol(symbol1).metadata;
|
||||||
|
expect(symbol2.name).toEqual('Thirty');
|
||||||
|
expect(symbol2.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cache tracing a named export', () => {
|
||||||
|
const moduleNameToFileNameSpy = spyOn(host, 'moduleNameToFileName').and.callThrough();
|
||||||
|
const getMetadataForSpy = spyOn(host, 'getMetadataFor').and.callThrough();
|
||||||
|
symbolResolver.resolveSymbol(
|
||||||
|
symbolResolver.getSymbolByModule('./reexport/reexport', 'One', '/tmp/src/main.ts'));
|
||||||
|
moduleNameToFileNameSpy.calls.reset();
|
||||||
|
getMetadataForSpy.calls.reset();
|
||||||
|
|
||||||
|
const symbol = symbolResolver
|
||||||
|
.resolveSymbol(symbolResolver.getSymbolByModule(
|
||||||
|
'./reexport/reexport', 'One', '/tmp/src/main.ts'))
|
||||||
|
.metadata;
|
||||||
|
expect(moduleNameToFileNameSpy.calls.count()).toBe(1);
|
||||||
|
expect(getMetadataForSpy.calls.count()).toBe(0);
|
||||||
|
expect(symbol.name).toEqual('One');
|
||||||
|
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export class MockSummaryResolver implements SummaryResolver<StaticSymbol> {
|
||||||
|
constructor(private summaries: Summary<StaticSymbol>[] = []) {}
|
||||||
|
|
||||||
|
resolveSummary(reference: StaticSymbol): Summary<StaticSymbol> {
|
||||||
|
return this.summaries.find(summary => summary.symbol === reference);
|
||||||
|
};
|
||||||
|
getSymbolsOf(filePath: string): StaticSymbol[] {
|
||||||
|
return this.summaries.filter(summary => summary.symbol.filePath === filePath)
|
||||||
|
.map(summary => summary.symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost {
|
||||||
|
private collector = new MetadataCollector();
|
||||||
|
|
||||||
|
constructor(private data: {[key: string]: any}) {}
|
||||||
|
|
||||||
|
// In tests, assume that symbols are not re-exported
|
||||||
|
moduleNameToFileName(modulePath: string, containingFile?: string): string {
|
||||||
|
function splitPath(path: string): string[] { return path.split(/\/|\\/g); }
|
||||||
|
|
||||||
|
function resolvePath(pathParts: string[]): string {
|
||||||
|
const result: string[] = [];
|
||||||
|
pathParts.forEach((part, index) => {
|
||||||
|
switch (part) {
|
||||||
|
case '':
|
||||||
|
case '.':
|
||||||
|
if (index > 0) return;
|
||||||
|
break;
|
||||||
|
case '..':
|
||||||
|
if (index > 0 && result.length != 0) result.pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result.push(part);
|
||||||
|
});
|
||||||
|
return result.join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function pathTo(from: string, to: string): string {
|
||||||
|
let result = to;
|
||||||
|
if (to.startsWith('.')) {
|
||||||
|
const fromParts = splitPath(from);
|
||||||
|
fromParts.pop(); // remove the file name.
|
||||||
|
const toParts = splitPath(to);
|
||||||
|
result = resolvePath(fromParts.concat(toParts));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modulePath.indexOf('.') === 0) {
|
||||||
|
const baseName = pathTo(containingFile, modulePath);
|
||||||
|
const tsName = baseName + '.ts';
|
||||||
|
if (this._getMetadataFor(tsName)) {
|
||||||
|
return tsName;
|
||||||
|
}
|
||||||
|
return baseName + '.d.ts';
|
||||||
|
}
|
||||||
|
return '/tmp/' + modulePath + '.d.ts';
|
||||||
|
}
|
||||||
|
|
||||||
|
getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); }
|
||||||
|
|
||||||
|
private _getMetadataFor(filePath: string): any {
|
||||||
|
if (this.data[filePath] && filePath.match(TS_EXT)) {
|
||||||
|
const text = this.data[filePath];
|
||||||
|
if (typeof text === 'string') {
|
||||||
|
const sf = ts.createSourceFile(
|
||||||
|
filePath, this.data[filePath], ts.ScriptTarget.ES5, /* setParentNodes */ true);
|
||||||
|
const diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
|
||||||
|
if (diagnostics && diagnostics.length) {
|
||||||
|
throw Error(`Error encountered during parse of file ${filePath}`);
|
||||||
|
}
|
||||||
|
return [this.collector.getMetadata(sf)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = this.data[filePath];
|
||||||
|
if (result) {
|
||||||
|
return Array.isArray(result) ? result : [result];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_TEST_DATA: {[key: string]: any} = {
|
||||||
|
'/tmp/src/version-error.d.ts': {'__symbolic': 'module', 'version': 100, metadata: {e: 's'}},
|
||||||
|
'/tmp/src/version-2-error.d.ts': {'__symbolic': 'module', 'version': 2, metadata: {e: 's'}},
|
||||||
|
'/tmp/src/reexport/reexport.d.ts': {
|
||||||
|
__symbolic: 'module',
|
||||||
|
version: 3,
|
||||||
|
metadata: {},
|
||||||
|
exports: [
|
||||||
|
{from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]},
|
||||||
|
{from: './src/origin5'}, {from: './src/reexport2'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'/tmp/src/reexport/src/origin1.d.ts': {
|
||||||
|
__symbolic: 'module',
|
||||||
|
version: 3,
|
||||||
|
metadata: {
|
||||||
|
One: {__symbolic: 'class'},
|
||||||
|
Two: {__symbolic: 'class'},
|
||||||
|
Three: {__symbolic: 'class'},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'/tmp/src/reexport/src/origin5.d.ts': {
|
||||||
|
__symbolic: 'module',
|
||||||
|
version: 3,
|
||||||
|
metadata: {
|
||||||
|
Five: {__symbolic: 'class'},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'/tmp/src/reexport/src/origin30.d.ts': {
|
||||||
|
__symbolic: 'module',
|
||||||
|
version: 3,
|
||||||
|
metadata: {
|
||||||
|
Thirty: {__symbolic: 'class'},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'/tmp/src/reexport/src/originNone.d.ts': {
|
||||||
|
__symbolic: 'module',
|
||||||
|
version: 3,
|
||||||
|
metadata: {},
|
||||||
|
},
|
||||||
|
'/tmp/src/reexport/src/reexport2.d.ts': {
|
||||||
|
__symbolic: 'module',
|
||||||
|
version: 3,
|
||||||
|
metadata: {},
|
||||||
|
exports: [{from: './originNone'}, {from: './origin30'}]
|
||||||
|
}
|
||||||
|
};
|
|
@ -6,128 +6,68 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AotSummaryResolver, AotSummaryResolverHost, CompileNgModuleSummary, CompileSummaryKind, CompileTypeSummary, StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler';
|
import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, CompileTypeSummary, ResolvedStaticSymbol, StaticSymbol, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
|
||||||
|
import {AotSummarySerializerHost} from '@angular/compiler/src/aot/summary_serializer';
|
||||||
|
import {deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import {MockStaticReflectorHost} from './static_reflector_spec';
|
import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';
|
||||||
|
|
||||||
const EXT = /\.ts$|.d.ts$/;
|
const EXT = /\.ts$|.d.ts$/;
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('AotSummaryResolver', () => {
|
describe('AotSummaryResolver', () => {
|
||||||
let resolver: AotSummaryResolver;
|
let summaryResolver: AotSummaryResolver;
|
||||||
let staticReflector: StaticReflector;
|
let symbolCache: StaticSymbolCache;
|
||||||
|
let host: MockAotSummaryResolverHost;
|
||||||
|
|
||||||
|
beforeEach(() => { symbolCache = new StaticSymbolCache(); });
|
||||||
|
|
||||||
function init(summaries: {[filePath: string]: string} = {}) {
|
function init(summaries: {[filePath: string]: string} = {}) {
|
||||||
// Note: We don't give the static reflector metadata files,
|
host = new MockAotSummaryResolverHost(summaries);
|
||||||
// so that we can test that we can deserialize summary files
|
summaryResolver = new AotSummaryResolver(host, symbolCache);
|
||||||
// without reading metadata files. This is important
|
|
||||||
// as summary files can contain references to files of transitive compilation
|
|
||||||
// dependencies, and we don't want to read their metadata files.
|
|
||||||
staticReflector = new StaticReflector(new MockStaticReflectorHost({}));
|
|
||||||
const host = new MockAotSummaryResolverHost(summaries);
|
|
||||||
resolver = new AotSummaryResolver(host, staticReflector, {excludeFilePattern: /\.d\.ts$/});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should add .ngsummary.json to the filename', () => {
|
function serialize(symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string {
|
||||||
init();
|
// Note: Don't use the top level host / summaryResolver as they might not be created yet
|
||||||
expect(resolver.serializeSummaries('a.ts', []).genFileUrl).toBe('a.ngsummary.json');
|
const mockSummaryResolver = new MockSummaryResolver([]);
|
||||||
expect(resolver.serializeSummaries('a.d.ts', []).genFileUrl).toBe('a.ngsummary.json');
|
const symbolResolver = new StaticSymbolResolver(
|
||||||
expect(resolver.serializeSummaries('a.js', []).genFileUrl).toBe('a.ngsummary.json');
|
new MockStaticSymbolResolverHost({}), symbolCache, mockSummaryResolver);
|
||||||
|
return serializeSummaries(
|
||||||
|
new MockAotSummarySerializerHost(), mockSummaryResolver, symbolResolver, symbols, types);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should load serialized summary files', () => {
|
||||||
|
const asymbol = symbolCache.get('/a.d.ts', 'a');
|
||||||
|
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
|
||||||
|
expect(summaryResolver.resolveSummary(asymbol)).toEqual({symbol: asymbol, metadata: 1});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should serialize various data correctly', () => {
|
it('should not load summaries for source files', () => {
|
||||||
init();
|
init({});
|
||||||
const serializedData = resolver.serializeSummaries(
|
spyOn(host, 'loadSummary').and.callThrough();
|
||||||
'/tmp/some_pipe.ts', [<any>{
|
|
||||||
summaryKind: CompileSummaryKind.Pipe,
|
|
||||||
type: {
|
|
||||||
reference: staticReflector.getStaticSymbol('/tmp/some_pipe.ts', 'SomePipe'),
|
|
||||||
},
|
|
||||||
aNumber: 1,
|
|
||||||
aString: 'hello',
|
|
||||||
anArray: [1, 2],
|
|
||||||
aStaticSymbol:
|
|
||||||
staticReflector.getStaticSymbol('/tmp/some_symbol.ts', 'someName', ['someMember'])
|
|
||||||
}]);
|
|
||||||
|
|
||||||
// Note: this creates a new staticReflector!
|
expect(summaryResolver.resolveSummary(symbolCache.get('/a.ts', 'a'))).toBeFalsy();
|
||||||
init({[serializedData.genFileUrl]: serializedData.source});
|
expect(host.loadSummary).not.toHaveBeenCalled();
|
||||||
|
|
||||||
const deserialized = resolver.resolveSummary(
|
|
||||||
staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomePipe'));
|
|
||||||
expect(deserialized.aNumber).toBe(1);
|
|
||||||
expect(deserialized.aString).toBe('hello');
|
|
||||||
expect(deserialized.anArray).toEqual([1, 2]);
|
|
||||||
expect(deserialized.aStaticSymbol instanceof StaticSymbol).toBe(true);
|
|
||||||
// Note: change from .ts to .d.ts is expected
|
|
||||||
expect(deserialized.aStaticSymbol)
|
|
||||||
.toEqual(
|
|
||||||
staticReflector.getStaticSymbol('/tmp/some_symbol.d.ts', 'someName', ['someMember']));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should store reexports in the same file', () => {
|
it('should cache summaries', () => {
|
||||||
init();
|
const asymbol = symbolCache.get('/a.d.ts', 'a');
|
||||||
const reexportedData = resolver.serializeSummaries(
|
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
|
||||||
'/tmp/some_pipe.ts', [{
|
expect(summaryResolver.resolveSummary(asymbol)).toBe(summaryResolver.resolveSummary(asymbol));
|
||||||
summaryKind: CompileSummaryKind.Pipe,
|
|
||||||
type: {
|
|
||||||
reference: staticReflector.getStaticSymbol('/tmp/some_pipe.ts', 'SomeReexportedPipe'),
|
|
||||||
diDeps: [],
|
|
||||||
lifecycleHooks: []
|
|
||||||
},
|
|
||||||
}]);
|
|
||||||
|
|
||||||
init({[reexportedData.genFileUrl]: reexportedData.source});
|
|
||||||
const serializedData = resolver.serializeSummaries('/tmp/some_module.ts', [
|
|
||||||
<CompileNgModuleSummary>{
|
|
||||||
summaryKind: CompileSummaryKind.NgModule,
|
|
||||||
type: {
|
|
||||||
reference: staticReflector.getStaticSymbol('/tmp/some_module.ts', 'SomeModule'),
|
|
||||||
diDeps: [],
|
|
||||||
lifecycleHooks: []
|
|
||||||
},
|
|
||||||
exportedPipes: [{
|
|
||||||
reference: staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomeReexportedPipe')
|
|
||||||
}],
|
|
||||||
exportedDirectives: [],
|
|
||||||
providers: [],
|
|
||||||
entryComponents: [],
|
|
||||||
modules: []
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
init({[serializedData.genFileUrl]: serializedData.source});
|
|
||||||
|
|
||||||
resolver.resolveSummary(
|
|
||||||
staticReflector.getStaticSymbol('/tmp/some_module.d.ts', 'SomeModule'));
|
|
||||||
expect(resolver.resolveSummary(
|
|
||||||
staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomeReexportedPipe')))
|
|
||||||
.toEqual({
|
|
||||||
summaryKind: CompileSummaryKind.Pipe,
|
|
||||||
type: {
|
|
||||||
reference:
|
|
||||||
staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomeReexportedPipe'),
|
|
||||||
diDeps: [],
|
|
||||||
lifecycleHooks: []
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return all sumbols in a summary', () => {
|
||||||
|
const asymbol = symbolCache.get('/a.d.ts', 'a');
|
||||||
|
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
|
||||||
|
expect(summaryResolver.getSymbolsOf('/a.d.ts')).toEqual([asymbol]);
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockAotSummaryResolverHost implements AotSummaryResolverHost {
|
|
||||||
constructor(private summaries: {[fileName: string]: string}) {}
|
|
||||||
|
|
||||||
loadSummary(filePath: string): string {
|
|
||||||
const result = this.summaries[filePath];
|
|
||||||
if (!result) {
|
|
||||||
throw new Error(`Could not find summary for ${filePath}`);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export class MockAotSummarySerializerHost implements AotSummarySerializerHost {
|
||||||
fileNameToModuleName(fileName: string): string {
|
fileNameToModuleName(fileName: string): string {
|
||||||
return './' + path.basename(fileName).replace(EXT, '');
|
return './' + path.basename(fileName).replace(EXT, '');
|
||||||
}
|
}
|
||||||
|
@ -135,4 +75,13 @@ class MockAotSummaryResolverHost implements AotSummaryResolverHost {
|
||||||
getOutputFileName(sourceFileName: string): string {
|
getOutputFileName(sourceFileName: string): string {
|
||||||
return sourceFileName.replace(EXT, '') + '.d.ts';
|
return sourceFileName.replace(EXT, '') + '.d.ts';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSourceFile(filePath: string) { return !filePath.endsWith('.d.ts'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MockAotSummaryResolverHost extends MockAotSummarySerializerHost implements
|
||||||
|
AotSummaryResolverHost {
|
||||||
|
constructor(private summaries: {[fileName: string]: string}) { super(); }
|
||||||
|
|
||||||
|
loadSummary(filePath: string): string { return this.summaries[filePath]; }
|
||||||
}
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler';
|
||||||
|
import {AotSummarySerializerHost, deserializeSummaries, serializeSummaries, summaryFileName} from '@angular/compiler/src/aot/summary_serializer';
|
||||||
|
|
||||||
|
import {MockStaticSymbolResolverHost} from './static_symbol_resolver_spec';
|
||||||
|
import {MockAotSummaryResolverHost} from './summary_resolver_spec';
|
||||||
|
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('summary serializer', () => {
|
||||||
|
let summaryResolver: AotSummaryResolver;
|
||||||
|
let symbolResolver: StaticSymbolResolver;
|
||||||
|
let symbolCache: StaticSymbolCache;
|
||||||
|
let host: MockAotSummaryResolverHost;
|
||||||
|
|
||||||
|
beforeEach(() => { symbolCache = new StaticSymbolCache(); });
|
||||||
|
|
||||||
|
function init(
|
||||||
|
summaries: {[filePath: string]: string} = {}, metadata: {[key: string]: any} = {}) {
|
||||||
|
host = new MockAotSummaryResolverHost(summaries);
|
||||||
|
summaryResolver = new AotSummaryResolver(host, symbolCache);
|
||||||
|
symbolResolver = new StaticSymbolResolver(
|
||||||
|
new MockStaticSymbolResolverHost(metadata), symbolCache, summaryResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('summaryFileName', () => {
|
||||||
|
it('should add .ngsummary.json to the filename', () => {
|
||||||
|
init();
|
||||||
|
expect(summaryFileName('a.ts')).toBe('a.ngsummary.json');
|
||||||
|
expect(summaryFileName('a.d.ts')).toBe('a.ngsummary.json');
|
||||||
|
expect(summaryFileName('a.js')).toBe('a.ngsummary.json');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize various data correctly', () => {
|
||||||
|
init();
|
||||||
|
const serializedData = serializeSummaries(
|
||||||
|
host, summaryResolver, symbolResolver,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
symbol: symbolCache.get('/tmp/some_values.ts', 'Values'),
|
||||||
|
metadata: {
|
||||||
|
aNumber: 1,
|
||||||
|
aString: 'hello',
|
||||||
|
anArray: [1, 2],
|
||||||
|
aStaticSymbol: symbolCache.get('/tmp/some_symbol.ts', 'someName')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol: symbolCache.get('/tmp/some_service.ts', 'SomeService'),
|
||||||
|
metadata: {
|
||||||
|
__symbolic: 'class',
|
||||||
|
members: {'aMethod': {__symbolic: 'function'}},
|
||||||
|
statics: {aStatic: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[<any>{
|
||||||
|
summaryKind: CompileSummaryKind.Injectable,
|
||||||
|
type: {
|
||||||
|
reference: symbolCache.get('/tmp/some_service.ts', 'SomeService'),
|
||||||
|
},
|
||||||
|
}]);
|
||||||
|
|
||||||
|
|
||||||
|
const summaries = deserializeSummaries(symbolCache, serializedData);
|
||||||
|
expect(summaries.length).toBe(2);
|
||||||
|
|
||||||
|
// Note: change from .ts to .d.ts is expected
|
||||||
|
expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/some_values.d.ts', 'Values'));
|
||||||
|
expect(summaries[0].metadata).toEqual({
|
||||||
|
aNumber: 1,
|
||||||
|
aString: 'hello',
|
||||||
|
anArray: [1, 2],
|
||||||
|
aStaticSymbol: symbolCache.get('/tmp/some_symbol.d.ts', 'someName')
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService'));
|
||||||
|
// serialization should only keep the statics...
|
||||||
|
expect(summaries[1].metadata).toEqual({__symbolic: 'class', statics: {aStatic: true}});
|
||||||
|
expect(summaries[1].type.type.reference)
|
||||||
|
.toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should automatically add exported directives / pipes of NgModules that are not source files',
|
||||||
|
() => {
|
||||||
|
init({});
|
||||||
|
const externalSerialized = serializeSummaries(host, summaryResolver, symbolResolver, [], [
|
||||||
|
<any>{
|
||||||
|
summaryKind: CompileSummaryKind.Pipe,
|
||||||
|
type: {
|
||||||
|
reference: symbolCache.get('/tmp/external.ts', 'SomeExternalPipe'),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
<any>{
|
||||||
|
summaryKind: CompileSummaryKind.Directive,
|
||||||
|
type: {
|
||||||
|
reference: symbolCache.get('/tmp/external.ts', 'SomeExternalDir'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
init({
|
||||||
|
'/tmp/external.ngsummary.json': externalSerialized,
|
||||||
|
});
|
||||||
|
|
||||||
|
const serialized = serializeSummaries(
|
||||||
|
host, summaryResolver, symbolResolver, [], [<any>{
|
||||||
|
summaryKind: CompileSummaryKind.NgModule,
|
||||||
|
type: {reference: symbolCache.get('/tmp/some_module.ts', 'SomeModule')},
|
||||||
|
exportedPipes: [
|
||||||
|
{reference: symbolCache.get('/tmp/some_pipe.ts', 'SomePipe')},
|
||||||
|
{reference: symbolCache.get('/tmp/external.d.ts', 'SomeExternalPipe')}
|
||||||
|
],
|
||||||
|
exportedDirectives: [
|
||||||
|
{reference: symbolCache.get('/tmp/some_dir.ts', 'SomeDir')},
|
||||||
|
{reference: symbolCache.get('/tmp/external.d.ts', 'SomeExternalDir')}
|
||||||
|
]
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const summaries = deserializeSummaries(symbolCache, serialized);
|
||||||
|
expect(summaries.length).toBe(3);
|
||||||
|
expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/some_module.d.ts', 'SomeModule'));
|
||||||
|
expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/external.d.ts', 'SomeExternalDir'));
|
||||||
|
expect(summaries[2].symbol)
|
||||||
|
.toBe(symbolCache.get('/tmp/external.d.ts', 'SomeExternalPipe'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should automatically add the metadata of referenced symbols that are not in the soure files',
|
||||||
|
() => {
|
||||||
|
const externalSerialized = serializeSummaries(
|
||||||
|
host, summaryResolver, symbolResolver,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
symbol: symbolCache.get('/tmp/external.ts', 'PROVIDERS'),
|
||||||
|
metadata: [symbolCache.get('/tmp/external_svc.ts', 'SomeService')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol: symbolCache.get('/tmp/external_svc.ts', 'SomeService'),
|
||||||
|
metadata: {__symbolic: 'class'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[<any>{
|
||||||
|
summaryKind: CompileSummaryKind.Injectable,
|
||||||
|
type: {
|
||||||
|
reference: symbolCache.get('/tmp/external_svc.ts', 'SomeService'),
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
init(
|
||||||
|
{
|
||||||
|
'/tmp/external.ngsummary.json': externalSerialized,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'/tmp/local.ts': `
|
||||||
|
export var local = 'a';
|
||||||
|
`,
|
||||||
|
'/tmp/non_summary.d.ts':
|
||||||
|
{__symbolic: 'module', version: 3, metadata: {'external': 'b'}}
|
||||||
|
});
|
||||||
|
const serialized = serializeSummaries(
|
||||||
|
host, summaryResolver, symbolResolver, [{
|
||||||
|
symbol: symbolCache.get('/tmp/test.ts', 'main'),
|
||||||
|
metadata: {
|
||||||
|
local: symbolCache.get('/tmp/local.ts', 'local'),
|
||||||
|
external: symbolCache.get('/tmp/external.d.ts', 'PROVIDERS'),
|
||||||
|
externalNonSummary: symbolCache.get('/tmp/non_summary.d.ts', 'external')
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
[]);
|
||||||
|
|
||||||
|
const summaries = deserializeSummaries(symbolCache, serialized);
|
||||||
|
// Note: local should not show up!
|
||||||
|
expect(summaries.length).toBe(4);
|
||||||
|
expect(summaries[0].symbol).toBe(symbolCache.get('/tmp/test.d.ts', 'main'));
|
||||||
|
expect(summaries[0].metadata).toEqual({
|
||||||
|
local: symbolCache.get('/tmp/local.d.ts', 'local'),
|
||||||
|
external: symbolCache.get('/tmp/external.d.ts', 'PROVIDERS'),
|
||||||
|
externalNonSummary: symbolCache.get('/tmp/non_summary.d.ts', 'external')
|
||||||
|
});
|
||||||
|
expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/external.d.ts', 'PROVIDERS'));
|
||||||
|
expect(summaries[1].metadata).toEqual([symbolCache.get(
|
||||||
|
'/tmp/external_svc.d.ts', 'SomeService')]);
|
||||||
|
// there was no summary for non_summary, but it should have
|
||||||
|
// been serialized as well.
|
||||||
|
expect(summaries[2].symbol).toBe(symbolCache.get('/tmp/non_summary.d.ts', 'external'));
|
||||||
|
expect(summaries[2].metadata).toEqual('b');
|
||||||
|
// SomService is a transitive dep, but sould have been serialized as well.
|
||||||
|
expect(summaries[3].symbol).toBe(symbolCache.get('/tmp/external_svc.d.ts', 'SomeService'));
|
||||||
|
expect(summaries[3].type.type.reference)
|
||||||
|
.toBe(symbolCache.get('/tmp/external_svc.d.ts', 'SomeService'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler';
|
import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler';
|
||||||
import * as o from '@angular/compiler/src/output/output_ast';
|
import * as o from '@angular/compiler/src/output/output_ast';
|
||||||
import {ImportResolver} from '@angular/compiler/src/output/path_util';
|
import {ImportResolver} from '@angular/compiler/src/output/path_util';
|
||||||
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
|
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
|
||||||
|
@ -14,6 +14,8 @@ import {convertValueToOutputAst} from '@angular/compiler/src/output/value_util';
|
||||||
import {MetadataCollector, isClassMetadata, isMetadataSymbolicCallExpression} from '@angular/tsc-wrapped';
|
import {MetadataCollector, isClassMetadata, isMetadataSymbolicCallExpression} from '@angular/tsc-wrapped';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
import {MockSummaryResolver} from '../aot/static_symbol_resolver_spec';
|
||||||
|
|
||||||
describe('TypeScriptEmitter (node only)', () => {
|
describe('TypeScriptEmitter (node only)', () => {
|
||||||
it('should quote identifiers quoted in the source', () => {
|
it('should quote identifiers quoted in the source', () => {
|
||||||
const sourceText = `
|
const sourceText = `
|
||||||
|
@ -27,7 +29,10 @@ describe('TypeScriptEmitter (node only)', () => {
|
||||||
const source = ts.createSourceFile('test.ts', sourceText, ts.ScriptTarget.Latest);
|
const source = ts.createSourceFile('test.ts', sourceText, ts.ScriptTarget.Latest);
|
||||||
const collector = new MetadataCollector({quotedNames: true});
|
const collector = new MetadataCollector({quotedNames: true});
|
||||||
const stubHost = new StubReflectorHost();
|
const stubHost = new StubReflectorHost();
|
||||||
const reflector = new StaticReflector(stubHost);
|
const symbolCache = new StaticSymbolCache();
|
||||||
|
const symbolResolver =
|
||||||
|
new StaticSymbolResolver(stubHost, symbolCache, new MockSummaryResolver());
|
||||||
|
const reflector = new StaticReflector(symbolResolver);
|
||||||
|
|
||||||
// Get the metadata from the above source
|
// Get the metadata from the above source
|
||||||
const metadata = collector.getMetadata(source);
|
const metadata = collector.getMetadata(source);
|
||||||
|
@ -62,9 +67,9 @@ describe('TypeScriptEmitter (node only)', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
class StubReflectorHost implements StaticReflectorHost {
|
class StubReflectorHost implements StaticSymbolResolverHost {
|
||||||
getMetadataFor(modulePath: string): {[key: string]: any}[] { return []; }
|
getMetadataFor(modulePath: string): {[key: string]: any}[] { return []; }
|
||||||
moduleNameToFileName(moduleName: string, containingFile: string): string { return ''; }
|
moduleNameToFileName(moduleName: string, containingFile: string): string { return 'somePath'; }
|
||||||
}
|
}
|
||||||
|
|
||||||
class StubImportResolver extends ImportResolver {
|
class StubImportResolver extends ImportResolver {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {OpaqueToken} from '../di';
|
import {Injectable, OpaqueToken} from '../di';
|
||||||
import {BaseError} from '../facade/errors';
|
import {BaseError} from '../facade/errors';
|
||||||
import {stringify} from '../facade/lang';
|
import {stringify} from '../facade/lang';
|
||||||
import {ViewEncapsulation} from '../metadata';
|
import {ViewEncapsulation} from '../metadata';
|
||||||
|
@ -54,6 +54,7 @@ function _throwError() {
|
||||||
* of components.
|
* of components.
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
|
@Injectable()
|
||||||
export class Compiler {
|
export class Compiler {
|
||||||
/**
|
/**
|
||||||
* Compiles the given NgModule and all of its components. All templates of the components listed
|
* Compiles the given NgModule and all of its components. All templates of the components listed
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompilerConfig, StaticReflector, StaticSymbol, StaticSymbolCache, componentModuleUrl, createOfflineCompileUrlResolver} from '@angular/compiler';
|
import {AotSummaryResolver, CompileDirectiveMetadata, CompilerConfig, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, componentModuleUrl, createOfflineCompileUrlResolver} from '@angular/compiler';
|
||||||
import {NgAnalyzedModules, analyzeNgModules, extractProgramSymbols} from '@angular/compiler/src/aot/compiler';
|
import {NgAnalyzedModules, analyzeNgModules, extractProgramSymbols} from '@angular/compiler/src/aot/compiler';
|
||||||
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
|
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
|
||||||
import {DirectiveResolver} from '@angular/compiler/src/directive_resolver';
|
import {DirectiveResolver} from '@angular/compiler/src/directive_resolver';
|
||||||
|
@ -30,6 +30,7 @@ import {ReflectorHost} from './reflector_host';
|
||||||
import {BuiltinType, CompletionKind, Declaration, DeclarationError, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types';
|
import {BuiltinType, CompletionKind, Declaration, DeclarationError, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a `LanguageServiceHost`
|
* Create a `LanguageServiceHost`
|
||||||
*/
|
*/
|
||||||
|
@ -75,6 +76,7 @@ export class DummyResourceLoader extends ResourceLoader {
|
||||||
export class TypeScriptServiceHost implements LanguageServiceHost {
|
export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
private _resolver: CompileMetadataResolver;
|
private _resolver: CompileMetadataResolver;
|
||||||
private _staticSymbolCache = new StaticSymbolCache();
|
private _staticSymbolCache = new StaticSymbolCache();
|
||||||
|
private _staticSymbolResolver: StaticSymbolResolver;
|
||||||
private _reflector: StaticReflector;
|
private _reflector: StaticReflector;
|
||||||
private _reflectorHost: ReflectorHost;
|
private _reflectorHost: ReflectorHost;
|
||||||
private _checker: ts.TypeChecker;
|
private _checker: ts.TypeChecker;
|
||||||
|
@ -159,10 +161,13 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
private ensureAnalyzedModules(): NgAnalyzedModules {
|
private ensureAnalyzedModules(): NgAnalyzedModules {
|
||||||
let analyzedModules = this.analyzedModules;
|
let analyzedModules = this.analyzedModules;
|
||||||
if (!analyzedModules) {
|
if (!analyzedModules) {
|
||||||
|
const analyzeHost = {isSourceFile(filePath: string) { return true; }};
|
||||||
const programSymbols = extractProgramSymbols(
|
const programSymbols = extractProgramSymbols(
|
||||||
this.reflector, this.program.getSourceFiles().map(sf => sf.fileName), {});
|
this.staticSymbolResolver, this.program.getSourceFiles().map(sf => sf.fileName),
|
||||||
|
analyzeHost);
|
||||||
|
|
||||||
analyzedModules = this.analyzedModules = analyzeNgModules(programSymbols, {}, this.resolver);
|
analyzedModules = this.analyzedModules =
|
||||||
|
analyzeNgModules(programSymbols, analyzeHost, this.resolver);
|
||||||
}
|
}
|
||||||
return analyzedModules;
|
return analyzedModules;
|
||||||
}
|
}
|
||||||
|
@ -224,6 +229,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
if (this.modulesOutOfDate) {
|
if (this.modulesOutOfDate) {
|
||||||
this.analyzedModules = null;
|
this.analyzedModules = null;
|
||||||
this._reflector = null;
|
this._reflector = null;
|
||||||
|
this._staticSymbolResolver = null;
|
||||||
this.templateReferences = null;
|
this.templateReferences = null;
|
||||||
this.fileToComponent = null;
|
this.fileToComponent = null;
|
||||||
this.ensureAnalyzedModules();
|
this.ensureAnalyzedModules();
|
||||||
|
@ -385,12 +391,27 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
errors.push(error);
|
errors.push(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get staticSymbolResolver(): StaticSymbolResolver {
|
||||||
|
let result = this._staticSymbolResolver;
|
||||||
|
if (!result) {
|
||||||
|
const summaryResolver = new AotSummaryResolver(
|
||||||
|
{
|
||||||
|
loadSummary(filePath: string) { return null; },
|
||||||
|
isSourceFile(sourceFilePath: string) { return true; }
|
||||||
|
},
|
||||||
|
this._staticSymbolCache);
|
||||||
|
result = this._staticSymbolResolver = new StaticSymbolResolver(
|
||||||
|
this.reflectorHost, this._staticSymbolCache, summaryResolver,
|
||||||
|
(e, filePath) => this.collectError(e, filePath));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private get reflector(): StaticReflector {
|
private get reflector(): StaticReflector {
|
||||||
let result = this._reflector;
|
let result = this._reflector;
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = this._reflector = new StaticReflector(
|
result = this._reflector = new StaticReflector(
|
||||||
this.reflectorHost, this._staticSymbolCache, [], [],
|
this.staticSymbolResolver, [], [], (e, filePath) => this.collectError(e, filePath));
|
||||||
(e, filePath) => this.collectError(e, filePath));
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue