feat(compiler): read and write `.ngsummary.json` files
When compiling libraries, this feature extracts the minimal information from the directives/pipes/modules of the library into `.ngsummary.json` files, so that applications that use this library only need to be recompiled if one of the summary files change, but not on every change of the libraries (e.g. one of the templates). Only works if individual codegen for libraries is enabled, see the `generateCodeForLibraries: false` option. Closes #12787
This commit is contained in:
parent
9ab401f4d3
commit
614a35d539
|
@ -11,7 +11,9 @@ import {FormsModule} from '@angular/forms';
|
||||||
import {ServerModule} from '@angular/platform-server';
|
import {ServerModule} from '@angular/platform-server';
|
||||||
import {MdButtonModule} from '@angular2-material/button';
|
import {MdButtonModule} from '@angular2-material/button';
|
||||||
|
|
||||||
import {ThirdpartyModule} from '../third_party_src/module';
|
// Note: don't refer to third_party_src as we want to test that
|
||||||
|
// we can compile components from node_modules!
|
||||||
|
import {ThirdpartyModule} from 'third_party/module';
|
||||||
|
|
||||||
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
|
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
|
||||||
import {AnimateCmp} from './animate';
|
import {AnimateCmp} from './animate';
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
/* tslint:disable:no-console */
|
||||||
|
|
||||||
|
// Must be imported first, because angular2 decorators throws on load.
|
||||||
|
import 'reflect-metadata';
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import {tsc} from '@angular/tsc-wrapped/src/tsc';
|
||||||
|
import {AngularCompilerOptions, CodeGenerator, CompilerHostContext, NodeCompilerHostContext} from '@angular/compiler-cli';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main method.
|
||||||
|
* Standalone program that executes the real codegen and tests that
|
||||||
|
* ngsummary.json files are used for libraries.
|
||||||
|
*/
|
||||||
|
function main() {
|
||||||
|
console.log(`testing usage of ngsummary.json files in libraries...`);
|
||||||
|
const basePath = path.resolve(__dirname, '..');
|
||||||
|
const project = path.resolve(basePath, 'tsconfig-build.json');
|
||||||
|
const readFiles: string[] = [];
|
||||||
|
|
||||||
|
class AssertingHostContext extends NodeCompilerHostContext {
|
||||||
|
readFile(fileName: string): string {
|
||||||
|
readFiles.push(path.relative(basePath, fileName));
|
||||||
|
return super.readFile(fileName);
|
||||||
|
}
|
||||||
|
readResource(s: string): Promise<string> {
|
||||||
|
readFiles.push(path.relative(basePath, s));
|
||||||
|
return super.readResource(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = tsc.readConfiguration(project, basePath);
|
||||||
|
config.ngOptions.basePath = basePath;
|
||||||
|
// This flag tells ngc do not recompile libraries.
|
||||||
|
config.ngOptions.generateCodeForLibraries = false;
|
||||||
|
|
||||||
|
console.log(`>>> running codegen for ${project}`);
|
||||||
|
codegen(config, (host) => new AssertingHostContext())
|
||||||
|
.then((exitCode: any) => {
|
||||||
|
console.log(`>>> codegen done, asserting read files`);
|
||||||
|
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
|
||||||
|
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
|
||||||
|
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
|
||||||
|
|
||||||
|
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
|
||||||
|
assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
|
||||||
|
assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
|
||||||
|
console.log(`done, no errors.`);
|
||||||
|
process.exit(exitCode);
|
||||||
|
})
|
||||||
|
.catch((e: any) => {
|
||||||
|
console.error(e.stack);
|
||||||
|
console.error('Compilation failed');
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple adaption of tsc-wrapped main to just run codegen with a CompilerHostContext
|
||||||
|
*/
|
||||||
|
function codegen(
|
||||||
|
config: {parsed: ts.ParsedCommandLine, ngOptions: AngularCompilerOptions},
|
||||||
|
hostContextFactory: (host: ts.CompilerHost) => CompilerHostContext) {
|
||||||
|
const host = ts.createCompilerHost(config.parsed.options, true);
|
||||||
|
|
||||||
|
// HACK: patch the realpath to solve symlink issue here:
|
||||||
|
// https://github.com/Microsoft/TypeScript/issues/9552
|
||||||
|
// todo(misko): remove once facade symlinks are removed
|
||||||
|
host.realpath = (path) => path;
|
||||||
|
|
||||||
|
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
||||||
|
|
||||||
|
return CodeGenerator.create(config.ngOptions, {
|
||||||
|
} as any, program, host, hostContextFactory(host)).codegen();
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertSomeFileMatch(fileNames: string[], pattern: RegExp) {
|
||||||
|
assert(
|
||||||
|
fileNames.some(fileName => pattern.test(fileName)),
|
||||||
|
`Expected some read files match ${pattern}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertNoFileMatch(fileNames: string[], pattern: RegExp) {
|
||||||
|
const matches = fileNames.filter(fileName => pattern.test(fileName));
|
||||||
|
assert(
|
||||||
|
matches.length === 0,
|
||||||
|
`Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
|
@ -21,6 +21,7 @@
|
||||||
"src/module",
|
"src/module",
|
||||||
"src/bootstrap",
|
"src/bootstrap",
|
||||||
"test/all_spec",
|
"test/all_spec",
|
||||||
|
"test/test_summaries",
|
||||||
"benchmarks/src/tree/ng2/index_aot.ts",
|
"benchmarks/src/tree/ng2/index_aot.ts",
|
||||||
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
||||||
"benchmarks/src/largetable/ng2/index_aot.ts",
|
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
||||||
import {Console} from './private_import_core';
|
import {Console} from './private_import_core';
|
||||||
|
|
||||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||||
|
const GENERATED_META_FILES = /\.json$/;
|
||||||
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||||
|
|
||||||
const PREAMBLE = `/**
|
const PREAMBLE = `/**
|
||||||
|
@ -68,10 +69,11 @@ export class CodeGenerator {
|
||||||
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
|
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
|
||||||
.then(generatedModules => {
|
.then(generatedModules => {
|
||||||
generatedModules.forEach(generatedModule => {
|
generatedModules.forEach(generatedModule => {
|
||||||
const sourceFile = this.program.getSourceFile(generatedModule.fileUrl);
|
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
|
||||||
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
const emitPath = this.calculateEmitPath(generatedModule.genFileUrl);
|
||||||
this.host.writeFile(
|
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
|
||||||
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
|
PREAMBLE + generatedModule.source;
|
||||||
|
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,6 +209,12 @@ 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); }
|
||||||
|
|
||||||
|
getOutputFileName(sourceFilePath: string): string {
|
||||||
|
return sourceFilePath.replace(EXT, '') + '.d.ts';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompilerHostContextAdapter {
|
export class CompilerHostContextAdapter {
|
||||||
|
|
|
@ -36,6 +36,7 @@ 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/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';
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {SchemaMetadata} from '@angular/core';
|
||||||
|
|
||||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||||
import {AnimationParser} from '../animation/animation_parser';
|
import {AnimationParser} from '../animation/animation_parser';
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {ListWrapper} from '../facade/collection';
|
||||||
|
@ -24,12 +24,11 @@ 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 {AotCompilerOptions} from './compiler_options';
|
||||||
|
import {GeneratedFile} from './generated_file';
|
||||||
import {StaticReflector} from './static_reflector';
|
import {StaticReflector} from './static_reflector';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
|
import {AotSummaryResolver} from './summary_resolver';
|
||||||
export class SourceModule {
|
import {filterFileByPatterns} from './utils';
|
||||||
constructor(public fileUrl: string, public moduleUrl: string, public source: string) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AotCompiler {
|
export class AotCompiler {
|
||||||
private _animationCompiler = new AnimationCompiler();
|
private _animationCompiler = new AnimationCompiler();
|
||||||
|
@ -39,13 +38,13 @@ export class AotCompiler {
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
||||||
private _localeId: string, private _translationFormat: string,
|
private _summaryResolver: AotSummaryResolver, private _localeId: string,
|
||||||
private _animationParser: AnimationParser, private _staticReflector: StaticReflector,
|
private _translationFormat: string, private _animationParser: AnimationParser,
|
||||||
private _options: AotCompilerOptions) {}
|
private _staticReflector: StaticReflector, private _options: AotCompilerOptions) {}
|
||||||
|
|
||||||
clearCache() { this._metadataResolver.clearCache(); }
|
clearCache() { this._metadataResolver.clearCache(); }
|
||||||
|
|
||||||
compileAll(rootFiles: string[]): Promise<SourceModule[]> {
|
compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
|
||||||
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options);
|
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options);
|
||||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||||
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
|
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
|
||||||
|
@ -56,18 +55,28 @@ export class AotCompiler {
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const sourceModules = files.map(
|
const sourceModules = files.map(
|
||||||
file => this._compileSrcFile(
|
file => this._compileSrcFile(
|
||||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
|
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
|
||||||
|
file.ngModules));
|
||||||
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[], ngModules: StaticSymbol[]): SourceModule[] {
|
directives: StaticSymbol[], pipes: StaticSymbol[],
|
||||||
|
ngModules: 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 outputSourceModules: SourceModule[] = [];
|
const generatedFiles: GeneratedFile[] = [];
|
||||||
|
|
||||||
|
// write summary files
|
||||||
|
const summaries: CompileTypeSummary[] = [
|
||||||
|
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)),
|
||||||
|
...directives.map(ref => this._metadataResolver.getDirectiveSummary(ref)),
|
||||||
|
...ngModules.map(ref => this._metadataResolver.getNgModuleSummary(ref))
|
||||||
|
];
|
||||||
|
generatedFiles.push(this._summaryResolver.serializeSummaries(srcFileUrl, summaries));
|
||||||
|
|
||||||
// compile all ng modules
|
// compile all ng modules
|
||||||
exportedVars.push(
|
exportedVars.push(
|
||||||
|
@ -94,7 +103,7 @@ export class AotCompiler {
|
||||||
// compile styles
|
// compile styles
|
||||||
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
||||||
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
||||||
outputSourceModules.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
generatedFiles.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
||||||
});
|
});
|
||||||
|
|
||||||
// compile components
|
// compile components
|
||||||
|
@ -107,9 +116,9 @@ export class AotCompiler {
|
||||||
if (statements.length > 0) {
|
if (statements.length > 0) {
|
||||||
const srcModule = this._codegenSourceModule(
|
const srcModule = this._codegenSourceModule(
|
||||||
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
||||||
outputSourceModules.unshift(srcModule);
|
generatedFiles.unshift(srcModule);
|
||||||
}
|
}
|
||||||
return outputSourceModules;
|
return generatedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
||||||
|
@ -205,7 +214,7 @@ export class AotCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _codgenStyles(
|
private _codgenStyles(
|
||||||
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
|
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile {
|
||||||
_resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix);
|
_resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix);
|
||||||
return this._codegenSourceModule(
|
return this._codegenSourceModule(
|
||||||
fileUrl, _stylesModuleUrl(
|
fileUrl, _stylesModuleUrl(
|
||||||
|
@ -214,11 +223,11 @@ export class AotCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _codegenSourceModule(
|
private _codegenSourceModule(
|
||||||
fileUrl: string, moduleUrl: string, statements: o.Statement[],
|
srcFileUrl: string, genFileUrl: string, statements: o.Statement[],
|
||||||
exportedVars: string[]): SourceModule {
|
exportedVars: string[]): GeneratedFile {
|
||||||
return new SourceModule(
|
return new GeneratedFile(
|
||||||
fileUrl, moduleUrl,
|
srcFileUrl, genFileUrl,
|
||||||
this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
|
this._outputEmitter.emitStatements(genFileUrl, statements, exportedVars));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +299,12 @@ function _splitTypescriptSuffix(path: string): string[] {
|
||||||
export interface NgAnalyzedModules {
|
export interface NgAnalyzedModules {
|
||||||
ngModules: CompileNgModuleMetadata[];
|
ngModules: CompileNgModuleMetadata[];
|
||||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
||||||
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>;
|
files: Array<{
|
||||||
|
srcUrl: string,
|
||||||
|
directives: StaticSymbol[],
|
||||||
|
pipes: StaticSymbol[],
|
||||||
|
ngModules: StaticSymbol[]
|
||||||
|
}>;
|
||||||
symbolsMissingModule?: StaticSymbol[];
|
symbolsMissingModule?: StaticSymbol[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,11 +339,13 @@ function _analyzeNgModules(
|
||||||
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 filePaths = new Set<string>();
|
const filePaths = new Set<string>();
|
||||||
|
|
||||||
// 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`,
|
||||||
|
// - a map from file to pipes `ngPipesByFile`,
|
||||||
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
|
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
|
||||||
ngModuleMetas.forEach((ngModuleMeta) => {
|
ngModuleMetas.forEach((ngModuleMeta) => {
|
||||||
const srcFileUrl = ngModuleMeta.type.reference.filePath;
|
const srcFileUrl = ngModuleMeta.type.reference.filePath;
|
||||||
|
@ -347,16 +363,23 @@ function _analyzeNgModules(
|
||||||
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
|
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
|
||||||
const fileUrl = pipeIdentifier.reference.filePath;
|
const fileUrl = pipeIdentifier.reference.filePath;
|
||||||
filePaths.add(fileUrl);
|
filePaths.add(fileUrl);
|
||||||
|
ngPipesByFile.set(
|
||||||
|
fileUrl, (ngPipesByFile.get(fileUrl) || []).concat(pipeIdentifier.reference));
|
||||||
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
|
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
|
const files:
|
||||||
|
{srcUrl: string,
|
||||||
|
directives: StaticSymbol[],
|
||||||
|
pipes: StaticSymbol[],
|
||||||
|
ngModules: StaticSymbol[]}[] = [];
|
||||||
|
|
||||||
filePaths.forEach((srcUrl) => {
|
filePaths.forEach((srcUrl) => {
|
||||||
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
||||||
|
const pipes = ngPipesByFile.get(srcUrl) || [];
|
||||||
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
||||||
files.push({srcUrl, directives, ngModules});
|
files.push({srcUrl, directives, pipes, ngModules});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -372,7 +395,7 @@ export function extractProgramSymbols(
|
||||||
staticReflector: StaticReflector, files: string[],
|
staticReflector: StaticReflector, files: string[],
|
||||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] {
|
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] {
|
||||||
const staticSymbols: StaticSymbol[] = [];
|
const staticSymbols: StaticSymbol[] = [];
|
||||||
files.filter(fileName => _filterFileByPatterns(fileName, options)).forEach(sourceFile => {
|
files.filter(fileName => filterFileByPatterns(fileName, options)).forEach(sourceFile => {
|
||||||
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
|
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
|
||||||
if (!moduleMetadata) {
|
if (!moduleMetadata) {
|
||||||
console.error(`WARNING: no metadata found for ${sourceFile}`);
|
console.error(`WARNING: no metadata found for ${sourceFile}`);
|
||||||
|
@ -410,7 +433,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) || !filterFileByPatterns(staticSymbol.filePath, options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
|
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
|
||||||
|
@ -436,15 +459,3 @@ function _createNgModules(
|
||||||
|
|
||||||
return {ngModules: Array.from(ngModules.values()), symbolsMissingModule};
|
return {ngModules: Array.from(ngModules.values()), symbolsMissingModule};
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -34,8 +34,7 @@ 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 {AotSummaryResolver} from './summary_resolver';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new AotCompiler based on options and a host.
|
* Creates a new AotCompiler based on options and a host.
|
||||||
|
@ -61,15 +60,17 @@ 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), elementSchemaRegistry, normalizer, staticReflector);
|
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||||
|
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),
|
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), options.locale,
|
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
|
||||||
options.i18nFormat, new AnimationParser(elementSchemaRegistry), staticReflector, options);
|
options.i18nFormat, new AnimationParser(elementSchemaRegistry), staticReflector, options);
|
||||||
return {compiler, reflector: staticReflector};
|
return {compiler, reflector: staticReflector};
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,16 @@ import {ImportResolver} from '../output/path_util';
|
||||||
|
|
||||||
import {StaticReflectorHost} from './static_reflector';
|
import {StaticReflectorHost} from './static_reflector';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
|
import {AotSummaryResolverHost} from './summary_resolver';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 StaticReflectorHost, ImportResolver,
|
||||||
|
AotSummaryResolverHost {
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
loadResource(path: string): Promise<string>;
|
loadResource(path: string): Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
/**
|
||||||
|
* @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 class GeneratedFile {
|
||||||
|
constructor(public srcFileUrl: string, public genFileUrl: string, public source: string) {}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/**
|
||||||
|
* @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 {CompileIdentifierMetadata, CompileTypeMetadata, CompileTypeSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||||
|
import {SummaryResolver} from '../summary_resolver';
|
||||||
|
|
||||||
|
import {GeneratedFile} from './generated_file';
|
||||||
|
import {StaticReflector} from './static_reflector';
|
||||||
|
import {StaticSymbol, isStaticSymbol} from './static_symbol';
|
||||||
|
import {filterFileByPatterns} from './utils';
|
||||||
|
|
||||||
|
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
|
|
||||||
|
export interface AotSummaryResolverHost {
|
||||||
|
/**
|
||||||
|
* Loads an NgModule/Directive/Pipe summary file
|
||||||
|
*/
|
||||||
|
loadSummary(filePath: string): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the output file path of a source file.
|
||||||
|
* E.g.
|
||||||
|
* `some_file.ts` -> `some_file.d.ts`
|
||||||
|
*/
|
||||||
|
getOutputFileName(sourceFilePath: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AotSummaryResolverOptions {
|
||||||
|
includeFilePattern?: RegExp;
|
||||||
|
excludeFilePattern?: RegExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AotSummaryResolver implements SummaryResolver {
|
||||||
|
private summaryCache: {[srcFilePath: string]: CompileTypeSummary[]} = {};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private host: AotSummaryResolverHost, private staticReflector: StaticReflector,
|
||||||
|
private options: AotSummaryResolverOptions) {}
|
||||||
|
|
||||||
|
serializeSummaries(srcFileUrl: string, summaries: CompileTypeSummary[]): GeneratedFile {
|
||||||
|
const jsonReplacer = (key: string, value: any) => {
|
||||||
|
if (key === 'reference' && isStaticSymbol(value)) {
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
return new GeneratedFile(
|
||||||
|
srcFileUrl, summaryFileName(srcFileUrl), JSON.stringify(summaries, jsonReplacer));
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveSummary(staticSymbol: StaticSymbol): any {
|
||||||
|
const filePath = staticSymbol.filePath;
|
||||||
|
const name = staticSymbol.name;
|
||||||
|
if (!filterFileByPatterns(filePath, this.options)) {
|
||||||
|
let summaries = this.summaryCache[filePath];
|
||||||
|
const summaryFilePath = summaryFileName(filePath);
|
||||||
|
if (!summaries) {
|
||||||
|
try {
|
||||||
|
const jsonReviver = (key: string, value: any) => {
|
||||||
|
if (key === 'reference' && value && value['__symbolic__'] === 'symbol') {
|
||||||
|
// Note: We can't use staticReflector.findDeclaration here:
|
||||||
|
// Summary files can contain symbols of transitive compilation units
|
||||||
|
// (via the providers), and findDeclaration needs .metadata.json / .d.ts files,
|
||||||
|
// but we don't want to depend on these for transitive dependencies.
|
||||||
|
return this.staticReflector.getStaticSymbol(
|
||||||
|
value['path'], value['name'], value['members']);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
summaries = JSON.parse(this.host.loadSummary(summaryFilePath), jsonReviver);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error loading summary file ${summaryFilePath}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
this.summaryCache[filePath] = summaries;
|
||||||
|
}
|
||||||
|
const result = summaries.find((summary) => summary.type.reference === staticSymbol);
|
||||||
|
if (!result) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find the symbol ${name} in the summary file ${summaryFilePath}!`);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function summaryFileName(fileName: string): string {
|
||||||
|
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
|
||||||
|
return `${fileNameWithoutSuffix}.ngsummary.json`;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
|
@ -123,6 +123,8 @@ export interface CompileSummary {
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CompileTypeSummary extends CompileSummary { type: CompileTypeMetadata; }
|
||||||
|
|
||||||
export interface CompileDiDependencyMetadata {
|
export interface CompileDiDependencyMetadata {
|
||||||
isAttribute?: boolean;
|
isAttribute?: boolean;
|
||||||
isSelf?: boolean;
|
isSelf?: boolean;
|
||||||
|
@ -262,7 +264,7 @@ export class CompileTemplateMetadata {
|
||||||
|
|
||||||
// Note: This should only use interfaces as nested data types
|
// Note: This should only use interfaces as nested data types
|
||||||
// as we need to be able to serialize this from/to JSON!
|
// as we need to be able to serialize this from/to JSON!
|
||||||
export interface CompileDirectiveSummary extends CompileSummary {
|
export interface CompileDirectiveSummary extends CompileTypeSummary {
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
isComponent: boolean;
|
isComponent: boolean;
|
||||||
|
@ -470,7 +472,7 @@ export function createHostComponentMeta(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompilePipeSummary extends CompileSummary {
|
export interface CompilePipeSummary extends CompileTypeSummary {
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -499,7 +501,7 @@ export class CompilePipeMetadata {
|
||||||
|
|
||||||
// Note: This should only use interfaces as nested data types
|
// Note: This should only use interfaces as nested data types
|
||||||
// as we need to be able to serialize this from/to JSON!
|
// as we need to be able to serialize this from/to JSON!
|
||||||
export interface CompileNgModuleSummary {
|
export interface CompileNgModuleSummary extends CompileTypeSummary {
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
|
|
||||||
|
@ -510,9 +512,9 @@ export interface CompileNgModuleSummary {
|
||||||
// Note: This is transitive.
|
// Note: This is transitive.
|
||||||
entryComponents: CompileIdentifierMetadata[];
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
// Note: This is transitive.
|
// Note: This is transitive.
|
||||||
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[],
|
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[];
|
||||||
// Note: This is transitive.
|
// Note: This is transitive.
|
||||||
modules: CompileTypeMetadata[];
|
modules: CompileTypeMetadata[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,6 +15,7 @@ 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, StaticReflectorHost} from '../aot/static_reflector';
|
||||||
|
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';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
|
@ -41,7 +42,7 @@ export interface ExtractorOptions {
|
||||||
* 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 {
|
export interface ExtractorHost extends StaticReflectorHost, AotSummaryResolverHost {
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
|
@ -110,7 +111,8 @@ 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), elementSchemaRegistry, normalizer, staticReflector);
|
new PipeResolver(staticReflector), new AotSummaryResolver(host, staticReflector, options),
|
||||||
|
elementSchemaRegistry, normalizer, staticReflector);
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
// TODO(vicb): implicit tags & attributes
|
||||||
const messageBundle = new MessageBundle(htmlParser, [], {});
|
const messageBundle = new MessageBundle(htmlParser, [], {});
|
||||||
|
|
|
@ -101,18 +101,14 @@ export class JitCompiler implements Compiler {
|
||||||
|
|
||||||
private _loadModules(mainModule: any, isSync: boolean): Promise<any> {
|
private _loadModules(mainModule: any, isSync: boolean): Promise<any> {
|
||||||
const loadingPromises: Promise<any>[] = [];
|
const loadingPromises: Promise<any>[] = [];
|
||||||
const {ngModule, loading} =
|
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
|
||||||
this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(mainModule, isSync);
|
|
||||||
loadingPromises.push(loading);
|
|
||||||
// Note: the loadingPromise for a module only includes the loading of the exported directives
|
// Note: the loadingPromise for a module only includes the loading of the exported directives
|
||||||
// of imported modules.
|
// of imported modules.
|
||||||
// However, for runtime compilation, we want to transitively compile all modules,
|
// However, for runtime compilation, we want to transitively compile all modules,
|
||||||
// so we also need to call loadNgModuleMetadata for all nested modules.
|
// so we also need to call loadNgModuleMetadata for all nested modules.
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
||||||
loadingPromises.push(
|
loadingPromises.push(this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||||
this._metadataResolver
|
localModuleMeta.reference, isSync));
|
||||||
.loadNgModuleDirectiveAndPipeMetadata(localModuleMeta.reference, isSync)
|
|
||||||
.loading);
|
|
||||||
});
|
});
|
||||||
return Promise.all(loadingPromises);
|
return Promise.all(loadingPromises);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {ResourceLoader} from '../resource_loader';
|
||||||
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
||||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
import {StyleCompiler} from '../style_compiler';
|
import {StyleCompiler} from '../style_compiler';
|
||||||
|
import {SummaryResolver} from '../summary_resolver';
|
||||||
import {TemplateParser} from '../template_parser/template_parser';
|
import {TemplateParser} from '../template_parser/template_parser';
|
||||||
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
|
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
|
||||||
import {ViewCompiler} from '../view_compiler/view_compiler';
|
import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
@ -46,6 +47,7 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
||||||
{provide: Reflector, useValue: reflector},
|
{provide: Reflector, useValue: reflector},
|
||||||
{provide: ReflectorReader, useExisting: Reflector},
|
{provide: ReflectorReader, useExisting: Reflector},
|
||||||
{provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
|
{provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
|
||||||
|
SummaryResolver,
|
||||||
Console,
|
Console,
|
||||||
Lexer,
|
Lexer,
|
||||||
Parser,
|
Parser,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {NgModuleResolver} from './ng_module_resolver';
|
||||||
import {PipeResolver} from './pipe_resolver';
|
import {PipeResolver} from './pipe_resolver';
|
||||||
import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
|
import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
|
||||||
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||||
|
import {SummaryResolver} from './summary_resolver';
|
||||||
import {getUrlScheme} from './url_resolver';
|
import {getUrlScheme} from './url_resolver';
|
||||||
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './util';
|
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './util';
|
||||||
|
|
||||||
|
@ -39,12 +40,14 @@ export class CompileMetadataResolver {
|
||||||
private _directiveSummaryCache = new Map<Type<any>, cpl.CompileDirectiveSummary>();
|
private _directiveSummaryCache = new Map<Type<any>, cpl.CompileDirectiveSummary>();
|
||||||
private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>();
|
private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>();
|
||||||
private _pipeSummaryCache = new Map<Type<any>, cpl.CompilePipeSummary>();
|
private _pipeSummaryCache = new Map<Type<any>, cpl.CompilePipeSummary>();
|
||||||
|
private _ngModuleSummaryCache = new Map<Type<any>, cpl.CompileNgModuleSummary>();
|
||||||
private _ngModuleCache = new Map<Type<any>, cpl.CompileNgModuleMetadata>();
|
private _ngModuleCache = new Map<Type<any>, cpl.CompileNgModuleMetadata>();
|
||||||
private _ngModuleOfTypes = new Map<Type<any>, Type<any>>();
|
private _ngModuleOfTypes = new Map<Type<any>, Type<any>>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
|
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
|
||||||
private _pipeResolver: PipeResolver, private _schemaRegistry: ElementSchemaRegistry,
|
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver,
|
||||||
|
private _schemaRegistry: ElementSchemaRegistry,
|
||||||
private _directiveNormalizer: DirectiveNormalizer,
|
private _directiveNormalizer: DirectiveNormalizer,
|
||||||
private _reflector: ReflectorReader = reflector) {}
|
private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
|
@ -54,6 +57,7 @@ export class CompileMetadataResolver {
|
||||||
this._directiveSummaryCache.delete(type);
|
this._directiveSummaryCache.delete(type);
|
||||||
this._pipeCache.delete(type);
|
this._pipeCache.delete(type);
|
||||||
this._pipeSummaryCache.delete(type);
|
this._pipeSummaryCache.delete(type);
|
||||||
|
this._ngModuleSummaryCache.delete(type);
|
||||||
this._ngModuleOfTypes.delete(type);
|
this._ngModuleOfTypes.delete(type);
|
||||||
// Clear all of the NgModule as they contain transitive information!
|
// Clear all of the NgModule as they contain transitive information!
|
||||||
this._ngModuleCache.clear();
|
this._ngModuleCache.clear();
|
||||||
|
@ -69,6 +73,7 @@ export class CompileMetadataResolver {
|
||||||
this._pipeSummaryCache.clear();
|
this._pipeSummaryCache.clear();
|
||||||
this._ngModuleCache.clear();
|
this._ngModuleCache.clear();
|
||||||
this._ngModuleOfTypes.clear();
|
this._ngModuleOfTypes.clear();
|
||||||
|
this._ngModuleSummaryCache.clear();
|
||||||
this._directiveNormalizer.clearCache();
|
this._directiveNormalizer.clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +131,7 @@ export class CompileMetadataResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): () => Promise<any> {
|
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> {
|
||||||
if (this._directiveCache.has(directiveType)) {
|
if (this._directiveCache.has(directiveType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -176,7 +181,7 @@ export class CompileMetadataResolver {
|
||||||
if (isSync) {
|
if (isSync) {
|
||||||
throw new ComponentStillLoadingError(directiveType);
|
throw new ComponentStillLoadingError(directiveType);
|
||||||
}
|
}
|
||||||
return () => templateMeta.asyncResult.then(createDirectiveMetadata);
|
return templateMeta.asyncResult.then(createDirectiveMetadata);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// directive
|
// directive
|
||||||
|
@ -288,10 +293,15 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveSummary(dirType: any): cpl.CompileDirectiveSummary {
|
getDirectiveSummary(dirType: any): cpl.CompileDirectiveSummary {
|
||||||
const dirSummary = this._directiveSummaryCache.get(dirType);
|
let dirSummary = this._directiveSummaryCache.get(dirType);
|
||||||
if (!dirSummary) {
|
if (!dirSummary) {
|
||||||
throw new Error(
|
dirSummary = <cpl.CompileDirectiveSummary>this._summaryResolver.resolveSummary(dirType);
|
||||||
`Illegal state: getDirectiveSummary can only be called after loadNgModuleMetadata for a module that imports it. Directive ${stringify(dirType)}.`);
|
if (dirSummary) {
|
||||||
|
this._directiveSummaryCache.set(dirType, dirSummary);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Illegal state: Could not load the summary for directive ${stringify(dirType)}.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return dirSummary;
|
return dirSummary;
|
||||||
}
|
}
|
||||||
|
@ -300,33 +310,38 @@ export class CompileMetadataResolver {
|
||||||
|
|
||||||
isPipe(type: any) { return this._pipeResolver.isPipe(type); }
|
isPipe(type: any) { return this._pipeResolver.isPipe(type); }
|
||||||
|
|
||||||
private _getNgModuleSummary(moduleType: any): cpl.CompileNgModuleSummary {
|
getNgModuleSummary(moduleType: any): cpl.CompileNgModuleSummary {
|
||||||
// TODO(tbosch): add logic to read summary files!
|
let moduleSummary = this._ngModuleSummaryCache.get(moduleType);
|
||||||
// - needs to add directive / pipe summaries to this._directiveSummaryCache /
|
if (!moduleSummary) {
|
||||||
// this._pipeSummaryCache as well!
|
moduleSummary = <cpl.CompileNgModuleSummary>this._summaryResolver.resolveSummary(moduleType);
|
||||||
const moduleMeta = this.getNgModuleMetadata(moduleType, false);
|
if (!moduleSummary) {
|
||||||
return moduleMeta ? moduleMeta.toSummary() : null;
|
const moduleMeta = this.getNgModuleMetadata(moduleType, false);
|
||||||
|
moduleSummary = moduleMeta ? moduleMeta.toSummary() : null;
|
||||||
|
}
|
||||||
|
if (moduleSummary) {
|
||||||
|
this._ngModuleSummaryCache.set(moduleType, moduleSummary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return moduleSummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an NgModule and all of its directives. This includes loading the exported directives of
|
* Loads the declared directives and pipes of an NgModule.
|
||||||
* imported modules,
|
|
||||||
* but not private directives of imported modules.
|
|
||||||
*/
|
*/
|
||||||
loadNgModuleDirectiveAndPipeMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
|
loadNgModuleDirectiveAndPipeMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
|
||||||
{ngModule: cpl.CompileNgModuleMetadata, loading: Promise<any>} {
|
Promise<any> {
|
||||||
const ngModule = this.getNgModuleMetadata(moduleType, throwIfNotFound);
|
const ngModule = this.getNgModuleMetadata(moduleType, throwIfNotFound);
|
||||||
let loading: Promise<any>;
|
const loading: Promise<any>[] = [];
|
||||||
if (ngModule) {
|
if (ngModule) {
|
||||||
loading = Promise.all([
|
ngModule.declaredDirectives.forEach((id) => {
|
||||||
...ngModule.transitiveModule.directives.map(
|
const promise = this._loadDirectiveMetadata(id.reference, isSync);
|
||||||
(id) => this._loadDirectiveMetadata(id.reference, isSync)),
|
if (promise) {
|
||||||
...ngModule.transitiveModule.pipes.map((id) => this._loadPipeMetadata(id.reference)),
|
loading.push(promise);
|
||||||
]);
|
}
|
||||||
} else {
|
});
|
||||||
loading = Promise.resolve(null);
|
ngModule.declaredPipes.forEach((id) => this._loadPipeMetadata(id.reference));
|
||||||
}
|
}
|
||||||
return {ngModule, loading};
|
return Promise.all(loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNgModuleMetadata(moduleType: any, throwIfNotFound = true): cpl.CompileNgModuleMetadata {
|
getNgModuleMetadata(moduleType: any, throwIfNotFound = true): cpl.CompileNgModuleMetadata {
|
||||||
|
@ -365,7 +380,7 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importedModuleType) {
|
if (importedModuleType) {
|
||||||
const importedModuleSummary = this._getNgModuleSummary(importedModuleType);
|
const importedModuleSummary = this.getNgModuleSummary(importedModuleType);
|
||||||
if (!importedModuleSummary) {
|
if (!importedModuleSummary) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
|
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
|
||||||
|
@ -384,7 +399,7 @@ export class CompileMetadataResolver {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`);
|
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`);
|
||||||
}
|
}
|
||||||
const exportedModuleSummary = this._getNgModuleSummary(exportedType);
|
const exportedModuleSummary = this.getNgModuleSummary(exportedType);
|
||||||
if (exportedModuleSummary) {
|
if (exportedModuleSummary) {
|
||||||
exportedModules.push(exportedModuleSummary);
|
exportedModules.push(exportedModuleSummary);
|
||||||
} else {
|
} else {
|
||||||
|
@ -543,6 +558,7 @@ export class CompileMetadataResolver {
|
||||||
entryComponents.push(comp);
|
entryComponents.push(comp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const addedTokens = new Set<any>();
|
||||||
modSummary.providers.forEach((entry) => {
|
modSummary.providers.forEach((entry) => {
|
||||||
const tokenRef = cpl.tokenReference(entry.provider.token);
|
const tokenRef = cpl.tokenReference(entry.provider.token);
|
||||||
let prevModules = modulesByToken.get(tokenRef);
|
let prevModules = modulesByToken.get(tokenRef);
|
||||||
|
@ -551,8 +567,11 @@ export class CompileMetadataResolver {
|
||||||
modulesByToken.set(tokenRef, prevModules);
|
modulesByToken.set(tokenRef, prevModules);
|
||||||
}
|
}
|
||||||
const moduleRef = entry.module.reference;
|
const moduleRef = entry.module.reference;
|
||||||
if (!prevModules.has(moduleRef)) {
|
// Note: the providers of one module may still contain multiple providers
|
||||||
|
// per token (e.g. for multi providers), and we need to preserve these.
|
||||||
|
if (addedTokens.has(tokenRef) || !prevModules.has(moduleRef)) {
|
||||||
prevModules.add(moduleRef);
|
prevModules.add(moduleRef);
|
||||||
|
addedTokens.add(tokenRef);
|
||||||
providers.push(entry);
|
providers.push(entry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -575,7 +594,7 @@ export class CompileMetadataResolver {
|
||||||
visitedModules.add(ngModule.type.reference);
|
visitedModules.add(ngModule.type.reference);
|
||||||
targetModules.push(ngModule);
|
targetModules.push(ngModule);
|
||||||
this._getTransitiveExportedModules(
|
this._getTransitiveExportedModules(
|
||||||
ngModule.exportedModules.map(id => this._getNgModuleSummary(id.reference)),
|
ngModule.exportedModules.map(id => this.getNgModuleSummary(id.reference)),
|
||||||
targetModules, visitedModules);
|
targetModules, visitedModules);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -617,10 +636,15 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
getPipeSummary(pipeType: any): cpl.CompilePipeSummary {
|
getPipeSummary(pipeType: any): cpl.CompilePipeSummary {
|
||||||
const pipeSummary = this._pipeSummaryCache.get(pipeType);
|
let pipeSummary = this._pipeSummaryCache.get(pipeType);
|
||||||
if (!pipeSummary) {
|
if (!pipeSummary) {
|
||||||
throw new Error(
|
pipeSummary = <cpl.CompilePipeSummary>this._summaryResolver.resolveSummary(pipeType);
|
||||||
`Illegal state: getPipeSummary can only be called after loadNgModuleMetadata for a module that imports it. Pipe ${stringify(pipeType)}.`);
|
if (pipeSummary) {
|
||||||
|
this._pipeSummaryCache.set(pipeType, pipeSummary);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Illegal state: Could not load the summary for pipe ${stringify(pipeType)}.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return pipeSummary;
|
return pipeSummary;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* @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 {Injectable} from '@angular/core';
|
||||||
|
import {CompileSummary} from './compile_metadata';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SummaryResolver {
|
||||||
|
resolveSummary(reference: any): CompileSummary { return null; }
|
||||||
|
}
|
|
@ -694,7 +694,7 @@ describe('StaticReflector', () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
class MockStaticReflectorHost implements StaticReflectorHost {
|
export class MockStaticReflectorHost implements StaticReflectorHost {
|
||||||
private collector = new MetadataCollector();
|
private collector = new MetadataCollector();
|
||||||
|
|
||||||
constructor(private data: {[key: string]: any}) {}
|
constructor(private data: {[key: string]: any}) {}
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/**
|
||||||
|
* @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, CompileTypeSummary, StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import {MockStaticReflectorHost} from './static_reflector_spec';
|
||||||
|
|
||||||
|
const EXT = /\.ts$|.d.ts$/;
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('AotSummaryResolver', () => {
|
||||||
|
let resolver: AotSummaryResolver;
|
||||||
|
let staticReflector: StaticReflector;
|
||||||
|
|
||||||
|
function init(summaries: {[filePath: string]: string} = {}) {
|
||||||
|
// Note: We don't give the static reflector metadata files,
|
||||||
|
// so that we can test that we can deserialize summary files
|
||||||
|
// 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', () => {
|
||||||
|
init();
|
||||||
|
expect(resolver.serializeSummaries('a.ts', []).genFileUrl).toBe('a.ngsummary.json');
|
||||||
|
expect(resolver.serializeSummaries('a.d.ts', []).genFileUrl).toBe('a.ngsummary.json');
|
||||||
|
expect(resolver.serializeSummaries('a.js', []).genFileUrl).toBe('a.ngsummary.json');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize plain data', () => {
|
||||||
|
init();
|
||||||
|
const data = <any>[{a: 'b'}];
|
||||||
|
expect(JSON.parse(resolver.serializeSummaries('someSourceFile', data).source)).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize summary for .ts files and deserialize based on .d.ts files', () => {
|
||||||
|
init();
|
||||||
|
const serializedData = resolver.serializeSummaries(
|
||||||
|
'/tmp/some_class.ts', [{
|
||||||
|
isSummary: true,
|
||||||
|
type: {
|
||||||
|
reference: staticReflector.getStaticSymbol('/tmp/some_class.ts', 'SomeClass'),
|
||||||
|
diDeps: [],
|
||||||
|
lifecycleHooks: []
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
// Note: this creates a new staticReflector!
|
||||||
|
init({[serializedData.genFileUrl]: serializedData.source});
|
||||||
|
|
||||||
|
expect(resolver.resolveSummary(
|
||||||
|
staticReflector.getStaticSymbol('/tmp/some_class.d.ts', 'SomeClass')))
|
||||||
|
.toEqual({
|
||||||
|
isSummary: true,
|
||||||
|
type: {
|
||||||
|
reference: staticReflector.getStaticSymbol('/tmp/some_class.d.ts', 'SomeClass'),
|
||||||
|
diDeps: [],
|
||||||
|
lifecycleHooks: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileNameToModuleName(fileName: string): string {
|
||||||
|
return './' + path.basename(fileName).replace(EXT, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOutputFileName(sourceFileName: string): string {
|
||||||
|
return sourceFileName.replace(EXT, '') + '.d.ts';
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,7 +84,7 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceLoader.when('someTemplateUrl', 'someTemplate');
|
resourceLoader.when('someTemplateUrl', 'someTemplate');
|
||||||
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).loading.then(() => {
|
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).then(() => {
|
||||||
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
|
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
|
||||||
expect(meta.selector).toEqual('someSelector');
|
expect(meta.selector).toEqual('someSelector');
|
||||||
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
|
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
|
||||||
|
@ -94,29 +94,6 @@ export function main() {
|
||||||
resourceLoader.flush();
|
resourceLoader.flush();
|
||||||
})));
|
})));
|
||||||
|
|
||||||
it('should wait for external resources of imported modules',
|
|
||||||
async(inject(
|
|
||||||
[CompileMetadataResolver, ResourceLoader],
|
|
||||||
(resolver: CompileMetadataResolver, resourceLoader: MockResourceLoader) => {
|
|
||||||
@NgModule({
|
|
||||||
declarations: [ComponentWithExternalResources],
|
|
||||||
exports: [ComponentWithExternalResources]
|
|
||||||
})
|
|
||||||
class SomeImportedModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({imports: [SomeImportedModule]})
|
|
||||||
class SomeModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceLoader.when('someTemplateUrl', 'someTemplate');
|
|
||||||
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).loading.then(() => {
|
|
||||||
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
|
|
||||||
expect(meta.selector).toEqual('someSelector');
|
|
||||||
});
|
|
||||||
resourceLoader.flush();
|
|
||||||
})));
|
|
||||||
|
|
||||||
it('should use `./` as base url for templates during runtime compilation if no moduleId is given',
|
it('should use `./` as base url for templates during runtime compilation if no moduleId is given',
|
||||||
async(inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
async(inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
@Component({selector: 'someComponent', templateUrl: 'someUrl'})
|
@Component({selector: 'someComponent', templateUrl: 'someUrl'})
|
||||||
|
@ -128,7 +105,7 @@ export function main() {
|
||||||
class SomeModule {
|
class SomeModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).loading.then(() => {
|
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).then(() => {
|
||||||
const value: string =
|
const value: string =
|
||||||
resolver.getDirectiveMetadata(ComponentWithoutModuleId).template.templateUrl;
|
resolver.getDirectiveMetadata(ComponentWithoutModuleId).template.templateUrl;
|
||||||
const expectedEndValue = './someUrl';
|
const expectedEndValue = './someUrl';
|
||||||
|
@ -329,7 +306,7 @@ export function main() {
|
||||||
class MyModule {
|
class MyModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
const modMeta = resolver.loadNgModuleDirectiveAndPipeMetadata(MyModule, true).ngModule;
|
const modMeta = resolver.getNgModuleMetadata(MyModule);
|
||||||
expect(modMeta.declaredDirectives.length).toBe(1);
|
expect(modMeta.declaredDirectives.length).toBe(1);
|
||||||
expect(modMeta.declaredDirectives[0].reference).toBe(MyComp);
|
expect(modMeta.declaredDirectives[0].reference).toBe(MyComp);
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {NgModuleResolver} from '@angular/compiler/src/ng_module_resolver';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||||
import {ResourceLoader} from '@angular/compiler/src/resource_loader';
|
import {ResourceLoader} from '@angular/compiler/src/resource_loader';
|
||||||
import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry';
|
||||||
|
import {SummaryResolver} from '@angular/compiler/src/summary_resolver';
|
||||||
import {UrlResolver} from '@angular/compiler/src/url_resolver';
|
import {UrlResolver} from '@angular/compiler/src/url_resolver';
|
||||||
import {Type, ViewEncapsulation} from '@angular/core';
|
import {Type, ViewEncapsulation} from '@angular/core';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
@ -124,8 +125,8 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
new DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
new DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
||||||
|
|
||||||
result = this._resolver = new CompileMetadataResolver(
|
result = this._resolver = new CompileMetadataResolver(
|
||||||
moduleResolver, directiveResolver, pipeResolver, elementSchemaRegistry,
|
moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(),
|
||||||
directiveNormalizer, this.reflector);
|
elementSchemaRegistry, directiveNormalizer, this.reflector);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,8 @@ cp -v package.json $TMP
|
||||||
./node_modules/.bin/ng-xi18n -p tsconfig-build.json --i18nFormat=xlf
|
./node_modules/.bin/ng-xi18n -p tsconfig-build.json --i18nFormat=xlf
|
||||||
./node_modules/.bin/ng-xi18n -p tsconfig-build.json --i18nFormat=xmb
|
./node_modules/.bin/ng-xi18n -p tsconfig-build.json --i18nFormat=xmb
|
||||||
|
|
||||||
|
node test/test_summaries.js
|
||||||
|
|
||||||
./node_modules/.bin/jasmine init
|
./node_modules/.bin/jasmine init
|
||||||
# Run compiler-cli integration tests in node
|
# Run compiler-cli integration tests in node
|
||||||
./node_modules/.bin/webpack ./webpack.config.js
|
./node_modules/.bin/webpack ./webpack.config.js
|
||||||
|
|
Loading…
Reference in New Issue