Skip to content

Commit ebd8de7

Browse files
committed
align more
1 parent 9855e53 commit ebd8de7

File tree

31 files changed

+7860
-304
lines changed

31 files changed

+7860
-304
lines changed

crates/oxc_angular_compiler/src/component/decorator.rs

Lines changed: 381 additions & 1 deletion
Large diffs are not rendered by default.

crates/oxc_angular_compiler/src/component/transform.rs

Lines changed: 1178 additions & 0 deletions
Large diffs are not rendered by default.

crates/oxc_angular_compiler/src/directive/decorator.rs

Lines changed: 1117 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
//! Directive definition generation (ɵdir and ɵfac).
2+
//!
3+
//! This module generates the Angular runtime definitions that are added
4+
//! as static properties on directive classes:
5+
//!
6+
//! - `ɵdir`: Directive definition created by `ɵɵdefineDirective()`
7+
//! - `ɵfac`: Factory function for instantiating the directive
8+
//!
9+
//! These definitions are used by Angular's runtime to:
10+
//! - Match the directive to elements via selector
11+
//! - Handle host bindings and listeners
12+
//! - Inject dependencies
13+
//! - Manage directive lifecycle
14+
15+
use oxc_allocator::{Allocator, Vec};
16+
17+
use super::compiler::compile_directive;
18+
use super::metadata::R3DirectiveMetadata;
19+
use crate::factory::{
20+
FactoryTarget, R3ConstructorFactoryMetadata, R3DependencyMetadata, R3FactoryDeps,
21+
R3FactoryMetadata, compile_factory_function,
22+
};
23+
use crate::output::ast::OutputExpression;
24+
25+
/// Result of generating directive definitions.
26+
pub struct DirectiveDefinitions<'a> {
27+
/// The ɵdir definition (directive metadata for Angular runtime).
28+
pub dir_definition: OutputExpression<'a>,
29+
30+
/// The ɵfac factory function.
31+
pub fac_definition: OutputExpression<'a>,
32+
}
33+
34+
/// Generate ɵdir and ɵfac definitions for a directive.
35+
///
36+
/// # Arguments
37+
///
38+
/// * `allocator` - Memory allocator
39+
/// * `metadata` - Directive metadata extracted from decorator
40+
///
41+
/// # Returns
42+
///
43+
/// The ɵdir and ɵfac definitions as output expressions.
44+
///
45+
/// # Example Output
46+
///
47+
/// ```javascript
48+
/// // ɵdir definition:
49+
/// MyDirective.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({
50+
/// type: MyDirective,
51+
/// selectors: [["", "myDir", ""]],
52+
/// inputs: { prop: "prop" },
53+
/// outputs: { click: "click" },
54+
/// hostBindings: function(rf, ctx) { ... },
55+
/// features: [i0.ɵɵNgOnChangesFeature]
56+
/// });
57+
///
58+
/// // ɵfac definition:
59+
/// MyDirective.ɵfac = function MyDirective_Factory(__ngFactoryType__) {
60+
/// return new (__ngFactoryType__ || MyDirective)(
61+
/// i0.ɵɵdirectiveInject(ServiceA),
62+
/// i0.ɵɵdirectiveInject(ServiceB, 8) // 8 = Optional flag
63+
/// );
64+
/// };
65+
/// ```
66+
pub fn generate_directive_definitions<'a>(
67+
allocator: &'a Allocator,
68+
metadata: &R3DirectiveMetadata<'a>,
69+
) -> DirectiveDefinitions<'a> {
70+
let dir_definition = generate_dir_definition(allocator, metadata);
71+
let fac_definition = generate_fac_definition(allocator, metadata);
72+
73+
DirectiveDefinitions { dir_definition, fac_definition }
74+
}
75+
76+
/// Generate the ɵdir definition.
77+
///
78+
/// This calls `compile_directive()` which creates an expression like:
79+
/// ```javascript
80+
/// /*@__PURE__*/ i0.ɵɵdefineDirective({
81+
/// type: MyDirective,
82+
/// selectors: [["", "myDir", ""]],
83+
/// inputs: { prop: "prop" },
84+
/// outputs: { click: "click" },
85+
/// hostBindings: function(rf, ctx) { ... },
86+
/// features: [i0.ɵɵNgOnChangesFeature]
87+
/// })
88+
/// ```
89+
fn generate_dir_definition<'a>(
90+
allocator: &'a Allocator,
91+
metadata: &R3DirectiveMetadata<'a>,
92+
) -> OutputExpression<'a> {
93+
let result = compile_directive(allocator, metadata);
94+
result.expression
95+
}
96+
97+
/// Generate the ɵfac factory function.
98+
///
99+
/// Creates an expression like:
100+
/// ```javascript
101+
/// // Without dependencies:
102+
/// function DirectiveClass_Factory(__ngFactoryType__) {
103+
/// return new (__ngFactoryType__ || DirectiveClass)();
104+
/// }
105+
///
106+
/// // With dependencies:
107+
/// function DirectiveClass_Factory(__ngFactoryType__) {
108+
/// return new (__ngFactoryType__ || DirectiveClass)(
109+
/// i0.ɵɵdirectiveInject(ServiceA),
110+
/// i0.ɵɵdirectiveInject(ServiceB, 8) // 8 = Optional flag
111+
/// );
112+
/// }
113+
/// ```
114+
///
115+
/// Ported from: `packages/compiler/src/render3/r3_factory.ts`
116+
fn generate_fac_definition<'a>(
117+
allocator: &'a Allocator,
118+
metadata: &R3DirectiveMetadata<'a>,
119+
) -> OutputExpression<'a> {
120+
// Factory function name: DirectiveName_Factory
121+
let factory_name = allocator.alloc_str(&format!("{}_Factory", metadata.name));
122+
123+
// Convert deps to R3FactoryDeps
124+
let factory_deps = match &metadata.deps {
125+
Some(deps) => {
126+
// Clone deps into a new Vec for R3FactoryDeps
127+
let mut factory_deps: Vec<'a, R3DependencyMetadata<'a>> =
128+
Vec::with_capacity_in(deps.len(), allocator);
129+
for dep in deps {
130+
factory_deps.push(R3DependencyMetadata {
131+
token: dep.token.as_ref().map(|t| t.clone_in(allocator)),
132+
attribute_name_type: dep
133+
.attribute_name_type
134+
.as_ref()
135+
.map(|a| a.clone_in(allocator)),
136+
host: dep.host,
137+
optional: dep.optional,
138+
self_: dep.self_,
139+
skip_self: dep.skip_self,
140+
});
141+
}
142+
R3FactoryDeps::Valid(factory_deps)
143+
}
144+
None => R3FactoryDeps::None,
145+
};
146+
147+
// Create factory metadata
148+
let factory_meta = R3FactoryMetadata::Constructor(R3ConstructorFactoryMetadata {
149+
name: metadata.name,
150+
type_expr: metadata.r#type.clone_in(allocator),
151+
type_decl: metadata.r#type.clone_in(allocator),
152+
type_argument_count: metadata.type_argument_count,
153+
deps: factory_deps,
154+
target: FactoryTarget::Directive,
155+
});
156+
157+
// Compile the factory function
158+
let result = compile_factory_function(allocator, &factory_meta, factory_name);
159+
result.expression
160+
}
161+
162+
#[cfg(test)]
163+
mod tests {
164+
use super::*;
165+
use crate::directive::metadata::R3HostMetadata;
166+
use crate::output::ast::ReadVarExpr;
167+
use crate::output::emitter::JsEmitter;
168+
use oxc_allocator::Box;
169+
use oxc_span::Atom;
170+
171+
fn create_test_metadata<'a>(allocator: &'a Allocator) -> R3DirectiveMetadata<'a> {
172+
let type_expr = OutputExpression::ReadVar(Box::new_in(
173+
ReadVarExpr { name: Atom::from("TestDirective"), source_span: None },
174+
allocator,
175+
));
176+
177+
R3DirectiveMetadata {
178+
name: Atom::from("TestDirective"),
179+
r#type: type_expr,
180+
type_argument_count: 0,
181+
deps: None,
182+
selector: Some(Atom::from("[testDir]")),
183+
queries: Vec::new_in(allocator),
184+
view_queries: Vec::new_in(allocator),
185+
host: R3HostMetadata::new(allocator),
186+
uses_on_changes: false,
187+
inputs: Vec::new_in(allocator),
188+
outputs: Vec::new_in(allocator),
189+
uses_inheritance: false,
190+
export_as: Vec::new_in(allocator),
191+
providers: None,
192+
is_standalone: true,
193+
is_signal: false,
194+
host_directives: Vec::new_in(allocator),
195+
}
196+
}
197+
198+
#[test]
199+
fn test_generate_directive_definitions() {
200+
let allocator = Allocator::default();
201+
let metadata = create_test_metadata(&allocator);
202+
203+
let definitions = generate_directive_definitions(&allocator, &metadata);
204+
205+
let emitter = JsEmitter::new();
206+
207+
// Check dir definition
208+
let dir_js = emitter.emit_expression(&definitions.dir_definition);
209+
assert!(dir_js.contains("defineDirective"));
210+
assert!(dir_js.contains("TestDirective"));
211+
assert!(dir_js.contains("selectors"));
212+
213+
// Check fac definition
214+
let fac_js = emitter.emit_expression(&definitions.fac_definition);
215+
assert!(fac_js.contains("TestDirective_Factory"));
216+
assert!(fac_js.contains("__ngFactoryType__"));
217+
}
218+
219+
#[test]
220+
fn test_generate_fac_definition_without_deps() {
221+
let allocator = Allocator::default();
222+
let metadata = create_test_metadata(&allocator);
223+
224+
let fac = generate_fac_definition(&allocator, &metadata);
225+
226+
let emitter = JsEmitter::new();
227+
let js = emitter.emit_expression(&fac);
228+
229+
// Should have inherited factory pattern (IIFE with getInheritedFactory)
230+
// because deps is None
231+
assert!(js.contains("getInheritedFactory") || js.contains("ɵɵgetInheritedFactory"));
232+
assert!(js.contains("TestDirective"));
233+
}
234+
235+
#[test]
236+
fn test_generate_fac_definition_with_empty_deps() {
237+
let allocator = Allocator::default();
238+
let type_expr = OutputExpression::ReadVar(Box::new_in(
239+
ReadVarExpr { name: Atom::from("TestDirective"), source_span: None },
240+
&allocator,
241+
));
242+
243+
let metadata = R3DirectiveMetadata {
244+
name: Atom::from("TestDirective"),
245+
r#type: type_expr,
246+
type_argument_count: 0,
247+
deps: Some(Vec::new_in(&allocator)), // Empty deps - has constructor but no params
248+
selector: Some(Atom::from("[testDir]")),
249+
queries: Vec::new_in(&allocator),
250+
view_queries: Vec::new_in(&allocator),
251+
host: R3HostMetadata::new(&allocator),
252+
uses_on_changes: false,
253+
inputs: Vec::new_in(&allocator),
254+
outputs: Vec::new_in(&allocator),
255+
uses_inheritance: false,
256+
export_as: Vec::new_in(&allocator),
257+
providers: None,
258+
is_standalone: true,
259+
is_signal: false,
260+
host_directives: Vec::new_in(&allocator),
261+
};
262+
263+
let fac = generate_fac_definition(&allocator, &metadata);
264+
265+
let emitter = JsEmitter::new();
266+
let js = emitter.emit_expression(&fac);
267+
268+
// Should have simple factory (no inherited factory pattern)
269+
assert!(js.contains("TestDirective_Factory"));
270+
assert!(js.contains("__ngFactoryType__"));
271+
assert!(js.contains("new"));
272+
// Should NOT have getInheritedFactory
273+
assert!(!js.contains("getInheritedFactory"));
274+
}
275+
276+
#[test]
277+
fn test_generate_fac_definition_with_deps() {
278+
let allocator = Allocator::default();
279+
let type_expr = OutputExpression::ReadVar(Box::new_in(
280+
ReadVarExpr { name: Atom::from("TestDirective"), source_span: None },
281+
&allocator,
282+
));
283+
284+
let dep_token = OutputExpression::ReadVar(Box::new_in(
285+
ReadVarExpr { name: Atom::from("SomeService"), source_span: None },
286+
&allocator,
287+
));
288+
289+
let mut deps = Vec::new_in(&allocator);
290+
deps.push(crate::factory::R3DependencyMetadata::simple(dep_token));
291+
292+
let metadata = R3DirectiveMetadata {
293+
name: Atom::from("TestDirective"),
294+
r#type: type_expr,
295+
type_argument_count: 0,
296+
deps: Some(deps),
297+
selector: Some(Atom::from("[testDir]")),
298+
queries: Vec::new_in(&allocator),
299+
view_queries: Vec::new_in(&allocator),
300+
host: R3HostMetadata::new(&allocator),
301+
uses_on_changes: false,
302+
inputs: Vec::new_in(&allocator),
303+
outputs: Vec::new_in(&allocator),
304+
uses_inheritance: false,
305+
export_as: Vec::new_in(&allocator),
306+
providers: None,
307+
is_standalone: true,
308+
is_signal: false,
309+
host_directives: Vec::new_in(&allocator),
310+
};
311+
312+
let fac = generate_fac_definition(&allocator, &metadata);
313+
314+
let emitter = JsEmitter::new();
315+
let js = emitter.emit_expression(&fac);
316+
317+
// Should have directiveInject call (not just inject)
318+
assert!(js.contains("directiveInject"));
319+
assert!(js.contains("SomeService"));
320+
}
321+
322+
#[test]
323+
fn test_generate_dir_definition() {
324+
let allocator = Allocator::default();
325+
let metadata = create_test_metadata(&allocator);
326+
327+
let dir = generate_dir_definition(&allocator, &metadata);
328+
329+
let emitter = JsEmitter::new();
330+
let js = emitter.emit_expression(&dir);
331+
332+
assert!(js.contains("ɵɵdefineDirective"));
333+
assert!(js.contains("type"));
334+
assert!(js.contains("TestDirective"));
335+
assert!(js.contains("selectors"));
336+
assert!(js.contains("testDir"));
337+
}
338+
}

crates/oxc_angular_compiler/src/directive/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@
1313
//! - Lifecycle hooks
1414
1515
mod compiler;
16+
mod decorator;
17+
mod definition;
1618
mod metadata;
1719
mod property_decorators;
1820
mod query;
1921

2022
pub use compiler::{DirectiveCompileResult, compile_directive, compile_directive_from_metadata};
23+
pub use decorator::{extract_directive_metadata, find_directive_decorator_span};
24+
pub use definition::{DirectiveDefinitions, generate_directive_definitions};
2125
pub use metadata::{
2226
QueryPredicate, R3DirectiveMetadata, R3DirectiveMetadataBuilder, R3HostDirectiveMetadata,
2327
R3HostMetadata, R3InputMetadata, R3QueryMetadata,

0 commit comments

Comments
 (0)