@@ -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