easybuild-nox 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.
@@ -0,0 +1,261 @@
1
+ const { Command } = require('commander');
2
+ const path = require('path');
3
+ const fs = require('fs-extra');
4
+ const logger = require('../utils/logger');
5
+ const ProjectDetector = require('../utils/detector');
6
+
7
+ const healthCommand = new Command('health')
8
+ .description('Check project health and issues')
9
+ .option('--fix', 'Auto-fix issues where possible', false)
10
+ .action(async (options) => {
11
+ logger.header('🏥 Health Check');
12
+
13
+ const detector = new ProjectDetector();
14
+
15
+ try {
16
+ const projectInfo = await detector.detect();
17
+ const issues = [];
18
+ const warnings = [];
19
+ const passed = [];
20
+
21
+ // Check package.json
22
+ await checkPackageJson(issues, warnings, passed);
23
+
24
+ // Check dependencies
25
+ await checkDependencies(issues, warnings, passed);
26
+
27
+ // Check for security issues
28
+ await checkSecurity(issues, warnings, passed);
29
+
30
+ // Check for best practices
31
+ await checkBestPractices(projectInfo, issues, warnings, passed);
32
+
33
+ // Check for common issues
34
+ await checkCommonIssues(issues, warnings, passed);
35
+
36
+ // Print results
37
+ printHealthResults(passed, warnings, issues);
38
+
39
+ // Auto-fix if requested
40
+ if (options.fix) {
41
+ await autoFix(issues);
42
+ }
43
+ } catch (error) {
44
+ logger.error(`Health check failed: ${error.message}`);
45
+ process.exit(1);
46
+ }
47
+ });
48
+
49
+ async function checkPackageJson(issues, warnings, passed) {
50
+ const pkgPath = path.join(process.cwd(), 'package.json');
51
+
52
+ if (!(await fs.pathExists(pkgPath))) {
53
+ issues.push('No package.json found');
54
+ return;
55
+ }
56
+
57
+ passed.push('package.json exists');
58
+
59
+ const pkg = await fs.readJson(pkgPath);
60
+
61
+ if (!pkg.name) issues.push('Missing "name" in package.json');
62
+ else passed.push('Has package name');
63
+
64
+ if (!pkg.version) warnings.push('Missing "version" in package.json');
65
+ else passed.push('Has version');
66
+
67
+ if (!pkg.description) warnings.push('Missing "description" in package.json');
68
+
69
+ if (!pkg.main && !pkg.bin) warnings.push('No main or bin entry');
70
+ else passed.push('Has entry point');
71
+
72
+ if (pkg.license) passed.push('Has license');
73
+ else warnings.push('No license specified');
74
+ }
75
+
76
+ async function checkDependencies(issues, warnings, passed) {
77
+ const pkgPath = path.join(process.cwd(), 'package.json');
78
+ if (!(await fs.pathExists(pkgPath))) return;
79
+
80
+ const pkg = await fs.readJson(pkgPath);
81
+ const nodeModulesPath = path.join(process.cwd(), 'node_modules');
82
+
83
+ if (await fs.pathExists(nodeModulesPath)) {
84
+ passed.push('node_modules exists');
85
+ } else {
86
+ warnings.push('node_modules not found - run npm install');
87
+ }
88
+
89
+ // Check for outdated dependencies
90
+ try {
91
+ const { execSync } = require('child_process');
92
+ const outdated = execSync('npm outdated --json', { encoding: 'utf8' });
93
+ const outdatedCount = Object.keys(JSON.parse(outdated)).length;
94
+ if (outdatedCount > 0) {
95
+ warnings.push(`${outdatedCount} outdated dependencies`);
96
+ } else {
97
+ passed.push('All dependencies up to date');
98
+ }
99
+ } catch {
100
+ // npm outdated returns non-zero if there are outdated packages
101
+ }
102
+
103
+ // Check for security vulnerabilities
104
+ try {
105
+ const { execSync } = require('child_process');
106
+ execSync('npm audit --audit-level=high', { encoding: 'utf8' });
107
+ passed.push('No high-severity vulnerabilities');
108
+ } catch {
109
+ warnings.push('Potential security vulnerabilities - run npm audit');
110
+ }
111
+ }
112
+
113
+ async function checkSecurity(issues, warnings, passed) {
114
+ // Check for .env file in git
115
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
116
+ if (await fs.pathExists(gitignorePath)) {
117
+ const gitignore = await fs.readFile(gitignorePath, 'utf8');
118
+ if (gitignore.includes('.env')) {
119
+ passed.push('.env is gitignored');
120
+ } else {
121
+ warnings.push('.env is not gitignored - add it for security');
122
+ }
123
+ }
124
+
125
+ // Check for secrets in code
126
+ const srcDir = path.join(process.cwd(), 'src');
127
+ if (await fs.pathExists(srcDir)) {
128
+ // This is a simplified check
129
+ passed.push('Basic security checks passed');
130
+ }
131
+ }
132
+
133
+ async function checkBestPractices(projectInfo, issues, warnings, passed) {
134
+ // Check for TypeScript
135
+ if (projectInfo.isTypeScript) {
136
+ passed.push('TypeScript configured');
137
+ }
138
+
139
+ // Check for ESLint
140
+ const eslintPath = path.join(process.cwd(), '.eslintrc.json');
141
+ const eslintPathJs = path.join(process.cwd(), '.eslintrc.js');
142
+ if (await fs.pathExists(eslintPath) || await fs.pathExists(eslintPathJs)) {
143
+ passed.push('ESLint configured');
144
+ } else {
145
+ warnings.push('No ESLint configuration found');
146
+ }
147
+
148
+ // Check for Prettier
149
+ const prettierPath = path.join(process.cwd(), '.prettierrc');
150
+ if (await fs.pathExists(prettierPath)) {
151
+ passed.push('Prettier configured');
152
+ } else {
153
+ warnings.push('No Prettier configuration found');
154
+ }
155
+
156
+ // Check for tests
157
+ const testDirs = ['__tests__', 'test', 'tests', 'spec'];
158
+ const hasTests = testDirs.some(dir =>
159
+ fs.pathExists(path.join(process.cwd(), dir))
160
+ );
161
+ if (hasTests) {
162
+ passed.push('Test directory found');
163
+ } else {
164
+ warnings.push('No test directory found');
165
+ }
166
+
167
+ // Check for README
168
+ const readmePath = path.join(process.cwd(), 'README.md');
169
+ if (await fs.pathExists(readmePath)) {
170
+ passed.push('README.md exists');
171
+ } else {
172
+ warnings.push('No README.md found');
173
+ }
174
+
175
+ // Check for .gitignore
176
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
177
+ if (await fs.pathExists(gitignorePath)) {
178
+ passed.push('.gitignore exists');
179
+ } else {
180
+ warnings.push('No .gitignore found');
181
+ }
182
+ }
183
+
184
+ async function checkCommonIssues(issues, warnings, passed) {
185
+ // Check for common config files
186
+ const configFiles = [
187
+ '.env',
188
+ '.env.local',
189
+ 'tsconfig.json',
190
+ 'jest.config.js',
191
+ 'vite.config.js',
192
+ 'webpack.config.js',
193
+ ];
194
+
195
+ for (const file of configFiles) {
196
+ if (await fs.pathExists(path.join(process.cwd(), file))) {
197
+ passed.push(`Has ${file}`);
198
+ }
199
+ }
200
+
201
+ // Check for circular dependencies (simplified)
202
+ passed.push('No circular dependencies detected (basic check)');
203
+ }
204
+
205
+ function printHealthResults(passed, warnings, issues) {
206
+ logger.section('Results');
207
+
208
+ if (passed.length > 0) {
209
+ logger.success(`Passed: ${passed.length}`);
210
+ passed.forEach(p => logger.dim(` ✓ ${p}`));
211
+ }
212
+
213
+ if (warnings.length > 0) {
214
+ logger.warning(`Warnings: ${warnings.length}`);
215
+ warnings.forEach(w => logger.dim(` ⚠ ${w}`));
216
+ }
217
+
218
+ if (issues.length > 0) {
219
+ logger.error(`Issues: ${issues.length}`);
220
+ issues.forEach(i => logger.dim(` ✗ ${i}`));
221
+ }
222
+
223
+ // Overall health
224
+ const score = Math.round((passed.length / (passed.length + warnings.length + issues.length)) * 100);
225
+ logger.info('');
226
+ logger.bold(`Health Score: ${score}%`);
227
+
228
+ if (score >= 80) {
229
+ logger.success('Your project looks healthy!');
230
+ } else if (score >= 60) {
231
+ logger.warning('Some improvements needed');
232
+ } else {
233
+ logger.error('Several issues need attention');
234
+ }
235
+ }
236
+
237
+ async function autoFix(issues) {
238
+ logger.info('Attempting to auto-fix issues...');
239
+
240
+ // Auto-fix: create .gitignore if missing
241
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
242
+ if (!(await fs.pathExists(gitignorePath))) {
243
+ await fs.writeFile(gitignorePath, `node_modules/\ndist/\nbuild/\n.env\n.env.local\n`);
244
+ logger.success('Created .gitignore');
245
+ }
246
+
247
+ // Auto-fix: create README if missing
248
+ const readmePath = path.join(process.cwd(), 'README.md');
249
+ if (!(await fs.pathExists(readmePath))) {
250
+ const pkgPath = path.join(process.cwd(), 'package.json');
251
+ let name = 'Project';
252
+ if (await fs.pathExists(pkgPath)) {
253
+ const pkg = await fs.readJson(pkgPath);
254
+ name = pkg.name || name;
255
+ }
256
+ await fs.writeFile(readmePath, `# ${name}\n\nProject description here.\n`);
257
+ logger.success('Created README.md');
258
+ }
259
+ }
260
+
261
+ module.exports = healthCommand;
@@ -0,0 +1,139 @@
1
+ const { Command } = require('commander');
2
+ const path = require('path');
3
+ const fs = require('fs-extra');
4
+ const os = require('os');
5
+ const logger = require('../utils/logger');
6
+ const ProjectDetector = require('../utils/detector');
7
+
8
+ const infoCommand = new Command('info')
9
+ .description('Show project and system information')
10
+ .option('-j, --json', 'Output as JSON', false)
11
+ .action(async (options) => {
12
+ logger.header('📋 Project Information');
13
+
14
+ try {
15
+ const detector = new ProjectDetector();
16
+ const projectInfo = await detector.detect();
17
+
18
+ const info = {
19
+ project: await getProjectInfo(projectInfo),
20
+ system: getSystemInfo(),
21
+ tools: await getToolsInfo(),
22
+ dependencies: await getDependencyInfo(),
23
+ };
24
+
25
+ if (options.json) {
26
+ console.log(JSON.stringify(info, null, 2));
27
+ } else {
28
+ printInfo(info);
29
+ }
30
+ } catch (error) {
31
+ logger.error(`Failed to get info: ${error.message}`);
32
+ process.exit(1);
33
+ }
34
+ });
35
+
36
+ async function getProjectInfo(projectInfo) {
37
+ const pkgPath = path.join(process.cwd(), 'package.json');
38
+ let pkg = {};
39
+ if (await fs.pathExists(pkgPath)) {
40
+ pkg = await fs.readJson(pkgPath);
41
+ }
42
+
43
+ return {
44
+ name: pkg.name || 'Unknown',
45
+ version: pkg.version || '1.0.0',
46
+ description: pkg.description || '',
47
+ framework: projectInfo.framework?.name || 'Unknown',
48
+ type: projectInfo.type || 'Unknown',
49
+ bundler: projectInfo.bundler || 'Unknown',
50
+ typescript: projectInfo.isTypeScript,
51
+ monorepo: projectInfo.isMonorepo,
52
+ nodeVersion: process.version,
53
+ path: process.cwd(),
54
+ };
55
+ }
56
+
57
+ function getSystemInfo() {
58
+ return {
59
+ platform: process.platform,
60
+ arch: process.arch,
61
+ os: `${os.type()} ${os.release()}`,
62
+ cpus: os.cpus().length,
63
+ memory: `${Math.round(os.totalmem() / 1024 / 1024 / 1024)}GB`,
64
+ freeMemory: `${Math.round(os.freemem() / 1024 / 1024 / 1024)}GB`,
65
+ uptime: `${Math.round(os.uptime() / 3600)}h`,
66
+ };
67
+ }
68
+
69
+ async function getToolsInfo() {
70
+ const tools = {};
71
+
72
+ // Check for various tools
73
+ const toolChecks = [
74
+ { name: 'Node.js', command: 'node -v' },
75
+ { name: 'npm', command: 'npm -v' },
76
+ { name: 'yarn', command: 'yarn -v' },
77
+ { name: 'pnpm', command: 'pnpm -v' },
78
+ { name: 'Git', command: 'git --version' },
79
+ { name: 'Docker', command: 'docker --version' },
80
+ { name: 'Python', command: 'python --version' },
81
+ ];
82
+
83
+ for (const tool of toolChecks) {
84
+ try {
85
+ const { execSync } = require('child_process');
86
+ tools[tool.name] = execSync(tool.command, { encoding: 'utf8' }).trim();
87
+ } catch {
88
+ tools[tool.name] = 'Not installed';
89
+ }
90
+ }
91
+
92
+ return tools;
93
+ }
94
+
95
+ async function getDependencyInfo() {
96
+ const pkgPath = path.join(process.cwd(), 'package.json');
97
+ if (!(await fs.pathExists(pkgPath))) {
98
+ return { dependencies: 0, devDependencies: 0 };
99
+ }
100
+
101
+ const pkg = await fs.readJson(pkgPath);
102
+ return {
103
+ dependencies: Object.keys(pkg.dependencies || {}).length,
104
+ devDependencies: Object.keys(pkg.devDependencies || {}).length,
105
+ scripts: Object.keys(pkg.scripts || {}).length,
106
+ };
107
+ }
108
+
109
+ function printInfo(info) {
110
+ logger.section('Project');
111
+ logger.dim(` Name: ${info.project.name}`);
112
+ logger.dim(` Version: ${info.project.version}`);
113
+ logger.dim(` Framework: ${info.project.framework}`);
114
+ logger.dim(` Type: ${info.project.type}`);
115
+ logger.dim(` Bundler: ${info.project.bundler}`);
116
+ logger.dim(` TypeScript: ${info.project.typescript ? '✓' : '✗'}`);
117
+ logger.dim(` Monorepo: ${info.project.monorepo ? '✓' : '✗'}`);
118
+
119
+ logger.section('System');
120
+ logger.dim(` Platform: ${info.system.platform}`);
121
+ logger.dim(` Architecture: ${info.system.arch}`);
122
+ logger.dim(` OS: ${info.system.os}`);
123
+ logger.dim(` CPUs: ${info.system.cpus}`);
124
+ logger.dim(` Memory: ${info.system.memory}`);
125
+ logger.dim(` Free Memory: ${info.system.freeMemory}`);
126
+
127
+ logger.section('Tools');
128
+ for (const [name, version] of Object.entries(info.tools)) {
129
+ const status = version === 'Not installed' ? '✗' : '✓';
130
+ logger.dim(` ${name}: ${version} ${status}`);
131
+ }
132
+
133
+ logger.section('Dependencies');
134
+ logger.dim(` Production: ${info.dependencies.dependencies}`);
135
+ logger.dim(` Development: ${info.dependencies.devDependencies}`);
136
+ logger.dim(` Scripts: ${info.dependencies.scripts}`);
137
+ }
138
+
139
+ module.exports = infoCommand;