Files
worldmonitor/pro-test/prerender.mjs
Elie Habib 4306322b67 fix(seo): comprehensive SEO improvements for /pro and main pages (#1271)
Pro page (/pro):
- Shorten title to ≤60 chars for SERP visibility
- Fix canonical + all URLs to www.worldmonitor.app
- Fix multiple H1 (Enterprise modal H1→H2)
- Fix heading hierarchy (H2→H4 jumps → proper H2→H3→H4)
- Sync FAQPage JSON-LD with all 8 visible FAQs
- Add twitter:title, twitter:description, og:locale, og:image:alt
- Add hreflang tags for all 21 supported languages
- Add <noscript> fallback with key SEO content
- Add SSG prerender script (injects text into built HTML)
- Self-host WIRED logo SVG (was loading from Wikipedia)
- Add aria-labels on footer links and CTAs
- Fix i18n to read ?lang= query parameter (was only localStorage + navigator)

Main page:
- Fix canonical + OG/Twitter URLs to www.worldmonitor.app
- Update twitter:site/creator to @worldmonitorai
- Add <noscript> with H1, description, features, and /pro link
- Add hreflang tags for all 21 languages
- Add og:image:alt meta tag
- Add @worldmonitorai to JSON-LD sameAs
- Align title with variant-meta.ts

Shared:
- Update sitemap.xml URLs to www.worldmonitor.app
- Update robots.txt sitemap reference to www
- Update variant-meta.ts full variant URL to www
2026-03-08 14:46:20 +04:00

90 lines
3.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Postbuild prerender script — injects critical SEO content into the built HTML
* so search engines see real content without executing JavaScript.
*
* This is a lightweight SSG alternative: it embeds key text content
* (headings, descriptions, FAQ answers) directly into the HTML body
* as a hidden div that gets replaced when React hydrates.
*/
import { readFileSync, writeFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const htmlPath = resolve(__dirname, '../public/pro/index.html');
const en = JSON.parse(readFileSync(resolve(__dirname, 'src/locales/en.json'), 'utf-8'));
const seoContent = `
<div id="seo-prerender" style="position:absolute;left:-9999px;top:-9999px;overflow:hidden;width:1px;height:1px;">
<h1>${en.hero.title1} ${en.hero.title2}</h1>
<p>${en.hero.subtitle}</p>
<p>${en.hero.missionLine}</p>
<h2>Plans</h2>
<h3>${en.twoPath.proTitle}</h3>
<p>${en.twoPath.proDesc}</p>
<p>${en.twoPath.proF1}</p>
<p>${en.twoPath.proF2}</p>
<p>${en.twoPath.proF3}</p>
<p>${en.twoPath.proF4}</p>
<h3>${en.twoPath.entTitle}</h3>
<p>${en.twoPath.entDesc}</p>
<h2>${en.whyUpgrade.title}</h2>
<h3>${en.whyUpgrade.noiseTitle}</h3><p>${en.whyUpgrade.noiseDesc}</p>
<h3>${en.whyUpgrade.fasterTitle}</h3><p>${en.whyUpgrade.fasterDesc}</p>
<h3>${en.whyUpgrade.controlTitle}</h3><p>${en.whyUpgrade.controlDesc}</p>
<h3>${en.whyUpgrade.deeperTitle}</h3><p>${en.whyUpgrade.deeperDesc}</p>
<h2>${en.proShowcase.title}</h2>
<p>${en.proShowcase.subtitle}</p>
<h3>${en.proShowcase.equityResearch}</h3><p>${en.proShowcase.equityResearchDesc}</p>
<h3>${en.proShowcase.geopoliticalAnalysis}</h3><p>${en.proShowcase.geopoliticalAnalysisDesc}</p>
<h3>${en.proShowcase.economyAnalytics}</h3><p>${en.proShowcase.economyAnalyticsDesc}</p>
<h3>${en.proShowcase.riskMonitoring}</h3><p>${en.proShowcase.riskMonitoringDesc}</p>
<h3>${en.proShowcase.morningBriefs}</h3><p>${en.proShowcase.morningBriefsDesc}</p>
<h3>${en.proShowcase.oneKey}</h3><p>${en.proShowcase.oneKeyDesc}</p>
<h2>${en.audience.title}</h2>
<h3>${en.audience.investorsTitle}</h3><p>${en.audience.investorsDesc}</p>
<h3>${en.audience.tradersTitle}</h3><p>${en.audience.tradersDesc}</p>
<h3>${en.audience.researchersTitle}</h3><p>${en.audience.researchersDesc}</p>
<h3>${en.audience.journalistsTitle}</h3><p>${en.audience.journalistsDesc}</p>
<h3>${en.audience.govTitle}</h3><p>${en.audience.govDesc}</p>
<h3>${en.audience.teamsTitle}</h3><p>${en.audience.teamsDesc}</p>
<h2>${en.dataCoverage.title}</h2>
<p>${en.dataCoverage.subtitle}</p>
<h2>${en.apiSection.title}</h2>
<p>${en.apiSection.subtitle}</p>
<h2>${en.enterpriseShowcase.title}</h2>
<p>${en.enterpriseShowcase.subtitle}</p>
<h2>${en.pricingTable.title}</h2>
<h2>${en.faq.title}</h2>
<dl>
<dt>${en.faq.q1}</dt><dd>${en.faq.a1}</dd>
<dt>${en.faq.q2}</dt><dd>${en.faq.a2}</dd>
<dt>${en.faq.q3}</dt><dd>${en.faq.a3}</dd>
<dt>${en.faq.q4}</dt><dd>${en.faq.a4}</dd>
<dt>${en.faq.q5}</dt><dd>${en.faq.a5}</dd>
<dt>${en.faq.q6}</dt><dd>${en.faq.a6}</dd>
<dt>${en.faq.q7}</dt><dd>${en.faq.a7}</dd>
<dt>${en.faq.q8}</dt><dd>${en.faq.a8}</dd>
</dl>
<h2>${en.finalCta.title}</h2>
<p>${en.finalCta.subtitle}</p>
</div>`;
let html = readFileSync(htmlPath, 'utf-8');
html = html.replace('<div id="root"></div>', `<div id="root">${seoContent}</div>`);
writeFileSync(htmlPath, html, 'utf-8');
console.log('[prerender] Injected SEO content into public/pro/index.html');