11import type { Bom } from '$lib/cyclonedx/models' ;
2- import type { TreeNode } from 'carbon-components-svelte/src/TreeView/TreeView.svelte' ;
3-
4- // Used for the tree chart
5- export interface TreeChartItem {
6- name : string ;
7- value ?: string ;
8- children ?: TreeChartItem [ ] ;
9- }
10-
11- // The combined type that can be used for the TreeView and the TreeChart
12- export type TreeItem = TreeChartItem & TreeNode ;
13-
14- class TreeItemImpl implements TreeItem {
15- private readonly _name : string ;
16- private readonly _ref : string ;
17- private readonly _children : TreeItem [ ] | undefined ;
18-
19- constructor ( name : string , ref : string , children ?: TreeItem [ ] ) {
20- this . _name = name ;
21- this . _ref = ref ;
22- this . _children = children && children . length > 0 ? children : undefined ;
23- }
24-
25- get name ( ) {
26- return this . _name ;
27- }
28-
29- get text ( ) {
30- return this . _name ;
31- }
32-
33- get id ( ) {
34- return this . _ref ;
35- }
36-
37- get value ( ) {
38- return this . _ref ;
39- }
40-
41- get children ( ) {
42- return this . _children ;
43- }
44-
45- get nodes ( ) {
46- return this . _children ;
47- }
48-
49- get icon ( ) {
50- return undefined ;
51- }
52-
53- get disabled ( ) {
54- return undefined ;
55- }
56- }
2+ import { type TreeItem , TreeItemImpl } from '$lib/models/tree' ;
573
584export function createTreeDataFromBom ( bom : Bom ) : TreeItem [ ] {
595 const componentRefToName = new Map < string , string > ( ) ;
@@ -73,27 +19,33 @@ export function createTreeDataFromBom(bom: Bom): TreeItem[] {
7319
7420 const subject = bom . metadata ?. component ;
7521
76- function getChildTreeItems ( componentRef : string ) {
22+ function getChildTreeItems ( componentRef : string , visited : Set < string > ) : TreeItem [ ] {
23+ // If we've already visited this component, return an empty array to prevent cycles
24+ if ( visited . has ( componentRef ) ) {
25+ console . warn ( `Cycle detected for ${ componentRef } ` ) ;
26+ return [ ] ;
27+ }
28+
29+ // Mark the current component as visited
30+ visited . add ( componentRef ) ;
31+
7732 return ( dependencyMap . get ( componentRef ) ?? [ ] )
7833 . map ( ( child ) => {
7934 if ( componentRefToName . has ( child ) ) {
80- return getTreeItem ( child , componentRefToName . get ( child ) ! ) ;
35+ return new TreeItemImpl (
36+ componentRefToName . get ( child ) ! ,
37+ child ,
38+ getChildTreeItems ( child , new Set ( visited ) )
39+ ) ;
8140 } else {
8241 return null ;
8342 }
8443 } )
85- . filter ( ( child ) => child !== null ) ;
44+ . filter ( ( child ) => child !== null ) as TreeItem [ ] ;
8645 }
8746
88- const getTreeItem : ( componentRef : string , componentName : string ) => TreeItem = (
89- componentRef ,
90- componentName
91- ) => {
92- return new TreeItemImpl ( componentName , componentRef , getChildTreeItems ( componentRef ) ) ;
93- } ;
94-
9547 if ( subject && subject [ 'bom-ref' ] ) {
96- data . push ( ...getChildTreeItems ( subject [ 'bom-ref' ] ) ) ;
48+ data . push ( ...getChildTreeItems ( subject [ 'bom-ref' ] , new Set < string > ( ) ) ) ;
9749 } else {
9850 console . error ( `No subject found in ${ bom . metadata } ` ) ;
9951 }
0 commit comments