mvn-tree-visualizer 1.1.0__py3-none-any.whl → 1.3.0__py3-none-any.whl
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.
- mvn_tree_visualizer/cli.py +150 -21
- mvn_tree_visualizer/diagram.py +19 -6
- mvn_tree_visualizer/exceptions.py +25 -0
- mvn_tree_visualizer/file_watcher.py +71 -0
- mvn_tree_visualizer/get_dependencies_in_one_file.py +37 -8
- mvn_tree_visualizer/outputs/html_output.py +32 -16
- mvn_tree_visualizer/outputs/json_output.py +20 -8
- mvn_tree_visualizer/validation.py +86 -0
- mvn_tree_visualizer-1.3.0.dist-info/METADATA +190 -0
- mvn_tree_visualizer-1.3.0.dist-info/RECORD +16 -0
- mvn_tree_visualizer-1.1.0.dist-info/METADATA +0 -71
- mvn_tree_visualizer-1.1.0.dist-info/RECORD +0 -13
- {mvn_tree_visualizer-1.1.0.dist-info → mvn_tree_visualizer-1.3.0.dist-info}/WHEEL +0 -0
- {mvn_tree_visualizer-1.1.0.dist-info → mvn_tree_visualizer-1.3.0.dist-info}/entry_points.txt +0 -0
- {mvn_tree_visualizer-1.1.0.dist-info → mvn_tree_visualizer-1.3.0.dist-info}/licenses/LICENSE +0 -0
mvn_tree_visualizer/cli.py
CHANGED
|
@@ -1,13 +1,130 @@
|
|
|
1
1
|
import argparse
|
|
2
|
+
import time
|
|
2
3
|
from pathlib import Path
|
|
4
|
+
from typing import NoReturn
|
|
3
5
|
|
|
4
6
|
from .diagram import create_diagram
|
|
7
|
+
from .exceptions import DependencyFileNotFoundError, DependencyParsingError, MvnTreeVisualizerError, OutputGenerationError
|
|
8
|
+
from .file_watcher import FileWatcher
|
|
5
9
|
from .get_dependencies_in_one_file import merge_files
|
|
6
10
|
from .outputs.html_output import create_html_diagram
|
|
7
11
|
from .outputs.json_output import create_json_output
|
|
12
|
+
from .validation import find_dependency_files, print_maven_help, validate_dependency_files, validate_output_directory
|
|
8
13
|
|
|
9
14
|
|
|
10
|
-
def
|
|
15
|
+
def generate_diagram(
|
|
16
|
+
directory: str,
|
|
17
|
+
output_file: str,
|
|
18
|
+
filename: str,
|
|
19
|
+
keep_tree: bool,
|
|
20
|
+
output_format: str,
|
|
21
|
+
show_versions: bool,
|
|
22
|
+
) -> None:
|
|
23
|
+
"""Generate the dependency diagram with comprehensive error handling."""
|
|
24
|
+
timestamp = time.strftime("%H:%M:%S")
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
# Validate inputs
|
|
28
|
+
validate_dependency_files(directory, filename)
|
|
29
|
+
validate_output_directory(output_file)
|
|
30
|
+
|
|
31
|
+
# Show what files we found
|
|
32
|
+
dependency_files = find_dependency_files(directory, filename)
|
|
33
|
+
if len(dependency_files) > 1:
|
|
34
|
+
print(f"[{timestamp}] Found {len(dependency_files)} dependency files")
|
|
35
|
+
|
|
36
|
+
# Setup paths
|
|
37
|
+
dir_to_create_files = Path(output_file).parent
|
|
38
|
+
dir_to_create_intermediate_files = Path(dir_to_create_files)
|
|
39
|
+
intermediate_file_path: Path = dir_to_create_intermediate_files / "dependency_tree.txt"
|
|
40
|
+
|
|
41
|
+
# Merge dependency files
|
|
42
|
+
try:
|
|
43
|
+
merge_files(
|
|
44
|
+
output_file=intermediate_file_path,
|
|
45
|
+
root_dir=directory,
|
|
46
|
+
target_filename=filename,
|
|
47
|
+
)
|
|
48
|
+
except FileNotFoundError as e:
|
|
49
|
+
raise DependencyParsingError(f"Error reading dependency file: {e}\nThe file may have been moved or deleted during processing.")
|
|
50
|
+
except PermissionError as e:
|
|
51
|
+
raise DependencyParsingError(f"Permission denied while reading dependency files: {e}\nPlease check file permissions and try again.")
|
|
52
|
+
except UnicodeDecodeError as e:
|
|
53
|
+
raise DependencyParsingError(
|
|
54
|
+
f"Error decoding dependency file content: {e}\n"
|
|
55
|
+
f"The file may contain invalid characters or use an unsupported encoding.\n"
|
|
56
|
+
f"Please ensure the file is in UTF-8 format."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Validate merged content
|
|
60
|
+
if not intermediate_file_path.exists() or intermediate_file_path.stat().st_size == 0:
|
|
61
|
+
raise DependencyParsingError(
|
|
62
|
+
"Generated dependency tree file is empty.\n"
|
|
63
|
+
"This usually means the Maven dependency files contain no valid dependency information.\n"
|
|
64
|
+
"Please check that your Maven dependency files were generated correctly."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Create diagram from merged content
|
|
68
|
+
try:
|
|
69
|
+
dependency_tree = create_diagram(
|
|
70
|
+
keep_tree=keep_tree,
|
|
71
|
+
intermediate_filename=str(intermediate_file_path),
|
|
72
|
+
)
|
|
73
|
+
except FileNotFoundError:
|
|
74
|
+
raise DependencyParsingError("Intermediate dependency tree file was not found.\nThis is an internal error - please report this issue.")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
raise DependencyParsingError(f"Error processing dependency tree: {e}\nThe dependency file format may be invalid or corrupted.")
|
|
77
|
+
|
|
78
|
+
# Validate that we have content to work with
|
|
79
|
+
if not dependency_tree.strip():
|
|
80
|
+
raise DependencyParsingError(
|
|
81
|
+
"Dependency tree is empty after processing.\n"
|
|
82
|
+
"Please check that your Maven dependency files contain valid dependency information.\n"
|
|
83
|
+
"You can verify this by opening the files and checking their content."
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Generate output
|
|
87
|
+
try:
|
|
88
|
+
if output_format == "html":
|
|
89
|
+
create_html_diagram(dependency_tree, output_file, show_versions)
|
|
90
|
+
elif output_format == "json":
|
|
91
|
+
create_json_output(dependency_tree, output_file, show_versions)
|
|
92
|
+
else:
|
|
93
|
+
raise OutputGenerationError(f"Unsupported output format: {output_format}")
|
|
94
|
+
except PermissionError:
|
|
95
|
+
raise OutputGenerationError(
|
|
96
|
+
f"Permission denied writing to '{output_file}'.\nPlease check that you have write permissions to this location."
|
|
97
|
+
)
|
|
98
|
+
except OSError as e:
|
|
99
|
+
raise OutputGenerationError(
|
|
100
|
+
f"Error writing output file '{output_file}': {e}\nPlease check that you have enough disk space and write permissions."
|
|
101
|
+
)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
raise OutputGenerationError(f"Error generating {output_format.upper()} output: {e}")
|
|
104
|
+
|
|
105
|
+
print(f"[{timestamp}] ✅ Diagram generated and saved to {output_file}")
|
|
106
|
+
|
|
107
|
+
except MvnTreeVisualizerError as e:
|
|
108
|
+
# Our custom errors already have helpful messages
|
|
109
|
+
print(f"[{timestamp}] ❌ Error: {e}")
|
|
110
|
+
if isinstance(e, DependencyFileNotFoundError):
|
|
111
|
+
print_maven_help()
|
|
112
|
+
except KeyboardInterrupt:
|
|
113
|
+
print(f"\n[{timestamp}] ⏹️ Operation cancelled by user")
|
|
114
|
+
except Exception as e:
|
|
115
|
+
# Unexpected errors
|
|
116
|
+
print(f"[{timestamp}] ❌ Unexpected error: {e}")
|
|
117
|
+
print("This is an internal error. Please report this issue with the following details:")
|
|
118
|
+
print(f" - Directory: {directory}")
|
|
119
|
+
print(f" - Filename: {filename}")
|
|
120
|
+
print(f" - Output: {output_file}")
|
|
121
|
+
print(f" - Format: {output_format}")
|
|
122
|
+
import traceback
|
|
123
|
+
|
|
124
|
+
traceback.print_exc()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def cli() -> NoReturn:
|
|
11
128
|
parser = argparse.ArgumentParser(
|
|
12
129
|
prog="mvn-tree-visualizer",
|
|
13
130
|
description="Generate a dependency diagram from a file.",
|
|
@@ -50,37 +167,49 @@ def cli():
|
|
|
50
167
|
help="Keep the dependency tree file after generating the diagram. Default is False.",
|
|
51
168
|
)
|
|
52
169
|
|
|
170
|
+
parser.add_argument(
|
|
171
|
+
"--show-versions",
|
|
172
|
+
action="store_true",
|
|
173
|
+
help="Show dependency versions in the diagram. Applicable to both HTML and JSON output formats.",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
parser.add_argument(
|
|
177
|
+
"--watch",
|
|
178
|
+
action="store_true",
|
|
179
|
+
help="Watch for changes in Maven dependency files and automatically regenerate the diagram.",
|
|
180
|
+
)
|
|
181
|
+
|
|
53
182
|
args = parser.parse_args()
|
|
54
183
|
directory: str = args.directory
|
|
55
184
|
output_file: str = args.output
|
|
56
185
|
filename: str = args.filename
|
|
57
186
|
keep_tree: bool = args.keep_tree
|
|
58
187
|
output_format: str = args.format
|
|
188
|
+
show_versions: bool = args.show_versions
|
|
189
|
+
watch_mode: bool = args.watch
|
|
59
190
|
|
|
60
|
-
|
|
191
|
+
# Generate initial diagram
|
|
192
|
+
print("Generating initial diagram...")
|
|
193
|
+
generate_diagram(directory, output_file, filename, keep_tree, output_format, show_versions)
|
|
61
194
|
|
|
62
|
-
|
|
195
|
+
if not watch_mode:
|
|
196
|
+
print("You can open it in your browser to view the dependency tree.")
|
|
197
|
+
print("Thank you for using mvn-tree-visualizer!")
|
|
198
|
+
return
|
|
63
199
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
dependency_tree = create_diagram(
|
|
71
|
-
keep_tree=keep_tree,
|
|
72
|
-
intermediate_filename="dependency_tree.txt",
|
|
73
|
-
)
|
|
200
|
+
# Watch mode
|
|
201
|
+
def regenerate_callback():
|
|
202
|
+
"""Callback function for file watcher."""
|
|
203
|
+
generate_diagram(directory, output_file, filename, keep_tree, output_format, show_versions)
|
|
74
204
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
elif output_format == "json":
|
|
78
|
-
create_json_output(dependency_tree, output_file)
|
|
205
|
+
watcher = FileWatcher(directory, filename, regenerate_callback)
|
|
206
|
+
watcher.start()
|
|
79
207
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
208
|
+
try:
|
|
209
|
+
watcher.wait()
|
|
210
|
+
finally:
|
|
211
|
+
print("Thank you for using mvn-tree-visualizer!")
|
|
83
212
|
|
|
84
213
|
|
|
85
214
|
if __name__ == "__main__":
|
|
86
|
-
cli()
|
|
215
|
+
cli()
|
mvn_tree_visualizer/diagram.py
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
def create_diagram(
|
|
2
5
|
keep_tree: bool = False,
|
|
3
6
|
intermediate_filename: str = "dependency_tree.txt",
|
|
4
|
-
):
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
) -> str:
|
|
8
|
+
"""Create diagram from dependency tree file."""
|
|
9
|
+
try:
|
|
10
|
+
with open(intermediate_filename, "r", encoding="utf-8") as file:
|
|
11
|
+
dependency_tree: str = file.read()
|
|
12
|
+
except FileNotFoundError:
|
|
13
|
+
raise FileNotFoundError(f"Dependency tree file '{intermediate_filename}' not found")
|
|
14
|
+
except PermissionError:
|
|
15
|
+
raise PermissionError(f"Permission denied reading '{intermediate_filename}'")
|
|
16
|
+
except UnicodeDecodeError as e:
|
|
17
|
+
raise UnicodeDecodeError(e.encoding, e.object, e.start, e.end, f"Error decoding '{intermediate_filename}': {e.reason}")
|
|
7
18
|
|
|
8
19
|
if not keep_tree:
|
|
9
|
-
|
|
20
|
+
try:
|
|
21
|
+
os.remove(intermediate_filename)
|
|
22
|
+
except OSError:
|
|
23
|
+
# If we can't remove the intermediate file, it's not critical
|
|
24
|
+
pass
|
|
10
25
|
|
|
11
|
-
os.remove(intermediate_filename)
|
|
12
|
-
|
|
13
26
|
return dependency_tree
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Custom exceptions for mvn-tree-visualizer."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MvnTreeVisualizerError(Exception):
|
|
5
|
+
"""Base exception for mvn-tree-visualizer errors."""
|
|
6
|
+
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DependencyFileNotFoundError(MvnTreeVisualizerError):
|
|
11
|
+
"""Raised when no dependency files are found."""
|
|
12
|
+
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DependencyParsingError(MvnTreeVisualizerError):
|
|
17
|
+
"""Raised when there's an error parsing dependency files."""
|
|
18
|
+
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OutputGenerationError(MvnTreeVisualizerError):
|
|
23
|
+
"""Raised when there's an error generating output files."""
|
|
24
|
+
|
|
25
|
+
pass
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""File watcher functionality for monitoring Maven dependency files."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
from watchdog.events import FileSystemEventHandler
|
|
7
|
+
from watchdog.observers import Observer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DependencyFileHandler(FileSystemEventHandler):
|
|
11
|
+
"""Handler for file system events to trigger diagram regeneration."""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
filename: str,
|
|
16
|
+
callback: Callable[[], None],
|
|
17
|
+
):
|
|
18
|
+
"""Initialize the file handler.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
filename: Name of the file to monitor for changes
|
|
22
|
+
callback: Function to call when file changes are detected
|
|
23
|
+
"""
|
|
24
|
+
self.filename = filename
|
|
25
|
+
self.callback = callback
|
|
26
|
+
|
|
27
|
+
def on_modified(self, event):
|
|
28
|
+
"""Handle file modification events."""
|
|
29
|
+
if not event.is_directory and event.src_path.endswith(self.filename):
|
|
30
|
+
print(f"Detected change in {event.src_path}")
|
|
31
|
+
self.callback()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FileWatcher:
|
|
35
|
+
"""File system watcher for monitoring Maven dependency files."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, directory: str, filename: str, callback: Callable[[], None]):
|
|
38
|
+
"""Initialize the file watcher.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
directory: Directory to watch for file changes
|
|
42
|
+
filename: Name of the file to monitor
|
|
43
|
+
callback: Function to call when file changes are detected
|
|
44
|
+
"""
|
|
45
|
+
self.directory = directory
|
|
46
|
+
self.filename = filename
|
|
47
|
+
self.callback = callback
|
|
48
|
+
self.observer = Observer()
|
|
49
|
+
self.event_handler = DependencyFileHandler(filename, callback)
|
|
50
|
+
|
|
51
|
+
def start(self) -> None:
|
|
52
|
+
"""Start watching for file changes."""
|
|
53
|
+
print(f"Watching for changes in '{self.filename}' files in '{self.directory}'...")
|
|
54
|
+
print("Press Ctrl+C to stop watching.")
|
|
55
|
+
|
|
56
|
+
self.observer.schedule(self.event_handler, self.directory, recursive=True)
|
|
57
|
+
self.observer.start()
|
|
58
|
+
|
|
59
|
+
def stop(self) -> None:
|
|
60
|
+
"""Stop watching for file changes."""
|
|
61
|
+
print("\nStopping file watcher...")
|
|
62
|
+
self.observer.stop()
|
|
63
|
+
self.observer.join()
|
|
64
|
+
|
|
65
|
+
def wait(self) -> None:
|
|
66
|
+
"""Wait for file changes (blocking)."""
|
|
67
|
+
try:
|
|
68
|
+
while True:
|
|
69
|
+
time.sleep(1)
|
|
70
|
+
except KeyboardInterrupt:
|
|
71
|
+
self.stop()
|
|
@@ -1,11 +1,40 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Union
|
|
2
4
|
|
|
3
5
|
|
|
4
|
-
def merge_files(output_file: str, root_dir: str = ".", target_filename: str = "maven_dependency_file"):
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
def merge_files(output_file: Union[str, Path], root_dir: str = ".", target_filename: str = "maven_dependency_file") -> None:
|
|
7
|
+
"""Merge all dependency files from the directory tree into a single file."""
|
|
8
|
+
files_found = 0
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
with open(output_file, "w", encoding="utf-8") as outfile:
|
|
12
|
+
for dirpath, _, filenames in os.walk(root_dir):
|
|
13
|
+
for fname in filenames:
|
|
14
|
+
if fname == target_filename:
|
|
15
|
+
file_path: str = os.path.join(dirpath, fname)
|
|
16
|
+
try:
|
|
17
|
+
with open(file_path, "r", encoding="utf-8") as infile:
|
|
18
|
+
content = infile.read()
|
|
19
|
+
if content.strip(): # Only write non-empty content
|
|
20
|
+
outfile.write(content)
|
|
21
|
+
if not content.endswith("\n"):
|
|
22
|
+
outfile.write("\n")
|
|
23
|
+
files_found += 1
|
|
24
|
+
except UnicodeDecodeError as e:
|
|
25
|
+
raise UnicodeDecodeError(
|
|
26
|
+
e.encoding,
|
|
27
|
+
e.object,
|
|
28
|
+
e.start,
|
|
29
|
+
e.end,
|
|
30
|
+
f"Error reading '{file_path}': {e.reason}. Please ensure the file is in UTF-8 format.",
|
|
31
|
+
)
|
|
32
|
+
except PermissionError:
|
|
33
|
+
raise PermissionError(f"Permission denied reading '{file_path}'")
|
|
34
|
+
except PermissionError:
|
|
35
|
+
raise PermissionError(f"Permission denied writing to '{output_file}'")
|
|
36
|
+
except OSError as e:
|
|
37
|
+
raise OSError(f"Error writing to '{output_file}': {e}")
|
|
38
|
+
|
|
39
|
+
if files_found == 0:
|
|
40
|
+
raise FileNotFoundError(f"No '{target_filename}' files found in '{root_dir}' or its subdirectories")
|
|
@@ -1,49 +1,65 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
+
from typing import List, Set, Tuple
|
|
3
|
+
|
|
2
4
|
from jinja2 import BaseLoader, Environment
|
|
5
|
+
|
|
3
6
|
from ..TEMPLATE import HTML_TEMPLATE
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
|
|
9
|
+
def create_html_diagram(dependency_tree: str, output_filename: str, show_versions: bool = False) -> None:
|
|
10
|
+
mermaid_diagram: str = _convert_to_mermaid(dependency_tree, show_versions)
|
|
7
11
|
template = Environment(loader=BaseLoader).from_string(HTML_TEMPLATE)
|
|
8
|
-
rendered = template.render(diagram_definition=mermaid_diagram)
|
|
9
|
-
parent_dir = Path(output_filename).parent
|
|
12
|
+
rendered: str = template.render(diagram_definition=mermaid_diagram)
|
|
13
|
+
parent_dir: Path = Path(output_filename).parent
|
|
10
14
|
if not parent_dir.exists():
|
|
11
15
|
parent_dir.mkdir(parents=True, exist_ok=True)
|
|
12
16
|
with open(output_filename, "w") as f:
|
|
13
17
|
f.write(rendered)
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
|
|
20
|
+
def _convert_to_mermaid(dependency_tree: str, show_versions: bool = False) -> str:
|
|
16
21
|
# generate a `graph LR` format for Mermaid
|
|
17
|
-
lines = dependency_tree.strip().split("\n")
|
|
18
|
-
mermaid_lines = set()
|
|
19
|
-
previous_dependency = []
|
|
22
|
+
lines: List[str] = dependency_tree.strip().split("\n")
|
|
23
|
+
mermaid_lines: Set[str] = set()
|
|
24
|
+
previous_dependency: List[Tuple[str, int]] = []
|
|
20
25
|
for line in lines:
|
|
21
26
|
if not line:
|
|
22
27
|
continue
|
|
23
28
|
if line.startswith("[INFO] "):
|
|
24
29
|
line = line[7:] # Remove the "[INFO] " prefix
|
|
25
|
-
parts = line.split(":")
|
|
30
|
+
parts: List[str] = line.split(":")
|
|
26
31
|
if len(parts) < 3:
|
|
27
32
|
continue
|
|
28
33
|
if len(parts) == 4:
|
|
29
34
|
group_id, artifact_id, app, version = parts
|
|
30
|
-
|
|
35
|
+
if show_versions:
|
|
36
|
+
node_label: str = f"{artifact_id}:{version}"
|
|
37
|
+
mermaid_lines.add(f"\t{node_label};")
|
|
38
|
+
else:
|
|
39
|
+
node_label: str = artifact_id
|
|
40
|
+
mermaid_lines.add(f"\t{artifact_id};")
|
|
31
41
|
if previous_dependency: # Re initialize the list if it wasn't empty
|
|
32
42
|
previous_dependency = []
|
|
33
|
-
previous_dependency.append((
|
|
43
|
+
previous_dependency.append((node_label, 0)) # The second element is the depth
|
|
34
44
|
else:
|
|
35
|
-
depth = len(parts[0].split(" ")) - 1
|
|
45
|
+
depth: int = len(parts[0].split(" ")) - 1
|
|
36
46
|
if len(parts) == 6:
|
|
37
47
|
dirty_group_id, artifact_id, app, ejb_client, version, dependency = parts
|
|
38
48
|
else:
|
|
39
49
|
dirty_group_id, artifact_id, app, version, dependency = parts
|
|
50
|
+
|
|
51
|
+
if show_versions:
|
|
52
|
+
node_label: str = f"{artifact_id}:{version}"
|
|
53
|
+
else:
|
|
54
|
+
node_label: str = artifact_id
|
|
55
|
+
|
|
40
56
|
if previous_dependency[-1][1] < depth:
|
|
41
|
-
mermaid_lines.add(f"\t{previous_dependency[-1][0]} --> {
|
|
42
|
-
previous_dependency.append((
|
|
57
|
+
mermaid_lines.add(f"\t{previous_dependency[-1][0]} --> {node_label};")
|
|
58
|
+
previous_dependency.append((node_label, depth))
|
|
43
59
|
else:
|
|
44
60
|
# remove all dependencies that are deeper or equal to the current depth
|
|
45
61
|
while previous_dependency and previous_dependency[-1][1] >= depth:
|
|
46
62
|
previous_dependency.pop()
|
|
47
|
-
mermaid_lines.add(f"\t{previous_dependency[-1][0]} --> {
|
|
48
|
-
previous_dependency.append((
|
|
63
|
+
mermaid_lines.add(f"\t{previous_dependency[-1][0]} --> {node_label};")
|
|
64
|
+
previous_dependency.append((node_label, depth))
|
|
49
65
|
return "graph LR\n" + "\n".join(mermaid_lines)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from typing import Any, Dict, List, Tuple
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
|
|
5
|
+
def create_json_output(dependency_tree: str, output_filename: str, show_versions: bool = False) -> None:
|
|
6
|
+
lines: List[str] = dependency_tree.strip().split("\n")
|
|
7
|
+
tree: Dict[str, Any] = {}
|
|
8
|
+
node_stack: List[Tuple[Dict[str, Any], int]] = [] # Stack to keep track of nodes and their depth
|
|
7
9
|
|
|
8
10
|
for line in lines:
|
|
9
11
|
if not line:
|
|
@@ -11,33 +13,43 @@ def create_json_output(dependency_tree: str, output_filename: str):
|
|
|
11
13
|
if line.startswith("[INFO] "):
|
|
12
14
|
line = line[7:] # Remove the "[INFO] " prefix
|
|
13
15
|
|
|
14
|
-
parts = line.split(":")
|
|
16
|
+
parts: List[str] = line.split(":")
|
|
15
17
|
if len(parts) < 3:
|
|
16
18
|
continue
|
|
17
19
|
|
|
18
20
|
# Root node
|
|
19
21
|
if len(parts) == 4:
|
|
20
22
|
group_id, artifact_id, _, version = parts
|
|
21
|
-
|
|
23
|
+
if show_versions:
|
|
24
|
+
node_id: str = f"{artifact_id}:{version}"
|
|
25
|
+
else:
|
|
26
|
+
node_id: str = artifact_id
|
|
27
|
+
node: Dict[str, Any] = {"id": node_id, "children": []}
|
|
22
28
|
tree = node
|
|
23
29
|
node_stack = [(node, 0)] # Reset stack with root node at depth 0
|
|
24
30
|
# Child node
|
|
25
31
|
else:
|
|
26
32
|
# This depth calculation is based on the mermaid logic's whitespace parsing
|
|
27
|
-
depth = len(parts[0].split(" ")) - 1
|
|
33
|
+
depth: int = len(parts[0].split(" ")) - 1
|
|
28
34
|
|
|
29
35
|
if len(parts) == 6:
|
|
30
36
|
_, artifact_id, _, _, version, _ = parts
|
|
31
37
|
else:
|
|
32
38
|
_, artifact_id, _, version, _ = parts
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
if show_versions:
|
|
41
|
+
node_id: str = f"{artifact_id}:{version}"
|
|
42
|
+
else:
|
|
43
|
+
node_id: str = artifact_id
|
|
44
|
+
|
|
45
|
+
node: Dict[str, Any] = {"id": node_id, "children": []}
|
|
35
46
|
|
|
36
47
|
# Go up the stack to find the correct parent
|
|
37
48
|
while node_stack and node_stack[-1][1] >= depth:
|
|
38
49
|
node_stack.pop()
|
|
39
50
|
|
|
40
51
|
if node_stack:
|
|
52
|
+
parent_node: Dict[str, Any]
|
|
41
53
|
parent_node, _ = node_stack[-1]
|
|
42
54
|
parent_node["children"].append(node)
|
|
43
55
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Validation utilities for mvn-tree-visualizer."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from .exceptions import DependencyFileNotFoundError, OutputGenerationError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def find_dependency_files(directory: str, filename: str) -> list[str]:
|
|
10
|
+
"""Find all dependency files in the directory tree."""
|
|
11
|
+
found_files = []
|
|
12
|
+
for dirpath, _, filenames in os.walk(directory):
|
|
13
|
+
if filename in filenames:
|
|
14
|
+
found_files.append(os.path.join(dirpath, filename))
|
|
15
|
+
return found_files
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def print_maven_help() -> None:
|
|
19
|
+
"""Print helpful Maven commands for generating dependency files."""
|
|
20
|
+
print("\n💡 To generate a Maven dependency file, try one of these commands:")
|
|
21
|
+
print(" mvn dependency:tree -DoutputFile=maven_dependency_file")
|
|
22
|
+
print(" mvn dependency:tree > maven_dependency_file")
|
|
23
|
+
print(" mvn dependency:tree -DoutputFile=maven_dependency_file -DoutputType=text")
|
|
24
|
+
print("\n📍 Make sure you're in a directory with a pom.xml file.")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def validate_directory(directory: str) -> None:
|
|
28
|
+
"""Validate that the directory exists and is accessible."""
|
|
29
|
+
if not os.path.exists(directory):
|
|
30
|
+
raise DependencyFileNotFoundError(f"Directory '{directory}' does not exist.\nPlease check the path and try again.")
|
|
31
|
+
|
|
32
|
+
if not os.path.isdir(directory):
|
|
33
|
+
raise DependencyFileNotFoundError(f"'{directory}' is not a directory.\nPlease provide a valid directory path.")
|
|
34
|
+
|
|
35
|
+
if not os.access(directory, os.R_OK):
|
|
36
|
+
raise DependencyFileNotFoundError(f"Cannot read from directory '{directory}'.\nPlease check your permissions and try again.")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def validate_dependency_files(directory: str, filename: str) -> list[str]:
|
|
40
|
+
"""Validate that dependency files exist and are readable."""
|
|
41
|
+
validate_directory(directory)
|
|
42
|
+
|
|
43
|
+
dependency_files = find_dependency_files(directory, filename)
|
|
44
|
+
|
|
45
|
+
if not dependency_files:
|
|
46
|
+
abs_directory = os.path.abspath(directory)
|
|
47
|
+
raise DependencyFileNotFoundError(
|
|
48
|
+
f"No '{filename}' files found in '{abs_directory}' or its subdirectories.\n\n"
|
|
49
|
+
f"📂 Searched in: {abs_directory}\n"
|
|
50
|
+
f"🔍 Looking for: {filename}\n"
|
|
51
|
+
f"\n💡 To generate a Maven dependency file, run:"
|
|
52
|
+
f"\n mvn dependency:tree -DoutputFile={filename}"
|
|
53
|
+
f"\n\n📍 Make sure you're in a directory with a pom.xml file."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Validate that files are readable
|
|
57
|
+
unreadable_files = []
|
|
58
|
+
for file_path in dependency_files:
|
|
59
|
+
if not os.access(file_path, os.R_OK):
|
|
60
|
+
unreadable_files.append(file_path)
|
|
61
|
+
|
|
62
|
+
if unreadable_files:
|
|
63
|
+
files_list = "\n".join(f" - {f}" for f in unreadable_files)
|
|
64
|
+
raise DependencyFileNotFoundError(
|
|
65
|
+
f"Found {len(dependency_files)} dependency file(s), but cannot read {len(unreadable_files)} of them:\n"
|
|
66
|
+
f"{files_list}\n\n"
|
|
67
|
+
f"Please check file permissions and try again."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
return dependency_files
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def validate_output_directory(output_file: str) -> None:
|
|
74
|
+
"""Validate that the output directory exists and is writable."""
|
|
75
|
+
output_dir = Path(output_file).parent
|
|
76
|
+
|
|
77
|
+
if not output_dir.exists():
|
|
78
|
+
try:
|
|
79
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
except PermissionError:
|
|
81
|
+
raise OutputGenerationError(f"Cannot create output directory '{output_dir}'.\nPlease check your permissions and try again.")
|
|
82
|
+
except Exception as e:
|
|
83
|
+
raise OutputGenerationError(f"Failed to create output directory '{output_dir}': {e}")
|
|
84
|
+
|
|
85
|
+
if not os.access(output_dir, os.W_OK):
|
|
86
|
+
raise OutputGenerationError(f"Cannot write to output directory '{output_dir}'.\nPlease check your permissions and try again.")
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mvn-tree-visualizer
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: A simple command line tool to visualize the dependency tree of a Maven project in a graphical format.
|
|
5
|
+
Project-URL: source, https://github.com/dyka3773/mvn-tree-visualizer
|
|
6
|
+
Author-email: Iraklis Konsoulas <dyka3773@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: cli,command-line,dependency,graph,maven,mermaid,tool,tree,visualization
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: >=3.13
|
|
17
|
+
Requires-Dist: jinja2>=3.1.6
|
|
18
|
+
Requires-Dist: watchdog>=6.0.0
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Maven Dependency Tree Visualizer
|
|
22
|
+
|
|
23
|
+
[](https://badge.fury.io/py/mvn-tree-visualizer)
|
|
24
|
+

|
|
25
|
+

|
|
26
|
+
[](https://pepy.tech/project/mvn-tree-visualizer)
|
|
27
|
+
[](https://github.com/dyka3773/mvn-tree-visualizer/actions)
|
|
28
|
+
[](https://github.com/astral-sh/ruff)
|
|
29
|
+
|
|
30
|
+
A simple command-line tool to visualize the dependency tree of a Maven project in a graphical and interactive format.
|
|
31
|
+
|
|
32
|
+
This tool was born out of the frustration of not being able to easily visualize the dependency tree of a Maven project. The `mvn dependency:tree` command is great, but the output can be hard to read, especially for large projects. This tool aims to solve that problem by providing a simple way to generate an interactive diagram or a structured JSON output of the dependency tree.
|
|
33
|
+
|
|
34
|
+
## Table of Contents
|
|
35
|
+
- [Features](#features)
|
|
36
|
+
- [Installation](#installation)
|
|
37
|
+
- [How to Use](#how-to-use)
|
|
38
|
+
- [Examples](#examples)
|
|
39
|
+
- [Options](#options)
|
|
40
|
+
- [Performance](#performance)
|
|
41
|
+
- [Troubleshooting](#troubleshooting)
|
|
42
|
+
- [Contributing](#contributing)
|
|
43
|
+
- [License](#license)
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
Install the package from PyPI:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install mvn-tree-visualizer
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- **🌐 Multiple Output Formats:**
|
|
56
|
+
- **HTML:** Generates an interactive HTML diagram of your dependency tree using Mermaid.js.
|
|
57
|
+
- **JSON:** Creates a structured JSON representation of the dependency tree, perfect for scripting or integration with other tools.
|
|
58
|
+
- **🔄 Watch Mode:** Automatically regenerates diagrams when Maven dependency files change using the `--watch` flag.
|
|
59
|
+
- **📋 Version Display:** Show or hide dependency versions in both HTML and JSON outputs using the `--show-versions` flag.
|
|
60
|
+
- **⚡ Easy to Use:** A simple command-line interface that gets the job done with minimal configuration.
|
|
61
|
+
- **📂 File Merging:** Automatically finds and merges multiple `maven_dependency_file` files from different subdirectories.
|
|
62
|
+
- **🎨 Customizable Output:** Specify the output file name and location.
|
|
63
|
+
- **💾 SVG Export:** Download the generated diagram as an SVG file directly from the HTML page.
|
|
64
|
+
|
|
65
|
+
## How to Use
|
|
66
|
+
|
|
67
|
+
### Step 1: Generate the dependency file
|
|
68
|
+
|
|
69
|
+
Run the following command in your terminal at the root of your Maven project. This will generate a file named `maven_dependency_file` in each module's `target` directory.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
mvn dependency:tree -DoutputFile=maven_dependency_file -DappendOutput=true
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> **💡 Tip:** You can add other options like `-Dincludes="org.example"` to filter the dependencies.
|
|
76
|
+
|
|
77
|
+
### Step 2: Visualize the dependency tree
|
|
78
|
+
|
|
79
|
+
Use the `mvn-tree-visualizer` command to generate the diagram.
|
|
80
|
+
|
|
81
|
+
#### HTML Output (Interactive Diagram)
|
|
82
|
+
```bash
|
|
83
|
+
mvn_tree_visualizer --filename "maven_dependency_file" --output "diagram.html" --format html
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### JSON Output (Structured Data)
|
|
87
|
+
```bash
|
|
88
|
+
mvn_tree_visualizer --filename "maven_dependency_file" --output "dependencies.json" --format json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### With Version Information
|
|
92
|
+
```bash
|
|
93
|
+
mvn_tree_visualizer --filename "maven_dependency_file" --output "diagram.html" --show-versions
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### JSON Output with Versions
|
|
97
|
+
```bash
|
|
98
|
+
mvn_tree_visualizer --filename "maven_dependency_file" --output "dependencies.json" --format json --show-versions
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### Watch Mode (Auto-regeneration)
|
|
102
|
+
```bash
|
|
103
|
+
mvn_tree_visualizer --filename "maven_dependency_file" --output "diagram.html" --watch
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
> **💡 Tip:** In watch mode, the tool will monitor for changes to your Maven dependency files and automatically regenerate the diagram. Perfect for development workflows! Press `Ctrl+C` to stop watching.
|
|
107
|
+
|
|
108
|
+
### Step 3: View the output
|
|
109
|
+
|
|
110
|
+
- **HTML:** Open the generated `diagram.html` file in your web browser to view the interactive dependency tree.
|
|
111
|
+
- **JSON:** Use the `dependencies.json` file in your scripts or other tools.
|
|
112
|
+
|
|
113
|
+
## Examples
|
|
114
|
+
|
|
115
|
+
Check out the [`examples/`](examples/) directory for sample Maven dependency files and their outputs:
|
|
116
|
+
|
|
117
|
+
- **Simple Project**: Basic Spring Boot application with common dependencies
|
|
118
|
+
- **Complex Project**: Realistic microservice with comprehensive dependencies
|
|
119
|
+
|
|
120
|
+
Each example includes:
|
|
121
|
+
- Sample Maven dependency tree file
|
|
122
|
+
- Generated HTML and JSON outputs
|
|
123
|
+
- Usage instructions
|
|
124
|
+
|
|
125
|
+
## Options
|
|
126
|
+
|
|
127
|
+
| Option | Description | Default |
|
|
128
|
+
|--------|-------------|---------|
|
|
129
|
+
| `--filename` | The name of the file containing the Maven dependency tree | `maven_dependency_file` |
|
|
130
|
+
| `--output` | The name of the output file | `diagram.html` |
|
|
131
|
+
| `--format` | The output format (`html` or `json`) | `html` |
|
|
132
|
+
| `--show-versions` | Show dependency versions in the diagram | `False` |
|
|
133
|
+
| `--watch` | Watch for file changes and auto-regenerate diagram | `False` |
|
|
134
|
+
| `--directory` | The directory to scan for the Maven dependency file(s) | current directory |
|
|
135
|
+
| `--keep-tree` | Keep the intermediate `dependency_tree.txt` file | `False` |
|
|
136
|
+
| `--help` | Show the help message and exit | - |
|
|
137
|
+
|
|
138
|
+
## Performance
|
|
139
|
+
|
|
140
|
+
**For Large Projects:**
|
|
141
|
+
- Consider filtering dependencies at the Maven level using `-Dincludes` or `-Dexcludes` parameters
|
|
142
|
+
- The tool can handle projects with hundreds of dependencies efficiently
|
|
143
|
+
|
|
144
|
+
**Memory Usage:**
|
|
145
|
+
- Memory usage scales with the number of dependencies
|
|
146
|
+
- Typical projects (50-200 dependencies) use minimal memory
|
|
147
|
+
- Very large projects (1000+ dependencies) may require more memory
|
|
148
|
+
|
|
149
|
+
## Troubleshooting
|
|
150
|
+
|
|
151
|
+
### Common Issues
|
|
152
|
+
|
|
153
|
+
**"No dependency files found"**
|
|
154
|
+
- The tool now provides detailed guidance including:
|
|
155
|
+
- Exact directory searched and filename expected
|
|
156
|
+
- Maven commands to generate dependency files
|
|
157
|
+
- Instructions to ensure you're in a directory with pom.xml
|
|
158
|
+
|
|
159
|
+
**"Empty or invalid output"**
|
|
160
|
+
- Enhanced error messages now include:
|
|
161
|
+
- Specific error details (encoding, permissions, empty files)
|
|
162
|
+
- Validation of file content and format
|
|
163
|
+
- Suggestions for fixing common parsing issues
|
|
164
|
+
|
|
165
|
+
**"Browser doesn't display the diagram"**
|
|
166
|
+
- Ensure you're opening the HTML file in a modern browser
|
|
167
|
+
- Check browser console for JavaScript errors
|
|
168
|
+
- Try a different browser (Chrome, Firefox, Safari)
|
|
169
|
+
|
|
170
|
+
**"Permission denied errors"**
|
|
171
|
+
- Improved diagnostics for:
|
|
172
|
+
- Directory read/write permissions
|
|
173
|
+
- File access issues
|
|
174
|
+
- Output directory creation problems
|
|
175
|
+
|
|
176
|
+
### Getting Help
|
|
177
|
+
|
|
178
|
+
- Check the [examples](examples/) directory for working samples
|
|
179
|
+
- Review the [issues](https://github.com/dyka3773/mvn-tree-visualizer/issues) page
|
|
180
|
+
- Create a new issue with your Maven dependency file sample
|
|
181
|
+
|
|
182
|
+
## Contributing
|
|
183
|
+
|
|
184
|
+
Contributions are welcome! If you have any ideas, suggestions, or bug reports, please open an issue or submit a pull request.
|
|
185
|
+
|
|
186
|
+
Please read our [CONTRIBUTING.md](CONTRIBUTING.md) file for more details.
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
mvn_tree_visualizer/TEMPLATE.py,sha256=WIQfSNBygUZVkBrERq7QzqouGURA0NYVqUUm-11wMvo,2499
|
|
2
|
+
mvn_tree_visualizer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
mvn_tree_visualizer/__main__.py,sha256=yIQFAdWjthKAFbSzzRuz5_YGlK0c6BnR2ypjNRDq180,82
|
|
4
|
+
mvn_tree_visualizer/cli.py,sha256=33K5ujgB85xejcSUKMNAwirpLfnGMGfrnhqyLFOy69k,8219
|
|
5
|
+
mvn_tree_visualizer/diagram.py,sha256=UfvP_J4Im4JQLe3EWlY3TsP4tua3oYk5NiCGbZNQwoA,933
|
|
6
|
+
mvn_tree_visualizer/exceptions.py,sha256=R4nnJ0xrOpd84GfPD9rFSDk40etDLoph7iZpj1CCR0c,543
|
|
7
|
+
mvn_tree_visualizer/file_watcher.py,sha256=JtmV1KW08_Az-XqpKhcd342WpxV1vUW-Dge9lodjjJY,2284
|
|
8
|
+
mvn_tree_visualizer/get_dependencies_in_one_file.py,sha256=nXhEhU-dI7tXa3TpoW1pv2t86t1K0yppSw8FYDtmTlQ,1973
|
|
9
|
+
mvn_tree_visualizer/validation.py,sha256=UQ194gHiVS8UnJpp90sCM-Vjn3aeXT6scdwOplAoaSE,3689
|
|
10
|
+
mvn_tree_visualizer/outputs/html_output.py,sha256=QwUZRzNUCKrpdOq6BHadIJSU47W_1Kat-ouzwue-olA,2857
|
|
11
|
+
mvn_tree_visualizer/outputs/json_output.py,sha256=cXntw9ndE_BcrmFnuV61cEwZaRMp9Ev0SxaK1SUedlw,2037
|
|
12
|
+
mvn_tree_visualizer-1.3.0.dist-info/METADATA,sha256=YHhhNT8CjZUB_rSbdOip-7anjiGD7NxmoYPdl4bZw50,7834
|
|
13
|
+
mvn_tree_visualizer-1.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
+
mvn_tree_visualizer-1.3.0.dist-info/entry_points.txt,sha256=Mu3QZhrlvbYuCxqmluVGi2efgKjkQY6T8Opf-vdb7hU,68
|
|
15
|
+
mvn_tree_visualizer-1.3.0.dist-info/licenses/LICENSE,sha256=4zi6unpe17RUDMBu7ebh14jdbyvyeT-UA3n8Zl7aW74,1075
|
|
16
|
+
mvn_tree_visualizer-1.3.0.dist-info/RECORD,,
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: mvn-tree-visualizer
|
|
3
|
-
Version: 1.1.0
|
|
4
|
-
Summary: A simple command line tool to visualize the dependency tree of a Maven project in a graphical format.
|
|
5
|
-
Project-URL: source, https://github.com/dyka3773/mvn-tree-visualizer
|
|
6
|
-
Author-email: Iraklis Konsoulas <dyka3773@gmail.com>
|
|
7
|
-
License-Expression: MIT
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Keywords: cli,command-line,dependency,graph,maven,mermaid,tool,tree,visualization
|
|
10
|
-
Classifier: Development Status :: 4 - Beta
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
-
Classifier: Topic :: Software Development :: Build Tools
|
|
15
|
-
Requires-Python: >=3.13
|
|
16
|
-
Requires-Dist: jinja2>=3.1.6
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
|
|
19
|
-
# Maven Dependency Tree Visualizer
|
|
20
|
-
|
|
21
|
-
[](https://badge.fury.io/py/mvn-tree-visualizer)
|
|
22
|
-
|
|
23
|
-
A simple command-line tool to visualize the dependency tree of a Maven project in a graphical and interactive format.
|
|
24
|
-
|
|
25
|
-
This tool was born out of the frustration of not being able to easily visualize the dependency tree of a Maven project. The `mvn dependency:tree` command is great, but the output can be hard to read, especially for large projects. This tool aims to solve that problem by providing a simple way to generate an interactive diagram of the dependency tree.
|
|
26
|
-
|
|
27
|
-
## Features
|
|
28
|
-
|
|
29
|
-
* **Interactive Diagrams:** Generates an interactive HTML diagram of your dependency tree using Mermaid.js.
|
|
30
|
-
* **Easy to Use:** A simple command-line interface that gets the job done with minimal configuration.
|
|
31
|
-
* **File Merging:** Automatically finds and merges multiple `maven_dependency_file` files from different subdirectories.
|
|
32
|
-
* **Customizable Output:** Specify the output file name and location.
|
|
33
|
-
* **SVG Export:** Download the generated diagram as an SVG file directly from the HTML page.
|
|
34
|
-
|
|
35
|
-
## How to Use
|
|
36
|
-
|
|
37
|
-
1. **Generate the dependency file:**
|
|
38
|
-
Run the following command in your terminal at the root of your Maven project. This will generate a file named `maven_dependency_file` in each module's `target` directory.
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
mvn dependency:tree -DoutputFile=maven_dependency_file -DappendOutput=true
|
|
42
|
-
```
|
|
43
|
-
> You can add other options like `-Dincludes="org.example"` to filter the dependencies.
|
|
44
|
-
|
|
45
|
-
2. **Visualize the dependency tree:**
|
|
46
|
-
Use the `mvn-tree-visualizer` command to generate the diagram.
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
mvn_tree_visualizer --filename "maven_dependency_file" --output "diagram.html"
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
3. **View the diagram:**
|
|
53
|
-
Open the generated `diagram.html` file in your web browser to view the interactive dependency tree.
|
|
54
|
-
|
|
55
|
-
## Options
|
|
56
|
-
|
|
57
|
-
* `--filename`: The name of the file containing the Maven dependency tree. Defaults to `maven_dependency_file`.
|
|
58
|
-
* `--output`: The name of the output HTML file. Defaults to `diagram.html`.
|
|
59
|
-
* `--directory`: The directory to scan for the Maven dependency file(s). Defaults to the current directory.
|
|
60
|
-
* `--keep-tree`: Keep the intermediate `dependency_tree.txt` file after generating the diagram. Defaults to `False`.
|
|
61
|
-
* `--help`: Show the help message and exit.
|
|
62
|
-
|
|
63
|
-
## Contributing
|
|
64
|
-
|
|
65
|
-
Contributions are welcome! If you have any ideas, suggestions, or bug reports, please open an issue or submit a pull request.
|
|
66
|
-
|
|
67
|
-
Please read our [CONTRIBUTING.md](CONTRIBUTING.md) file for more details.
|
|
68
|
-
|
|
69
|
-
## License
|
|
70
|
-
|
|
71
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
mvn_tree_visualizer/TEMPLATE.py,sha256=WIQfSNBygUZVkBrERq7QzqouGURA0NYVqUUm-11wMvo,2499
|
|
2
|
-
mvn_tree_visualizer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
mvn_tree_visualizer/__main__.py,sha256=yIQFAdWjthKAFbSzzRuz5_YGlK0c6BnR2ypjNRDq180,82
|
|
4
|
-
mvn_tree_visualizer/cli.py,sha256=P3v27Axod-aYkhtnoAh06RnScUTR2k9QZ3vVGkolHg8,2451
|
|
5
|
-
mvn_tree_visualizer/diagram.py,sha256=elUQHCFu9DPeCpcLkjlRg-xl-fAEn8TvMWpIho1vo0E,312
|
|
6
|
-
mvn_tree_visualizer/get_dependencies_in_one_file.py,sha256=sX669Lo3gsv-dTvaf4PNSQMTp3Gx3CKwqBmJQFiQF04,504
|
|
7
|
-
mvn_tree_visualizer/outputs/html_output.py,sha256=rCD55g8aUdYiPs2Mkd9VB0dBKvMZu0Va3uYGZkJxO20,2295
|
|
8
|
-
mvn_tree_visualizer/outputs/json_output.py,sha256=v9aRPaCDMG-zyVdg82YPhPRyFCNLaWXPcdRmnpzuajg,1554
|
|
9
|
-
mvn_tree_visualizer-1.1.0.dist-info/METADATA,sha256=VmIy1sZ1XHWlqnXJwzFKuG1Z3yxqZ2YV7OlEPaNvIuM,3470
|
|
10
|
-
mvn_tree_visualizer-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
11
|
-
mvn_tree_visualizer-1.1.0.dist-info/entry_points.txt,sha256=Mu3QZhrlvbYuCxqmluVGi2efgKjkQY6T8Opf-vdb7hU,68
|
|
12
|
-
mvn_tree_visualizer-1.1.0.dist-info/licenses/LICENSE,sha256=4zi6unpe17RUDMBu7ebh14jdbyvyeT-UA3n8Zl7aW74,1075
|
|
13
|
-
mvn_tree_visualizer-1.1.0.dist-info/RECORD,,
|
|
File without changes
|
{mvn_tree_visualizer-1.1.0.dist-info → mvn_tree_visualizer-1.3.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{mvn_tree_visualizer-1.1.0.dist-info → mvn_tree_visualizer-1.3.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|