Skip to content

Commit b3d139a

Browse files
ashh640claude
authored andcommitted
refactor(jit): consolidate member decorator extraction into single pass
Merge extract_jit_member_decorators and extract_non_angular_member_decorators into a single extract_all_jit_member_decorators function that iterates class members once. Extract ANGULAR_FIELD_DECORATORS and ANGULAR_DECORATOR_NAMES into module-level constants to eliminate duplication. https://claude.ai/code/session_01BbwLMsG3SjXcCbvDxAyW2Z
1 parent 8216cd7 commit b3d139a

1 file changed

Lines changed: 74 additions & 136 deletions

File tree

crates/oxc_angular_compiler/src/component/transform.rs

Lines changed: 74 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -828,38 +828,72 @@ fn extract_jit_ctor_params(
828828
params
829829
}
830830

831-
/// Extract Angular member decorators for JIT propDecorators generation.
831+
/// Angular field decorators that go into `static propDecorators`.
832+
/// Matches Angular's official `FIELD_DECORATORS` constant from `@angular/compiler-cli`.
833+
const ANGULAR_FIELD_DECORATORS: &[&str] = &[
834+
"Input",
835+
"Output",
836+
"HostBinding",
837+
"HostListener",
838+
"ViewChild",
839+
"ViewChildren",
840+
"ContentChild",
841+
"ContentChildren",
842+
];
843+
844+
/// All Angular decorator names from `@angular/core`.
845+
/// Any decorator with one of these names is treated as Angular and excluded from
846+
/// non-Angular `__decorate()` lowering. Angular identifies decorators by import source;
847+
/// we use names since they're unique to `@angular/core`.
848+
const ANGULAR_DECORATOR_NAMES: &[&str] = &[
849+
// Field decorators (→ propDecorators)
850+
"Input",
851+
"Output",
852+
"HostBinding",
853+
"HostListener",
854+
"ViewChild",
855+
"ViewChildren",
856+
"ContentChild",
857+
"ContentChildren",
858+
// Parameter decorators (→ ctorParameters)
859+
"Inject",
860+
"Optional",
861+
"Self",
862+
"SkipSelf",
863+
"Host",
864+
"Attribute",
865+
// Class decorators (→ class __decorate)
866+
"Component",
867+
"Directive",
868+
"Pipe",
869+
"Injectable",
870+
"NgModule",
871+
];
872+
873+
/// Extract all member decorators for JIT transformation in a single pass.
832874
///
833-
/// Collects all Angular-relevant decorators from class properties/methods
834-
/// (excluding constructor) so they can be emitted as a `static propDecorators` property.
835-
fn extract_jit_member_decorators(
875+
/// Returns two collections:
876+
/// - Angular field decorators → emitted as `static propDecorators = { ... }`
877+
/// - Non-Angular decorators → emitted as `__decorate([...], target, "name", desc)` calls
878+
fn extract_all_jit_member_decorators(
836879
source: &str,
837880
class: &oxc_ast::ast::Class<'_>,
838-
) -> std::vec::Vec<JitMemberDecorator> {
881+
) -> (std::vec::Vec<JitMemberDecorator>, std::vec::Vec<JitNonAngularMemberDecorator>) {
839882
use oxc_ast::ast::{ClassElement, MethodDefinitionKind, PropertyKey};
840883

841-
const ANGULAR_MEMBER_DECORATORS: &[&str] = &[
842-
"Input",
843-
"Output",
844-
"HostBinding",
845-
"HostListener",
846-
"ViewChild",
847-
"ViewChildren",
848-
"ContentChild",
849-
"ContentChildren",
850-
];
851-
852-
let mut result: std::vec::Vec<JitMemberDecorator> = std::vec::Vec::new();
884+
let mut angular_members: std::vec::Vec<JitMemberDecorator> = std::vec::Vec::new();
885+
let mut non_angular_members: std::vec::Vec<JitNonAngularMemberDecorator> =
886+
std::vec::Vec::new();
853887

854888
for element in &class.body.body {
855-
let (member_name, decorators) = match element {
889+
let (member_name, is_static, is_property, decorators) = match element {
856890
ClassElement::PropertyDefinition(prop) => {
857891
let name = match &prop.key {
858892
PropertyKey::StaticIdentifier(id) => id.name.to_string(),
859893
PropertyKey::StringLiteral(s) => s.value.to_string(),
860894
_ => continue,
861895
};
862-
(name, &prop.decorators)
896+
(name, prop.r#static, true, &prop.decorators)
863897
}
864898
ClassElement::MethodDefinition(method) => {
865899
if method.kind == MethodDefinitionKind::Constructor {
@@ -870,20 +904,21 @@ fn extract_jit_member_decorators(
870904
PropertyKey::StringLiteral(s) => s.value.to_string(),
871905
_ => continue,
872906
};
873-
(name, &method.decorators)
907+
(name, method.r#static, false, &method.decorators)
874908
}
875909
ClassElement::AccessorProperty(accessor) => {
876910
let name = match &accessor.key {
877911
PropertyKey::StaticIdentifier(id) => id.name.to_string(),
878912
PropertyKey::StringLiteral(s) => s.value.to_string(),
879913
_ => continue,
880914
};
881-
(name, &accessor.decorators)
915+
(name, accessor.r#static, false, &accessor.decorators)
882916
}
883917
_ => continue,
884918
};
885919

886920
let mut angular_decs: std::vec::Vec<JitParamDecorator> = std::vec::Vec::new();
921+
let mut non_angular_texts: std::vec::Vec<String> = std::vec::Vec::new();
887922

888923
for decorator in decorators {
889924
let (dec_name, call_args) = match &decorator.expression {
@@ -906,123 +941,28 @@ fn extract_jit_member_decorators(
906941
_ => continue,
907942
};
908943

909-
if ANGULAR_MEMBER_DECORATORS.contains(&dec_name.as_str()) {
944+
if ANGULAR_FIELD_DECORATORS.contains(&dec_name.as_str()) {
945+
// Angular field decorator → goes into propDecorators
910946
angular_decs.push(JitParamDecorator { name: dec_name, args: call_args });
947+
} else if !ANGULAR_DECORATOR_NAMES.contains(&dec_name.as_str()) {
948+
// Non-Angular decorator → goes into __decorate() call
949+
let expr_start = decorator.expression.span().start;
950+
let expr_end = decorator.expression.span().end;
951+
non_angular_texts.push(source[expr_start as usize..expr_end as usize].to_string());
911952
}
953+
// Angular non-field decorators (e.g. @Inject on a member) are silently dropped
954+
// since they have no meaningful effect on members.
912955
}
913956

914957
if !angular_decs.is_empty() {
915-
result.push(JitMemberDecorator { member_name, decorators: angular_decs });
916-
}
917-
}
918-
919-
result
920-
}
921-
922-
/// Extract non-Angular member decorators that need to be lowered via __decorate() calls.
923-
///
924-
/// These are decorators on methods/properties that are NOT Angular-specific
925-
/// (e.g., NGXS @Action, @Selector). They need to be emitted as:
926-
/// - `__decorate([Decorator()], Class.prototype, "method", null)` for instance members
927-
/// - `__decorate([Decorator()], Class, "method", null)` for static members
928-
fn extract_non_angular_member_decorators(
929-
source: &str,
930-
class: &oxc_ast::ast::Class<'_>,
931-
) -> std::vec::Vec<JitNonAngularMemberDecorator> {
932-
use oxc_ast::ast::{ClassElement, MethodDefinitionKind, PropertyKey};
933-
934-
// All Angular decorators that should NOT be lowered via __decorate().
935-
// This includes field decorators (handled via propDecorators), parameter decorators
936-
// (handled via ctorParameters), and class decorators (handled via class __decorate).
937-
// Angular identifies these by import source (@angular/core); we use names since
938-
// they're unique enough and matches the official FIELD_DECORATORS list.
939-
const ANGULAR_DECORATORS: &[&str] = &[
940-
// Field decorators (go into propDecorators)
941-
"Input",
942-
"Output",
943-
"HostBinding",
944-
"HostListener",
945-
"ViewChild",
946-
"ViewChildren",
947-
"ContentChild",
948-
"ContentChildren",
949-
// Parameter decorators (go into ctorParameters, but could appear on members)
950-
"Inject",
951-
"Optional",
952-
"Self",
953-
"SkipSelf",
954-
"Host",
955-
"Attribute",
956-
// Class decorators (shouldn't appear on members, but exclude defensively)
957-
"Component",
958-
"Directive",
959-
"Pipe",
960-
"Injectable",
961-
"NgModule",
962-
];
963-
964-
let mut result: std::vec::Vec<JitNonAngularMemberDecorator> = std::vec::Vec::new();
965-
966-
for element in &class.body.body {
967-
let (member_name, is_static, is_property, decorators) = match element {
968-
ClassElement::PropertyDefinition(prop) => {
969-
let name = match &prop.key {
970-
PropertyKey::StaticIdentifier(id) => id.name.to_string(),
971-
PropertyKey::StringLiteral(s) => s.value.to_string(),
972-
_ => continue,
973-
};
974-
(name, prop.r#static, true, &prop.decorators)
975-
}
976-
ClassElement::MethodDefinition(method) => {
977-
if method.kind == MethodDefinitionKind::Constructor {
978-
continue;
979-
}
980-
let name = match &method.key {
981-
PropertyKey::StaticIdentifier(id) => id.name.to_string(),
982-
PropertyKey::StringLiteral(s) => s.value.to_string(),
983-
_ => continue,
984-
};
985-
(name, method.r#static, false, &method.decorators)
986-
}
987-
ClassElement::AccessorProperty(accessor) => {
988-
let name = match &accessor.key {
989-
PropertyKey::StaticIdentifier(id) => id.name.to_string(),
990-
PropertyKey::StringLiteral(s) => s.value.to_string(),
991-
_ => continue,
992-
};
993-
(name, accessor.r#static, false, &accessor.decorators)
994-
}
995-
_ => continue,
996-
};
997-
998-
let mut non_angular_texts: std::vec::Vec<String> = std::vec::Vec::new();
999-
1000-
for decorator in decorators {
1001-
let dec_name = match &decorator.expression {
1002-
Expression::CallExpression(call) => match &call.callee {
1003-
Expression::Identifier(id) => Some(id.name.to_string()),
1004-
Expression::StaticMemberExpression(m) => Some(m.property.name.to_string()),
1005-
_ => None,
1006-
},
1007-
Expression::Identifier(id) => Some(id.name.to_string()),
1008-
_ => None,
1009-
};
1010-
1011-
let is_angular = dec_name
1012-
.as_ref()
1013-
.is_some_and(|n| ANGULAR_DECORATORS.contains(&n.as_str()));
1014-
1015-
if !is_angular {
1016-
// Extract the decorator expression text from source (without the @)
1017-
let expr_start = decorator.expression.span().start;
1018-
let expr_end = decorator.expression.span().end;
1019-
let text = source[expr_start as usize..expr_end as usize].to_string();
1020-
non_angular_texts.push(text);
1021-
}
958+
angular_members.push(JitMemberDecorator {
959+
member_name: member_name.clone(),
960+
decorators: angular_decs,
961+
});
1022962
}
1023963

1024964
if !non_angular_texts.is_empty() {
1025-
result.push(JitNonAngularMemberDecorator {
965+
non_angular_members.push(JitNonAngularMemberDecorator {
1026966
member_name,
1027967
is_static,
1028968
is_property,
@@ -1031,7 +971,7 @@ fn extract_non_angular_member_decorators(
1031971
}
1032972
}
1033973

1034-
result
974+
(angular_members, non_angular_members)
1035975
}
1036976

1037977
/// Build the propDecorators static property text for JIT member decorator metadata.
@@ -1353,11 +1293,9 @@ fn transform_angular_file_jit(
13531293
// Extract constructor parameters for ctorParameters
13541294
let ctor_params = extract_jit_ctor_params(source, class);
13551295

1356-
// Extract Angular member decorators for propDecorators
1357-
let member_decorators = extract_jit_member_decorators(source, class);
1358-
1359-
// Extract non-Angular member decorators for __decorate() calls
1360-
let non_angular_member_decorators = extract_non_angular_member_decorators(source, class);
1296+
// Extract Angular and non-Angular member decorators
1297+
let (member_decorators, non_angular_member_decorators) =
1298+
extract_all_jit_member_decorators(source, class);
13611299

13621300
jit_classes.push(JitClassInfo {
13631301
class_name,

0 commit comments

Comments
 (0)