1818//! };
1919//! })()
2020//! ```
21+ //!
22+ //! For delegated factories (useFactory/useClass with deps), generates:
23+ //! ```javascript
24+ //! function MyService_Factory(__ngFactoryType__) {
25+ //! let __ngConditionalFactory__ = null;
26+ //! if (__ngFactoryType__) {
27+ //! __ngConditionalFactory__ = new (__ngFactoryType__ || MyService)();
28+ //! } else {
29+ //! __ngConditionalFactory__ = delegatedFactory(ɵɵinject(Dep1), ɵɵinject(Dep2));
30+ //! }
31+ //! return __ngConditionalFactory__;
32+ //! }
33+ //! ```
2134
2235use oxc_allocator:: { Allocator , Box , FromIn , Vec } ;
2336use oxc_span:: Atom ;
2437
25- use super :: metadata:: { FactoryTarget , R3DependencyMetadata , R3FactoryDeps , R3FactoryMetadata } ;
38+ use super :: metadata:: {
39+ FactoryTarget , R3DependencyMetadata , R3FactoryDelegateType , R3FactoryDeps , R3FactoryMetadata ,
40+ } ;
2641use crate :: output:: ast:: {
2742 ArrowFunctionBody , ArrowFunctionExpr , BinaryOperator , BinaryOperatorExpr , DeclareVarStmt ,
28- ExpressionStatement , FnParam , FunctionExpr , InstantiateExpr , InvokeFunctionExpr , LiteralExpr ,
29- LiteralValue , OutputExpression , OutputStatement , ReadPropExpr , ReadVarExpr , ReturnStatement ,
30- StmtModifier ,
43+ ExpressionStatement , FnParam , FunctionExpr , IfStmt , InstantiateExpr , InvokeFunctionExpr ,
44+ LiteralExpr , LiteralValue , OutputExpression , OutputStatement , ReadPropExpr , ReadVarExpr ,
45+ ReturnStatement , StmtModifier ,
3146} ;
3247use crate :: r3:: Identifiers ;
3348
@@ -68,6 +83,21 @@ pub struct FactoryCompileResult<'a> {
6883/// return new (__ngFactoryType__ || MyClass)(ɵɵdirectiveInject(Dep1));
6984/// }
7085/// ```
86+ ///
87+ /// For delegated factories (useFactory/useClass with deps), generates the conditional pattern:
88+ /// ```javascript
89+ /// function MyService_Factory(__ngFactoryType__) {
90+ /// let __ngConditionalFactory__ = null;
91+ /// if (__ngFactoryType__) {
92+ /// __ngConditionalFactory__ = new (__ngFactoryType__ || MyService)();
93+ /// } else {
94+ /// __ngConditionalFactory__ = delegatedFactory(ɵɵinject(Dep1), ɵɵinject(Dep2));
95+ /// }
96+ /// return __ngConditionalFactory__;
97+ /// }
98+ /// ```
99+ ///
100+ /// See: `packages/compiler/src/render3/r3_factory.ts:106-200`
71101pub fn compile_factory_function < ' a > (
72102 allocator : & ' a Allocator ,
73103 meta : & R3FactoryMetadata < ' a > ,
@@ -76,67 +106,153 @@ pub fn compile_factory_function<'a>(
76106 let base = meta. base ( ) ;
77107 let factory_type_param = Atom :: from ( "__ngFactoryType__" ) ;
78108
79- // (__ngFactoryType__ || MyClass)
80- let type_for_ctor = OutputExpression :: BinaryOperator ( Box :: new_in (
81- BinaryOperatorExpr {
82- operator : BinaryOperator :: Or ,
83- lhs : Box :: new_in (
84- OutputExpression :: ReadVar ( Box :: new_in (
85- ReadVarExpr { name : factory_type_param. clone ( ) , source_span : None } ,
109+ // The type to instantiate via constructor invocation. If there is no delegated factory,
110+ // meaning this type is always created by constructor invocation, then this is the
111+ // type-to-create parameter provided by the user (t) if specified, or the current type if not.
112+ // If there is a delegated factory (which is used to create the current type) then this is
113+ // only the type-to-create parameter (t).
114+ // See: r3_factory.ts:115-118
115+ let type_for_ctor = if meta. is_delegated ( ) {
116+ // For delegated factories, only use the type parameter
117+ OutputExpression :: ReadVar ( Box :: new_in (
118+ ReadVarExpr { name : factory_type_param. clone ( ) , source_span : None } ,
119+ allocator,
120+ ) )
121+ } else {
122+ // (__ngFactoryType__ || MyClass)
123+ OutputExpression :: BinaryOperator ( Box :: new_in (
124+ BinaryOperatorExpr {
125+ operator : BinaryOperator :: Or ,
126+ lhs : Box :: new_in (
127+ OutputExpression :: ReadVar ( Box :: new_in (
128+ ReadVarExpr { name : factory_type_param. clone ( ) , source_span : None } ,
129+ allocator,
130+ ) ) ,
86131 allocator,
87- ) ) ,
88- allocator,
89- ) ,
90- rhs : Box :: new_in ( base. type_expr . clone_in ( allocator) , allocator) ,
91- source_span : None ,
92- } ,
93- allocator,
94- ) ) ;
95-
96- let mut body: Vec < ' a , OutputStatement < ' a > > = Vec :: new_in ( allocator) ;
132+ ) ,
133+ rhs : Box :: new_in ( base. type_expr . clone_in ( allocator) , allocator) ,
134+ source_span : None ,
135+ } ,
136+ allocator,
137+ ) )
138+ } ;
97139
98- // Determine the constructor expression based on deps
99- match & base. deps {
140+ // Build the constructor expression based on deps
141+ // See: r3_factory.ts:119-129
142+ let ctor_expr: Option < OutputExpression < ' a > > = match & base. deps {
100143 R3FactoryDeps :: Valid ( deps) => {
101144 // new (type)(ɵɵinject(Dep1), ɵɵinject(Dep2), ...)
102145 let inject_args = inject_dependencies ( allocator, deps. as_slice ( ) , base. target ) ;
103- let ctor_expr = OutputExpression :: Instantiate ( Box :: new_in (
146+ Some ( OutputExpression :: Instantiate ( Box :: new_in (
104147 InstantiateExpr {
105- class_expr : Box :: new_in ( type_for_ctor, allocator) ,
148+ class_expr : Box :: new_in ( type_for_ctor. clone_in ( allocator ) , allocator) ,
106149 args : inject_args,
107150 source_span : None ,
108151 } ,
109152 allocator,
153+ ) ) )
154+ }
155+ R3FactoryDeps :: Invalid => None ,
156+ R3FactoryDeps :: None => {
157+ // No constructor - use inherited factory pattern.
158+ // This is handled specially below for non-delegated cases.
159+ None
160+ }
161+ } ;
162+
163+ // Check if we need inherited factory pattern for non-delegated case with no deps
164+ if !meta. is_delegated ( ) && !meta. is_expression ( ) && matches ! ( & base. deps, R3FactoryDeps :: None ) {
165+ return compile_inherited_factory ( allocator, base, factory_name) ;
166+ }
167+
168+ let mut body: Vec < ' a , OutputStatement < ' a > > = Vec :: new_in ( allocator) ;
169+ let ret_expr: Option < OutputExpression < ' a > > ;
170+
171+ // Handle the different metadata types
172+ // See: r3_factory.ts:145-159
173+ match meta {
174+ R3FactoryMetadata :: Delegated ( delegated_meta) => {
175+ // This type is created with a delegated factory. If a type parameter is not specified,
176+ // call the factory instead.
177+ let delegate_args =
178+ inject_dependencies ( allocator, & delegated_meta. delegate_deps , base. target ) ;
179+
180+ // Either call `new delegate(...)` or `delegate(...)` depending on delegate_type
181+ let factory_expr = match delegated_meta. delegate_type {
182+ R3FactoryDelegateType :: Class => {
183+ // new delegate(...)
184+ OutputExpression :: Instantiate ( Box :: new_in (
185+ InstantiateExpr {
186+ class_expr : Box :: new_in (
187+ delegated_meta. delegate . clone_in ( allocator) ,
188+ allocator,
189+ ) ,
190+ args : delegate_args,
191+ source_span : None ,
192+ } ,
193+ allocator,
194+ ) )
195+ }
196+ R3FactoryDelegateType :: Function => {
197+ // delegate(...)
198+ OutputExpression :: InvokeFunction ( Box :: new_in (
199+ InvokeFunctionExpr {
200+ fn_expr : Box :: new_in (
201+ delegated_meta. delegate . clone_in ( allocator) ,
202+ allocator,
203+ ) ,
204+ args : delegate_args,
205+ pure : false ,
206+ source_span : None ,
207+ } ,
208+ allocator,
209+ ) )
210+ }
211+ } ;
212+
213+ ret_expr = Some ( make_conditional_factory (
214+ allocator,
215+ & mut body,
216+ & factory_type_param,
217+ ctor_expr,
218+ factory_expr,
110219 ) ) ;
111- // Add return statement
112- body. push ( OutputStatement :: Return ( Box :: new_in (
113- ReturnStatement { value : ctor_expr, source_span : None } ,
220+ }
221+ R3FactoryMetadata :: Expression ( expr_meta) => {
222+ // useValue or useExisting case
223+ ret_expr = Some ( make_conditional_factory (
114224 allocator,
115- ) ) ) ;
225+ & mut body,
226+ & factory_type_param,
227+ ctor_expr,
228+ expr_meta. expression . clone_in ( allocator) ,
229+ ) ) ;
116230 }
117- R3FactoryDeps :: Invalid => {
118- // Add invalidFactory() call as expression statement (not return).
119- // This matches TypeScript behavior at r3_factory.ts:161-163.
231+ R3FactoryMetadata :: Constructor ( _) => {
232+ // Standard constructor case - just use ctor_expr directly
233+ ret_expr = ctor_expr;
234+ }
235+ }
236+
237+ // Generate the return or invalidFactory call
238+ // See: r3_factory.ts:161-177
239+ match ret_expr {
240+ None => {
241+ // The expression cannot be formed so render an `ɵɵinvalidFactory()` call.
120242 let invalid_factory_call = create_invalid_factory_call ( allocator) ;
121243 body. push ( OutputStatement :: Expression ( Box :: new_in (
122244 ExpressionStatement { expr : invalid_factory_call, source_span : None } ,
123245 allocator,
124246 ) ) ) ;
125247 }
126- R3FactoryDeps :: None => {
127- // No constructor - use inherited factory pattern.
128- // Generates:
129- // ```javascript
130- // /*@__PURE__*/ (() => {
131- // let ɵMyClass_BaseFactory;
132- // return function MyClass_Factory(__ngFactoryType__) {
133- // return (ɵMyClass_BaseFactory || (ɵMyClass_BaseFactory = ɵɵgetInheritedFactory(MyClass)))(__ngFactoryType__ || MyClass);
134- // };
135- // })()
136- // ```
137- return compile_inherited_factory ( allocator, base, factory_name) ;
248+ Some ( expr) => {
249+ // Return the result
250+ body. push ( OutputStatement :: Return ( Box :: new_in (
251+ ReturnStatement { value : expr, source_span : None } ,
252+ allocator,
253+ ) ) ) ;
138254 }
139- } ;
255+ }
140256
141257 // Create the factory function
142258 let mut params = Vec :: new_in ( allocator) ;
@@ -155,6 +271,129 @@ pub fn compile_factory_function<'a>(
155271 FactoryCompileResult { expression : factory_fn, statements : Vec :: new_in ( allocator) }
156272}
157273
274+ /// Creates the conditional factory pattern for delegated/expression factories.
275+ ///
276+ /// Generates:
277+ /// ```javascript
278+ /// let __ngConditionalFactory__ = null;
279+ /// if (__ngFactoryType__) {
280+ /// __ngConditionalFactory__ = <ctor_expr>; // or invalidFactory() if None
281+ /// } else {
282+ /// __ngConditionalFactory__ = <non_ctor_expr>;
283+ /// }
284+ /// ```
285+ ///
286+ /// Returns the `__ngConditionalFactory__` variable reference.
287+ ///
288+ /// See: `packages/compiler/src/render3/r3_factory.ts:134-143`
289+ fn make_conditional_factory < ' a > (
290+ allocator : & ' a Allocator ,
291+ body : & mut Vec < ' a , OutputStatement < ' a > > ,
292+ factory_type_param : & Atom < ' a > ,
293+ ctor_expr : Option < OutputExpression < ' a > > ,
294+ non_ctor_expr : OutputExpression < ' a > ,
295+ ) -> OutputExpression < ' a > {
296+ let conditional_factory_var = Atom :: from ( "__ngConditionalFactory__" ) ;
297+
298+ // let __ngConditionalFactory__ = null;
299+ body. push ( OutputStatement :: DeclareVar ( Box :: new_in (
300+ DeclareVarStmt {
301+ name : conditional_factory_var. clone ( ) ,
302+ value : Some ( OutputExpression :: Literal ( Box :: new_in (
303+ LiteralExpr { value : LiteralValue :: Null , source_span : None } ,
304+ allocator,
305+ ) ) ) ,
306+ modifiers : StmtModifier :: NONE ,
307+ leading_comment : None ,
308+ source_span : None ,
309+ } ,
310+ allocator,
311+ ) ) ) ;
312+
313+ // Create the true case: __ngConditionalFactory__ = <ctor_expr> or invalidFactory()
314+ let true_stmt = match ctor_expr {
315+ Some ( expr) => {
316+ // __ngConditionalFactory__ = <ctor_expr>
317+ let assignment = OutputExpression :: BinaryOperator ( Box :: new_in (
318+ BinaryOperatorExpr {
319+ operator : BinaryOperator :: Assign ,
320+ lhs : Box :: new_in (
321+ OutputExpression :: ReadVar ( Box :: new_in (
322+ ReadVarExpr {
323+ name : conditional_factory_var. clone ( ) ,
324+ source_span : None ,
325+ } ,
326+ allocator,
327+ ) ) ,
328+ allocator,
329+ ) ,
330+ rhs : Box :: new_in ( expr, allocator) ,
331+ source_span : None ,
332+ } ,
333+ allocator,
334+ ) ) ;
335+ OutputStatement :: Expression ( Box :: new_in (
336+ ExpressionStatement { expr : assignment, source_span : None } ,
337+ allocator,
338+ ) )
339+ }
340+ None => {
341+ // invalidFactory()
342+ let invalid_factory_call = create_invalid_factory_call ( allocator) ;
343+ OutputStatement :: Expression ( Box :: new_in (
344+ ExpressionStatement { expr : invalid_factory_call, source_span : None } ,
345+ allocator,
346+ ) )
347+ }
348+ } ;
349+
350+ // Create the false case: __ngConditionalFactory__ = <non_ctor_expr>
351+ let false_assignment = OutputExpression :: BinaryOperator ( Box :: new_in (
352+ BinaryOperatorExpr {
353+ operator : BinaryOperator :: Assign ,
354+ lhs : Box :: new_in (
355+ OutputExpression :: ReadVar ( Box :: new_in (
356+ ReadVarExpr { name : conditional_factory_var. clone ( ) , source_span : None } ,
357+ allocator,
358+ ) ) ,
359+ allocator,
360+ ) ,
361+ rhs : Box :: new_in ( non_ctor_expr, allocator) ,
362+ source_span : None ,
363+ } ,
364+ allocator,
365+ ) ) ;
366+ let false_stmt = OutputStatement :: Expression ( Box :: new_in (
367+ ExpressionStatement { expr : false_assignment, source_span : None } ,
368+ allocator,
369+ ) ) ;
370+
371+ // Create the if statement
372+ let mut true_case = Vec :: new_in ( allocator) ;
373+ true_case. push ( true_stmt) ;
374+ let mut false_case = Vec :: new_in ( allocator) ;
375+ false_case. push ( false_stmt) ;
376+
377+ body. push ( OutputStatement :: If ( Box :: new_in (
378+ IfStmt {
379+ condition : OutputExpression :: ReadVar ( Box :: new_in (
380+ ReadVarExpr { name : factory_type_param. clone ( ) , source_span : None } ,
381+ allocator,
382+ ) ) ,
383+ true_case,
384+ false_case,
385+ source_span : None ,
386+ } ,
387+ allocator,
388+ ) ) ) ;
389+
390+ // Return the variable reference
391+ OutputExpression :: ReadVar ( Box :: new_in (
392+ ReadVarExpr { name : conditional_factory_var, source_span : None } ,
393+ allocator,
394+ ) )
395+ }
396+
158397/// Compiles an inherited factory using the IIFE memoization pattern.
159398///
160399/// Generates:
0 commit comments