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 {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
||||||
import {Decorator, ReflectionHost} from '../../host';
|
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 {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
|
||||||
|
|
||||||
import {generateSetClassMetadataCall} from './metadata';
|
import {generateSetClassMetadataCall} from './metadata';
|
||||||
|
@ -223,12 +223,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
||||||
|
|
||||||
const arg = type.typeArguments[0];
|
const arg = type.typeArguments[0];
|
||||||
|
|
||||||
// If the argument isn't an Identifier, bail.
|
return typeNodeToValueExpr(arg);
|
||||||
if (!ts.isTypeReferenceNode(arg) || !ts.isIdentifier(arg.typeName)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arg.typeName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,5 +8,5 @@
|
||||||
|
|
||||||
/// <reference types="node" />
|
/// <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';
|
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)) {
|
if (ts.isTypeReferenceNode(node)) {
|
||||||
return entityNameToValue(node.typeName);
|
return entityNameToValue(node.typeName);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -375,9 +375,10 @@ describe('ngtsc behavioral tests', () => {
|
||||||
'i0.ɵNgModuleDefWithMeta<TestModule, [typeof TestPipe, typeof TestCmp], never, never>');
|
'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', () => {
|
||||||
env.tsconfig();
|
it('should extract the generic type and include it in the module\'s declaration', () => {
|
||||||
env.write(`test.ts`, `
|
env.tsconfig();
|
||||||
|
env.write(`test.ts`, `
|
||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {RouterModule} from 'router';
|
import {RouterModule} from 'router';
|
||||||
|
|
||||||
|
@ -385,7 +386,7 @@ describe('ngtsc behavioral tests', () => {
|
||||||
export class TestModule {}
|
export class TestModule {}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
env.write('node_modules/router/index.d.ts', `
|
env.write('node_modules/router/index.d.ts', `
|
||||||
import {ModuleWithProviders} from '@angular/core';
|
import {ModuleWithProviders} from '@angular/core';
|
||||||
|
|
||||||
declare class RouterModule {
|
declare class RouterModule {
|
||||||
|
@ -393,15 +394,51 @@ describe('ngtsc behavioral tests', () => {
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
env.driveMain();
|
env.driveMain();
|
||||||
|
|
||||||
const jsContents = env.getContents('test.js');
|
const jsContents = env.getContents('test.js');
|
||||||
expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]');
|
expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]');
|
||||||
|
|
||||||
const dtsContents = env.getContents('test.d.ts');
|
const dtsContents = env.getContents('test.d.ts');
|
||||||
expect(dtsContents).toContain(`import * as i1 from 'router';`);
|
expect(dtsContents).toContain(`import * as i1 from 'router';`);
|
||||||
expect(dtsContents)
|
expect(dtsContents)
|
||||||
.toContain('i0.ɵNgModuleDefWithMeta<TestModule, never, [typeof i1.RouterModule], never>');
|
.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', () => {
|
it('should inject special types according to the metadata', () => {
|
||||||
|
|
Loading…
Reference in New Issue