feat: add .ngsummary.ts files to support AOT unit tests
Design doc: https://docs.google.com/document/d/1VmTkz0EbEVSWfEEWEvQ5sXyQXSCvtMOw4t7pKU-jOwc/edit?usp=sharing
This commit is contained in:
parent
2714644528
commit
547c363473
|
@ -0,0 +1 @@
|
||||||
|
Hello world!
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* @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 {Component, Directive, Injectable, NgModule, Pipe} from '@angular/core';
|
||||||
|
|
||||||
|
const instances = new Map<any, Base>();
|
||||||
|
|
||||||
|
export function expectInstanceCreated(type: any) {
|
||||||
|
const instance = instances.get(type) !;
|
||||||
|
expect(instance).toBeDefined();
|
||||||
|
expect(instance.dep instanceof SomeDep).toBe(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SomeDep {}
|
||||||
|
|
||||||
|
export class Base {
|
||||||
|
constructor(public dep: SomeDep) { instances.set(Object.getPrototypeOf(this).constructor, this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({templateUrl: './jit_summaries.html'})
|
||||||
|
export class SomePrivateComponent extends Base {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({templateUrl: './jit_summaries.html'})
|
||||||
|
export class SomePublicComponent extends Base {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[someDir]'})
|
||||||
|
export class SomeDirective extends Base {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Pipe({name: 'somePipe'})
|
||||||
|
export class SomePipe extends Base {
|
||||||
|
transform(value: any) { return value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SomeService extends Base {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [SomePublicComponent, SomePrivateComponent, SomeDirective, SomePipe],
|
||||||
|
exports: [SomeDirective, SomePipe, SomePublicComponent],
|
||||||
|
providers: [SomeService]
|
||||||
|
})
|
||||||
|
export class SomeModule extends Base {
|
||||||
|
}
|
|
@ -28,6 +28,9 @@ import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomeLibM
|
||||||
import {CompWithNgContent, ProjectingComp} from './projection';
|
import {CompWithNgContent, ProjectingComp} from './projection';
|
||||||
import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries';
|
import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries';
|
||||||
|
|
||||||
|
// Adding an export here so that TypeScript compiles the file as well
|
||||||
|
export {SomeModule as JitSummariesSomeModule} from './jit_summaries';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AnimateCmp,
|
AnimateCmp,
|
||||||
|
|
|
@ -16,4 +16,5 @@ import './i18n_spec';
|
||||||
import './ng_module_spec';
|
import './ng_module_spec';
|
||||||
import './projection_spec';
|
import './projection_spec';
|
||||||
import './query_spec';
|
import './query_spec';
|
||||||
import './source_map_spec';
|
import './source_map_spec';
|
||||||
|
import './jit_summaries_spec';
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
* @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 {Component} from '@angular/core';
|
||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
import {ServerTestingModule, platformServerTesting} from '@angular/platform-server/testing';
|
||||||
|
|
||||||
|
import {SomeDep, SomeDirective, SomeModule, SomePipe, SomePrivateComponent, SomeService, expectInstanceCreated} from '../src/jit_summaries';
|
||||||
|
import {SomeModuleNgSummary} from '../src/jit_summaries.ngsummary';
|
||||||
|
|
||||||
|
describe('Jit Summaries', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.initTestEnvironment(ServerTestingModule, platformServerTesting(), SomeModuleNgSummary);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => { TestBed.resetTestEnvironment(); });
|
||||||
|
|
||||||
|
it('should use directive metadata from summaries', () => {
|
||||||
|
@Component({template: '<div someDir></div>'})
|
||||||
|
class TestComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({providers: [SomeDep], declarations: [TestComp, SomeDirective]})
|
||||||
|
.createComponent(TestComp);
|
||||||
|
expectInstanceCreated(SomeDirective);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use pipe metadata from summaries', () => {
|
||||||
|
@Component({template: '{{1 | somePipe}}'})
|
||||||
|
class TestComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({providers: [SomeDep], declarations: [TestComp, SomePipe]})
|
||||||
|
.createComponent(TestComp);
|
||||||
|
expectInstanceCreated(SomePipe);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use Service metadata from summaries', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [SomeService, SomeDep],
|
||||||
|
});
|
||||||
|
TestBed.get(SomeService);
|
||||||
|
expectInstanceCreated(SomeService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use NgModule metadata from summaries', () => {
|
||||||
|
@Component({template: '<div someDir>{{1 | somePipe}}</div>'})
|
||||||
|
class TestComp {
|
||||||
|
constructor(service: SomeService) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed
|
||||||
|
.configureTestingModule(
|
||||||
|
{providers: [SomeDep], declarations: [TestComp], imports: [SomeModule]})
|
||||||
|
.createComponent(TestComp);
|
||||||
|
|
||||||
|
expectInstanceCreated(SomeModule);
|
||||||
|
expectInstanceCreated(SomeDirective);
|
||||||
|
expectInstanceCreated(SomePipe);
|
||||||
|
expectInstanceCreated(SomeService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to create private components from imported NgModule summaries', () => {
|
||||||
|
TestBed.configureTestingModule({providers: [SomeDep], imports: [SomeModule]})
|
||||||
|
.createComponent(SomePrivateComponent);
|
||||||
|
expectInstanceCreated(SomePrivateComponent);
|
||||||
|
});
|
||||||
|
});
|
|
@ -8,14 +8,14 @@
|
||||||
|
|
||||||
import {NgModuleRef} from '@angular/core';
|
import {NgModuleRef} from '@angular/core';
|
||||||
import {ComponentFixture} from '@angular/core/testing';
|
import {ComponentFixture} from '@angular/core/testing';
|
||||||
import {platformServer} from '@angular/platform-server';
|
import {platformServerTesting} from '@angular/platform-server/testing';
|
||||||
|
|
||||||
import {MainModule} from '../src/module';
|
import {MainModule} from '../src/module';
|
||||||
import {MainModuleNgFactory} from '../src/module.ngfactory';
|
import {MainModuleNgFactory} from '../src/module.ngfactory';
|
||||||
|
|
||||||
let mainModuleRef: NgModuleRef<MainModule> = null !;
|
let mainModuleRef: NgModuleRef<MainModule> = null !;
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
platformServer().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef: any) => {
|
platformServerTesting().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef: any) => {
|
||||||
mainModuleRef = moduleRef;
|
mainModuleRef = moduleRef;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,9 +15,9 @@ import * as ts from 'typescript';
|
||||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
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|ngsummary)$/;
|
||||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$|\.ngsummary\.ts$/;
|
||||||
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$|\.ngsummary\.ts$/;
|
||||||
const SHALLOW_IMPORT = /^((\w|-)+|(@(\w|-)+(\/(\w|-)+)+))$/;
|
const SHALLOW_IMPORT = /^((\w|-)+|(@(\w|-)+(\/(\w|-)+)+))$/;
|
||||||
|
|
||||||
export interface CompilerHostContext extends ts.ModuleResolutionHost {
|
export interface CompilerHostContext extends ts.ModuleResolutionHost {
|
||||||
|
|
|
@ -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, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTypeSummary, componentFactoryName, createHostComponentMeta, flatten, identifierName, sourceUrl, templateSourceUrl} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileNgModuleSummary, CompilePipeMetadata, CompileProviderMetadata, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, componentFactoryName, createHostComponentMeta, flatten, identifierName, sourceUrl, templateSourceUrl} from '../compile_metadata';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
|
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
|
||||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||||
|
@ -22,9 +22,9 @@ import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
import {AotCompilerHost} from './compiler_host';
|
import {AotCompilerHost} from './compiler_host';
|
||||||
import {GeneratedFile} from './generated_file';
|
import {GeneratedFile} from './generated_file';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
import {StaticSymbolResolver} from './static_symbol_resolver';
|
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
import {serializeSummaries} from './summary_serializer';
|
import {serializeSummaries} from './summary_serializer';
|
||||||
import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName} from './util';
|
import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName, summaryForJitFileName, summaryForJitName} from './util';
|
||||||
|
|
||||||
export class AotCompiler {
|
export class AotCompiler {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -59,12 +59,12 @@ export class AotCompiler {
|
||||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
|
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
|
||||||
injectables: StaticSymbol[]): GeneratedFile[] {
|
injectables: StaticSymbol[]): GeneratedFile[] {
|
||||||
const fileSuffix = splitTypescriptSuffix(srcFileUrl)[1];
|
const fileSuffix = splitTypescriptSuffix(srcFileUrl, true)[1];
|
||||||
const statements: o.Statement[] = [];
|
const statements: o.Statement[] = [];
|
||||||
const exportedVars: string[] = [];
|
const exportedVars: string[] = [];
|
||||||
const generatedFiles: GeneratedFile[] = [];
|
const generatedFiles: GeneratedFile[] = [];
|
||||||
|
|
||||||
generatedFiles.push(this._createSummary(
|
generatedFiles.push(...this._createSummary(
|
||||||
srcFileUrl, directives, pipes, ngModules, injectables, statements, exportedVars));
|
srcFileUrl, directives, pipes, ngModules, injectables, statements, exportedVars));
|
||||||
|
|
||||||
// compile all ng modules
|
// compile all ng modules
|
||||||
|
@ -101,7 +101,7 @@ export class AotCompiler {
|
||||||
});
|
});
|
||||||
if (statements.length > 0) {
|
if (statements.length > 0) {
|
||||||
const srcModule = this._codegenSourceModule(
|
const srcModule = this._codegenSourceModule(
|
||||||
srcFileUrl, ngfactoryFilePath(srcFileUrl), statements, exportedVars);
|
srcFileUrl, ngfactoryFilePath(srcFileUrl, true), statements, exportedVars);
|
||||||
generatedFiles.unshift(srcModule);
|
generatedFiles.unshift(srcModule);
|
||||||
}
|
}
|
||||||
return generatedFiles;
|
return generatedFiles;
|
||||||
|
@ -110,23 +110,45 @@ export class AotCompiler {
|
||||||
private _createSummary(
|
private _createSummary(
|
||||||
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
|
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
|
||||||
ngModules: StaticSymbol[], injectables: StaticSymbol[], targetStatements: o.Statement[],
|
ngModules: StaticSymbol[], injectables: StaticSymbol[], targetStatements: o.Statement[],
|
||||||
targetExportedVars: string[]): GeneratedFile {
|
targetExportedVars: string[]): GeneratedFile[] {
|
||||||
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
|
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
|
||||||
.map(symbol => this._symbolResolver.resolveSymbol(symbol));
|
.map(symbol => this._symbolResolver.resolveSymbol(symbol));
|
||||||
const typeSummaries: CompileTypeSummary[] = [
|
const typeData: {
|
||||||
...ngModules.map(ref => this._metadataResolver.getNgModuleSummary(ref) !),
|
summary: CompileTypeSummary,
|
||||||
...directives.map(ref => this._metadataResolver.getDirectiveSummary(ref) !),
|
metadata: CompileNgModuleMetadata | CompileDirectiveMetadata | CompilePipeMetadata |
|
||||||
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref) !),
|
CompileTypeMetadata
|
||||||
...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref) !)
|
}[] =
|
||||||
];
|
[
|
||||||
const {json, exportAs} = serializeSummaries(
|
...ngModules.map(ref => ({
|
||||||
this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
|
summary: this._metadataResolver.getNgModuleSummary(ref) !,
|
||||||
|
metadata: this._metadataResolver.getNgModuleMetadata(ref) !
|
||||||
|
})),
|
||||||
|
...directives.map(ref => ({
|
||||||
|
summary: this._metadataResolver.getDirectiveSummary(ref) !,
|
||||||
|
metadata: this._metadataResolver.getDirectiveMetadata(ref) !
|
||||||
|
})),
|
||||||
|
...pipes.map(ref => ({
|
||||||
|
summary: this._metadataResolver.getPipeSummary(ref) !,
|
||||||
|
metadata: this._metadataResolver.getPipeMetadata(ref) !
|
||||||
|
})),
|
||||||
|
...injectables.map(ref => ({
|
||||||
|
summary: this._metadataResolver.getInjectableSummary(ref) !,
|
||||||
|
metadata: this._metadataResolver.getInjectableSummary(ref) !.type
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
const {json, exportAs, forJit} =
|
||||||
|
serializeSummaries(this._summaryResolver, this._symbolResolver, symbolSummaries, typeData);
|
||||||
exportAs.forEach((entry) => {
|
exportAs.forEach((entry) => {
|
||||||
targetStatements.push(
|
targetStatements.push(
|
||||||
o.variable(entry.exportAs).set(o.importExpr({reference: entry.symbol})).toDeclStmt());
|
o.variable(entry.exportAs).set(o.importExpr({reference: entry.symbol})).toDeclStmt());
|
||||||
targetExportedVars.push(entry.exportAs);
|
targetExportedVars.push(entry.exportAs);
|
||||||
});
|
});
|
||||||
return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json);
|
return [
|
||||||
|
new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json),
|
||||||
|
this._codegenSourceModule(
|
||||||
|
srcFileUrl, summaryForJitFileName(srcFileUrl, true), forJit.statements,
|
||||||
|
forJit.exportedVars)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
||||||
|
|
|
@ -35,7 +35,6 @@ 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.
|
||||||
*/
|
*/
|
||||||
|
@ -69,16 +68,10 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
||||||
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||||
console, symbolCache, staticReflector);
|
console, symbolCache, staticReflector);
|
||||||
// TODO(vicb): do not pass options.i18nFormat here
|
// TODO(vicb): do not pass options.i18nFormat here
|
||||||
const importResolver = {
|
|
||||||
getImportAs: (symbol: StaticSymbol) => symbolResolver.getImportAs(symbol) !,
|
|
||||||
fileNameToModuleName: (fileName: string, containingFilePath: string) =>
|
|
||||||
compilerHost.fileNameToModuleName(fileName, containingFilePath),
|
|
||||||
getTypeArity: (symbol: StaticSymbol) => symbolResolver.getTypeArity(symbol) !
|
|
||||||
};
|
|
||||||
const viewCompiler = new ViewCompiler(config, elementSchemaRegistry);
|
const viewCompiler = new ViewCompiler(config, elementSchemaRegistry);
|
||||||
const compiler = new AotCompiler(
|
const compiler = new AotCompiler(
|
||||||
config, compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver), viewCompiler,
|
config, compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver), viewCompiler,
|
||||||
new NgModuleCompiler(), new TypeScriptEmitter(importResolver), summaryResolver,
|
new NgModuleCompiler(), new TypeScriptEmitter(symbolResolver), summaryResolver,
|
||||||
options.locale || null, options.i18nFormat || null, options.genFilePreamble || null,
|
options.locale || null, options.i18nFormat || null, options.genFilePreamble || null,
|
||||||
symbolResolver);
|
symbolResolver);
|
||||||
return {compiler, reflector: staticReflector};
|
return {compiler, reflector: staticReflector};
|
||||||
|
|
|
@ -14,14 +14,6 @@ import {AotSummaryResolverHost} from './summary_resolver';
|
||||||
* services and from underlying file systems.
|
* services and from underlying file systems.
|
||||||
*/
|
*/
|
||||||
export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
||||||
/**
|
|
||||||
* 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`.
|
|
||||||
*
|
|
||||||
* See ImportResolver.
|
|
||||||
*/
|
|
||||||
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string|null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {SummaryResolver} from '../summary_resolver';
|
||||||
import {ValueTransformer, visitValue} from '../util';
|
import {ValueTransformer, visitValue} from '../util';
|
||||||
|
|
||||||
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||||
import {isNgFactoryFile} from './util';
|
import {isGeneratedFile, stripSummaryForJitFileSuffix, stripSummaryForJitNameSuffix, summaryForJitFileName, summaryForJitName} from './util';
|
||||||
|
|
||||||
export class ResolvedStaticSymbol {
|
export class ResolvedStaticSymbol {
|
||||||
constructor(public symbol: StaticSymbol, public metadata: any) {}
|
constructor(public symbol: StaticSymbol, public metadata: any) {}
|
||||||
|
@ -39,6 +39,13 @@ export interface StaticSymbolResolverHost {
|
||||||
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
|
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
|
||||||
*/
|
*/
|
||||||
moduleNameToFileName(moduleName: string, containingFile?: string): string|null;
|
moduleNameToFileName(moduleName: string, containingFile?: string): string|null;
|
||||||
|
/**
|
||||||
|
* 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`.
|
||||||
|
*
|
||||||
|
* See ImportResolver.
|
||||||
|
*/
|
||||||
|
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SUPPORTED_SCHEMA_VERSION = 3;
|
const SUPPORTED_SCHEMA_VERSION = 3;
|
||||||
|
@ -60,6 +67,7 @@ export class StaticSymbolResolver {
|
||||||
private importAs = new Map<StaticSymbol, StaticSymbol>();
|
private importAs = new Map<StaticSymbol, StaticSymbol>();
|
||||||
private symbolResourcePaths = new Map<StaticSymbol, string>();
|
private symbolResourcePaths = new Map<StaticSymbol, string>();
|
||||||
private symbolFromFile = new Map<string, StaticSymbol[]>();
|
private symbolFromFile = new Map<string, StaticSymbol[]>();
|
||||||
|
private knownFileNameToModuleNames = new Map<string, string>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
|
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
|
||||||
|
@ -103,6 +111,18 @@ export class StaticSymbolResolver {
|
||||||
this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) :
|
this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) :
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
const summarizedFileName = stripSummaryForJitFileSuffix(staticSymbol.filePath);
|
||||||
|
if (summarizedFileName !== staticSymbol.filePath) {
|
||||||
|
const summarizedName = stripSummaryForJitNameSuffix(staticSymbol.name);
|
||||||
|
const baseSymbol =
|
||||||
|
this.getStaticSymbol(summarizedFileName, summarizedName, staticSymbol.members);
|
||||||
|
const baseImportAs = this.getImportAs(baseSymbol);
|
||||||
|
return baseImportAs ?
|
||||||
|
this.getStaticSymbol(
|
||||||
|
summaryForJitFileName(baseImportAs.filePath), summaryForJitName(baseImportAs.name),
|
||||||
|
baseSymbol.members) :
|
||||||
|
null;
|
||||||
|
}
|
||||||
let result = this.summaryResolver.getImportAs(staticSymbol);
|
let result = this.summaryResolver.getImportAs(staticSymbol);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = this.importAs.get(staticSymbol) !;
|
result = this.importAs.get(staticSymbol) !;
|
||||||
|
@ -124,11 +144,11 @@ export class StaticSymbolResolver {
|
||||||
* has. If the symbol is not a type the result is null.
|
* has. If the symbol is not a type the result is null.
|
||||||
*/
|
*/
|
||||||
getTypeArity(staticSymbol: StaticSymbol): number|null {
|
getTypeArity(staticSymbol: StaticSymbol): number|null {
|
||||||
// If the file is a factory file, don't resolve the symbol as doing so would
|
// If the file is a factory/ngsummary file, don't resolve the symbol as doing so would
|
||||||
// cause the metadata for an factory file to be loaded which doesn't exist.
|
// cause the metadata for an factory/ngsummary file to be loaded which doesn't exist.
|
||||||
// All references to generated classes must include the correct arity whenever
|
// All references to generated classes must include the correct arity whenever
|
||||||
// generating code.
|
// generating code.
|
||||||
if (isNgFactoryFile(staticSymbol.filePath)) {
|
if (isGeneratedFile(staticSymbol.filePath)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let resolvedSymbol = this.resolveSymbol(staticSymbol);
|
let resolvedSymbol = this.resolveSymbol(staticSymbol);
|
||||||
|
@ -138,6 +158,17 @@ export class StaticSymbolResolver {
|
||||||
return (resolvedSymbol && resolvedSymbol.metadata && resolvedSymbol.metadata.arity) || null;
|
return (resolvedSymbol && resolvedSymbol.metadata && resolvedSymbol.metadata.arity) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a file path to a module name that can be used as an `import`.
|
||||||
|
*/
|
||||||
|
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string|null {
|
||||||
|
if (importedFilePath === containingFilePath) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.knownFileNameToModuleNames.get(importedFilePath) ||
|
||||||
|
this.host.fileNameToModuleName(importedFilePath, containingFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
recordImportAs(sourceSymbol: StaticSymbol, targetSymbol: StaticSymbol) {
|
recordImportAs(sourceSymbol: StaticSymbol, targetSymbol: StaticSymbol) {
|
||||||
sourceSymbol.assertNoMembers();
|
sourceSymbol.assertNoMembers();
|
||||||
targetSymbol.assertNoMembers();
|
targetSymbol.assertNoMembers();
|
||||||
|
@ -226,6 +257,11 @@ export class StaticSymbolResolver {
|
||||||
this.resolvedFilePaths.add(filePath);
|
this.resolvedFilePaths.add(filePath);
|
||||||
const resolvedSymbols: ResolvedStaticSymbol[] = [];
|
const resolvedSymbols: ResolvedStaticSymbol[] = [];
|
||||||
const metadata = this.getModuleMetadata(filePath);
|
const metadata = this.getModuleMetadata(filePath);
|
||||||
|
if (metadata['importAs']) {
|
||||||
|
// Index bundle indices should use the importAs module name defined
|
||||||
|
// in the bundle.
|
||||||
|
this.knownFileNameToModuleNames.set(filePath, metadata['importAs']);
|
||||||
|
}
|
||||||
if (metadata['metadata']) {
|
if (metadata['metadata']) {
|
||||||
// handle direct declarations of the symbol
|
// handle direct declarations of the symbol
|
||||||
const topLevelSymbolNames =
|
const topLevelSymbolNames =
|
||||||
|
@ -236,13 +272,6 @@ export class StaticSymbolResolver {
|
||||||
const name = unescapeIdentifier(metadataKey);
|
const name = unescapeIdentifier(metadataKey);
|
||||||
|
|
||||||
const symbol = this.getStaticSymbol(filePath, name);
|
const symbol = this.getStaticSymbol(filePath, name);
|
||||||
let importSymbol: StaticSymbol|undefined = undefined;
|
|
||||||
if (metadata['importAs']) {
|
|
||||||
// Index bundle indexes should use the importAs module name instead of a reference
|
|
||||||
// to the .d.ts file directly.
|
|
||||||
importSymbol = this.getStaticSymbol(metadata['importAs'], name);
|
|
||||||
this.recordImportAs(symbol, importSymbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
const origin = origins.hasOwnProperty(metadataKey) && origins[metadataKey];
|
const origin = origins.hasOwnProperty(metadataKey) && origins[metadataKey];
|
||||||
if (origin) {
|
if (origin) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {Summary, SummaryResolver} from '../summary_resolver';
|
||||||
|
|
||||||
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||||
import {deserializeSummaries} from './summary_serializer';
|
import {deserializeSummaries} from './summary_serializer';
|
||||||
import {ngfactoryFilePath, stripNgFactory, summaryFileName} from './util';
|
import {ngfactoryFilePath, stripGeneratedFileSuffix, summaryFileName} from './util';
|
||||||
|
|
||||||
export interface AotSummaryResolverHost {
|
export interface AotSummaryResolverHost {
|
||||||
/**
|
/**
|
||||||
|
@ -43,7 +43,7 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
|
||||||
// Note: We need to strip the .ngfactory. file path,
|
// Note: We need to strip the .ngfactory. file path,
|
||||||
// so this method also works for generated files
|
// so this method also works for generated files
|
||||||
// (for which host.isSourceFile will always return false).
|
// (for which host.isSourceFile will always return false).
|
||||||
return !this.host.isSourceFile(stripNgFactory(filePath));
|
return !this.host.isSourceFile(stripGeneratedFileSuffix(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
getLibraryFileName(filePath: string) { return this.host.getOutputFileName(filePath); }
|
getLibraryFileName(filePath: string) { return this.host.getOutputFileName(filePath); }
|
||||||
|
@ -68,6 +68,8 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
|
||||||
return this.importAs.get(staticSymbol) !;
|
return this.importAs.get(staticSymbol) !;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addSummary(summary: Summary<StaticSymbol>) { this.summaryCache.set(summary.symbol, summary); }
|
||||||
|
|
||||||
private _loadSummaryFile(filePath: string) {
|
private _loadSummaryFile(filePath: string) {
|
||||||
if (this.loadedFilePaths.has(filePath)) {
|
if (this.loadedFilePaths.has(filePath)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -5,31 +5,40 @@
|
||||||
* 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 {CompileNgModuleSummary, CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileNgModuleSummary, CompilePipeMetadata, CompileProviderMetadata, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary} from '../compile_metadata';
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
import {Summary, SummaryResolver} from '../summary_resolver';
|
import {Summary, SummaryResolver} from '../summary_resolver';
|
||||||
import {ValueTransformer, visitValue} from '../util';
|
import {ValueTransformer, ValueVisitor, visitValue} from '../util';
|
||||||
|
|
||||||
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||||
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
|
import {summaryForJitFileName, summaryForJitName} from './util';
|
||||||
|
|
||||||
export function serializeSummaries(
|
export function serializeSummaries(
|
||||||
summaryResolver: SummaryResolver<StaticSymbol>, symbolResolver: StaticSymbolResolver,
|
summaryResolver: SummaryResolver<StaticSymbol>, symbolResolver: StaticSymbolResolver,
|
||||||
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]):
|
symbols: ResolvedStaticSymbol[], types: {
|
||||||
{json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
|
summary: CompileTypeSummary,
|
||||||
const serializer = new Serializer(symbolResolver, summaryResolver);
|
metadata: CompileNgModuleMetadata | CompileDirectiveMetadata | CompilePipeMetadata |
|
||||||
|
CompileTypeMetadata
|
||||||
|
}[]): {
|
||||||
|
json: string,
|
||||||
|
exportAs: {symbol: StaticSymbol, exportAs: string}[],
|
||||||
|
forJit: {statements: o.Statement[], exportedVars: string[]}
|
||||||
|
} {
|
||||||
|
const toJsonSerializer = new ToJsonSerializer(symbolResolver, summaryResolver);
|
||||||
|
const forJitSerializer = new ForJitSerializer(symbolResolver);
|
||||||
|
|
||||||
// for symbols, we use everything except for the class metadata itself
|
// for symbols, we use everything except for the class metadata itself
|
||||||
// (we keep the statics though), as the class metadata is contained in the
|
// (we keep the statics though), as the class metadata is contained in the
|
||||||
// CompileTypeSummary.
|
// CompileTypeSummary.
|
||||||
symbols.forEach(
|
symbols.forEach(
|
||||||
(resolvedSymbol) => serializer.addOrMergeSummary(
|
(resolvedSymbol) => toJsonSerializer.addOrMergeSummary(
|
||||||
{symbol: resolvedSymbol.symbol, metadata: resolvedSymbol.metadata}));
|
{symbol: resolvedSymbol.symbol, metadata: resolvedSymbol.metadata}));
|
||||||
// Add summaries that are referenced by the given symbols (transitively)
|
// Add summaries that are referenced by the given symbols (transitively)
|
||||||
// Note: the serializer.symbols array might be growing while
|
// Note: the serializer.symbols array might be growing while
|
||||||
// we execute the loop!
|
// we execute the loop!
|
||||||
for (let processedIndex = 0; processedIndex < serializer.symbols.length; processedIndex++) {
|
for (let processedIndex = 0; processedIndex < toJsonSerializer.symbols.length; processedIndex++) {
|
||||||
const symbol = serializer.symbols[processedIndex];
|
const symbol = toJsonSerializer.symbols[processedIndex];
|
||||||
if (summaryResolver.isLibraryFile(symbol.filePath)) {
|
if (summaryResolver.isLibraryFile(symbol.filePath)) {
|
||||||
let summary = summaryResolver.resolveSummary(symbol);
|
let summary = summaryResolver.resolveSummary(symbol);
|
||||||
if (!summary) {
|
if (!summary) {
|
||||||
|
@ -42,7 +51,10 @@ export function serializeSummaries(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (summary) {
|
if (summary) {
|
||||||
serializer.addOrMergeSummary(summary);
|
if (summary.type) {
|
||||||
|
forJitSerializer.addLibType(summary.type);
|
||||||
|
}
|
||||||
|
toJsonSerializer.addOrMergeSummary(summary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,32 +63,35 @@ export function serializeSummaries(
|
||||||
// Note: We don't add the summaries of all referenced symbols as for the ResolvedSymbols,
|
// 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
|
// as the type summaries already contain the transitive data that they require
|
||||||
// (in a minimal way).
|
// (in a minimal way).
|
||||||
types.forEach((typeSummary) => {
|
types.forEach(({summary, metadata}) => {
|
||||||
serializer.addOrMergeSummary(
|
forJitSerializer.addSourceType(summary, metadata);
|
||||||
{symbol: typeSummary.type.reference, metadata: null, type: typeSummary});
|
toJsonSerializer.addOrMergeSummary(
|
||||||
if (typeSummary.summaryKind === CompileSummaryKind.NgModule) {
|
{symbol: summary.type.reference, metadata: null, type: summary});
|
||||||
const ngModuleSummary = <CompileNgModuleSummary>typeSummary;
|
if (summary.summaryKind === CompileSummaryKind.NgModule) {
|
||||||
|
const ngModuleSummary = <CompileNgModuleSummary>summary;
|
||||||
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
|
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
|
||||||
const symbol: StaticSymbol = id.reference;
|
const symbol: StaticSymbol = id.reference;
|
||||||
if (summaryResolver.isLibraryFile(symbol.filePath)) {
|
if (summaryResolver.isLibraryFile(symbol.filePath)) {
|
||||||
const summary = summaryResolver.resolveSummary(symbol);
|
const summary = summaryResolver.resolveSummary(symbol);
|
||||||
if (summary) {
|
if (summary) {
|
||||||
serializer.addOrMergeSummary(summary);
|
toJsonSerializer.addOrMergeSummary(summary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return serializer.serialize();
|
const {json, exportAs} = toJsonSerializer.serialize();
|
||||||
|
const {statements, exportedVars} = forJitSerializer.serialize(exportAs);
|
||||||
|
return {json, forJit: {statements, exportedVars}, exportAs};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deserializeSummaries(symbolCache: StaticSymbolCache, json: string):
|
export function deserializeSummaries(symbolCache: StaticSymbolCache, json: string):
|
||||||
{summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: string}[]} {
|
{summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: string}[]} {
|
||||||
const deserializer = new Deserializer(symbolCache);
|
const deserializer = new FromJsonDeserializer(symbolCache);
|
||||||
return deserializer.deserialize(json);
|
return deserializer.deserialize(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Serializer extends ValueTransformer {
|
class ToJsonSerializer extends ValueTransformer {
|
||||||
// Note: This only contains symbols without members.
|
// Note: This only contains symbols without members.
|
||||||
symbols: StaticSymbol[] = [];
|
symbols: StaticSymbol[] = [];
|
||||||
private indexBySymbol = new Map<StaticSymbol, number>();
|
private indexBySymbol = new Map<StaticSymbol, number>();
|
||||||
|
@ -169,7 +184,132 @@ class Serializer extends ValueTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Deserializer extends ValueTransformer {
|
class ForJitSerializer {
|
||||||
|
private data = new Map<StaticSymbol, {
|
||||||
|
summary: CompileTypeSummary,
|
||||||
|
metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|CompilePipeMetadata|
|
||||||
|
CompileTypeMetadata|null,
|
||||||
|
isLibrary: boolean
|
||||||
|
}>();
|
||||||
|
|
||||||
|
constructor(private symbolResolver: StaticSymbolResolver) {}
|
||||||
|
|
||||||
|
addSourceType(
|
||||||
|
summary: CompileTypeSummary, metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|
|
||||||
|
CompilePipeMetadata|CompileTypeMetadata) {
|
||||||
|
this.data.set(summary.type.reference, {summary, metadata, isLibrary: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
addLibType(summary: CompileTypeSummary) {
|
||||||
|
this.data.set(summary.type.reference, {summary, metadata: null, isLibrary: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize(exportAs: {symbol: StaticSymbol, exportAs: string}[]):
|
||||||
|
{statements: o.Statement[], exportedVars: string[]} {
|
||||||
|
const statements: o.Statement[] = [];
|
||||||
|
const exportedVars: string[] = [];
|
||||||
|
const ngModuleSymbols = new Set<StaticSymbol>();
|
||||||
|
|
||||||
|
Array.from(this.data.values()).forEach(({summary, metadata, isLibrary}) => {
|
||||||
|
if (summary.summaryKind === CompileSummaryKind.NgModule) {
|
||||||
|
// collect the symbols that refer to NgModule classes.
|
||||||
|
// Note: we can't just rely on `summary.type.summaryKind` to determine this as
|
||||||
|
// we don't add the summaries of all referenced symbols when we serialize type summaries.
|
||||||
|
// See serializeSummaries for details.
|
||||||
|
ngModuleSymbols.add(summary.type.reference);
|
||||||
|
const modSummary = <CompileNgModuleSummary>summary;
|
||||||
|
modSummary.modules.forEach((mod) => { ngModuleSymbols.add(mod.reference); });
|
||||||
|
}
|
||||||
|
if (!isLibrary) {
|
||||||
|
const fnName = summaryForJitName(summary.type.reference.name);
|
||||||
|
statements.push(
|
||||||
|
o.fn([], [new o.ReturnStatement(this.serializeSummaryWithDeps(summary, metadata !))],
|
||||||
|
new o.ArrayType(o.DYNAMIC_TYPE))
|
||||||
|
.toDeclStmt(fnName, [o.StmtModifier.Final]));
|
||||||
|
exportedVars.push(fnName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exportAs.forEach((entry) => {
|
||||||
|
const symbol = entry.symbol;
|
||||||
|
if (ngModuleSymbols.has(symbol)) {
|
||||||
|
const jitExportAsName = summaryForJitName(entry.exportAs);
|
||||||
|
statements.push(
|
||||||
|
o.variable(jitExportAsName).set(this.serializeSummaryRef(symbol)).toDeclStmt());
|
||||||
|
exportedVars.push(jitExportAsName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {statements, exportedVars};
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeSummaryWithDeps(
|
||||||
|
summary: CompileTypeSummary, metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|
|
||||||
|
CompilePipeMetadata|CompileTypeMetadata): o.Expression {
|
||||||
|
const expressions: o.Expression[] = [this.serializeSummary(summary)];
|
||||||
|
let providers: CompileProviderMetadata[] = [];
|
||||||
|
if (metadata instanceof CompileNgModuleMetadata) {
|
||||||
|
expressions.push(...
|
||||||
|
// For directives / pipes, we only add the declared ones,
|
||||||
|
// and rely on transitively importing NgModules to get the transitive
|
||||||
|
// summaries.
|
||||||
|
metadata.declaredDirectives.concat(metadata.declaredPipes)
|
||||||
|
.map(type => type.reference)
|
||||||
|
// For modules,
|
||||||
|
// we also add the summaries for modules
|
||||||
|
// from libraries.
|
||||||
|
// This is ok as we produce reexports for all transitive modules.
|
||||||
|
.concat(metadata.transitiveModule.modules.map(type => type.reference)
|
||||||
|
.filter(ref => ref !== metadata.type.reference))
|
||||||
|
.map((ref) => this.serializeSummaryRef(ref)));
|
||||||
|
// Note: We don't use `NgModuleSummary.providers`, as that one is transitive,
|
||||||
|
// and we already have transitive modules.
|
||||||
|
providers = metadata.providers;
|
||||||
|
} else if (summary.summaryKind === CompileSummaryKind.Directive) {
|
||||||
|
const dirSummary = <CompileDirectiveSummary>summary;
|
||||||
|
providers = dirSummary.providers.concat(dirSummary.viewProviders);
|
||||||
|
}
|
||||||
|
// Note: We can't just refer to the `ngsummary.ts` files for `useClass` providers (as we do for
|
||||||
|
// declaredDirectives / declaredPipes), as we allow
|
||||||
|
// providers without ctor arguments to skip the `@Injectable` decorator,
|
||||||
|
// i.e. we didn't generate .ngsummary.ts files for these.
|
||||||
|
expressions.push(
|
||||||
|
...providers.filter(provider => !!provider.useClass).map(provider => this.serializeSummary({
|
||||||
|
summaryKind: CompileSummaryKind.Injectable, type: provider.useClass
|
||||||
|
} as CompileTypeSummary)));
|
||||||
|
return o.literalArr(expressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeSummaryRef(typeSymbol: StaticSymbol): o.Expression {
|
||||||
|
const jitImportedSymbol = this.symbolResolver.getStaticSymbol(
|
||||||
|
summaryForJitFileName(typeSymbol.filePath), summaryForJitName(typeSymbol.name));
|
||||||
|
return o.importExpr({reference: jitImportedSymbol});
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeSummary(data: {[key: string]: any}): o.Expression {
|
||||||
|
class Transformer implements ValueVisitor {
|
||||||
|
visitArray(arr: any[], context: any): any {
|
||||||
|
return o.literalArr(arr.map(entry => visitValue(entry, this, context)));
|
||||||
|
}
|
||||||
|
visitStringMap(map: {[key: string]: any}, context: any): any {
|
||||||
|
return new o.LiteralMapExpr(Object.keys(map).map(
|
||||||
|
(key) => new o.LiteralMapEntry(key, visitValue(map[key], this, context))));
|
||||||
|
}
|
||||||
|
visitPrimitive(value: any, context: any): any { return o.literal(value); }
|
||||||
|
visitOther(value: any, context: any): any {
|
||||||
|
if (value instanceof StaticSymbol) {
|
||||||
|
return o.importExpr({reference: value});
|
||||||
|
} else {
|
||||||
|
throw new Error(`Illegal State: Encountered value ${value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return visitValue(data, new Transformer(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FromJsonDeserializer extends ValueTransformer {
|
||||||
private symbols: StaticSymbol[];
|
private symbols: StaticSymbol[];
|
||||||
|
|
||||||
constructor(private symbolCache: StaticSymbolCache) { super(); }
|
constructor(private symbolCache: StaticSymbolCache) { super(); }
|
||||||
|
|
|
@ -7,24 +7,27 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
const NG_FACTORY = /\.ngfactory\./;
|
const GENERATED_FILE = /\.ngfactory\.|\.ngsummary\./;
|
||||||
|
const GENERATED_MODULE = /\.ngfactory$|\.ngsummary$/;
|
||||||
|
const JIT_SUMMARY_FILE = /\.ngsummary\./;
|
||||||
|
const JIT_SUMMARY_NAME = /NgSummary$/;
|
||||||
|
|
||||||
export function ngfactoryFilePath(filePath: string): string {
|
export function ngfactoryFilePath(filePath: string, forceSourceFile = false): string {
|
||||||
const urlWithSuffix = splitTypescriptSuffix(filePath);
|
const urlWithSuffix = splitTypescriptSuffix(filePath, forceSourceFile);
|
||||||
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stripNgFactory(filePath: string): string {
|
export function stripGeneratedFileSuffix(filePath: string): string {
|
||||||
return filePath.replace(NG_FACTORY, '.');
|
return filePath.replace(GENERATED_FILE, '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNgFactoryFile(filePath: string): boolean {
|
export function isGeneratedFile(filePath: string): boolean {
|
||||||
return NG_FACTORY.test(filePath);
|
return GENERATED_FILE.test(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function splitTypescriptSuffix(path: string): string[] {
|
export function splitTypescriptSuffix(path: string, forceSourceFile = false): string[] {
|
||||||
if (path.endsWith('.d.ts')) {
|
if (path.endsWith('.d.ts')) {
|
||||||
return [path.slice(0, -5), '.ts'];
|
return [path.slice(0, -5), forceSourceFile ? '.ts' : '.d.ts'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastDot = path.lastIndexOf('.');
|
const lastDot = path.lastIndexOf('.');
|
||||||
|
@ -40,3 +43,20 @@ export function summaryFileName(fileName: string): string {
|
||||||
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
|
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
|
||||||
return `${fileNameWithoutSuffix}.ngsummary.json`;
|
return `${fileNameWithoutSuffix}.ngsummary.json`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function summaryForJitFileName(fileName: string, forceSourceFile = false): string {
|
||||||
|
const urlWithSuffix = splitTypescriptSuffix(stripGeneratedFileSuffix(fileName), forceSourceFile);
|
||||||
|
return `${urlWithSuffix[0]}.ngsummary${urlWithSuffix[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stripSummaryForJitFileSuffix(filePath: string): string {
|
||||||
|
return filePath.replace(JIT_SUMMARY_FILE, '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function summaryForJitName(symbolName: string): string {
|
||||||
|
return `${symbolName}NgSummary`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stripSummaryForJitNameSuffix(symbolName: string): string {
|
||||||
|
return symbolName.replace(JIT_SUMMARY_NAME, '');
|
||||||
|
}
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {Compiler, ComponentFactory, Inject, Injector, ModuleWithComponentFactories, NgModuleFactory, Type, ɵConsole as Console, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵstringify as stringify} from '@angular/core';
|
import {Compiler, ComponentFactory, Inject, Injector, ModuleWithComponentFactories, NgModuleFactory, Type, ɵConsole as Console, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵstringify as stringify} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileStylesheetMetadata, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName, ngModuleJitUrl, sharedStylesheetJitUrl, templateJitUrl, templateSourceUrl} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileStylesheetMetadata, CompileTypeSummary, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName, ngModuleJitUrl, sharedStylesheetJitUrl, templateJitUrl, templateSourceUrl} from '../compile_metadata';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {CompilerInjectable} from '../injectable';
|
import {CompilerInjectable} from '../injectable';
|
||||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||||
|
@ -17,6 +17,7 @@ import * as ir from '../output/output_ast';
|
||||||
import {interpretStatements} from '../output/output_interpreter';
|
import {interpretStatements} from '../output/output_interpreter';
|
||||||
import {jitStatements} from '../output/output_jit';
|
import {jitStatements} from '../output/output_jit';
|
||||||
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 {SyncAsyncResult} from '../util';
|
import {SyncAsyncResult} from '../util';
|
||||||
import {ViewCompiler} from '../view_compiler/view_compiler';
|
import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
@ -44,7 +45,8 @@ export class JitCompiler implements Compiler {
|
||||||
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
|
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
|
||||||
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
||||||
private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler,
|
private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler,
|
||||||
private _compilerConfig: CompilerConfig, private _console: Console) {}
|
private _summaryResolver: SummaryResolver<Type<any>>, private _compilerConfig: CompilerConfig,
|
||||||
|
private _console: Console) {}
|
||||||
|
|
||||||
get injector(): Injector { return this._injector; }
|
get injector(): Injector { return this._injector; }
|
||||||
|
|
||||||
|
@ -75,6 +77,25 @@ export class JitCompiler implements Compiler {
|
||||||
return template.compMeta.template !.ngContentSelectors;
|
return template.compMeta.template !.ngContentSelectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getComponentFactory<T>(component: Type<T>): ComponentFactory<T> {
|
||||||
|
const summary = this._metadataResolver.getDirectiveSummary(component);
|
||||||
|
return <ComponentFactory<T>>summary.componentFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAotSummaries(summaries: () => any[]) {
|
||||||
|
this.clearCache();
|
||||||
|
flattenSummaries(summaries).forEach((summary) => {
|
||||||
|
this._summaryResolver.addSummary(
|
||||||
|
{symbol: summary.type.reference, metadata: null, type: summary});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAotSummary(ref: Type<any>) { return !!this._summaryResolver.resolveSummary(ref); }
|
||||||
|
|
||||||
|
private _filterJitIdentifiers(ids: CompileIdentifierMetadata[]): any[] {
|
||||||
|
return ids.map(mod => mod.reference).filter((ref) => !this.hasAotSummary(ref));
|
||||||
|
}
|
||||||
|
|
||||||
private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
|
private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
|
||||||
SyncAsyncResult<NgModuleFactory<T>> {
|
SyncAsyncResult<NgModuleFactory<T>> {
|
||||||
const loadingPromise = this._loadModules(moduleType, isSync);
|
const loadingPromise = this._loadModules(moduleType, isSync);
|
||||||
|
@ -106,14 +127,21 @@ 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 = this._metadataResolver.getNgModuleMetadata(mainModule) !;
|
const mainNgModule = this._metadataResolver.getNgModuleMetadata(mainModule) !;
|
||||||
// Note: the loadingPromise for a module only includes the loading of the exported directives
|
// Note: for runtime compilation, we want to transitively compile all modules,
|
||||||
// of imported modules.
|
// so we also need to load the declared directives / pipes for all nested modules.
|
||||||
// However, for runtime compilation, we want to transitively compile all modules,
|
this._filterJitIdentifiers(mainNgModule.transitiveModule.modules).forEach((nestedNgModule) => {
|
||||||
// so we also need to call loadNgModuleDirectiveAndPipeMetadata for all nested modules.
|
// getNgModuleMetadata only returns null if the value passed in is not an NgModule
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
const moduleMeta = this._metadataResolver.getNgModuleMetadata(nestedNgModule) !;
|
||||||
loadingPromises.push(this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
this._filterJitIdentifiers(moduleMeta.declaredDirectives).forEach((ref) => {
|
||||||
localModuleMeta.reference, isSync));
|
const promise =
|
||||||
|
this._metadataResolver.loadDirectiveMetadata(moduleMeta.type.reference, ref, isSync);
|
||||||
|
if (promise) {
|
||||||
|
loadingPromises.push(promise);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._filterJitIdentifiers(moduleMeta.declaredPipes)
|
||||||
|
.forEach((ref) => this._metadataResolver.getOrLoadPipeMetadata(ref));
|
||||||
});
|
});
|
||||||
return Promise.all(loadingPromises);
|
return Promise.all(loadingPromises);
|
||||||
}
|
}
|
||||||
|
@ -144,15 +172,15 @@ export class JitCompiler implements Compiler {
|
||||||
*/
|
*/
|
||||||
_compileComponents(mainModule: Type<any>, allComponentFactories: ComponentFactory<any>[]|null) {
|
_compileComponents(mainModule: Type<any>, allComponentFactories: ComponentFactory<any>[]|null) {
|
||||||
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule) !;
|
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule) !;
|
||||||
const moduleByDirective = new Map<any, CompileNgModuleMetadata>();
|
const moduleByJitDirective = new Map<any, CompileNgModuleMetadata>();
|
||||||
const templates = new Set<CompiledTemplate>();
|
const templates = new Set<CompiledTemplate>();
|
||||||
|
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
|
const transJitModules = this._filterJitIdentifiers(ngModule.transitiveModule.modules);
|
||||||
const localModuleMeta =
|
transJitModules.forEach((localMod) => {
|
||||||
this._metadataResolver.getNgModuleMetadata(localModuleSummary.reference) !;
|
const localModuleMeta = this._metadataResolver.getNgModuleMetadata(localMod) !;
|
||||||
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
this._filterJitIdentifiers(localModuleMeta.declaredDirectives).forEach((dirRef) => {
|
||||||
moduleByDirective.set(dirIdentifier.reference, localModuleMeta);
|
moduleByJitDirective.set(dirRef, localModuleMeta);
|
||||||
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirRef);
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta.isComponent) {
|
||||||
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
|
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
|
||||||
if (allComponentFactories) {
|
if (allComponentFactories) {
|
||||||
|
@ -164,23 +192,24 @@ export class JitCompiler implements Compiler {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
|
transJitModules.forEach((localMod) => {
|
||||||
const localModuleMeta =
|
const localModuleMeta = this._metadataResolver.getNgModuleMetadata(localMod) !;
|
||||||
this._metadataResolver.getNgModuleMetadata(localModuleSummary.reference) !;
|
this._filterJitIdentifiers(localModuleMeta.declaredDirectives).forEach((dirRef) => {
|
||||||
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirRef);
|
||||||
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta.isComponent) {
|
||||||
dirMeta.entryComponents.forEach((entryComponentType) => {
|
dirMeta.entryComponents.forEach((entryComponentType) => {
|
||||||
const moduleMeta = moduleByDirective.get(entryComponentType.componentType) !;
|
const moduleMeta = moduleByJitDirective.get(entryComponentType.componentType) !;
|
||||||
templates.add(
|
templates.add(
|
||||||
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
|
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
localModuleMeta.entryComponents.forEach((entryComponentType) => {
|
localModuleMeta.entryComponents.forEach((entryComponentType) => {
|
||||||
const moduleMeta = moduleByDirective.get(entryComponentType.componentType) !;
|
if (!this.hasAotSummary(entryComponentType.componentType.reference)) {
|
||||||
templates.add(
|
const moduleMeta = moduleByJitDirective.get(entryComponentType.componentType) !;
|
||||||
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
|
templates.add(
|
||||||
|
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
templates.forEach((template) => this._compileTemplate(template));
|
templates.forEach((template) => this._compileTemplate(template));
|
||||||
|
@ -353,7 +382,6 @@ class ModuleBoundCompiler implements Compiler {
|
||||||
return this._delegate.getNgContentSelectors(component);
|
return this._delegate.getNgContentSelectors(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all caches
|
* Clears all caches
|
||||||
*/
|
*/
|
||||||
|
@ -364,3 +392,15 @@ class ModuleBoundCompiler implements Compiler {
|
||||||
*/
|
*/
|
||||||
clearCacheFor(type: Type<any>) { this._delegate.clearCacheFor(type); }
|
clearCacheFor(type: Type<any>) { this._delegate.clearCacheFor(type); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function flattenSummaries(fn: () => any[], out: CompileTypeSummary[] = []): CompileTypeSummary[] {
|
||||||
|
fn().forEach((entry) => {
|
||||||
|
if (typeof entry === 'function') {
|
||||||
|
flattenSummaries(entry, out);
|
||||||
|
} else {
|
||||||
|
out.push(entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, MissingTranslationStrategy, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore, ɵConsole as Console, ɵReflectionCapabilities as ReflectionCapabilities, ɵReflector as Reflector, ɵReflectorReader as ReflectorReader, ɵreflector as reflector} from '@angular/core';
|
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, MissingTranslationStrategy, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore, ɵConsole as Console, ɵReflectionCapabilities as ReflectionCapabilities, ɵReflector as Reflector, ɵReflectorReader as ReflectorReader, ɵreflector as reflector} from '@angular/core';
|
||||||
|
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
import {DirectiveResolver} from '../directive_resolver';
|
import {DirectiveResolver} from '../directive_resolver';
|
||||||
|
@ -23,7 +24,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 {JitSummaryResolver, 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';
|
||||||
|
@ -38,13 +39,6 @@ const _NO_RESOURCE_LOADER: ResourceLoader = {
|
||||||
|
|
||||||
const baseHtmlParser = new InjectionToken('HtmlParser');
|
const baseHtmlParser = new InjectionToken('HtmlParser');
|
||||||
|
|
||||||
export function i18nHtmlParserFactory(
|
|
||||||
parser: HtmlParser, translations: string, format: string, config: CompilerConfig,
|
|
||||||
console: Console): i18n.I18NHtmlParser {
|
|
||||||
return new i18n.I18NHtmlParser(
|
|
||||||
parser, translations, format, config.missingTranslation !, console);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of providers that provide `JitCompiler` and its dependencies to use for
|
* A set of providers that provide `JitCompiler` and its dependencies to use for
|
||||||
* template compilation.
|
* template compilation.
|
||||||
|
@ -53,7 +47,8 @@ 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,
|
JitSummaryResolver,
|
||||||
|
{provide: SummaryResolver, useExisting: JitSummaryResolver},
|
||||||
Console,
|
Console,
|
||||||
Lexer,
|
Lexer,
|
||||||
Parser,
|
Parser,
|
||||||
|
@ -63,7 +58,10 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: i18n.I18NHtmlParser,
|
provide: i18n.I18NHtmlParser,
|
||||||
useFactory: i18nHtmlParserFactory,
|
useFactory: (parser: HtmlParser, translations: string, format: string, config: CompilerConfig,
|
||||||
|
console: Console) =>
|
||||||
|
new i18n.I18NHtmlParser(
|
||||||
|
parser, translations, format, config.missingTranslation !, console),
|
||||||
deps: [
|
deps: [
|
||||||
baseHtmlParser,
|
baseHtmlParser,
|
||||||
[new Optional(), new Inject(TRANSLATIONS)],
|
[new Optional(), new Inject(TRANSLATIONS)],
|
||||||
|
|
|
@ -167,8 +167,7 @@ export class CompileMetadataResolver {
|
||||||
return typeSummary && typeSummary.summaryKind === kind ? typeSummary : null;
|
return typeSummary && typeSummary.summaryKind === kind ? typeSummary : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadDirectiveMetadata(ngModuleType: any, directiveType: any, isSync: boolean):
|
loadDirectiveMetadata(ngModuleType: any, directiveType: any, isSync: boolean): Promise<any>|null {
|
||||||
Promise<any>|null {
|
|
||||||
if (this._directiveCache.has(directiveType)) {
|
if (this._directiveCache.has(directiveType)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -377,9 +376,20 @@ export class CompileMetadataResolver {
|
||||||
return dirSummary;
|
return dirSummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
isDirective(type: any) { return this._directiveResolver.isDirective(type); }
|
isDirective(type: any) {
|
||||||
|
return !!this._loadSummary(type, cpl.CompileSummaryKind.Directive) ||
|
||||||
|
this._directiveResolver.isDirective(type);
|
||||||
|
}
|
||||||
|
|
||||||
isPipe(type: any) { return this._pipeResolver.isPipe(type); }
|
isPipe(type: any) {
|
||||||
|
return !!this._loadSummary(type, cpl.CompileSummaryKind.Pipe) ||
|
||||||
|
this._pipeResolver.isPipe(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
isNgModule(type: any) {
|
||||||
|
return !!this._loadSummary(type, cpl.CompileSummaryKind.NgModule) ||
|
||||||
|
this._ngModuleResolver.isNgModule(type);
|
||||||
|
}
|
||||||
|
|
||||||
getNgModuleSummary(moduleType: any): cpl.CompileNgModuleSummary|null {
|
getNgModuleSummary(moduleType: any): cpl.CompileNgModuleSummary|null {
|
||||||
let moduleSummary: cpl.CompileNgModuleSummary|null =
|
let moduleSummary: cpl.CompileNgModuleSummary|null =
|
||||||
|
@ -403,7 +413,7 @@ export class CompileMetadataResolver {
|
||||||
const loading: Promise<any>[] = [];
|
const loading: Promise<any>[] = [];
|
||||||
if (ngModule) {
|
if (ngModule) {
|
||||||
ngModule.declaredDirectives.forEach((id) => {
|
ngModule.declaredDirectives.forEach((id) => {
|
||||||
const promise = this._loadDirectiveMetadata(moduleType, id.reference, isSync);
|
const promise = this.loadDirectiveMetadata(moduleType, id.reference, isSync);
|
||||||
if (promise) {
|
if (promise) {
|
||||||
loading.push(promise);
|
loading.push(promise);
|
||||||
}
|
}
|
||||||
|
@ -501,11 +511,11 @@ export class CompileMetadataResolver {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const declaredIdentifier = this._getIdentifierMetadata(declaredType);
|
const declaredIdentifier = this._getIdentifierMetadata(declaredType);
|
||||||
if (this._directiveResolver.isDirective(declaredType)) {
|
if (this.isDirective(declaredType)) {
|
||||||
transitiveModule.addDirective(declaredIdentifier);
|
transitiveModule.addDirective(declaredIdentifier);
|
||||||
declaredDirectives.push(declaredIdentifier);
|
declaredDirectives.push(declaredIdentifier);
|
||||||
this._addTypeToModule(declaredType, moduleType);
|
this._addTypeToModule(declaredType, moduleType);
|
||||||
} else if (this._pipeResolver.isPipe(declaredType)) {
|
} else if (this.isPipe(declaredType)) {
|
||||||
transitiveModule.addPipe(declaredIdentifier);
|
transitiveModule.addPipe(declaredIdentifier);
|
||||||
transitiveModule.pipes.push(declaredIdentifier);
|
transitiveModule.pipes.push(declaredIdentifier);
|
||||||
declaredPipes.push(declaredIdentifier);
|
declaredPipes.push(declaredIdentifier);
|
||||||
|
@ -604,15 +614,15 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getTypeDescriptor(type: Type<any>): string {
|
private _getTypeDescriptor(type: Type<any>): string {
|
||||||
if (this._directiveResolver.isDirective(type)) {
|
if (this.isDirective(type)) {
|
||||||
return 'directive';
|
return 'directive';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._pipeResolver.isPipe(type)) {
|
if (this.isPipe(type)) {
|
||||||
return 'pipe';
|
return 'pipe';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._ngModuleResolver.isNgModule(type)) {
|
if (this.isNgModule(type)) {
|
||||||
return 'module';
|
return 'module';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* 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 {Type} from '@angular/core';
|
||||||
import {CompileTypeSummary} from './compile_metadata';
|
import {CompileTypeSummary} from './compile_metadata';
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
|
|
||||||
|
@ -14,11 +15,25 @@ export interface Summary<T> {
|
||||||
type?: CompileTypeSummary;
|
type?: CompileTypeSummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export abstract class SummaryResolver<T> {
|
||||||
|
abstract isLibraryFile(fileName: string): boolean;
|
||||||
|
abstract getLibraryFileName(fileName: string): string|null;
|
||||||
|
abstract resolveSummary(reference: T): Summary<T>|null;
|
||||||
|
abstract getSymbolsOf(filePath: string): T[];
|
||||||
|
abstract getImportAs(reference: T): T;
|
||||||
|
abstract addSummary(summary: Summary<T>): void;
|
||||||
|
}
|
||||||
|
|
||||||
@CompilerInjectable()
|
@CompilerInjectable()
|
||||||
export class SummaryResolver<T> {
|
export class JitSummaryResolver implements SummaryResolver<Type<any>> {
|
||||||
|
private _summaries = new Map<Type<any>, Summary<Type<any>>>();
|
||||||
|
|
||||||
isLibraryFile(fileName: string): boolean { return false; };
|
isLibraryFile(fileName: string): boolean { return false; };
|
||||||
getLibraryFileName(fileName: string): string|null { return null; }
|
getLibraryFileName(fileName: string): string|null { return null; }
|
||||||
resolveSummary(reference: T): Summary<T>|null { return null; };
|
resolveSummary(reference: Type<any>): Summary<Type<any>>|null {
|
||||||
getSymbolsOf(filePath: string): T[] { return []; }
|
return this._summaries.get(reference) || null;
|
||||||
getImportAs(reference: T): T { return reference; }
|
};
|
||||||
|
getSymbolsOf(filePath: string): Type<any>[] { return []; }
|
||||||
|
getImportAs(reference: Type<any>): Type<any> { return reference; }
|
||||||
|
addSummary(summary: Summary<Type<any>>) { this._summaries.set(summary.symbol, summary); };
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,347 @@
|
||||||
|
/**
|
||||||
|
* @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 {AotCompiler, AotCompilerHost, AotCompilerOptions, CompileSummaryKind, GeneratedFile, createAotCompiler} from '@angular/compiler';
|
||||||
|
import {fakeAsync, tick} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {MockDirectory, compile, setup} from './test_util';
|
||||||
|
|
||||||
|
describe('aot summaries for jit', () => {
|
||||||
|
let angularFiles = setup();
|
||||||
|
|
||||||
|
function compileApp(rootDir: MockDirectory, options: {useSummaries?: boolean} = {}):
|
||||||
|
{genFiles: GeneratedFile[], outDir: MockDirectory} {
|
||||||
|
let result: {genFiles: GeneratedFile[], outDir: MockDirectory} = null !;
|
||||||
|
let error: Error|null = null;
|
||||||
|
compile([rootDir, angularFiles], options).then((r) => result = r, (e) => error = e);
|
||||||
|
tick();
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should create @Injectable summaries', fakeAsync(() => {
|
||||||
|
const appDir = {
|
||||||
|
'app.module.ts': `
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
export class Dep {}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MyService {
|
||||||
|
constructor(d: Dep) {}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
|
const genFile =
|
||||||
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
|
||||||
|
expect(genFile.source).toContain(`import * as import0 from '/app/app.module'`);
|
||||||
|
expect(genFile.source).toContain('export function MyServiceNgSummary()');
|
||||||
|
// Note: CompileSummaryKind.Injectable = 3
|
||||||
|
expect(genFile.source).toMatch(/summaryKind: 3,\s*type: \{\s*reference: import0.MyService/);
|
||||||
|
expect(genFile.source).toContain('token: {identifier: {reference: import0.Dep}}');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create @Pipe summaries', fakeAsync(() => {
|
||||||
|
const appDir = {
|
||||||
|
'app.module.ts': `
|
||||||
|
import { Pipe, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
export class Dep {}
|
||||||
|
|
||||||
|
@Pipe({name: 'myPipe'})
|
||||||
|
export class MyPipe {
|
||||||
|
constructor(d: Dep) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyPipe]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
|
const genFile =
|
||||||
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
|
||||||
|
expect(genFile.source).toContain(`import * as import0 from '/app/app.module'`);
|
||||||
|
expect(genFile.source).toContain('export function MyPipeNgSummary()');
|
||||||
|
// Note: CompileSummaryKind.Pipe = 1
|
||||||
|
expect(genFile.source).toMatch(/summaryKind: 0,\s*type: \{\s*reference: import0.MyPipe/);
|
||||||
|
expect(genFile.source).toContain('token: {identifier: {reference: import0.Dep}}');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create @Directive summaries', fakeAsync(() => {
|
||||||
|
const appDir = {
|
||||||
|
'app.module.ts': `
|
||||||
|
import { Directive, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
export class Dep {}
|
||||||
|
|
||||||
|
@Directive({selector: '[myDir]'})
|
||||||
|
export class MyDirective {
|
||||||
|
constructor(a: Dep) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyDirective]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
|
const genFile =
|
||||||
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
|
||||||
|
expect(genFile.source).toContain(`import * as import0 from '/app/app.module'`);
|
||||||
|
expect(genFile.source).toContain('export function MyDirectiveNgSummary()');
|
||||||
|
// Note: CompileSummaryKind.Directive = 1
|
||||||
|
expect(genFile.source)
|
||||||
|
.toMatch(/summaryKind: 1,\s*type: \{\s*reference: import0.MyDirective/);
|
||||||
|
expect(genFile.source).toContain('token: {identifier: {reference: import0.Dep}}');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create @NgModule summaries', fakeAsync(() => {
|
||||||
|
const appDir = {
|
||||||
|
'app.module.ts': `
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
export class Dep {}
|
||||||
|
|
||||||
|
@NgModule()
|
||||||
|
export class MyModule {
|
||||||
|
constructor(d: Dep) {}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
|
const genFile =
|
||||||
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
|
||||||
|
expect(genFile.source).toContain(`import * as import0 from '/app/app.module'`);
|
||||||
|
expect(genFile.source).toContain('export function MyModuleNgSummary()');
|
||||||
|
// Note: CompileSummaryKind.NgModule = 2
|
||||||
|
expect(genFile.source).toMatch(/summaryKind: 2,\s*type: \{\s*reference: import0.MyModule/);
|
||||||
|
expect(genFile.source).toContain('token: {identifier: {reference: import0.Dep}}');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should embed useClass provider summaries in @Directive summaries', fakeAsync(() => {
|
||||||
|
const appDir = {
|
||||||
|
'app.service.ts': `
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
export class Dep {}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MyService {
|
||||||
|
constructor(d: Dep) {}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'app.module.ts': `
|
||||||
|
import { Directive, NgModule } from '@angular/core';
|
||||||
|
import { MyService } from './app.service';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[myDir]',
|
||||||
|
providers: [MyService]
|
||||||
|
})
|
||||||
|
export class MyDirective {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyDirective]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
|
const genFile =
|
||||||
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
|
||||||
|
expect(genFile.source).toMatch(/useClass: \{\s*reference: import1.MyService/);
|
||||||
|
// Note: CompileSummaryKind.Injectable = 3
|
||||||
|
expect(genFile.source).toMatch(/summaryKind: 3,\s*type: \{\s*reference: import1.MyService/);
|
||||||
|
expect(genFile.source).toContain('token: {identifier: {reference: import1.Dep}}');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should embed useClass provider summaries into @NgModule summaries', fakeAsync(() => {
|
||||||
|
const appDir = {
|
||||||
|
'app.service.ts': `
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
export class Dep {}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MyService {
|
||||||
|
constructor(d: Dep) {}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'app.module.ts': `
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { MyService } from './app.service';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
providers: [MyService]
|
||||||
|
})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
|
const genFile =
|
||||||
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
|
||||||
|
expect(genFile.source).toMatch(/useClass: \{\s*reference: import1.MyService/);
|
||||||
|
// Note: CompileSummaryKind.Injectable = 3
|
||||||
|
expect(genFile.source).toMatch(/summaryKind: 3,\s*type: \{\s*reference: import1.MyService/);
|
||||||
|
expect(genFile.source).toContain('token: {identifier: {reference: import1.Dep}}');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should reference declared @Directive and @Pipe summaries in @NgModule summaries',
|
||||||
|
fakeAsync(() => {
|
||||||
|
const appDir = {
|
||||||
|
'app.module.ts': `
|
||||||
|
import { Directive, Pipe, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({selector: '[myDir]'})
|
||||||
|
export class MyDirective {}
|
||||||
|
|
||||||
|
@Pipe({name: 'myPipe'})
|
||||||
|
export class MyPipe {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyDirective, MyPipe]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
|
const genFile =
|
||||||
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
|
||||||
|
expect(genFile.source)
|
||||||
|
.toMatch(
|
||||||
|
/export function MyModuleNgSummary()[^;]*,\s*MyDirectiveNgSummary,\s*MyPipeNgSummary\s*\]\s*;/);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should reference imported @NgModule summaries in @NgModule summaries', fakeAsync(() => {
|
||||||
|
const appDir = {
|
||||||
|
'app.module.ts': `
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
@NgModule()
|
||||||
|
export class MyImportedModule {}
|
||||||
|
|
||||||
|
@NgModule({imports: [MyImportedModule]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
|
const genFile =
|
||||||
|
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
||||||
|
|
||||||
|
expect(genFile.source)
|
||||||
|
.toMatch(
|
||||||
|
/export function MyModuleNgSummary()[^;]*,\s*MyImportedModuleNgSummary\s*\]\s*;/);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create and use reexports for imported NgModules ' +
|
||||||
|
'accross compilation units',
|
||||||
|
fakeAsync(() => {
|
||||||
|
const lib1In = {
|
||||||
|
'lib1': {
|
||||||
|
'module.ts': `
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
@NgModule()
|
||||||
|
export class Lib1Module {}
|
||||||
|
`,
|
||||||
|
'reexport.ts': `
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
@NgModule()
|
||||||
|
export class ReexportModule {}
|
||||||
|
|
||||||
|
export const reexports: any[] = [ ReexportModule ];
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const {outDir: lib2In, genFiles: lib1Gen} = compileApp(lib1In, {useSummaries: true});
|
||||||
|
|
||||||
|
lib2In['lib2'] = {
|
||||||
|
'module.ts': `
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { Lib1Module } from '../lib1/module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [Lib1Module]
|
||||||
|
})
|
||||||
|
export class Lib2Module {}
|
||||||
|
`,
|
||||||
|
'reexport.ts': `
|
||||||
|
import { reexports as reexports_lib1 } from '../lib1/reexport';
|
||||||
|
export const reexports: any[] = [ reexports_lib1 ];
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
const {outDir: lib3In, genFiles: lib2Gen} = compileApp(lib2In, {useSummaries: true});
|
||||||
|
|
||||||
|
const lib2ModuleNgSummary = lib2Gen.find(f => f.genFileUrl === '/lib2/module.ngsummary.ts');
|
||||||
|
const lib2ReexportNgSummary =
|
||||||
|
lib2Gen.find(f => f.genFileUrl === '/lib2/reexport.ngsummary.ts');
|
||||||
|
|
||||||
|
// ngsummaries should add reexports for imported NgModules from a direct dependency
|
||||||
|
expect(lib2ModuleNgSummary.source)
|
||||||
|
.toContain(
|
||||||
|
`export {Lib1ModuleNgSummary as Lib1Module_1NgSummary} from '/lib1/module.ngsummary'`);
|
||||||
|
// ngsummaries should add reexports for reexported values from a direct dependency
|
||||||
|
expect(lib2ReexportNgSummary.source)
|
||||||
|
.toContain(
|
||||||
|
`export {ReexportModuleNgSummary as ReexportModule_2NgSummary} from '/lib1/reexport.ngsummary'`);
|
||||||
|
|
||||||
|
lib3In['lib3'] = {
|
||||||
|
'module.ts': `
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { Lib2Module } from '../lib2/module';
|
||||||
|
import { reexports } from '../lib2/reexport';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [Lib2Module, reexports]
|
||||||
|
})
|
||||||
|
export class Lib3Module {}
|
||||||
|
`,
|
||||||
|
'reexport.ts': `
|
||||||
|
import { reexports as reexports_lib2 } from '../lib2/reexport';
|
||||||
|
export const reexports: any[] = [ reexports_lib2 ];
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const lib3Gen = compileApp(lib3In, {useSummaries: true}).genFiles;
|
||||||
|
const lib3ModuleNgSummary = lib3Gen.find(f => f.genFileUrl === '/lib3/module.ngsummary.ts');
|
||||||
|
const lib3ReexportNgSummary =
|
||||||
|
lib3Gen.find(f => f.genFileUrl === '/lib3/reexport.ngsummary.ts');
|
||||||
|
|
||||||
|
// ngsummary.ts files should use the reexported values from direct and deep deps
|
||||||
|
expect(lib3ModuleNgSummary.source)
|
||||||
|
.toContain(`import * as import4 from '/lib2/module.ngsummary'`);
|
||||||
|
expect(lib3ModuleNgSummary.source)
|
||||||
|
.toContain(`import * as import5 from '/lib2/reexport.ngsummary'`);
|
||||||
|
expect(lib3ModuleNgSummary.source)
|
||||||
|
.toMatch(
|
||||||
|
/export function Lib3ModuleNgSummary()[^;]*,\s*import4.Lib1Module_1NgSummary,\s*import4.Lib2ModuleNgSummary,\s*import5.ReexportModule_2NgSummary\s*\]\s*;/);
|
||||||
|
|
||||||
|
// ngsummaries should add reexports for imported NgModules from a deep dependency
|
||||||
|
expect(lib3ModuleNgSummary.source)
|
||||||
|
.toContain(
|
||||||
|
`export {Lib1Module_1NgSummary as Lib1Module_1NgSummary,Lib2ModuleNgSummary as Lib2Module_2NgSummary} from '/lib2/module.ngsummary'`);
|
||||||
|
// ngsummaries should add reexports for reexported values from a deep dependency
|
||||||
|
expect(lib3ReexportNgSummary.source)
|
||||||
|
.toContain(
|
||||||
|
`export {ReexportModule_2NgSummary as ReexportModule_3NgSummary} from '/lib2/reexport.ngsummary'`);
|
||||||
|
}));
|
||||||
|
});
|
|
@ -372,7 +372,7 @@ export class MockSummaryResolver implements SummaryResolver<StaticSymbol> {
|
||||||
symbol: StaticSymbol,
|
symbol: StaticSymbol,
|
||||||
importAs: StaticSymbol
|
importAs: StaticSymbol
|
||||||
}[] = []) {}
|
}[] = []) {}
|
||||||
|
addSummary(summary: Summary<StaticSymbol>) { this.summaries.push(summary); };
|
||||||
resolveSummary(reference: StaticSymbol): Summary<StaticSymbol> {
|
resolveSummary(reference: StaticSymbol): Summary<StaticSymbol> {
|
||||||
return this.summaries.find(summary => summary.symbol === reference);
|
return this.summaries.find(summary => summary.symbol === reference);
|
||||||
};
|
};
|
||||||
|
@ -442,6 +442,10 @@ export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost {
|
||||||
return '/tmp/' + modulePath + '.d.ts';
|
return '/tmp/' + modulePath + '.d.ts';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileNameToModuleName(filePath: string, containingFile: string) {
|
||||||
|
return filePath.replace(/(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); }
|
getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); }
|
||||||
|
|
||||||
private _getMetadataFor(filePath: string): any {
|
private _getMetadataFor(filePath: string): any {
|
||||||
|
|
|
@ -27,17 +27,17 @@ export function main() {
|
||||||
summaryResolver = new AotSummaryResolver(host, symbolCache);
|
summaryResolver = new AotSummaryResolver(host, symbolCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
function serialize(symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string {
|
function serialize(symbols: ResolvedStaticSymbol[]): string {
|
||||||
// Note: Don't use the top level host / summaryResolver as they might not be created yet
|
// Note: Don't use the top level host / summaryResolver as they might not be created yet
|
||||||
const mockSummaryResolver = new MockSummaryResolver([]);
|
const mockSummaryResolver = new MockSummaryResolver([]);
|
||||||
const symbolResolver = new StaticSymbolResolver(
|
const symbolResolver = new StaticSymbolResolver(
|
||||||
new MockStaticSymbolResolverHost({}), symbolCache, mockSummaryResolver);
|
new MockStaticSymbolResolverHost({}), symbolCache, mockSummaryResolver);
|
||||||
return serializeSummaries(mockSummaryResolver, symbolResolver, symbols, types).json;
|
return serializeSummaries(mockSummaryResolver, symbolResolver, symbols, []).json;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should load serialized summary files', () => {
|
it('should load serialized summary files', () => {
|
||||||
const asymbol = symbolCache.get('/a.d.ts', 'a');
|
const asymbol = symbolCache.get('/a.d.ts', 'a');
|
||||||
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
|
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}])});
|
||||||
expect(summaryResolver.resolveSummary(asymbol)).toEqual({symbol: asymbol, metadata: 1});
|
expect(summaryResolver.resolveSummary(asymbol)).toEqual({symbol: asymbol, metadata: 1});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,13 +51,13 @@ export function main() {
|
||||||
|
|
||||||
it('should cache summaries', () => {
|
it('should cache summaries', () => {
|
||||||
const asymbol = symbolCache.get('/a.d.ts', 'a');
|
const asymbol = symbolCache.get('/a.d.ts', 'a');
|
||||||
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
|
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}])});
|
||||||
expect(summaryResolver.resolveSummary(asymbol)).toBe(summaryResolver.resolveSummary(asymbol));
|
expect(summaryResolver.resolveSummary(asymbol)).toBe(summaryResolver.resolveSummary(asymbol));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return all symbols in a summary', () => {
|
it('should return all symbols in a summary', () => {
|
||||||
const asymbol = symbolCache.get('/a.d.ts', 'a');
|
const asymbol = symbolCache.get('/a.d.ts', 'a');
|
||||||
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}], [])});
|
init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}])});
|
||||||
expect(summaryResolver.getSymbolsOf('/a.d.ts')).toEqual([asymbol]);
|
expect(summaryResolver.getSymbolsOf('/a.d.ts')).toEqual([asymbol]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,13 +66,13 @@ export function main() {
|
||||||
const srcSymbol = symbolCache.get('/src.ts', 'Src');
|
const srcSymbol = symbolCache.get('/src.ts', 'Src');
|
||||||
init({
|
init({
|
||||||
'/src.ngsummary.json':
|
'/src.ngsummary.json':
|
||||||
serialize([{symbol: srcSymbol, metadata: 1}, {symbol: libSymbol, metadata: 2}], [])
|
serialize([{symbol: srcSymbol, metadata: 1}, {symbol: libSymbol, metadata: 2}])
|
||||||
});
|
});
|
||||||
summaryResolver.getSymbolsOf('/src.d.ts');
|
summaryResolver.getSymbolsOf('/src.d.ts');
|
||||||
|
|
||||||
expect(summaryResolver.getImportAs(symbolCache.get('/src.d.ts', 'Src'))).toBeFalsy();
|
expect(summaryResolver.getImportAs(symbolCache.get('/src.d.ts', 'Src'))).toBeFalsy();
|
||||||
expect(summaryResolver.getImportAs(libSymbol))
|
expect(summaryResolver.getImportAs(libSymbol))
|
||||||
.toBe(symbolCache.get('/src.ngfactory.ts', 'Lib_1'));
|
.toBe(symbolCache.get('/src.ngfactory.d.ts', 'Lib_1'));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isLibraryFile', () => {
|
describe('isLibraryFile', () => {
|
||||||
|
|
|
@ -66,11 +66,14 @@ export function main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[<any>{
|
[{
|
||||||
summaryKind: CompileSummaryKind.Injectable,
|
summary: {
|
||||||
type: {
|
summaryKind: CompileSummaryKind.Injectable,
|
||||||
reference: symbolCache.get('/tmp/some_service.ts', 'SomeService'),
|
type: {
|
||||||
}
|
reference: symbolCache.get('/tmp/some_service.ts', 'SomeService'),
|
||||||
|
}
|
||||||
|
} as any,
|
||||||
|
metadata: null as any
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,17 +106,25 @@ export function main() {
|
||||||
() => {
|
() => {
|
||||||
init();
|
init();
|
||||||
const externalSerialized = serializeSummaries(summaryResolver, symbolResolver, [], [
|
const externalSerialized = serializeSummaries(summaryResolver, symbolResolver, [], [
|
||||||
<any>{
|
{
|
||||||
summaryKind: CompileSummaryKind.Pipe,
|
summary: {
|
||||||
type: {
|
summaryKind: CompileSummaryKind.Pipe,
|
||||||
reference: symbolCache.get('/tmp/external.ts', 'SomeExternalPipe'),
|
type: {
|
||||||
}
|
reference: symbolCache.get('/tmp/external.ts', 'SomeExternalPipe'),
|
||||||
|
}
|
||||||
|
} as any,
|
||||||
|
metadata: null as any
|
||||||
},
|
},
|
||||||
<any>{
|
{
|
||||||
summaryKind: CompileSummaryKind.Directive,
|
summary: {
|
||||||
type: {
|
summaryKind: CompileSummaryKind.Directive,
|
||||||
reference: symbolCache.get('/tmp/external.ts', 'SomeExternalDir'),
|
type: {
|
||||||
}
|
reference: symbolCache.get('/tmp/external.ts', 'SomeExternalDir'),
|
||||||
|
},
|
||||||
|
providers: [],
|
||||||
|
viewProviders: [],
|
||||||
|
} as any,
|
||||||
|
metadata: null as any
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
init({
|
init({
|
||||||
|
@ -121,17 +132,22 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const serialized = serializeSummaries(
|
const serialized = serializeSummaries(
|
||||||
summaryResolver, symbolResolver, [], [<any>{
|
summaryResolver, symbolResolver, [], [{
|
||||||
summaryKind: CompileSummaryKind.NgModule,
|
summary: <any>{
|
||||||
type: {reference: symbolCache.get('/tmp/some_module.ts', 'SomeModule')},
|
summaryKind: CompileSummaryKind.NgModule,
|
||||||
exportedPipes: [
|
type: {reference: symbolCache.get('/tmp/some_module.ts', 'SomeModule')},
|
||||||
{reference: symbolCache.get('/tmp/some_pipe.ts', 'SomePipe')},
|
exportedPipes: [
|
||||||
{reference: symbolCache.get('/tmp/external.d.ts', 'SomeExternalPipe')}
|
{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')},
|
exportedDirectives: [
|
||||||
{reference: symbolCache.get('/tmp/external.d.ts', 'SomeExternalDir')}
|
{reference: symbolCache.get('/tmp/some_dir.ts', 'SomeDir')},
|
||||||
]
|
{reference: symbolCache.get('/tmp/external.d.ts', 'SomeExternalDir')}
|
||||||
|
],
|
||||||
|
providers: [],
|
||||||
|
modules: [],
|
||||||
|
},
|
||||||
|
metadata: null as any
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
const summaries = deserializeSummaries(symbolCache, serialized.json).summaries;
|
const summaries = deserializeSummaries(symbolCache, serialized.json).summaries;
|
||||||
|
@ -157,11 +173,14 @@ export function main() {
|
||||||
metadata: {__symbolic: 'class'}
|
metadata: {__symbolic: 'class'}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[<any>{
|
[{
|
||||||
summaryKind: CompileSummaryKind.Injectable,
|
summary: {
|
||||||
type: {
|
summaryKind: CompileSummaryKind.Injectable,
|
||||||
reference: symbolCache.get('/tmp/external_svc.ts', 'SomeService'),
|
type: {
|
||||||
}
|
reference: symbolCache.get('/tmp/external_svc.ts', 'SomeService'),
|
||||||
|
}
|
||||||
|
} as any,
|
||||||
|
metadata: null as any
|
||||||
}]);
|
}]);
|
||||||
init(
|
init(
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,9 +26,9 @@ export * from './directive_resolver_mock';
|
||||||
export * from './ng_module_resolver_mock';
|
export * from './ng_module_resolver_mock';
|
||||||
export * from './pipe_resolver_mock';
|
export * from './pipe_resolver_mock';
|
||||||
|
|
||||||
import {createPlatformFactory, ModuleWithComponentFactories, Injectable, CompilerOptions, COMPILER_OPTIONS, CompilerFactory, NgModuleFactory, Injector, NgModule, Component, Directive, Pipe, Type, PlatformRef} from '@angular/core';
|
import {createPlatformFactory, ModuleWithComponentFactories, Injectable, CompilerOptions, COMPILER_OPTIONS, CompilerFactory, ComponentFactory, NgModuleFactory, Injector, NgModule, Component, Directive, Pipe, Type, PlatformRef, ɵstringify} from '@angular/core';
|
||||||
import {MetadataOverride, ɵTestingCompilerFactory as TestingCompilerFactory, ɵTestingCompiler as TestingCompiler} from '@angular/core/testing';
|
import {MetadataOverride, ɵTestingCompilerFactory as TestingCompilerFactory, ɵTestingCompiler as TestingCompiler} from '@angular/core/testing';
|
||||||
import {platformCoreDynamic, JitCompiler, DirectiveResolver, NgModuleResolver, PipeResolver} from '@angular/compiler';
|
import {platformCoreDynamic, JitCompiler, DirectiveResolver, NgModuleResolver, PipeResolver, CompileMetadataResolver} from '@angular/compiler';
|
||||||
import {MockDirectiveResolver} from './directive_resolver_mock';
|
import {MockDirectiveResolver} from './directive_resolver_mock';
|
||||||
import {MockNgModuleResolver} from './ng_module_resolver_mock';
|
import {MockNgModuleResolver} from './ng_module_resolver_mock';
|
||||||
import {MockPipeResolver} from './pipe_resolver_mock';
|
import {MockPipeResolver} from './pipe_resolver_mock';
|
||||||
|
@ -42,7 +42,8 @@ export class TestingCompilerFactoryImpl implements TestingCompilerFactory {
|
||||||
const compiler = <JitCompiler>this._compilerFactory.createCompiler(options);
|
const compiler = <JitCompiler>this._compilerFactory.createCompiler(options);
|
||||||
return new TestingCompilerImpl(
|
return new TestingCompilerImpl(
|
||||||
compiler, compiler.injector.get(MockDirectiveResolver),
|
compiler, compiler.injector.get(MockDirectiveResolver),
|
||||||
compiler.injector.get(MockPipeResolver), compiler.injector.get(MockNgModuleResolver));
|
compiler.injector.get(MockPipeResolver), compiler.injector.get(MockNgModuleResolver),
|
||||||
|
compiler.injector.get(CompileMetadataResolver));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +51,8 @@ export class TestingCompilerImpl implements TestingCompiler {
|
||||||
private _overrider = new MetadataOverrider();
|
private _overrider = new MetadataOverrider();
|
||||||
constructor(
|
constructor(
|
||||||
private _compiler: JitCompiler, private _directiveResolver: MockDirectiveResolver,
|
private _compiler: JitCompiler, private _directiveResolver: MockDirectiveResolver,
|
||||||
private _pipeResolver: MockPipeResolver, private _moduleResolver: MockNgModuleResolver) {}
|
private _pipeResolver: MockPipeResolver, private _moduleResolver: MockNgModuleResolver,
|
||||||
|
private _metadataResolver: CompileMetadataResolver) {}
|
||||||
get injector(): Injector { return this._compiler.injector; }
|
get injector(): Injector { return this._compiler.injector; }
|
||||||
|
|
||||||
compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> {
|
compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> {
|
||||||
|
@ -73,25 +75,40 @@ export class TestingCompilerImpl implements TestingCompiler {
|
||||||
return this._compiler.getNgContentSelectors(component);
|
return this._compiler.getNgContentSelectors(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getComponentFactory<T>(component: Type<T>): ComponentFactory<T> {
|
||||||
|
return this._compiler.getComponentFactory(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkOverrideAllowed(type: Type<any>) {
|
||||||
|
if (this._compiler.hasAotSummary(type)) {
|
||||||
|
throw new Error(`${ɵstringify(type)} was AOT compiled, so its metadata cannot be changed.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
|
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
|
||||||
|
this.checkOverrideAllowed(ngModule);
|
||||||
const oldMetadata = this._moduleResolver.resolve(ngModule, false);
|
const oldMetadata = this._moduleResolver.resolve(ngModule, false);
|
||||||
this._moduleResolver.setNgModule(
|
this._moduleResolver.setNgModule(
|
||||||
ngModule, this._overrider.overrideMetadata(NgModule, oldMetadata, override));
|
ngModule, this._overrider.overrideMetadata(NgModule, oldMetadata, override));
|
||||||
}
|
}
|
||||||
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void {
|
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void {
|
||||||
|
this.checkOverrideAllowed(directive);
|
||||||
const oldMetadata = this._directiveResolver.resolve(directive, false);
|
const oldMetadata = this._directiveResolver.resolve(directive, false);
|
||||||
this._directiveResolver.setDirective(
|
this._directiveResolver.setDirective(
|
||||||
directive, this._overrider.overrideMetadata(Directive, oldMetadata !, override));
|
directive, this._overrider.overrideMetadata(Directive, oldMetadata !, override));
|
||||||
}
|
}
|
||||||
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void {
|
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void {
|
||||||
|
this.checkOverrideAllowed(component);
|
||||||
const oldMetadata = this._directiveResolver.resolve(component, false);
|
const oldMetadata = this._directiveResolver.resolve(component, false);
|
||||||
this._directiveResolver.setDirective(
|
this._directiveResolver.setDirective(
|
||||||
component, this._overrider.overrideMetadata(Component, oldMetadata !, override));
|
component, this._overrider.overrideMetadata(Component, oldMetadata !, override));
|
||||||
}
|
}
|
||||||
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void {
|
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void {
|
||||||
|
this.checkOverrideAllowed(pipe);
|
||||||
const oldMetadata = this._pipeResolver.resolve(pipe, false);
|
const oldMetadata = this._pipeResolver.resolve(pipe, false);
|
||||||
this._pipeResolver.setPipe(pipe, this._overrider.overrideMetadata(Pipe, oldMetadata, override));
|
this._pipeResolver.setPipe(pipe, this._overrider.overrideMetadata(Pipe, oldMetadata, override));
|
||||||
}
|
}
|
||||||
|
loadAotSummaries(summaries: () => any[]) { this._compiler.loadAotSummaries(summaries); }
|
||||||
clearCache(): void { this._compiler.clearCache(); }
|
clearCache(): void { this._compiler.clearCache(); }
|
||||||
clearCacheFor(type: Type<any>) { this._compiler.clearCacheFor(type); }
|
clearCacheFor(type: Type<any>) { this._compiler.clearCacheFor(type); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
/**
|
||||||
|
* @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 {ResourceLoader} from '@angular/compiler';
|
||||||
|
import {CompileMetadataResolver} from '@angular/compiler/src/metadata_resolver';
|
||||||
|
import {MockResourceLoader} from '@angular/compiler/testing/src/resource_loader_mock';
|
||||||
|
import {Component, Directive, Injectable, NgModule, Pipe, Type} from '@angular/core';
|
||||||
|
import {TestBed, async, getTestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('Jit Summaries', () => {
|
||||||
|
let instances: Map<any, Base>;
|
||||||
|
|
||||||
|
class SomeDep {}
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
static annotations: any[];
|
||||||
|
static parameters: any[][];
|
||||||
|
|
||||||
|
constructor(public dep: SomeDep) {
|
||||||
|
instances.set(Object.getPrototypeOf(this).constructor, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectInstanceCreated(type: any) {
|
||||||
|
const instance = instances.get(type) !;
|
||||||
|
expect(instance).toBeDefined();
|
||||||
|
expect(instance.dep instanceof SomeDep).toBe(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SomeModule extends Base {}
|
||||||
|
|
||||||
|
class SomePrivateComponent extends Base {}
|
||||||
|
|
||||||
|
class SomePublicComponent extends Base {}
|
||||||
|
|
||||||
|
class SomeDirective extends Base {}
|
||||||
|
|
||||||
|
class SomePipe extends Base {
|
||||||
|
transform(value: any) { return value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SomeService extends Base {}
|
||||||
|
|
||||||
|
function resetTestEnvironmentWithSummaries(summaries?: () => any[]) {
|
||||||
|
const {platform, ngModule} = getTestBed();
|
||||||
|
TestBed.resetTestEnvironment();
|
||||||
|
TestBed.initTestEnvironment(ngModule, platform, summaries);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSummaries() {
|
||||||
|
const resourceLoader = new MockResourceLoader();
|
||||||
|
|
||||||
|
setMetadata(resourceLoader);
|
||||||
|
|
||||||
|
TestBed.configureCompiler({providers: [{provide: ResourceLoader, useValue: resourceLoader}]});
|
||||||
|
TestBed.configureTestingModule({imports: [SomeModule], providers: [SomeDep]});
|
||||||
|
|
||||||
|
TestBed.compileComponents().then(() => {
|
||||||
|
const metadataResolver = TestBed.get(CompileMetadataResolver) as CompileMetadataResolver;
|
||||||
|
const summaries = [
|
||||||
|
metadataResolver.getNgModuleSummary(SomeModule),
|
||||||
|
// test nesting via closures, as we use this in the generated code too.
|
||||||
|
() =>
|
||||||
|
[metadataResolver.getDirectiveSummary(SomePublicComponent),
|
||||||
|
metadataResolver.getDirectiveSummary(SomePrivateComponent),
|
||||||
|
],
|
||||||
|
metadataResolver.getDirectiveSummary(SomeDirective),
|
||||||
|
metadataResolver.getPipeSummary(SomePipe),
|
||||||
|
metadataResolver.getInjectableSummary(SomeService)
|
||||||
|
];
|
||||||
|
clearMetadata();
|
||||||
|
resetTestEnvironmentWithSummaries(() => summaries);
|
||||||
|
});
|
||||||
|
|
||||||
|
resourceLoader.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMetadata(resourceLoader: MockResourceLoader) {
|
||||||
|
Base.parameters = [[SomeDep]];
|
||||||
|
|
||||||
|
SomeModule.annotations = [new NgModule({
|
||||||
|
declarations: [SomePublicComponent, SomePrivateComponent, SomeDirective, SomePipe],
|
||||||
|
exports: [SomeDirective, SomePipe, SomePublicComponent],
|
||||||
|
providers: [SomeService]
|
||||||
|
})];
|
||||||
|
|
||||||
|
SomePublicComponent.annotations = [new Component({templateUrl: 'somePublicUrl.html'})];
|
||||||
|
resourceLoader.expect('somePublicUrl.html', `Hello public world!`);
|
||||||
|
|
||||||
|
SomePrivateComponent.annotations = [new Component({templateUrl: 'somePrivateUrl.html'})];
|
||||||
|
resourceLoader.expect('somePrivateUrl.html', `Hello private world!`);
|
||||||
|
|
||||||
|
SomeDirective.annotations = [new Directive({selector: '[someDir]'})];
|
||||||
|
|
||||||
|
SomePipe.annotations = [new Pipe({name: 'somePipe'})];
|
||||||
|
|
||||||
|
SomeService.annotations = [new Injectable()];
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearMetadata() {
|
||||||
|
Base.parameters = [];
|
||||||
|
SomeModule.annotations = [];
|
||||||
|
SomePublicComponent.annotations = [];
|
||||||
|
SomePrivateComponent.annotations = [];
|
||||||
|
SomeDirective.annotations = [];
|
||||||
|
SomePipe.annotations = [];
|
||||||
|
SomeService.annotations = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
instances = new Map<any, any>();
|
||||||
|
createSummaries();
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(() => { resetTestEnvironmentWithSummaries(); });
|
||||||
|
|
||||||
|
it('should use directive metadata from summaries', () => {
|
||||||
|
@Component({template: '<div someDir></div>'})
|
||||||
|
class TestComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed
|
||||||
|
.configureTestingModule({providers: [SomeDep], declarations: [TestComp, SomeDirective]})
|
||||||
|
.createComponent(TestComp);
|
||||||
|
expectInstanceCreated(SomeDirective);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use pipe metadata from summaries', () => {
|
||||||
|
@Component({template: '{{1 | somePipe}}'})
|
||||||
|
class TestComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({providers: [SomeDep], declarations: [TestComp, SomePipe]})
|
||||||
|
.createComponent(TestComp);
|
||||||
|
expectInstanceCreated(SomePipe);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use Service metadata from summaries', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [SomeService, SomeDep],
|
||||||
|
});
|
||||||
|
TestBed.get(SomeService);
|
||||||
|
expectInstanceCreated(SomeService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use NgModule metadata from summaries', () => {
|
||||||
|
@Component({template: '<div someDir>{{1 | somePipe}}</div>'})
|
||||||
|
class TestComp {
|
||||||
|
constructor(service: SomeService) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed
|
||||||
|
.configureTestingModule(
|
||||||
|
{providers: [SomeDep], declarations: [TestComp], imports: [SomeModule]})
|
||||||
|
.createComponent(TestComp);
|
||||||
|
|
||||||
|
expectInstanceCreated(SomeModule);
|
||||||
|
expectInstanceCreated(SomeDirective);
|
||||||
|
expectInstanceCreated(SomePipe);
|
||||||
|
expectInstanceCreated(SomeService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to create private components from imported NgModule summaries', () => {
|
||||||
|
TestBed.configureTestingModule({providers: [SomeDep], imports: [SomeModule]})
|
||||||
|
.createComponent(SomePrivateComponent);
|
||||||
|
expectInstanceCreated(SomePrivateComponent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when trying to mock a type with a summary', () => {
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
expect(() => TestBed.overrideComponent(SomePrivateComponent, {add: {}}).compileComponents())
|
||||||
|
.toThrowError(
|
||||||
|
'SomePrivateComponent was AOT compiled, so its metadata cannot be changed.');
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
expect(() => TestBed.overrideDirective(SomeDirective, {add: {}}).compileComponents())
|
||||||
|
.toThrowError('SomeDirective was AOT compiled, so its metadata cannot be changed.');
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
expect(() => TestBed.overridePipe(SomePipe, {add: {name: 'test'}}).compileComponents())
|
||||||
|
.toThrowError('SomePipe was AOT compiled, so its metadata cannot be changed.');
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
expect(() => TestBed.overrideModule(SomeModule, {add: {}}).compileComponents())
|
||||||
|
.toThrowError('SomeModule was AOT compiled, so its metadata cannot be changed.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -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 {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, ɵERROR_COMPONENT_TYPE, ɵstringify as stringify} from '@angular/core';
|
import {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, ɵERROR_COMPONENT_TYPE, ɵstringify as stringify} from '@angular/core';
|
||||||
|
|
||||||
import {AsyncTestCompleter} from './async_test_completer';
|
import {AsyncTestCompleter} from './async_test_completer';
|
||||||
import {ComponentFixture} from './component_fixture';
|
import {ComponentFixture} from './component_fixture';
|
||||||
import {MetadataOverride} from './metadata_override';
|
import {MetadataOverride} from './metadata_override';
|
||||||
|
@ -69,9 +70,10 @@ export class TestBed implements Injector {
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
static initTestEnvironment(ngModule: Type<any>|Type<any>[], platform: PlatformRef): TestBed {
|
static initTestEnvironment(
|
||||||
|
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): TestBed {
|
||||||
const testBed = getTestBed();
|
const testBed = getTestBed();
|
||||||
testBed.initTestEnvironment(ngModule, platform);
|
testBed.initTestEnvironment(ngModule, platform, aotSummaries);
|
||||||
return testBed;
|
return testBed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +153,7 @@ export class TestBed implements Injector {
|
||||||
|
|
||||||
private _compiler: TestingCompiler = null !;
|
private _compiler: TestingCompiler = null !;
|
||||||
private _moduleRef: NgModuleRef<any> = null !;
|
private _moduleRef: NgModuleRef<any> = null !;
|
||||||
private _moduleWithComponentFactories: ModuleWithComponentFactories<any> = null !;
|
private _moduleFactory: NgModuleFactory<any> = null !;
|
||||||
|
|
||||||
private _compilerOptions: CompilerOptions[] = [];
|
private _compilerOptions: CompilerOptions[] = [];
|
||||||
|
|
||||||
|
@ -166,6 +168,12 @@ export class TestBed implements Injector {
|
||||||
private _schemas: Array<SchemaMetadata|any[]> = [];
|
private _schemas: Array<SchemaMetadata|any[]> = [];
|
||||||
private _activeFixtures: ComponentFixture<any>[] = [];
|
private _activeFixtures: ComponentFixture<any>[] = [];
|
||||||
|
|
||||||
|
private _aotSummaries: () => any[] = () => [];
|
||||||
|
|
||||||
|
platform: PlatformRef = null !;
|
||||||
|
|
||||||
|
ngModule: Type<any>|Type<any>[] = null !;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an
|
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an
|
||||||
* angular module. These are common to every test in the suite.
|
* angular module. These are common to every test in the suite.
|
||||||
|
@ -179,12 +187,16 @@ export class TestBed implements Injector {
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
initTestEnvironment(ngModule: Type<any>|Type<any>[], platform: PlatformRef) {
|
initTestEnvironment(
|
||||||
|
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]) {
|
||||||
if (this.platform || this.ngModule) {
|
if (this.platform || this.ngModule) {
|
||||||
throw new Error('Cannot set base providers because it has already been called');
|
throw new Error('Cannot set base providers because it has already been called');
|
||||||
}
|
}
|
||||||
this.platform = platform;
|
this.platform = platform;
|
||||||
this.ngModule = ngModule;
|
this.ngModule = ngModule;
|
||||||
|
if (aotSummaries) {
|
||||||
|
this._aotSummaries = aotSummaries;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -196,6 +208,7 @@ export class TestBed implements Injector {
|
||||||
this.resetTestingModule();
|
this.resetTestingModule();
|
||||||
this.platform = null !;
|
this.platform = null !;
|
||||||
this.ngModule = null !;
|
this.ngModule = null !;
|
||||||
|
this._aotSummaries = () => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
resetTestingModule() {
|
resetTestingModule() {
|
||||||
|
@ -206,7 +219,7 @@ export class TestBed implements Injector {
|
||||||
this._pipeOverrides = [];
|
this._pipeOverrides = [];
|
||||||
|
|
||||||
this._moduleRef = null !;
|
this._moduleRef = null !;
|
||||||
this._moduleWithComponentFactories = null !;
|
this._moduleFactory = null !;
|
||||||
this._compilerOptions = [];
|
this._compilerOptions = [];
|
||||||
this._providers = [];
|
this._providers = [];
|
||||||
this._declarations = [];
|
this._declarations = [];
|
||||||
|
@ -223,10 +236,6 @@ export class TestBed implements Injector {
|
||||||
this._activeFixtures = [];
|
this._activeFixtures = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
platform: PlatformRef = null !;
|
|
||||||
|
|
||||||
ngModule: Type<any>|Type<any>[] = null !;
|
|
||||||
|
|
||||||
configureCompiler(config: {providers?: any[], useJit?: boolean}) {
|
configureCompiler(config: {providers?: any[], useJit?: boolean}) {
|
||||||
this._assertNotInstantiated('TestBed.configureCompiler', 'configure the compiler');
|
this._assertNotInstantiated('TestBed.configureCompiler', 'configure the compiler');
|
||||||
this._compilerOptions.push(config);
|
this._compilerOptions.push(config);
|
||||||
|
@ -249,14 +258,14 @@ export class TestBed implements Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponents(): Promise<any> {
|
compileComponents(): Promise<any> {
|
||||||
if (this._moduleWithComponentFactories || this._instantiated) {
|
if (this._moduleFactory || this._instantiated) {
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const moduleType = this._createCompilerAndModule();
|
const moduleType = this._createCompilerAndModule();
|
||||||
return this._compiler.compileModuleAndAllComponentsAsync(moduleType)
|
return this._compiler.compileModuleAndAllComponentsAsync(moduleType)
|
||||||
.then((moduleAndComponentFactories) => {
|
.then((moduleAndComponentFactories) => {
|
||||||
this._moduleWithComponentFactories = moduleAndComponentFactories;
|
this._moduleFactory = moduleAndComponentFactories.ngModuleFactory;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,11 +273,11 @@ export class TestBed implements Injector {
|
||||||
if (this._instantiated) {
|
if (this._instantiated) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this._moduleWithComponentFactories) {
|
if (!this._moduleFactory) {
|
||||||
try {
|
try {
|
||||||
const moduleType = this._createCompilerAndModule();
|
const moduleType = this._createCompilerAndModule();
|
||||||
this._moduleWithComponentFactories =
|
this._moduleFactory =
|
||||||
this._compiler.compileModuleAndAllComponentsSync(moduleType);
|
this._compiler.compileModuleAndAllComponentsSync(moduleType).ngModuleFactory;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (getComponentType(e)) {
|
if (getComponentType(e)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -282,7 +291,7 @@ export class TestBed implements Injector {
|
||||||
const ngZone = new NgZone({enableLongStackTrace: true});
|
const ngZone = new NgZone({enableLongStackTrace: true});
|
||||||
const ngZoneInjector = ReflectiveInjector.resolveAndCreate(
|
const ngZoneInjector = ReflectiveInjector.resolveAndCreate(
|
||||||
[{provide: NgZone, useValue: ngZone}], this.platform.injector);
|
[{provide: NgZone, useValue: ngZone}], this.platform.injector);
|
||||||
this._moduleRef = this._moduleWithComponentFactories.ngModuleFactory.create(ngZoneInjector);
|
this._moduleRef = this._moduleFactory.create(ngZoneInjector);
|
||||||
this._instantiated = true;
|
this._instantiated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,6 +309,7 @@ export class TestBed implements Injector {
|
||||||
this.platform.injector.get(TestingCompilerFactory);
|
this.platform.injector.get(TestingCompilerFactory);
|
||||||
this._compiler =
|
this._compiler =
|
||||||
compilerFactory.createTestingCompiler(this._compilerOptions.concat([{useDebug: true}]));
|
compilerFactory.createTestingCompiler(this._compilerOptions.concat([{useDebug: true}]));
|
||||||
|
this._compiler.loadAotSummaries(this._aotSummaries);
|
||||||
this._moduleOverrides.forEach((entry) => this._compiler.overrideModule(entry[0], entry[1]));
|
this._moduleOverrides.forEach((entry) => this._compiler.overrideModule(entry[0], entry[1]));
|
||||||
this._componentOverrides.forEach(
|
this._componentOverrides.forEach(
|
||||||
(entry) => this._compiler.overrideComponent(entry[0], entry[1]));
|
(entry) => this._compiler.overrideComponent(entry[0], entry[1]));
|
||||||
|
@ -356,8 +366,7 @@ export class TestBed implements Injector {
|
||||||
|
|
||||||
createComponent<T>(component: Type<T>): ComponentFixture<T> {
|
createComponent<T>(component: Type<T>): ComponentFixture<T> {
|
||||||
this._initIfNeeded();
|
this._initIfNeeded();
|
||||||
const componentFactory = this._moduleWithComponentFactories.componentFactories.find(
|
const componentFactory = this._compiler.getComponentFactory(component);
|
||||||
(compFactory) => compFactory.componentType === component);
|
|
||||||
|
|
||||||
if (!componentFactory) {
|
if (!componentFactory) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
@ -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, CompilerOptions, Component, Directive, Injector, NgModule, Pipe, Type} from '@angular/core';
|
import {Compiler, CompilerOptions, Component, ComponentFactory, Directive, Injector, NgModule, Pipe, Type} from '@angular/core';
|
||||||
|
|
||||||
import {MetadataOverride} from './metadata_override';
|
import {MetadataOverride} from './metadata_override';
|
||||||
|
|
||||||
|
@ -33,6 +33,18 @@ export class TestingCompiler extends Compiler {
|
||||||
overridePipe(directive: Type<any>, overrides: MetadataOverride<Pipe>): void {
|
overridePipe(directive: Type<any>, overrides: MetadataOverride<Pipe>): void {
|
||||||
throw unimplemented();
|
throw unimplemented();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Allows to pass the compile summary from AOT compilation to the JIT compiler,
|
||||||
|
* so that it can use the code generated by AOT.
|
||||||
|
*/
|
||||||
|
loadAotSummaries(summaries: () => any[]) { throw unimplemented(); };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the component factory for the given component.
|
||||||
|
* This assumes that the component has been compiled before calling this call using
|
||||||
|
* `compileModuleAndAllComponents*`.
|
||||||
|
*/
|
||||||
|
getComponentFactory<T>(component: Type<T>): ComponentFactory<T> { throw unimplemented(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 {AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, InterpolationConfig, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, PipeResolver, ResourceLoader, StaticAndDynamicReflectionCapabilities, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, SummaryResolver, analyzeNgModules, componentModuleUrl, createOfflineCompileUrlResolver, extractProgramSymbols} from '@angular/compiler';
|
import {AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, InterpolationConfig, JitSummaryResolver, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, PipeResolver, ResourceLoader, StaticAndDynamicReflectionCapabilities, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, analyzeNgModules, componentModuleUrl, createOfflineCompileUrlResolver, extractProgramSymbols} from '@angular/compiler';
|
||||||
import {AngularCompilerOptions} from '@angular/compiler-cli';
|
import {AngularCompilerOptions} from '@angular/compiler-cli';
|
||||||
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
@ -118,7 +118,7 @@ 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(
|
||||||
config, moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(),
|
config, moduleResolver, directiveResolver, pipeResolver, new JitSummaryResolver(),
|
||||||
elementSchemaRegistry, directiveNormalizer, new Console(), this._staticSymbolCache,
|
elementSchemaRegistry, directiveNormalizer, new Console(), this._staticSymbolCache,
|
||||||
this.reflector, (error, type) => this.collectError(error, type && type.filePath));
|
this.reflector, (error, type) => this.collectError(error, type && type.filePath));
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ export declare class TestBed implements Injector {
|
||||||
createComponent<T>(component: Type<T>): ComponentFixture<T>;
|
createComponent<T>(component: Type<T>): ComponentFixture<T>;
|
||||||
execute(tokens: any[], fn: Function, context?: any): any;
|
execute(tokens: any[], fn: Function, context?: any): any;
|
||||||
get(token: any, notFoundValue?: any): any;
|
get(token: any, notFoundValue?: any): any;
|
||||||
/** @experimental */ initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef): void;
|
/** @experimental */ initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): void;
|
||||||
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void;
|
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void;
|
||||||
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void;
|
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void;
|
||||||
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void;
|
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void;
|
||||||
|
@ -84,7 +84,7 @@ export declare class TestBed implements Injector {
|
||||||
static configureTestingModule(moduleDef: TestModuleMetadata): typeof TestBed;
|
static configureTestingModule(moduleDef: TestModuleMetadata): typeof TestBed;
|
||||||
static createComponent<T>(component: Type<T>): ComponentFixture<T>;
|
static createComponent<T>(component: Type<T>): ComponentFixture<T>;
|
||||||
static get(token: any, notFoundValue?: any): any;
|
static get(token: any, notFoundValue?: any): any;
|
||||||
/** @experimental */ static initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef): TestBed;
|
/** @experimental */ static initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): TestBed;
|
||||||
static overrideComponent(component: Type<any>, override: MetadataOverride<Component>): typeof TestBed;
|
static overrideComponent(component: Type<any>, override: MetadataOverride<Component>): typeof TestBed;
|
||||||
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): typeof TestBed;
|
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): typeof TestBed;
|
||||||
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): typeof TestBed;
|
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): typeof TestBed;
|
||||||
|
|
Loading…
Reference in New Issue