feat: implement sitemap generation and search engine pinging in build process
parent
61b08e7cf2
commit
b4365ec877
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
];
|
||||
|
|
@ -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}`);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue