impact-analysis 1.0.2 → 1.0.3
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/impact-analysis.html +295 -0
- package/index.mjs +148 -138
- package/index.zip +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<title>Impact Analysis</title>
|
|
7
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
+
<link href="https://fonts.googleapis.com/css2?family=Exo+2:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
|
11
|
+
<style>
|
|
12
|
+
body { font-family: "Exo 2", serif; background-color: #282828; color: #fff; overflow-x: hidden;
|
|
13
|
+
font-optical-sizing: auto; margin: 0; padding: 0; text-align: center; font-weight: 400;}
|
|
14
|
+
svg { width: 100%; height: 100vh; }
|
|
15
|
+
circle { cursor: pointer; }
|
|
16
|
+
text { font-size: 12px; fill: #fff; }
|
|
17
|
+
line { stroke: #fefefe; stroke-opacity: 0.6; }
|
|
18
|
+
#tooltip { position: absolute; background: #fff;color: #000; padding: 8px; border: 1px solid #ddd; display: none; }
|
|
19
|
+
.svg-wrapper{ padding: 10px; }
|
|
20
|
+
.impact-file-list {
|
|
21
|
+
display: list-item;
|
|
22
|
+
list-style: number;
|
|
23
|
+
text-align: left;
|
|
24
|
+
padding-left: 7px;
|
|
25
|
+
}
|
|
26
|
+
.impact-analysis-table {
|
|
27
|
+
width: 100%; border-collapse: collapse;
|
|
28
|
+
}
|
|
29
|
+
.impact-analysis-table td {
|
|
30
|
+
padding: 10px 20px;
|
|
31
|
+
}
|
|
32
|
+
th {
|
|
33
|
+
padding: 10px;
|
|
34
|
+
}
|
|
35
|
+
td:first-child {
|
|
36
|
+
background: #91fd9117;
|
|
37
|
+
}
|
|
38
|
+
.btn-theme {
|
|
39
|
+
min-width: 142px;
|
|
40
|
+
padding: 12px 16px;
|
|
41
|
+
font-size: 14px;
|
|
42
|
+
border-radius: 100px;
|
|
43
|
+
border: 1px solid #565454;
|
|
44
|
+
outline: none;
|
|
45
|
+
box-shadow: none;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
transition: all .3s ease-in-out;
|
|
48
|
+
text-decoration: none;
|
|
49
|
+
display: inline-flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
font-family: "Exo 2", serif;
|
|
52
|
+
background: #222;
|
|
53
|
+
color: #fff;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.btn-active {
|
|
57
|
+
background: #1f4afe;
|
|
58
|
+
background: linear-gradient(90deg,#1f4afe 35%,#167cfc);
|
|
59
|
+
color: #fff
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.btn-svg path {
|
|
63
|
+
fill: #fefefe;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.btn-active:hover svg path {
|
|
67
|
+
fill: #1f4afe;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.btn-theme:hover svg path {
|
|
71
|
+
fill: #1f4afe;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.btn-theme:hover {
|
|
75
|
+
background: #fff;
|
|
76
|
+
color: #1f4afe;
|
|
77
|
+
border: 1px solid #1F4AFE
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.btn-svg {
|
|
81
|
+
width: 20px;
|
|
82
|
+
height: 20px;
|
|
83
|
+
margin-right: 10px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
th {
|
|
87
|
+
background: #1f4afe;
|
|
88
|
+
background: linear-gradient(90deg,#1f4afe 35%,#167cfc);
|
|
89
|
+
width: 50%;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
th:last-child {
|
|
93
|
+
background: #1f4afe;
|
|
94
|
+
background: linear-gradient(-90deg,#1f4afe 35%,#167cfc);
|
|
95
|
+
}
|
|
96
|
+
</style>
|
|
97
|
+
</head>
|
|
98
|
+
<body>
|
|
99
|
+
<h1>Impact Analysis</h1>
|
|
100
|
+
<div id="tooltip"></div>
|
|
101
|
+
<div style="text-align: center; margin-bottom: 10px;">
|
|
102
|
+
<button id="graphViewBtn" class="btn-theme btn-active"><svg class="btn-svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
|
|
103
|
+
<g><g><path fill="#000000" d="M224.5,7.9c-11.8,0-21.5,9.6-21.5,21.5c0,7.4,3.8,14,9.6,17.9l-40.5,106.9c-1.1-0.2-2.3-0.3-3.4-0.3c-5.2,0-9.8,1.9-13.5,4.9l-40.6-33.5c1.6-3,2.6-6.4,2.6-10c0-11.8-9.6-21.5-21.5-21.5c-11.8,0-21.5,9.6-21.5,21.5c0,6.4,2.9,12.1,7.3,16l-43.3,75.3c-2.2-0.7-4.5-1.3-6.9-1.3c-11.8,0-21.5,9.6-21.5,21.5c0,11.8,9.6,21.5,21.5,21.5c11.8,0,21.5-9.6,21.5-21.5c0-6.3-2.8-11.9-7.1-15.8l43.3-75.4c2.1,0.7,4.3,1.2,6.7,1.2c5.1,0,9.7-1.9,13.4-4.8l40.6,33.6c-1.6,3-2.5,6.3-2.5,9.9c0,11.8,9.6,21.5,21.5,21.5c11.8,0,21.5-9.6,21.5-21.5c0-7.6-4-14.3-10-18.1l40.5-106.7c1.3,0.2,2.6,0.4,3.9,0.4c11.8,0,21.5-9.6,21.5-21.5C246,17.5,236.4,7.9,224.5,7.9L224.5,7.9z M31.5,239.6c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9c7.1,0,12.9,5.8,12.9,12.9C44.3,233.8,38.6,239.6,31.5,239.6z M95.7,128c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9c7.1,0,12.9,5.8,12.9,12.9C108.6,122.3,102.8,128,95.7,128z M168.7,188.1c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9c7.1,0,12.9,5.8,12.9,12.9C181.6,182.3,175.8,188.1,168.7,188.1L168.7,188.1z M224.5,42.2c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9c7.1,0,12.9,5.8,12.9,12.9C237.4,36.5,231.6,42.2,224.5,42.2L224.5,42.2z"/></g></g>
|
|
104
|
+
</svg> Graph View</button>
|
|
105
|
+
<button id="tableViewBtn" class="btn-theme"><svg class="btn-svg" width="12px" height="12px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
106
|
+
</defs>
|
|
107
|
+
<g id="64px-Line" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
108
|
+
<g id="db-table">
|
|
109
|
+
|
|
110
|
+
</g>
|
|
111
|
+
<path d="M56,12 C56,9.794 54.206,8 52,8 L10,8 C7.794,8 6,9.794 6,12 L6,54 C6,56.206 7.794,58 10,58 L52,58 C54.206,58 56,56.206 56,54 L56,12 L56,12 Z M30,22 L30,32 L20,32 L20,22 L30,22 L30,22 Z M42,22 L42,32 L32,32 L32,22 L42,22 L42,22 Z M54,22 L54,32 L44,32 L44,22 L54,22 L54,22 Z M18,32 L8,32 L8,22 L18,22 L18,32 L18,32 Z M8,34 L18,34 L18,44 L8,44 L8,34 L8,34 Z M20,34 L30,34 L30,44 L20,44 L20,34 L20,34 Z M30,46 L30,56 L20,56 L20,46 L30,46 L30,46 Z M32,46 L42,46 L42,56 L32,56 L32,46 L32,46 Z M32,44 L32,34 L42,34 L42,44 L32,44 L32,44 Z M44,34 L54,34 L54,44 L44,44 L44,34 L44,34 Z M10,10 L52,10 C53.103,10 54,10.897 54,12 L54,20 L8,20 L8,12 C8,10.897 8.897,10 10,10 L10,10 Z M8,54 L8,46 L18,46 L18,56 L10,56 C8.897,56 8,55.103 8,54 L8,54 Z M52,56 L44,56 L44,46 L54,46 L54,54 C54,55.103 53.103,56 52,56 L52,56 Z" id="Shape" fill="#000000">
|
|
112
|
+
|
|
113
|
+
</path>
|
|
114
|
+
</g>
|
|
115
|
+
</svg> Table View</button>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div id="graphView" style="display: block;">
|
|
119
|
+
<svg id="svgGraph" width="1000" height="600"></svg>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div id="tableView" style="display: none;padding: 20px;">
|
|
123
|
+
<table class="impact-analysis-table" border="1">
|
|
124
|
+
<thead>
|
|
125
|
+
<tr>
|
|
126
|
+
<th>Changed File</th>
|
|
127
|
+
<th>Impacted Files</th>
|
|
128
|
+
</tr>
|
|
129
|
+
</thead>
|
|
130
|
+
<tbody id="tableBody"></tbody>
|
|
131
|
+
</table>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
<script>
|
|
136
|
+
const impactData = [];
|
|
137
|
+
function getFileName(path) {
|
|
138
|
+
return path.split(/[/\\\\]/).pop();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const nodesMap = new Map();
|
|
142
|
+
impactData.forEach(d => {
|
|
143
|
+
nodesMap.set(d.changedFile, { id: d.changedFile, name: getFileName(d.changedFile), isChangedFile: true });
|
|
144
|
+
d.impactsTo.forEach(target => {
|
|
145
|
+
nodesMap.set(target, { id: target, name: getFileName(target), isChangedFile: false });
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
const nodes = Array.from(nodesMap.values());
|
|
149
|
+
const links = [];
|
|
150
|
+
const impactMap = new Map();
|
|
151
|
+
|
|
152
|
+
impactData.forEach(d => {
|
|
153
|
+
d.impactsTo.forEach(target => {
|
|
154
|
+
links.push({ source: d.changedFile, target });
|
|
155
|
+
if (!impactMap.has(target)) impactMap.set(target, new Set());
|
|
156
|
+
impactMap.get(target).add(d.changedFile);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
impactMap.forEach((sources, target) => {
|
|
161
|
+
sources.forEach(source1 => {
|
|
162
|
+
sources.forEach(source2 => {
|
|
163
|
+
if (source1 !== source2) {
|
|
164
|
+
links.push({ source: source1, target: source2 });
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const graph = { nodes, links };
|
|
171
|
+
|
|
172
|
+
const width = window.innerWidth;
|
|
173
|
+
const height = window.innerHeight - 100;
|
|
174
|
+
|
|
175
|
+
const svg = d3.select("#svgGraph")
|
|
176
|
+
.attr("width", width)
|
|
177
|
+
.attr("height", height);
|
|
178
|
+
|
|
179
|
+
const simulation = d3.forceSimulation(graph.nodes)
|
|
180
|
+
.force("link", d3.forceLink(graph.links)
|
|
181
|
+
.id(d => d.id)
|
|
182
|
+
.distance(80) // Reduce distance for a more compact layout
|
|
183
|
+
)
|
|
184
|
+
.force("charge", d3.forceManyBody()
|
|
185
|
+
.strength(-50) // Decrease negative strength to reduce spacing
|
|
186
|
+
)
|
|
187
|
+
.force("center", d3.forceCenter(width / 2, height / 2))
|
|
188
|
+
.force("collision", d3.forceCollide()
|
|
189
|
+
.radius(35) // Adjust radius to ensure nodes don't overlap but are closer
|
|
190
|
+
)
|
|
191
|
+
.on("tick", ticked);
|
|
192
|
+
|
|
193
|
+
const link = svg.append("g")
|
|
194
|
+
.selectAll("line")
|
|
195
|
+
.data(graph.links)
|
|
196
|
+
.enter().append("line")
|
|
197
|
+
.attr("stroke", "#aaa");
|
|
198
|
+
|
|
199
|
+
const nodeGroup = svg.append("g")
|
|
200
|
+
.selectAll("g")
|
|
201
|
+
.data(graph.nodes)
|
|
202
|
+
.enter().append("g")
|
|
203
|
+
.call(d3.drag()
|
|
204
|
+
.on("start", dragStarted)
|
|
205
|
+
.on("drag", dragged)
|
|
206
|
+
.on("end", dragEnded));
|
|
207
|
+
|
|
208
|
+
const node = nodeGroup.append("circle")
|
|
209
|
+
.attr("r", 10)
|
|
210
|
+
.attr("fill", d => d.isChangedFile ? "red" : "steelblue");
|
|
211
|
+
|
|
212
|
+
const labels = nodeGroup.append("text")
|
|
213
|
+
.attr("x", 12)
|
|
214
|
+
.attr("y", 5)
|
|
215
|
+
.text(d => d.name);
|
|
216
|
+
|
|
217
|
+
simulation.on("tick", () => {
|
|
218
|
+
link.attr("x1", d => d.source.x)
|
|
219
|
+
.attr("y1", d => d.source.y)
|
|
220
|
+
.attr("x2", d => d.target.x)
|
|
221
|
+
.attr("y2", d => d.target.y);
|
|
222
|
+
|
|
223
|
+
nodeGroup.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
function dragStarted(event, d) {
|
|
227
|
+
if (!event.active) simulation.alphaTarget(0.3).restart();
|
|
228
|
+
d.fx = d.x;
|
|
229
|
+
d.fy = d.y;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function dragged(event, d) {
|
|
233
|
+
d.fx = event.x;
|
|
234
|
+
d.fy = event.y;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function dragEnded(event, d) {
|
|
238
|
+
if (!event.active) simulation.alphaTarget(0);
|
|
239
|
+
d.fx = null;
|
|
240
|
+
d.fy = null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
document.getElementById("graphViewBtn").addEventListener("click", function () {
|
|
244
|
+
document.getElementById("graphView").style.display = "block";
|
|
245
|
+
document.getElementById("tableView").style.display = "none";
|
|
246
|
+
setActiveButton(this);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
document.getElementById("tableViewBtn").addEventListener("click", function () {
|
|
250
|
+
document.getElementById("graphView").style.display = "none";
|
|
251
|
+
document.getElementById("tableView").style.display = "block";
|
|
252
|
+
setActiveButton(this);
|
|
253
|
+
generateTableView(impactData);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
function setActiveButton(activeBtn) {
|
|
257
|
+
document.querySelectorAll(".btn-theme").forEach(button => {
|
|
258
|
+
button.classList.remove("btn-active");
|
|
259
|
+
});
|
|
260
|
+
activeBtn.classList.add("btn-active");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function generateTableView(data) {
|
|
264
|
+
const tableBody = document.getElementById("tableBody");
|
|
265
|
+
tableBody.innerHTML = "";
|
|
266
|
+
|
|
267
|
+
data.forEach(row => {
|
|
268
|
+
const tr = document.createElement("tr");
|
|
269
|
+
|
|
270
|
+
const tdFile = document.createElement("td");
|
|
271
|
+
tdFile.textContent = row.changedFile;
|
|
272
|
+
tr.appendChild(tdFile);
|
|
273
|
+
|
|
274
|
+
const tdImpact = document.createElement("td");
|
|
275
|
+
tdImpact.textContent = row.impactsTo.length ? row.impactsTo.join(", ") : "None";
|
|
276
|
+
tr.appendChild(tdImpact);
|
|
277
|
+
|
|
278
|
+
tableBody.appendChild(tr);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function ticked() {
|
|
282
|
+
node.attr("cx", d => d.x = Math.max(20, Math.min(width - 20, d.x)))
|
|
283
|
+
.attr("cy", d => d.y = Math.max(20, Math.min(height - 20, d.y)));
|
|
284
|
+
|
|
285
|
+
link.attr("x1", d => d.source.x)
|
|
286
|
+
.attr("y1", d => d.source.y)
|
|
287
|
+
.attr("x2", d => d.target.x)
|
|
288
|
+
.attr("y2", d => d.target.y);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
</script>
|
|
294
|
+
</body>
|
|
295
|
+
</html>
|
package/index.mjs
CHANGED
|
@@ -149,7 +149,7 @@ function generateGraphData(dependencyData) {
|
|
|
149
149
|
if (!nodes.has(impactedFile)) {
|
|
150
150
|
nodes.set(impactedFile, { id: impactedFileName, path: impactedFile, isChangedFile: false });
|
|
151
151
|
}
|
|
152
|
-
links.push({ source: changedFile, target: impactedFileName });
|
|
152
|
+
links.push({ id: changedFile, source: changedFile, target: impactedFileName });
|
|
153
153
|
});
|
|
154
154
|
});
|
|
155
155
|
|
|
@@ -160,7 +160,6 @@ function generateGraphData(dependencyData) {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
const graphData = generateGraphData(results);
|
|
163
|
-
|
|
164
163
|
const htmlContent = `
|
|
165
164
|
<!DOCTYPE html>
|
|
166
165
|
<html lang="en">
|
|
@@ -249,6 +248,7 @@ const htmlContent = `
|
|
|
249
248
|
th {
|
|
250
249
|
background: #1f4afe;
|
|
251
250
|
background: linear-gradient(90deg,#1f4afe 35%,#167cfc);
|
|
251
|
+
width: 50%;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
th:last-child {
|
|
@@ -295,151 +295,161 @@ const htmlContent = `
|
|
|
295
295
|
|
|
296
296
|
|
|
297
297
|
<script>
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const svg = d3.select("#svgGraph")
|
|
304
|
-
.attr("width", width)
|
|
305
|
-
.attr("height", height);
|
|
306
|
-
|
|
307
|
-
const simulation = d3.forceSimulation(graph.nodes)
|
|
308
|
-
.force("link", d3.forceLink(graph.links).id(d => d.id).distance(150)) // Increased distance for better spacing
|
|
309
|
-
.force("charge", d3.forceManyBody().strength(-200)) // Stronger repulsion to avoid overlap
|
|
310
|
-
.force("center", d3.forceCenter(width / 2, height / 2)) // Centering the graph
|
|
311
|
-
.force("collision", d3.forceCollide().radius(20)) // Prevents overlap
|
|
312
|
-
.on("tick", ticked);
|
|
313
|
-
|
|
314
|
-
const link = svg.selectAll("line")
|
|
315
|
-
.data(graph.links)
|
|
316
|
-
.enter().append("line");
|
|
317
|
-
|
|
318
|
-
const node = svg.selectAll("circle")
|
|
319
|
-
.data(graph.nodes)
|
|
320
|
-
.enter().append("circle")
|
|
321
|
-
.attr("r", d => d.isChangedFile ? 18 : 12) // Slightly larger nodes
|
|
322
|
-
.attr("fill", d => d.isChangedFile ? "#ff4500" : "#007bff") // Bright orange for changedFile, blue for others
|
|
323
|
-
.style("stroke", "#fff") // White border for clarity
|
|
324
|
-
.style("stroke-width", 2)
|
|
325
|
-
.call(drag(simulation));
|
|
326
|
-
|
|
327
|
-
const tooltip = d3.select("#tooltip");
|
|
328
|
-
|
|
329
|
-
node.on("mouseover", (event, d) => {
|
|
330
|
-
tooltip.style("display", "block")
|
|
331
|
-
.html(\`<strong>\${d.isChangedFile ? d.id : d.path}</strong>\`)
|
|
332
|
-
.style("left", (event.pageX + 10) + "px")
|
|
333
|
-
.style("top", (event.pageY - 10) + "px");
|
|
334
|
-
}).on("mouseout", () => tooltip.style("display", "none"));
|
|
335
|
-
|
|
336
|
-
svg.selectAll("text")
|
|
337
|
-
.data(graph.nodes)
|
|
338
|
-
.enter().append("text")
|
|
339
|
-
.attr("dy", 4)
|
|
340
|
-
.attr("x", 12)
|
|
341
|
-
.text(d => d.isChangedFile ? d.name : d.id);
|
|
342
|
-
|
|
343
|
-
simulation.on("tick", () => {
|
|
344
|
-
link.attr("x1", d => clamp(d.source.x, 0, width))
|
|
345
|
-
.attr("y1", d => clamp(d.source.y, 0, height))
|
|
346
|
-
.attr("x2", d => clamp(d.target.x, 0, width))
|
|
347
|
-
.attr("y2", d => clamp(d.target.y, 0, height));
|
|
348
|
-
|
|
349
|
-
node.attr("cx", d => clamp(d.x, 10, width - 10))
|
|
350
|
-
.attr("cy", d => clamp(d.y, 10, height - 10));
|
|
351
|
-
|
|
352
|
-
svg.selectAll("text")
|
|
353
|
-
.attr("x", d => clamp(d.x + 20, 10, width - 10))
|
|
354
|
-
.attr("y", d => clamp(d.y + 6, 10, height - 10));
|
|
355
|
-
});
|
|
356
|
-
function clamp(value, min, max) {
|
|
357
|
-
return Math.max(min, Math.min(max, value));
|
|
358
|
-
}
|
|
298
|
+
const impactData = ${JSON.stringify(dependencyJson)};
|
|
299
|
+
function getFileName(path) {
|
|
300
|
+
return path.split(/[/\\\\\\\\]/).pop();
|
|
301
|
+
}
|
|
359
302
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
303
|
+
const nodesMap = new Map();
|
|
304
|
+
impactData.forEach(d => {
|
|
305
|
+
nodesMap.set(d.changedFile, { id: d.changedFile, name: getFileName(d.changedFile), isChangedFile: true });
|
|
306
|
+
d.impactsTo.forEach(target => {
|
|
307
|
+
nodesMap.set(target, { id: target, name: getFileName(target), isChangedFile: false });
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
const nodes = Array.from(nodesMap.values());
|
|
311
|
+
const links = [];
|
|
312
|
+
const impactMap = new Map();
|
|
313
|
+
|
|
314
|
+
impactData.forEach(d => {
|
|
315
|
+
d.impactsTo.forEach(target => {
|
|
316
|
+
links.push({ source: d.changedFile, target });
|
|
317
|
+
if (!impactMap.has(target)) impactMap.set(target, new Set());
|
|
318
|
+
impactMap.get(target).add(d.changedFile);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
366
321
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
322
|
+
impactMap.forEach((sources, target) => {
|
|
323
|
+
sources.forEach(source1 => {
|
|
324
|
+
sources.forEach(source2 => {
|
|
325
|
+
if (source1 !== source2) {
|
|
326
|
+
links.push({ source: source1, target: source2 });
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
});
|
|
371
331
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
332
|
+
const graph = { nodes, links };
|
|
333
|
+
|
|
334
|
+
const width = window.innerWidth;
|
|
335
|
+
const height = window.innerHeight - 100;
|
|
336
|
+
|
|
337
|
+
const svg = d3.select("#svgGraph")
|
|
338
|
+
.attr("width", width)
|
|
339
|
+
.attr("height", height);
|
|
340
|
+
|
|
341
|
+
const simulation = d3.forceSimulation(graph.nodes)
|
|
342
|
+
.force("link", d3.forceLink(graph.links)
|
|
343
|
+
.id(d => d.id)
|
|
344
|
+
.distance(80)
|
|
345
|
+
)
|
|
346
|
+
.force("charge", d3.forceManyBody()
|
|
347
|
+
.strength(-50)
|
|
348
|
+
)
|
|
349
|
+
.force("center", d3.forceCenter(width / 2, height / 2))
|
|
350
|
+
.force("collision", d3.forceCollide()
|
|
351
|
+
.radius(35)
|
|
352
|
+
)
|
|
353
|
+
.on("tick", ticked);
|
|
354
|
+
|
|
355
|
+
const link = svg.append("g")
|
|
356
|
+
.selectAll("line")
|
|
357
|
+
.data(graph.links)
|
|
358
|
+
.enter().append("line")
|
|
359
|
+
.attr("stroke", "#aaa");
|
|
360
|
+
|
|
361
|
+
const nodeGroup = svg.append("g")
|
|
362
|
+
.selectAll("g")
|
|
363
|
+
.data(graph.nodes)
|
|
364
|
+
.enter().append("g")
|
|
365
|
+
.call(d3.drag()
|
|
366
|
+
.on("start", dragStarted)
|
|
367
|
+
.on("drag", dragged)
|
|
368
|
+
.on("end", dragEnded));
|
|
369
|
+
|
|
370
|
+
const node = nodeGroup.append("circle")
|
|
371
|
+
.attr("r", 10)
|
|
372
|
+
.attr("fill", d => d.isChangedFile ? "red" : "steelblue");
|
|
373
|
+
|
|
374
|
+
const labels = nodeGroup.append("text")
|
|
375
|
+
.attr("x", 12)
|
|
376
|
+
.attr("y", 5)
|
|
377
|
+
.text(d => d.name);
|
|
378
|
+
|
|
379
|
+
simulation.on("tick", () => {
|
|
380
|
+
link.attr("x1", d => d.source.x)
|
|
381
|
+
.attr("y1", d => d.source.y)
|
|
382
|
+
.attr("x2", d => d.target.x)
|
|
383
|
+
.attr("y2", d => d.target.y);
|
|
384
|
+
|
|
385
|
+
nodeGroup.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
function dragStarted(event, d) {
|
|
389
|
+
if (!event.active) simulation.alphaTarget(0.3).restart();
|
|
390
|
+
d.fx = d.x;
|
|
391
|
+
d.fy = d.y;
|
|
392
|
+
}
|
|
377
393
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
function ticked() {
|
|
384
|
-
link
|
|
385
|
-
.attr("x1", d => d.source.x)
|
|
386
|
-
.attr("y1", d => d.source.y)
|
|
387
|
-
.attr("x2", d => d.target.x)
|
|
388
|
-
.attr("y2", d => d.target.y);
|
|
389
|
-
|
|
390
|
-
node
|
|
391
|
-
.attr("cx", d => d.x)
|
|
392
|
-
.attr("cy", d => d.y);
|
|
393
|
-
|
|
394
|
-
label
|
|
395
|
-
.attr("x", d => d.x + 10)
|
|
396
|
-
.attr("y", d => d.y + 5);
|
|
397
|
-
}
|
|
394
|
+
function dragged(event, d) {
|
|
395
|
+
d.fx = event.x;
|
|
396
|
+
d.fy = event.y;
|
|
397
|
+
}
|
|
398
398
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
document.getElementById("tableViewBtn").addEventListener("click", function () {
|
|
406
|
-
document.getElementById("graphView").style.display = "none";
|
|
407
|
-
document.getElementById("tableView").style.display = "block";
|
|
408
|
-
setActiveButton(this);
|
|
409
|
-
generateTableView(${JSON.stringify(dependencyJson)});
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
function setActiveButton(activeBtn) {
|
|
413
|
-
document.querySelectorAll(".btn-theme").forEach(button => {
|
|
414
|
-
button.classList.remove("btn-active");
|
|
415
|
-
});
|
|
416
|
-
activeBtn.classList.add("btn-active");
|
|
417
|
-
}
|
|
399
|
+
function dragEnded(event, d) {
|
|
400
|
+
if (!event.active) simulation.alphaTarget(0);
|
|
401
|
+
d.fx = null;
|
|
402
|
+
d.fy = null;
|
|
403
|
+
}
|
|
418
404
|
|
|
419
|
-
function
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
tableBody.innerHTML = '<tr><td colspan="2">No data available</td></tr>';
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
tableData.forEach(function (result) {
|
|
428
|
-
var row = document.createElement("tr");
|
|
405
|
+
document.getElementById("graphViewBtn").addEventListener("click", function () {
|
|
406
|
+
document.getElementById("graphView").style.display = "block";
|
|
407
|
+
document.getElementById("tableView").style.display = "none";
|
|
408
|
+
setActiveButton(this);
|
|
409
|
+
});
|
|
429
410
|
|
|
430
|
-
|
|
431
|
-
|
|
411
|
+
document.getElementById("tableViewBtn").addEventListener("click", function () {
|
|
412
|
+
document.getElementById("graphView").style.display = "none";
|
|
413
|
+
document.getElementById("tableView").style.display = "block";
|
|
414
|
+
setActiveButton(this);
|
|
415
|
+
generateTableView(impactData);
|
|
416
|
+
});
|
|
432
417
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
418
|
+
function setActiveButton(activeBtn) {
|
|
419
|
+
document.querySelectorAll(".btn-theme").forEach(button => {
|
|
420
|
+
button.classList.remove("btn-active");
|
|
421
|
+
});
|
|
422
|
+
activeBtn.classList.add("btn-active");
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function generateTableView(data) {
|
|
426
|
+
const tableBody = document.getElementById("tableBody");
|
|
427
|
+
tableBody.innerHTML = "";
|
|
428
|
+
|
|
429
|
+
data.forEach(row => {
|
|
430
|
+
const tr = document.createElement("tr");
|
|
431
|
+
|
|
432
|
+
const tdFile = document.createElement("td");
|
|
433
|
+
tdFile.textContent = row.changedFile;
|
|
434
|
+
tr.appendChild(tdFile);
|
|
435
|
+
|
|
436
|
+
const tdImpact = document.createElement("td");
|
|
437
|
+
tdImpact.textContent = row.impactsTo.length ? row.impactsTo.join(", ") : "No Impact";
|
|
438
|
+
tr.appendChild(tdImpact);
|
|
439
|
+
|
|
440
|
+
tableBody.appendChild(tr);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
function ticked() {
|
|
444
|
+
node.attr("cx", d => d.x = Math.max(20, Math.min(width - 20, d.x)))
|
|
445
|
+
.attr("cy", d => d.y = Math.max(20, Math.min(height - 20, d.y)));
|
|
446
|
+
|
|
447
|
+
link.attr("x1", d => d.source.x)
|
|
448
|
+
.attr("y1", d => d.source.y)
|
|
449
|
+
.attr("x2", d => d.target.x)
|
|
450
|
+
.attr("y2", d => d.target.y);
|
|
451
|
+
}
|
|
437
452
|
|
|
438
|
-
row.appendChild(changedFileCell);
|
|
439
|
-
row.appendChild(impactsToCell);
|
|
440
|
-
tableBody.appendChild(row);
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
453
|
|
|
444
454
|
|
|
445
455
|
</script>
|
package/index.zip
ADDED
|
Binary file
|