From a8ebc837eaab1e5db1a33a58fffe49a52fe6eed2 Mon Sep 17 00:00:00 2001 From: JoostK Date: Sun, 9 Dec 2018 15:20:31 +0100 Subject: [PATCH] fix(ivy): support complex type nodes in ModuleWithProviders (#27562) With ngcc's ability to fixup pre-Ivy ModuleWithProviders such that they include a reference to the NgModule type, the type may become a qualified name: ``` import {ModuleWithProviders} from '@angular/core'; import * as ngcc0 from './module'; export declare provide(): ModuleWithProviders; ``` ngtsc now takes this situation into account when reflecting a ModuleWithProvider's type argument. PR Close #27562 --- .../src/ngtsc/annotations/src/ng_module.ts | 9 +-- .../compiler-cli/src/ngtsc/metadata/index.ts | 2 +- .../src/ngtsc/metadata/src/reflector.ts | 2 +- .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 59 +++++++++++++++---- 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index 5544ddeca8..0b9e025873 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {Decorator, ReflectionHost} from '../../host'; -import {Reference, ResolvedReference, ResolvedValue, reflectObjectLiteral, staticallyResolve} from '../../metadata'; +import {Reference, ResolvedReference, ResolvedValue, reflectObjectLiteral, staticallyResolve, typeNodeToValueExpr} from '../../metadata'; import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform'; import {generateSetClassMetadataCall} from './metadata'; @@ -223,12 +223,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler -export {TypeScriptReflectionHost, filterToMembersWithDecorator, findMember, reflectObjectLiteral, reflectTypeEntityToDeclaration} from './src/reflector'; +export {TypeScriptReflectionHost, filterToMembersWithDecorator, findMember, reflectObjectLiteral, reflectTypeEntityToDeclaration, typeNodeToValueExpr} from './src/reflector'; export {AbsoluteReference, EnumValue, ImportMode, Reference, ResolvedReference, ResolvedValue, isDynamicValue, staticallyResolve} from './src/resolver'; diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/reflector.ts b/packages/compiler-cli/src/ngtsc/metadata/src/reflector.ts index 32b5dcdab5..fffd71f355 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/reflector.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/reflector.ts @@ -436,7 +436,7 @@ function parameterName(name: ts.BindingName): string|null { } } -function typeNodeToValueExpr(node: ts.TypeNode): ts.Expression|null { +export function typeNodeToValueExpr(node: ts.TypeNode): ts.Expression|null { if (ts.isTypeReferenceNode(node)) { return entityNameToValue(node.typeName); } else { diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 95be43f914..22313073dd 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -375,9 +375,10 @@ describe('ngtsc behavioral tests', () => { 'i0.ɵNgModuleDefWithMeta'); }); - it('should unwrap a ModuleWithProviders function if a generic type is provided for it', () => { - env.tsconfig(); - env.write(`test.ts`, ` + describe('unwrapping ModuleWithProviders functions', () => { + it('should extract the generic type and include it in the module\'s declaration', () => { + env.tsconfig(); + env.write(`test.ts`, ` import {NgModule} from '@angular/core'; import {RouterModule} from 'router'; @@ -385,7 +386,7 @@ describe('ngtsc behavioral tests', () => { export class TestModule {} `); - env.write('node_modules/router/index.d.ts', ` + env.write('node_modules/router/index.d.ts', ` import {ModuleWithProviders} from '@angular/core'; declare class RouterModule { @@ -393,15 +394,51 @@ describe('ngtsc behavioral tests', () => { } `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); - const dtsContents = env.getContents('test.d.ts'); - expect(dtsContents).toContain(`import * as i1 from 'router';`); - expect(dtsContents) - .toContain('i0.ɵNgModuleDefWithMeta'); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain(`import * as i1 from 'router';`); + expect(dtsContents) + .toContain('i0.ɵNgModuleDefWithMeta'); + }); + + it('should extract the generic type if it is provided as qualified type name', () => { + env.tsconfig(); + env.write(`test.ts`, ` + import {NgModule} from '@angular/core'; + import {RouterModule} from 'router'; + + @NgModule({imports: [RouterModule.forRoot()]}) + export class TestModule {} + `); + + env.write('node_modules/router/index.d.ts', ` + import {ModuleWithProviders} from '@angular/core'; + import * as internal from './internal'; + + declare class RouterModule { + static forRoot(): ModuleWithProviders; + } + `); + + env.write('node_modules/router/internal.d.ts', ` + export declare class InternalRouterModule {} + `); + + env.driveMain(); + + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); + + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain(`import * as i1 from 'router';`); + expect(dtsContents) + .toContain( + 'i0.ɵNgModuleDefWithMeta'); + }); }); it('should inject special types according to the metadata', () => {