feat(ivy): pass information about used directive selectors on elements (#31782)

Extend indexing API interface to provide information about used
directives' selectors on template elements. This enables an indexer to
xref element attributes to the directives that match them.

The current way this matching is done is by mapping selectors to indexed
directives. However, this fails in cases where the directive is not
indexed by the indexer API, like for transitive dependencies. This
solution is much more general.

PR Close #31782
This commit is contained in:
Ayaz Hafiz 2019-07-22 10:24:43 -07:00 committed by Misko Hevery
parent a445826dad
commit 44039a4b16
3 changed files with 27 additions and 3 deletions

View File

@ -8,6 +8,7 @@
import {ParseSourceFile} from '@angular/compiler'; import {ParseSourceFile} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ClassDeclaration} from '../../reflection';
/** /**
* Describes the kind of identifier found in a template. * Describes the kind of identifier found in a template.
@ -38,6 +39,11 @@ export interface MethodIdentifier extends TemplateIdentifier { kind: IdentifierK
/** Describes an element attribute in a template. */ /** Describes an element attribute in a template. */
export interface AttributeIdentifier extends TemplateIdentifier { kind: IdentifierKind.Attribute; } export interface AttributeIdentifier extends TemplateIdentifier { kind: IdentifierKind.Attribute; }
/** A reference to a directive node and its selector. */
interface DirectiveReference {
node: ClassDeclaration;
selector: string;
}
/** /**
* Describes an indexed element in a template. The name of an `ElementIdentifier` is the entire * Describes an indexed element in a template. The name of an `ElementIdentifier` is the entire
* element tag, which can be parsed by an indexer to determine where used directives should be * element tag, which can be parsed by an indexer to determine where used directives should be
@ -50,7 +56,7 @@ export interface ElementIdentifier extends TemplateIdentifier {
attributes: Set<AttributeIdentifier>; attributes: Set<AttributeIdentifier>;
/** Directives applied to an element. */ /** Directives applied to an element. */
usedDirectives: Set<ts.Declaration>; usedDirectives: Set<DirectiveReference>;
} }
/** /**

View File

@ -157,7 +157,12 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
span: new AbsoluteSourceSpan(start, start + name.length), span: new AbsoluteSourceSpan(start, start + name.length),
kind: IdentifierKind.Element, kind: IdentifierKind.Element,
attributes: new Set(attributes), attributes: new Set(attributes),
usedDirectives: new Set(usedDirectives.map(dir => dir.ref.node)), usedDirectives: new Set(usedDirectives.map(dir => {
return {
node: dir.ref.node,
selector: dir.selector,
};
})),
}; };
this.identifiers.add(elId); this.identifiers.add(elId);

View File

@ -246,7 +246,20 @@ runInEachFileSystem(() => {
const refs = getTemplateIdentifiers(boundTemplate); const refs = getTemplateIdentifiers(boundTemplate);
const [ref] = Array.from(refs); const [ref] = Array.from(refs);
const usedDirectives = (ref as ElementIdentifier).usedDirectives; const usedDirectives = (ref as ElementIdentifier).usedDirectives;
expect(usedDirectives).toEqual(new Set([declA, declB, declC])); expect(usedDirectives).toEqual(new Set([
{
node: declA,
selector: 'a-selector',
},
{
node: declB,
selector: '[b-selector]',
},
{
node: declC,
selector: ':not(never-selector)',
}
]));
}); });
}); });
}); });