diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/host_directives_resolver.ts b/packages/compiler-cli/src/ngtsc/metadata/src/host_directives_resolver.ts index 16a8929373a53..eebe3a2763c93 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/host_directives_resolver.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/host_directives_resolver.ts @@ -43,12 +43,9 @@ export class HostDirectivesResolver { for (const current of directives) { const hostMeta = flattenInheritedDirectiveMetadata(this.metaReader, current.directive); - // This case has been checked for already, but we keep the assertion here so that the - // user gets a better error message than "Cannot read property foo of null" in case - // something slipped through. + // This case has been checked for already and produces a diagnostic if (hostMeta === null) { - throw new Error( - `Could not resolve host directive metadata of ${current.directive.debugName}`); + continue; } if (hostMeta.hostDirectives) { diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts index 097e1ef422515..9ed774f1d7ff7 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts @@ -21,10 +21,10 @@ import {ClassPropertyMapping, ClassPropertyName} from './property_mapping'; * followed. */ export function flattenInheritedDirectiveMetadata( - reader: MetadataReader, dir: Reference): DirectiveMeta { + reader: MetadataReader, dir: Reference): DirectiveMeta|null { const topMeta = reader.getDirectiveMetadata(dir); if (topMeta === null) { - throw new Error(`Metadata not found for directive: ${dir.debugName}`); + return null; } if (topMeta.baseClass === null) { return topMeta; diff --git a/packages/compiler-cli/src/ngtsc/scope/src/typecheck.ts b/packages/compiler-cli/src/ngtsc/scope/src/typecheck.ts index 5cab1f9beb353..ace986ea88da5 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/typecheck.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/typecheck.ts @@ -99,6 +99,9 @@ export class TypeCheckScopeRegistry { for (const meta of dependencies) { if (meta.kind === MetaKind.Directive && meta.selector !== null) { const extMeta = this.getTypeCheckDirectiveMetadata(meta.ref); + if (extMeta === null) { + continue; + } matcher.addSelectables( CssSelector.parse(meta.selector), [...this.hostDirectivesResolver.resolve(extMeta), extMeta]); @@ -125,13 +128,16 @@ export class TypeCheckScopeRegistry { return typeCheckScope; } - getTypeCheckDirectiveMetadata(ref: Reference): DirectiveMeta { + getTypeCheckDirectiveMetadata(ref: Reference): DirectiveMeta|null { const clazz = ref.node; if (this.flattenedDirectiveMetaCache.has(clazz)) { return this.flattenedDirectiveMetaCache.get(clazz)!; } const meta = flattenInheritedDirectiveMetadata(this.metaReader, ref); + if (meta === null) { + return null; + } this.flattenedDirectiveMetaCache.set(clazz, meta); return meta; } diff --git a/packages/compiler-cli/test/ngtsc/host_directives_spec.ts b/packages/compiler-cli/test/ngtsc/host_directives_spec.ts index cbade7b9e2aa6..95beaf66dbf64 100644 --- a/packages/compiler-cli/test/ngtsc/host_directives_spec.ts +++ b/packages/compiler-cli/test/ngtsc/host_directives_spec.ts @@ -451,6 +451,24 @@ runInEachFileSystem(() => { expect(messages).toEqual(['Host directive HostDir must be standalone']); }); + it('should produce a diagnostic if a host directive is not a directive', () => { + env.write('test.ts', ` + import {Directive, Pipe, Component, NgModule} from '@angular/core'; + + @Pipe({name: 'hostDir'}) + export class HostDir {} + + @Directive({ + hostDirectives: [HostDir], + }) + export class Dir {} + `); + + const messages = env.driveDiagnostics().map(extractMessage); + expect(messages).toEqual( + ['HostDir must be a standalone directive to be used as a host directive']); + }); + it('should produce a diagnostic if a host directive is a component', () => { env.write('test.ts', ` import {Directive, Component, NgModule} from '@angular/core';