fix(ngcc): support ModuleWithProviders functions that delegate (#36948)
In #36892 the `ModuleWithProviders` type parameter becomes required. This exposes a bug in ngcc, where it can only handle functions that have a specific form: ``` function forRoot() { return { ... }; } ``` In other words, it only accepts functions that return an object literal. In some libraries, the function instead returns a call to another function. For example in `angular-in-memory-web-api`: ``` InMemoryWebApiModule.forFeature = function (dbCreator, options) { return InMemoryWebApiModule_1.forRoot(dbCreator, options); }; ``` This commit changes the parsing of such functions to use the `PartialEvaluator`, which can evaluate these more complex function bodies. PR Close #36948
This commit is contained in:
parent
e010f2ca54
commit
fafa50d97f
|
@ -9,53 +9,67 @@ import * as ts from 'typescript';
|
|||
|
||||
import {ReferencesRegistry} from '../../../src/ngtsc/annotations';
|
||||
import {Reference} from '../../../src/ngtsc/imports';
|
||||
import {ClassDeclaration, ConcreteDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
|
||||
import {ClassDeclaration, isNamedClassDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {hasNameIdentifier, isDefined} from '../utils';
|
||||
|
||||
/**
|
||||
* A structure returned from `getModuleWithProvidersFunctions()` that describes functions
|
||||
* that return ModuleWithProviders objects.
|
||||
*/
|
||||
export interface ModuleWithProvidersInfo {
|
||||
/**
|
||||
* The declaration (in the .d.ts file) of the function that returns
|
||||
* a `ModuleWithProviders object, but has a signature that needs
|
||||
* a type parameter adding.
|
||||
* The name of the declared function.
|
||||
*/
|
||||
declaration: ts.MethodDeclaration|ts.FunctionDeclaration;
|
||||
name: string;
|
||||
/**
|
||||
* The NgModule class declaration (in the .d.ts file) to add as a type parameter.
|
||||
* The declaration of the function that returns the `ModuleWithProviders` object.
|
||||
*/
|
||||
ngModule: ConcreteDeclaration<ClassDeclaration>;
|
||||
declaration: ts.SignatureDeclaration;
|
||||
/**
|
||||
* Declaration of the containing class (if this is a method)
|
||||
*/
|
||||
container: ts.Declaration|null;
|
||||
/**
|
||||
* The declaration of the class that the `ngModule` property on the `ModuleWithProviders` object
|
||||
* refers to.
|
||||
*/
|
||||
ngModule: Reference<ClassDeclaration>;
|
||||
}
|
||||
|
||||
export type ModuleWithProvidersAnalyses = Map<ts.SourceFile, ModuleWithProvidersInfo[]>;
|
||||
export const ModuleWithProvidersAnalyses = Map;
|
||||
|
||||
export class ModuleWithProvidersAnalyzer {
|
||||
private evaluator = new PartialEvaluator(this.host, this.typeChecker, null);
|
||||
|
||||
constructor(
|
||||
private host: NgccReflectionHost, private referencesRegistry: ReferencesRegistry,
|
||||
private processDts: boolean) {}
|
||||
private host: NgccReflectionHost, private typeChecker: ts.TypeChecker,
|
||||
private referencesRegistry: ReferencesRegistry, private processDts: boolean) {}
|
||||
|
||||
analyzeProgram(program: ts.Program): ModuleWithProvidersAnalyses {
|
||||
const analyses = new ModuleWithProvidersAnalyses();
|
||||
const analyses: ModuleWithProvidersAnalyses = new ModuleWithProvidersAnalyses();
|
||||
const rootFiles = this.getRootFiles(program);
|
||||
rootFiles.forEach(f => {
|
||||
const fns = this.getModuleWithProvidersFunctions(f);
|
||||
fns && fns.forEach(fn => {
|
||||
if (fn.ngModule.viaModule === null) {
|
||||
if (fn.ngModule.bestGuessOwningModule === null) {
|
||||
// Record the usage of an internal module as it needs to become an exported symbol
|
||||
this.referencesRegistry.add(fn.ngModule.node, new Reference(fn.ngModule.node));
|
||||
}
|
||||
|
||||
// Only when processing the dts files do we need to determine which declaration to update.
|
||||
if (this.processDts) {
|
||||
const dtsFn = this.getDtsDeclarationForFunction(fn);
|
||||
const typeParam = dtsFn.type && ts.isTypeReferenceNode(dtsFn.type) &&
|
||||
dtsFn.type.typeArguments && dtsFn.type.typeArguments[0] ||
|
||||
const dtsFn = this.getDtsModuleWithProvidersFunction(fn);
|
||||
const dtsFnType = dtsFn.declaration.type;
|
||||
const typeParam = dtsFnType && ts.isTypeReferenceNode(dtsFnType) &&
|
||||
dtsFnType.typeArguments && dtsFnType.typeArguments[0] ||
|
||||
null;
|
||||
if (!typeParam || isAnyKeyword(typeParam)) {
|
||||
const ngModule = this.resolveNgModuleReference(fn);
|
||||
const dtsFile = dtsFn.getSourceFile();
|
||||
const analysis = analyses.has(dtsFile) ? analyses.get(dtsFile) : [];
|
||||
analysis.push({declaration: dtsFn, ngModule});
|
||||
const dtsFile = dtsFn.declaration.getSourceFile();
|
||||
const analysis = analyses.has(dtsFile) ? analyses.get(dtsFile)! : [];
|
||||
analysis.push(dtsFn);
|
||||
analyses.set(dtsFile, analysis);
|
||||
}
|
||||
}
|
||||
|
@ -68,11 +82,11 @@ export class ModuleWithProvidersAnalyzer {
|
|||
return program.getRootFileNames().map(f => program.getSourceFile(f)).filter(isDefined);
|
||||
}
|
||||
|
||||
private getModuleWithProvidersFunctions(f: ts.SourceFile): ModuleWithProvidersFunction[] {
|
||||
private getModuleWithProvidersFunctions(f: ts.SourceFile): ModuleWithProvidersInfo[] {
|
||||
const exports = this.host.getExportsOfModule(f);
|
||||
if (!exports) return [];
|
||||
const infos: ModuleWithProvidersFunction[] = [];
|
||||
exports.forEach((declaration, name) => {
|
||||
const infos: ModuleWithProvidersInfo[] = [];
|
||||
exports.forEach((declaration) => {
|
||||
if (declaration.node === null) {
|
||||
return;
|
||||
}
|
||||
|
@ -111,7 +125,7 @@ export class ModuleWithProvidersAnalyzer {
|
|||
*/
|
||||
private parseForModuleWithProviders(
|
||||
name: string, node: ts.Node|null, implementation: ts.Node|null = node,
|
||||
container: ts.Declaration|null = null): ModuleWithProvidersFunction|null {
|
||||
container: ts.Declaration|null = null): ModuleWithProvidersInfo|null {
|
||||
if (implementation === null ||
|
||||
(!ts.isFunctionDeclaration(implementation) && !ts.isMethodDeclaration(implementation) &&
|
||||
!ts.isFunctionExpression(implementation))) {
|
||||
|
@ -122,47 +136,40 @@ export class ModuleWithProvidersAnalyzer {
|
|||
if (definition === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const body = definition.body;
|
||||
const lastStatement = body && body[body.length - 1];
|
||||
const returnExpression =
|
||||
lastStatement && ts.isReturnStatement(lastStatement) && lastStatement.expression || null;
|
||||
const ngModuleProperty = returnExpression && ts.isObjectLiteralExpression(returnExpression) &&
|
||||
returnExpression.properties.find(
|
||||
prop =>
|
||||
!!prop.name && ts.isIdentifier(prop.name) && prop.name.text === 'ngModule') ||
|
||||
null;
|
||||
|
||||
if (!ngModuleProperty || !ts.isPropertyAssignment(ngModuleProperty)) {
|
||||
if (body === null || body.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The ngModuleValue could be of the form `SomeModule` or `namespace_1.SomeModule`
|
||||
let ngModuleValue = ngModuleProperty.initializer;
|
||||
if (ts.isPropertyAccessExpression(ngModuleValue)) {
|
||||
ngModuleValue = ngModuleValue.expression;
|
||||
}
|
||||
|
||||
if (!ts.isIdentifier(ngModuleValue)) {
|
||||
// Get hold of the return statement expression for the function
|
||||
const lastStatement = body[body.length - 1];
|
||||
if (!ts.isReturnStatement(lastStatement) || lastStatement.expression === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ngModuleDeclaration = this.host.getDeclarationOfIdentifier(ngModuleValue);
|
||||
if (!ngModuleDeclaration || ngModuleDeclaration.node === null) {
|
||||
throw new Error(`Cannot find a declaration for NgModule ${
|
||||
ngModuleValue.getText()} referenced in "${declaration!.getText()}"`);
|
||||
}
|
||||
if (!hasNameIdentifier(ngModuleDeclaration.node)) {
|
||||
// Evaluate this expression and extract the `ngModule` reference
|
||||
const result = this.evaluator.evaluate(lastStatement.expression);
|
||||
if (!(result instanceof Map) || !result.has('ngModule')) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
name,
|
||||
ngModule: ngModuleDeclaration as ConcreteDeclaration<ClassDeclaration>,
|
||||
declaration,
|
||||
container
|
||||
};
|
||||
|
||||
const ngModuleRef = result.get('ngModule')!;
|
||||
if (!(ngModuleRef instanceof Reference)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isNamedClassDeclaration(ngModuleRef.node) &&
|
||||
!isNamedVariableDeclaration(ngModuleRef.node)) {
|
||||
throw new Error(`The identity given by ${ngModuleRef.debugName} referenced in "${
|
||||
declaration!.getText()}" doesn't appear to be a "class" declaration.`);
|
||||
}
|
||||
|
||||
const ngModule = ngModuleRef as Reference<ClassDeclaration>;
|
||||
return {name, ngModule, declaration, container};
|
||||
}
|
||||
|
||||
private getDtsDeclarationForFunction(fn: ModuleWithProvidersFunction) {
|
||||
private getDtsModuleWithProvidersFunction(fn: ModuleWithProvidersInfo): ModuleWithProvidersInfo {
|
||||
let dtsFn: ts.Declaration|null = null;
|
||||
const containerClass = fn.container && this.host.getClassSymbol(fn.container);
|
||||
if (containerClass) {
|
||||
|
@ -183,15 +190,16 @@ export class ModuleWithProvidersAnalyzer {
|
|||
throw new Error(`Matching type declaration for ${
|
||||
fn.declaration.getText()} is not a function: ${dtsFn.getText()}`);
|
||||
}
|
||||
return dtsFn;
|
||||
const container = containerClass ? containerClass.declaration.valueDeclaration : null;
|
||||
const ngModule = this.resolveNgModuleReference(fn);
|
||||
return {name: fn.name, container, declaration: dtsFn, ngModule};
|
||||
}
|
||||
|
||||
private resolveNgModuleReference(fn: ModuleWithProvidersFunction):
|
||||
ConcreteDeclaration<ClassDeclaration> {
|
||||
private resolveNgModuleReference(fn: ModuleWithProvidersInfo): Reference<ClassDeclaration> {
|
||||
const ngModule = fn.ngModule;
|
||||
|
||||
// For external module references, use the declaration as is.
|
||||
if (ngModule.viaModule !== null) {
|
||||
if (ngModule.bestGuessOwningModule !== null) {
|
||||
return ngModule;
|
||||
}
|
||||
|
||||
|
@ -202,14 +210,13 @@ export class ModuleWithProvidersAnalyzer {
|
|||
throw new Error(`No typings declaration can be found for the referenced NgModule class in ${
|
||||
fn.declaration.getText()}.`);
|
||||
}
|
||||
if (!ts.isClassDeclaration(dtsNgModule) || !hasNameIdentifier(dtsNgModule)) {
|
||||
if (!isNamedClassDeclaration(dtsNgModule)) {
|
||||
throw new Error(`The referenced NgModule in ${
|
||||
fn.declaration
|
||||
.getText()} is not a named class declaration in the typings program; instead we get ${
|
||||
dtsNgModule.getText()}`);
|
||||
}
|
||||
|
||||
return {node: dtsNgModule, known: null, viaModule: null, identity: null};
|
||||
return new Reference(dtsNgModule, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,27 +229,3 @@ function isFunctionOrMethod(declaration: ts.Declaration): declaration is ts.Func
|
|||
function isAnyKeyword(typeParam: ts.TypeNode): typeParam is ts.KeywordTypeNode {
|
||||
return typeParam.kind === ts.SyntaxKind.AnyKeyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* A structure returned from `getModuleWithProvidersFunction` that describes functions
|
||||
* that return ModuleWithProviders objects.
|
||||
*/
|
||||
export interface ModuleWithProvidersFunction {
|
||||
/**
|
||||
* The name of the declared function.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The declaration of the function that returns the `ModuleWithProviders` object.
|
||||
*/
|
||||
declaration: ts.SignatureDeclaration;
|
||||
/**
|
||||
* Declaration of the containing class (if this is a method)
|
||||
*/
|
||||
container: ts.Declaration|null;
|
||||
/**
|
||||
* The declaration of the class that the `ngModule` property on the `ModuleWithProviders` object
|
||||
* refers to.
|
||||
*/
|
||||
ngModule: ConcreteDeclaration<ClassDeclaration>;
|
||||
}
|
||||
|
|
|
@ -156,8 +156,9 @@ export class Transformer {
|
|||
diagnostic => diagnostics.push(diagnostic), this.tsConfig);
|
||||
const decorationAnalyses = decorationAnalyzer.analyzeProgram();
|
||||
|
||||
const moduleWithProvidersAnalyzer =
|
||||
new ModuleWithProvidersAnalyzer(reflectionHost, referencesRegistry, bundle.dts !== null);
|
||||
const moduleWithProvidersAnalyzer = new ModuleWithProvidersAnalyzer(
|
||||
reflectionHost, bundle.src.program.getTypeChecker(), referencesRegistry,
|
||||
bundle.dts !== null);
|
||||
const moduleWithProvidersAnalyses = moduleWithProvidersAnalyzer &&
|
||||
moduleWithProvidersAnalyzer.analyzeProgram(bundle.src.program);
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ export class EsmRenderingFormatter implements RenderingFormatter {
|
|||
const ngModuleName = info.ngModule.node.name.text;
|
||||
const declarationFile = absoluteFromSourceFile(info.declaration.getSourceFile());
|
||||
const ngModuleFile = absoluteFromSourceFile(info.ngModule.node.getSourceFile());
|
||||
const importPath = info.ngModule.viaModule ||
|
||||
const importPath = info.ngModule.ownedByModuleGuess ||
|
||||
(declarationFile !== ngModuleFile ?
|
||||
stripExtension(`./${relative(dirname(declarationFile), ngModuleFile)}`) :
|
||||
null);
|
||||
|
|
|
@ -9,6 +9,7 @@ import * as ts from 'typescript';
|
|||
|
||||
import {absoluteFrom, AbsoluteFsPath, getSourceFileOrError} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing';
|
||||
import {isNamedClassDeclaration} from '../../../src/ngtsc/reflection';
|
||||
import {getDeclaration} from '../../../src/ngtsc/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer';
|
||||
|
@ -39,6 +40,7 @@ runInEachFileSystem(() => {
|
|||
export * from './implicit';
|
||||
export * from './no-providers';
|
||||
export * from './module';
|
||||
export * from './delegated';
|
||||
`
|
||||
},
|
||||
{
|
||||
|
@ -214,6 +216,89 @@ runInEachFileSystem(() => {
|
|||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test-package/src/delegated.js'),
|
||||
contents: `
|
||||
import * as implicit from './implicit';
|
||||
import * as explicit from './explicit';
|
||||
import * as anyModule from './any';
|
||||
|
||||
export function delegatedImplicitInternalFunction() {
|
||||
return implicit.implicitInternalFunction();
|
||||
}
|
||||
export function delegatedImplicitExternalFunction() {
|
||||
return implicit.implicitExternalFunction();
|
||||
}
|
||||
export function delegatedImplicitLibraryFunction() {
|
||||
return implicit.implicitLibraryFunction();
|
||||
}
|
||||
export class DelegatedImplicitClass {
|
||||
static implicitInternalMethod() {
|
||||
return implicit.ImplicitClass.implicitInternalMethod();
|
||||
}
|
||||
static implicitExternalMethod() {
|
||||
return implicit.ImplicitClass.implicitExternalMethod();
|
||||
}
|
||||
static implicitLibraryMethod() {
|
||||
return implicit.ImplicitClass.implicitLibraryMethod();
|
||||
}
|
||||
}
|
||||
|
||||
export function delegatedExplicitInternalFunction() {
|
||||
return explicit.explicitInternalFunction();
|
||||
}
|
||||
export function delegatedExplicitExternalFunction() {
|
||||
return explicit.explicitExternalFunction();
|
||||
}
|
||||
export function delegatedExplicitLibraryFunction() {
|
||||
return explicit.explicitLibraryFunction();
|
||||
}
|
||||
export class DelegatedExplicitClass {
|
||||
static explicitInternalMethod() {
|
||||
return explicit.ExplicitClass.explicitInternalMethod();
|
||||
}
|
||||
static explicitExternalMethod() {
|
||||
return explicit.ExplicitClass.explicitExternalMethod();
|
||||
}
|
||||
static explicitLibraryMethod() {
|
||||
return explicit.ExplicitClass.explicitLibraryMethod();
|
||||
}
|
||||
}
|
||||
|
||||
export function delegatedAnyInternalFunction() {
|
||||
return anyModule.anyInternalFunction();
|
||||
}
|
||||
export function delegatedAnyExternalFunction() {
|
||||
return anyModule.anyExternalFunction();
|
||||
}
|
||||
export function delegatedAnyLibraryFunction() {
|
||||
return anyModule.anyLibraryFunction();
|
||||
}
|
||||
export class DelegatedAnyClass {
|
||||
static anyInternalMethod() {
|
||||
return anyModule.AnyClass.anyInternalMethod();
|
||||
}
|
||||
static anyExternalMethod() {
|
||||
return anyModule.AnyClass.anyExternalMethod();
|
||||
}
|
||||
static anyLibraryMethod() {
|
||||
return anyModule.AnyClass.anyLibraryMethod();
|
||||
}
|
||||
}
|
||||
|
||||
export function withParams(a: string) {
|
||||
return explicit.explicitInternalFunction();
|
||||
}
|
||||
|
||||
export function withOptionalParams(a: string = 'default') {
|
||||
return explicit.explicitInternalFunction();
|
||||
}
|
||||
|
||||
export function doubleDelegation(a: string = 'default') {
|
||||
return withParams(a);
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test-package/src/module.js'),
|
||||
contents: `
|
||||
|
@ -234,6 +319,7 @@ runInEachFileSystem(() => {
|
|||
export * from './implicit';
|
||||
export * from './no-providers';
|
||||
export * from './module';
|
||||
export * from './delegated';
|
||||
`
|
||||
},
|
||||
{
|
||||
|
@ -284,6 +370,47 @@ runInEachFileSystem(() => {
|
|||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test-package/typings/delegated.d.ts'),
|
||||
contents: `
|
||||
// None of the ModuleWithProviders functions/methods in this file provide the
|
||||
// necessary type parameters and so need to be processed by the analyzer.
|
||||
// Each group of functions/methods here delegate their return values to other
|
||||
// functions/methods that either explicitly provide a type parameter or need
|
||||
// processing by the analyzer themselves.
|
||||
|
||||
export declare function delegatedImplicitInternalFunction(): ModuleWithProviders;
|
||||
export declare function delegatedImplicitExternalFunction(): ModuleWithProviders;
|
||||
export declare function delegatedImplicitLibraryFunction(): ModuleWithProviders;
|
||||
export declare class DelegatedImplicitClass {
|
||||
static implicitInternalMethod(): ModuleWithProviders;
|
||||
static implicitExternalMethod(): ModuleWithProviders;
|
||||
static implicitLibraryMethod(): ModuleWithProviders;
|
||||
}
|
||||
|
||||
export declare function delegatedExplicitInternalFunction(): ModuleWithProviders;
|
||||
export declare function delegatedExplicitExternalFunction(): ModuleWithProviders;
|
||||
export declare function delegatedExplicitLibraryFunction(): ModuleWithProviders;
|
||||
export declare class DelegatedExplicitClass {
|
||||
static explicitInternalMethod(): ModuleWithProviders;
|
||||
static explicitExternalMethod(): ModuleWithProviders;
|
||||
static explicitLibraryMethod(): ModuleWithProviders;
|
||||
}
|
||||
|
||||
export declare function delegatedAnyInternalFunction(): ModuleWithProviders;
|
||||
export declare function delegatedAnyExternalFunction(): ModuleWithProviders;
|
||||
export declare function delegatedAnyLibraryFunction(): ModuleWithProviders;
|
||||
export declare class DelegatedAnyClass {
|
||||
static anyInternalMethod(): ModuleWithProviders;
|
||||
static anyExternalMethod(): ModuleWithProviders;
|
||||
static anyLibraryMethod(): ModuleWithProviders;
|
||||
}
|
||||
|
||||
export declare function withParams(a: string): ModuleWithProviders;
|
||||
export declare function withOptionalParams(a: string = 'default'): ModuleWithProviders;
|
||||
export declare function doubleDelegation(a: string = 'default'): ModuleWithProviders;
|
||||
`
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test-package/typings/no-providers.d.ts'),
|
||||
contents: `
|
||||
|
@ -338,7 +465,8 @@ runInEachFileSystem(() => {
|
|||
referencesRegistry = new NgccReferencesRegistry(host);
|
||||
|
||||
const processDts = true;
|
||||
const analyzer = new ModuleWithProvidersAnalyzer(host, referencesRegistry, processDts);
|
||||
const analyzer = new ModuleWithProvidersAnalyzer(
|
||||
host, bundle.src.program.getTypeChecker(), referencesRegistry, processDts);
|
||||
analyses = analyzer.analyzeProgram(program);
|
||||
});
|
||||
|
||||
|
@ -354,9 +482,11 @@ runInEachFileSystem(() => {
|
|||
expect(anyAnalysis).toContain(['anyInternalFunction', 'AnyInternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['anyExternalFunction', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['anyLibraryFunction', 'LibraryModule', 'some-library']);
|
||||
expect(anyAnalysis).toContain(['anyInternalMethod', 'AnyInternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['anyExternalMethod', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['anyLibraryMethod', 'LibraryModule', 'some-library']);
|
||||
expect(anyAnalysis).toContain(['AnyClass.anyInternalMethod', 'AnyInternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['AnyClass.anyExternalMethod', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain([
|
||||
'AnyClass.anyLibraryMethod', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should track internal module references in the references registry', () => {
|
||||
|
@ -377,9 +507,82 @@ runInEachFileSystem(() => {
|
|||
expect(anyAnalysis).toContain(['implicitInternalFunction', 'ImplicitInternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['implicitExternalFunction', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['implicitLibraryFunction', 'LibraryModule', 'some-library']);
|
||||
expect(anyAnalysis).toContain(['implicitInternalMethod', 'ImplicitInternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['implicitExternalMethod', 'ExternalModule', null]);
|
||||
expect(anyAnalysis).toContain(['implicitLibraryMethod', 'LibraryModule', 'some-library']);
|
||||
expect(anyAnalysis).toContain([
|
||||
'ImplicitClass.implicitInternalMethod', 'ImplicitInternalModule', null
|
||||
]);
|
||||
expect(anyAnalysis).toContain([
|
||||
'ImplicitClass.implicitExternalMethod', 'ExternalModule', null
|
||||
]);
|
||||
expect(anyAnalysis).toContain([
|
||||
'ImplicitClass.implicitLibraryMethod', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should find declarations that delegate by calling another function', () => {
|
||||
const delegatedAnalysis = getAnalysisDescription(
|
||||
analyses, _('/node_modules/test-package/typings/delegated.d.ts'));
|
||||
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'delegatedExplicitInternalFunction', 'ExplicitInternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'delegatedExplicitExternalFunction', 'ExternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'delegatedExplicitLibraryFunction', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'DelegatedExplicitClass.explicitInternalMethod', 'ExplicitInternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'DelegatedExplicitClass.explicitExternalMethod', 'ExternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'DelegatedExplicitClass.explicitLibraryMethod', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'delegatedImplicitInternalFunction', 'ImplicitInternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'delegatedImplicitExternalFunction', 'ExternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'delegatedImplicitLibraryFunction', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'DelegatedImplicitClass.implicitInternalMethod', 'ImplicitInternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'DelegatedImplicitClass.implicitExternalMethod', 'ExternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'DelegatedImplicitClass.implicitLibraryMethod', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'delegatedAnyInternalFunction', 'AnyInternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'delegatedAnyExternalFunction', 'ExternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'delegatedAnyLibraryFunction', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'DelegatedAnyClass.anyInternalMethod', 'AnyInternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'DelegatedAnyClass.anyExternalMethod', 'ExternalModule', null
|
||||
]);
|
||||
expect(delegatedAnalysis).toContain([
|
||||
'DelegatedAnyClass.anyLibraryMethod', 'LibraryModule', 'some-library'
|
||||
]);
|
||||
|
||||
expect(delegatedAnalysis).toContain(['withParams', 'ExplicitInternalModule', null]);
|
||||
expect(delegatedAnalysis).toContain(['withOptionalParams', 'ExplicitInternalModule', null]);
|
||||
expect(delegatedAnalysis).toContain(['doubleDelegation', 'ExplicitInternalModule', null]);
|
||||
});
|
||||
|
||||
it('should find declarations that do not specify a `providers` property in the return type',
|
||||
|
@ -416,11 +619,15 @@ runInEachFileSystem(() => {
|
|||
const analysis = analyses.get(file);
|
||||
return analysis ? analysis.map(
|
||||
info =>
|
||||
[info.declaration.name!.getText(),
|
||||
[getName(info.container) + info.declaration.name!.getText(),
|
||||
(info.ngModule.node as ts.ClassDeclaration).name!.getText(),
|
||||
info.ngModule.viaModule]) :
|
||||
info.ngModule.ownedByModuleGuess]) :
|
||||
[];
|
||||
}
|
||||
|
||||
function getName(node: ts.Declaration|null): string {
|
||||
return node && isNamedClassDeclaration(node) ? `${node.name.text}.` : '';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -540,7 +747,8 @@ runInEachFileSystem(() => {
|
|||
const referencesRegistry = new NgccReferencesRegistry(host);
|
||||
|
||||
const processDts = true;
|
||||
const analyzer = new ModuleWithProvidersAnalyzer(host, referencesRegistry, processDts);
|
||||
const analyzer = new ModuleWithProvidersAnalyzer(
|
||||
host, bundle.src.program.getTypeChecker(), referencesRegistry, processDts);
|
||||
const analyses = analyzer.analyzeProgram(program);
|
||||
|
||||
const file = getSourceFileOrError(
|
||||
|
@ -570,7 +778,8 @@ runInEachFileSystem(() => {
|
|||
const referencesRegistry = new NgccReferencesRegistry(host);
|
||||
|
||||
const processDts = false; // Emulate the scenario where typings have already been processed
|
||||
const analyzer = new ModuleWithProvidersAnalyzer(host, referencesRegistry, processDts);
|
||||
const analyzer = new ModuleWithProvidersAnalyzer(
|
||||
host, bundle.src.program.getTypeChecker(), referencesRegistry, processDts);
|
||||
const analyses = analyzer.analyzeProgram(program);
|
||||
|
||||
expect(analyses.size).toBe(0);
|
||||
|
|
|
@ -78,7 +78,8 @@ function createTestRenderer(
|
|||
const decorationAnalyses =
|
||||
new DecorationAnalyzer(fs, bundle, host, referencesRegistry).analyzeProgram();
|
||||
const moduleWithProvidersAnalyses =
|
||||
new ModuleWithProvidersAnalyzer(host, referencesRegistry, true)
|
||||
new ModuleWithProvidersAnalyzer(
|
||||
host, bundle.src.program.getTypeChecker(), referencesRegistry, true)
|
||||
.analyzeProgram(bundle.src.program);
|
||||
const privateDeclarationsAnalyses =
|
||||
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
||||
|
|
|
@ -574,7 +574,8 @@ export { D };
|
|||
|
||||
const referencesRegistry = new NgccReferencesRegistry(host);
|
||||
const moduleWithProvidersAnalyses =
|
||||
new ModuleWithProvidersAnalyzer(host, referencesRegistry, true)
|
||||
new ModuleWithProvidersAnalyzer(
|
||||
host, bundle.src.program.getTypeChecker(), referencesRegistry, true)
|
||||
.analyzeProgram(bundle.src.program);
|
||||
const typingsFile = getSourceFileOrError(
|
||||
bundle.dts!.program, _('/node_modules/test-package/typings/index.d.ts'));
|
||||
|
@ -611,7 +612,8 @@ export { D };
|
|||
|
||||
const referencesRegistry = new NgccReferencesRegistry(host);
|
||||
const moduleWithProvidersAnalyses =
|
||||
new ModuleWithProvidersAnalyzer(host, referencesRegistry, true)
|
||||
new ModuleWithProvidersAnalyzer(
|
||||
host, bundle.src.program.getTypeChecker(), referencesRegistry, true)
|
||||
.analyzeProgram(bundle.src.program);
|
||||
const typingsFile = getSourceFileOrError(
|
||||
bundle.dts!.program, _('/node_modules/test-package/typings/module.d.ts'));
|
||||
|
|
Loading…
Reference in New Issue