easy-dep-graph 1.1.0 → 1.1.1
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 +279 -263
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -7,295 +7,311 @@ import fs from "node:fs";
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
let flatDeps;
|
|
9
9
|
void (async function main() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
10
|
+
// Check if peer dependencies mode
|
|
11
|
+
const peerDependenciesMode =
|
|
12
|
+
process.argv.findIndex((a) => a.toLowerCase() === "--peer-dependencies") !==
|
|
13
|
+
-1;
|
|
14
|
+
// List of packages user wants to see the dependencies
|
|
15
|
+
const packagesArgIndex = process.argv.findIndex(
|
|
16
|
+
(a) => a.toLowerCase() === "--packages",
|
|
17
|
+
);
|
|
18
|
+
const packagesFilter =
|
|
19
|
+
packagesArgIndex !== -1 ? process.argv[packagesArgIndex + 1] : undefined;
|
|
20
|
+
// Only get the dependents of a specific package
|
|
21
|
+
const packageArgIndex = process.argv.findIndex(
|
|
22
|
+
(a) => a.toLowerCase() === "--package-dependents",
|
|
23
|
+
);
|
|
24
|
+
const packageName =
|
|
25
|
+
packageArgIndex !== -1 ? process.argv[packageArgIndex + 1] : undefined;
|
|
26
|
+
// Get port number
|
|
27
|
+
const portArgIndex = process.argv.findIndex(
|
|
28
|
+
(a) => a.toLowerCase() === "--port",
|
|
29
|
+
);
|
|
30
|
+
const port = portArgIndex !== -1 ? +process.argv[portArgIndex + 1] : 8080;
|
|
31
|
+
// Get if should not open browser
|
|
32
|
+
const shouldOpenBrowser =
|
|
33
|
+
process.argv.findIndex((a) => a.toLowerCase() === "--no-open") === -1;
|
|
34
|
+
// Get if should not apply force layout
|
|
35
|
+
const shouldApplyForceLayout =
|
|
36
|
+
process.argv.findIndex((a) => a.toLowerCase() === "--no-force-layout") ===
|
|
37
|
+
-1;
|
|
38
|
+
if (peerDependenciesMode) {
|
|
39
|
+
await runPeerDependenciesMode(port, shouldOpenBrowser);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Run npm list command
|
|
43
|
+
const result = shell.exec(`npm list --json ${packageName ?? "--all"}`, {
|
|
44
|
+
windowsHide: true,
|
|
45
|
+
silent: true,
|
|
46
|
+
});
|
|
47
|
+
// Parse the result to a JSON object
|
|
48
|
+
const packageInfo = JSON.parse(result.stdout);
|
|
49
|
+
console.log(`Generating dependency graph for: "${packageInfo.name}"...`);
|
|
50
|
+
// Filter dependencies if needed
|
|
51
|
+
if (packagesFilter != null) {
|
|
52
|
+
const packagesFilterList = packagesFilter.split(",").map((d) => d.trim());
|
|
53
|
+
for (const key of Object.keys(packageInfo.dependencies)) {
|
|
54
|
+
if (!packagesFilterList.includes(key)) {
|
|
55
|
+
delete packageInfo.dependencies[key];
|
|
56
|
+
}
|
|
47
57
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
}
|
|
59
|
+
// Flat the dependencies from npm list
|
|
60
|
+
flatDeps = {};
|
|
61
|
+
flatDepsRecursive(packageInfo.dependencies);
|
|
62
|
+
// Turn deps into nodes and edges
|
|
63
|
+
const nodeEntries = Object.entries(flatDeps);
|
|
64
|
+
const nodeCount = nodeEntries.length;
|
|
65
|
+
const data = {
|
|
66
|
+
name: packageInfo.name,
|
|
67
|
+
applyForceLayout: shouldApplyForceLayout,
|
|
68
|
+
nodes: JSON.stringify(
|
|
69
|
+
nodeEntries.map((dep, index) => {
|
|
70
|
+
return {
|
|
71
|
+
key: dep[0],
|
|
72
|
+
label: `${dep[0]} V${dep[1].version}`,
|
|
73
|
+
x: Math.random() * 100 - 50,
|
|
74
|
+
y: Math.random() * 100 - 50,
|
|
75
|
+
size: 10,
|
|
76
|
+
color: dep[1].isRoot ? "#22c55e" : "#3b82f6",
|
|
77
|
+
};
|
|
78
|
+
}),
|
|
79
|
+
),
|
|
80
|
+
edges: JSON.stringify(
|
|
81
|
+
Object.entries(flatDeps)
|
|
82
|
+
.filter((dep) => !!dep[1].dependencies.length)
|
|
83
|
+
.flatMap((dep) =>
|
|
84
|
+
dep[1].dependencies.map((d) => ({
|
|
70
85
|
source: dep[0],
|
|
71
86
|
target: d,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
})),
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
};
|
|
91
|
+
// Render the UI using mustache
|
|
92
|
+
const html = mustache.render(getTemplate(), data);
|
|
93
|
+
// Initialize server to serve graph
|
|
94
|
+
const app = fastify({
|
|
95
|
+
logger: false,
|
|
96
|
+
});
|
|
97
|
+
app.get("/", (_req, resp) => resp.type("text/html").send(html));
|
|
98
|
+
// Run the server
|
|
99
|
+
app.listen({ port }, (err, address) => {
|
|
100
|
+
if (err) throw err;
|
|
101
|
+
console.log(`Done! Visit "${address}" to see dependecy graph.`);
|
|
102
|
+
if (shouldOpenBrowser) open(address);
|
|
103
|
+
});
|
|
89
104
|
})();
|
|
90
105
|
function flatDepsRecursive(deps, parentDepName) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
parentDep.dependencies.push(depName);
|
|
100
|
-
}
|
|
101
|
-
if (flatDeps[depName] == null) {
|
|
102
|
-
flatDeps[depName] = {
|
|
103
|
-
version: depInfo.version,
|
|
104
|
-
isRoot,
|
|
105
|
-
dependencies: [],
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
flatDepsRecursive(depInfo.dependencies, depName);
|
|
106
|
+
if (deps == null) return;
|
|
107
|
+
for (const dep of Object.entries(deps)) {
|
|
108
|
+
const depName = dep[0];
|
|
109
|
+
const depInfo = dep[1];
|
|
110
|
+
const isRoot = parentDepName == null;
|
|
111
|
+
if (!isRoot) {
|
|
112
|
+
const parentDep = flatDeps[parentDepName];
|
|
113
|
+
parentDep.dependencies.push(depName);
|
|
109
114
|
}
|
|
115
|
+
if (flatDeps[depName] == null) {
|
|
116
|
+
flatDeps[depName] = {
|
|
117
|
+
version: depInfo.version,
|
|
118
|
+
isRoot,
|
|
119
|
+
dependencies: [],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
flatDepsRecursive(depInfo.dependencies, depName);
|
|
123
|
+
}
|
|
110
124
|
}
|
|
111
125
|
async function runPeerDependenciesMode(port, shouldOpenBrowser) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
126
|
+
console.log("Analyzing peer dependencies...");
|
|
127
|
+
// Get the current project's package.json
|
|
128
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
129
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
130
|
+
const installedPackages = {
|
|
131
|
+
...packageJson.dependencies,
|
|
132
|
+
...packageJson.devDependencies,
|
|
133
|
+
};
|
|
134
|
+
// Run npm list to get all dependencies
|
|
135
|
+
const result = shell.exec("npm list --json --all", {
|
|
136
|
+
windowsHide: true,
|
|
137
|
+
silent: true,
|
|
138
|
+
});
|
|
139
|
+
const packageInfo = JSON.parse(result.stdout);
|
|
140
|
+
// Collect all package names from the dependency tree
|
|
141
|
+
const allPackageNames = new Set();
|
|
142
|
+
collectAllPackageNames(packageInfo.dependencies, allPackageNames);
|
|
143
|
+
// Collect all peer dependencies by reading package.json files
|
|
144
|
+
const peerDeps = collectPeerDependenciesFromNodeModules(allPackageNames);
|
|
145
|
+
// Resolve versions
|
|
146
|
+
const peerDepsList = Object.entries(peerDeps).map(([name, info]) => {
|
|
147
|
+
const resolvedVersion = resolveVersion(info.versions);
|
|
148
|
+
const isInstalled = installedPackages[name] !== undefined;
|
|
149
|
+
return {
|
|
150
|
+
name,
|
|
151
|
+
version: resolvedVersion.version,
|
|
152
|
+
isConflict: resolvedVersion.isConflict,
|
|
153
|
+
requiredBy: info.requiredBy,
|
|
154
|
+
isInstalled,
|
|
119
155
|
};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
isInstalled,
|
|
141
|
-
};
|
|
142
|
-
});
|
|
143
|
-
// Sort by name
|
|
144
|
-
peerDepsList.sort((a, b) => a.name.localeCompare(b.name));
|
|
145
|
-
const data = {
|
|
146
|
-
projectName: packageJson.name,
|
|
147
|
-
peerDeps: peerDepsList,
|
|
148
|
-
};
|
|
149
|
-
// Render the UI using mustache
|
|
150
|
-
const html = mustache.render(getPeerDepsTemplate(), data);
|
|
151
|
-
// Initialize server to serve graph
|
|
152
|
-
const app = fastify({
|
|
153
|
-
logger: false,
|
|
154
|
-
});
|
|
155
|
-
app.get("/", (_req, resp) => resp.type("text/html").send(html));
|
|
156
|
-
app.post("/install", async (req, resp) => {
|
|
157
|
-
const { package: pkg, version } = req.body;
|
|
158
|
-
console.log(`Installing ${pkg}@${version}...`);
|
|
159
|
-
const installResult = shell.exec(`npm install ${pkg}@${version}`, {
|
|
160
|
-
windowsHide: true,
|
|
161
|
-
silent: true,
|
|
162
|
-
});
|
|
163
|
-
if (installResult.code === 0) {
|
|
164
|
-
console.log(`Successfully installed ${pkg}@${version}`);
|
|
165
|
-
return {
|
|
166
|
-
success: true,
|
|
167
|
-
message: `Successfully installed ${pkg}@${version}`,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
console.error(`Failed to install ${pkg}@${version}`);
|
|
172
|
-
return {
|
|
173
|
-
success: false,
|
|
174
|
-
message: installResult.stderr || "Installation failed",
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
// Run the server
|
|
179
|
-
app.listen({ port }, (err, address) => {
|
|
180
|
-
if (err)
|
|
181
|
-
throw err;
|
|
182
|
-
console.log(`Done! Visit "${address}" to see peer dependencies.`);
|
|
183
|
-
if (shouldOpenBrowser)
|
|
184
|
-
open(address);
|
|
156
|
+
});
|
|
157
|
+
// Sort by name
|
|
158
|
+
peerDepsList.sort((a, b) => a.name.localeCompare(b.name));
|
|
159
|
+
const data = {
|
|
160
|
+
projectName: packageJson.name,
|
|
161
|
+
peerDeps: peerDepsList,
|
|
162
|
+
};
|
|
163
|
+
// Render the UI using mustache
|
|
164
|
+
const html = mustache.render(getPeerDepsTemplate(), data);
|
|
165
|
+
// Initialize server to serve graph
|
|
166
|
+
const app = fastify({
|
|
167
|
+
logger: false,
|
|
168
|
+
});
|
|
169
|
+
app.get("/", (_req, resp) => resp.type("text/html").send(html));
|
|
170
|
+
app.post("/install", async (req, resp) => {
|
|
171
|
+
const { package: pkg, version } = req.body;
|
|
172
|
+
console.log(`Installing ${pkg}@${version}...`);
|
|
173
|
+
const installResult = shell.exec(`npm install ${pkg}@${version}`, {
|
|
174
|
+
windowsHide: true,
|
|
175
|
+
silent: true,
|
|
185
176
|
});
|
|
177
|
+
if (installResult.code === 0) {
|
|
178
|
+
console.log(`Successfully installed ${pkg}@${version}`);
|
|
179
|
+
return {
|
|
180
|
+
success: true,
|
|
181
|
+
message: `Successfully installed ${pkg}@${version}`,
|
|
182
|
+
};
|
|
183
|
+
} else {
|
|
184
|
+
console.error(`Failed to install ${pkg}@${version}`);
|
|
185
|
+
return {
|
|
186
|
+
success: false,
|
|
187
|
+
message: installResult.stderr || "Installation failed",
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
// Run the server
|
|
192
|
+
app.listen({ port }, (err, address) => {
|
|
193
|
+
if (err) throw err;
|
|
194
|
+
console.log(`Done! Visit "${address}" to see peer dependencies.`);
|
|
195
|
+
if (shouldOpenBrowser) open(address);
|
|
196
|
+
});
|
|
186
197
|
}
|
|
187
198
|
function collectAllPackageNames(deps, packageNames) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
collectAllPackageNames(depInfo.dependencies, packageNames);
|
|
194
|
-
}
|
|
199
|
+
if (deps == null) return;
|
|
200
|
+
for (const [depName, depInfo] of Object.entries(deps)) {
|
|
201
|
+
packageNames.add(depName);
|
|
202
|
+
if (depInfo.dependencies) {
|
|
203
|
+
collectAllPackageNames(depInfo.dependencies, packageNames);
|
|
195
204
|
}
|
|
205
|
+
}
|
|
196
206
|
}
|
|
197
207
|
function collectPeerDependenciesFromNodeModules(packageNames) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
208
|
+
const peerDeps = {};
|
|
209
|
+
const nodeModulesPath = path.join(process.cwd(), "node_modules");
|
|
210
|
+
for (const packageName of packageNames) {
|
|
211
|
+
try {
|
|
212
|
+
const pkgJsonPath = path.join(
|
|
213
|
+
nodeModulesPath,
|
|
214
|
+
packageName,
|
|
215
|
+
"package.json",
|
|
216
|
+
);
|
|
217
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
221
|
+
if (pkgJson.peerDependencies) {
|
|
222
|
+
for (const [peerDepName, peerDepVersion] of Object.entries(
|
|
223
|
+
pkgJson.peerDependencies,
|
|
224
|
+
)) {
|
|
225
|
+
if (!peerDeps[peerDepName]) {
|
|
226
|
+
peerDeps[peerDepName] = {
|
|
227
|
+
versions: [],
|
|
228
|
+
requiredBy: [],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const versionStr = peerDepVersion;
|
|
232
|
+
if (!peerDeps[peerDepName].versions.includes(versionStr)) {
|
|
233
|
+
peerDeps[peerDepName].versions.push(versionStr);
|
|
234
|
+
}
|
|
235
|
+
if (!peerDeps[peerDepName].requiredBy.includes(packageName)) {
|
|
236
|
+
peerDeps[peerDepName].requiredBy.push(packageName);
|
|
237
|
+
}
|
|
228
238
|
}
|
|
239
|
+
}
|
|
240
|
+
} catch (error) {
|
|
241
|
+
// Skip packages that can't be read
|
|
242
|
+
console.warn(`Warning: Could not read package.json for ${packageName}`);
|
|
229
243
|
}
|
|
230
|
-
|
|
244
|
+
}
|
|
245
|
+
return peerDeps;
|
|
231
246
|
}
|
|
232
247
|
function collectPeerDependencies(deps, peerDeps = {}, parentName) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (!peerDeps[peerDepName].requiredBy.includes(depName)) {
|
|
249
|
-
peerDeps[peerDepName].requiredBy.push(depName);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
248
|
+
if (deps == null) return peerDeps;
|
|
249
|
+
for (const [depName, depInfo] of Object.entries(deps)) {
|
|
250
|
+
// Check if this dependency has peer dependencies
|
|
251
|
+
if (depInfo.peerDependencies) {
|
|
252
|
+
for (const [peerDepName, peerDepVersion] of Object.entries(
|
|
253
|
+
depInfo.peerDependencies,
|
|
254
|
+
)) {
|
|
255
|
+
if (!peerDeps[peerDepName]) {
|
|
256
|
+
peerDeps[peerDepName] = {
|
|
257
|
+
versions: [],
|
|
258
|
+
requiredBy: [],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (!peerDeps[peerDepName].versions.includes(peerDepVersion)) {
|
|
262
|
+
peerDeps[peerDepName].versions.push(peerDepVersion);
|
|
252
263
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
collectPeerDependencies(depInfo.dependencies, peerDeps, depName);
|
|
264
|
+
if (!peerDeps[peerDepName].requiredBy.includes(depName)) {
|
|
265
|
+
peerDeps[peerDepName].requiredBy.push(depName);
|
|
256
266
|
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Recursively check nested dependencies
|
|
270
|
+
if (depInfo.dependencies) {
|
|
271
|
+
collectPeerDependencies(depInfo.dependencies, peerDeps, depName);
|
|
257
272
|
}
|
|
258
|
-
|
|
273
|
+
}
|
|
274
|
+
return peerDeps;
|
|
259
275
|
}
|
|
260
276
|
function resolveVersion(versions) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
277
|
+
if (versions.length === 0) {
|
|
278
|
+
return { version: "*", isConflict: false };
|
|
279
|
+
}
|
|
280
|
+
if (versions.length === 1) {
|
|
281
|
+
return { version: versions[0], isConflict: false };
|
|
282
|
+
}
|
|
283
|
+
// Try to find a compatible version
|
|
284
|
+
// For simplicity, if all versions are the same, use that
|
|
285
|
+
const uniqueVersions = [...new Set(versions)];
|
|
286
|
+
if (uniqueVersions.length === 1) {
|
|
287
|
+
return { version: uniqueVersions[0], isConflict: false };
|
|
288
|
+
}
|
|
289
|
+
// Check if all are compatible (simple heuristic: same major version for ^)
|
|
290
|
+
const allCompatible = uniqueVersions.every((v) => {
|
|
291
|
+
const baseVersion = uniqueVersions[0];
|
|
292
|
+
// If both start with ^, check major version
|
|
293
|
+
if (v.startsWith("^") && baseVersion.startsWith("^")) {
|
|
294
|
+
const vMajor = v.substring(1).split(".")[0];
|
|
295
|
+
const baseMajor = baseVersion.substring(1).split(".")[0];
|
|
296
|
+
return vMajor === baseMajor;
|
|
266
297
|
}
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
// If both start with ^, check major version
|
|
277
|
-
if (v.startsWith("^") && baseVersion.startsWith("^")) {
|
|
278
|
-
const vMajor = v.substring(1).split(".")[0];
|
|
279
|
-
const baseMajor = baseVersion.substring(1).split(".")[0];
|
|
280
|
-
return vMajor === baseMajor;
|
|
281
|
-
}
|
|
282
|
-
// If both are exact versions, they must match
|
|
283
|
-
return v === baseVersion;
|
|
298
|
+
// If both are exact versions, they must match
|
|
299
|
+
return v === baseVersion;
|
|
300
|
+
});
|
|
301
|
+
if (allCompatible && uniqueVersions[0].startsWith("^")) {
|
|
302
|
+
// Return the highest version requirement
|
|
303
|
+
const sorted = [...uniqueVersions].sort((a, b) => {
|
|
304
|
+
const aVer = a.substring(1);
|
|
305
|
+
const bVer = b.substring(1);
|
|
306
|
+
return bVer.localeCompare(aVer, undefined, { numeric: true });
|
|
284
307
|
});
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const bVer = b.substring(1);
|
|
290
|
-
return bVer.localeCompare(aVer, undefined, { numeric: true });
|
|
291
|
-
});
|
|
292
|
-
return { version: sorted[0], isConflict: false };
|
|
293
|
-
}
|
|
294
|
-
// Conflict detected
|
|
295
|
-
return { version: uniqueVersions.join(" | "), isConflict: true };
|
|
308
|
+
return { version: sorted[0], isConflict: false };
|
|
309
|
+
}
|
|
310
|
+
// Conflict detected
|
|
311
|
+
return { version: uniqueVersions.join(" | "), isConflict: true };
|
|
296
312
|
}
|
|
297
313
|
function getPeerDepsTemplate() {
|
|
298
|
-
|
|
314
|
+
return `
|
|
299
315
|
<html>
|
|
300
316
|
<head>
|
|
301
317
|
<title>{{projectName}} - Peer Dependencies</title>
|
|
@@ -584,7 +600,7 @@ function getPeerDepsTemplate() {
|
|
|
584
600
|
`;
|
|
585
601
|
}
|
|
586
602
|
function getTemplate() {
|
|
587
|
-
|
|
603
|
+
return `
|
|
588
604
|
<html>
|
|
589
605
|
<head>
|
|
590
606
|
<title>{{name}}'s Dependency Graph</title>
|
|
@@ -678,7 +694,7 @@ function getTemplate() {
|
|
|
678
694
|
});
|
|
679
695
|
|
|
680
696
|
edges.forEach(function(edge, index) {
|
|
681
|
-
graph.
|
|
697
|
+
graph.mergeEdge(edge.source, edge.target, {
|
|
682
698
|
size: 2,
|
|
683
699
|
color: '#94a3b8'
|
|
684
700
|
});
|