diff --git a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts index 4f2b652371b..28cc68a7906 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts @@ -72,6 +72,8 @@ import { SemanticDepGraphUpdater, } from '../../../incremental/semantic_graph'; import {IndexingContext} from '../../../indexer'; +import {AbstractBoundTemplate} from '../../../indexer/src/api'; + import { DirectiveMeta, extractDirectiveTypeCheckMeta, @@ -1130,10 +1132,31 @@ export class ComponentDecoratorHandler implements DecoratorHandler< const binder = new R3TargetBinder(matcher); const boundTemplate = binder.bind({template: analysis.template.diagNodes}); + const abstractBoundTemplate: AbstractBoundTemplate = { + getDirectivesOfNode(node) { + return boundTemplate.getDirectivesOfNode(node); + }, + getReferenceTarget(node) { + return boundTemplate.getReferenceTarget(node); + }, + getExpressionTarget(ast) { + return boundTemplate.getExpressionTarget(ast); + }, + getUsedDirectives() { + return boundTemplate.getUsedDirectives().map((dir) => ({ + ref: {node: dir.ref.node}, + isComponent: dir.isComponent, + })); + }, + getTemplateAst() { + return boundTemplate.target.template; + }, + }; + context.addComponent({ declaration: node, selector, - boundTemplate, + boundTemplate: abstractBoundTemplate, templateMeta: { isInline: analysis.template.declaration.isInline, file: analysis.template.file, diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 1f9df679fa7..2a36d7134db 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -64,6 +64,7 @@ import { } from '../../incremental'; import {SemanticSymbol} from '../../incremental/semantic_graph'; import {generateAnalysis, IndexedComponent, IndexingContext} from '../../indexer'; +import {NodeAdapter} from '../../indexer/src/api'; import { CompoundMetadataReader, CompoundMetadataRegistry, @@ -919,7 +920,20 @@ export class NgCompiler { const compilation = this.ensureAnalyzed(); const context = new IndexingContext(); compilation.traitCompiler.index(context); - return generateAnalysis(context); + + const adapter: NodeAdapter = { + getName(node: DeclarationNode): string { + return ts.isClassDeclaration(node) && node.name ? node.name.getText() : ''; + }, + getFileName(node: DeclarationNode): string { + return node.getSourceFile().fileName; + }, + getContent(node: DeclarationNode): string { + return node.getSourceFile().getFullText(); + }, + }; + + return generateAnalysis(context, adapter); } /** diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/api.ts b/packages/compiler-cli/src/ngtsc/indexer/src/api.ts index 9182b5c12c4..d4fb40ec952 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/api.ts @@ -6,9 +6,19 @@ * found in the LICENSE file at https://angular.dev/license */ -import {ParseSourceFile} from '@angular/compiler'; - -import {ClassDeclaration, DeclarationNode} from '../../reflection'; +import { + AST, + ParseSourceFile, + TmplAstComponent, + TmplAstDirective, + TmplAstElement, + TmplAstLetDeclaration, + TmplAstNode, + TmplAstReference, + TmplAstTemplate, + TmplAstVariable, +} from '@angular/compiler'; +import {DeclarationNode} from '../../reflection'; /** * Describes the kind of identifier found in a template. @@ -37,16 +47,16 @@ export interface TemplateIdentifier { } /** Describes a template expression, which may have a template reference or variable target. */ -interface ExpressionIdentifier extends TemplateIdentifier { +interface ExpressionIdentifier extends TemplateIdentifier { /** * ReferenceIdentifier or VariableIdentifier in the template that this identifier targets, if * any. If the target is `null`, it points to a declaration on the component class. - * */ - target: ReferenceIdentifier | VariableIdentifier | LetDeclarationIdentifier | null; + */ + target: ReferenceIdentifier | VariableIdentifier | LetDeclarationIdentifier | null; } /** Describes a property accessed in a template. */ -export interface PropertyIdentifier extends ExpressionIdentifier { +export interface PropertyIdentifier extends ExpressionIdentifier { kind: IdentifierKind.Property; } @@ -54,7 +64,7 @@ export interface PropertyIdentifier extends ExpressionIdentifier { * Describes a method accessed in a template. * @deprecated No longer being used. To be removed. */ -export interface MethodIdentifier extends ExpressionIdentifier { +export interface MethodIdentifier extends ExpressionIdentifier { kind: IdentifierKind.Method; } @@ -64,57 +74,63 @@ export interface AttributeIdentifier extends TemplateIdentifier { } /** A reference to a directive node and its selector. */ -interface DirectiveReference { - node: ClassDeclaration; +interface DirectiveReference { + node: T; selector: string; } /** A base interface for element and template identifiers. */ -interface BaseDirectiveHostIdentifier extends TemplateIdentifier { +interface BaseDirectiveHostIdentifier extends TemplateIdentifier { /** Attributes on an element or template. */ attributes: Set; /** Directives applied to an element or template. */ - usedDirectives: Set; + usedDirectives: Set>; } /** * 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 * referenced. */ -export interface ElementIdentifier extends BaseDirectiveHostIdentifier { +export interface ElementIdentifier extends BaseDirectiveHostIdentifier { kind: IdentifierKind.Element; } /** Describes an indexed template node in a component template file. */ -export interface TemplateNodeIdentifier extends BaseDirectiveHostIdentifier { +export interface TemplateNodeIdentifier< + T = DeclarationNode, +> extends BaseDirectiveHostIdentifier { kind: IdentifierKind.Template; } /** Describes a selectorless component node in a template file. */ -export interface ComponentNodeIdentifier extends BaseDirectiveHostIdentifier { +export interface ComponentNodeIdentifier< + T = DeclarationNode, +> extends BaseDirectiveHostIdentifier { kind: IdentifierKind.Component; } /** Describes a selectorless directive node in a template file. */ -export interface DirectiveNodeIdentifier extends BaseDirectiveHostIdentifier { +export interface DirectiveNodeIdentifier< + T = DeclarationNode, +> extends BaseDirectiveHostIdentifier { kind: IdentifierKind.Directive; } /** Describes a reference in a template like "foo" in `
`. */ -export interface ReferenceIdentifier extends TemplateIdentifier { +export interface ReferenceIdentifier extends TemplateIdentifier { kind: IdentifierKind.Reference; /** The target of this reference. If the target is not known, this is `null`. */ target: { /** The template AST node that the reference targets. */ - node: DirectiveHostIdentifier; + node: DirectiveHostIdentifier; /** * The directive on `node` that the reference targets. If no directive is targeted, this is * `null`. */ - directive: ClassDeclaration | null; + directive: T | null; } | null; } @@ -132,23 +148,23 @@ export interface LetDeclarationIdentifier extends TemplateIdentifier { * Identifiers recorded at the top level of the template, without any context about the HTML nodes * they were discovered in. */ -export type TopLevelIdentifier = - | PropertyIdentifier - | ElementIdentifier - | TemplateNodeIdentifier - | ReferenceIdentifier +export type TopLevelIdentifier = + | PropertyIdentifier + | ElementIdentifier + | TemplateNodeIdentifier + | ReferenceIdentifier | VariableIdentifier - | MethodIdentifier + | MethodIdentifier | LetDeclarationIdentifier - | ComponentNodeIdentifier - | DirectiveNodeIdentifier; + | ComponentNodeIdentifier + | DirectiveNodeIdentifier; /** Identifiers that can bring in directives to the template. */ -export type DirectiveHostIdentifier = - | ElementIdentifier - | TemplateNodeIdentifier - | ComponentNodeIdentifier - | DirectiveNodeIdentifier; +export type DirectiveHostIdentifier = + | ElementIdentifier + | TemplateNodeIdentifier + | ComponentNodeIdentifier + | DirectiveNodeIdentifier; /** * Describes the absolute byte offsets of a text anchor in a source code. @@ -163,15 +179,47 @@ export class AbsoluteSourceSpan { /** * Describes an analyzed, indexed component and its template. */ -export interface IndexedComponent { +export interface IndexedComponent { name: string; selector: string | null; file: ParseSourceFile; template: { - identifiers: Set; - usedComponents: Set; + identifiers: Set>; + usedComponents: Set; isInline: boolean; file: ParseSourceFile; }; errors: Error[]; } + +/** + * Abstract representation of a bound template, providing methods to query + * directives and targets in the template. + */ +export interface AbstractBoundTemplate { + getDirectivesOfNode( + node: TmplAstElement | TmplAstTemplate | TmplAstComponent | TmplAstDirective, + ): Array<{ref: {node: T}; selector: string | null}> | null; + getReferenceTarget(node: TmplAstReference): + | TmplAstElement + | TmplAstTemplate + | TmplAstComponent + | TmplAstDirective + | { + node: TmplAstElement | TmplAstTemplate | TmplAstComponent | TmplAstDirective; + directive: {ref: {node: T}}; + } + | null; + getExpressionTarget(ast: AST): TmplAstReference | TmplAstVariable | TmplAstLetDeclaration | null; + getUsedDirectives(): Array<{ref: {node: T}; isComponent: boolean}>; + getTemplateAst(): TmplAstNode[] | undefined; +} + +/** + * Adapter to extract information from a node, such as its name and file name. + */ +export interface NodeAdapter { + getName(node: T): string; + getFileName(node: T): string; + getContent(node: T): string; +} diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/context.ts b/packages/compiler-cli/src/ngtsc/indexer/src/context.ts index fb02695188d..094cd520f6a 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/context.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/context.ts @@ -6,13 +6,16 @@ * found in the LICENSE file at https://angular.dev/license */ -import {BoundTarget, DirectiveMeta, ParseSourceFile} from '@angular/compiler'; +import {DirectiveMeta, ParseSourceFile} from '@angular/compiler'; +import {DeclarationNode} from '../../reflection'; -import {Reference} from '../../imports'; -import {ClassDeclaration} from '../../reflection'; +import {AbstractBoundTemplate} from './api.js'; -export interface ComponentMeta extends DirectiveMeta { - ref: Reference; +/** + * Metadata about a component, extending DirectiveMeta to include a reference to the node. + */ +export interface ComponentMeta extends DirectiveMeta { + ref: {key: string; node: T}; /** * Unparsed selector of the directive, or null if the directive does not have a selector. */ @@ -22,9 +25,9 @@ export interface ComponentMeta extends DirectiveMeta { /** * An intermediate representation of a component. */ -export interface ComponentInfo { +export interface ComponentInfo { /** Component TypeScript class declaration */ - declaration: ClassDeclaration; + declaration: T; /** Component template selector if it exists, otherwise null. */ selector: string | null; @@ -33,7 +36,7 @@ export interface ComponentInfo { * BoundTarget containing the parsed template. Can also be used to query for directives used in * the template. */ - boundTemplate: BoundTarget; + boundTemplate: AbstractBoundTemplate; /** Metadata about the template */ templateMeta: { @@ -51,13 +54,13 @@ export interface ComponentInfo { * An `IndexingContext` collects component and template analysis information from * `DecoratorHandler`s and exposes them to be indexed. */ -export class IndexingContext { - readonly components = new Set(); +export class IndexingContext { + readonly components = new Set>(); /** * Adds a component to the context. */ - addComponent(info: ComponentInfo) { + addComponent(info: ComponentInfo) { this.components.add(info); } } diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/template.ts b/packages/compiler-cli/src/ngtsc/indexer/src/template.ts index 6203d78b4b1..c0820f20a06 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/template.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/template.ts @@ -8,7 +8,6 @@ import { AST, ASTWithSource, - BoundTarget, CombinedRecursiveAstVisitor, ImplicitReceiver, ParseSourceSpan, @@ -26,10 +25,11 @@ import { tmplAstVisitAll, } from '@angular/compiler'; -import {ClassDeclaration, DeclarationNode} from '../../reflection'; +import {DeclarationNode} from '../../reflection'; import { AbsoluteSourceSpan, + AbstractBoundTemplate, AttributeIdentifier, ComponentNodeIdentifier, DirectiveHostIdentifier, @@ -44,30 +44,32 @@ import { TopLevelIdentifier, VariableIdentifier, } from './api'; -import {ComponentMeta} from './context'; -type ExpressionIdentifier = PropertyIdentifier | MethodIdentifier; +type ExpressionIdentifier = PropertyIdentifier | MethodIdentifier; type TmplTarget = TmplAstReference | TmplAstVariable | TmplAstLetDeclaration; -type TargetIdentifier = ReferenceIdentifier | VariableIdentifier | LetDeclarationIdentifier; -type TargetIdentifierMap = Map; +type TargetIdentifier = + | ReferenceIdentifier + | VariableIdentifier + | LetDeclarationIdentifier; +type TargetIdentifierMap = Map>; /** * Visits the AST of a parsed Angular template. Discovers and stores * identifiers of interest, deferring to an `ExpressionVisitor` as needed. */ -class TemplateVisitor extends CombinedRecursiveAstVisitor { +class TemplateVisitor extends CombinedRecursiveAstVisitor { // Identifiers of interest found in the template. - readonly identifiers = new Set(); + readonly identifiers = new Set>(); readonly errors: Error[] = []; private currentAstWithSource: {source: string | null; absoluteOffset: number} | null = null; // Map of targets in a template to their identifiers. - private readonly targetIdentifierCache: TargetIdentifierMap = new Map(); + private readonly targetIdentifierCache: TargetIdentifierMap = new Map(); // Map of elements and templates to their identifiers. private readonly directiveHostIdentifierCache = new Map< TmplAstElement | TmplAstTemplate | TmplAstComponent | TmplAstDirective, - DirectiveHostIdentifier + DirectiveHostIdentifier >(); /** @@ -76,7 +78,7 @@ class TemplateVisitor extends CombinedRecursiveAstVisitor { * * @param boundTemplate bound template target */ - constructor(private boundTemplate: BoundTarget) { + constructor(private boundTemplate: AbstractBoundTemplate) { super(); } @@ -158,7 +160,7 @@ class TemplateVisitor extends CombinedRecursiveAstVisitor { /** Creates an identifier for a template element or template node. */ private directiveHostToIdentifier( node: TmplAstElement | TmplAstTemplate | TmplAstComponent | TmplAstDirective, - ): DirectiveHostIdentifier | null { + ): DirectiveHostIdentifier | null { // If this node has already been seen, return the cached result. if (this.directiveHostIdentifierCache.has(node)) { return this.directiveHostIdentifierCache.get(node)!; @@ -229,17 +231,17 @@ class TemplateVisitor extends CombinedRecursiveAstVisitor { ), // cast b/c pre-TypeScript 3.5 unions aren't well discriminated } as - | ElementIdentifier - | TemplateNodeIdentifier - | ComponentNodeIdentifier - | DirectiveNodeIdentifier; + | ElementIdentifier + | TemplateNodeIdentifier + | ComponentNodeIdentifier + | DirectiveNodeIdentifier; this.directiveHostIdentifierCache.set(node, identifier); return identifier; } /** Creates an identifier for a template reference or template variable target. */ - private targetToIdentifier(node: TmplTarget): TargetIdentifier | null { + private targetToIdentifier(node: TmplTarget): TargetIdentifier | null { // If this node has already been seen, return the cached result. if (this.targetIdentifierCache.has(node)) { return this.targetIdentifierCache.get(node)!; @@ -252,7 +254,7 @@ class TemplateVisitor extends CombinedRecursiveAstVisitor { } const span = new AbsoluteSourceSpan(start, start + name.length); - let identifier: ReferenceIdentifier | VariableIdentifier | LetDeclarationIdentifier; + let identifier: ReferenceIdentifier | VariableIdentifier | LetDeclarationIdentifier; if (node instanceof TmplAstReference) { // If the node is a reference, we care about its target. The target can be an element, a // template, a directive applied on a template or element (in which case the directive field @@ -260,8 +262,8 @@ class TemplateVisitor extends CombinedRecursiveAstVisitor { const refTarget = this.boundTemplate.getReferenceTarget(node); let target = null; if (refTarget) { - let node: DirectiveHostIdentifier | null = null; - let directive: ClassDeclaration | null = null; + let node: DirectiveHostIdentifier | null = null; + let directive: T | null = null; if ( refTarget instanceof TmplAstElement || refTarget instanceof TmplAstTemplate || @@ -342,7 +344,7 @@ class TemplateVisitor extends CombinedRecursiveAstVisitor { */ private visitIdentifier( ast: AST & {name: string; receiver: AST}, - kind: ExpressionIdentifier['kind'], + kind: ExpressionIdentifier['kind'], ) { // Only handle identifiers in expressions that have a source location. if (this.currentAstWithSource === null || this.currentAstWithSource.source === null) { @@ -383,7 +385,7 @@ class TemplateVisitor extends CombinedRecursiveAstVisitor { const span = new AbsoluteSourceSpan(absoluteStart, absoluteStart + ast.name.length); const targetAst = this.boundTemplate.getExpressionTarget(ast); const target = targetAst ? this.targetToIdentifier(targetAst) : null; - const identifier: ExpressionIdentifier = { + const identifier: ExpressionIdentifier = { name: ast.name, span, kind, @@ -400,13 +402,16 @@ class TemplateVisitor extends CombinedRecursiveAstVisitor { * @param boundTemplate bound template target, which can be used for querying expression targets. * @return identifiers in template */ -export function getTemplateIdentifiers(boundTemplate: BoundTarget): { - identifiers: Set; +export function getTemplateIdentifiers( + boundTemplate: AbstractBoundTemplate, +): { + identifiers: Set>; errors: Error[]; } { - const visitor = new TemplateVisitor(boundTemplate); - if (boundTemplate.target.template !== undefined) { - tmplAstVisitAll(visitor, boundTemplate.target.template); + const visitor = new TemplateVisitor(boundTemplate); + const template = boundTemplate.getTemplateAst(); + if (template !== undefined) { + tmplAstVisitAll(visitor, template); } return {identifiers: visitor.identifiers, errors: visitor.errors}; } diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/transform.ts b/packages/compiler-cli/src/ngtsc/indexer/src/transform.ts index 993c55d0473..18014578dad 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/transform.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/transform.ts @@ -10,9 +10,9 @@ import {ParseSourceFile} from '@angular/compiler'; import {DeclarationNode} from '../../reflection'; -import {IndexedComponent} from './api'; -import {IndexingContext} from './context'; -import {getTemplateIdentifiers} from './template'; +import {IndexedComponent, NodeAdapter} from './api.js'; +import {IndexingContext} from './context.js'; +import {getTemplateIdentifiers} from './template.js'; /** * Generates `IndexedComponent` entries from a `IndexingContext`, which has information @@ -20,13 +20,17 @@ import {getTemplateIdentifiers} from './template'; * * The context must be populated before `generateAnalysis` is called. */ -export function generateAnalysis(context: IndexingContext): Map { - const analysis = new Map(); +export function generateAnalysis( + context: IndexingContext, + adapter: NodeAdapter, +): Map> { + const analysis = new Map>(); context.components.forEach(({declaration, selector, boundTemplate, templateMeta}) => { - const name = declaration.name.getText(); + const name = adapter.getName(declaration); + const fileName = adapter.getFileName(declaration); - const usedComponents = new Set(); + const usedComponents = new Set(); const usedDirs = boundTemplate.getUsedDirectives(); usedDirs.forEach((dir) => { if (dir.isComponent) { @@ -36,10 +40,7 @@ export function generateAnalysis(context: IndexingContext): Map(boundTemplate); analysis.set(declaration, { name, selector, diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/context_spec.ts b/packages/compiler-cli/src/ngtsc/indexer/test/context_spec.ts index 72071f77a74..672bf184f05 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/context_spec.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/context_spec.ts @@ -9,6 +9,8 @@ import {ParseSourceFile} from '@angular/compiler'; import {runInEachFileSystem} from '../../file_system/testing'; import {IndexingContext} from '../src/context'; import * as util from './util'; +import {AbstractBoundTemplate} from '../src/api'; +import {DeclarationNode} from '../../reflection'; runInEachFileSystem(() => { describe('ComponentAnalysisContext', () => { diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts b/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts index 5c16242da73..4fc4c5aa781 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts @@ -10,6 +10,7 @@ import {BoundTarget} from '@angular/compiler'; import { AbsoluteSourceSpan, + AbstractBoundTemplate, AttributeIdentifier, DirectiveHostIdentifier, ElementIdentifier, @@ -25,6 +26,7 @@ import {ComponentMeta} from '../src/context'; import {getTemplateIdentifiers as getTemplateIdentifiersAndErrors} from '../src/template'; import * as util from './util'; +import {DeclarationNode} from '../../reflection'; function bind(template: string, enableSelectorless = false) { return util.getBoundTemplate(template, { @@ -34,7 +36,7 @@ function bind(template: string, enableSelectorless = false) { }); } -function getTemplateIdentifiers(boundTemplate: BoundTarget) { +function getTemplateIdentifiers(boundTemplate: AbstractBoundTemplate) { return getTemplateIdentifiersAndErrors(boundTemplate).identifiers; } diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/transform_spec.ts b/packages/compiler-cli/src/ngtsc/indexer/test/transform_spec.ts index 331667bd5d2..fe37bd2ac21 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/transform_spec.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/transform_spec.ts @@ -5,15 +5,17 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ -import {BoundTarget, ParseSourceFile} from '@angular/compiler'; +import {ParseSourceFile} from '@angular/compiler'; import {runInEachFileSystem} from '../../file_system/testing'; -import {ClassDeclaration} from '../../reflection'; -import {ComponentMeta, IndexingContext} from '../src/context'; +import {ClassDeclaration, DeclarationNode} from '../../reflection'; +import {IndexingContext} from '../src/context'; import {getTemplateIdentifiers} from '../src/template'; import {generateAnalysis} from '../src/transform'; import * as util from './util'; +import {AbstractBoundTemplate, NodeAdapter} from '../src/api'; +import ts from 'typescript'; /** * Adds information about a component to a context. @@ -23,7 +25,7 @@ function populateContext( component: ClassDeclaration, selector: string, template: string, - boundTemplate: BoundTarget, + boundTemplate: AbstractBoundTemplate, isInline: boolean = false, ) { context.addComponent({ @@ -37,6 +39,18 @@ function populateContext( }); } +const adapter: NodeAdapter = { + getName(node: DeclarationNode): string { + return ts.isClassDeclaration(node) && node.name ? node.name.getText() : ''; + }, + getFileName(node: DeclarationNode): string { + return node.getSourceFile().fileName; + }, + getContent(node: DeclarationNode): string { + return node.getSourceFile().getFullText(); + }, +}; + runInEachFileSystem(() => { describe('generateAnalysis', () => { it('should emit component and template analysis information', () => { @@ -44,7 +58,7 @@ runInEachFileSystem(() => { const decl = util.getComponentDeclaration('class C {}', 'C'); const template = '
{{foo}}
'; populateContext(context, decl, 'c-selector', template, util.getBoundTemplate(template)); - const analysis = generateAnalysis(context); + const analysis = generateAnalysis(context, adapter); expect(analysis.size).toBe(1); @@ -76,7 +90,7 @@ runInEachFileSystem(() => { util.getBoundTemplate(template), /* inline template */ true, ); - const analysis = generateAnalysis(context); + const analysis = generateAnalysis(context, adapter); expect(analysis.size).toBe(1); @@ -92,7 +106,7 @@ runInEachFileSystem(() => { const decl = util.getComponentDeclaration('class C {}', 'C'); const template = '
{{foo}}
'; populateContext(context, decl, 'c-selector', template, util.getBoundTemplate(template)); - const analysis = generateAnalysis(context); + const analysis = generateAnalysis(context, adapter); expect(analysis.size).toBe(1); @@ -122,7 +136,7 @@ runInEachFileSystem(() => { populateContext(context, declA, 'a-selector', templateA, boundA); populateContext(context, declB, 'b-selector', templateB, boundB); - const analysis = generateAnalysis(context); + const analysis = generateAnalysis(context, adapter); expect(analysis.size).toBe(2); diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/util.ts b/packages/compiler-cli/src/ngtsc/indexer/test/util.ts index 43f9d93cb5d..a3d0f320a3a 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/util.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/util.ts @@ -22,9 +22,10 @@ import ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath} from '../../file_system'; import {Reference} from '../../imports'; -import {ClassDeclaration} from '../../reflection'; +import {ClassDeclaration, DeclarationNode} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {ComponentMeta} from '../src/context'; +import {AbstractBoundTemplate} from '../src/api'; /** Dummy file URL */ function getTestFilePath(): AbsoluteFsPath { @@ -57,7 +58,7 @@ export function getBoundTemplate( template: string, options: ParseTemplateOptions = {}, components: Array<{selector: string | null; declaration: ClassDeclaration}> = [], -): BoundTarget { +): AbstractBoundTemplate { const componentsMeta = components.map(({selector, declaration}) => ({ ref: new Reference(declaration), selector, @@ -95,5 +96,30 @@ export function getBoundTemplate( const binder = new R3TargetBinder(matcher); - return binder.bind({template: parseTemplate(template, getTestFilePath(), options).nodes}); + const boundTemplate = binder.bind({ + template: parseTemplate(template, getTestFilePath(), options).nodes, + }); + const abstractBoundTemplate: AbstractBoundTemplate = { + getDirectivesOfNode(node) { + return boundTemplate.getDirectivesOfNode(node); + }, + getReferenceTarget(node) { + return boundTemplate.getReferenceTarget(node); + }, + getExpressionTarget(ast) { + return boundTemplate.getExpressionTarget(ast); + }, + getUsedDirectives() { + return boundTemplate.getUsedDirectives().map((dir) => ({ + ref: {node: dir.ref.node}, + isComponent: dir.isComponent, + })); + }, + getTemplateAst() { + return boundTemplate.target.template; + }, + }; + return abstractBoundTemplate; } + +function createAbstractBoundTemplate() {}