UNPKG

auto-readme-creator

Version:

A CLI tool to automatically generate README.md files based on comments in your code.

120 lines (100 loc) 4.17 kB
#!/usr/bin/env node const chokidar = require('chokidar'); const fs = require('fs'); const path = require('path'); const { parse } = require('@babel/parser'); // Function to extract comments based on file type const extractComments = (code, fileType) => { let comments = []; if (['js', 'jsx', 'ts'].includes(fileType)) { // Parse JavaScript/TypeScript code with Babel const ast = parse(code, { sourceType: 'module', plugins: ['jsx', 'typescript'] }); comments = ast.comments || []; return comments.map(comment => comment.value.trim()); } else if (fileType === 'py') { // Extract Python comments (lines starting with #) const regex = /#(.*)/g; let match; while ((match = regex.exec(code)) !== null) { comments.push(match[1].trim()); } } else if (fileType === 'css' || fileType === 'html') { // Extract CSS/HTML comments (/* ... */ for CSS and <!-- ... --> for HTML) const regex = fileType === 'css' ? /\/\*([\s\S]*?)\*\//g : /<!--([\s\S]*?)-->/g; let match; while ((match = regex.exec(code)) !== null) { comments.push(match[1].trim()); } } else if (fileType === 'java') { // Extract Java comments (// ... and /* ... */) const regex = /\/\/(.*)|\/\*([\s\S]*?)\*\//g; let match; while ((match = regex.exec(code)) !== null) { comments.push((match[1] || match[2]).trim()); } } return comments; }; // Function to generate markdown from comments const generateMarkdown = (comments, fileName) => { // Extract the base name without extension for section title const baseName = path.basename(fileName, path.extname(fileName)); let markdown = `<!-- START ${baseName.toUpperCase()} SECTION -->\n`; markdown += `# ${baseName}\n\n`; let currentHeading = null; comments.forEach(comment => { if (comment.startsWith('~')) { // Treat comments starting with ~ as paragraphs under the last heading if (currentHeading) { markdown += `${comment.substring(1).trim()}\n\n`; } } else { // Use comments without ~ as headings currentHeading = comment; markdown += `## ${currentHeading}\n\n`; } }); if (!currentHeading) { markdown += `No comments found in ${fileName}.\n\n`; } markdown += `<!-- END ${baseName.toUpperCase()} SECTION -->\n\n`; return markdown; }; // Function to update the README with content from each file const updateReadme = (fileName, markdownContent) => { let readmeContent = ''; // Check if README exists if (fs.existsSync('README.md')) { readmeContent = fs.readFileSync('README.md', 'utf-8'); } const sectionStart = `<!-- START ${path.basename(fileName, path.extname(fileName)).toUpperCase()} SECTION -->`; const sectionEnd = `<!-- END ${path.basename(fileName, path.extname(fileName)).toUpperCase()} SECTION -->`; // Replace existing content for the file if it already exists const regex = new RegExp(`${sectionStart}[\\s\\S]*?${sectionEnd}`, 'g'); if (readmeContent.match(regex)) { readmeContent = readmeContent.replace(regex, markdownContent); } else { // Append new content at the end readmeContent += markdownContent; } // Write the updated content back to README.md fs.writeFileSync('README.md', readmeContent); console.log(`README.md has been updated with changes from ${fileName}.`); }; // Function to watch files and generate README const watchFiles = () => { const watcher = chokidar.watch('./**/*.{js,jsx,ts,py,css,html,java}', { ignored: /node_modules/, persistent: true }); watcher.on('change', (filePath) => { console.log(`File ${filePath} has been changed. Generating README...`); const code = fs.readFileSync(filePath, 'utf-8'); const fileType = path.extname(filePath).slice(1); // Get the file extension const comments = extractComments(code, fileType); const markdown = generateMarkdown(comments, path.basename(filePath)); updateReadme(path.basename(filePath), markdown); }); console.log('Watching for file changes...'); }; watchFiles();