UNPKG

@lenne.tech/cli

Version:

lenne.Tech CLI: lt

274 lines (273 loc) 12.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const https_1 = require("https"); const path_1 = require("path"); /** * Fetch current LTS version from Node.js API */ function fetchCurrentLtsVersion() { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve) => { const timeout = setTimeout(() => resolve(null), 5000); (0, https_1.get)('https://nodejs.org/dist/index.json', (res) => { let data = ''; res.on('data', (chunk) => (data += chunk)); res.on('end', () => { clearTimeout(timeout); try { const releases = JSON.parse(data); // Find the first (latest) release with an LTS codename const ltsRelease = releases.find((r) => r.lts !== false); if (ltsRelease) { const major = parseInt(ltsRelease.version.replace('v', '').split('.')[0], 10); resolve({ codename: ltsRelease.lts, major }); } else { resolve(null); } } catch (_a) { resolve(null); } }); res.on('error', () => { clearTimeout(timeout); resolve(null); }); }).on('error', () => { clearTimeout(timeout); resolve(null); }); }); }); } /** * Diagnose common issues */ const DoctorCommand = { alias: ['dr'], description: 'Diagnose common issues', hidden: false, name: 'doctor', run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () { const { filesystem, parameters, print: { colors, info, spin, success }, system, } = toolbox; const fix = parameters.options.fix || parameters.options.f; const offline = parameters.options.offline || parameters.options.o; const checks = []; info(''); info(colors.bold('lt doctor')); info(colors.dim('Checking your development environment...')); info(''); // Check Node.js version const nodeSpinner = spin('Checking Node.js...'); try { const nodeVersion = yield system.run('node --version'); const version = nodeVersion === null || nodeVersion === void 0 ? void 0 : nodeVersion.trim().replace('v', ''); const major = parseInt((version === null || version === void 0 ? void 0 : version.split('.')[0]) || '0', 10); // Fetch current LTS version (fallback to 22 if API fails or offline) const ltsInfo = offline ? null : yield fetchCurrentLtsVersion(); const ltsVersion = (ltsInfo === null || ltsInfo === void 0 ? void 0 : ltsInfo.major) || 22; const ltsCodename = (ltsInfo === null || ltsInfo === void 0 ? void 0 : ltsInfo.codename) || 'LTS'; const minSupported = ltsVersion - 4; // Previous LTS (e.g., 22 -> 18) if (major >= ltsVersion) { nodeSpinner.succeed(`Node.js ${nodeVersion === null || nodeVersion === void 0 ? void 0 : nodeVersion.trim()} (${ltsCodename} LTS)`); checks.push({ name: 'Node.js', status: 'ok' }); } else if (major >= minSupported) { nodeSpinner.warn(`Node.js ${nodeVersion === null || nodeVersion === void 0 ? void 0 : nodeVersion.trim()} (v${ltsVersion}+ ${ltsCodename} LTS recommended)`); checks.push({ details: `Current: ${nodeVersion === null || nodeVersion === void 0 ? void 0 : nodeVersion.trim()}, Latest LTS: v${ltsVersion} (${ltsCodename})`, fix: `Upgrade to Node.js ${ltsVersion} LTS or newer`, name: 'Node.js', status: 'warning', }); } else { nodeSpinner.fail(`Node.js ${nodeVersion === null || nodeVersion === void 0 ? void 0 : nodeVersion.trim()} (v${minSupported}+ required)`); checks.push({ details: `Current: ${nodeVersion === null || nodeVersion === void 0 ? void 0 : nodeVersion.trim()}, Latest LTS: v${ltsVersion} (${ltsCodename})`, fix: `Upgrade to Node.js ${ltsVersion} LTS or newer`, name: 'Node.js', status: 'error', }); } } catch (_a) { nodeSpinner.fail('Node.js not found'); checks.push({ fix: 'Install Node.js from https://nodejs.org', name: 'Node.js', status: 'error', }); } // Check package manager const detectedPm = toolbox.pm.detect(); const npmSpinner = spin(`Checking package manager (${detectedPm})...`); try { const pmVersion = yield system.run(`${detectedPm} --version`); npmSpinner.succeed(`${detectedPm} ${pmVersion === null || pmVersion === void 0 ? void 0 : pmVersion.trim()}`); checks.push({ name: `Package Manager (${detectedPm})`, status: 'ok' }); } catch (_b) { npmSpinner.fail(`${detectedPm} not found`); checks.push({ fix: `Install ${detectedPm}`, name: `Package Manager (${detectedPm})`, status: 'error', }); } // Check git const gitSpinner = spin('Checking Git...'); try { const gitVersion = yield system.run('git --version'); gitSpinner.succeed(gitVersion === null || gitVersion === void 0 ? void 0 : gitVersion.trim()); checks.push({ name: 'Git', status: 'ok' }); } catch (_c) { gitSpinner.fail('Git not found'); checks.push({ fix: 'Install Git from https://git-scm.com', name: 'Git', status: 'error', }); } // Check lt CLI version const ltSpinner = spin('Checking lt CLI...'); try { const packageJsonPath = (0, path_1.join)(__dirname, '..', '..', 'package.json'); const packageJson = JSON.parse(filesystem.read(packageJsonPath) || '{}'); const currentVersion = packageJson.version; // Check for updates (skip in offline mode) if (offline) { ltSpinner.succeed(`lt CLI v${currentVersion}`); checks.push({ name: 'lt CLI', status: 'ok' }); } else { try { const latestVersion = yield system.run('npm view @lenne.tech/cli version 2>/dev/null'); if ((latestVersion === null || latestVersion === void 0 ? void 0 : latestVersion.trim()) && latestVersion.trim() !== currentVersion) { ltSpinner.warn(`lt CLI v${currentVersion} (v${latestVersion.trim()} available)`); checks.push({ details: `Current: v${currentVersion}, Latest: v${latestVersion.trim()}`, fix: 'Run: lt update', name: 'lt CLI', status: 'warning', }); } else { ltSpinner.succeed(`lt CLI v${currentVersion}`); checks.push({ name: 'lt CLI', status: 'ok' }); } } catch (_d) { ltSpinner.succeed(`lt CLI v${currentVersion}`); checks.push({ name: 'lt CLI', status: 'ok' }); } } } catch (_e) { ltSpinner.warn('Could not determine lt CLI version'); checks.push({ name: 'lt CLI', status: 'warning' }); } // Check for lt.config in current directory const configSpinner = spin('Checking project configuration...'); const cwd = filesystem.cwd(); const configFiles = ['lt.config.json', 'lt.config.yaml', 'lt.config']; let hasConfig = false; for (const configFile of configFiles) { if (filesystem.exists((0, path_1.join)(cwd, configFile))) { hasConfig = true; configSpinner.succeed(`Found ${configFile}`); checks.push({ name: 'lt.config', status: 'ok' }); break; } } if (!hasConfig) { configSpinner.info('No lt.config found (optional)'); checks.push({ details: 'Configuration file is optional but recommended', fix: 'Run: lt config init', name: 'lt.config', status: 'warning', }); } // Check for package.json const pkgSpinner = spin('Checking package.json...'); if (filesystem.exists((0, path_1.join)(cwd, 'package.json'))) { pkgSpinner.succeed('Found package.json'); checks.push({ name: 'package.json', status: 'ok' }); // Check node_modules const nmSpinner = spin('Checking dependencies...'); if (filesystem.exists((0, path_1.join)(cwd, 'node_modules'))) { nmSpinner.succeed('Dependencies installed'); checks.push({ name: 'Dependencies', status: 'ok' }); } else { nmSpinner.warn('Dependencies not installed'); checks.push({ fix: `Run: ${toolbox.pm.install()}`, name: 'Dependencies', status: 'warning', }); if (fix) { info(''); const installSpinner = spin('Installing dependencies...'); try { yield system.run(toolbox.pm.install()); installSpinner.succeed('Dependencies installed'); } catch (_f) { installSpinner.fail('Failed to install dependencies'); } } } } else { pkgSpinner.info('No package.json (not a Node.js project)'); } // Summary info(''); info(colors.dim('─'.repeat(50))); const errors = checks.filter((c) => c.status === 'error'); const warnings = checks.filter((c) => c.status === 'warning'); const ok = checks.filter((c) => c.status === 'ok'); if (errors.length === 0 && warnings.length === 0) { success('All checks passed!'); } else { info(`Checks: ${ok.length} passed, ${warnings.length} warnings, ${errors.length} errors`); } // Show issues and fixes if (errors.length > 0 || warnings.length > 0) { info(''); info(colors.bold('Issues:')); for (const check of [...errors, ...warnings]) { const icon = check.status === 'error' ? colors.red('✖') : colors.yellow('⚠'); info(` ${icon} ${check.name}`); if (check.details) { info(colors.dim(` ${check.details}`)); } if (check.fix) { info(colors.cyan(` Fix: ${check.fix}`)); } } } if (!fix && warnings.length > 0) { info(''); info(colors.dim('Run "lt doctor --fix" to attempt automatic fixes')); } info(''); // For tests return `doctor ${errors.length === 0 ? 'ok' : 'issues'}`; }), }; exports.default = DoctorCommand;