Use @astrojs/sitemap instead of manual sitemap.xml.ts

- astro.config.mjs: site=https://dealplustech.co.th, add sitemap integration
- Remove hardcoded sitemap.xml.ts (now auto-generated)
This commit is contained in:
Kunthawat Greethong
2026-06-16 09:48:38 +07:00
parent 5c1d3c7ef9
commit 85a0a54eb4
4 changed files with 74 additions and 161 deletions

View File

@@ -1,153 +0,0 @@
/**
* /sitemap.xml.ts — Custom sitemap with per-type priority/changefreq.
*
* Generates a sitemap by:
* 1. Listing all static .astro pages (excluding 404 + dynamic [slug])
* 2. Walking content collections (blog) for dynamic routes
* 3. Classifying each URL into a category to assign priority + changefreq
*
* Per Astro 6, the file lives in src/pages/ and exports a GET handler.
*/
import type { APIRoute } from 'astro';
import { getCollection } from 'astro:content';
const SITE = 'https://dealplustech.co.th';
// ---------------------------------------------------------------------------
// URL classification
// ---------------------------------------------------------------------------
const NOINDEX_PAGES = new Set([
'privacy-policy',
'terms-and-conditions',
'404',
]);
const PRODUCT_SLUGS = new Set([
'aeroflex', 'armflex', 'durgo-avvs', 'grilles', 'maxflex',
'pipe-coupling', 'realflex', 'water-pump', 'water-treatment',
'ตู้ดับเพลิง',
'ท่อ-hdpe', 'ท่อ-ppr-scg', 'ท่อ-ppr-thai-ppr', 'ท่อ-syler',
'ท่อ-upvc', 'ท่อ-xy-lent',
'ระบบรั้วไวน์แมน', 'รั้วเทวดา',
'วาล์ว-valve', 'หัวจ่าย-ball-jet',
'เครื่องเชื่อม-hdpe', 'เครื่องเชื่อม-ppr',
'เทอร์โมเบรค-thermobreak', 'เม็กกรู๊ฟ-คับปลิ้ง',
]);
const HOMEPAGE_SLUGS = new Set(['index', 'all-products']);
const CATEGORY_SLUGS = new Set(['ระบบน้ำ']);
const STATIC_SLUGS = new Set(['about-us', 'contact-us', 'portfolio']);
type Entry = {
loc: string;
priority: number;
changefreq: 'daily' | 'weekly' | 'monthly' | 'yearly';
lastmod?: string;
};
function classify(slug: string): { priority: number; changefreq: Entry['changefreq'] } {
if (slug === 'index') return { priority: 1.0, changefreq: 'weekly' };
if (slug === 'all-products') return { priority: 0.9, changefreq: 'weekly' };
if (PRODUCT_SLUGS.has(slug)) return { priority: 0.9, changefreq: 'monthly' };
if (CATEGORY_SLUGS.has(slug)) return { priority: 0.8, changefreq: 'monthly' };
if (STATIC_SLUGS.has(slug)) return { priority: 0.6, changefreq: 'yearly' };
return { priority: 0.5, changefreq: 'monthly' };
}
function urlFromSlug(slug: string): string {
if (slug === 'index') return `${SITE}/`;
return `${SITE}/${slug}`;
}
// ---------------------------------------------------------------------------
// Build entries
// ---------------------------------------------------------------------------
async function buildEntries(): Promise<Entry[]> {
// 1. Static pages — discovered via Vite's import.meta.glob
const modules = import.meta.glob<true>('./*.astro');
const pageFiles = Object.keys(modules)
.map(p => p.replace(/^\.\//, '').replace(/\.astro$/, ''))
.filter(slug => !slug.startsWith('[') && !NOINDEX_PAGES.has(slug));
const entries: Entry[] = pageFiles.map(slug => {
const { priority, changefreq } = classify(slug);
return { loc: urlFromSlug(slug), priority, changefreq };
});
// 2. Blog posts (dynamic [slug] route)
const articles = await getCollection('blog');
for (const article of articles) {
entries.push({
loc: `${SITE}/${encodeURI('บทความ')}/${encodeURIComponent(article.id)}`,
priority: 0.7,
changefreq: 'yearly',
lastmod: article.data.published_at.toISOString(),
});
}
// 3. Blog index
entries.push({
loc: `${SITE}/${encodeURI('บทความ')}`,
priority: 0.7,
changefreq: 'weekly',
});
// Sort: homepage first, then by priority desc, then by URL asc
entries.sort((a, b) => {
if (a.loc === `${SITE}/`) return -1;
if (b.loc === `${SITE}/`) return 1;
if (a.priority !== b.priority) return b.priority - a.priority;
return a.loc.localeCompare(b.loc);
});
return entries;
}
// ---------------------------------------------------------------------------
// XML serialiser
// ---------------------------------------------------------------------------
function escapeXml(s: string): string {
return s
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
}
function toXml(entries: Entry[]): string {
const urls = entries.map(e => {
const lastmod = e.lastmod ? `\n <lastmod>${e.lastmod}</lastmod>` : '';
return ` <url>
<loc>${escapeXml(e.loc)}</loc>${lastmod}
<changefreq>${e.changefreq}</changefreq>
<priority>${e.priority.toFixed(1)}</priority>
</url>`;
}).join('\n');
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>
`;
}
// ---------------------------------------------------------------------------
// Handler
// ---------------------------------------------------------------------------
export const GET: APIRoute = async () => {
const entries = await buildEntries();
const xml = toXml(entries);
return new Response(xml, {
status: 200,
headers: {
'Content-Type': 'application/xml; charset=utf-8',
'Cache-Control': 'public, max-age=3600',
},
});
};