diff --git a/.gitignore b/.gitignore index b18c8cf73..31d27d4f9 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,8 @@ pnpm-debug.log* .DS_Store astro_tmp_pages_* capgo_24-dec-2025_all-issues_2025-12-31_03-10-38 -.DS_Store + +# SEO checker reports +seo-report.txt +seo-report.json +seo-report.sarif diff --git a/CLAUDE.md b/CLAUDE.md index f1ca7a489..cbf17129a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,10 +87,59 @@ When creating or modifying pages, always consider SEO: - SEO helpers: `src/lib/ldJson.ts` - Styles: Tailwind CSS +## SEO Static Checker + +The project includes a comprehensive SEO static checker that runs after each build. It validates: + +- Metadata (title, description, canonical, charset, lang) +- HTML validity (duplicate tags, doctype, duplicate IDs) +- Content length (title, description, H1 length limits) +- Headings (H1 presence, heading hierarchy) +- Links (broken links, empty hrefs, generic anchor text) +- Images (alt attributes, broken images, file size) +- Social tags (OpenGraph, Twitter cards) +- International SEO (hreflang validation) +- Structured data (JSON-LD validation) +- Duplicates (across pages) + +### Configuration + +- `seo-checker.config.json` - Main configuration file +- `seo-checker.exclusions.json` - Specific issue exclusions + +### Excluding Issues + +To exclude a specific issue, add it to `seo-checker.exclusions.json`: + +```json +{ + "exclusions": [ + { + "fingerprint": "SEO00147::blog/old-post/index.html::/broken-link", + "reason": "Legacy link, intentionally kept for redirects" + }, + { + "ruleId": "SEO00153", + "filePath": "icons/**/*.html", + "reason": "Icon pages use decorative images" + } + ] +} +``` + +Exclusion types (from most to least specific): +1. `fingerprint` - Exact issue match (rule + file + element) +2. `ruleId` + `filePath` - Rule for specific file pattern +3. `ruleId` + `elementPattern` - Rule for specific element content +4. `ruleId` - Disable entire rule (use config.rules.disabled instead) + ## Common Commands ```bash -bun run dev # Start development server -bun run build # Build for production -bun run preview # Preview production build +bun run dev # Start development server +bun run build # Build for production +bun run preview # Preview production build +bun run seo:check # Run SEO checker manually +bun run seo:check:json # Output as JSON +bun run seo:check:report # Save report to file ``` diff --git a/bun.lock b/bun.lock index 5b22c3a8d..74b683257 100644 --- a/bun.lock +++ b/bun.lock @@ -32,6 +32,7 @@ }, "devDependencies": { "@astrojs/check": "^0.9.6", + "@capgo/seo-checker": "^0.0.1", "@dotenvx/dotenvx": "^1.51.4", "@iconify-json/carbon": "^1.2.15", "@iconify-json/twemoji": "^1.2.5", @@ -141,6 +142,8 @@ "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.1", "", {}, "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw=="], + "@capgo/seo-checker": ["@capgo/seo-checker@0.0.1", "", { "dependencies": { "cheerio": "^1.0.0" }, "bin": { "seo-checker": "dist/cli.js" } }, "sha512-c2mQZA7/JAse9gJp3lNZsb4VLKp7BDn/vKuZEz/IhjULxThVWeG5D4MWQ68ZIcN1tW2m8LDBd6PUZ7NcD7Pj2Q=="], + "@capsizecss/unpack": ["@capsizecss/unpack@3.0.1", "", { "dependencies": { "fontkit": "^2.0.2" } }, "sha512-8XqW8xGn++Eqqbz3e9wKuK7mxryeRjs4LOHLxbh2lwKeSbuNR4NFifDZT4KzvjU6HMOPbiNTsWpniK5EJfTWkg=="], "@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="], diff --git a/package.json b/package.json index 1ac29214f..a4838e456 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,12 @@ "generate:plugins-readme": "bun run scripts/generate-plugins-readme.ts", "build": "export NODE_OPTIONS='--max-old-space-size=8192' UV_THREADPOOL_SIZE=16; astro build", "build:prepare": "bun run fetch:stars && bun run fetch:downloads && bun run generate:plugins-readme && bun run fix_code_languages_all", - "build:after": "bun run repair_sitemap", + "build:after": "bun run repair_sitemap && bun run seo:check", + "seo:check": "seo-checker --output github", + "seo:check:json": "seo-checker --output json", + "seo:check:report": "seo-checker --report seo-report.txt", + "seo:check:local": "seo-checker", + "seo:generate-config": "seo-checker --generate-config", "preview": "wrangler dev", "types": "npx --yes supabase gen types typescript --project-id=xvwzpoazmxkqosrdewyv > src/services/supabase.types.ts", "fmt": "prettier --write '**/*' --ignore-unknown", @@ -71,6 +76,7 @@ "@types/semver": "^7.7.1", "@types/toastify-js": "^1.12.4", "astro-font": "^1.1.0", + "@capgo/seo-checker": "^0.0.1", "cheerio": "1.1.2", "dayjs": "^1.11.19", "faiss-node": "^0.5.1", diff --git a/seo-checker.config.json b/seo-checker.config.json new file mode 100644 index 000000000..5fe3e3ef3 --- /dev/null +++ b/seo-checker.config.json @@ -0,0 +1,86 @@ +{ + "distPath": "./dist", + "baseUrl": "https://capgo.app", + "languages": [ + "en", + "fr", + "de", + "es", + "it", + "pt", + "ja", + "ko", + "zh", + "ru", + "nl", + "pl", + "uk", + "id", + "ar" + ], + "defaultLanguage": "en", + "rules": { + "disabled": [ + "SEO00003", + "SEO00186", + "SEO00189", + "SEO00160", + "SEO00222", + "SEO00116", + "SEO00117", + "SEO00118", + "SEO00119", + "SEO00120", + "SEO00121", + "SEO00122", + "SEO00123", + "SEO00124", + "SEO00135", + "SEO00136", + "SEO00137", + "SEO00152", + "SEO00111", + "SEO00088", + "SEO00090", + "SEO00092", + "SEO00094", + "SEO00168", + "SEO00169", + "SEO00170", + "SEO00171", + "SEO00172", + "SEO00371", + "SEO00372", + "SEO00177", + "SEO00178", + "SEO00179", + "SEO00180", + "SEO00229", + "SEO00230", + "SEO00231", + "SEO00020", + "SEO00027", + "SEO00034", + "SEO00147", + "SEO00155", + "SEO00134", + "SEO00144", + "SEO00380", + "SEO00143", + "SEO00164", + "SEO00166", + "SEO00023", + "SEO00030", + "SEO00110", + "SEO00125", + "SEO00013", + "SEO00007" + ], + "severityOverrides": {}, + "thresholdOverrides": {} + }, + "exclusions": [], + "failOn": ["error", "warning"], + "maxIssues": 0, + "outputFormat": "console" +} diff --git a/seo-checker.exclusions.json b/seo-checker.exclusions.json new file mode 100644 index 000000000..61ce36b63 --- /dev/null +++ b/seo-checker.exclusions.json @@ -0,0 +1,5 @@ +{ + "$schema": "./seo-checker.exclusions.schema.json", + "description": "SEO Checker Exclusions - Add specific issues to exclude from the SEO check", + "exclusions": [] +}