diff --git a/astro.config.mjs b/astro.config.mjs
index d5d014e..2317744 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -1,6 +1,5 @@
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
-import sitemap from '@astrojs/sitemap';
// Salida estática pura: HTML/JSON servidos por Caddy, sin proceso Node en
// producción. Los datos se consultan a Postgres una única vez, en build-time
@@ -8,7 +7,7 @@ import sitemap from '@astrojs/sitemap';
export default defineConfig({
output: 'static',
site: 'https://www.eivissaenvironmentalwatch.es',
- integrations: [preact(), sitemap()],
+ integrations: [preact()],
build: {
inlineStylesheets: 'auto', // inline CSS pequeños, externos los grandes (ej. Leaflet)
},
diff --git a/package.json b/package.json
index 6918149..e8d4e2c 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,6 @@
"dependencies": {
"@astrojs/preact": "^6.0.1",
"@astrojs/rss": "^4.0.19",
- "@astrojs/sitemap": "^3.7.3",
"astro": "^7.1.3",
"leaflet": "^1.9.4",
"pg": "^8.13.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ecb393f..5e266bb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,9 +14,6 @@ importers:
'@astrojs/rss':
specifier: ^4.0.19
version: 4.0.19
- '@astrojs/sitemap':
- specifier: ^3.7.3
- version: 3.7.3
astro:
specifier: ^7.1.3
version: 7.1.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.1)(rollup@4.62.2)(tsx@4.23.1)(yaml@2.9.0)
@@ -180,9 +177,6 @@ packages:
'@astrojs/rss@4.0.19':
resolution: {integrity: sha512-e+z5wYeYtffQdHQO8c2tkSd2JEBdAuRXJV4ZEU5IxkYeE6e39woDd7nw1PH1Kk2tEYNCYuKdylnnbhGmt61awA==}
- '@astrojs/sitemap@3.7.3':
- resolution: {integrity: sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==}
-
'@astrojs/telemetry@3.3.3':
resolution: {integrity: sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==}
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
@@ -1293,18 +1287,12 @@ packages:
'@types/nlcst@2.0.3':
resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==}
- '@types/node@24.13.3':
- resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==}
-
'@types/node@26.1.1':
resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==}
'@types/pg@8.20.0':
resolution: {integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==}
- '@types/sax@1.2.7':
- resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
-
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@@ -1444,9 +1432,6 @@ packages:
anynum@1.0.1:
resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==}
- arg@5.0.2:
- resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
-
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -2824,11 +2809,6 @@ packages:
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
- sitemap@9.0.1:
- resolution: {integrity: sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==}
- engines: {node: '>=20.19.5', npm: '>=10.8.2'}
- hasBin: true
-
smol-toml@1.7.0:
resolution: {integrity: sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==}
engines: {node: '>= 18'}
@@ -2856,9 +2836,6 @@ packages:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
- stream-replace-string@2.0.0:
- resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==}
-
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -2988,9 +2965,6 @@ packages:
uncrypto@0.1.3:
resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
- undici-types@7.18.2:
- resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
-
undici-types@8.3.0:
resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==}
@@ -3498,12 +3472,6 @@ snapshots:
piccolore: 0.1.3
zod: 4.4.3
- '@astrojs/sitemap@3.7.3':
- dependencies:
- sitemap: 9.0.1
- stream-replace-string: 2.0.0
- zod: 4.4.3
-
'@astrojs/telemetry@3.3.3':
dependencies:
ci-info: 4.4.0
@@ -4385,10 +4353,6 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
- '@types/node@24.13.3':
- dependencies:
- undici-types: 7.18.2
-
'@types/node@26.1.1':
dependencies:
undici-types: 8.3.0
@@ -4399,10 +4363,6 @@ snapshots:
pg-protocol: 1.15.0
pg-types: 2.2.0
- '@types/sax@1.2.7':
- dependencies:
- '@types/node': 26.1.1
-
'@types/unist@3.0.3': {}
'@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)':
@@ -4593,8 +4553,6 @@ snapshots:
anynum@1.0.1: {}
- arg@5.0.2: {}
-
argparse@2.0.1: {}
aria-query@5.3.2: {}
@@ -6289,13 +6247,6 @@ snapshots:
sisteransi@1.0.5: {}
- sitemap@9.0.1:
- dependencies:
- '@types/node': 24.13.3
- '@types/sax': 1.2.7
- arg: 5.0.2
- sax: 1.6.0
-
smol-toml@1.7.0: {}
source-map-js@1.2.1: {}
@@ -6313,8 +6264,6 @@ snapshots:
es-errors: 1.3.0
internal-slot: 1.1.0
- stream-replace-string@2.0.0: {}
-
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@@ -6477,8 +6426,6 @@ snapshots:
uncrypto@0.1.3: {}
- undici-types@7.18.2: {}
-
undici-types@8.3.0: {}
unified@11.0.5:
diff --git a/src/pages/sitemap-0.xml.ts b/src/pages/sitemap-0.xml.ts
new file mode 100644
index 0000000..127efa4
--- /dev/null
+++ b/src/pages/sitemap-0.xml.ts
@@ -0,0 +1,55 @@
+import type { APIContext } from 'astro';
+import { getAvisosAmbientales } from '../lib/db';
+
+const escapeXml = (s: string): string =>
+ s
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+
+const STATIC_PATHS = ['/', '/huella/'] as const;
+
+export async function GET(context: APIContext) {
+ const site = (context.site ?? new URL(context.url.origin)).toString().replace(/\/$/, '');
+ const avisos = await getAvisosAmbientales();
+
+ const staticUrls = STATIC_PATHS.map(
+ (p) => `
+ ${site}${p}
+ `
+ ).join('\n');
+
+ const avisoUrls = avisos
+ .map((aviso) => {
+ const path = `/avis/${aviso.origen}-${aviso.id}/`;
+ const title = escapeXml(aviso.titulo);
+ const keywords = escapeXml(aviso.temas.join(', '));
+ const pubDate = new Date(aviso.fecha).toISOString();
+ return `
+ ${site}${path}
+
+
+ ${escapeXml(aviso.fuente)}
+ ca
+
+ ${pubDate}
+ ${title}
+ ${keywords}
+
+ `;
+ })
+ .join('\n');
+
+ const body = `
+
+${staticUrls}
+${avisoUrls}
+
+`;
+
+ return new Response(body, {
+ headers: { 'Content-Type': 'application/xml; charset=utf-8' },
+ });
+}
diff --git a/src/pages/sitemap-index.xml.ts b/src/pages/sitemap-index.xml.ts
new file mode 100644
index 0000000..c04c1e0
--- /dev/null
+++ b/src/pages/sitemap-index.xml.ts
@@ -0,0 +1,15 @@
+import type { APIContext } from 'astro';
+
+export function GET(context: APIContext) {
+ const site = (context.site ?? new URL(context.url.origin)).toString().replace(/\/$/, '');
+ const body = `
+
+
+ ${site}/sitemap-0.xml
+
+
+`;
+ return new Response(body, {
+ headers: { 'Content-Type': 'application/xml; charset=utf-8' },
+ });
+}
diff --git a/tests/e2e/site.spec.ts b/tests/e2e/site.spec.ts
index 8312a7e..e29920d 100644
--- a/tests/e2e/site.spec.ts
+++ b/tests/e2e/site.spec.ts
@@ -129,6 +129,16 @@ test.describe('site smoke', () => {
expect(r.status(), p).toBe(200);
}
});
+
+ test('news sitemap declares Google News namespace and per-aviso news block', async ({ request }) => {
+ const res = await request.get('/sitemap-0.xml');
+ const sitemap = await res.text();
+ expect(sitemap).toContain('xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"');
+ const newsBlocks = sitemap.match(//g) ?? [];
+ expect(newsBlocks.length).toBeGreaterThan(0);
+ expect(sitemap).toMatch(/[^<]+<\/news:publication_date>/);
+ expect(sitemap).toMatch(/ca<\/news:language>/);
+ });
});
test.describe('build artifacts', () => {