@@ -559,6 +559,43 @@ fn resolve_factory_dep_namespaces<'a>(
559559 }
560560}
561561
562+ /// Resolves namespace imports for host directive references in `R3HostDirectiveMetadata`.
563+ ///
564+ /// Replaces bare `ReadVar("X")` references with namespace-prefixed `ReadProp(ReadVar("i1"), "X")`
565+ /// for any host directive that has a corresponding import in the import map.
566+ /// This ensures the compiled output works correctly even after import elision.
567+ fn resolve_host_directive_namespaces < ' a > (
568+ allocator : & ' a Allocator ,
569+ host_directives : & mut oxc_allocator:: Vec < ' a , crate :: R3HostDirectiveMetadata < ' a > > ,
570+ import_map : & ImportMap < ' a > ,
571+ namespace_registry : & mut NamespaceRegistry < ' a > ,
572+ ) {
573+ for hd in host_directives. iter_mut ( ) {
574+ // Only process bare variable references (ReadVar)
575+ let OutputExpression :: ReadVar ( ref var) = hd. directive else { continue } ;
576+ let name = & var. name ;
577+ // Look up this identifier in the import map
578+ let Some ( import_info) = import_map. get ( name) else { continue } ;
579+ // Replace with namespace-prefixed reference: i1.BrnTooltipTrigger instead of BrnTooltipTrigger
580+ let namespace = namespace_registry. get_or_assign ( & import_info. source_module ) ;
581+ hd. directive = OutputExpression :: ReadProp ( oxc_allocator:: Box :: new_in (
582+ ReadPropExpr {
583+ receiver : oxc_allocator:: Box :: new_in (
584+ OutputExpression :: ReadVar ( oxc_allocator:: Box :: new_in (
585+ ReadVarExpr { name : namespace, source_span : None } ,
586+ allocator,
587+ ) ) ,
588+ allocator,
589+ ) ,
590+ name : name. clone ( ) ,
591+ optional : false ,
592+ source_span : None ,
593+ } ,
594+ allocator,
595+ ) ) ;
596+ }
597+ }
598+
562599/// This is used to determine where to insert namespace imports so they appear
563600/// AFTER existing imports but BEFORE other code (like class declarations).
564601///
@@ -968,6 +1005,17 @@ pub fn transform_angular_file(
9681005 ) ;
9691006 }
9701007
1008+ // Resolve namespace imports for hostDirectives references.
1009+ // Host directive references (e.g., BrnTooltipTrigger from '@spartan-ng/brain/tooltip')
1010+ // must use namespace-prefixed references (e.g., i1.BrnTooltipTrigger) because the
1011+ // original named import may be elided and replaced by a namespace import.
1012+ resolve_host_directive_namespaces (
1013+ allocator,
1014+ & mut directive_metadata. host_directives ,
1015+ & import_map,
1016+ & mut file_namespace_registry,
1017+ ) ;
1018+
9711019 // Compile directive and generate definitions
9721020 // Pass shared_pool_index to ensure unique constant names across the file
9731021 let definitions = generate_directive_definitions (
@@ -5036,4 +5084,59 @@ export class TestNgModule {}
50365084 result. code
50375085 ) ;
50385086 }
5087+
5088+ #[ test]
5089+ fn test_directive_host_directives_get_namespace_resolution ( ) {
5090+ // Regression test for https://github.com/voidzero-dev/oxc-angular-compiler/issues/68
5091+ // hostDirectives references must use namespace-prefixed references (e.g., i1.BrnTooltipTrigger)
5092+ // instead of bare variable references (e.g., BrnTooltipTrigger), because the original
5093+ // named import may be elided and replaced by a namespace import.
5094+ let allocator = Allocator :: default ( ) ;
5095+ let source = r#"
5096+ import { Directive } from '@angular/core';
5097+ import { BrnTooltipTrigger } from '@spartan-ng/brain/tooltip';
5098+
5099+ @Directive({
5100+ selector: '[uTooltip]',
5101+ hostDirectives: [{ directive: BrnTooltipTrigger }]
5102+ })
5103+ export class UnityTooltipTrigger {}
5104+ "# ;
5105+
5106+ let result = transform_angular_file (
5107+ & allocator,
5108+ "tooltip.directive.ts" ,
5109+ source,
5110+ & TransformOptions :: default ( ) ,
5111+ None ,
5112+ ) ;
5113+
5114+ assert ! ( !result. has_errors( ) , "Transform should not have errors: {:?}" , result. diagnostics) ;
5115+
5116+ // Verify namespace import is generated for the external module
5117+ assert ! (
5118+ result. code. contains( "import * as i1 from '@spartan-ng/brain/tooltip'" ) ,
5119+ "Should generate namespace import for @spartan-ng/brain/tooltip, but got:\n {}" ,
5120+ result. code
5121+ ) ;
5122+
5123+ // Verify the host directive uses the namespace-prefixed reference
5124+ assert ! (
5125+ result. code. contains( "i1.BrnTooltipTrigger" ) ,
5126+ "Host directive should reference BrnTooltipTrigger as i1.BrnTooltipTrigger, but got:\n {}" ,
5127+ result. code
5128+ ) ;
5129+
5130+ // Verify there's no bare BrnTooltipTrigger reference in the features array
5131+ // (it should only appear in the import statement and as i1.BrnTooltipTrigger)
5132+ let features_section = result. code . split ( "features:" ) . nth ( 1 ) ;
5133+ if let Some ( features) = features_section {
5134+ assert ! (
5135+ !features. contains( "BrnTooltipTrigger" )
5136+ || features. contains( "i1.BrnTooltipTrigger" ) ,
5137+ "Features should NOT contain bare BrnTooltipTrigger reference, but got:\n {}" ,
5138+ result. code
5139+ ) ;
5140+ }
5141+ }
50395142}
0 commit comments