feat(ivy): augment selector scopes to extract additional metadata (#26203)
Before type checking can be turned on in ngtsc, appropriate metadata for each component and directive must be determined. This commit adds tracking of the extra metadata in *DefWithMeta types to the selector scope handling, allowing for later extraction for type-checking purposes. PR Close #26203
This commit is contained in:
parent
5f1273ba2e
commit
868047e87f
|
@ -15,5 +15,6 @@ ts_library(
|
|||
"//packages/compiler-cli/src/ngtsc/host",
|
||||
"//packages/compiler-cli/src/ngtsc/metadata",
|
||||
"//packages/compiler-cli/src/ngtsc/transform",
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -134,7 +134,14 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
|
|||
// If the component has a selector, it should be registered with the `SelectorScopeRegistry` so
|
||||
// when this component appears in an `@NgModule` scope, its selector can be determined.
|
||||
if (metadata.selector !== null) {
|
||||
this.scopeRegistry.registerSelector(node, metadata.selector);
|
||||
this.scopeRegistry.registerDirective(node, {
|
||||
selector: metadata.selector,
|
||||
exportAs: metadata.exportAs,
|
||||
inputs: metadata.inputs,
|
||||
outputs: metadata.outputs,
|
||||
queries: metadata.queries.map(query => query.propertyName),
|
||||
isComponent: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Construct the list of view queries.
|
||||
|
@ -198,7 +205,9 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
|
|||
// Replace the empty components and directives from the analyze() step with a fully expanded
|
||||
// scope. This is possible now because during compile() the whole compilation unit has been
|
||||
// fully analyzed.
|
||||
const {directives, pipes, containsForwardDecls} = scope;
|
||||
const {pipes, containsForwardDecls} = scope;
|
||||
const directives = new Map<string, Expression>();
|
||||
scope.directives.forEach((meta, selector) => directives.set(selector, meta.directive));
|
||||
const wrapDirectivesInClosure: boolean = !!containsForwardDecls;
|
||||
analysis = {...analysis, directives, pipes, wrapDirectivesInClosure};
|
||||
}
|
||||
|
|
|
@ -40,7 +40,14 @@ export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMe
|
|||
// If the directive has a selector, it should be registered with the `SelectorScopeRegistry` so
|
||||
// when this directive appears in an `@NgModule` scope, its selector can be determined.
|
||||
if (analysis && analysis.selector !== null) {
|
||||
this.scopeRegistry.registerSelector(node, analysis.selector);
|
||||
this.scopeRegistry.registerDirective(node, {
|
||||
selector: analysis.selector,
|
||||
exportAs: analysis.exportAs,
|
||||
inputs: analysis.inputs,
|
||||
outputs: analysis.outputs,
|
||||
queries: analysis.queries.map(query => query.propertyName),
|
||||
isComponent: false,
|
||||
});
|
||||
}
|
||||
|
||||
return {analysis};
|
||||
|
|
|
@ -65,13 +65,13 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||
}
|
||||
|
||||
// Extract the module declarations, imports, and exports.
|
||||
let declarations: Reference[] = [];
|
||||
let declarations: Reference<ts.Declaration>[] = [];
|
||||
if (ngModule.has('declarations')) {
|
||||
const expr = ngModule.get('declarations') !;
|
||||
const declarationMeta = staticallyResolve(expr, this.reflector, this.checker);
|
||||
declarations = this.resolveTypeList(expr, declarationMeta, 'declarations');
|
||||
}
|
||||
let imports: Reference[] = [];
|
||||
let imports: Reference<ts.Declaration>[] = [];
|
||||
if (ngModule.has('imports')) {
|
||||
const expr = ngModule.get('imports') !;
|
||||
const importsMeta = staticallyResolve(
|
||||
|
@ -79,7 +79,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||
ref => this._extractModuleFromModuleWithProvidersFn(ref.node));
|
||||
imports = this.resolveTypeList(expr, importsMeta, 'imports');
|
||||
}
|
||||
let exports: Reference[] = [];
|
||||
let exports: Reference<ts.Declaration>[] = [];
|
||||
if (ngModule.has('exports')) {
|
||||
const expr = ngModule.get('exports') !;
|
||||
const exportsMeta = staticallyResolve(
|
||||
|
@ -87,7 +87,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||
ref => this._extractModuleFromModuleWithProvidersFn(ref.node));
|
||||
exports = this.resolveTypeList(expr, exportsMeta, 'exports');
|
||||
}
|
||||
let bootstrap: Reference[] = [];
|
||||
let bootstrap: Reference<ts.Declaration>[] = [];
|
||||
if (ngModule.has('bootstrap')) {
|
||||
const expr = ngModule.get('bootstrap') !;
|
||||
const bootstrapMeta = staticallyResolve(expr, this.reflector, this.checker);
|
||||
|
@ -198,8 +198,9 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||
/**
|
||||
* Compute a list of `Reference`s from a resolved metadata value.
|
||||
*/
|
||||
private resolveTypeList(expr: ts.Node, resolvedList: ResolvedValue, name: string): Reference[] {
|
||||
const refList: Reference[] = [];
|
||||
private resolveTypeList(expr: ts.Node, resolvedList: ResolvedValue, name: string):
|
||||
Reference<ts.Declaration>[] {
|
||||
const refList: Reference<ts.Declaration>[] = [];
|
||||
if (!Array.isArray(resolvedList)) {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, expr, `Expected array when reading property ${name}`);
|
||||
|
@ -215,7 +216,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||
if (Array.isArray(entry)) {
|
||||
// Recurse into nested arrays.
|
||||
refList.push(...this.resolveTypeList(expr, entry, name));
|
||||
} else if (entry instanceof Reference) {
|
||||
} else if (isDeclarationReference(entry)) {
|
||||
if (!entry.expressable) {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, expr, `One entry in ${name} is not a type`);
|
||||
|
@ -234,3 +235,9 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||
return refList;
|
||||
}
|
||||
}
|
||||
|
||||
function isDeclarationReference(ref: any): ref is Reference<ts.Declaration> {
|
||||
return ref instanceof Reference &&
|
||||
(ts.isClassDeclaration(ref.node) || ts.isFunctionDeclaration(ref.node) ||
|
||||
ts.isVariableDeclaration(ref.node));
|
||||
}
|
||||
|
|
|
@ -12,18 +12,18 @@ import * as ts from 'typescript';
|
|||
import {ReflectionHost} from '../../host';
|
||||
import {AbsoluteReference, Reference, ResolvedReference, reflectTypeEntityToDeclaration} from '../../metadata';
|
||||
import {reflectIdentifierOfDeclaration, reflectNameOfDeclaration} from '../../metadata/src/reflector';
|
||||
import {TypeCheckableDirectiveMeta} from '../../typecheck';
|
||||
|
||||
import {toR3Reference} from './util';
|
||||
|
||||
import {extractDirectiveGuards, toR3Reference} from './util';
|
||||
|
||||
|
||||
/**
|
||||
* Metadata extracted for a given NgModule that can be used to compute selector scopes.
|
||||
*/
|
||||
export interface ModuleData {
|
||||
declarations: Reference[];
|
||||
imports: Reference[];
|
||||
exports: Reference[];
|
||||
declarations: Reference<ts.Declaration>[];
|
||||
imports: Reference<ts.Declaration>[];
|
||||
exports: Reference<ts.Declaration>[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,11 +31,16 @@ export interface ModuleData {
|
|||
* context of some module.
|
||||
*/
|
||||
export interface CompilationScope<T> {
|
||||
directives: Map<string, T>;
|
||||
directives: Map<string, ScopeDirective<T>>;
|
||||
pipes: Map<string, T>;
|
||||
containsForwardDecls?: boolean;
|
||||
}
|
||||
|
||||
export interface ScopeDirective<T> extends TypeCheckableDirectiveMeta {
|
||||
selector: string;
|
||||
directive: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Both transitively expanded scopes for a given NgModule.
|
||||
*/
|
||||
|
@ -44,13 +49,13 @@ interface SelectorScopes {
|
|||
* Set of components, directives, and pipes visible to all components being compiled in the
|
||||
* context of some module.
|
||||
*/
|
||||
compilation: Reference[];
|
||||
compilation: Reference<ts.Declaration>[];
|
||||
|
||||
/**
|
||||
* Set of components, directives, and pipes added to the compilation scope of any module importing
|
||||
* some module.
|
||||
*/
|
||||
exported: Reference[];
|
||||
exported: Reference<ts.Declaration>[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,9 +76,9 @@ export class SelectorScopeRegistry {
|
|||
private _compilationScopeCache = new Map<ts.Declaration, CompilationScope<Reference>>();
|
||||
|
||||
/**
|
||||
* Map of components/directives to their selector.
|
||||
* Map of components/directives to their metadata.
|
||||
*/
|
||||
private _directiveToSelector = new Map<ts.Declaration, string>();
|
||||
private _directiveToMetadata = new Map<ts.Declaration, ScopeDirective<Reference>>();
|
||||
|
||||
/**
|
||||
* Map of pipes to their name.
|
||||
|
@ -105,15 +110,16 @@ export class SelectorScopeRegistry {
|
|||
}
|
||||
|
||||
/**
|
||||
* Register the selector of a component or directive with the registry.
|
||||
* Register the metadata of a component or directive with the registry.
|
||||
*/
|
||||
registerSelector(node: ts.Declaration, selector: string): void {
|
||||
registerDirective(node: ts.Declaration, metadata: ScopeDirective<Reference>): void {
|
||||
node = ts.getOriginalNode(node) as ts.Declaration;
|
||||
|
||||
if (this._directiveToSelector.has(node)) {
|
||||
throw new Error(`Selector already registered: ${reflectNameOfDeclaration(node)} ${selector}`);
|
||||
if (this._directiveToMetadata.has(node)) {
|
||||
throw new Error(
|
||||
`Selector already registered: ${reflectNameOfDeclaration(node)} ${metadata.selector}`);
|
||||
}
|
||||
this._directiveToSelector.set(node, selector);
|
||||
this._directiveToMetadata.set(node, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,11 +131,7 @@ export class SelectorScopeRegistry {
|
|||
this._pipeToName.set(node, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce the compilation scope of a component, which is determined by the module that declares
|
||||
* it.
|
||||
*/
|
||||
lookupCompilationScope(node: ts.Declaration): CompilationScope<Expression>|null {
|
||||
lookupCompilationScopeAsRefs(node: ts.Declaration): CompilationScope<Reference>|null {
|
||||
node = ts.getOriginalNode(node) as ts.Declaration;
|
||||
|
||||
// If the component has no associated module, then it has no compilation scope.
|
||||
|
@ -147,11 +149,11 @@ export class SelectorScopeRegistry {
|
|||
|
||||
// The scope as cached is in terms of References, not Expressions. Converting between them
|
||||
// requires knowledge of the context file (in this case, the component node's source file).
|
||||
return convertScopeToExpressions(scope, node);
|
||||
return scope;
|
||||
}
|
||||
|
||||
// This is the first time the scope for this module is being computed.
|
||||
const directives = new Map<string, Reference>();
|
||||
const directives = new Map<string, ScopeDirective<Reference<ts.Declaration>>>();
|
||||
const pipes = new Map<string, Reference>();
|
||||
|
||||
// Process the declaration scope of the module, and lookup the selector of every declared type.
|
||||
|
@ -161,10 +163,10 @@ export class SelectorScopeRegistry {
|
|||
const node = ts.getOriginalNode(ref.node) as ts.Declaration;
|
||||
|
||||
// Either the node represents a directive or a pipe. Look for both.
|
||||
const selector = this.lookupDirectiveSelector(node);
|
||||
const metadata = this.lookupDirectiveMetadata(ref);
|
||||
// Only directives/components with selectors get added to the scope.
|
||||
if (selector != null) {
|
||||
directives.set(selector, ref);
|
||||
if (metadata != null) {
|
||||
directives.set(metadata.selector, {...metadata, directive: ref});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -180,7 +182,16 @@ export class SelectorScopeRegistry {
|
|||
this._compilationScopeCache.set(node, scope);
|
||||
|
||||
// Convert References to Expressions in the context of the component's source file.
|
||||
return convertScopeToExpressions(scope, node);
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce the compilation scope of a component, which is determined by the module that declares
|
||||
* it.
|
||||
*/
|
||||
lookupCompilationScope(node: ts.Declaration): CompilationScope<Expression>|null {
|
||||
const scope = this.lookupCompilationScopeAsRefs(node);
|
||||
return scope !== null ? convertScopeToExpressions(scope, node) : null;
|
||||
}
|
||||
|
||||
private lookupScopesOrDie(node: ts.Declaration, ngModuleImportedFrom: string|null):
|
||||
|
@ -210,7 +221,7 @@ 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.
|
||||
data = this._readMetadataFromCompiledClass(node, ngModuleImportedFrom);
|
||||
data = this._readModuleDataFromCompiledClass(node, ngModuleImportedFrom);
|
||||
// Note that data here could still be null, if the class didn't have a precompiled
|
||||
// ngModuleDef.
|
||||
}
|
||||
|
@ -245,17 +256,18 @@ export class SelectorScopeRegistry {
|
|||
}
|
||||
|
||||
/**
|
||||
* Lookup the selector of a component or directive class.
|
||||
* Lookup the metadata of a component or directive class.
|
||||
*
|
||||
* Potentially this class is declared in a .d.ts file or otherwise has a manually created
|
||||
* ngComponentDef/ngDirectiveDef. In this case, the type metadata of that definition is read
|
||||
* to determine the selector.
|
||||
* to determine the metadata.
|
||||
*/
|
||||
private lookupDirectiveSelector(node: ts.Declaration): string|null {
|
||||
if (this._directiveToSelector.has(node)) {
|
||||
return this._directiveToSelector.get(node) !;
|
||||
private lookupDirectiveMetadata(ref: Reference<ts.Declaration>): ScopeDirective<Reference>|null {
|
||||
const node = ts.getOriginalNode(ref.node) as ts.Declaration;
|
||||
if (this._directiveToMetadata.has(node)) {
|
||||
return this._directiveToMetadata.get(node) !;
|
||||
} else {
|
||||
return this._readSelectorFromCompiledClass(node);
|
||||
return this._readMetadataFromCompiledClass(ref as Reference<ts.ClassDeclaration>);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,8 +287,8 @@ 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|null):
|
||||
ModuleData|null {
|
||||
private _readModuleDataFromCompiledClass(
|
||||
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.
|
||||
const ngModuleDef = this.reflector.getMembersOfClass(clazz).find(
|
||||
|
@ -304,7 +316,9 @@ export class SelectorScopeRegistry {
|
|||
* Get the selector from type metadata for a class with a precompiled ngComponentDef or
|
||||
* ngDirectiveDef.
|
||||
*/
|
||||
private _readSelectorFromCompiledClass(clazz: ts.Declaration): string|null {
|
||||
private _readMetadataFromCompiledClass(ref: Reference<ts.ClassDeclaration>):
|
||||
ScopeDirective<Reference>|null {
|
||||
const clazz = ts.getOriginalNode(ref.node) as ts.ClassDeclaration;
|
||||
const def = this.reflector.getMembersOfClass(clazz).find(
|
||||
field =>
|
||||
field.isStatic && (field.name === 'ngComponentDef' || field.name === 'ngDirectiveDef'));
|
||||
|
@ -317,12 +331,22 @@ export class SelectorScopeRegistry {
|
|||
// The type metadata was the wrong shape.
|
||||
return null;
|
||||
}
|
||||
const type = def.type.typeArguments[1];
|
||||
if (!ts.isLiteralTypeNode(type) || !ts.isStringLiteral(type.literal)) {
|
||||
// The type metadata was the wrong type.
|
||||
const selector = readStringType(def.type.typeArguments[1]);
|
||||
if (selector === null) {
|
||||
return null;
|
||||
}
|
||||
return type.literal.text;
|
||||
|
||||
return {
|
||||
ref,
|
||||
name: clazz.name !.text,
|
||||
directive: ref,
|
||||
isComponent: def.name === 'ngComponentDef', selector,
|
||||
exportAs: readStringType(def.type.typeArguments[2]),
|
||||
inputs: readStringMapType(def.type.typeArguments[3]),
|
||||
outputs: readStringMapType(def.type.typeArguments[4]),
|
||||
queries: readStringArrayType(def.type.typeArguments[5]),
|
||||
...extractDirectiveGuards(clazz, this.reflector),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -357,7 +381,7 @@ export class SelectorScopeRegistry {
|
|||
* they themselves were imported from another absolute path.
|
||||
*/
|
||||
private _extractReferencesFromType(def: ts.TypeNode, ngModuleImportedFrom: string|null):
|
||||
Reference[] {
|
||||
Reference<ts.Declaration>[] {
|
||||
if (!ts.isTupleTypeNode(def)) {
|
||||
return [];
|
||||
}
|
||||
|
@ -394,23 +418,33 @@ function absoluteModuleName(ref: Reference): string|null {
|
|||
return ref.moduleName;
|
||||
}
|
||||
|
||||
function convertReferenceMap(
|
||||
function convertDirectiveReferenceMap(
|
||||
map: Map<string, ScopeDirective<Reference>>,
|
||||
context: ts.SourceFile): Map<string, ScopeDirective<Expression>> {
|
||||
const newMap = new Map<string, ScopeDirective<Expression>>();
|
||||
map.forEach((meta, selector) => {
|
||||
newMap.set(selector, {...meta, directive: toR3Reference(meta.directive, context).value});
|
||||
});
|
||||
return newMap;
|
||||
}
|
||||
|
||||
function convertPipeReferenceMap(
|
||||
map: Map<string, Reference>, context: ts.SourceFile): Map<string, Expression> {
|
||||
return new Map<string, Expression>(Array.from(map.entries()).map(([selector, ref]): [
|
||||
string, Expression
|
||||
] => [selector, toR3Reference(ref, context).value]));
|
||||
const newMap = new Map<string, Expression>();
|
||||
map.forEach((meta, selector) => { newMap.set(selector, toR3Reference(meta, context).value); });
|
||||
return newMap;
|
||||
}
|
||||
|
||||
function convertScopeToExpressions(
|
||||
scope: CompilationScope<Reference>, context: ts.Declaration): CompilationScope<Expression> {
|
||||
const sourceContext = ts.getOriginalNode(context).getSourceFile();
|
||||
const directives = convertReferenceMap(scope.directives, sourceContext);
|
||||
const pipes = convertReferenceMap(scope.pipes, sourceContext);
|
||||
const directives = convertDirectiveReferenceMap(scope.directives, sourceContext);
|
||||
const pipes = convertPipeReferenceMap(scope.pipes, sourceContext);
|
||||
const declPointer = maybeUnwrapNameOfDeclaration(context);
|
||||
let containsForwardDecls = false;
|
||||
directives.forEach(expr => {
|
||||
containsForwardDecls =
|
||||
containsForwardDecls || isExpressionForwardReference(expr, declPointer, sourceContext);
|
||||
containsForwardDecls = containsForwardDecls ||
|
||||
isExpressionForwardReference(expr.directive, declPointer, sourceContext);
|
||||
});
|
||||
!containsForwardDecls && pipes.forEach(expr => {
|
||||
containsForwardDecls =
|
||||
|
@ -438,4 +472,44 @@ function maybeUnwrapNameOfDeclaration(decl: ts.Declaration): ts.Declaration|ts.I
|
|||
return decl.name;
|
||||
}
|
||||
return decl;
|
||||
}
|
||||
}
|
||||
|
||||
function readStringType(type: ts.TypeNode): string|null {
|
||||
if (!ts.isLiteralTypeNode(type) || !ts.isStringLiteral(type.literal)) {
|
||||
return null;
|
||||
}
|
||||
return type.literal.text;
|
||||
}
|
||||
|
||||
function readStringMapType(type: ts.TypeNode): {[key: string]: string} {
|
||||
if (!ts.isTypeLiteralNode(type)) {
|
||||
return {};
|
||||
}
|
||||
const obj: {[key: string]: string} = {};
|
||||
type.members.forEach(member => {
|
||||
if (!ts.isPropertySignature(member) || member.type === undefined || member.name === undefined ||
|
||||
!ts.isStringLiteral(member.name)) {
|
||||
return;
|
||||
}
|
||||
const value = readStringType(member.type);
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
obj[member.name.text] = value;
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
function readStringArrayType(type: ts.TypeNode): string[] {
|
||||
if (!ts.isTupleTypeNode(type)) {
|
||||
return [];
|
||||
}
|
||||
const res: string[] = [];
|
||||
type.elementTypes.forEach(el => {
|
||||
if (!ts.isLiteralTypeNode(el) || !ts.isStringLiteral(el.literal)) {
|
||||
return;
|
||||
}
|
||||
res.push(el.literal.text);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,8 @@ describe('SelectorScopeRegistry', () => {
|
|||
expect(ProgramModule).toBeDefined();
|
||||
expect(SomeModule).toBeDefined();
|
||||
|
||||
const ProgramCmpRef = new ResolvedReference(ProgramCmp, ProgramCmp.name !);
|
||||
|
||||
const registry = new SelectorScopeRegistry(checker, host);
|
||||
|
||||
registry.registerModule(ProgramModule, {
|
||||
|
@ -71,7 +73,20 @@ describe('SelectorScopeRegistry', () => {
|
|||
imports: [new AbsoluteReference(SomeModule, SomeModule.name !, 'some_library', 'SomeModule')],
|
||||
});
|
||||
|
||||
registry.registerSelector(ProgramCmp, 'program-cmp');
|
||||
const ref = new ResolvedReference(ProgramCmp, ProgramCmp.name !);
|
||||
registry.registerDirective(ProgramCmp, {
|
||||
name: 'ProgramCmp',
|
||||
ref: ProgramCmpRef,
|
||||
directive: ProgramCmpRef,
|
||||
selector: 'program-cmp',
|
||||
isComponent: true,
|
||||
exportAs: null,
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
queries: [],
|
||||
hasNgTemplateContextGuard: false,
|
||||
ngTemplateGuards: [],
|
||||
});
|
||||
|
||||
const scope = registry.lookupCompilationScope(ProgramCmp) !;
|
||||
expect(scope).toBeDefined();
|
||||
|
@ -120,6 +135,8 @@ describe('SelectorScopeRegistry', () => {
|
|||
expect(ProgramModule).toBeDefined();
|
||||
expect(SomeModule).toBeDefined();
|
||||
|
||||
const ProgramCmpRef = new ResolvedReference(ProgramCmp, ProgramCmp.name !);
|
||||
|
||||
const registry = new SelectorScopeRegistry(checker, host);
|
||||
|
||||
registry.registerModule(ProgramModule, {
|
||||
|
@ -128,7 +145,19 @@ describe('SelectorScopeRegistry', () => {
|
|||
imports: [],
|
||||
});
|
||||
|
||||
registry.registerSelector(ProgramCmp, 'program-cmp');
|
||||
registry.registerDirective(ProgramCmp, {
|
||||
name: 'ProgramCmp',
|
||||
ref: ProgramCmpRef,
|
||||
directive: ProgramCmpRef,
|
||||
selector: 'program-cmp',
|
||||
isComponent: true,
|
||||
exportAs: null,
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
queries: [],
|
||||
hasNgTemplateContextGuard: false,
|
||||
ngTemplateGuards: [],
|
||||
});
|
||||
|
||||
const scope = registry.lookupCompilationScope(ProgramCmp) !;
|
||||
expect(scope).toBeDefined();
|
||||
|
|
|
@ -183,10 +183,10 @@ export class ResolvedReference<T extends ts.Node = ts.Node> extends Reference<T>
|
|||
* An `AbsoluteReference` can be resolved to an `Expression`, and if that expression is an import
|
||||
* the module specifier will be an absolute module name, not a relative path.
|
||||
*/
|
||||
export class AbsoluteReference extends Reference {
|
||||
export class AbsoluteReference<T extends ts.Node> extends Reference<T> {
|
||||
private identifiers: ts.Identifier[] = [];
|
||||
constructor(
|
||||
node: ts.Node, private primaryIdentifier: ts.Identifier, readonly moduleName: string,
|
||||
node: T, private primaryIdentifier: ts.Identifier, readonly moduleName: string,
|
||||
readonly symbolName: string) {
|
||||
super(node);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue