dependency-radar 0.5.0 → 0.5.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/dist/runners/lockfileGraph.js +45 -4
- package/dist/utils.js +36 -1
- package/package.json +1 -1
|
@@ -437,6 +437,7 @@ function parseNpmTree(projectPath, searchRoot) {
|
|
|
437
437
|
* @returns A ResolvedTree mapping root dependency names to resolved nodes, or `undefined` if no valid root entry exists in `packages`
|
|
438
438
|
*/
|
|
439
439
|
function parseNpmTreeFromPackages(packages, projectPath, lockDir) {
|
|
440
|
+
const installState = createNpmInstallState(projectPath, lockDir);
|
|
440
441
|
const projectRel = toPosixRelative(lockDir, projectPath);
|
|
441
442
|
const rootKey = projectRel === '' ? '' : projectRel;
|
|
442
443
|
if (!(rootKey in packages) && rootKey !== '') {
|
|
@@ -454,7 +455,7 @@ function parseNpmTreeFromPackages(packages, projectPath, lockDir) {
|
|
|
454
455
|
const childKey = resolveNpmPackagePath(packageKey, depName, packages);
|
|
455
456
|
if (!childKey)
|
|
456
457
|
continue;
|
|
457
|
-
const childNode = buildNpmNodeFromPackages(childKey, depName, packages, memo, stack);
|
|
458
|
+
const childNode = buildNpmNodeFromPackages(childKey, depName, packages, memo, stack, installState);
|
|
458
459
|
if (childNode)
|
|
459
460
|
dependencies[childNode.name] = childNode;
|
|
460
461
|
}
|
|
@@ -470,18 +471,22 @@ function parseNpmTreeFromPackages(packages, projectPath, lockDir) {
|
|
|
470
471
|
* @param stack - Recursion stack used to detect and avoid cycles while building the dependency graph.
|
|
471
472
|
* @returns The constructed `ResolvedNode` for `packageKey`, or `undefined` if the entry is missing, invalid, cyclic, or cannot be resolved.
|
|
472
473
|
*/
|
|
473
|
-
function buildNpmNodeFromPackages(packageKey, fallbackName, packages, memo, stack) {
|
|
474
|
+
function buildNpmNodeFromPackages(packageKey, fallbackName, packages, memo, stack, installState) {
|
|
474
475
|
if (memo.has(packageKey))
|
|
475
476
|
return memo.get(packageKey);
|
|
476
477
|
if (stack.has(packageKey))
|
|
477
478
|
return undefined;
|
|
479
|
+
if (!isNpmPackageInstalled(packageKey, installState)) {
|
|
480
|
+
memo.set(packageKey, undefined);
|
|
481
|
+
return undefined;
|
|
482
|
+
}
|
|
478
483
|
const entry = packages[packageKey];
|
|
479
484
|
if (!entry || typeof entry !== 'object')
|
|
480
485
|
return undefined;
|
|
481
486
|
if (entry.link === true && typeof entry.resolved === 'string') {
|
|
482
487
|
const linkedKey = normalizeLockPackageKey(entry.resolved);
|
|
483
488
|
if (linkedKey && linkedKey in packages) {
|
|
484
|
-
const linkedNode = buildNpmNodeFromPackages(linkedKey, fallbackName, packages, memo, stack);
|
|
489
|
+
const linkedNode = buildNpmNodeFromPackages(linkedKey, fallbackName, packages, memo, stack, installState);
|
|
485
490
|
memo.set(packageKey, linkedNode);
|
|
486
491
|
return linkedNode;
|
|
487
492
|
}
|
|
@@ -506,7 +511,7 @@ function buildNpmNodeFromPackages(packageKey, fallbackName, packages, memo, stac
|
|
|
506
511
|
const childKey = resolveNpmPackagePath(packageKey, depName, packages);
|
|
507
512
|
if (!childKey)
|
|
508
513
|
continue;
|
|
509
|
-
const childNode = buildNpmNodeFromPackages(childKey, depName, packages, memo, stack);
|
|
514
|
+
const childNode = buildNpmNodeFromPackages(childKey, depName, packages, memo, stack, installState);
|
|
510
515
|
if (!childNode)
|
|
511
516
|
continue;
|
|
512
517
|
out.dependencies[childNode.name] = childNode;
|
|
@@ -1037,6 +1042,21 @@ function createPnpmInstallState(projectPath) {
|
|
|
1037
1042
|
installedCache: new Map()
|
|
1038
1043
|
};
|
|
1039
1044
|
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Create npm installation state used to filter lockfile package entries to actually installed paths.
|
|
1047
|
+
*
|
|
1048
|
+
* @param projectPath - Filesystem path inside the project to start discovery from
|
|
1049
|
+
* @param lockDir - Directory containing the npm lockfile
|
|
1050
|
+
* @returns Installation state with detected node_modules roots and per-package-key cache
|
|
1051
|
+
*/
|
|
1052
|
+
function createNpmInstallState(projectPath, lockDir) {
|
|
1053
|
+
const nodeModulesRoots = findNodeModulesRoots(projectPath);
|
|
1054
|
+
return {
|
|
1055
|
+
enabled: nodeModulesRoots.length > 0,
|
|
1056
|
+
lockDir,
|
|
1057
|
+
installedByKeyCache: new Map()
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1040
1060
|
/**
|
|
1041
1061
|
* Discover node_modules directories by walking upward from a starting path.
|
|
1042
1062
|
*
|
|
@@ -1058,6 +1078,27 @@ function findNodeModulesRoots(startPath) {
|
|
|
1058
1078
|
}
|
|
1059
1079
|
return roots;
|
|
1060
1080
|
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Determine whether a package-lock `packages` entry key points to an installed package path.
|
|
1083
|
+
*
|
|
1084
|
+
* @param packageKey - Key from package-lock `packages` map
|
|
1085
|
+
* @param installState - npm installation state with lockDir and cache
|
|
1086
|
+
* @returns `true` when the key should be considered installed, `false` otherwise
|
|
1087
|
+
*/
|
|
1088
|
+
function isNpmPackageInstalled(packageKey, installState) {
|
|
1089
|
+
if (!installState.enabled)
|
|
1090
|
+
return true;
|
|
1091
|
+
const normalizedKey = normalizeLockPackageKey(packageKey);
|
|
1092
|
+
if (!normalizedKey)
|
|
1093
|
+
return true;
|
|
1094
|
+
const cached = installState.installedByKeyCache.get(normalizedKey);
|
|
1095
|
+
if (cached !== undefined)
|
|
1096
|
+
return cached;
|
|
1097
|
+
const candidate = path_1.default.join(installState.lockDir, ...normalizedKey.split('/'));
|
|
1098
|
+
const installed = safePathExists(candidate);
|
|
1099
|
+
installState.installedByKeyCache.set(normalizedKey, installed);
|
|
1100
|
+
return installed;
|
|
1101
|
+
}
|
|
1061
1102
|
/**
|
|
1062
1103
|
* Read the names of entries in a directory, returning an empty array if the directory cannot be read.
|
|
1063
1104
|
*
|
package/dist/utils.js
CHANGED
|
@@ -58,11 +58,46 @@ function getDependencyRadarVersion() {
|
|
|
58
58
|
async function ensureDir(dir) {
|
|
59
59
|
await promises_1.default.mkdir(dir, { recursive: true });
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Write JSON data to a file, creating parent directories as needed.
|
|
63
|
+
*
|
|
64
|
+
* Attempts to write a pretty-printed JSON representation of `data` to `filePath`. If pretty-printing fails due to an "Invalid string length" RangeError, falls back to a compact JSON representation.
|
|
65
|
+
*
|
|
66
|
+
* @param filePath - The path of the file to write
|
|
67
|
+
* @param data - The value to serialize to JSON (typically JSON-serializable)
|
|
68
|
+
* @throws Rethrows errors from JSON serialization (except handled "Invalid string length" for pretty-printing) and filesystem write operations
|
|
69
|
+
*/
|
|
61
70
|
async function writeJsonFile(filePath, data) {
|
|
62
71
|
await ensureDir(path_1.default.dirname(filePath));
|
|
63
|
-
|
|
72
|
+
let content;
|
|
73
|
+
try {
|
|
74
|
+
content = JSON.stringify(data, null, 2);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
// Large lockfile-derived trees can overflow string length when pretty-printing.
|
|
78
|
+
if (!isInvalidStringLengthError(err))
|
|
79
|
+
throw err;
|
|
80
|
+
content = JSON.stringify(data);
|
|
81
|
+
}
|
|
64
82
|
await promises_1.default.writeFile(filePath, content, 'utf8');
|
|
65
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Determines whether a value is a RangeError whose message indicates an "Invalid string length".
|
|
86
|
+
*
|
|
87
|
+
* @param error - The value to inspect
|
|
88
|
+
* @returns `true` if `error` is a `RangeError` with a message matching "Invalid string length" (case-insensitive), `false` otherwise.
|
|
89
|
+
*/
|
|
90
|
+
function isInvalidStringLengthError(error) {
|
|
91
|
+
if (!(error instanceof RangeError))
|
|
92
|
+
return false;
|
|
93
|
+
return /Invalid string length/i.test(error.message || '');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check whether a filesystem path exists and is accessible.
|
|
97
|
+
*
|
|
98
|
+
* @param target - The path to check
|
|
99
|
+
* @returns `true` if the path exists and is accessible, `false` otherwise
|
|
100
|
+
*/
|
|
66
101
|
async function pathExists(target) {
|
|
67
102
|
try {
|
|
68
103
|
await promises_1.default.access(target);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dependency-radar",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Local-first dependency analysis tool that generates a single HTML report showing risk, size, usage, and structure of your project's dependencies.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|