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<ngcc0.Module>; ``` ngtsc now takes this situation into account when reflecting a ModuleWithProvider's type argument. PR Close #27562
This commit is contained in:
parent
7fae9114c8
commit
a8ebc837ea
|
@ -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<NgModuleAnalys
|
|||
|
||||
const arg = type.typeArguments[0];
|
||||
|
||||
// If the argument isn't an Identifier, bail.
|
||||
if (!ts.isTypeReferenceNode(arg) || !ts.isIdentifier(arg.typeName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return arg.typeName;
|
||||
return typeNodeToValueExpr(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
|
||||
/// <reference types="node" />
|
||||
|
||||
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';
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -375,7 +375,8 @@ describe('ngtsc behavioral tests', () => {
|
|||
'i0.ɵNgModuleDefWithMeta<TestModule, [typeof TestPipe, typeof TestCmp], never, never>');
|
||||
});
|
||||
|
||||
it('should unwrap a ModuleWithProviders function if a generic type is provided for it', () => {
|
||||
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';
|
||||
|
@ -404,6 +405,42 @@ describe('ngtsc behavioral tests', () => {
|
|||
.toContain('i0.ɵNgModuleDefWithMeta<TestModule, never, [typeof i1.RouterModule], never>');
|
||||
});
|
||||
|
||||
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<internal.InternalRouterModule>;
|
||||
}
|
||||
`);
|
||||
|
||||
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<TestModule, never, [typeof i1.InternalRouterModule], never>');
|
||||
});
|
||||
});
|
||||
|
||||
it('should inject special types according to the metadata', () => {
|
||||
env.tsconfig();
|
||||
env.write(`test.ts`, `
|
||||
|
|
Loading…
Reference in New Issue