diff --git a/index.js b/index.js index ebf79ba..95738c4 100644 --- a/index.js +++ b/index.js @@ -1,271 +1,20 @@ #!/usr/bin/env node const { program } = require("commander"); -const { glob } = require("glob"); -const fs = require("fs/promises"); -const path = require("path"); -// const crypto = require("crypto"); - -program - .name("codecare") - .description("A CLI for codebase health check") - .version("1.0.0"); - -// Function to fetch all files from the project directory -const getAllFiles = async (pattern) => { - try { - const allItems = await glob(pattern, { - ignore: ["node_modules/**"], // Excluding the node_modules directory - absolute: true, - }); - - // Filter only files from the list of items - const files = []; - for (const item of allItems) { - const stats = await fs.stat(item); - if (stats.isFile()) { - files.push(item); - } - } - return files; - } catch (err) { - throw new Error(`Failed to get files: ${err.message}`); - } -}; - -// Function to identify large files -const getLargeFiles = async (files, sizeLimit) => { - const largeFiles = []; - for (const file of files) { - const stats = await fs.stat(file); - if (stats.size > sizeLimit) { - largeFiles.push({ file, size: stats.size }); - } - } - return largeFiles; -}; - -// Function to generate code statistics for all files -const getCodeStats = async (files) => { - let totalLines = 0; - let totalFiles = files.length; - - for (const file of files) { - const content = await fs.readFile(file, "utf-8"); - totalLines += content.split("\n").length; - } - - return { totalLines, totalFiles }; -}; - -// Function to find the duplicate files by name and content -const findDuplicates = async (files) => { - const fileMap = new Map(); - const duplicates = []; - for (const file of files) { - const fileName = path.basename(file); - if (fileMap.has(fileName)) { - const existingFile = fileMap.get(fileName); - // Comparing file content to identify duplicates separately - const fileContent = await fs.readFile(file); - const existingContent = await fs.readFile(existingFile); - if (fileContent.equals(existingContent)) { - duplicates.push({ file1: file, file2: existingFile }); - } - } else { - fileMap.set(fileName, file); - } - } - return duplicates; -}; - -// Function to detect empty files -const findEmptyFiles = async (files) => { - const emptyFiles = []; - for (const file of files) { - const stats = await fs.stat(file); - if (stats.size === 0) { - emptyFiles.push(file); - } - } - return emptyFiles; -}; - -// Function to generate HTML report -const generateHtmlReport = ( - stats, - largeFiles, - duplicates, - emptyFiles, - outputPath -) => { - const htmlContent = ` - - - - - - Codebase Health Check Report - - - -

šŸŽÆ Codebase Health Check Report šŸŽÆ

-
-

šŸ“Š Codebase Statistics

-

Total files scanned: ${stats.totalFiles}

-

Total lines of code: ${stats.totalLines}

-

Total duplicate files: ${duplicates.length}

-

Total empty files: ${emptyFiles.length}

-
-
-

šŸ—‚ļø Large Files Found

- ${ - largeFiles.length === 0 - ? "

No large files found! Your code is neat and optimized!

" - : largeFiles - .map( - ({ file, size }) => - `

šŸ”“ ${file} - ${size} bytes

` - ) - .join("") - } -
-
-

šŸ“„ Duplicate Files Found

- ${ - duplicates.length === 0 - ? "

No duplicate files found!

" - : duplicates - .map( - ({ file1, file2 }) => - `

šŸ”“ ${file1} and ${file2}

` - ) - .join("") - } -
-
-

šŸ“‚ Empty Files Found

- ${ - emptyFiles.length === 0 - ? "

No empty files found! Your code is in great shape!

" - : emptyFiles - .map((file) => `

šŸ”“ ${file} is empty.

`) - .join("") - } -
- - - - `; - - // Write the HTML content to the file - fs.writeFile(outputPath, htmlContent, "utf-8") - .then(() => { - console.log(`āœ… Report generated at ${outputPath}`); - }) - .catch((err) => { - console.error(`āŒ Error writing report to file: ${err.message}`); - }); -}; - -// Command's action function -// This function will be executed when the user runs the `check` command in the CLI -// This function will check the health of the codebase and generate a report -// Default 50KB for large files -// Default output format is HTML -// Default directory to save the report is `./reports` -program - .command("check") - .description("Check the health of the codebase") - .option("-p, --pattern ", "Glob pattern for files", "**/*") - .option("-s, --size ", "Size limit in bytes for large files", "50000") - .option("-o, --output ", "Output format (html/json)", "html") - .option( - "-d, --directory ", - "Directory to save the report", - "./reports" +const clc = require("cli-color"); +const { customizeDefault } = require("./utils/customizeDefault"); +const checkCommand = require("./commands/checkCommand"); +const helpCommand = require("./commands/helpCommand"); + +program.name("codecare"); +program.version( + clc.greenBright( + program.name() + " CLI version:" + " v" + require("./package.json").version ) - .action(async (options) => { - const { pattern, size, output, directory } = options; - - try { - console.log("\nšŸŽÆ **Codebase Health Check** šŸŽÆ"); - console.log( - "Scanning your codebase for large files, duplicates, and empty files... šŸ•µļøā€ā™‚ļø\n" - ); - - const files = await getAllFiles(pattern); - const largeFiles = await getLargeFiles(files, parseInt(size)); - const { totalLines, totalFiles } = await getCodeStats(files); - const duplicates = await findDuplicates(files); - const emptyFiles = await findEmptyFiles(files); - - console.log("\nšŸ“Š **Codebase Statistics** šŸ“Š"); - console.log(`- Total files scanned: ${totalFiles}`); - console.log(`- Total lines of code: ${totalLines}`); - console.log(`- Total duplicate files: ${duplicates.length}`); - console.log(`- Total empty files: ${emptyFiles.length}`); - console.log("\nšŸ—‚ļø **Large Files Found** šŸ—‚ļø"); - if (largeFiles.length === 0) { - console.log( - "āœ… No large files found! Your code is neat and optimized!" - ); - } else { - largeFiles.forEach(({ file, size }) => { - console.log(` šŸ”“ ${file} - ${size} bytes`); - }); - } - - console.log("\nšŸ“„ **Duplicate Files Found** šŸ“„"); - if (duplicates.length === 0) { - console.log("āœ… No duplicate files found!"); - } else { - duplicates.forEach(({ file1, file2 }) => { - console.log(` šŸ”“ Duplicate files: ${file1} and ${file2}`); - }); - } - - console.log("\nšŸ“‚ **Empty Files Found** šŸ“‚"); - if (emptyFiles.length === 0) { - console.log("āœ… No empty files found! Your code is in great shape!"); - } else { - emptyFiles.forEach((file) => { - console.log(` šŸ”“ Empty file: ${file}`); - }); - } - - console.log("\nšŸ”§ **Health Check Complete!** šŸ”§"); - - // Ensuring that, directory exists if no then create it - const reportDirectory = path.resolve(directory); - await fs.mkdir(reportDirectory, { recursive: true }); +); - // Here it will generate HTML report - const reportPath = path.join(reportDirectory, "report.html"); - if (output === "html") { - generateHtmlReport( - { totalLines, totalFiles }, - largeFiles, - duplicates, - emptyFiles, - reportPath - ); - } else if (output === "json") { - // Based on options in CLI, Generate JSON report - console.log("JSON report option not implemented yet"); - } - } catch (err) { - console.error("āŒ An error occurred:", err.message); - } - }); +customizeDefault(program); +checkCommand(); +helpCommand(); program.parse(process.argv);