dependency-radar 0.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/LICENSE +21 -0
- package/README.md +78 -0
- package/dist/aggregator.js +585 -0
- package/dist/cli.js +158 -0
- package/dist/index.js +18 -0
- package/dist/report.js +1525 -0
- package/dist/runners/depcheckRunner.js +23 -0
- package/dist/runners/licenseChecker.js +33 -0
- package/dist/runners/madgeRunner.js +29 -0
- package/dist/runners/npmAudit.js +31 -0
- package/dist/runners/npmLs.js +32 -0
- package/dist/types.js +2 -0
- package/dist/utils.js +148 -0
- package/package.json +56 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runDepcheck = runDepcheck;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const depcheck_1 = __importDefault(require("depcheck"));
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
async function runDepcheck(projectPath, tempDir) {
|
|
11
|
+
const targetFile = path_1.default.join(tempDir, 'depcheck.json');
|
|
12
|
+
try {
|
|
13
|
+
const result = await (0, depcheck_1.default)(projectPath, {
|
|
14
|
+
ignoreDirs: ['.dependency-radar', 'dist', 'build', 'coverage', 'node_modules']
|
|
15
|
+
});
|
|
16
|
+
await (0, utils_1.writeJsonFile)(targetFile, result);
|
|
17
|
+
return { ok: true, data: result, file: targetFile };
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
await (0, utils_1.writeJsonFile)(targetFile, { error: String(err) });
|
|
21
|
+
return { ok: false, error: `depcheck failed: ${String(err)}`, file: targetFile };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runLicenseChecker = runLicenseChecker;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
async function runLicenseChecker(projectPath, tempDir) {
|
|
10
|
+
const targetFile = path_1.default.join(tempDir, 'license-checker.json');
|
|
11
|
+
const bin = (0, utils_1.findBin)(projectPath, 'license-checker');
|
|
12
|
+
try {
|
|
13
|
+
const result = await (0, utils_1.runCommand)(bin, ['--json'], { cwd: projectPath });
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = JSON.parse(result.stdout || '{}');
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
parsed = undefined;
|
|
20
|
+
}
|
|
21
|
+
if (parsed) {
|
|
22
|
+
await (0, utils_1.writeJsonFile)(targetFile, parsed);
|
|
23
|
+
return { ok: true, data: parsed, file: targetFile };
|
|
24
|
+
}
|
|
25
|
+
await (0, utils_1.writeJsonFile)(targetFile, { stdout: result.stdout, stderr: result.stderr, code: result.code });
|
|
26
|
+
const error = result.code && result.code !== 0 ? `license-checker exited with code ${result.code}` : 'Failed to parse license-checker output';
|
|
27
|
+
return { ok: false, error, file: targetFile };
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
await (0, utils_1.writeJsonFile)(targetFile, { error: String(err) });
|
|
31
|
+
return { ok: false, error: `license-checker failed: ${String(err)}`, file: targetFile };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runMadge = runMadge;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const madge_1 = __importDefault(require("madge"));
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
async function runMadge(projectPath, tempDir) {
|
|
11
|
+
const targetFile = path_1.default.join(tempDir, 'madge.json');
|
|
12
|
+
try {
|
|
13
|
+
const srcPath = path_1.default.join(projectPath, 'src');
|
|
14
|
+
const hasSrc = await (0, utils_1.pathExists)(srcPath);
|
|
15
|
+
const entry = hasSrc ? srcPath : projectPath;
|
|
16
|
+
const result = await (0, madge_1.default)(entry, {
|
|
17
|
+
baseDir: projectPath,
|
|
18
|
+
includeNpm: true,
|
|
19
|
+
excludeRegExp: [/node_modules/, /dist/, /build/, /coverage/, /.dependency-radar/]
|
|
20
|
+
});
|
|
21
|
+
const graph = await result.obj();
|
|
22
|
+
await (0, utils_1.writeJsonFile)(targetFile, graph);
|
|
23
|
+
return { ok: true, data: graph, file: targetFile };
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
await (0, utils_1.writeJsonFile)(targetFile, { error: String(err) });
|
|
27
|
+
return { ok: false, error: `madge failed: ${String(err)}`, file: targetFile };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runNpmAudit = runNpmAudit;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
async function runNpmAudit(projectPath, tempDir) {
|
|
10
|
+
const targetFile = path_1.default.join(tempDir, 'npm-audit.json');
|
|
11
|
+
try {
|
|
12
|
+
const result = await (0, utils_1.runCommand)('npm', ['audit', '--json'], { cwd: projectPath });
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(result.stdout || '{}');
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
parsed = undefined;
|
|
19
|
+
}
|
|
20
|
+
if (parsed) {
|
|
21
|
+
await (0, utils_1.writeJsonFile)(targetFile, parsed);
|
|
22
|
+
return { ok: true, data: parsed, file: targetFile };
|
|
23
|
+
}
|
|
24
|
+
await (0, utils_1.writeJsonFile)(targetFile, { stdout: result.stdout, stderr: result.stderr, code: result.code });
|
|
25
|
+
return { ok: false, error: 'Failed to parse npm audit output', file: targetFile };
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
await (0, utils_1.writeJsonFile)(targetFile, { error: String(err) });
|
|
29
|
+
return { ok: false, error: `npm audit failed: ${String(err)}`, file: targetFile };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runNpmLs = runNpmLs;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
async function runNpmLs(projectPath, tempDir) {
|
|
10
|
+
const targetFile = path_1.default.join(tempDir, 'npm-ls.json');
|
|
11
|
+
try {
|
|
12
|
+
const result = await (0, utils_1.runCommand)('npm', ['ls', '--json', '--all'], { cwd: projectPath });
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(result.stdout || '{}');
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
parsed = undefined;
|
|
19
|
+
}
|
|
20
|
+
if (parsed) {
|
|
21
|
+
await (0, utils_1.writeJsonFile)(targetFile, parsed);
|
|
22
|
+
return { ok: true, data: parsed, file: targetFile };
|
|
23
|
+
}
|
|
24
|
+
await (0, utils_1.writeJsonFile)(targetFile, { stdout: result.stdout, stderr: result.stderr, code: result.code });
|
|
25
|
+
const error = result.code && result.code !== 0 ? `npm ls exited with code ${result.code}` : 'Failed to parse npm ls output';
|
|
26
|
+
return { ok: false, error, file: targetFile };
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
await (0, utils_1.writeJsonFile)(targetFile, { error: String(err) });
|
|
30
|
+
return { ok: false, error: `npm ls failed: ${String(err)}`, file: targetFile };
|
|
31
|
+
}
|
|
32
|
+
}
|
package/dist/types.js
ADDED
package/dist/utils.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runCommand = runCommand;
|
|
7
|
+
exports.delay = delay;
|
|
8
|
+
exports.getDependencyRadarVersion = getDependencyRadarVersion;
|
|
9
|
+
exports.ensureDir = ensureDir;
|
|
10
|
+
exports.writeJsonFile = writeJsonFile;
|
|
11
|
+
exports.pathExists = pathExists;
|
|
12
|
+
exports.removeDir = removeDir;
|
|
13
|
+
exports.readJsonFile = readJsonFile;
|
|
14
|
+
exports.readPackageJson = readPackageJson;
|
|
15
|
+
exports.findBin = findBin;
|
|
16
|
+
exports.licenseRiskLevel = licenseRiskLevel;
|
|
17
|
+
exports.vulnRiskLevel = vulnRiskLevel;
|
|
18
|
+
exports.maintenanceRisk = maintenanceRisk;
|
|
19
|
+
exports.readLicenseFromPackageJson = readLicenseFromPackageJson;
|
|
20
|
+
const child_process_1 = require("child_process");
|
|
21
|
+
const fs_1 = __importDefault(require("fs"));
|
|
22
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
23
|
+
const path_1 = __importDefault(require("path"));
|
|
24
|
+
function runCommand(command, args, options = {}) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const child = (0, child_process_1.spawn)(command, args, {
|
|
27
|
+
cwd: options.cwd,
|
|
28
|
+
shell: false
|
|
29
|
+
});
|
|
30
|
+
const stdoutChunks = [];
|
|
31
|
+
const stderrChunks = [];
|
|
32
|
+
child.stdout.on('data', (d) => stdoutChunks.push(Buffer.from(d)));
|
|
33
|
+
child.stderr.on('data', (d) => stderrChunks.push(Buffer.from(d)));
|
|
34
|
+
child.on('error', (err) => reject(err));
|
|
35
|
+
child.on('close', (code) => {
|
|
36
|
+
resolve({
|
|
37
|
+
stdout: Buffer.concat(stdoutChunks).toString('utf8'),
|
|
38
|
+
stderr: Buffer.concat(stderrChunks).toString('utf8'),
|
|
39
|
+
code
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function delay(ms) {
|
|
45
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
46
|
+
}
|
|
47
|
+
function getDependencyRadarVersion() {
|
|
48
|
+
try {
|
|
49
|
+
const pkgPath = path_1.default.join(__dirname, '..', 'package.json');
|
|
50
|
+
const raw = fs_1.default.readFileSync(pkgPath, 'utf8');
|
|
51
|
+
const pkg = JSON.parse(raw);
|
|
52
|
+
return pkg.version || 'unknown';
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return 'unknown';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function ensureDir(dir) {
|
|
59
|
+
await promises_1.default.mkdir(dir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
async function writeJsonFile(filePath, data) {
|
|
62
|
+
await ensureDir(path_1.default.dirname(filePath));
|
|
63
|
+
const content = JSON.stringify(data, null, 2);
|
|
64
|
+
await promises_1.default.writeFile(filePath, content, 'utf8');
|
|
65
|
+
}
|
|
66
|
+
async function pathExists(target) {
|
|
67
|
+
try {
|
|
68
|
+
await promises_1.default.access(target);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function removeDir(target) {
|
|
76
|
+
await promises_1.default.rm(target, { recursive: true, force: true });
|
|
77
|
+
}
|
|
78
|
+
async function readJsonFile(filePath) {
|
|
79
|
+
const raw = await promises_1.default.readFile(filePath, 'utf8');
|
|
80
|
+
return JSON.parse(raw);
|
|
81
|
+
}
|
|
82
|
+
async function readPackageJson(projectPath) {
|
|
83
|
+
const pkgPath = path_1.default.join(projectPath, 'package.json');
|
|
84
|
+
const pkgRaw = await promises_1.default.readFile(pkgPath, 'utf8');
|
|
85
|
+
return JSON.parse(pkgRaw);
|
|
86
|
+
}
|
|
87
|
+
function findBin(projectPath, binName) {
|
|
88
|
+
const ext = process.platform === 'win32' ? '.cmd' : '';
|
|
89
|
+
const candidates = [
|
|
90
|
+
path_1.default.join(projectPath, 'node_modules', '.bin', `${binName}${ext}`),
|
|
91
|
+
path_1.default.join(process.cwd(), 'node_modules', '.bin', `${binName}${ext}`),
|
|
92
|
+
path_1.default.join(__dirname, '..', 'node_modules', '.bin', `${binName}${ext}`),
|
|
93
|
+
`${binName}${ext}`
|
|
94
|
+
];
|
|
95
|
+
for (const candidate of candidates) {
|
|
96
|
+
if (fs_1.default.existsSync(candidate))
|
|
97
|
+
return candidate;
|
|
98
|
+
}
|
|
99
|
+
return candidates[candidates.length - 1];
|
|
100
|
+
}
|
|
101
|
+
function licenseRiskLevel(license) {
|
|
102
|
+
if (!license)
|
|
103
|
+
return 'red';
|
|
104
|
+
const normalized = license.toUpperCase();
|
|
105
|
+
const green = ['MIT', 'BSD-2-CLAUSE', 'BSD-3-CLAUSE', 'APACHE-2.0', 'ISC'];
|
|
106
|
+
const amber = ['LGPL', 'LGPL-2.1', 'LGPL-3.0', 'MPL', 'MPL-2.0'];
|
|
107
|
+
if (green.includes(normalized))
|
|
108
|
+
return 'green';
|
|
109
|
+
if (amber.includes(normalized))
|
|
110
|
+
return 'amber';
|
|
111
|
+
return 'red';
|
|
112
|
+
}
|
|
113
|
+
function vulnRiskLevel(counts) {
|
|
114
|
+
const { low = 0, moderate = 0, high = 0, critical = 0 } = counts;
|
|
115
|
+
if (high > 0 || critical > 0)
|
|
116
|
+
return 'red';
|
|
117
|
+
if (low > 0 || moderate > 0)
|
|
118
|
+
return 'amber';
|
|
119
|
+
return 'green';
|
|
120
|
+
}
|
|
121
|
+
function maintenanceRisk(lastPublished) {
|
|
122
|
+
if (!lastPublished)
|
|
123
|
+
return 'unknown';
|
|
124
|
+
const last = new Date(lastPublished).getTime();
|
|
125
|
+
if (Number.isNaN(last))
|
|
126
|
+
return 'unknown';
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
const months = (now - last) / (1000 * 60 * 60 * 24 * 30);
|
|
129
|
+
if (months <= 12)
|
|
130
|
+
return 'green';
|
|
131
|
+
if (months <= 36)
|
|
132
|
+
return 'amber';
|
|
133
|
+
return 'red';
|
|
134
|
+
}
|
|
135
|
+
async function readLicenseFromPackageJson(pkgName, projectPath) {
|
|
136
|
+
try {
|
|
137
|
+
const pkgJsonPath = require.resolve(path_1.default.join(pkgName, 'package.json'), { paths: [projectPath] });
|
|
138
|
+
const pkgRaw = await promises_1.default.readFile(pkgJsonPath, 'utf8');
|
|
139
|
+
const pkg = JSON.parse(pkgRaw);
|
|
140
|
+
const license = pkg.license || (Array.isArray(pkg.licenses) ? pkg.licenses.map((l) => (typeof l === 'string' ? l : l === null || l === void 0 ? void 0 : l.type)).filter(Boolean).join(' OR ') : undefined);
|
|
141
|
+
if (!license)
|
|
142
|
+
return undefined;
|
|
143
|
+
return { license, licenseFile: pkgJsonPath };
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dependency-radar",
|
|
3
|
+
"version": "0.1.1",
|
|
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
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dependency-radar": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "ts-node src/cli.ts scan",
|
|
15
|
+
"scan": "node dist/cli.js scan",
|
|
16
|
+
"prepublishOnly": "npm run build",
|
|
17
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
18
|
+
},
|
|
19
|
+
"author": " ",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/JosephMaynard/dependency-radar.git"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "http://dependency-radar.com",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/JosephMaynard/dependency-radar/issues"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"dependency",
|
|
34
|
+
"dependencies",
|
|
35
|
+
"analysis",
|
|
36
|
+
"audit",
|
|
37
|
+
"security",
|
|
38
|
+
"license",
|
|
39
|
+
"cli",
|
|
40
|
+
"report",
|
|
41
|
+
"scan",
|
|
42
|
+
"npm",
|
|
43
|
+
"node",
|
|
44
|
+
"html"
|
|
45
|
+
],
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"depcheck": "^1.4.7",
|
|
48
|
+
"license-checker": "^25.0.1",
|
|
49
|
+
"madge": "^8.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^20.11.30",
|
|
53
|
+
"ts-node": "^10.9.2",
|
|
54
|
+
"typescript": "^5.4.3"
|
|
55
|
+
}
|
|
56
|
+
}
|