@cyclonedx/cdxgen 9.2.1 → 9.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,39 +12,39 @@ CycloneDX 1.5 specification is brand new and unsupported by many downstream tool
12
12
 
13
13
  ## Supported languages and package format
14
14
 
15
- | Language/Platform | Package format | Transitive dependencies |
16
- | ------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
17
- | node.js | npm-shrinkwrap.json, package-lock.json, pnpm-lock.yaml, yarn.lock, rush.js, bower.json, .min.js | Yes except .min.js |
18
- | java | maven (pom.xml [1]), gradle (build.gradle, .kts), scala (sbt), bazel | Yes unless pom.xml is manually parsed due to unavailability of maven or errors |
19
- | php | composer.lock | Yes |
20
- | python | pyproject.toml, setup.py, requirements.txt [2], Pipfile.lock, poetry.lock, bdist_wheel, .whl, .egg-info | Yes using the automatic pip install/freeze. When disabled, only with Pipfile.lock and poetry.lock |
21
- | go | binary, go.mod, go.sum, Gopkg.lock | Yes except binary |
22
- | ruby | Gemfile.lock, gemspec | Only for Gemfile.lock |
23
- | rust | binary, Cargo.toml, Cargo.lock | Only for Cargo.lock |
24
- | .Net | .csproj, packages.config, project.assets.json [3], packages.lock.json, .nupkg | Only for project.assets.json, packages.lock.json |
25
- | dart | pubspec.lock, pubspec.yaml | Only for pubspec.lock |
26
- | haskell | cabal.project.freeze | Yes |
27
- | elixir | mix.lock | Yes |
28
- | c/c++ | conan.lock, conanfile.txt | Yes only for conan.lock |
29
- | clojure | Clojure CLI (deps.edn), Leiningen (project.clj) | Yes unless the files are parsed manually due to lack of clojure cli or leiningen command |
30
- | swift | Package.resolved, Package.swift (swiftpm) | Yes |
31
- | docker / oci image | All supported languages. Linux OS packages with plugins [4] | Best effort based on lock files |
32
- | GitHub Actions | .github/workflows/\*.yml | N/A |
33
- | Linux | All supported languages. Linux OS packages with plugins [5] | Best effort based on lock files |
34
- | Windows | All supported languages. OS packages with best effort [5] | Best effort based on lock files |
35
- | Jenkins Plugins | .hpi files | |
36
- | Helm Charts | .yaml | N/A |
37
- | Skaffold | .yaml | N/A |
38
- | kustomization | .yaml | N/A |
39
- | Tekton tasks | .yaml | N/A |
40
- | Kubernetes | .yaml | N/A |
41
- | Maven Cache | $HOME/.m2/repository/\*\*/\*.jar | N/A |
42
- | SBT Cache | $HOME/.ivy2/cache/\*\*/\*.jar | N/A |
43
- | Gradle Cache | $HOME/caches/modules-2/files-2.1/\*\*/\*.jar | N/A |
44
- | Helm Index | $HOME/.cache/helm/repository/\*\*/\*.yaml | N/A |
45
- | Docker compose | docker-compose\*.yml. Images would also be scanned. | N/A |
46
- | Google CloudBuild configuration | cloudbuild.yaml | N/A |
47
- | OpenAPI | openapi\*.json, openapi\*.yaml | N/A |
15
+ | Language/Platform | Package format | Transitive dependencies |
16
+ | ------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
17
+ | node.js | npm-shrinkwrap.json, package-lock.json, pnpm-lock.yaml, yarn.lock, rush.js, bower.json, .min.js | Yes except .min.js |
18
+ | java | maven (pom.xml [1]), gradle (build.gradle, .kts), scala (sbt), bazel | Yes unless pom.xml is manually parsed due to unavailability of maven or errors |
19
+ | php | composer.lock | Yes |
20
+ | python | pyproject.toml, setup.py, requirements.txt [2], Pipfile.lock, poetry.lock, pdm.lock, bdist_wheel, .whl, .egg-info | Yes using the automatic pip install/freeze. When disabled, only with Pipfile.lock and poetry.lock |
21
+ | go | binary, go.mod, go.sum, Gopkg.lock | Yes except binary |
22
+ | ruby | Gemfile.lock, gemspec | Only for Gemfile.lock |
23
+ | rust | binary, Cargo.toml, Cargo.lock | Only for Cargo.lock |
24
+ | .Net | .csproj, packages.config, project.assets.json [3], packages.lock.json, .nupkg | Only for project.assets.json, packages.lock.json |
25
+ | dart | pubspec.lock, pubspec.yaml | Only for pubspec.lock |
26
+ | haskell | cabal.project.freeze | Yes |
27
+ | elixir | mix.lock | Yes |
28
+ | c/c++ | conan.lock, conanfile.txt | Yes only for conan.lock |
29
+ | clojure | Clojure CLI (deps.edn), Leiningen (project.clj) | Yes unless the files are parsed manually due to lack of clojure cli or leiningen command |
30
+ | swift | Package.resolved, Package.swift (swiftpm) | Yes |
31
+ | docker / oci image | All supported languages. Linux OS packages with plugins [4] | Best effort based on lock files |
32
+ | GitHub Actions | .github/workflows/\*.yml | N/A |
33
+ | Linux | All supported languages. Linux OS packages with plugins [5] | Best effort based on lock files |
34
+ | Windows | All supported languages. OS packages with best effort [5] | Best effort based on lock files |
35
+ | Jenkins Plugins | .hpi files | |
36
+ | Helm Charts | .yaml | N/A |
37
+ | Skaffold | .yaml | N/A |
38
+ | kustomization | .yaml | N/A |
39
+ | Tekton tasks | .yaml | N/A |
40
+ | Kubernetes | .yaml | N/A |
41
+ | Maven Cache | $HOME/.m2/repository/\*\*/\*.jar | N/A |
42
+ | SBT Cache | $HOME/.ivy2/cache/\*\*/\*.jar | N/A |
43
+ | Gradle Cache | $HOME/caches/modules-2/files-2.1/\*\*/\*.jar | N/A |
44
+ | Helm Index | $HOME/.cache/helm/repository/\*\*/\*.yaml | N/A |
45
+ | Docker compose | docker-compose\*.yml. Images would also be scanned. | N/A |
46
+ | Google CloudBuild configuration | cloudbuild.yaml | N/A |
47
+ | OpenAPI | openapi\*.json, openapi\*.yaml | N/A |
48
48
 
49
49
  NOTE:
50
50
 
@@ -276,7 +276,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s
276
276
  - pnpm-lock.yaml
277
277
  - Maven (pom.xml)
278
278
  - Gradle
279
- - Python (requirements.txt, setup.py, pyproject.toml)
279
+ - Python (requirements.txt, setup.py, pyproject.toml, poetry.lock)
280
280
 
281
281
  ## Environment variables
282
282
 
@@ -12,6 +12,7 @@
12
12
  "absl": "absl-py",
13
13
  "abstract": "abstract.jwrotator",
14
14
  "abu": "abu.admin",
15
+ "ac-flask": "ac-flask-hipchat",
15
16
  "acg": "anikom15",
16
17
  "acme": "acme.dchat",
17
18
  "acme": "acme.hello",
@@ -20,7 +21,6 @@
20
21
  "actionbar": "actionbar.panel",
21
22
  "activehomed": "afn",
22
23
  "activepapers": "activepapers.py",
23
- "ac-flask": "ac-flask-hipchat",
24
24
  "adafruit": "adafruit-libraries",
25
25
  "address-book": "address-book-lansry",
26
26
  "adi": "adi.commons",
@@ -39,9 +39,9 @@
39
39
  "adjector": "adjectorclient",
40
40
  "adjector": "adjectortracplugin",
41
41
  "adkit": "banner-ad-toolkit",
42
+ "admin-tools": "django-admin-tools",
42
43
  "adminishcategories": "adminish-categories",
43
44
  "adminsortable": "django-admin-sortable",
44
- "admin-tools": "django-admin-tools",
45
45
  "adspygoogle": "adspygoogle.adwords",
46
46
  "advancedcaching": "agtl",
47
47
  "adytum": "adytum-pymonitor",
@@ -52,6 +52,8 @@
52
52
  "affinitic": "affinitic.zamqp",
53
53
  "afpy": "afpy.xap",
54
54
  "afq": "pyafq",
55
+ "after-response": "django-after-response",
56
+ "ag-fft-tools": "agpy",
55
57
  "agatesql": "agate-sql",
56
58
  "ageliaco": "ageliaco.recipe.csvconfig",
57
59
  "agent-http": "agent.http",
@@ -74,7 +76,6 @@
74
76
  "agx": "agx.generator.zca",
75
77
  "agx": "agx.transform.uml2fs",
76
78
  "agx": "agx.transform.xmi2uml",
77
- "ag-fft-tools": "agpy",
78
79
  "aimes": "aimes.bundle",
79
80
  "aimes": "aimes.skeleton",
80
81
  "aio": "aio.app",
@@ -150,10 +151,10 @@
150
151
  "ambilight": "ambilightparty",
151
152
  "amifs": "amifs-core",
152
153
  "amiorganizer": "ami-organizer",
153
- "amitu": "amitu.lipy",
154
154
  "amitu": "amitu-putils",
155
155
  "amitu": "amitu-websocket-client",
156
156
  "amitu": "amitu-zutils",
157
+ "amitu": "amitu.lipy",
157
158
  "amltlearn": "amlt-learn",
158
159
  "amocrm": "amocrm-api",
159
160
  "amqpdispatcher": "amqp-dispatcher",
@@ -184,9 +185,9 @@
184
185
  "anthrax": "anthraximage",
185
186
  "antisphinx": "antiweb",
186
187
  "antispoofing": "antispoofing.evaluation",
188
+ "antlr4": "antlr4-python-alt",
187
189
  "antlr4": "antlr4-python2-runtime",
188
190
  "antlr4": "antlr4-python3-runtime",
189
- "antlr4": "antlr4-python-alt",
190
191
  "anybox": "anybox.buildbot.openerp",
191
192
  "anybox": "anybox.nose.odoo",
192
193
  "anybox": "anybox.paster.odoo",
@@ -197,6 +198,7 @@
197
198
  "apitools": "google-apitools",
198
199
  "apm": "arpm",
199
200
  "app": "zope2",
201
+ "app-data": "django-appdata",
200
202
  "appconf": "django-appconf",
201
203
  "appd": "appdynamicsdownloader",
202
204
  "appd": "appdynamicsrest",
@@ -209,7 +211,6 @@
209
211
  "appium": "appium-python-client",
210
212
  "appliapps": "applibase",
211
213
  "appserver": "broadwick",
212
- "app-data": "django-appdata",
213
214
  "aptsources": "python-apt-repo",
214
215
  "archetypes": "archetypes.kss",
215
216
  "archetypes": "archetypes.multilingual",
@@ -294,10 +295,10 @@
294
295
  "aufrefer": "auf-refer",
295
296
  "auslfe": "auslfe.formonline.content",
296
297
  "auspost": "auspost-apis",
298
+ "auth-server-client": "authserverclient",
297
299
  "auth0": "auth0-python",
298
300
  "authorize": "authorizesauce",
299
301
  "authzpolicy": "authzpolicyplugin",
300
- "auth-server-client": "authserverclient",
301
302
  "autobahn": "autobahn-rce",
302
303
  "avatar": "geonode-avatar",
303
304
  "awebview": "android-webview",
@@ -331,6 +332,7 @@
331
332
  "babel": "babel",
332
333
  "babelglade": "babelgladeextractor",
333
334
  "backplane": "backplane2-pyclient",
335
+ "backport-abcoll": "backport-collections",
334
336
  "backports": "backports.functools-lru-cache",
335
337
  "backports": "backports.inspect",
336
338
  "backports": "backports.pbkdf2",
@@ -339,18 +341,17 @@
339
341
  "backports": "backports.ssl",
340
342
  "backports": "backports.ssl-match-hostname",
341
343
  "backports": "backports.statistics",
342
- "backport-abcoll": "backport-collections",
343
344
  "badgekit": "badgekit-api-client",
344
345
  "badlinks": "badlinksplugin",
345
346
  "bael": "bael.project",
346
347
  "baidu": "baidupy",
347
348
  "balrog": "buildtools",
348
349
  "baluhn": "baluhn-redux",
350
+ "bamboo": "bamboo-data",
351
+ "bamboo": "bamboo-server",
349
352
  "bamboo": "bamboo.pantrybell",
350
353
  "bamboo": "bamboo.scaffold",
351
354
  "bamboo": "bamboo.setuptools-version",
352
- "bamboo": "bamboo-data",
353
- "bamboo": "bamboo-server",
354
355
  "bambu": "bambu-codemirror",
355
356
  "bambu": "bambu-dataportability",
356
357
  "bambu": "bambu-enqueue",
@@ -474,6 +475,7 @@
474
475
  "buildslave": "buildbot-slave",
475
476
  "buildtools": "ajk-ios-buildtools",
476
477
  "builtins": "pies2overrides",
478
+ "bulk-update-or-create": "django-bulk-update-or-create",
477
479
  "bumper": "bumper-lib",
478
480
  "bumple": "bumple-downloader",
479
481
  "bundesliga": "bundesliga-cli",
@@ -493,6 +495,7 @@
493
495
  "bzrlib": "bzr-colo",
494
496
  "bzrlib": "bzr-killtrailing",
495
497
  "bzrlib": "bzr-pqm",
498
+ "c-ast": "pycparser",
496
499
  "c2c": "c2c.cssmin",
497
500
  "c2c": "c2c.recipe.closurecompile",
498
501
  "c2c": "c2c.recipe.cssmin",
@@ -505,7 +508,6 @@
505
508
  "cabalgata": "cabalgata-silla-de-montar",
506
509
  "cabalgata": "cabalgata-zookeeper",
507
510
  "cache-utils": "django-cache-utils",
508
- "c-ast": "pycparser",
509
511
  "cairo": "pycairo",
510
512
  "captcha": "django-recaptcha",
511
513
  "cartridge": "cartridge",
@@ -519,12 +521,13 @@
519
521
  "chef": "pychef",
520
522
  "chip8": "c8d",
521
523
  "cjson": "python-cjson",
524
+ "ckeditor": "django-ckeditor",
522
525
  "classytags": "django-classy-tags",
523
526
  "cloghandler": "concurrentloghandler",
524
527
  "clonevirtualenv": "virtualenv-clone",
528
+ "cloud-admin": "adminapi",
525
529
  "cloud-insight": "al-cloudinsight",
526
530
  "cloudservers": "python-cloudservers",
527
- "cloud-admin": "adminapi",
528
531
  "clusterconsole": "cerebrod",
529
532
  "clustersitter": "cerebrod",
530
533
  "cms": "django-cms",
@@ -561,10 +564,13 @@
561
564
  "cv2": "opencv-python",
562
565
  "daemon": "python-daemon",
563
566
  "dare": "dare",
567
+ "database-locks": "django-database-locks",
568
+ "daterangefilter": "django-daterangefilter",
564
569
  "dateutil": "python-dateutil",
565
570
  "dawg": "dawg",
566
571
  "deb822": "python-debian",
567
572
  "debian": "python-debian",
573
+ "debug-toolbar": "django-debug-toolbar",
568
574
  "decouple": "python-decouple",
569
575
  "demo": "webunit",
570
576
  "demosongs": "pysynth",
@@ -578,8 +584,8 @@
578
584
  "distribute-setup": "ez-setup",
579
585
  "distutils2": "distutils2",
580
586
  "django": "django",
581
- "djangobower": "django-bower",
582
587
  "django-hstore": "amitu-hstore",
588
+ "djangobower": "django-bower",
583
589
  "djcelery": "django-celery",
584
590
  "djkombu": "django-kombu",
585
591
  "djorm-pgarray": "djorm-ext-pgarray",
@@ -589,13 +595,16 @@
589
595
  "dogpile": "dogpile.cache",
590
596
  "dogpile": "dogpile.core",
591
597
  "dogshell": "dogapi",
592
- "dotenv": "python-dotenv",
593
598
  "dot-parser": "pydot",
594
599
  "dot-parser": "pydot2",
595
600
  "dot-parser": "pydot3k",
601
+ "dotenv": "python-dotenv",
596
602
  "dpkt": "dpkt-fix",
603
+ "drf-queryfields": "djangorestframework-queryfields",
597
604
  "dsml": "python-ldap",
598
605
  "durationfield": "django-durationfield",
606
+ "dynamicsettings": "django-dynamicsettings",
607
+ "dynamicsettings": "django-dynamicsettings",
599
608
  "dzclient": "datazilla",
600
609
  "easybuild": "easybuild-framework",
601
610
  "editor": "python-editor",
@@ -622,8 +631,8 @@
622
631
  "fdpexpect": "pexpect",
623
632
  "fedora": "python-fedora",
624
633
  "fias": "ailove-django-fias",
625
- "fiftyonedegrees": "51degrees-mobile-detector-v3-wrapper",
626
634
  "fiftyone-degrees": "51degrees-mobile-detector",
635
+ "fiftyonedegrees": "51degrees-mobile-detector-v3-wrapper",
627
636
  "five": "five.customerize",
628
637
  "five": "five.globalrequest",
629
638
  "five": "five.intid",
@@ -631,9 +640,9 @@
631
640
  "five": "five.pt",
632
641
  "flasher": "android-flasher",
633
642
  "flask": "flask",
634
- "flaskext": "flask-bcrypt",
635
643
  "flask-frozen": "frozen-flask",
636
644
  "flask-redis": "flask-and-redis",
645
+ "flaskext": "flask-bcrypt",
637
646
  "flvscreen": "vnc2flv",
638
647
  "followit": "django-followit",
639
648
  "forge": "pyforge",
@@ -650,9 +659,9 @@
650
659
  "fuse": "fusepy",
651
660
  "fuzzy": "fuzzy",
652
661
  "gabbi": "tiddlyweb",
662
+ "gen-3dwallet": "3d-wallet-generator",
653
663
  "gendimen": "android-gendimen",
654
664
  "genshi": "genshi",
655
- "gen-3dwallet": "3d-wallet-generator",
656
665
  "geobasemain": "geobasesdev",
657
666
  "geobases": "geobasesdev",
658
667
  "geohash": "python-geohash",
@@ -700,6 +709,7 @@
700
709
  "igraph": "python-igraph",
701
710
  "imdb": "imdbpy",
702
711
  "impala": "impyla",
712
+ "impersonate": "django-impersonate",
703
713
  "inmemorystorage": "ambition-inmemorystorage",
704
714
  "ipaddress": "backport-ipaddress",
705
715
  "ipython": "ipython",
@@ -780,9 +790,9 @@
780
790
  "mkrst-themes": "2lazy2rest",
781
791
  "mockredis": "mockredispy",
782
792
  "modargs": "python-modargs",
793
+ "model-utils": "django-model-utils",
783
794
  "models": "asposebarcode",
784
795
  "models": "asposestorage",
785
- "model-utils": "django-model-utils",
786
796
  "moksha": "moksha.common",
787
797
  "moksha": "moksha.hub",
788
798
  "moksha": "moksha.wsgi",
@@ -808,8 +818,8 @@
808
818
  "nester": "amauri",
809
819
  "nester": "bssm-pythonsig",
810
820
  "novaclient": "python-novaclient",
811
- "oauth2client": "oauth2client",
812
821
  "oauth2-provider": "alauda-django-oauth",
822
+ "oauth2client": "oauth2client",
813
823
  "odf": "odfpy",
814
824
  "ofs": "zope2",
815
825
  "ometa": "parsley",
@@ -821,8 +831,8 @@
821
831
  "oslo-serialization": "oslo.serialization",
822
832
  "oslo-utils": "oslo.utils",
823
833
  "oss": "alioss",
824
- "oss": "aliyunoss",
825
834
  "oss": "aliyun-python-sdk-oss",
835
+ "oss": "aliyunoss",
826
836
  "output": "cashew",
827
837
  "owslib": "owslib",
828
838
  "packetdiag": "nwdiag",
@@ -842,8 +852,8 @@
842
852
  "pil": "pillow",
843
853
  "pilot": "bigjob",
844
854
  "pivotal": "pivotal-py",
845
- "playhouse": "peewee",
846
855
  "play-wav": "pysynth",
856
+ "playhouse": "peewee",
847
857
  "plivoxml": "plivo",
848
858
  "plone": "plone.alterego",
849
859
  "plone": "plone.api",
@@ -997,9 +1007,9 @@
997
1007
  "reprlib": "pies2overrides",
998
1008
  "requests": "requests",
999
1009
  "requirements": "requirements-parser",
1010
+ "rest-framework": "djangorestframework",
1000
1011
  "restclient": "py-restclient",
1001
1012
  "restructuredtext": "zope2",
1002
- "rest-framework": "djangorestframework",
1003
1013
  "retrial": "async-retrial",
1004
1014
  "reversion": "django-reversion",
1005
1015
  "rhaptos2": "rhaptos2.common",
@@ -1035,6 +1045,7 @@
1035
1045
  "shared": "zope2",
1036
1046
  "signals": "zope2",
1037
1047
  "sika": "ahonya-sika",
1048
+ "simple-history": "django-simple-history",
1038
1049
  "singleton": "pysingleton",
1039
1050
  "sittercommon": "cerebrod",
1040
1051
  "skbio": "scikit-bio",
@@ -1046,6 +1057,7 @@
1046
1057
  "slugify": "unicode-slugify",
1047
1058
  "smarkets": "smk-python-sdk",
1048
1059
  "snappy": "ctypes-snappy",
1060
+ "social-django": "social-auth-app-django",
1049
1061
  "socketio": "python-socketio",
1050
1062
  "socketserver": "pies2overrides",
1051
1063
  "sockjs": "sockjs-tornado",
@@ -1055,14 +1067,14 @@
1055
1067
  "sorl": "sorl-thumbnail",
1056
1068
  "south": "south",
1057
1069
  "sphinx": "sphinx",
1070
+ "sphinx-pypi-upload": "atd-document",
1058
1071
  "sphinxcontrib": "sphinxcontrib-programoutput",
1059
1072
  "sphinxext": "matplotlib",
1060
- "sphinx-pypi-upload": "atd-document",
1061
1073
  "sqlalchemy": "sqlalchemy",
1062
1074
  "src": "atlas",
1063
1075
  "src": "auto-mix-prep",
1064
- "statsd": "dogstatsd-python",
1065
1076
  "stats-toolkit": "bw-stats-toolkit",
1077
+ "statsd": "dogstatsd-python",
1066
1078
  "stdnum": "python-stdnum",
1067
1079
  "stemmer": "pystemmer",
1068
1080
  "stoneagehtml": "stoneagehtml",
package/display.js CHANGED
@@ -9,6 +9,8 @@ const SYMBOLS_ANSI = {
9
9
  VERTICAL: "│ "
10
10
  };
11
11
 
12
+ const MAX_TREE_DEPTH = 3;
13
+
12
14
  export const printTable = (bomJson) => {
13
15
  const data = [["Group", "Name", "Version", "Scope"]];
14
16
  for (const comp of bomJson.components) {
@@ -37,28 +39,12 @@ export const printDependencyTree = (bomJson) => {
37
39
  const depMap = {};
38
40
  for (const d of dependencies) {
39
41
  if (d.dependsOn && d.dependsOn.length) {
40
- depMap[d.ref] = d.dependsOn;
42
+ depMap[d.ref] = d.dependsOn.sort();
41
43
  }
42
44
  }
43
45
  const shownList = [];
44
46
  const treeGraphics = [];
45
47
  recursePrint(depMap, dependencies, 0, shownList, treeGraphics);
46
- if (treeGraphics && treeGraphics.length) {
47
- // Patch up the last line
48
- treeGraphics[treeGraphics.length - 1] = treeGraphics[
49
- treeGraphics.length - 1
50
- ].replace(SYMBOLS_ANSI.BRANCH, SYMBOLS_ANSI.LAST_BRANCH);
51
- if (treeGraphics[treeGraphics.length - 1].includes(SYMBOLS_ANSI.VERTICAL)) {
52
- treeGraphics[treeGraphics.length - 1] = treeGraphics[
53
- treeGraphics.length - 1
54
- ]
55
- .replace(SYMBOLS_ANSI.VERTICAL, SYMBOLS_ANSI.LAST_BRANCH)
56
- .replace(
57
- SYMBOLS_ANSI.INDENT + SYMBOLS_ANSI.LAST_BRANCH,
58
- SYMBOLS_ANSI.LAST_BRANCH
59
- );
60
- }
61
- }
62
48
  const config = {
63
49
  header: {
64
50
  alignment: "center",
@@ -68,26 +54,51 @@ export const printDependencyTree = (bomJson) => {
68
54
  console.log(table([[treeGraphics.join("\n")]], config));
69
55
  };
70
56
 
71
- const levelPrefix = (level) => {
57
+ const levelPrefix = (level, isLast) => {
72
58
  if (level === 0) {
73
59
  return SYMBOLS_ANSI.EMPTY;
74
60
  }
75
- let prefix = `${SYMBOLS_ANSI.BRANCH}`;
61
+ let prefix = `${isLast ? SYMBOLS_ANSI.LAST_BRANCH : SYMBOLS_ANSI.BRANCH}`;
76
62
  for (let i = 0; i < level - 1; i++) {
77
- prefix = `${SYMBOLS_ANSI.VERTICAL}${SYMBOLS_ANSI.INDENT}${prefix}`;
63
+ prefix = `${
64
+ isLast
65
+ ? SYMBOLS_ANSI.LAST_BRANCH.replace(" ", "─")
66
+ : SYMBOLS_ANSI.VERTICAL
67
+ }${isLast ? "" : SYMBOLS_ANSI.INDENT}${prefix}`;
78
68
  }
79
69
  return prefix;
80
70
  };
81
71
 
72
+ const isReallyRoot = (depMap, refStr) => {
73
+ for (const k of Object.keys(depMap)) {
74
+ const dependsOn = depMap[k] || [];
75
+ if (
76
+ dependsOn.includes(refStr) ||
77
+ dependsOn.includes(refStr.toLowerCase())
78
+ ) {
79
+ return false;
80
+ }
81
+ }
82
+ return true;
83
+ };
84
+
82
85
  const recursePrint = (depMap, subtree, level, shownList, treeGraphics) => {
83
86
  const listToUse = Array.isArray(subtree) ? subtree : [subtree];
84
- for (const l of listToUse) {
87
+ for (let i = 0; i < listToUse.length; i++) {
88
+ const l = listToUse[i];
85
89
  const refStr = l.ref || l;
86
- if (!shownList.includes(refStr.toLowerCase())) {
90
+ if (
91
+ (level === 0 &&
92
+ isReallyRoot(depMap, refStr) &&
93
+ !shownList.includes(refStr.toLowerCase())) ||
94
+ level > 0
95
+ ) {
96
+ treeGraphics.push(
97
+ `${levelPrefix(level, i == listToUse.length - 1)}${refStr}`
98
+ );
87
99
  shownList.push(refStr.toLowerCase());
88
- treeGraphics.push(`${levelPrefix(level)}${refStr}`);
89
100
  if (l && depMap[refStr]) {
90
- if (level < 3) {
101
+ if (level < MAX_TREE_DEPTH) {
91
102
  recursePrint(
92
103
  depMap,
93
104
  depMap[refStr],