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:
@@ -1,13 +1,14 @@
|
|||||||
import { defineConfig } from 'astro/config'
|
import { defineConfig } from 'astro/config'
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
import react from '@astrojs/react'
|
import react from '@astrojs/react'
|
||||||
|
import sitemap from '@astrojs/sitemap'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: 'https://dealplustech.com',
|
site: 'https://dealplustech.co.th',
|
||||||
output: 'static',
|
output: 'static',
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [tailwindcss()],
|
plugins: [tailwindcss()],
|
||||||
@@ -22,6 +23,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
integrations: [
|
integrations: [
|
||||||
react(),
|
react(),
|
||||||
|
sitemap(),
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
assets: '_assets',
|
assets: '_assets',
|
||||||
|
|||||||
77
package-lock.json
generated
77
package-lock.json
generated
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "dealplustech-emdash",
|
"name": "dealplustech-astroreal",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "dealplustech-emdash",
|
"name": "dealplustech-astroreal",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.4",
|
||||||
"@astrojs/react": "^5.0.5",
|
"@astrojs/react": "^5.0.5",
|
||||||
|
"@astrojs/sitemap": "^3.7.3",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"astro": "^6.1.7",
|
"astro": "^6.1.7",
|
||||||
@@ -160,6 +161,17 @@
|
|||||||
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0"
|
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@astrojs/sitemap": {
|
||||||
|
"version": "3.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.3.tgz",
|
||||||
|
"integrity": "sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sitemap": "^9.0.0",
|
||||||
|
"stream-replace-string": "^2.0.0",
|
||||||
|
"zod": "^4.3.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@astrojs/telemetry": {
|
"node_modules/@astrojs/telemetry": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.2.tgz",
|
||||||
@@ -2329,8 +2341,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
||||||
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": ">=7.24.0 <7.24.7"
|
"undici-types": ">=7.24.0 <7.24.7"
|
||||||
}
|
}
|
||||||
@@ -2355,6 +2365,15 @@
|
|||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/sax": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/unist": {
|
"node_modules/@types/unist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||||
@@ -2562,6 +2581,12 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/arg": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
@@ -5748,6 +5773,40 @@
|
|||||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/sitemap": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^24.9.2",
|
||||||
|
"@types/sax": "^1.2.1",
|
||||||
|
"arg": "^5.0.0",
|
||||||
|
"sax": "^1.4.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sitemap": "dist/esm/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.5",
|
||||||
|
"npm": ">=10.8.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sitemap/node_modules/@types/node": {
|
||||||
|
"version": "24.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
|
||||||
|
"integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sitemap/node_modules/undici-types": {
|
||||||
|
"version": "7.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||||
|
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/smol-toml": {
|
"node_modules/smol-toml": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
|
||||||
@@ -5779,6 +5838,12 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stream-replace-string": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
@@ -5980,9 +6045,7 @@
|
|||||||
"version": "7.24.6",
|
"version": "7.24.6",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
||||||
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/unified": {
|
"node_modules/unified": {
|
||||||
"version": "11.0.5",
|
"version": "11.0.5",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.4",
|
||||||
"@astrojs/react": "^5.0.5",
|
"@astrojs/react": "^5.0.5",
|
||||||
|
"@astrojs/sitemap": "^3.7.3",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"astro": "^6.1.7",
|
"astro": "^6.1.7",
|
||||||
|
|||||||
@@ -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, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, ''');
|
|
||||||
}
|
|
||||||
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user