@@ -4,8 +4,8 @@ use oxc_allocator::{Box, Vec as OxcVec};
44use oxc_span:: Atom ;
55
66use crate :: output:: ast:: {
7- DeclareVarStmt , LiteralExpr , LiteralValue , OutputExpression , OutputStatement , ReadVarExpr ,
8- StmtModifier ,
7+ DeclareVarStmt , LiteralExpr , LiteralValue , OutputExpression , OutputStatement , ReadPropExpr ,
8+ ReadVarExpr , StmtModifier ,
99} ;
1010use crate :: r3:: Identifiers ;
1111
@@ -20,7 +20,7 @@ use super::super::utils::create_instruction_call_stmt;
2020/// - vars: Number of variable slots
2121/// - tag: Optional tag name (null for control flow blocks)
2222/// - constIndex: Optional const array index for attributes
23- /// - localRefs: Optional local refs index (not implemented yet )
23+ /// - localRefs: Optional local refs index (if present, also adds templateRefExtractor )
2424///
2525/// Ported from Angular's `conditionalCreate()` in `instruction.ts`.
2626/// Args are trimmed from the end if they are null values.
@@ -32,6 +32,7 @@ pub fn create_conditional_create_stmt<'a>(
3232 vars : Option < u32 > ,
3333 tag : Option < & Atom < ' a > > ,
3434 attributes : Option < u32 > ,
35+ local_refs_index : Option < u32 > ,
3536) -> OutputStatement < ' a > {
3637 let mut args = OxcVec :: new_in ( allocator) ;
3738
@@ -96,6 +97,31 @@ pub fn create_conditional_create_stmt<'a>(
9697 ) ) ) ;
9798 }
9899
100+ // Local refs index and templateRefExtractor
101+ // Ported from Angular's instruction.ts conditionalCreate(): when localRefs !== null,
102+ // push the refs const index and i0.ɵɵtemplateRefExtractor.
103+ if let Some ( refs_idx) = local_refs_index {
104+ args. push ( OutputExpression :: Literal ( Box :: new_in (
105+ LiteralExpr { value : LiteralValue :: Number ( refs_idx as f64 ) , source_span : None } ,
106+ allocator,
107+ ) ) ) ;
108+ args. push ( OutputExpression :: ReadProp ( Box :: new_in (
109+ ReadPropExpr {
110+ receiver : Box :: new_in (
111+ OutputExpression :: ReadVar ( Box :: new_in (
112+ ReadVarExpr { name : Atom :: from ( "i0" ) , source_span : None } ,
113+ allocator,
114+ ) ) ,
115+ allocator,
116+ ) ,
117+ name : Atom :: from ( Identifiers :: TEMPLATE_REF_EXTRACTOR ) ,
118+ optional : false ,
119+ source_span : None ,
120+ } ,
121+ allocator,
122+ ) ) ) ;
123+ }
124+
99125 // Trim trailing null arguments (matching Angular's behavior)
100126 while let Some ( OutputExpression :: Literal ( lit) ) = args. last ( ) {
101127 if matches ! ( lit. value, LiteralValue :: Null ) {
@@ -135,7 +161,7 @@ pub fn create_conditional_update_stmt<'a>(
135161/// - vars: Number of variable slots
136162/// - tag: Optional tag name (null for control flow blocks)
137163/// - constIndex: Optional const array index for attributes
138- /// - localRefs: Optional local refs index (not implemented yet )
164+ /// - localRefs: Optional local refs index (if present, also adds templateRefExtractor )
139165///
140166/// Ported from Angular's `conditionalBranchCreate()` in `instruction.ts`.
141167/// Args are trimmed from the end if they are null values.
@@ -147,6 +173,7 @@ pub fn create_conditional_branch_create_stmt<'a>(
147173 vars : Option < u32 > ,
148174 tag : Option < & Atom < ' a > > ,
149175 attributes : Option < u32 > ,
176+ local_refs_index : Option < u32 > ,
150177) -> OutputStatement < ' a > {
151178 let mut args = OxcVec :: new_in ( allocator) ;
152179
@@ -211,6 +238,31 @@ pub fn create_conditional_branch_create_stmt<'a>(
211238 ) ) ) ;
212239 }
213240
241+ // Local refs index and templateRefExtractor
242+ // Ported from Angular's instruction.ts conditionalBranchCreate(): when localRefs !== null,
243+ // push the refs const index and i0.ɵɵtemplateRefExtractor.
244+ if let Some ( refs_idx) = local_refs_index {
245+ args. push ( OutputExpression :: Literal ( Box :: new_in (
246+ LiteralExpr { value : LiteralValue :: Number ( refs_idx as f64 ) , source_span : None } ,
247+ allocator,
248+ ) ) ) ;
249+ args. push ( OutputExpression :: ReadProp ( Box :: new_in (
250+ ReadPropExpr {
251+ receiver : Box :: new_in (
252+ OutputExpression :: ReadVar ( Box :: new_in (
253+ ReadVarExpr { name : Atom :: from ( "i0" ) , source_span : None } ,
254+ allocator,
255+ ) ) ,
256+ allocator,
257+ ) ,
258+ name : Atom :: from ( Identifiers :: TEMPLATE_REF_EXTRACTOR ) ,
259+ optional : false ,
260+ source_span : None ,
261+ } ,
262+ allocator,
263+ ) ) ) ;
264+ }
265+
214266 // Trim trailing null arguments (matching Angular's behavior)
215267 while let Some ( OutputExpression :: Literal ( lit) ) = args. last ( ) {
216268 if matches ! ( lit. value, LiteralValue :: Null ) {
@@ -456,3 +508,111 @@ pub fn create_declare_let_stmt<'a>(
456508// StoreLet as an update op should have been converted to a StoreLet expression
457509// during the store_let_optimization phase. If it reaches reify, it's a compiler bug.
458510// This matches Angular's behavior which throws: "AssertionError: unexpected storeLet"
511+
512+ #[ cfg( test) ]
513+ mod tests {
514+ use super :: * ;
515+ use crate :: output:: emitter:: JsEmitter ;
516+
517+ #[ test]
518+ fn conditional_create_emits_local_refs_and_template_ref_extractor ( ) {
519+ let allocator = oxc_allocator:: Allocator :: default ( ) ;
520+ let emitter = JsEmitter :: new ( ) ;
521+
522+ // With local_refs_index = Some(3), the instruction should include the refs index
523+ // and i0.ɵɵtemplateRefExtractor, matching Angular's instruction.ts conditionalCreate().
524+ let stmt = create_conditional_create_stmt (
525+ & allocator,
526+ 0 ,
527+ Some ( Atom :: from ( "TestComponent_Conditional_0_Template" ) ) ,
528+ Some ( 1 ) ,
529+ Some ( 0 ) ,
530+ None ,
531+ None ,
532+ Some ( 3 ) ,
533+ ) ;
534+ let js = emitter. emit_statement ( & stmt) ;
535+ assert ! (
536+ js. contains( "i0.ɵɵtemplateRefExtractor" ) ,
537+ "conditionalCreate with localRefs should emit templateRefExtractor. Got: {js}"
538+ ) ;
539+ // When tag and constIndex are null but localRefs is set, they remain as null placeholders
540+ // because localRefs+templateRefExtractor come after them, preventing null-trimming.
541+ assert ! (
542+ js. contains( "ɵɵconditionalCreate(0,TestComponent_Conditional_0_Template,1,0,null,null,3,i0.ɵɵtemplateRefExtractor)" ) ,
543+ "conditionalCreate should emit: slot, fnRef, decls, vars, tag, constIndex, refsIdx, templateRefExtractor. Got: {js}"
544+ ) ;
545+ }
546+
547+ #[ test]
548+ fn conditional_create_without_local_refs_omits_template_ref_extractor ( ) {
549+ let allocator = oxc_allocator:: Allocator :: default ( ) ;
550+ let emitter = JsEmitter :: new ( ) ;
551+
552+ // Without local refs, templateRefExtractor should not appear
553+ let stmt = create_conditional_create_stmt (
554+ & allocator,
555+ 0 ,
556+ Some ( Atom :: from ( "TestComponent_Conditional_0_Template" ) ) ,
557+ Some ( 1 ) ,
558+ Some ( 0 ) ,
559+ None ,
560+ None ,
561+ None ,
562+ ) ;
563+ let js = emitter. emit_statement ( & stmt) ;
564+ assert ! (
565+ !js. contains( "templateRefExtractor" ) ,
566+ "conditionalCreate without localRefs should NOT emit templateRefExtractor. Got: {js}"
567+ ) ;
568+ }
569+
570+ #[ test]
571+ fn conditional_branch_create_emits_local_refs_and_template_ref_extractor ( ) {
572+ let allocator = oxc_allocator:: Allocator :: default ( ) ;
573+ let emitter = JsEmitter :: new ( ) ;
574+
575+ let stmt = create_conditional_branch_create_stmt (
576+ & allocator,
577+ 1 ,
578+ Some ( Atom :: from ( "TestComponent_Conditional_1_Template" ) ) ,
579+ Some ( 1 ) ,
580+ Some ( 0 ) ,
581+ None ,
582+ None ,
583+ Some ( 5 ) ,
584+ ) ;
585+ let js = emitter. emit_statement ( & stmt) ;
586+ assert ! (
587+ js. contains( "i0.ɵɵtemplateRefExtractor" ) ,
588+ "conditionalBranchCreate with localRefs should emit templateRefExtractor. Got: {js}"
589+ ) ;
590+ // When tag and constIndex are null but localRefs is set, they remain as null placeholders.
591+ assert ! (
592+ js. contains( "ɵɵconditionalBranchCreate(1,TestComponent_Conditional_1_Template,1,0,null,null,\n 5,i0.ɵɵtemplateRefExtractor)" ) ,
593+ "conditionalBranchCreate should emit: slot, fnRef, decls, vars, tag, constIndex, refsIdx, templateRefExtractor. Got: {js}"
594+ ) ;
595+ }
596+
597+ #[ test]
598+ fn conditional_branch_create_without_local_refs_omits_template_ref_extractor ( ) {
599+ let allocator = oxc_allocator:: Allocator :: default ( ) ;
600+ let emitter = JsEmitter :: new ( ) ;
601+
602+ let stmt = create_conditional_branch_create_stmt (
603+ & allocator,
604+ 1 ,
605+ Some ( Atom :: from ( "TestComponent_Conditional_1_Template" ) ) ,
606+ Some ( 1 ) ,
607+ Some ( 0 ) ,
608+ None ,
609+ None ,
610+ None ,
611+ ) ;
612+ let js = emitter. emit_statement ( & stmt) ;
613+ assert ! (
614+ !js. contains( "templateRefExtractor" ) ,
615+ "conditionalBranchCreate without localRefs should NOT emit templateRefExtractor. Got: {js}"
616+ ) ;
617+ }
618+ }
0 commit comments