fix(ivy): allow relative imports of .d.ts files (#25080)

ngtsc used to assume that all .d.ts dependencies (that is, third party
packages) were imported via an absolute module path. It turns out this
assumption isn't valid; some build tools allow relative imports of
other compilation units.

In the absolute case, ngtsc assumes (and still does) that all referenced
types are available through the entrypoint from which an @NgModule was
imported. This commit adds support for relative imports, in which case
ngtsc will use relative path resolution to determine the imports.

PR Close #25080
This commit is contained in:
Alex Rickabaugh 2018-07-26 08:49:14 -07:00 committed by Igor Minar
parent 13a0d527f6
commit e0c0c44d99
2 changed files with 15 additions and 13 deletions

View File

@ -10,7 +10,7 @@ import {Expression, ExternalExpr, ExternalReference} from '@angular/compiler';
import * as ts from 'typescript';
import {ReflectionHost} from '../../host';
import {AbsoluteReference, Reference, reflectTypeEntityToDeclaration} from '../../metadata';
import {AbsoluteReference, Reference, ResolvedReference, reflectTypeEntityToDeclaration} from '../../metadata';
import {reflectIdentifierOfDeclaration, reflectNameOfDeclaration} from '../../metadata/src/reflector';
import {toR3Reference} from './util';
@ -199,10 +199,6 @@ export class SelectorScopeRegistry {
} else {
// The module wasn't analyzed before, and probably has a precompiled ngModuleDef with a type
// annotation that specifies the needed metadata.
if (ngModuleImportedFrom === null) {
// TODO(alxhub): handle hand-compiled ngModuleDef in the current Program.
throw new Error(`Need to read .d.ts module but ngModuleImportedFrom is unspecified`);
}
data = this._readMetadataFromCompiledClass(node, ngModuleImportedFrom);
// Note that data here could still be null, if the class didn't have a precompiled
// ngModuleDef.
@ -267,7 +263,7 @@ export class SelectorScopeRegistry {
* @param ngModuleImportedFrom module specifier of the import path to assume for all declarations
* stemming from this module.
*/
private _readMetadataFromCompiledClass(clazz: ts.Declaration, ngModuleImportedFrom: string):
private _readMetadataFromCompiledClass(clazz: ts.Declaration, ngModuleImportedFrom: string|null):
ModuleData|null {
// This operation is explicitly not memoized, as it depends on `ngModuleImportedFrom`.
// TODO(alxhub): investigate caching of .d.ts module metadata.
@ -348,7 +344,8 @@ export class SelectorScopeRegistry {
* This operation assumes that these types should be imported from `ngModuleImportedFrom` unless
* they themselves were imported from another absolute path.
*/
private _extractReferencesFromType(def: ts.TypeNode, ngModuleImportedFrom: string): Reference[] {
private _extractReferencesFromType(def: ts.TypeNode, ngModuleImportedFrom: string|null):
Reference[] {
if (!ts.isTupleTypeNode(def)) {
return [];
}
@ -357,11 +354,16 @@ export class SelectorScopeRegistry {
throw new Error(`Expected TypeQueryNode`);
}
const type = element.exprName;
if (ngModuleImportedFrom !== null) {
const {node, from} = reflectTypeEntityToDeclaration(type, this.checker);
const moduleName = (from !== null && !from.startsWith('.') ? from : ngModuleImportedFrom);
const clazz = node as ts.Declaration;
const id = reflectIdentifierOfDeclaration(clazz);
const id = reflectIdentifierOfDeclaration(node);
return new AbsoluteReference(node, id !, moduleName, id !.text);
} else {
const {node} = reflectTypeEntityToDeclaration(type, this.checker);
const id = reflectIdentifierOfDeclaration(node);
return new ResolvedReference(node, id !);
}
});
}
}

View File

@ -7,4 +7,4 @@
*/
export {TypeScriptReflectionHost, filterToMembersWithDecorator, findMember, reflectObjectLiteral, reflectTypeEntityToDeclaration} from './src/reflector';
export {AbsoluteReference, ImportMode, Reference, ResolvedValue, isDynamicValue, staticallyResolve} from './src/resolver';
export {AbsoluteReference, ImportMode, Reference, ResolvedReference, ResolvedValue, isDynamicValue, staticallyResolve} from './src/resolver';