UNPKG

vibe-seo

Version:

AI-friendly SEO generator for modern web frameworks

246 lines (205 loc) 9.08 kB
#!/usr/bin/env node const { Command } = require('commander'); const fs = require('fs-extra'); const path = require('path'); const chalk = require('chalk'); const ora = require('ora'); const { loadConfigWithFrameworkDetection, generateSitemap, generateRobots, generateMetaTags, generateAll, scanPages } = require('../lib'); const program = new Command(); program .name('vibe-seo-gen') .description('Generate SEO files for your project') .version('1.0.0'); program .command('sitemap') .description('Generate sitemap.xml') .option('-c, --config <path>', 'configuration file path', './seo/config/seo.config.yaml') .option('-o, --output <path>', 'output directory', './seo/output') .option('--debug', 'enable debug logging') .action(async (options) => { const spinner = ora('Generating sitemap...').start(); try { if (options.debug) { spinner.stop(); console.log(chalk.yellow('Debug mode enabled')); console.log(chalk.cyan('Config path:'), options.config); console.log(chalk.cyan('Output path:'), options.output); spinner.start(); } const config = await loadConfigWithFrameworkDetection(options.config, process.cwd()); if (options.debug) { spinner.stop(); console.log(chalk.cyan('Loaded config:'), JSON.stringify(config, null, 2)); spinner.start(); } const pages = await scanPages(config); if (options.debug) { spinner.stop(); console.log(chalk.cyan('Detected pages:'), pages.length); pages.forEach(page => console.log(` - ${page.url} (${page.framework})`)); spinner.start(); } await generateSitemap(pages, config, options.output); spinner.succeed(chalk.green('Sitemap generated successfully!')); } catch (error) { spinner.fail(chalk.red('Failed to generate sitemap')); console.error(chalk.red('Error details:'), error.message); if (options.debug) { console.error(chalk.red('Stack trace:'), error.stack); } process.exit(1); } }); program .command('robots') .description('Generate robots.txt') .option('-c, --config <path>', 'configuration file path', './seo/config/seo.config.yaml') .option('-o, --output <path>', 'output directory', './seo/output') .option('--debug', 'enable debug logging') .action(async (options) => { const spinner = ora('Generating robots.txt...').start(); try { if (options.debug) { spinner.stop(); console.log(chalk.yellow('Debug mode enabled')); console.log(chalk.cyan('Config path:'), options.config); console.log(chalk.cyan('Output path:'), options.output); // Check if config file exists const configExists = await fs.pathExists(options.config); console.log(chalk.cyan('Config file exists:'), configExists); spinner.start(); } const config = await loadConfigWithFrameworkDetection(options.config, process.cwd()); if (options.debug) { spinner.stop(); console.log(chalk.cyan('Site URL:'), config.site?.url); console.log(chalk.cyan('Site Name:'), config.site?.name); console.log(chalk.cyan('Bots config:'), config.bots); spinner.start(); } await generateRobots(config, options.output); spinner.succeed(chalk.green('Robots.txt generated successfully!')); } catch (error) { spinner.fail(chalk.red('Failed to generate robots.txt')); console.error(chalk.red('Error details:'), error.message); if (options.debug) { console.error(chalk.red('Stack trace:'), error.stack); } process.exit(1); } }); program .command('meta') .description('Generate meta tags for pages') .option('-c, --config <path>', 'configuration file path', './seo/config/seo.config.yaml') .option('-o, --output <path>', 'output directory', './seo/output') .option('--debug', 'enable debug logging') .action(async (options) => { const spinner = ora('Generating meta tags...').start(); try { const config = await loadConfigWithFrameworkDetection(options.config, process.cwd()); const pages = await scanPages(config); await generateMetaTags(pages, config, options.output); spinner.succeed(chalk.green('Meta tags generated successfully!')); } catch (error) { spinner.fail(chalk.red('Failed to generate meta tags')); console.error(chalk.red('Error details:'), error.message); if (options.debug) { console.error(chalk.red('Stack trace:'), error.stack); } process.exit(1); } }); program .command('all') .description('Generate all SEO files') .option('-c, --config <path>', 'configuration file path', './seo/config/seo.config.yaml') .option('-o, --output <path>', 'output directory', './seo/output') .option('--debug', 'enable debug logging') .action(async (options) => { const spinner = ora('Generating all SEO files...').start(); try { if (options.debug) { spinner.stop(); console.log(chalk.yellow('Debug mode enabled')); console.log(chalk.cyan('Config path:'), options.config); console.log(chalk.cyan('Output path:'), options.output); console.log(chalk.cyan('Current directory:'), process.cwd()); // Check if config file exists const configExists = await fs.pathExists(options.config); console.log(chalk.cyan('Config file exists:'), configExists); spinner.start(); } const config = await loadConfigWithFrameworkDetection(options.config, process.cwd()); if (options.debug) { spinner.stop(); console.log(chalk.cyan('Loaded config successfully')); console.log(chalk.cyan('Site URL:'), config.site?.url); console.log(chalk.cyan('Site Name:'), config.site?.name); console.log(chalk.cyan('Framework:'), config.framework); spinner.start(); } spinner.text = 'Scanning pages...'; const pages = await scanPages(config); if (options.debug) { spinner.stop(); console.log(chalk.cyan('Pages detected:'), pages.length); pages.forEach(page => console.log(` - ${page.url} (${page.framework || page.type})`)); spinner.start(); } spinner.text = 'Generating all SEO files...'; const results = await generateAll(pages, config, options.output); spinner.succeed(chalk.green('All SEO files generated successfully!')); console.log('\nGenerated files:'); console.log(chalk.cyan('• sitemap.xml')); console.log(chalk.cyan('• robots.txt')); console.log(chalk.cyan('• meta-tags/ (directory)')); console.log(chalk.cyan('• jsonld/ (directory)')); if (results.fixes && results.fixes.length > 0) { console.log(chalk.green('\n🔧 Automatic compatibility fixes applied:')); results.fixes.forEach(fix => console.log(chalk.cyan(` • ${fix}`))); console.log(chalk.green('\n✅ Your Vite React app is now inherently compatible!')); console.log(chalk.gray(' No manual configuration needed - everything works out of the box.')); } if (results.deployment && results.deployment.length > 0) { console.log(chalk.yellow('\n📦 Deployment guides generated for reference:')); console.log(chalk.cyan('• VITE_REACT_DEPLOYMENT_GUIDE.md')); console.log(chalk.cyan('• vite.config.seo.ts')); console.log(chalk.cyan('• vercel.json')); console.log(chalk.cyan('• netlify.toml')); console.log(chalk.gray('\n These are for reference only - fixes are already applied automatically.')); } } catch (error) { spinner.fail(chalk.red('Failed to generate SEO files')); console.error(chalk.red('Error details:'), error.message); if (options.debug) { console.error(chalk.red('Stack trace:'), error.stack); } process.exit(1); } }); program .command('scan') .description('Scan and list detected pages') .option('-c, --config <path>', 'configuration file path', './seo/config/seo.config.yaml') .option('--debug', 'enable debug logging') .action(async (options) => { const spinner = ora('Scanning pages...').start(); try { const config = await loadConfigWithFrameworkDetection(options.config, process.cwd()); const pages = await scanPages(config); spinner.succeed(chalk.green(`Found ${pages.length} pages`)); console.log('\nDetected pages:'); pages.forEach(page => { console.log(chalk.cyan(`• ${page.url}`) + chalk.gray(` (${page.type})`)); }); } catch (error) { spinner.fail(chalk.red('Failed to scan pages')); console.error(chalk.red('Error details:'), error.message); if (options.debug) { console.error(chalk.red('Stack trace:'), error.stack); } process.exit(1); } }); program.parse();