Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions crates/oxc_angular_compiler/src/directive/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,15 +387,18 @@ fn extract_param_dependency<'a>(

// Handle @Attribute decorator
if let Some(attr_name) = attribute_name {
let name = OutputExpression::Literal(Box::new_in(
crate::output::ast::LiteralExpr {
value: crate::output::ast::LiteralValue::String(attr_name),
source_span: None,
},
&allocator,
));
// ngtsc's `attributeNameType` is the attribute name itself (di.ts), so it is
// set whatever the parameter's type and compiles to `ɵɵinjectAttribute`.
return R3DependencyMetadata {
token: Some(OutputExpression::Literal(Box::new_in(
crate::output::ast::LiteralExpr {
value: crate::output::ast::LiteralValue::String(attr_name),
source_span: None,
},
&allocator,
))),
attribute_name_type: token, // The type annotation
token: Some(name.clone_in(allocator)),
attribute_name_type: Some(name),
host,
optional,
self_,
Expand Down
19 changes: 11 additions & 8 deletions crates/oxc_angular_compiler/src/injectable/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,15 +651,18 @@ fn extract_param_dependency<'a>(

// Handle @Attribute decorator
if let Some(attr_name) = attribute_name {
let name = OutputExpression::Literal(Box::new_in(
crate::output::ast::LiteralExpr {
value: crate::output::ast::LiteralValue::String(attr_name),
source_span: None,
},
&allocator,
));
// ngtsc's `attributeNameType` is the attribute name itself (di.ts), so it is
// set whatever the parameter's type and compiles to `ɵɵinjectAttribute`.
return R3DependencyMetadata {
token: Some(OutputExpression::Literal(Box::new_in(
crate::output::ast::LiteralExpr {
value: crate::output::ast::LiteralValue::String(attr_name),
source_span: None,
},
&allocator,
))),
attribute_name_type: token, // The type annotation
token: Some(name.clone_in(allocator)),
attribute_name_type: Some(name),
host,
optional,
self_,
Expand Down
19 changes: 11 additions & 8 deletions crates/oxc_angular_compiler/src/ng_module/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,15 +584,18 @@ fn extract_param_dependency<'a>(

// Handle @Attribute decorator
if let Some(attr_name) = attribute_name {
let name = OutputExpression::Literal(Box::new_in(
crate::output::ast::LiteralExpr {
value: crate::output::ast::LiteralValue::String(attr_name),
source_span: None,
},
&allocator,
));
// ngtsc's `attributeNameType` is the attribute name itself (di.ts), so it is
// set whatever the parameter's type and compiles to `ɵɵinjectAttribute`.
return R3DependencyMetadata {
token: Some(OutputExpression::Literal(Box::new_in(
crate::output::ast::LiteralExpr {
value: crate::output::ast::LiteralValue::String(attr_name),
source_span: None,
},
&allocator,
))),
attribute_name_type: token, // The type annotation
token: Some(name.clone_in(allocator)),
attribute_name_type: Some(name),
host,
optional,
self_,
Expand Down
19 changes: 11 additions & 8 deletions crates/oxc_angular_compiler/src/pipe/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,15 +349,18 @@ fn extract_param_dependency<'a>(

// Handle @Attribute decorator
if let Some(attr_name) = attribute_name {
let name = OutputExpression::Literal(Box::new_in(
crate::output::ast::LiteralExpr {
value: crate::output::ast::LiteralValue::String(attr_name),
source_span: None,
},
&allocator,
));
// ngtsc's `attributeNameType` is the attribute name itself (di.ts), so it is
// set whatever the parameter's type and compiles to `ɵɵinjectAttribute`.
return R3DependencyMetadata {
token: Some(OutputExpression::Literal(Box::new_in(
crate::output::ast::LiteralExpr {
value: crate::output::ast::LiteralValue::String(attr_name),
source_span: None,
},
&allocator,
))),
attribute_name_type: token, // The type annotation
token: Some(name.clone_in(allocator)),
attribute_name_type: Some(name),
host,
optional,
self_,
Expand Down
88 changes: 88 additions & 0 deletions crates/oxc_angular_compiler/tests/attribute_inject_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! `@Attribute('name')` on a constructor parameter compiles to
//! `ɵɵinjectAttribute('name')` whatever the class kind, as ngtsc does
//! (`compileInjectDependency` in `r3_factory.ts` checks `attributeNameType`
//! before the target). A plain token inject makes Angular look the attribute
//! name up as a DI token at runtime.

use oxc_allocator::Allocator;
use oxc_angular_compiler::{TransformOptions, transform_angular_file};

/// The compiled code for `source`, without whitespace and with `'` as `"`.
fn compile(source: &str) -> String {
let allocator = Allocator::default();
let result = transform_angular_file(
&allocator,
"test.ts",
source,
Some(&TransformOptions::default()),
None,
);
assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics);
result
.code
.chars()
.filter(|c| !c.is_whitespace())
.map(|c| if c == '\'' { '"' } else { c })
.collect()
}

fn assert_injects_attribute(code: &str) {
assert!(code.contains(r#"(i0.ɵɵinjectAttribute("title"))"#), "Got:\n{code}");
}

#[test]
fn directive_factory_injects_the_attribute() {
let code = compile(
r"
import { Attribute, Directive } from '@angular/core';
@Directive({ selector: '[d]' })
export class D {
constructor(@Attribute('title') title: string) {}
}
",
);
assert_injects_attribute(&code);
}

#[test]
fn pipe_factory_injects_the_attribute() {
let code = compile(
r"
import { Attribute, Pipe } from '@angular/core';
@Pipe({ name: 'p' })
export class P {
constructor(@Attribute('title') title: string) {}
transform(v: unknown) { return v; }
}
",
);
assert_injects_attribute(&code);
}

#[test]
fn injectable_factory_injects_the_attribute() {
let code = compile(
r"
import { Attribute, Injectable } from '@angular/core';
@Injectable()
export class S {
constructor(@Attribute('title') title: string) {}
}
",
);
assert_injects_attribute(&code);
}

#[test]
fn ng_module_factory_injects_the_attribute() {
let code = compile(
r"
import { Attribute, NgModule } from '@angular/core';
@NgModule({})
export class M {
constructor(@Attribute('title') title: string) {}
}
",
);
assert_injects_attribute(&code);
}