@lenne.tech/cli
Version:
lenne.Tech CLI: lt
274 lines (273 loc) • 12.6 kB
JavaScript
;
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;