diff --git a/packages/compiler/src/jit_compiler_facade.ts b/packages/compiler/src/jit_compiler_facade.ts index 4c8c010d349..18cbe10aa06 100644 --- a/packages/compiler/src/jit_compiler_facade.ts +++ b/packages/compiler/src/jit_compiler_facade.ts @@ -497,6 +497,26 @@ function convertDirectiveFacadeToMetadata(facade: R3DirectiveMetadataFacade): R3 } } + const hostDirectives = facade.hostDirectives?.length + ? facade.hostDirectives.map((hostDirective) => { + return typeof hostDirective === 'function' + ? { + directive: wrapReference(hostDirective), + inputs: null, + outputs: null, + isForwardReference: false, + } + : { + directive: wrapReference(hostDirective.directive), + isForwardReference: false, + inputs: hostDirective.inputs ? parseMappingStringArray(hostDirective.inputs) : null, + outputs: hostDirective.outputs + ? parseMappingStringArray(hostDirective.outputs) + : null, + }; + }) + : null; + return { ...facade, typeArgumentCount: 0, @@ -512,7 +532,7 @@ function convertDirectiveFacadeToMetadata(facade: R3DirectiveMetadataFacade): R3 providers: facade.providers != null ? new WrappedNodeExpr(facade.providers) : null, viewQueries: facade.viewQueries.map(convertToR3QueryMetadata), fullInheritance: false, - hostDirectives: convertHostDirectivesToMetadata(facade), + hostDirectives, }; } @@ -520,6 +540,15 @@ function convertDeclareDirectiveFacadeToMetadata( declaration: R3DeclareDirectiveFacade, typeSourceSpan: ParseSourceSpan, ): R3DirectiveMetadata { + const hostDirectives = declaration.hostDirectives?.length + ? declaration.hostDirectives.map((dir) => ({ + directive: wrapReference(dir.directive), + isForwardReference: false, + inputs: dir.inputs ? getHostDirectiveBindingMapping(dir.inputs) : null, + outputs: dir.outputs ? getHostDirectiveBindingMapping(dir.outputs) : null, + })) + : null; + return { name: declaration.type.name, type: wrapReference(declaration.type), @@ -540,7 +569,7 @@ function convertDeclareDirectiveFacadeToMetadata( fullInheritance: false, isStandalone: declaration.isStandalone ?? false, isSignal: declaration.isSignal ?? false, - hostDirectives: convertHostDirectivesToMetadata(declaration), + hostDirectives, }; } @@ -558,28 +587,19 @@ function convertHostDeclarationToMetadata( }; } -function convertHostDirectivesToMetadata( - metadata: R3DeclareDirectiveFacade | R3DirectiveMetadataFacade, -): R3HostDirectiveMetadata[] | null { - if (metadata.hostDirectives?.length) { - return metadata.hostDirectives.map((hostDirective) => { - return typeof hostDirective === 'function' - ? { - directive: wrapReference(hostDirective), - inputs: null, - outputs: null, - isForwardReference: false, - } - : { - directive: wrapReference(hostDirective.directive), - isForwardReference: false, - inputs: hostDirective.inputs ? parseMappingStringArray(hostDirective.inputs) : null, - outputs: hostDirective.outputs ? parseMappingStringArray(hostDirective.outputs) : null, - }; - }); +/** + * Parses a host directive mapping where each odd array key is the name of an input/output + * and each even key is its public name, e.g. `['one', 'oneAlias', 'two', 'two']`. + */ +function getHostDirectiveBindingMapping(array: string[]) { + let result: {[publicName: string]: string} | null = null; + + for (let i = 1; i < array.length; i += 2) { + result = result || {}; + result[array[i - 1]] = array[i]; } - return null; + return result; } function convertOpaqueValuesToExpressions(obj: {[key: string]: OpaqueValue}): { diff --git a/packages/core/test/render3/jit/declare_directive_spec.ts b/packages/core/test/render3/jit/declare_directive_spec.ts index efed18ae037..6166bb11aa9 100644 --- a/packages/core/test/render3/jit/declare_directive_spec.ts +++ b/packages/core/test/render3/jit/declare_directive_spec.ts @@ -261,6 +261,47 @@ describe('directive declaration jit compilation', () => { features: [ɵɵNgOnChangesFeature], }); }); + + it('should compile host directives', () => { + class One {} + class Two {} + + const def = ɵɵngDeclareDirective({ + type: TestClass, + hostDirectives: [ + { + directive: One, + inputs: ['firstInput', 'firstInput', 'secondInput', 'secondInputAlias'], + outputs: ['firstOutput', 'firstOutput', 'secondOutput', 'secondOutputAlias'], + }, + { + directive: Two, + }, + ], + }) as DirectiveDef; + + expectDirectiveDef(def, { + features: [jasmine.any(Function)], + hostDirectives: [ + { + directive: One, + inputs: { + 'firstInput': 'firstInput', + 'secondInput': 'secondInputAlias', + }, + outputs: { + 'firstOutput': 'firstOutput', + 'secondOutput': 'secondOutputAlias', + }, + }, + { + directive: Two, + inputs: {}, + outputs: {}, + }, + ], + }); + }); }); type DirectiveDefExpectations = jasmine.Expected< @@ -278,6 +319,7 @@ type DirectiveDefExpectations = jasmine.Expected< | 'viewQuery' | 'exportAs' | 'providersResolver' + | 'hostDirectives' > >; @@ -303,6 +345,7 @@ function expectDirectiveDef( viewQuery: null, exportAs: null, providersResolver: null, + hostDirectives: null, ...expected, }; @@ -321,6 +364,7 @@ function expectDirectiveDef( expect(actual.providersResolver) .withContext('providersResolver') .toEqual(expectation.providersResolver); + expect(actual.hostDirectives).withContext('hostDirectives').toEqual(expectation.hostDirectives); } class TestClass {}