Skip to content

Commit 7ca88d4

Browse files
authored
fix(linker): update linker to support transform function (#198)
fix issue-187
1 parent f9c0863 commit 7ca88d4

File tree

3 files changed

+57
-21
lines changed

3 files changed

+57
-21
lines changed

crates/oxc_angular_compiler/src/linker/mod.rs

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,7 @@ fn link_class_metadata_async(
12211221
/// Declaration format (`ɵɵngDeclareDirective`):
12221222
/// - `propertyName: "publicName"` (simple)
12231223
/// - `propertyName: ["publicName", "classPropertyName"]` (aliased)
1224+
/// - `propertyName: ["publicName", "classPropertyName", transformFn]` (aliased with transform)
12241225
/// - `propertyName: { classPropertyName: "...", publicName: "...", isRequired: bool,
12251226
/// isSignal: bool, transformFunction: expr }` (Angular 16+ object format)
12261227
///
@@ -1256,28 +1257,27 @@ fn convert_inputs_to_definition_format(inputs_obj: &ObjectExpression<'_>, source
12561257
// Array: check if it's declaration format [publicName, classPropertyName]
12571258
// and convert to definition format [InputFlags, publicName, classPropertyName]
12581259
Expression::ArrayExpression(arr) => {
1259-
if arr.elements.len() == 2 {
1260-
// Check if first element is a string (declaration format)
1261-
let first_is_string = matches!(
1262-
arr.elements.first(),
1263-
Some(ArrayExpressionElement::StringLiteral(_))
1264-
);
1265-
if first_is_string {
1266-
// Declaration format: ["publicName", "classPropertyName"]
1267-
// Convert to: [0, "publicName", "classPropertyName"]
1268-
let arr_source =
1269-
&source[arr.span.start as usize + 1..arr.span.end as usize - 1];
1270-
entries.push(format!("{quoted_key}: [0, {arr_source}]"));
1271-
} else {
1272-
// Already in definition format or unknown, keep as is
1273-
let val =
1274-
&source[p.value.span().start as usize..p.value.span().end as usize];
1275-
entries.push(format!("{quoted_key}: {val}"));
1276-
}
1277-
} else {
1278-
// 3+ elements likely already in definition format, keep as is
1279-
let val = &source[p.value.span().start as usize..p.value.span().end as usize];
1260+
let val = &source[p.value.span().start as usize..p.value.span().end as usize];
1261+
let first_is_string =
1262+
matches!(arr.elements.first(), Some(ArrayExpressionElement::StringLiteral(_)));
1263+
1264+
if !first_is_string {
1265+
// Already in definition format or unknown, keep as is.
12801266
entries.push(format!("{quoted_key}: {val}"));
1267+
continue;
1268+
}
1269+
1270+
let arr_source = &source[arr.span.start as usize + 1..arr.span.end as usize - 1];
1271+
match arr.elements.len() {
1272+
// Declaration format: ["publicName", "classPropertyName"]
1273+
// Convert to: [0, "publicName", "classPropertyName"]
1274+
2 => entries.push(format!("{quoted_key}: [0, {arr_source}]")),
1275+
// Since Angular 16.1, support multi-directive inputs, so we need to convert to the definition format.
1276+
// Declaration format: ["publicName", "classPropertyName", transformFn]
1277+
// Convert to: [2, "publicName", "classPropertyName", transformFn]
1278+
3 => entries.push(format!("{quoted_key}: [2, {arr_source}]")),
1279+
// 4+ elements likely already in definition format, keep as is.
1280+
_ => entries.push(format!("{quoted_key}: {val}")),
12811281
}
12821282
}
12831283
// Object: Angular 16+ format with classPropertyName, publicName, isRequired, etc.
@@ -2439,6 +2439,26 @@ RxFor.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.1
24392439
);
24402440
}
24412441

2442+
#[test]
2443+
fn test_link_directive_input_array_with_transform() {
2444+
let allocator = Allocator::default();
2445+
let code = r#"
2446+
import * as i0 from "@angular/core";
2447+
class MyDirective {
2448+
}
2449+
MyDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "20.0.0", ngImport: i0, type: MyDirective, selector: "[myDir]", inputs: { push: ["cdkConnectedOverlayPush", "push", i0.booleanAttribute] } });
2450+
"#;
2451+
let result = link(&allocator, code, "test.mjs");
2452+
assert!(result.linked);
2453+
assert!(
2454+
result
2455+
.code
2456+
.contains(r#"push: [2, "cdkConnectedOverlayPush", "push", i0.booleanAttribute]"#),
2457+
"Expected transform input array to be converted with InputFlags.HasDecoratorInputTransform. Got: {}",
2458+
result.code
2459+
);
2460+
}
2461+
24422462
#[test]
24432463
fn test_link_component_basic() {
24442464
let allocator = Allocator::default();

crates/oxc_angular_compiler/tests/linker_test.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,12 @@ fn test_link_outputs_simple_identifier() {
8686
let result = link(&allocator, &code, "test.mjs");
8787
insta::assert_snapshot!(result.code);
8888
}
89+
90+
#[test]
91+
fn test_link_inputs_array_format_with_transform_function() {
92+
let allocator = Allocator::default();
93+
let code =
94+
make_directive_source(r#"push: ["cdkConnectedOverlayPush", "push", i0.booleanAttribute]"#);
95+
let result = link(&allocator, &code, "test.mjs");
96+
insta::assert_snapshot!(result.code);
97+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
source: crates/oxc_angular_compiler/tests/linker_test.rs
3+
expression: result.code
4+
---
5+
import * as i0 from "@angular/core";
6+
export class MyDir {}
7+
MyDirdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], inputs: { push: [2, "cdkConnectedOverlayPush", "push", i0.booleanAttribute] }, standalone: false });

0 commit comments

Comments
 (0)