Tell search engines and link previews what each page is
- /sitemap.xml, from the API's /slugs plus the landing and listings: every address in every language, with hreflang and x-default. A 503 when the API is down, rather than a sitemap missing half the site. - /robots.txt, naming the sitemap on whatever domain this is served on. - Slugs renamed in the panel now redirect. The API has always kept the old address the moment one changed; middleware/redirects.ts answers it with a 301, from a list kept for a minute and through outages. - og:url, og:image (the record's own picture, the logo otherwise) and schema.org JSON-LD: Organization on the landing; WebPage, CreativeWork, Person or Service elsewhere. Shared through core/head. - The proxy-derived origin moves to core/origin.ts, used by the page shell, the landing and both server routes. In development only /api/* reaches Nitro, so the two files 404 under `npm start`; they were checked against a production build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
49
landing/src/app/core/sitemap.spec.ts
Normal file
49
landing/src/app/core/sitemap.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { buildSitemap } from './sitemap';
|
||||
|
||||
const xml = buildSitemap('https://pireon.dev', {
|
||||
pages: [
|
||||
{
|
||||
id: 1,
|
||||
updated_at: '2026-09-27T20:36:14+00:00',
|
||||
alternates: {
|
||||
es: '/sobre-nosotros',
|
||||
en: '/en/about-us',
|
||||
ca: '/ca/qui-som',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
describe('buildSitemap', () => {
|
||||
it('lists every language of a record as its own url', () => {
|
||||
for (const path of ['/sobre-nosotros', '/en/about-us', '/ca/qui-som']) {
|
||||
expect(xml).toContain(`<loc>https://pireon.dev${path}</loc>`);
|
||||
}
|
||||
});
|
||||
|
||||
it('states every language on each url, with the default as x-default', () => {
|
||||
expect(xml).toContain(
|
||||
'hreflang="en" href="https://pireon.dev/en/about-us"',
|
||||
);
|
||||
expect(xml).toContain(
|
||||
'hreflang="x-default" href="https://pireon.dev/sobre-nosotros"',
|
||||
);
|
||||
expect(xml).toContain('<lastmod>2026-09-27T20:36:14+00:00</lastmod>');
|
||||
});
|
||||
|
||||
it('includes the landing and the listings this site composes', () => {
|
||||
expect(xml).toContain('<loc>https://pireon.dev/</loc>');
|
||||
// `&` escaped, as XML requires.
|
||||
expect(xml).toContain('<loc>https://pireon.dev/?lang=en</loc>');
|
||||
expect(xml).toContain('<loc>https://pireon.dev/radar</loc>');
|
||||
expect(xml).toContain('<loc>https://pireon.dev/ca/col-labora</loc>');
|
||||
});
|
||||
|
||||
it('is a well-formed document', () => {
|
||||
const document = new DOMParser().parseFromString(xml, 'application/xml');
|
||||
|
||||
expect(document.getElementsByTagName('parsererror')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user