devcompass 1.0.0

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 ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # DevCompass 🧭
2
+
3
+ > Health check for your JavaScript project
4
+
5
+ Find unused dependencies, outdated packages, and calculate your project's health score in seconds.
6
+
7
+ ## šŸš€ Quick Start
8
+ ```bash
9
+ npx devcompass analyze
10
+ ```
11
+
12
+ ## ✨ Features
13
+
14
+ - šŸ” Find unused dependencies
15
+ - šŸ“¦ Check for outdated packages
16
+ - šŸ“Š Calculate health score (0-10)
17
+ - šŸ’” Get actionable recommendations
18
+ - ⚔ Fast analysis (2-3 seconds)
19
+
20
+ ## šŸ“¦ Installation
21
+
22
+ ### Option 1: npx (Recommended)
23
+ ```bash
24
+ npx devcompass analyze
25
+ ```
26
+
27
+ ### Option 2: Global Installation
28
+ ```bash
29
+ npm install -g devcompass
30
+ devcompass analyze
31
+ ```
32
+
33
+ ## šŸ› ļø Requirements
34
+
35
+ - Node.js >= 16.0.0
36
+ - npm >= 7.0.0
37
+ - A project with `package.json`
38
+
39
+ ## šŸ“ License
40
+
41
+ MIT
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const chalk = require('chalk');
5
+ const { analyze } = require('../src/commands/analyze');
6
+ const packageJson = require('../package.json');
7
+
8
+ const program = new Command();
9
+
10
+ program
11
+ .name('devcompass')
12
+ .description('Health check for your JavaScript project')
13
+ .version(packageJson.version, '-v, --version', 'Display version information')
14
+ .addHelpText('after', `
15
+ ${chalk.gray('Author:')} Ajay Thorat
16
+ ${chalk.gray('GitHub:')} ${chalk.cyan('https://github.com/AjayBThorat-20/devcompass')}
17
+ `);
18
+
19
+ program
20
+ .command('analyze')
21
+ .description('Analyze your project dependencies')
22
+ .option('-p, --path <path>', 'Project path', process.cwd())
23
+ .action(analyze);
24
+
25
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "devcompass",
3
+ "version": "1.0.0",
4
+ "description": "Analyze your JavaScript projects for unused dependencies and outdated packages",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "devcompass": "./bin/devcompass.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "src/",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "test": "echo \"Error: no test specified\" && exit 1"
17
+ },
18
+ "keywords": [
19
+ "dependencies",
20
+ "npm",
21
+ "outdated",
22
+ "unused",
23
+ "analyzer",
24
+ "health",
25
+ "cli",
26
+ "devtools",
27
+ "package-manager",
28
+ "dependency-analysis"
29
+ ],
30
+ "author": "Ajay Thorat <ajaythorat988@gmail.com>",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "chalk": "^4.1.2",
34
+ "commander": "^11.1.0",
35
+ "depcheck": "^1.4.7",
36
+ "npm-check-updates": "^16.14.12",
37
+ "ora": "^5.4.1"
38
+ },
39
+ "engines": {
40
+ "node": ">=14.0.0"
41
+ },
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/AjayBThorat-20/devcompass.git"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/AjayBThorat-20/devcompass/issues"
48
+ },
49
+ "homepage": "https://github.com/AjayBThorat-20/devcompass#readme"
50
+ }
@@ -0,0 +1,52 @@
1
+ const ncu = require('npm-check-updates');
2
+ const path = require('path');
3
+
4
+ async function findOutdatedDeps(projectPath, dependencies) {
5
+ try {
6
+ const upgraded = await ncu.run({
7
+ packageFile: path.join(projectPath, 'package.json'),
8
+ upgrade: false,
9
+ silent: true,
10
+ jsonUpgraded: true,
11
+ timeout: 30000,
12
+ dep: 'prod,dev'
13
+ });
14
+
15
+ const outdatedDeps = Object.entries(upgraded || {}).map(
16
+ ([name, latest]) => {
17
+ const current = (dependencies[name] || '')
18
+ .replace(/^[\^~]/, '');
19
+
20
+ return {
21
+ name,
22
+ current,
23
+ latest,
24
+ versionsBehind: getVersionDiff(current, latest)
25
+ };
26
+ }
27
+ );
28
+
29
+ return outdatedDeps;
30
+ } catch (error) {
31
+ console.error('Error in findOutdatedDeps:', error.message);
32
+ throw error;
33
+ }
34
+ }
35
+
36
+ function getVersionDiff(current, latest) {
37
+ const currParts = current.split('.').map(Number);
38
+ const latestParts = latest.split('.').map(Number);
39
+
40
+ while (currParts.length < 3) currParts.push(0);
41
+ while (latestParts.length < 3) latestParts.push(0);
42
+
43
+ if (currParts[0] !== latestParts[0]) {
44
+ return 'major update';
45
+ } else if (currParts[1] !== latestParts[1]) {
46
+ return 'minor update';
47
+ } else {
48
+ return 'patch update';
49
+ }
50
+ }
51
+
52
+ module.exports = { findOutdatedDeps };
@@ -0,0 +1,37 @@
1
+ function calculateScore(totalDeps, unusedCount, outdatedCount) {
2
+ let score = 10;
3
+
4
+ if (totalDeps === 0) {
5
+ return {
6
+ total: 10.0,
7
+ breakdown: {
8
+ unused: 0,
9
+ outdated: 0,
10
+ unusedPenalty: 0,
11
+ outdatedPenalty: 0
12
+ }
13
+ };
14
+ }
15
+
16
+ const unusedRatio = unusedCount / totalDeps;
17
+ const unusedPenalty = unusedRatio * 4;
18
+ score -= unusedPenalty;
19
+
20
+ const outdatedRatio = outdatedCount / totalDeps;
21
+ const outdatedPenalty = outdatedRatio * 3;
22
+ score -= outdatedPenalty;
23
+
24
+ score = Math.max(0, Math.min(10, score));
25
+
26
+ return {
27
+ total: parseFloat(score.toFixed(1)),
28
+ breakdown: {
29
+ unused: unusedCount,
30
+ outdated: outdatedCount,
31
+ unusedPenalty: parseFloat(unusedPenalty.toFixed(1)),
32
+ outdatedPenalty: parseFloat(outdatedPenalty.toFixed(1))
33
+ }
34
+ };
35
+ }
36
+
37
+ module.exports = { calculateScore };
@@ -0,0 +1,51 @@
1
+ const depcheck = require('depcheck');
2
+
3
+ async function findUnusedDeps(projectPath, dependencies) {
4
+ const options = {
5
+ ignoreBinPackage: false,
6
+ skipMissing: true,
7
+ ignorePatterns: [
8
+ 'node_modules/**',
9
+ 'dist/**',
10
+ 'build/**',
11
+ '.next/**',
12
+ 'coverage/**',
13
+ 'out/**',
14
+ '*.min.js'
15
+ ],
16
+ ignoreMatches: [
17
+ 'react',
18
+ 'react-dom',
19
+ 'react-native',
20
+ 'next',
21
+ '@angular/core',
22
+ '@angular/common',
23
+ '@angular/platform-browser',
24
+ '@nestjs/core',
25
+ '@nestjs/common',
26
+ '@nestjs/platform-express',
27
+ 'typescript',
28
+ '@types/*',
29
+ 'webpack',
30
+ 'vite',
31
+ 'rollup',
32
+ 'esbuild',
33
+ 'jest',
34
+ 'vitest',
35
+ 'mocha',
36
+ '@testing-library/*'
37
+ ]
38
+ // REMOVED parsers - let depcheck use defaults
39
+ };
40
+
41
+ try {
42
+ const results = await depcheck(projectPath, options);
43
+ const unusedDeps = results.dependencies.map(name => ({ name }));
44
+ return unusedDeps;
45
+ } catch (error) {
46
+ console.error('Error in findUnusedDeps:', error.message);
47
+ throw error;
48
+ }
49
+ }
50
+
51
+ module.exports = { findUnusedDeps };
@@ -0,0 +1,188 @@
1
+ // src/commands/analyze.js
2
+ const chalk = require('chalk');
3
+ const ora = require('ora');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ const { findUnusedDeps } = require('../analyzers/unused-deps');
8
+ const { findOutdatedDeps } = require('../analyzers/outdated');
9
+ const { calculateScore } = require('../analyzers/scoring');
10
+ const {
11
+ log,
12
+ logSection,
13
+ logDivider,
14
+ getScoreColor
15
+ } = require('../utils/logger');
16
+
17
+ async function analyze(options) {
18
+ const projectPath = options.path || process.cwd();
19
+
20
+ console.log('\n');
21
+ log(chalk.cyan.bold('šŸ” DevCompass v1.0.0') + ' - Analyzing your project...\n');
22
+
23
+ const spinner = ora({
24
+ text: 'Loading project...',
25
+ color: 'cyan'
26
+ }).start();
27
+
28
+ try {
29
+ const packageJsonPath = path.join(projectPath, 'package.json');
30
+
31
+ if (!fs.existsSync(packageJsonPath)) {
32
+ spinner.fail(chalk.red('No package.json found in current directory'));
33
+ console.log(chalk.yellow('\nšŸ’” Make sure you run this command in a project directory.\n'));
34
+ process.exit(1);
35
+ }
36
+
37
+ let packageJson;
38
+ try {
39
+ const fileContent = fs.readFileSync(packageJsonPath, 'utf8');
40
+ packageJson = JSON.parse(fileContent);
41
+ } catch (error) {
42
+ spinner.fail(chalk.red('Invalid package.json format'));
43
+ console.log(chalk.yellow(`\nšŸ’” Error: ${error.message}\n`));
44
+ process.exit(1);
45
+ }
46
+
47
+ const dependencies = {
48
+ ...(packageJson.dependencies || {}),
49
+ ...(packageJson.devDependencies || {})
50
+ };
51
+
52
+ const totalDeps = Object.keys(dependencies).length;
53
+
54
+ if (totalDeps === 0) {
55
+ spinner.succeed(chalk.green('No dependencies found'));
56
+ console.log(chalk.gray('\n✨ Nothing to analyze - your project has no dependencies.\n'));
57
+ process.exit(0);
58
+ }
59
+
60
+ spinner.text = 'Detecting unused dependencies...';
61
+
62
+ let unusedDeps = [];
63
+ try {
64
+ unusedDeps = await findUnusedDeps(projectPath, dependencies);
65
+ } catch (error) {
66
+ console.log(chalk.yellow('\nāš ļø Could not detect unused dependencies'));
67
+ console.log(chalk.gray(` Error: ${error.message}\n`));
68
+ }
69
+
70
+ spinner.text = 'Checking for outdated packages...';
71
+
72
+ let outdatedDeps = [];
73
+ try {
74
+ outdatedDeps = await findOutdatedDeps(projectPath, dependencies);
75
+ } catch (error) {
76
+ console.log(chalk.yellow('\nāš ļø Could not check for outdated packages'));
77
+ console.log(chalk.gray(` Error: ${error.message}\n`));
78
+ }
79
+
80
+ const score = calculateScore(
81
+ totalDeps,
82
+ unusedDeps.length,
83
+ outdatedDeps.length
84
+ );
85
+
86
+ spinner.succeed(chalk.green(`Scanned ${totalDeps} dependencies in project`));
87
+
88
+ displayResults(unusedDeps, outdatedDeps, score, totalDeps);
89
+
90
+ } catch (error) {
91
+ spinner.fail(chalk.red('Analysis failed'));
92
+ console.log(chalk.red(`\nāŒ Error: ${error.message}\n`));
93
+ if (process.env.DEBUG) {
94
+ console.log(error.stack);
95
+ }
96
+ process.exit(1);
97
+ }
98
+ }
99
+
100
+ function displayResults(unusedDeps, outdatedDeps, score, totalDeps) {
101
+ logDivider();
102
+
103
+ if (unusedDeps.length > 0) {
104
+ logSection('šŸ”“ UNUSED DEPENDENCIES', unusedDeps.length);
105
+
106
+ const displayCount = Math.min(5, unusedDeps.length);
107
+ unusedDeps.slice(0, displayCount).forEach(dep => {
108
+ log(` ${chalk.red('ā—')} ${dep.name}`);
109
+ });
110
+
111
+ if (unusedDeps.length > 5) {
112
+ log(chalk.gray(`\n ... and ${unusedDeps.length - 5} more\n`));
113
+ }
114
+
115
+ log(chalk.gray('\n Why marked unused:'));
116
+ log(chalk.gray(' • No import/require found in source files'));
117
+ log(chalk.gray(' • Excludes node_modules, build folders'));
118
+ log(chalk.gray(' • May include false positives - verify before removing\n'));
119
+ } else {
120
+ logSection('āœ… UNUSED DEPENDENCIES');
121
+ log(chalk.green(' No unused dependencies detected!\n'));
122
+ }
123
+
124
+ logDivider();
125
+
126
+ if (outdatedDeps.length > 0) {
127
+ logSection('🟔 OUTDATED PACKAGES', outdatedDeps.length);
128
+
129
+ const displayCount = Math.min(5, outdatedDeps.length);
130
+ outdatedDeps.slice(0, displayCount).forEach(dep => {
131
+ const nameCol = dep.name.padEnd(20);
132
+ const currentVer = chalk.yellow(dep.current);
133
+ const arrow = chalk.gray('→');
134
+ const latestVer = chalk.green(dep.latest);
135
+ const updateType = chalk.gray(`(${dep.versionsBehind})`);
136
+
137
+ log(` ${nameCol} ${currentVer} ${arrow} ${latestVer} ${updateType}`);
138
+ });
139
+
140
+ if (outdatedDeps.length > 5) {
141
+ log(chalk.gray(`\n ... and ${outdatedDeps.length - 5} more\n`));
142
+ }
143
+ } else {
144
+ logSection('āœ… OUTDATED PACKAGES');
145
+ log(chalk.green(' All packages are up to date!\n'));
146
+ }
147
+
148
+ logDivider();
149
+
150
+ logSection('šŸ“Š PROJECT HEALTH');
151
+
152
+ const scoreColor = getScoreColor(score.total);
153
+ log(` Overall Score: ${scoreColor(score.total + '/10')}`);
154
+ log(` Total Dependencies: ${chalk.cyan(totalDeps)}`);
155
+ log(` Unused: ${chalk.red(unusedDeps.length)}`);
156
+ log(` Outdated: ${chalk.yellow(outdatedDeps.length)}\n`);
157
+
158
+ logDivider();
159
+
160
+ if (unusedDeps.length > 0) {
161
+ logSection('šŸ’” QUICK WIN');
162
+ log(' Clean up unused dependencies:\n');
163
+
164
+ const packagesToRemove = unusedDeps
165
+ .slice(0, 5)
166
+ .map(d => d.name)
167
+ .join(' ');
168
+
169
+ log(chalk.cyan(` npm uninstall ${packagesToRemove}\n`));
170
+
171
+ log(' Expected impact:');
172
+ log(` ${chalk.green('āœ“')} Remove ${unusedDeps.length} unused package${unusedDeps.length > 1 ? 's' : ''}`);
173
+ log(` ${chalk.green('āœ“')} Reduce node_modules size`);
174
+
175
+ const improvedScore = calculateScore(
176
+ totalDeps - unusedDeps.length,
177
+ 0,
178
+ outdatedDeps.length
179
+ );
180
+
181
+ const improvedScoreColor = getScoreColor(improvedScore.total);
182
+ log(` ${chalk.green('āœ“')} Improve health score → ${improvedScoreColor(improvedScore.total + '/10')}\n`);
183
+
184
+ logDivider();
185
+ }
186
+ }
187
+
188
+ module.exports = { analyze };
package/src/index.js ADDED
File without changes
@@ -0,0 +1,36 @@
1
+ const chalk = require('chalk');
2
+
3
+ function log(message) {
4
+ console.log(message);
5
+ }
6
+
7
+ function logSection(title, count) {
8
+ const countStr = count !== undefined
9
+ ? chalk.gray(` (${count})`)
10
+ : '';
11
+ log(chalk.bold(`\n${title}${countStr}\n`));
12
+ }
13
+
14
+ function logDivider() {
15
+ const line = '━'.repeat(70);
16
+ log(chalk.gray(line) + '\n');
17
+ }
18
+
19
+ function getScoreColor(score) {
20
+ if (score >= 8) {
21
+ return chalk.green.bold;
22
+ } else if (score >= 6) {
23
+ return chalk.yellow.bold;
24
+ } else {
25
+ return chalk.red.bold;
26
+ }
27
+ }
28
+
29
+ module.exports = {
30
+ log,
31
+ logSection,
32
+ logDivider,
33
+ getScoreColor
34
+ };
35
+
36
+