refactor(compiler-cli): make `TypeCheckingScopeRegistry` a general utility (#40032)

The `annotations` package in the compiler previously contained a registry
which tracks NgModule scopes for template type-checking, including unifying
all type-checking metadata across class inheritance lines.

This commit generalizes this utility and prepares it for use in the
`TemplateTypeChecker` as well, to back APIs used by the language service.

PR Close #40032
This commit is contained in:
Alex Rickabaugh 2020-12-03 10:52:42 -08:00
parent e42250f139
commit a543e69497
6 changed files with 69 additions and 35 deletions

View File

@ -16,7 +16,7 @@ import {absoluteFrom, absoluteFromSourceFile, dirname, FileSystem, LogicalFileSy
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports';
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../../src/ngtsc/metadata'; import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../../src/ngtsc/metadata';
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver, TypeCheckScopeRegistry} from '../../../src/ngtsc/scope';
import {DecoratorHandler} from '../../../src/ngtsc/transform'; import {DecoratorHandler} from '../../../src/ngtsc/transform';
import {NgccReflectionHost} from '../host/ngcc_host'; import {NgccReflectionHost} from '../host/ngcc_host';
import {Migration} from '../migrations/migration'; import {Migration} from '../migrations/migration';
@ -91,11 +91,13 @@ export class DecorationAnalyzer {
importGraph = new ImportGraph(this.moduleResolver); importGraph = new ImportGraph(this.moduleResolver);
cycleAnalyzer = new CycleAnalyzer(this.importGraph); cycleAnalyzer = new CycleAnalyzer(this.importGraph);
injectableRegistry = new InjectableClassRegistry(this.reflectionHost); injectableRegistry = new InjectableClassRegistry(this.reflectionHost);
typeCheckScopeRegistry = new TypeCheckScopeRegistry(this.scopeRegistry, this.fullMetaReader);
handlers: DecoratorHandler<unknown, unknown, unknown>[] = [ handlers: DecoratorHandler<unknown, unknown, unknown>[] = [
new ComponentDecoratorHandler( new ComponentDecoratorHandler(
this.reflectionHost, this.evaluator, this.fullRegistry, this.fullMetaReader, this.reflectionHost, this.evaluator, this.fullRegistry, this.fullMetaReader,
this.scopeRegistry, this.scopeRegistry, new ResourceRegistry(), this.isCore, this.scopeRegistry, this.scopeRegistry, this.typeCheckScopeRegistry, new ResourceRegistry(),
this.resourceManager, this.rootDirs, !!this.compilerOptions.preserveWhitespaces, this.isCore, this.resourceManager, this.rootDirs,
!!this.compilerOptions.preserveWhitespaces,
/* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat, /* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat,
/* usePoisonedData */ false, /* usePoisonedData */ false,
/* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer, /* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer,

View File

@ -18,7 +18,7 @@ import {IndexingContext} from '../../indexer';
import {ClassPropertyMapping, ComponentResources, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, Resource, ResourceRegistry} from '../../metadata'; import {ClassPropertyMapping, ComponentResources, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, Resource, ResourceRegistry} from '../../metadata';
import {EnumValue, PartialEvaluator} from '../../partial_evaluator'; import {EnumValue, PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
import {ComponentScopeReader, LocalModuleScopeRegistry} from '../../scope'; import {ComponentScopeReader, LocalModuleScopeRegistry, TypeCheckScopeRegistry} from '../../scope';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform';
import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck/api'; import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck/api';
import {getTemplateId, makeTemplateDiagnostic} from '../../typecheck/diagnostics'; import {getTemplateId, makeTemplateDiagnostic} from '../../typecheck/diagnostics';
@ -30,7 +30,6 @@ import {createValueHasWrongTypeError, getDirectiveDiagnostics, getProviderDiagno
import {extractDirectiveMetadata, parseFieldArrayValue} from './directive'; import {extractDirectiveMetadata, parseFieldArrayValue} from './directive';
import {compileNgFactoryDefField} from './factory'; import {compileNgFactoryDefField} from './factory';
import {generateSetClassMetadataCall} from './metadata'; import {generateSetClassMetadataCall} from './metadata';
import {TypeCheckScopes} from './typecheck_scopes';
import {findAngularDecorator, isAngularCoreReference, isExpressionForwardReference, readBaseClass, resolveProvidersRequiringFactory, unwrapExpression, wrapFunctionExpressionsInParens} from './util'; import {findAngularDecorator, isAngularCoreReference, isExpressionForwardReference, readBaseClass, resolveProvidersRequiringFactory, unwrapExpression, wrapFunctionExpressionsInParens} from './util';
const EMPTY_MAP = new Map<string, Expression>(); const EMPTY_MAP = new Map<string, Expression>();
@ -87,6 +86,7 @@ export class ComponentDecoratorHandler implements
private reflector: ReflectionHost, private evaluator: PartialEvaluator, private reflector: ReflectionHost, private evaluator: PartialEvaluator,
private metaRegistry: MetadataRegistry, private metaReader: MetadataReader, private metaRegistry: MetadataRegistry, private metaReader: MetadataReader,
private scopeReader: ComponentScopeReader, private scopeRegistry: LocalModuleScopeRegistry, private scopeReader: ComponentScopeReader, private scopeRegistry: LocalModuleScopeRegistry,
private typeCheckScopeRegistry: TypeCheckScopeRegistry,
private resourceRegistry: ResourceRegistry, private isCore: boolean, private resourceRegistry: ResourceRegistry, private isCore: boolean,
private resourceLoader: ResourceLoader, private rootDirs: ReadonlyArray<string>, private resourceLoader: ResourceLoader, private rootDirs: ReadonlyArray<string>,
private defaultPreserveWhitespaces: boolean, private i18nUseExternalIds: boolean, private defaultPreserveWhitespaces: boolean, private i18nUseExternalIds: boolean,
@ -100,7 +100,6 @@ export class ComponentDecoratorHandler implements
private literalCache = new Map<Decorator, ts.ObjectLiteralExpression>(); private literalCache = new Map<Decorator, ts.ObjectLiteralExpression>();
private elementSchemaRegistry = new DomElementSchemaRegistry(); private elementSchemaRegistry = new DomElementSchemaRegistry();
private typeCheckScopes = new TypeCheckScopes(this.scopeReader, this.metaReader);
/** /**
* During the asynchronous preanalyze phase, it's necessary to parse the template to extract * During the asynchronous preanalyze phase, it's necessary to parse the template to extract
@ -440,15 +439,14 @@ export class ComponentDecoratorHandler implements
typeCheck(ctx: TypeCheckContext, node: ClassDeclaration, meta: Readonly<ComponentAnalysisData>): typeCheck(ctx: TypeCheckContext, node: ClassDeclaration, meta: Readonly<ComponentAnalysisData>):
void { void {
if (!ts.isClassDeclaration(node)) { if (this.typeCheckScopeRegistry === null || !ts.isClassDeclaration(node)) {
return; return;
} }
if (meta.isPoisoned && !this.usePoisonedData) { if (meta.isPoisoned && !this.usePoisonedData) {
return; return;
} }
const scope = this.typeCheckScopeRegistry.getTypeCheckScope(node);
const scope = this.typeCheckScopes.getTypeCheckScope(node);
if (scope.isPoisoned && !this.usePoisonedData) { if (scope.isPoisoned && !this.usePoisonedData) {
// Don't type-check components that had errors in their scopes, unless requested. // Don't type-check components that had errors in their scopes, unless requested.
return; return;
@ -492,17 +490,17 @@ export class ComponentDecoratorHandler implements
// Determining this is challenging, because the TemplateDefinitionBuilder is responsible for // Determining this is challenging, because the TemplateDefinitionBuilder is responsible for
// matching directives and pipes in the template; however, that doesn't run until the actual // matching directives and pipes in the template; however, that doesn't run until the actual
// compile() step. It's not possible to run template compilation sooner as it requires the // compile() step. It's not possible to run template compilation sooner as it requires the
// ConstantPool for the overall file being compiled (which isn't available until the transform // ConstantPool for the overall file being compiled (which isn't available until the
// step). // transform step).
// //
// Instead, directives/pipes are matched independently here, using the R3TargetBinder. This is // Instead, directives/pipes are matched independently here, using the R3TargetBinder. This
// an alternative implementation of template matching which is used for template type-checking // is an alternative implementation of template matching which is used for template
// and will eventually replace matching in the TemplateDefinitionBuilder. // type-checking and will eventually replace matching in the TemplateDefinitionBuilder.
// Set up the R3TargetBinder, as well as a 'directives' array and a 'pipes' map that are later // Set up the R3TargetBinder, as well as a 'directives' array and a 'pipes' map that are
// fed to the TemplateDefinitionBuilder. First, a SelectorMatcher is constructed to match // later fed to the TemplateDefinitionBuilder. First, a SelectorMatcher is constructed to
// directives that are in scope. // match directives that are in scope.
type MatchedDirective = DirectiveMeta&{selector: string}; type MatchedDirective = DirectiveMeta&{selector: string};
const matcher = new SelectorMatcher<MatchedDirective>(); const matcher = new SelectorMatcher<MatchedDirective>();
@ -563,8 +561,8 @@ export class ComponentDecoratorHandler implements
} }
// Check whether the directive/pipe arrays in ɵcmp need to be wrapped in closures. // Check whether the directive/pipe arrays in ɵcmp need to be wrapped in closures.
// This is required if any directive/pipe reference is to a declaration in the same file but // This is required if any directive/pipe reference is to a declaration in the same file
// declared after this component. // but declared after this component.
const wrapDirectivesAndPipesInClosure = const wrapDirectivesAndPipesInClosure =
usedDirectives.some( usedDirectives.some(
dir => isExpressionForwardReference(dir.type, node.name, context)) || dir => isExpressionForwardReference(dir.type, node.name, context)) ||
@ -890,8 +888,8 @@ export class ComponentDecoratorHandler implements
// 2. By default, the template parser strips leading trivia characters (like spaces, tabs, and // 2. By default, the template parser strips leading trivia characters (like spaces, tabs, and
// newlines). This also destroys source mapping information. // newlines). This also destroys source mapping information.
// //
// In order to guarantee the correctness of diagnostics, templates are parsed a second time with // In order to guarantee the correctness of diagnostics, templates are parsed a second time
// the above options set to preserve source mappings. // with the above options set to preserve source mappings.
const {nodes: diagNodes} = parseTemplate(templateStr, templateUrl, { const {nodes: diagNodes} = parseTemplate(templateStr, templateUrl, {
preserveWhitespaces: true, preserveWhitespaces: true,

View File

@ -5,6 +5,9 @@
* Use of this source code is governed by an MIT-style license that can be * Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as ts from 'typescript';
import {CycleAnalyzer, ImportGraph} from '../../cycles'; import {CycleAnalyzer, ImportGraph} from '../../cycles';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {absoluteFrom} from '../../file_system'; import {absoluteFrom} from '../../file_system';
@ -13,7 +16,7 @@ import {ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '..
import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../metadata'; import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../metadata';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver, TypeCheckScopeRegistry} from '../../scope';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';
import {ResourceLoader} from '../src/api'; import {ResourceLoader} from '../src/api';
import {ComponentDecoratorHandler} from '../src/component'; import {ComponentDecoratorHandler} from '../src/component';
@ -48,17 +51,33 @@ function setup(program: ts.Program, options: ts.CompilerOptions, host: ts.Compil
const refEmitter = new ReferenceEmitter([]); const refEmitter = new ReferenceEmitter([]);
const injectableRegistry = new InjectableClassRegistry(reflectionHost); const injectableRegistry = new InjectableClassRegistry(reflectionHost);
const resourceRegistry = new ResourceRegistry(); const resourceRegistry = new ResourceRegistry();
const typeCheckScopeRegistry = new TypeCheckScopeRegistry(scopeRegistry, metaReader);
const handler = new ComponentDecoratorHandler( const handler = new ComponentDecoratorHandler(
reflectionHost, evaluator, metaRegistry, metaReader, scopeRegistry, scopeRegistry, reflectionHost,
evaluator,
metaRegistry,
metaReader,
scopeRegistry,
scopeRegistry,
typeCheckScopeRegistry,
resourceRegistry, resourceRegistry,
/* isCore */ false, new StubResourceLoader(), /* rootDirs */['/'], /* isCore */ false,
/* defaultPreserveWhitespaces */ false, /* i18nUseExternalIds */ true, new StubResourceLoader(),
/* rootDirs */['/'],
/* defaultPreserveWhitespaces */ false,
/* i18nUseExternalIds */ true,
/* enableI18nLegacyMessageIdFormat */ false, /* enableI18nLegacyMessageIdFormat */ false,
/* usePoisonedData */ false, /* usePoisonedData */ false,
/* i18nNormalizeLineEndingsInICUs */ undefined, moduleResolver, cycleAnalyzer, refEmitter, /* i18nNormalizeLineEndingsInICUs */ undefined,
NOOP_DEFAULT_IMPORT_RECORDER, /* depTracker */ null, injectableRegistry, moduleResolver,
/* annotateForClosureCompiler */ false); cycleAnalyzer,
refEmitter,
NOOP_DEFAULT_IMPORT_RECORDER,
/* depTracker */ null,
injectableRegistry,
/* annotateForClosureCompiler */ false,
);
return {reflectionHost, handler}; return {reflectionHost, handler};
} }

View File

@ -24,7 +24,7 @@ import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf';
import {DeclarationNode, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {DeclarationNode, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
import {AdapterResourceLoader} from '../../resource'; import {AdapterResourceLoader} from '../../resource';
import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing'; import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing';
import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver, TypeCheckScopeRegistry} from '../../scope';
import {generatedFactoryTransform} from '../../shims'; import {generatedFactoryTransform} from '../../shims';
import {ivySwitchTransform} from '../../switch'; import {ivySwitchTransform} from '../../switch';
import {aliasTransformFactory, CompilationMode, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform'; import {aliasTransformFactory, CompilationMode, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform';
@ -44,6 +44,7 @@ interface LazyCompilationState {
reflector: TypeScriptReflectionHost; reflector: TypeScriptReflectionHost;
metaReader: MetadataReader; metaReader: MetadataReader;
scopeRegistry: LocalModuleScopeRegistry; scopeRegistry: LocalModuleScopeRegistry;
typeCheckScopeRegistry: TypeCheckScopeRegistry;
exportReferenceGraph: ReferenceGraph|null; exportReferenceGraph: ReferenceGraph|null;
routeAnalyzer: NgModuleRouteAnalyzer; routeAnalyzer: NgModuleRouteAnalyzer;
dtsTransforms: DtsTransformRegistry; dtsTransforms: DtsTransformRegistry;
@ -732,6 +733,7 @@ export class NgCompiler {
const injectableRegistry = new InjectableClassRegistry(reflector); const injectableRegistry = new InjectableClassRegistry(reflector);
const metaReader = new CompoundMetadataReader([localMetaReader, dtsReader]); const metaReader = new CompoundMetadataReader([localMetaReader, dtsReader]);
const typeCheckScopeRegistry = new TypeCheckScopeRegistry(scopeReader, metaReader);
// If a flat module entrypoint was specified, then track references via a `ReferenceGraph` in // If a flat module entrypoint was specified, then track references via a `ReferenceGraph` in
@ -761,8 +763,9 @@ export class NgCompiler {
const handlers: DecoratorHandler<unknown, unknown, unknown>[] = [ const handlers: DecoratorHandler<unknown, unknown, unknown>[] = [
new ComponentDecoratorHandler( new ComponentDecoratorHandler(
reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry,
resourceRegistry, isCore, this.resourceManager, this.adapter.rootDirs, typeCheckScopeRegistry, resourceRegistry, isCore, this.resourceManager,
this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.adapter.rootDirs, this.options.preserveWhitespaces || false,
this.options.i18nUseExternalIds !== false,
this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData,
this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer,
refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry,
@ -814,6 +817,7 @@ export class NgCompiler {
routeAnalyzer, routeAnalyzer,
mwpScanner, mwpScanner,
metaReader, metaReader,
typeCheckScopeRegistry,
defaultImportTracker, defaultImportTracker,
aliasingHost, aliasingHost,
refEmitter, refEmitter,

View File

@ -10,3 +10,4 @@ export {ExportScope, ScopeData} from './src/api';
export {ComponentScopeReader, CompoundComponentScopeReader} from './src/component_scope'; export {ComponentScopeReader, CompoundComponentScopeReader} from './src/component_scope';
export {DtsModuleScopeResolver, MetadataDtsModuleScopeResolver} from './src/dependency'; export {DtsModuleScopeResolver, MetadataDtsModuleScopeResolver} from './src/dependency';
export {DeclarationData, LocalModuleScope, LocalModuleScopeRegistry, LocalNgModuleData} from './src/local'; export {DeclarationData, LocalModuleScope, LocalModuleScopeRegistry, LocalNgModuleData} from './src/local';
export {TypeCheckScope, TypeCheckScopeRegistry} from './src/typecheck';

View File

@ -12,7 +12,8 @@ import * as ts from 'typescript';
import {Reference} from '../../imports'; import {Reference} from '../../imports';
import {DirectiveMeta, flattenInheritedDirectiveMetadata, MetadataReader} from '../../metadata'; import {DirectiveMeta, flattenInheritedDirectiveMetadata, MetadataReader} from '../../metadata';
import {ClassDeclaration} from '../../reflection'; import {ClassDeclaration} from '../../reflection';
import {ComponentScopeReader} from '../../scope';
import {ComponentScopeReader} from './component_scope';
/** /**
* The scope that is used for type-check code generation of a component template. * The scope that is used for type-check code generation of a component template.
@ -24,6 +25,11 @@ export interface TypeCheckScope {
*/ */
matcher: SelectorMatcher<DirectiveMeta>; matcher: SelectorMatcher<DirectiveMeta>;
/**
* All of the directives available in the compilation scope of the declaring NgModule.
*/
directives: DirectiveMeta[];
/** /**
* The pipes that are available in the compilation scope. * The pipes that are available in the compilation scope.
*/ */
@ -44,7 +50,7 @@ export interface TypeCheckScope {
/** /**
* Computes scope information to be used in template type checking. * Computes scope information to be used in template type checking.
*/ */
export class TypeCheckScopes { export class TypeCheckScopeRegistry {
/** /**
* Cache of flattened directive metadata. Because flattened metadata is scope-invariant it's * Cache of flattened directive metadata. Because flattened metadata is scope-invariant it's
* cached individually, such that all scopes refer to the same flattened metadata. * cached individually, such that all scopes refer to the same flattened metadata.
@ -65,12 +71,14 @@ export class TypeCheckScopes {
*/ */
getTypeCheckScope(node: ClassDeclaration): TypeCheckScope { getTypeCheckScope(node: ClassDeclaration): TypeCheckScope {
const matcher = new SelectorMatcher<DirectiveMeta>(); const matcher = new SelectorMatcher<DirectiveMeta>();
const directives: DirectiveMeta[] = [];
const pipes = new Map<string, Reference<ClassDeclaration<ts.ClassDeclaration>>>(); const pipes = new Map<string, Reference<ClassDeclaration<ts.ClassDeclaration>>>();
const scope = this.scopeReader.getScopeForComponent(node); const scope = this.scopeReader.getScopeForComponent(node);
if (scope === null) { if (scope === null) {
return { return {
matcher, matcher,
directives,
pipes, pipes,
schemas: [], schemas: [],
isPoisoned: false, isPoisoned: false,
@ -83,8 +91,9 @@ export class TypeCheckScopes {
for (const meta of scope.compilation.directives) { for (const meta of scope.compilation.directives) {
if (meta.selector !== null) { if (meta.selector !== null) {
const extMeta = this.getInheritedDirectiveMetadata(meta.ref); const extMeta = this.getTypeCheckDirectiveMetadata(meta.ref);
matcher.addSelectables(CssSelector.parse(meta.selector), extMeta); matcher.addSelectables(CssSelector.parse(meta.selector), extMeta);
directives.push(extMeta);
} }
} }
@ -98,6 +107,7 @@ export class TypeCheckScopes {
const typeCheckScope: TypeCheckScope = { const typeCheckScope: TypeCheckScope = {
matcher, matcher,
directives,
pipes, pipes,
schemas: scope.schemas, schemas: scope.schemas,
isPoisoned: scope.compilation.isPoisoned || scope.exported.isPoisoned, isPoisoned: scope.compilation.isPoisoned || scope.exported.isPoisoned,
@ -106,7 +116,7 @@ export class TypeCheckScopes {
return typeCheckScope; return typeCheckScope;
} }
private getInheritedDirectiveMetadata(ref: Reference<ClassDeclaration>): DirectiveMeta { getTypeCheckDirectiveMetadata(ref: Reference<ClassDeclaration>): DirectiveMeta {
const clazz = ref.node; const clazz = ref.node;
if (this.flattenedDirectiveMetaCache.has(clazz)) { if (this.flattenedDirectiveMetaCache.has(clazz)) {
return this.flattenedDirectiveMetaCache.get(clazz)!; return this.flattenedDirectiveMetaCache.get(clazz)!;