feat: implement sitemap generation and search engine pinging in build process

pull/59/head
David Montero Crespo 2026-03-23 18:52:35 -03:00
parent 61b08e7cf2
commit b4365ec877
5 changed files with 128 additions and 35 deletions

View File

@ -56,6 +56,12 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Ping search engines with sitemap
if: success()
run: |
curl -s "https://www.google.com/ping?sitemap=https%3A%2F%2Fvelxio.dev%2Fsitemap.xml" -o /dev/null -w "Google ping: %{http_code}\n"
curl -s "https://www.bing.com/ping?sitemap=https%3A%2F%2Fvelxio.dev%2Fsitemap.xml" -o /dev/null -w "Bing ping: %{http_code}\n"
- name: Update Docker Hub description
uses: peter-evans/dockerhub-description@v4
with:

View File

@ -8,9 +8,10 @@
"generate:metadata": "cd .. && npx tsx scripts/generate-component-metadata.ts",
"generate:favicons": "node ../scripts/generate-favicons.mjs",
"generate:og-image": "node ../scripts/generate-og-image.mjs",
"generate:sitemap": "cd .. && npx tsx scripts/generate-sitemap.ts",
"dev": "npm run generate:metadata && vite",
"build": "npm run generate:metadata && tsc -b && vite build",
"build:docker": "vite build",
"build": "npm run generate:metadata && npm run generate:sitemap && tsc -b && vite build",
"build:docker": "npm run generate:sitemap && vite build",
"lint": "eslint .",
"preview": "vite preview",
"test": "vitest run",

View File

@ -2,192 +2,162 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<!-- ── Main pages ─────────────────────────────────── -->
<url>
<loc>https://velxio.dev/</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
<priority>1</priority>
</url>
<url>
<loc>https://velxio.dev/editor</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://velxio.dev/examples</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<!-- ── Documentation ──────────────────────────────── -->
<url>
<loc>https://velxio.dev/docs</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://velxio.dev/docs/intro</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://velxio.dev/docs/getting-started</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://velxio.dev/docs/emulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://velxio.dev/docs/esp32-emulation</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://velxio.dev/docs/riscv-emulation</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://velxio.dev/docs/rp2040-emulation</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://velxio.dev/docs/raspberry-pi3-emulation</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://velxio.dev/docs/components</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://velxio.dev/docs/architecture</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://velxio.dev/docs/wokwi-libs</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://velxio.dev/docs/mcp</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://velxio.dev/docs/setup</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://velxio.dev/docs/roadmap</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<!-- ── SEO keyword landing pages ──────────────────── -->
<url>
<loc>https://velxio.dev/arduino-simulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://velxio.dev/arduino-emulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://velxio.dev/atmega328p-simulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.85</priority>
</url>
<url>
<loc>https://velxio.dev/arduino-mega-simulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.85</priority>
</url>
<url>
<loc>https://velxio.dev/esp32-simulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://velxio.dev/esp32-s3-simulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.85</priority>
</url>
<url>
<loc>https://velxio.dev/esp32-c3-simulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.85</priority>
</url>
<url>
<loc>https://velxio.dev/raspberry-pi-pico-simulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://velxio.dev/raspberry-pi-simulator</loc>
<lastmod>2026-03-23</lastmod>
<changefreq>monthly</changefreq>
<priority>0.85</priority>
</url>
<!-- ── Release pages ────────────────────────────────── -->
<url>
<loc>https://velxio.dev/v2</loc>
<lastmod>2026-03-23</lastmod>
@ -195,6 +165,4 @@
<priority>0.9</priority>
</url>
<!-- Auth/admin/project pages intentionally excluded (noindex) -->
</urlset>

59
frontend/src/seoRoutes.ts Normal file
View File

@ -0,0 +1,59 @@
/**
* Single source of truth for all public, indexable routes.
* Used by:
* 1. scripts/generate-sitemap.ts builds sitemap.xml at build time
* 2. Any component that needs the canonical URL list
*
* Routes with `noindex: true` are excluded from the sitemap.
*/
export interface SeoRoute {
path: string;
/** 0.0 1.0 (default 0.5) */
priority?: number;
changefreq?: 'daily' | 'weekly' | 'monthly' | 'yearly';
/** If true, excluded from sitemap */
noindex?: boolean;
}
export const SEO_ROUTES: SeoRoute[] = [
// ── Main pages
{ path: '/', priority: 1.0, changefreq: 'weekly' },
{ path: '/editor', priority: 0.9, changefreq: 'weekly' },
{ path: '/examples', priority: 0.8, changefreq: 'weekly' },
// ── Documentation
{ path: '/docs', priority: 0.8, changefreq: 'monthly' },
{ path: '/docs/intro', priority: 0.8, changefreq: 'monthly' },
{ path: '/docs/getting-started', priority: 0.8, changefreq: 'monthly' },
{ path: '/docs/emulator', priority: 0.7, changefreq: 'monthly' },
{ path: '/docs/esp32-emulation', priority: 0.7, changefreq: 'monthly' },
{ path: '/docs/riscv-emulation', priority: 0.7, changefreq: 'monthly' },
{ path: '/docs/rp2040-emulation', priority: 0.7, changefreq: 'monthly' },
{ path: '/docs/raspberry-pi3-emulation', priority: 0.7, changefreq: 'monthly' },
{ path: '/docs/components', priority: 0.7, changefreq: 'monthly' },
{ path: '/docs/architecture', priority: 0.7, changefreq: 'monthly' },
{ path: '/docs/wokwi-libs', priority: 0.7, changefreq: 'monthly' },
{ path: '/docs/mcp', priority: 0.7, changefreq: 'monthly' },
{ path: '/docs/setup', priority: 0.6, changefreq: 'monthly' },
{ path: '/docs/roadmap', priority: 0.6, changefreq: 'monthly' },
// ── SEO keyword landing pages
{ path: '/arduino-simulator', priority: 0.9, changefreq: 'monthly' },
{ path: '/arduino-emulator', priority: 0.9, changefreq: 'monthly' },
{ path: '/atmega328p-simulator', priority: 0.85, changefreq: 'monthly' },
{ path: '/arduino-mega-simulator', priority: 0.85, changefreq: 'monthly' },
{ path: '/esp32-simulator', priority: 0.9, changefreq: 'monthly' },
{ path: '/esp32-s3-simulator', priority: 0.85, changefreq: 'monthly' },
{ path: '/esp32-c3-simulator', priority: 0.85, changefreq: 'monthly' },
{ path: '/raspberry-pi-pico-simulator', priority: 0.9, changefreq: 'monthly' },
{ path: '/raspberry-pi-simulator', priority: 0.85, changefreq: 'monthly' },
// ── Release pages
{ path: '/v2', priority: 0.9, changefreq: 'monthly' },
// ── Auth / admin (noindex)
{ path: '/login', noindex: true },
{ path: '/register', noindex: true },
{ path: '/admin', noindex: true },
];

View File

@ -0,0 +1,59 @@
/**
* Auto-generates frontend/public/sitemap.xml from seoRoutes.ts
* Run: npx tsx scripts/generate-sitemap.ts
* Integrated into: npm run build (via generate:sitemap)
*/
import { writeFileSync } from 'fs';
import { resolve } from 'path';
// Import the single source of truth
import { SEO_ROUTES } from '../frontend/src/seoRoutes';
const DOMAIN = 'https://velxio.dev';
const TODAY = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
const indexableRoutes = SEO_ROUTES.filter((r) => !r.noindex);
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
${indexableRoutes
.map(
(r) => `
<url>
<loc>${DOMAIN}${r.path}</loc>
<lastmod>${TODAY}</lastmod>
<changefreq>${r.changefreq ?? 'monthly'}</changefreq>
<priority>${r.priority ?? 0.5}</priority>
</url>`
)
.join('')}
</urlset>
`;
const outPath = resolve(__dirname, '../frontend/public/sitemap.xml');
writeFileSync(outPath, xml.trimStart(), 'utf-8');
console.log(`sitemap.xml generated → ${indexableRoutes.length} URLs (${TODAY})`);
// Also ping Google & Bing sitemap endpoints (non-blocking, best-effort)
const SITEMAP_URL = `${DOMAIN}/sitemap.xml`;
const PING_URLS = [
`https://www.google.com/ping?sitemap=${encodeURIComponent(SITEMAP_URL)}`,
`https://www.bing.com/ping?sitemap=${encodeURIComponent(SITEMAP_URL)}`,
];
if (process.argv.includes('--ping')) {
console.log('Pinging search engines...');
Promise.allSettled(
PING_URLS.map(async (url) => {
try {
const res = await fetch(url);
console.log(` ${res.ok ? 'OK' : 'FAIL'} ${url.split('?')[0]}`);
} catch (e: any) {
console.log(` FAIL ${url.split('?')[0]}: ${e.message}`);
}
})
);
}