@neurolint/cli
Version:
NeuroLint CLI for React/Next.js modernization with advanced 6-layer orchestration and intelligent AST transformations
263 lines (235 loc) • 8.87 kB
text/typescript
import chalk from "chalk";
import fs from "fs-extra";
import path from "path";
import { loadConfig, validateConfig } from "../utils/config";
import { withRetry } from "../utils/retry";
interface StatusOptions {
detailed?: boolean;
}
export async function statusCommand(options: StatusOptions) {
console.log(chalk.white.bold("React/Next.js Modernization Readiness\n"));
try {
// Check configuration
const configPath = path.join(process.cwd(), ".neurolint.json");
const hasConfig = await fs.pathExists(configPath);
console.log(chalk.white("Configuration:"));
if (hasConfig) {
console.log(
`${chalk.white("PASS")} Configuration file found: .neurolint.json`,
);
const config = await loadConfig();
console.log(`${chalk.white(" API URL:")} ${config.api.url}`);
console.log(
`${chalk.white(" Enabled Layers:")} ${config.layers.enabled.join(", ")}`,
);
console.log(`${chalk.white(" Output Format:")} ${config.output.format}`);
if (config.apiKey) {
console.log(`${chalk.white("PASS")} Premium features enabled`);
} else {
console.log(
`${chalk.white("FREE")} Free tier active (unlimited analysis)`,
);
console.log(
`${chalk.white("INFO")} Run 'neurolint login' for premium fixes & reports`,
);
}
} else {
console.log(
`${chalk.white("WARN")} No configuration found (run: neurolint init)`,
);
}
// Check project structure
console.log(chalk.white("\nProject Structure:"));
const packageJsonPath = path.join(process.cwd(), "package.json");
if (await fs.pathExists(packageJsonPath)) {
const packageJson = await fs.readJson(packageJsonPath);
console.log(`${chalk.white("PASS")} package.json found`);
const deps = {
...packageJson.dependencies,
...packageJson.devDependencies,
};
if (deps.typescript) {
console.log(`${chalk.white("PASS")} TypeScript project detected`);
}
if (deps.react) {
const reactVersion = deps.react?.replace('^', '').replace('~', '');
if (reactVersion && parseFloat(reactVersion) >= 18) {
console.log(`${chalk.white("PASS")} React 18+ detected (${reactVersion})`);
} else {
console.log(`${chalk.white("UPGRADE")} React ${reactVersion} - consider upgrading to React 18`);
}
}
if (deps.next) {
const nextVersion = deps.next?.replace('^', '').replace('~', '');
if (nextVersion && parseFloat(nextVersion) >= 14) {
console.log(`${chalk.white("PASS")} Next.js 14+ detected (${nextVersion})`);
} else {
console.log(`${chalk.white("UPGRADE")} Next.js ${nextVersion} - consider upgrading to Next.js 14`);
}
}
}
// Check for TypeScript config
const tsconfigPath = path.join(process.cwd(), "tsconfig.json");
if (await fs.pathExists(tsconfigPath)) {
console.log(`${chalk.white("PASS")} tsconfig.json found`);
}
// Scan for files
const { glob } = await import("glob");
const jsFiles = await glob("**/*.{js,jsx}", {
cwd: process.cwd(),
ignore: ["node_modules/**", "dist/**", "build/**"],
});
const tsFiles = await glob("**/*.{ts,tsx}", {
cwd: process.cwd(),
ignore: ["node_modules/**", "dist/**", "build/**"],
});
// Modernization Analysis
console.log(chalk.white("\nModernization Analysis:"));
console.log(`${chalk.white(" JavaScript files:")} ${jsFiles.length}`);
console.log(`${chalk.white(" TypeScript files:")} ${tsFiles.length}`);
console.log(
`${chalk.white(" Total files:")} ${jsFiles.length + tsFiles.length}`,
);
// Quick legacy pattern detection
let legacyPatterns = 0;
const sampleFiles = [...jsFiles, ...tsFiles].slice(0, 5); // Quick sample
for (const file of sampleFiles) {
try {
const content = await fs.readFile(path.join(process.cwd(), file), 'utf-8');
if (content.includes('class ') && content.includes('extends Component')) legacyPatterns++;
if (content.includes('componentDidMount')) legacyPatterns++;
if (content.includes('ReactDOM.render')) legacyPatterns++;
} catch (e) {
// Skip files that can't be read
}
}
if (legacyPatterns > 0) {
console.log(`${chalk.white("DETECTED")} ${legacyPatterns} legacy patterns in sample files`);
console.log(`${chalk.white("SUGGEST")} Run 'neurolint analyze' for full modernization report`);
} else {
console.log(`${chalk.white("PASS")} No obvious legacy patterns detected`);
}
if (options.detailed) {
console.log(chalk.white("\nDetailed Analysis:"));
// Analyze file extensions
const extensions: Record<string, number> = {};
[...jsFiles, ...tsFiles].forEach((file: string) => {
const ext = path.extname(file);
extensions[ext] = (extensions[ext] || 0) + 1;
});
Object.entries(extensions).forEach(([ext, count]) => {
console.log(`${chalk.white(` ${ext} files:`)} ${count}`);
});
// Check for common patterns
console.log(chalk.white("\nCommon Patterns:"));
const componentFiles = [...jsFiles, ...tsFiles].filter(
(f: string) =>
f.includes("component") ||
f.includes("Component") ||
/\/[A-Z]/.test(f),
);
console.log(
`${chalk.white(" Component files:")} ${componentFiles.length}`,
);
const testFiles = [...jsFiles, ...tsFiles].filter(
(f: string) =>
f.includes(".test.") ||
f.includes(".spec.") ||
f.includes("__tests__"),
);
console.log(`${chalk.white(" Test files:")} ${testFiles.length}`);
}
// Health check with retry logic
console.log(chalk.white("\nHealth Check:"));
try {
const axios = await import("axios");
const config = await loadConfig();
await withRetry(
async () => {
const response = await axios.default.get(
`${config.api?.url || "https://neurolint.dev/api"}/health`,
{
timeout: 5000,
},
);
if (response.status === 200) {
console.log(`${chalk.white("PASS")} NeuroLint API is accessible`);
if (response.data?.version) {
console.log(
`${chalk.white(" Version:")} ${response.data.version}`,
);
}
if (response.data?.status) {
console.log(
`${chalk.white(" Status:")} ${response.data.status}`,
);
}
}
},
{
maxAttempts: 2,
delay: 1000,
},
);
// Test authentication if API key is configured
if (config.apiKey) {
try {
const authResponse = await axios.default.get(
`${config.api?.url || "https://neurolint.dev/api"}/auth/verify`,
{
headers: { Authorization: `Bearer ${config.apiKey}` },
timeout: 5000,
},
);
if (authResponse.status === 200) {
console.log(`${chalk.white("PASS")} Authentication is valid`);
}
} catch (authError) {
console.log(`${chalk.white("FAIL")} Authentication failed`);
console.log(
`${chalk.gray(' Run "neurolint login" to re-authenticate')}`,
);
}
}
} catch (error) {
console.log(`${chalk.white("FAIL")} NeuroLint API is not accessible`);
if (error instanceof Error) {
if (error.message.includes("ECONNREFUSED")) {
console.log(
`${chalk.gray(" Make sure the server is running: npm run dev")}`,
);
} else if (error.message.includes("ENOTFOUND")) {
console.log(
`${chalk.gray(" Check the API URL in your configuration")}`,
);
} else {
console.log(`${chalk.gray(` Error: ${error.message}`)}`);
}
}
}
// Recommendations
console.log(chalk.white("\nRecommendations:"));
if (!hasConfig) {
console.log(
`${chalk.white("•")} Run ${chalk.white("neurolint init")} to set up configuration`,
);
}
const config = await loadConfig();
if (!config.apiKey) {
console.log(
`${chalk.white("•")} Run ${chalk.white("neurolint login")} to authenticate`,
);
}
if (jsFiles.length + tsFiles.length > 0) {
console.log(
`${chalk.white("•")} Run ${chalk.white("neurolint analyze")} to check your code`,
);
}
} catch (error) {
console.error(
chalk.red(
`Status check failed: ${error instanceof Error ? error.message : "Unknown error"}`,
),
);
}
}