fix(compiler-cli): attach the correct viaModule
to namespace imports (#33495)
Previously declarations that were imported via a namespace import were given the same `bestGuessOwningModule` as the context where they were imported to. This causes problems with resolving `ModuleWithProviders` that have a type that has been imported in this way, causing errors like: ``` ERROR in Symbol UIRouterModule declared in .../@uirouter/angular/uiRouterNgModule.d.ts is not exported from .../@uirouter/angular/uirouter-angular.d.ts (import into .../src/app/child.module.ts) ``` This commit modifies the `TypescriptReflectionHost.getDirectImportOfIdentifier()` method so that it also understands how to attach the correct `viaModule` to the identifier of the namespace import. Resolves #32166 PR Close #33495
This commit is contained in:

committed by
atscott

parent
d5ae854b5b
commit
1d141a8ab1
@ -191,28 +191,21 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ignore decorators that are defined locally (not imported).
|
||||
const decl: ts.Declaration = symbol.declarations[0];
|
||||
if (!ts.isImportSpecifier(decl)) {
|
||||
const importDecl = getContainingImportDeclaration(decl);
|
||||
|
||||
// Ignore declarations that are defined locally (not imported).
|
||||
if (importDecl === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Walk back from the specifier to find the declaration, which carries the module specifier.
|
||||
const importDecl = decl.parent !.parent !.parent !;
|
||||
|
||||
// The module specifier is guaranteed to be a string literal, so this should always pass.
|
||||
if (!ts.isStringLiteral(importDecl.moduleSpecifier)) {
|
||||
// Not allowed to happen in TypeScript ASTs.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Read the module specifier.
|
||||
const from = importDecl.moduleSpecifier.text;
|
||||
|
||||
// Compute the name by which the decorator was exported, not imported.
|
||||
const name = (decl.propertyName !== undefined ? decl.propertyName : decl.name).text;
|
||||
|
||||
return {from, name};
|
||||
return {from: importDecl.moduleSpecifier.text, name: getExportedName(decl, id)};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -549,3 +542,23 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I
|
||||
}
|
||||
return ts.isIdentifier(propertyAccess.expression) ? propertyAccess.expression : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ImportDeclaration for the given `node` if it is either an `ImportSpecifier` or a
|
||||
* `NamespaceImport`. If not return `null`.
|
||||
*/
|
||||
function getContainingImportDeclaration(node: ts.Node): ts.ImportDeclaration|null {
|
||||
return ts.isImportSpecifier(node) ? node.parent !.parent !.parent ! :
|
||||
ts.isNamespaceImport(node) ? node.parent.parent : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the name by which the `decl` was exported, not imported.
|
||||
* If no such declaration can be found (e.g. it is a namespace import)
|
||||
* then fallback to the `originalId`.
|
||||
*/
|
||||
function getExportedName(decl: ts.Declaration, originalId: ts.Identifier): string {
|
||||
return ts.isImportSpecifier(decl) ?
|
||||
(decl.propertyName !== undefined ? decl.propertyName : decl.name).text :
|
||||
originalId.text;
|
||||
}
|
Reference in New Issue
Block a user