easy-dep-graph 1.1.0 → 1.1.2
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/bin/index.js +109 -82
- package/package.json +3 -1
package/bin/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import fastify from "fastify";
|
|
|
5
5
|
import open from "open";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
|
+
import semver from "semver";
|
|
8
9
|
let flatDeps;
|
|
9
10
|
void (async function main() {
|
|
10
11
|
// Check if peer dependencies mode
|
|
@@ -55,11 +56,24 @@ void (async function main() {
|
|
|
55
56
|
name: packageInfo.name,
|
|
56
57
|
applyForceLayout: shouldApplyForceLayout,
|
|
57
58
|
nodes: JSON.stringify(nodeEntries.map((dep, index) => {
|
|
59
|
+
let x, y;
|
|
60
|
+
if (shouldApplyForceLayout) {
|
|
61
|
+
// Random initial position for force layout
|
|
62
|
+
x = Math.random() * 100 - 50;
|
|
63
|
+
y = Math.random() * 100 - 50;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Arrange in a circle with equal spacing
|
|
67
|
+
const radius = Math.max(50, nodeCount * 3);
|
|
68
|
+
const angle = (index / nodeCount) * 2 * Math.PI;
|
|
69
|
+
x = Math.cos(angle) * radius;
|
|
70
|
+
y = Math.sin(angle) * radius;
|
|
71
|
+
}
|
|
58
72
|
return {
|
|
59
73
|
key: dep[0],
|
|
60
74
|
label: `${dep[0]} V${dep[1].version}`,
|
|
61
|
-
x
|
|
62
|
-
y
|
|
75
|
+
x,
|
|
76
|
+
y,
|
|
63
77
|
size: 10,
|
|
64
78
|
color: dep[1].isRoot ? "#22c55e" : "#3b82f6",
|
|
65
79
|
};
|
|
@@ -117,27 +131,24 @@ async function runPeerDependenciesMode(port, shouldOpenBrowser) {
|
|
|
117
131
|
...packageJson.dependencies,
|
|
118
132
|
...packageJson.devDependencies,
|
|
119
133
|
};
|
|
120
|
-
//
|
|
121
|
-
const
|
|
122
|
-
windowsHide: true,
|
|
123
|
-
silent: true,
|
|
124
|
-
});
|
|
125
|
-
const packageInfo = JSON.parse(result.stdout);
|
|
126
|
-
// Collect all package names from the dependency tree
|
|
127
|
-
const allPackageNames = new Set();
|
|
128
|
-
collectAllPackageNames(packageInfo.dependencies, allPackageNames);
|
|
129
|
-
// Collect all peer dependencies by reading package.json files
|
|
130
|
-
const peerDeps = collectPeerDependenciesFromNodeModules(allPackageNames);
|
|
134
|
+
// Collect all peer dependencies recursively starting from installed packages
|
|
135
|
+
const peerDeps = collectPeerDependenciesRecursively(installedPackages);
|
|
131
136
|
// Resolve versions
|
|
132
137
|
const peerDepsList = Object.entries(peerDeps).map(([name, info]) => {
|
|
133
138
|
const resolvedVersion = resolveVersion(info.versions);
|
|
134
|
-
const
|
|
139
|
+
const isInPackageJson = installedPackages[name] !== undefined;
|
|
140
|
+
// Check if installed in node_modules (even if not in package.json)
|
|
141
|
+
const nodeModulesPath = path.join(process.cwd(), "node_modules");
|
|
142
|
+
const pkgPath = path.join(nodeModulesPath, name, "package.json");
|
|
143
|
+
const existsInNodeModules = fs.existsSync(pkgPath);
|
|
144
|
+
const isInstalledByDependency = existsInNodeModules && !isInPackageJson;
|
|
135
145
|
return {
|
|
136
146
|
name,
|
|
137
147
|
version: resolvedVersion.version,
|
|
138
148
|
isConflict: resolvedVersion.isConflict,
|
|
139
149
|
requiredBy: info.requiredBy,
|
|
140
|
-
isInstalled,
|
|
150
|
+
isInstalled: isInPackageJson,
|
|
151
|
+
isInstalledByDependency,
|
|
141
152
|
};
|
|
142
153
|
});
|
|
143
154
|
// Sort by name
|
|
@@ -184,26 +195,25 @@ async function runPeerDependenciesMode(port, shouldOpenBrowser) {
|
|
|
184
195
|
open(address);
|
|
185
196
|
});
|
|
186
197
|
}
|
|
187
|
-
function
|
|
188
|
-
if (deps == null)
|
|
189
|
-
return;
|
|
190
|
-
for (const [depName, depInfo] of Object.entries(deps)) {
|
|
191
|
-
packageNames.add(depName);
|
|
192
|
-
if (depInfo.dependencies) {
|
|
193
|
-
collectAllPackageNames(depInfo.dependencies, packageNames);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function collectPeerDependenciesFromNodeModules(packageNames) {
|
|
198
|
+
function collectPeerDependenciesRecursively(initialPackages) {
|
|
198
199
|
const peerDeps = {};
|
|
199
200
|
const nodeModulesPath = path.join(process.cwd(), "node_modules");
|
|
200
|
-
|
|
201
|
+
const visited = new Set();
|
|
202
|
+
const queue = Object.keys(initialPackages);
|
|
203
|
+
while (queue.length > 0) {
|
|
204
|
+
const packageName = queue.shift();
|
|
205
|
+
// Skip if already processed
|
|
206
|
+
if (visited.has(packageName)) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
visited.add(packageName);
|
|
201
210
|
try {
|
|
202
211
|
const pkgJsonPath = path.join(nodeModulesPath, packageName, "package.json");
|
|
203
212
|
if (!fs.existsSync(pkgJsonPath)) {
|
|
204
213
|
continue;
|
|
205
214
|
}
|
|
206
215
|
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
216
|
+
// Collect peer dependencies
|
|
207
217
|
if (pkgJson.peerDependencies) {
|
|
208
218
|
for (const [peerDepName, peerDepVersion] of Object.entries(pkgJson.peerDependencies)) {
|
|
209
219
|
if (!peerDeps[peerDepName]) {
|
|
@@ -219,40 +229,25 @@ function collectPeerDependenciesFromNodeModules(packageNames) {
|
|
|
219
229
|
if (!peerDeps[peerDepName].requiredBy.includes(packageName)) {
|
|
220
230
|
peerDeps[peerDepName].requiredBy.push(packageName);
|
|
221
231
|
}
|
|
232
|
+
// Add peer dependency to queue for processing
|
|
233
|
+
if (!visited.has(peerDepName)) {
|
|
234
|
+
queue.push(peerDepName);
|
|
235
|
+
}
|
|
222
236
|
}
|
|
223
237
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
function collectPeerDependencies(deps, peerDeps = {}, parentName) {
|
|
233
|
-
if (deps == null)
|
|
234
|
-
return peerDeps;
|
|
235
|
-
for (const [depName, depInfo] of Object.entries(deps)) {
|
|
236
|
-
// Check if this dependency has peer dependencies
|
|
237
|
-
if (depInfo.peerDependencies) {
|
|
238
|
-
for (const [peerDepName, peerDepVersion] of Object.entries(depInfo.peerDependencies)) {
|
|
239
|
-
if (!peerDeps[peerDepName]) {
|
|
240
|
-
peerDeps[peerDepName] = {
|
|
241
|
-
versions: [],
|
|
242
|
-
requiredBy: [],
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
if (!peerDeps[peerDepName].versions.includes(peerDepVersion)) {
|
|
246
|
-
peerDeps[peerDepName].versions.push(peerDepVersion);
|
|
247
|
-
}
|
|
248
|
-
if (!peerDeps[peerDepName].requiredBy.includes(depName)) {
|
|
249
|
-
peerDeps[peerDepName].requiredBy.push(depName);
|
|
238
|
+
// Collect dependencies and devDependencies to continue traversal
|
|
239
|
+
const allDeps = {
|
|
240
|
+
...pkgJson.dependencies,
|
|
241
|
+
...pkgJson.devDependencies,
|
|
242
|
+
};
|
|
243
|
+
for (const depName of Object.keys(allDeps)) {
|
|
244
|
+
if (!visited.has(depName)) {
|
|
245
|
+
queue.push(depName);
|
|
250
246
|
}
|
|
251
247
|
}
|
|
252
248
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
collectPeerDependencies(depInfo.dependencies, peerDeps, depName);
|
|
249
|
+
catch (error) {
|
|
250
|
+
console.warn(`Warning: Could not read package.json for ${packageName}`);
|
|
256
251
|
}
|
|
257
252
|
}
|
|
258
253
|
return peerDeps;
|
|
@@ -264,35 +259,42 @@ function resolveVersion(versions) {
|
|
|
264
259
|
if (versions.length === 1) {
|
|
265
260
|
return { version: versions[0], isConflict: false };
|
|
266
261
|
}
|
|
267
|
-
//
|
|
268
|
-
// For simplicity, if all versions are the same, use that
|
|
262
|
+
// Get unique versions
|
|
269
263
|
const uniqueVersions = [...new Set(versions)];
|
|
270
264
|
if (uniqueVersions.length === 1) {
|
|
271
265
|
return { version: uniqueVersions[0], isConflict: false };
|
|
272
266
|
}
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
const
|
|
280
|
-
|
|
267
|
+
// Use semver to find intersecting range
|
|
268
|
+
try {
|
|
269
|
+
// Try to find a version range that satisfies all requirements
|
|
270
|
+
let intersection = uniqueVersions[0];
|
|
271
|
+
for (let i = 1; i < uniqueVersions.length; i++) {
|
|
272
|
+
const range1 = intersection;
|
|
273
|
+
const range2 = uniqueVersions[i];
|
|
274
|
+
// Check if ranges intersect
|
|
275
|
+
if (semver.intersects(range1, range2)) {
|
|
276
|
+
// Find the more restrictive range
|
|
277
|
+
// Use the one with higher minimum version
|
|
278
|
+
const min1 = semver.minVersion(range1);
|
|
279
|
+
const min2 = semver.minVersion(range2);
|
|
280
|
+
if (min1 && min2) {
|
|
281
|
+
intersection = semver.gt(min1, min2) ? range1 : range2;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
intersection = range1;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
// Ranges don't intersect - conflict detected
|
|
289
|
+
return { version: uniqueVersions.join(" | "), isConflict: true };
|
|
290
|
+
}
|
|
281
291
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const sorted = [...uniqueVersions].sort((a, b) => {
|
|
288
|
-
const aVer = a.substring(1);
|
|
289
|
-
const bVer = b.substring(1);
|
|
290
|
-
return bVer.localeCompare(aVer, undefined, { numeric: true });
|
|
291
|
-
});
|
|
292
|
-
return { version: sorted[0], isConflict: false };
|
|
292
|
+
return { version: intersection, isConflict: false };
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
// If semver parsing fails, fall back to showing all versions
|
|
296
|
+
return { version: uniqueVersions.join(" | "), isConflict: true };
|
|
293
297
|
}
|
|
294
|
-
// Conflict detected
|
|
295
|
-
return { version: uniqueVersions.join(" | "), isConflict: true };
|
|
296
298
|
}
|
|
297
299
|
function getPeerDepsTemplate() {
|
|
298
300
|
return `
|
|
@@ -454,11 +456,28 @@ function getPeerDepsTemplate() {
|
|
|
454
456
|
font-weight: 600;
|
|
455
457
|
}
|
|
456
458
|
|
|
459
|
+
.installed-by-dep-badge {
|
|
460
|
+
display: flex;
|
|
461
|
+
align-items: center;
|
|
462
|
+
gap: 0.5rem;
|
|
463
|
+
padding: 0.5rem 1rem;
|
|
464
|
+
background: #e0e7ff;
|
|
465
|
+
color: #3730a3;
|
|
466
|
+
border-radius: 6px;
|
|
467
|
+
font-size: 0.875rem;
|
|
468
|
+
font-weight: 600;
|
|
469
|
+
}
|
|
470
|
+
|
|
457
471
|
.checkmark {
|
|
458
472
|
color: #10b981;
|
|
459
473
|
font-size: 1.25rem;
|
|
460
474
|
}
|
|
461
475
|
|
|
476
|
+
.info-icon {
|
|
477
|
+
color: #4f46e5;
|
|
478
|
+
font-size: 1.25rem;
|
|
479
|
+
}
|
|
480
|
+
|
|
462
481
|
.message {
|
|
463
482
|
margin-top: 0.5rem;
|
|
464
483
|
padding: 0.5rem;
|
|
@@ -515,11 +534,19 @@ function getPeerDepsTemplate() {
|
|
|
515
534
|
Installed
|
|
516
535
|
</div>
|
|
517
536
|
{{/isInstalled}}
|
|
537
|
+
{{#isInstalledByDependency}}
|
|
538
|
+
<div class="installed-by-dep-badge">
|
|
539
|
+
<span class="info-icon">ℹ</span>
|
|
540
|
+
Install by dependency
|
|
541
|
+
</div>
|
|
542
|
+
{{/isInstalledByDependency}}
|
|
518
543
|
{{^isInstalled}}
|
|
544
|
+
{{^isInstalledByDependency}}
|
|
519
545
|
<button class="install-btn" onclick="installPackage('{{name}}', '{{version}}', this)">
|
|
520
546
|
npm install
|
|
521
547
|
</button>
|
|
522
548
|
<div class="message" id="msg-{{name}}"></div>
|
|
549
|
+
{{/isInstalledByDependency}}
|
|
523
550
|
{{/isInstalled}}
|
|
524
551
|
</div>
|
|
525
552
|
</div>
|
|
@@ -595,8 +622,8 @@ function getTemplate() {
|
|
|
595
622
|
var nodes = graph.nodes();
|
|
596
623
|
var edges = graph.edges();
|
|
597
624
|
|
|
598
|
-
// Physics constants
|
|
599
|
-
var repulsionStrength =
|
|
625
|
+
// Physics constants - lower repulsion for better spacing
|
|
626
|
+
var repulsionStrength = 50;
|
|
600
627
|
var attractionStrength = 0.01;
|
|
601
628
|
var damping = 0.5;
|
|
602
629
|
|
|
@@ -678,7 +705,7 @@ function getTemplate() {
|
|
|
678
705
|
});
|
|
679
706
|
|
|
680
707
|
edges.forEach(function(edge, index) {
|
|
681
|
-
graph.
|
|
708
|
+
graph.mergeEdge(edge.source, edge.target, {
|
|
682
709
|
size: 2,
|
|
683
710
|
color: '#94a3b8'
|
|
684
711
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "easy-dep-graph",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Easily see the dependency graph of your npm project",
|
|
5
5
|
"homepage": "https://github.com/danisss9/easy-dep-graph#readme",
|
|
6
6
|
"repository": {
|
|
@@ -54,11 +54,13 @@
|
|
|
54
54
|
"fastify": "^5.7.4",
|
|
55
55
|
"mustache": "^4.2.0",
|
|
56
56
|
"open": "^11.0.0",
|
|
57
|
+
"semver": "^7.7.3",
|
|
57
58
|
"shelljs": "^0.10.0"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
61
|
"@types/mustache": "^4.2.6",
|
|
61
62
|
"@types/node": "^25.2.1",
|
|
63
|
+
"@types/semver": "^7.7.1",
|
|
62
64
|
"@types/shelljs": "^0.10.0",
|
|
63
65
|
"typescript": "^5.9.3"
|
|
64
66
|
}
|