@@ -1302,29 +1302,6 @@ fn extract_host_metadata_input(
13021302 input
13031303}
13041304
1305- /// In the defineComponent output, we just need the type references:
1306- /// ```javascript
1307- /// dependencies: [RouterOutlet]
1308- /// ```
1309- fn extract_dependency_types (
1310- arr : & oxc_ast:: ast:: ArrayExpression < ' _ > ,
1311- source : & str ,
1312- ) -> Option < String > {
1313- let mut types: Vec < String > = Vec :: new ( ) ;
1314- for el in & arr. elements {
1315- let expr = match el {
1316- ArrayExpressionElement :: SpreadElement ( _) => continue ,
1317- _ => el. to_expression ( ) ,
1318- } ;
1319- if let Expression :: ObjectExpression ( obj) = expr
1320- && let Some ( type_src) = get_property_source ( obj. as_ref ( ) , "type" , source)
1321- {
1322- types. push ( type_src. to_string ( ) ) ;
1323- }
1324- }
1325- if types. is_empty ( ) { None } else { Some ( format ! ( "[{}]" , types. join( ", " ) ) ) }
1326- }
1327-
13281305/// Build a query function (contentQueries or viewQuery) from query metadata.
13291306///
13301307/// Content query metadata format:
@@ -1634,11 +1611,73 @@ fn link_component(
16341611 // 17. template (reference to the compiled function)
16351612 parts. push ( format ! ( "template: {}" , template_output. template_fn_name) ) ;
16361613
1637- // 18. dependencies (extract type references from dependency objects)
1638- if let Some ( deps_arr) = get_array_property ( meta, "dependencies" )
1639- && let Some ( deps_str) = extract_dependency_types ( deps_arr, source)
1614+ // 18. dependencies — support both new-style (v14+) and old-style (v12-v13) fields
16401615 {
1641- parts. push ( format ! ( "dependencies: {deps_str}" ) ) ;
1616+ let capacity = get_array_property ( meta, "dependencies" ) . map_or ( 0 , |a| a. elements . len ( ) )
1617+ + get_array_property ( meta, "directives" ) . map_or ( 0 , |a| a. elements . len ( ) )
1618+ + get_array_property ( meta, "components" ) . map_or ( 0 , |a| a. elements . len ( ) )
1619+ + get_object_property ( meta, "pipes" ) . map_or ( 0 , |o| o. properties . len ( ) ) ;
1620+ let mut dep_types: Vec < String > = Vec :: with_capacity ( capacity) ;
1621+
1622+ // New style: unified `dependencies` array (v14+)
1623+ if let Some ( deps_arr) = get_array_property ( meta, "dependencies" ) {
1624+ for el in & deps_arr. elements {
1625+ let expr = match el {
1626+ ArrayExpressionElement :: SpreadElement ( _) => continue ,
1627+ _ => el. to_expression ( ) ,
1628+ } ;
1629+ if let Expression :: ObjectExpression ( obj) = expr
1630+ && let Some ( type_src) = get_property_source ( obj. as_ref ( ) , "type" , source)
1631+ {
1632+ dep_types. push ( type_src. to_string ( ) ) ;
1633+ }
1634+ }
1635+ }
1636+
1637+ // Old style: separate `directives` array (v12-v13)
1638+ if let Some ( dirs_arr) = get_array_property ( meta, "directives" ) {
1639+ for el in & dirs_arr. elements {
1640+ let expr = match el {
1641+ ArrayExpressionElement :: SpreadElement ( _) => continue ,
1642+ _ => el. to_expression ( ) ,
1643+ } ;
1644+ if let Expression :: ObjectExpression ( obj) = expr
1645+ && let Some ( type_src) = get_property_source ( obj. as_ref ( ) , "type" , source)
1646+ {
1647+ dep_types. push ( type_src. to_string ( ) ) ;
1648+ }
1649+ }
1650+ }
1651+
1652+ // Old style: separate `components` array (v12-v13)
1653+ if let Some ( comps_arr) = get_array_property ( meta, "components" ) {
1654+ for el in & comps_arr. elements {
1655+ let expr = match el {
1656+ ArrayExpressionElement :: SpreadElement ( _) => continue ,
1657+ _ => el. to_expression ( ) ,
1658+ } ;
1659+ if let Expression :: ObjectExpression ( obj) = expr
1660+ && let Some ( type_src) = get_property_source ( obj. as_ref ( ) , "type" , source)
1661+ {
1662+ dep_types. push ( type_src. to_string ( ) ) ;
1663+ }
1664+ }
1665+ }
1666+
1667+ // Old style: `pipes` object { pipeName: PipeType, ... } (v12-v13)
1668+ if let Some ( pipes_obj) = get_object_property ( meta, "pipes" ) {
1669+ for prop in & pipes_obj. properties {
1670+ if let ObjectPropertyKind :: ObjectProperty ( p) = prop {
1671+ let span = p. value . span ( ) ;
1672+ let type_src = & source[ span. start as usize ..span. end as usize ] ;
1673+ dep_types. push ( type_src. to_string ( ) ) ;
1674+ }
1675+ }
1676+ }
1677+
1678+ if !dep_types. is_empty ( ) {
1679+ parts. push ( format ! ( "dependencies: [{}]" , dep_types. join( ", " ) ) ) ;
1680+ }
16421681 }
16431682
16441683 // 19-20. styles + encapsulation (interdependent)
@@ -3536,4 +3575,85 @@ MyService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
35363575 result. code
35373576 ) ;
35383577 }
3578+
3579+ /// Issue #88: Old-style `directives` field (v12-v13) should be extracted into `dependencies`
3580+ #[ test]
3581+ fn test_link_component_v12_with_old_style_directives ( ) {
3582+ let allocator = Allocator :: default ( ) ;
3583+ let code = r#"
3584+ import * as i0 from "@angular/core";
3585+ import * as i1 from "@angular/common";
3586+ class OverlayPanel {
3587+ }
3588+ OverlayPanel.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.0.5", ngImport: i0, type: OverlayPanel, selector: "p-overlayPanel", template: "<div *ngIf=\"render\">Hello</div>", directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
3589+ "# ;
3590+ let result = link ( & allocator, code, "test.mjs" ) ;
3591+ assert ! ( result. linked, "Should be linked" ) ;
3592+ assert ! (
3593+ result. code. contains( "dependencies: [i1.NgIf, i1.NgClass]" ) ,
3594+ "Should extract directive types into dependencies array, got:\n {}" ,
3595+ result. code
3596+ ) ;
3597+ }
3598+
3599+ /// Issue #88: Old-style `components` field (v12-v13) should be extracted into `dependencies`
3600+ #[ test]
3601+ fn test_link_component_v12_with_old_style_components ( ) {
3602+ let allocator = Allocator :: default ( ) ;
3603+ let code = r#"
3604+ import * as i0 from "@angular/core";
3605+ import * as i1 from "./child";
3606+ class ParentComponent {
3607+ }
3608+ ParentComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.0.5", ngImport: i0, type: ParentComponent, selector: "app-parent", template: "<my-child></my-child>", components: [{ type: i1.ChildComponent, selector: "my-child" }] });
3609+ "# ;
3610+ let result = link ( & allocator, code, "test.mjs" ) ;
3611+ assert ! ( result. linked, "Should be linked" ) ;
3612+ assert ! (
3613+ result. code. contains( "dependencies: [i1.ChildComponent]" ) ,
3614+ "Should extract component types into dependencies array, got:\n {}" ,
3615+ result. code
3616+ ) ;
3617+ }
3618+
3619+ /// Issue #88: Old-style `pipes` object (v12-v13) should be extracted into `dependencies`
3620+ #[ test]
3621+ fn test_link_component_v12_with_old_style_pipes ( ) {
3622+ let allocator = Allocator :: default ( ) ;
3623+ let code = r#"
3624+ import * as i0 from "@angular/core";
3625+ import * as i1 from "@angular/common";
3626+ class MyComponent {
3627+ }
3628+ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.0.5", ngImport: i0, type: MyComponent, selector: "my-comp", template: "<div>{{ value | async }}</div>", pipes: { async: i1.AsyncPipe } });
3629+ "# ;
3630+ let result = link ( & allocator, code, "test.mjs" ) ;
3631+ assert ! ( result. linked, "Should be linked" ) ;
3632+ assert ! (
3633+ result. code. contains( "dependencies: [i1.AsyncPipe]" ) ,
3634+ "Should extract pipe types into dependencies array, got:\n {}" ,
3635+ result. code
3636+ ) ;
3637+ }
3638+
3639+ /// Issue #88: Mixed old-style fields — directives + components + pipes combined
3640+ #[ test]
3641+ fn test_link_component_v12_with_mixed_old_style_deps ( ) {
3642+ let allocator = Allocator :: default ( ) ;
3643+ let code = r#"
3644+ import * as i0 from "@angular/core";
3645+ import * as i1 from "@angular/common";
3646+ import * as i2 from "./child";
3647+ class MyComponent {
3648+ }
3649+ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.0.5", ngImport: i0, type: MyComponent, selector: "my-comp", template: "<div *ngIf=\"x\"><my-child></my-child>{{ val | async }}</div>", directives: [{ type: i1.NgIf, selector: "[ngIf]" }], components: [{ type: i2.ChildComponent, selector: "my-child" }], pipes: { async: i1.AsyncPipe } });
3650+ "# ;
3651+ let result = link ( & allocator, code, "test.mjs" ) ;
3652+ assert ! ( result. linked, "Should be linked" ) ;
3653+ assert ! (
3654+ result. code. contains( "dependencies: [i1.NgIf, i2.ChildComponent, i1.AsyncPipe]" ) ,
3655+ "Should extract all dependency types into dependencies array, got:\n {}" ,
3656+ result. code
3657+ ) ;
3658+ }
35393659}
0 commit comments