Skip to content

Commit b1141ac

Browse files
Brooooooklynclaude
andauthored
fix: i18n property bindings should use Bindings marker, not I18n marker in consts (#6)
Angular's compiler never produces I18n AttributeMarker (6) in consts arrays. The previous override in attribute_extraction converted i18n property bindings to BindingKind::I18n, creating duplicate consts entries that shifted all subsequent const indices. This fixes 8 mismatched files (~396 numeric diffs). Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 9797271 commit b1141ac

2 files changed

Lines changed: 38 additions & 33 deletions

File tree

crates/oxc_angular_compiler/src/pipeline/phases/attribute_extraction.rs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -187,22 +187,14 @@ fn process_view_attributes<'a>(
187187
continue;
188188
}
189189

190-
// Determine the extracted binding kind.
191-
// Ported from Angular's attribute_extraction.ts lines 32-39:
192-
// if (op.i18nMessage !== null && op.templateKind === null) {
193-
// bindingKind = ir.BindingKind.I18n;
194-
// } else if (op.isStructuralTemplateAttribute) {
195-
// bindingKind = ir.BindingKind.Template;
196-
// } else {
197-
// bindingKind = ir.BindingKind.Property;
198-
// }
199-
let binding_kind = if prop_op.i18n_message.is_some()
200-
&& prop_op.binding_kind != BindingKind::Template
201-
{
202-
BindingKind::I18n
203-
} else {
204-
prop_op.binding_kind
205-
};
190+
// Use the binding kind from the property op directly.
191+
// Angular's attribute_extraction.ts has a condition:
192+
// if (op.i18nMessage !== null && op.templateKind === null)
193+
// that sets bindingKind to I18n, but empirically the Angular
194+
// compiler never produces I18n AttributeMarker (6) in consts
195+
// arrays. The templateKind guard (which OXC's PropertyOp lacks)
196+
// prevents it from triggering in practice.
197+
let binding_kind = prop_op.binding_kind;
206198

207199
// Properties also generate extracted attributes for directive matching
208200
// Note: Property ops are NOT removed - they still need runtime updates

crates/oxc_angular_compiler/tests/integration_test.rs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3743,13 +3743,14 @@ fn test_let_declaration_with_multiple_context_refs_variable_naming() {
37433743
// Const reference index: i18n property binding extraction
37443744
// ============================================================================
37453745

3746-
/// Tests that property bindings with i18n markers are extracted as BindingKind::I18n
3747-
/// in the consts array. Angular's attribute_extraction.ts checks `op.i18nMessage !== null`
3748-
/// on Property ops and converts them to BindingKind.I18n. Without this, the const entry
3749-
/// would be `[3, "heading"]` (Bindings marker) instead of `[6, "heading"]` (I18n marker),
3750-
/// causing const index mismatches.
3751-
#[test]
3752-
fn test_i18n_property_binding_extracted_as_i18n_kind() {
3746+
/// Tests that property bindings with i18n markers are extracted as BindingKind::Property
3747+
/// in the consts array. Angular's attribute_extraction.ts has a condition
3748+
/// `op.i18nMessage !== null && op.templateKind === null` that would produce I18n kind,
3749+
/// but empirically Angular never produces I18n marker (6) in consts arrays across all
3750+
/// tested components. The i18n metadata is handled by the i18n pipeline separately.
3751+
/// The property binding should use Bindings marker (3) for directive matching.
3752+
#[test]
3753+
fn test_i18n_property_binding_extracted_as_property_kind() {
37533754
let allocator = Allocator::default();
37543755
let source = r#"
37553756
import { Component } from '@angular/core';
@@ -3772,20 +3773,26 @@ export class TestComponent {
37723773
None,
37733774
);
37743775

3775-
// The consts array should contain [6,"heading"] (AttributeMarker.I18n = 6)
3776-
// not [3,"heading"] (AttributeMarker.Bindings = 3)
3776+
// The consts array should contain [3,"heading"] (AttributeMarker.Bindings = 3)
3777+
// Angular never produces [6,"heading"] (AttributeMarker.I18n = 6) in consts arrays.
3778+
assert!(
3779+
result.code.contains(r#"3,"heading""#),
3780+
"Property binding with i18n marker should produce Bindings AttributeMarker (3), not I18n (6). Output:\n{}",
3781+
result.code
3782+
);
37773783
assert!(
3778-
result.code.contains(r#"6,"heading""#),
3779-
"Property binding with i18n marker should produce I18n AttributeMarker (6), not Bindings (3). Output:\n{}",
3784+
!result.code.contains(r#"6,"heading""#),
3785+
"Property binding with i18n marker should NOT produce I18n AttributeMarker (6). Output:\n{}",
37803786
result.code
37813787
);
37823788
}
37833789

37843790
/// Tests that interpolated attributes with i18n markers (e.g., heading="{{ name }}" i18n-heading)
3785-
/// are extracted as BindingKind::I18n in the consts array.
3791+
/// are extracted as BindingKind::Property (Bindings marker 3), not I18n marker 6.
3792+
/// Angular's compiler never produces I18n AttributeMarker (6) in consts arrays.
37863793
/// This matches the real-world pattern in ClickUp's old-join-team component.
37873794
#[test]
3788-
fn test_i18n_interpolated_attribute_extracted_as_i18n_kind() {
3795+
fn test_i18n_interpolated_attribute_extracted_as_property_kind() {
37893796
let allocator = Allocator::default();
37903797
let source = r#"
37913798
import { Component } from '@angular/core';
@@ -3808,11 +3815,17 @@ export class TestComponent {
38083815
None,
38093816
);
38103817

3811-
// The consts array should contain [6,"heading"] (AttributeMarker.I18n = 6)
3812-
// not [3,"heading"] (AttributeMarker.Bindings = 3)
3818+
// The consts array should contain [3,"heading"] (AttributeMarker.Bindings = 3)
3819+
// not [6,"heading"] (AttributeMarker.I18n = 6)
3820+
// Angular's compiler never produces I18n marker in consts arrays.
3821+
assert!(
3822+
result.code.contains(r#"3,"heading""#),
3823+
"Interpolated attribute with i18n marker should produce Bindings AttributeMarker (3), not I18n (6). Output:\n{}",
3824+
result.code
3825+
);
38133826
assert!(
3814-
result.code.contains(r#"6,"heading""#),
3815-
"Interpolated attribute with i18n marker should produce I18n AttributeMarker (6), not Bindings (3). Output:\n{}",
3827+
!result.code.contains(r#"6,"heading""#),
3828+
"Interpolated attribute with i18n marker should NOT produce I18n AttributeMarker (6). Output:\n{}",
38163829
result.code
38173830
);
38183831
}

0 commit comments

Comments
 (0)