vibe-seo
Version:
AI-friendly SEO generator for modern web frameworks
369 lines (311 loc) • 11.2 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const yaml = require('js-yaml');
/**
* Load and parse YAML configuration file
*/
async function loadConfig(configPath) {
try {
const configContent = await fs.readFile(configPath, 'utf8');
const config = yaml.load(configContent);
// Validate required fields
validateConfig(config);
// Set defaults
config.framework = config.framework || 'static';
config.outputDir = config.outputDir || './seo/output';
config.publicDir = config.publicDir || './public';
return config;
} catch (error) {
throw new Error(`Failed to load config: ${error.message}`);
}
}
/**
* Load config and auto-detect framework if needed
*/
async function loadConfigWithFrameworkDetection(configPath, projectDir) {
try {
const config = await loadConfig(configPath);
// If framework is static, try to auto-detect the actual framework
if (config.framework === 'static') {
const { detectFramework } = require('./utils');
const detectedFramework = await detectFramework(projectDir);
if (detectedFramework !== 'static') {
console.log(`🔄 Auto-detected framework: ${detectedFramework} (was set to 'static')`);
config.framework = detectedFramework;
// Update the config file with the correct framework
const yaml = require('js-yaml');
const updatedConfig = yaml.dump(config, { indent: 2 });
await fs.writeFile(configPath, updatedConfig);
console.log(`✅ Updated config file with correct framework: ${detectedFramework}`);
}
}
return config;
} catch (error) {
throw new Error(`Failed to load config: ${error.message}`);
}
}
/**
* Create default configuration for a framework
*/
async function createConfig(framework, outputDir) {
const configDir = path.join(outputDir, 'config');
await fs.ensureDir(configDir);
const configPath = path.join(configDir, 'seo.config.yaml');
// Get the YAML string with comments directly
const yamlContent = getDefaultConfig(framework);
await fs.writeFile(configPath, yamlContent);
return configPath;
}
/**
* Validate configuration object
*/
function validateConfig(config) {
const required = ['site', 'bots'];
for (const field of required) {
if (!config[field]) {
throw new Error(`Missing required config field: ${field}`);
}
}
if (!config.site.url) {
throw new Error('Missing required field: site.url');
}
if (!config.site.name) {
throw new Error('Missing required field: site.name');
}
}
/**
* Get default configuration for a framework
*/
function getDefaultConfig(framework) {
// Create a comprehensive YAML configuration with detailed comments
const yamlConfig = `# =============================================================================
# VIBE-SEO CONFIGURATION FILE
# =============================================================================
# This file configures SEO settings for your website.
# Edit the values below to match your site's information.
# =============================================================================
# Framework Detection (auto-detected, but you can override)
# Options: nextjs, nextjs-app, nextjs-pages, react, vue, angular, static
framework: ${framework}
# =============================================================================
# SITE INFORMATION
# =============================================================================
# Basic information about your website
site:
# Your site's base URL (required)
# This is used for generating absolute URLs in sitemaps and meta tags
url: "https://yoursite.com"
# Your site's name (required)
# Used in page titles, meta tags, and robots.txt
name: "Your Site Name"
# Default meta description for your site
# Used as fallback when page-specific descriptions aren't available
description: "Your compelling site description that explains what your site is about"
# Path to your site's logo
# Used for Open Graph images and structured data
logo: "/assets/logo.png"
# Primary language code (ISO 639-1)
# Used for hreflang tags and language detection
language: "en"
# Locale for Open Graph and social media
# Format: language_COUNTRY (e.g., en_US, es_ES)
locale: "en_US"
# Default author name
# Used in meta tags and structured data
author: "Your Name"
# Path to your favicon
# Used in meta tags and browser tabs
favicon: "/favicon.ico"
# =============================================================================
# MULTILINGUAL CONFIGURATION
# =============================================================================
# Configure multiple languages for international SEO
languages:
# Default language code
default: "en"
# Supported languages
# Add more languages to enable hreflang tags
supported:
# English (default)
- code: "en"
locale: "en_US"
name: "English"
url: "https://yoursite.com"
# Example: Add Spanish language
# - code: "es"
# locale: "es_ES"
# name: "Español"
# url: "https://yoursite.com/es"
# Example: Add German language
# - code: "de"
# locale: "de_DE"
# name: "Deutsch"
# url: "https://yoursite.de"
# =============================================================================
# BOT CONFIGURATION
# =============================================================================
# Control which search engines and AI crawlers can access your site
bots:
# Allowed crawlers (search engines and AI bots)
# These bots will be allowed to crawl your site
allow:
# Traditional search engines
- "Googlebot" # Google Search
- "Bingbot" # Microsoft Bing
- "DuckDuckBot" # DuckDuckGo
# AI-powered search and chat bots
- "GPTBot" # OpenAI ChatGPT
- "PerplexityBot" # Perplexity AI
- "ClaudeBot" # Anthropic Claude
- "Meta-ExternalAgent" # Meta AI
- "Applebot" # Apple Intelligence
- "YouBot" # You.com
# Blocked crawlers (optional)
# Add bots here to prevent them from crawling your site
disallow: []
# Crawl delay in seconds
# How long bots should wait between requests
crawlDelay: 1
# =============================================================================
# SEO TEMPLATES
# =============================================================================
# Templates for generating meta tags and titles
seo:
# Page title template
# Variables: {title}, {siteName}, {pageName}
titleTemplate: "{title} | {siteName}"
# Meta description template
# Variables: {description}, {siteDescription}
descriptionTemplate: "{description}"
# Open Graph image template
# Variables: {siteUrl}, {slug}, {pageName}
imageTemplate: "{siteUrl}/og-images/{slug}.png"
# Twitter card type
# Options: summary, summary_large_image, app, player
twitterCard: "summary_large_image"
# Default Open Graph type
# Options: website, article, book, profile, etc.
ogType: "website"
# Default keywords for meta tags
# Comma-separated list of relevant keywords
defaultKeywords: "keyword1, keyword2, keyword3"
# =============================================================================
# SITEMAP CONFIGURATION
# =============================================================================
# Settings for XML sitemap generation
sitemap:
# Default change frequency for pages
# Options: always, hourly, daily, weekly, monthly, yearly, never
changefreq: "weekly"
# Default priority for pages (0.0 to 1.0)
# Higher values indicate more important pages
priority: 0.8
# Paths to exclude from sitemap
# Use wildcards (*) to exclude entire directories
excludePaths:
- "/admin/*" # Admin pages
- "/api/*" # API endpoints
- "/_next/*" # Next.js internal files
- "/private/*" # Private pages
- "/temp/*" # Temporary files
- "/draft/*" # Draft content
# =============================================================================
# SEARCH ENGINE VERIFICATION
# =============================================================================
# Verification tokens for search engine webmaster tools
# Get these from your search engine webmaster console
verification:
# Google Search Console verification token
# Found in Google Search Console > Settings > Ownership
google: ""
# Bing Webmaster Tools verification token
# Found in Bing Webmaster Tools > Settings
bing: ""
# Yandex Webmaster verification token
# Found in Yandex Webmaster Tools
yandex: ""
# Pinterest verification token
# Found in Pinterest Business > Settings
pinterest: ""
# =============================================================================
# FRAMEWORK-SPECIFIC PATHS
# =============================================================================
# Directory paths for your specific framework
# These are auto-detected but can be customized
paths:
# Source directory (where your components/pages are)
srcDir: "./src"
# Public directory (static assets)
publicDir: "./public"
# Build output directory
buildDir: "./dist"
# Where to output SEO files
outputDir: "./public"
# =============================================================================
# TROUBLESHOOTING
# =============================================================================
# If you encounter Vite plugin errors like "Cannot find module '@vitejs/plugin-react-swc'":
#
# 1. The vibe-seo plugin is self-contained and doesn't require additional dependencies
# 2. Make sure vite-seo-plugin.js exists in your project root
# 3. Check that your Vite version is 4.0 or higher
# 4. If issues persist, you can manually add the plugin to your vite.config.ts:
#
# import seoPlugin from './vite-seo-plugin.js';
#
# export default defineConfig({
# plugins: [seoPlugin()]
# });
#
# For more help, visit: https://github.com/onecf/vibe-seo/issues
`;
return yamlConfig;
}
/**
* Get framework-specific path configurations
*/
function getFrameworkPaths(framework) {
const nextjsPaths = {
pagesDir: './pages',
appDir: './app',
publicDir: './public',
outputDir: './public'
};
const frameworks = {
nextjs: nextjsPaths,
'nextjs-pages': nextjsPaths,
'nextjs-app': nextjsPaths,
react: {
srcDir: './src',
publicDir: './public',
buildDir: './dist', // Vite uses 'dist' by default
outputDir: './public'
},
vue: {
srcDir: './src',
publicDir: './public',
distDir: './dist',
outputDir: './public'
},
angular: {
srcDir: './src',
distDir: './dist',
assetsDir: './src/assets',
outputDir: './src'
},
static: {
srcDir: './src',
publicDir: './public',
outputDir: './public'
}
};
return frameworks[framework] || frameworks.static;
}
module.exports = {
loadConfig,
loadConfigWithFrameworkDetection,
createConfig,
validateConfig,
getDefaultConfig,
getFrameworkPaths
};