From c3145580846d1a2cf2da048c3d105e63d825eb33 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Tue, 7 Oct 2025 17:34:14 +0200 Subject: [PATCH 1/2] feat(frontend): prerender canonical URL for subpages Signed-off-by: David Dal Busco --- frontend/package.json | 3 ++- frontend/scripts/build.seo.mjs | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 frontend/scripts/build.seo.mjs diff --git a/frontend/package.json b/frontend/package.json index ab30bee186d..cbb5e8960db 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,8 +5,9 @@ "license": "SEE LICENSE IN LICENSE.md", "scripts": { "build:csp": "node scripts/build.csp.mjs", + "build:seo": "node scripts/build.seo.mjs", "build:preload": "node scripts/build.preload.mjs", - "build:post-process": "npm run build:csp", + "build:post-process": "npm run build:seo && npm run build:csp", "i18n": "node scripts/i18n.types.js && prettier --write ./src/lib/types/i18n.d.ts", "dev": "npm run i18n && vite dev", "build": "npm run i18n && tsc --noEmit && vite build && npm run build:post-process", diff --git a/frontend/scripts/build.seo.mjs b/frontend/scripts/build.seo.mjs new file mode 100644 index 00000000000..e376b8e8c24 --- /dev/null +++ b/frontend/scripts/build.seo.mjs @@ -0,0 +1,38 @@ +import { readFileSync, writeFileSync } from "node:fs"; +import { dirname, join, relative } from "node:path"; +import { findHtmlFiles } from "./build.utils.mjs"; + +const OUTPUT_DIR = join(process.cwd(), "public"); +const SITE_ROOT_CANONICAL = "https://nns.ic0.app/"; + +const updateCanonical = (htmlFilePath) => { + // 1. We determine the route based on the output + const routePath = dirname(relative(OUTPUT_DIR, htmlFilePath)); + + // 2. Build the effective canonical route + const canonicalPath = `${SITE_ROOT_CANONICAL}${routePath}/`; + + // 2. Read content + let html = readFileSync(htmlFilePath, "utf-8"); + + // 3. Update canonical + html = html.replace( + ``, + `` + ); + + // 4. Update og:url to reflect the canonical + html = html.replace( + ``, + `` + ); + + // 5. Save the content with the updated canonical URL + writeFileSync(htmlFilePath, html); +}; + +// Do not replace canonical for root and 404 pages +const filterSubPages = (htmlFile) => dirname(htmlFile) !== OUTPUT_DIR; + +const htmlFiles = findHtmlFiles().filter(filterSubPages); +htmlFiles.forEach((htmlFile) => updateCanonical(htmlFile)); From 60706d399e919011ba5473e81107d557fc792914 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Tue, 7 Oct 2025 20:50:39 +0200 Subject: [PATCH 2/2] Update frontend/scripts/build.seo.mjs Co-authored-by: Yusef Habib --- frontend/scripts/build.seo.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/scripts/build.seo.mjs b/frontend/scripts/build.seo.mjs index e376b8e8c24..657a17df747 100644 --- a/frontend/scripts/build.seo.mjs +++ b/frontend/scripts/build.seo.mjs @@ -35,4 +35,4 @@ const updateCanonical = (htmlFilePath) => { const filterSubPages = (htmlFile) => dirname(htmlFile) !== OUTPUT_DIR; const htmlFiles = findHtmlFiles().filter(filterSubPages); -htmlFiles.forEach((htmlFile) => updateCanonical(htmlFile)); +htmlFiles.forEach(updateCanonical);