Skip to content

Commit 50538a6

Browse files
committed
fix more cases
1 parent b6576d9 commit 50538a6

File tree

11 files changed

+673
-345
lines changed

11 files changed

+673
-345
lines changed

crates/oxc_angular_compiler/src/factory/compiler.rs

Lines changed: 284 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,31 @@
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
2235
use oxc_allocator::{Allocator, Box, FromIn, Vec};
2336
use oxc_span::Atom;
2437

25-
use super::metadata::{FactoryTarget, R3DependencyMetadata, R3FactoryDeps, R3FactoryMetadata};
38+
use super::metadata::{
39+
FactoryTarget, R3DependencyMetadata, R3FactoryDelegateType, R3FactoryDeps, R3FactoryMetadata,
40+
};
2641
use 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
};
3247
use 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`
71101
pub 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

Comments
 (0)