fix(ivy): consider exported modules from other compilation scopes (#25425)
PR Close #25425
This commit is contained in:
parent
b40c437379
commit
26066f282e
|
@ -157,7 +157,7 @@ export class SelectorScopeRegistry {
|
|||
// Process the declaration scope of the module, and lookup the selector of every declared type.
|
||||
// The initial value of ngModuleImportedFrom is 'null' which signifies that the NgModule
|
||||
// was not imported from a .d.ts source.
|
||||
this.lookupScopes(module !, /* ngModuleImportedFrom */ null).compilation.forEach(ref => {
|
||||
this.lookupScopesOrDie(module !, /* ngModuleImportedFrom */ null).compilation.forEach(ref => {
|
||||
const node = ts.getOriginalNode(ref.node) as ts.Declaration;
|
||||
|
||||
// Either the node represents a directive or a pipe. Look for both.
|
||||
|
@ -183,6 +183,15 @@ export class SelectorScopeRegistry {
|
|||
return convertScopeToExpressions(scope, node);
|
||||
}
|
||||
|
||||
private lookupScopesOrDie(node: ts.Declaration, ngModuleImportedFrom: string|null):
|
||||
SelectorScopes {
|
||||
const result = this.lookupScopes(node, ngModuleImportedFrom);
|
||||
if (result === null) {
|
||||
throw new Error(`Module not found: ${reflectIdentifierOfDeclaration(node)}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup `SelectorScopes` for a given module.
|
||||
*
|
||||
|
@ -190,7 +199,8 @@ export class SelectorScopeRegistry {
|
|||
* (`ngModuleImportedFrom`) then all of its declarations are exported at that same path, as well
|
||||
* as imports and exports from other modules that are relatively imported.
|
||||
*/
|
||||
private lookupScopes(node: ts.Declaration, ngModuleImportedFrom: string|null): SelectorScopes {
|
||||
private lookupScopes(node: ts.Declaration, ngModuleImportedFrom: string|null): SelectorScopes
|
||||
|null {
|
||||
let data: ModuleData|null = null;
|
||||
|
||||
// Either this module was analyzed directly, or has a precompiled ngModuleDef.
|
||||
|
@ -206,7 +216,7 @@ export class SelectorScopeRegistry {
|
|||
}
|
||||
|
||||
if (data === null) {
|
||||
throw new Error(`Module not registered: ${reflectNameOfDeclaration(node)}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -214,18 +224,19 @@ export class SelectorScopeRegistry {
|
|||
...data.declarations,
|
||||
// Expand imports to the exported scope of those imports.
|
||||
...flatten(data.imports.map(
|
||||
ref =>
|
||||
this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref)).exported)),
|
||||
ref => this.lookupScopesOrDie(ref.node as ts.Declaration, absoluteModuleName(ref))
|
||||
.exported)),
|
||||
// And include the compilation scope of exported modules.
|
||||
...flatten(
|
||||
data.exports.filter(ref => this._moduleToData.has(ref.node as ts.Declaration))
|
||||
.map(
|
||||
ref => this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref))
|
||||
.exported))
|
||||
data.exports
|
||||
.map(ref => this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref)))
|
||||
.filter((scope: SelectorScopes | null): scope is SelectorScopes => scope !== null)
|
||||
.map(scope => scope.exported))
|
||||
],
|
||||
exported: flatten(data.exports.map(ref => {
|
||||
if (this._moduleToData.has(ref.node as ts.Declaration)) {
|
||||
return this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref)).exported;
|
||||
const scope = this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref));
|
||||
if (scope !== null) {
|
||||
return scope.exported;
|
||||
} else {
|
||||
return [ref];
|
||||
}
|
||||
|
|
|
@ -27,22 +27,22 @@ describe('SelectorScopeRegistry', () => {
|
|||
{
|
||||
name: 'node_modules/some_library/index.d.ts',
|
||||
contents: `
|
||||
import {NgComponentDef, NgModuleDef} from '@angular/core';
|
||||
import {NgModuleDef} from '@angular/core';
|
||||
import * as i0 from './component';
|
||||
|
||||
export declare class SomeModule {
|
||||
static ngModuleDef: NgModuleDef<SomeModule, [typeof i0.SomeCmp], never, [typeof i0.SomeCmp]>;
|
||||
}
|
||||
|
||||
export declare class SomeCmp {
|
||||
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: 'node_modules/some_library/component.d.ts',
|
||||
contents: `
|
||||
export declare class SomeCmp {}
|
||||
import {NgComponentDef} from '@angular/core';
|
||||
|
||||
export declare class SomeCmp {
|
||||
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
|
@ -76,6 +76,64 @@ describe('SelectorScopeRegistry', () => {
|
|||
const scope = registry.lookupCompilationScope(ProgramCmp) !;
|
||||
expect(scope).toBeDefined();
|
||||
expect(scope.directives).toBeDefined();
|
||||
expect(scope.directives.size).toBe(1);
|
||||
expect(scope.directives.size).toBe(2);
|
||||
});
|
||||
|
||||
it('exports of third-party libs work', () => {
|
||||
const {program} = makeProgram([
|
||||
{
|
||||
name: 'node_modules/@angular/core/index.d.ts',
|
||||
contents: `
|
||||
export interface NgComponentDef<A, B> {}
|
||||
export interface NgModuleDef<A, B, C, D> {}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: 'node_modules/some_library/index.d.ts',
|
||||
contents: `
|
||||
import {NgComponentDef, NgModuleDef} from '@angular/core';
|
||||
|
||||
export declare class SomeModule {
|
||||
static ngModuleDef: NgModuleDef<SomeModule, [typeof SomeCmp], never, [typeof SomeCmp]>;
|
||||
}
|
||||
|
||||
export declare class SomeCmp {
|
||||
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
|
||||
}
|
||||
`
|
||||
},
|
||||
{
|
||||
name: 'entry.ts',
|
||||
contents: `
|
||||
export class ProgramCmp {}
|
||||
export class ProgramModule {}
|
||||
`
|
||||
},
|
||||
]);
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const ProgramModule =
|
||||
getDeclaration(program, 'entry.ts', 'ProgramModule', ts.isClassDeclaration);
|
||||
const ProgramCmp = getDeclaration(program, 'entry.ts', 'ProgramCmp', ts.isClassDeclaration);
|
||||
const SomeModule = getDeclaration(
|
||||
program, 'node_modules/some_library/index.d.ts', 'SomeModule', ts.isClassDeclaration);
|
||||
expect(ProgramModule).toBeDefined();
|
||||
expect(SomeModule).toBeDefined();
|
||||
|
||||
const registry = new SelectorScopeRegistry(checker, host);
|
||||
|
||||
registry.registerModule(ProgramModule, {
|
||||
declarations: [new ResolvedReference(ProgramCmp, ProgramCmp.name !)],
|
||||
exports: [new AbsoluteReference(SomeModule, SomeModule.name !, 'some_library', 'SomeModule')],
|
||||
imports: [],
|
||||
});
|
||||
|
||||
registry.registerSelector(ProgramCmp, 'program-cmp');
|
||||
|
||||
debugger;
|
||||
const scope = registry.lookupCompilationScope(ProgramCmp) !;
|
||||
expect(scope).toBeDefined();
|
||||
expect(scope.directives).toBeDefined();
|
||||
expect(scope.directives.size).toBe(2);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue