Skip to content

Commit 3703a49

Browse files
feat: validate equal page names for posts and data
1 parent 7474e6c commit 3703a49

File tree

4 files changed

+68
-22
lines changed

4 files changed

+68
-22
lines changed

astro.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import tailwindcss from '@tailwindcss/vite';
33
import pagefind from 'astro-pagefind';
44
import icon from 'astro-icon';
55
import cookieconsent from '@jop-software/astro-cookieconsent';
6+
import equalPageNameValidator from './src/integrations/equal-page-name-validator';
67

78
// https://astro.build/config
89
export default defineConfig({
@@ -14,6 +15,8 @@ export default defineConfig({
1415
},
1516

1617
integrations: [
18+
equalPageNameValidator('posts/', 'en/posts/'),
19+
equalPageNameValidator('data/', 'en/data/'),
1720
icon({
1821
iconDir: './src/images/icons',
1922
}),

src/i18n/utils.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {getRelativeLocaleUrl} from 'astro:i18n';
22
import {translations, defaultLang, pages} from './translations';
3-
import {redirectSearchParam} from '../pages/404.astro';
43

54
// export function getLangFromUrl(url: URL) {
65
// const [, lang] = url.pathname.split('/');
@@ -61,7 +60,7 @@ export function pageInOtherLocale(lang: LocaleString, currentPath: string) {
6160
if (path.startsWith('/en/')) {
6261
path = path.substring(4); // remove /en/
6362
}
64-
return getRelativeLocaleUrl(lang, path) + '?' + redirectSearchParam;
63+
return getRelativeLocaleUrl(lang, path);
6564
}
6665

6766
function localePagePrefix(lang: LocaleString): string {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type {AstroIntegration} from 'astro';
2+
3+
/**
4+
* Validates that for every page under pathA there is a corresponding page under pathB and vice versa.
5+
*/
6+
export default function equalPageNameValidator(pathA: string, pathB: string): AstroIntegration {
7+
if (!pathA || !pathB) {
8+
throw new Error('Both paths must be provided');
9+
}
10+
if (pathA === pathB) {
11+
throw new Error('The two paths must be different');
12+
}
13+
14+
// Canonicalize paths to always end with a slash
15+
if (!pathA.endsWith('/')) {
16+
pathA += '/';
17+
}
18+
if (!pathB.endsWith('/')) {
19+
pathB += '/';
20+
}
21+
22+
return {
23+
name: 'equal-page-name-validator',
24+
hooks: {
25+
'astro:build:done': ({logger, dir, pages}) => {
26+
logger.info(`Validating equal page names between ${pathA} and ${pathB}…`);
27+
28+
const aPages = new Set<string>();
29+
const bPages = new Set<string>();
30+
31+
for (const page of pages) {
32+
if (page.pathname.startsWith(pathA)) {
33+
aPages.add(page.pathname.substring(pathA.length));
34+
} else if (page.pathname.startsWith(pathB)) {
35+
bPages.add(page.pathname.substring(pathB.length));
36+
}
37+
}
38+
39+
if (aPages.size === 0) {
40+
logger.warn(`No pages found for path A: ${pathA}`);
41+
}
42+
if (bPages.size === 0) {
43+
logger.warn(`No pages found for path B: ${pathB}`);
44+
}
45+
46+
const difference = aPages.difference(bPages);
47+
const reverseDifference = bPages.difference(aPages);
48+
const totalDifferences = difference.size + reverseDifference.size;
49+
if (totalDifferences > 0) {
50+
logger.error(`Found ${totalDifferences} page(s) that do not have a counterpart:`);
51+
for (const page of difference) {
52+
logger.error(` - ${pathA}${page}`);
53+
}
54+
for (const page of reverseDifference) {
55+
logger.error(` - ${pathB}${page}`);
56+
}
57+
throw new Error(`Validation failed: ${totalDifferences} page(s) do not have a counterpart.`);
58+
}
59+
60+
logger.info(`All ${aPages.size} page(s) in ${pathA} have a counterpart in ${pathB} and vice versa.`);
61+
},
62+
},
63+
};
64+
}

src/pages/404.astro

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import {Icon} from 'astro-icon/components';
88
const currentLang = currentLanguage(Astro.currentLocale);
99
const t = useTranslations(currentLang);
1010
const homeLink = getRelativeLocaleUrl(currentLang, '/');
11-
12-
export const redirectSearchParam = 'redirect-if-not-found';
1311
---
1412

1513
<Layout title={t('404.title')} description={t('404.description')}>
@@ -19,21 +17,3 @@ export const redirectSearchParam = 'redirect-if-not-found';
1917
<Icon name="404" class="text-main-lightgreen m-auto text-[25vw]" />
2018
</Section>
2119
</Layout>
22-
23-
<redirect-optionally data-home-link={homeLink} data-search-param={redirectSearchParam}></redirect-optionally>
24-
25-
<script>
26-
class Redirector extends HTMLElement {
27-
connectedCallback() {
28-
const searchParams = new URLSearchParams(window.location.search);
29-
const homeLink = this.dataset.homeLink;
30-
const redirectSearchParam = this.dataset.searchParam ?? '';
31-
32-
if (homeLink && searchParams.has(redirectSearchParam)) {
33-
window.location.href = homeLink;
34-
}
35-
}
36-
}
37-
38-
customElements.define('redirect-optionally', Redirector);
39-
</script>

0 commit comments

Comments
 (0)