feat: SEO crawlability + on-brand copy
Make every served byte crawlable without executing JS, remove off-brand copy, and cut third-party requests. - Copy: replace off-brand terms (survival/crisis/collapse) in title, meta, OG/Twitter, JSON-LD and webmanifest with on-brand preparedness copy. Update App.vue i18n page_title/meta_description (EN+DE) too, since the app overwrites the head at runtime. Reconcile area count to the real 6. - Prerender: vite-plugin-seo-snapshot injects a static <noscript> snapshot (real <h1>, content, <a href>) after #app. JS users ignore <noscript>, so the live app/UX is unchanged; curl/no-JS crawlers get real content. - 404: nginx now returns true 404s on unknown paths (no soft-200 SPA fallback) with a branded 404 page. - Sitemap: drop no-op hreflang alternates, add <lastmod>. - Fonts: self-host Space Mono / DM Serif Display / Barlow (woff2, latin + latin-ext, font-display: swap); preload above-the-fold faces. No more fonts.googleapis/gstatic requests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b845957314
commit
425e56c0ca
+12
-11
@@ -5,8 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
|
||||
|
||||
<!-- ── Primary meta ─────────────────────────────────────────────── -->
|
||||
<title>Plan-B — AI-Assisted Crisis Preparedness Advisor</title>
|
||||
<meta name="description" content="Free AI-assisted crisis-preparedness advisor for urban households. Assess your readiness across 9 collapse scenarios and get a personalised survival plan in minutes."/>
|
||||
<title>Plan-B — AI-Assisted Preparedness for Urban Households</title>
|
||||
<meta name="description" content="A calm, costed preparedness plan for your household in about 10 minutes. Check your readiness across 6 areas like water, energy and food, built on economics not fear."/>
|
||||
<meta name="author" content="Plan-B"/>
|
||||
<meta name="theme-color" content="#5A9A78"/>
|
||||
<meta name="color-scheme" content="light"/>
|
||||
@@ -16,8 +16,8 @@
|
||||
<!-- ── Open Graph (Facebook, LinkedIn, WhatsApp, Signal, Telegram…) ─ -->
|
||||
<meta property="og:type" content="website"/>
|
||||
<meta property="og:site_name" content="Plan-B"/>
|
||||
<meta property="og:title" content="Plan-B — AI-Assisted Crisis Preparedness Advisor"/>
|
||||
<meta property="og:description" content="Assess your household's readiness across 9 collapse scenarios and get a personalised survival plan — water, food, energy, medical and budget. Built for city apartments."/>
|
||||
<meta property="og:title" content="Plan-B — AI-Assisted Preparedness for Urban Households"/>
|
||||
<meta property="og:description" content="Find out where your household is exposed and get a clear, costed 90-day plan. Water, energy, food and more, made for city apartments."/>
|
||||
<meta property="og:url" content="https://plan-b.now/"/>
|
||||
<meta property="og:image" content="https://plan-b.now/images/og-image.png"/>
|
||||
<meta property="og:image:secure_url" content="https://plan-b.now/images/og-image.png"/>
|
||||
@@ -30,8 +30,8 @@
|
||||
|
||||
<!-- ── Twitter / X ─────────────────────────────────────────────── -->
|
||||
<meta name="twitter:card" content="summary_large_image"/>
|
||||
<meta name="twitter:title" content="Plan-B — AI-Assisted Crisis Preparedness Advisor"/>
|
||||
<meta name="twitter:description" content="Assess your household's readiness across 9 collapse scenarios and get a personalised survival plan. Built for city apartments."/>
|
||||
<meta name="twitter:title" content="Plan-B — AI-Assisted Preparedness for Urban Households"/>
|
||||
<meta name="twitter:description" content="A calm, costed 90-day preparedness plan for your household. Made for city apartments."/>
|
||||
<meta name="twitter:image" content="https://plan-b.now/images/og-image.png"/>
|
||||
<meta name="twitter:image:alt" content="Plan-B — AI-Assisted Preparedness for Urban Households"/>
|
||||
|
||||
@@ -42,10 +42,11 @@
|
||||
<link rel="apple-touch-icon" href="/images/apple-touch-icon.png"/>
|
||||
<link rel="manifest" href="/site.webmanifest"/>
|
||||
|
||||
<!-- ── Fonts ───────────────────────────────────────────────────── -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Serif+Display&family=Barlow:wght@300;400;500;600&display=swap" rel="stylesheet"/>
|
||||
<!-- ── Fonts (self-hosted — no third-party requests) ───────────── -->
|
||||
<link rel="preload" href="/fonts/barlow-400-latin.woff2" as="font" type="font/woff2" crossorigin/>
|
||||
<link rel="preload" href="/fonts/dm-serif-display-400-latin.woff2" as="font" type="font/woff2" crossorigin/>
|
||||
<link rel="preload" href="/fonts/space-mono-400-latin.woff2" as="font" type="font/woff2" crossorigin/>
|
||||
<link rel="stylesheet" href="/fonts/fonts.css"/>
|
||||
|
||||
<!-- ── Structured data (Google rich results) ───────────────────── -->
|
||||
<script type="application/ld+json">
|
||||
@@ -57,7 +58,7 @@
|
||||
"@id": "https://plan-b.now/#website",
|
||||
"url": "https://plan-b.now/",
|
||||
"name": "Plan-B",
|
||||
"description": "AI-assisted crisis-preparedness advisor for urban households.",
|
||||
"description": "AI-assisted preparedness for urban households.",
|
||||
"inLanguage": ["en", "de"],
|
||||
"publisher": { "@id": "https://plan-b.now/#org" }
|
||||
},
|
||||
|
||||
+18
-3
@@ -24,10 +24,25 @@ server {
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# SPA fallback — any non-asset path serves index.html so future
|
||||
# client-side routing works without 404s.
|
||||
# App shell — only the root path serves index.html. The app is a
|
||||
# single-route SPA (no history-based client routing), so there is no
|
||||
# need to fall back unknown paths to index.html.
|
||||
location = / {
|
||||
try_files /index.html =404;
|
||||
}
|
||||
|
||||
# Everything else serves a real file if it exists, otherwise a true 404.
|
||||
# This avoids the soft-404 (HTTP 200 on unknown paths) that hurts SEO.
|
||||
# When real redirects/removals appear, add `return 301`/`return 410`
|
||||
# location blocks above this one.
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
# Branded 404 page (shipped from public/404.html).
|
||||
error_page 404 /404.html;
|
||||
location = /404.html {
|
||||
internal;
|
||||
}
|
||||
|
||||
# Don't serve dotfiles
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta name="robots" content="noindex, follow"/>
|
||||
<title>Page not found — Plan-B</title>
|
||||
<link rel="icon" href="/images/favicon.svg" type="image/svg+xml"/>
|
||||
<style>
|
||||
:root { color-scheme: light; }
|
||||
html, body { height: 100%; margin: 0; }
|
||||
body {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: #FAFAFA; color: #2a3010;
|
||||
font-family: "Barlow", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
||||
text-align: center; padding: 2rem;
|
||||
}
|
||||
.wrap { max-width: 32rem; }
|
||||
h1 { font-size: 3.5rem; margin: 0 0 .5rem; font-weight: 600; letter-spacing: -0.02em; }
|
||||
p { font-size: 1.125rem; line-height: 1.6; margin: 0 0 1.75rem; color: #4a5340; }
|
||||
a.home {
|
||||
display: inline-block; padding: .75rem 1.5rem; border-radius: .5rem;
|
||||
background: #5A9A78; color: #fff; text-decoration: none; font-weight: 500;
|
||||
}
|
||||
a.home:hover { background: #4d8567; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<h1>404</h1>
|
||||
<p>This page isn’t here. The plan still is — head back to the start and check your household readiness.</p>
|
||||
<a class="home" href="/">Back to Plan-B</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
/* Self-hosted fonts — replaces fonts.googleapis.com.
|
||||
Subsets: latin + latin-ext. font-display: swap.
|
||||
Source: Google Fonts (SIL OFL 1.1). Regenerate via the fonts download step. */
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(/fonts/barlow-300-latin-ext.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(/fonts/barlow-300-latin.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/fonts/barlow-400-latin-ext.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/fonts/barlow-400-latin.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/fonts/barlow-500-latin-ext.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/fonts/barlow-500-latin.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(/fonts/barlow-600-latin-ext.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Barlow';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(/fonts/barlow-600-latin.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DM Serif Display';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/fonts/dm-serif-display-400-latin-ext.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DM Serif Display';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/fonts/dm-serif-display-400-latin.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/fonts/space-mono-400-latin-ext.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/fonts/space-mono-400-latin.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(/fonts/space-mono-700-latin-ext.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(/fonts/space-mono-700-latin.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Plan-B — Crisis Preparedness Advisor",
|
||||
"name": "Plan-B — Preparedness for Urban Households",
|
||||
"short_name": "Plan-B",
|
||||
"description": "AI-assisted crisis-preparedness advisor for urban households.",
|
||||
"description": "AI-assisted preparedness for urban households.",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
|
||||
+2
-5
@@ -1,12 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://plan-b.now/</loc>
|
||||
<lastmod>2026-06-18</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
<xhtml:link rel="alternate" hreflang="en" href="https://plan-b.now/"/>
|
||||
<xhtml:link rel="alternate" hreflang="de" href="https://plan-b.now/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://plan-b.now/"/>
|
||||
</url>
|
||||
</urlset>
|
||||
|
||||
+4
-4
@@ -589,8 +589,8 @@ import { onMounted } from 'vue'
|
||||
const T = {
|
||||
en: {
|
||||
brand: "Plan-B",
|
||||
page_title: "Plan-B — AI-Assisted Crisis Preparedness Advisor",
|
||||
meta_description: "Free AI-assisted crisis-preparedness advisor for urban households. Assess your readiness across 9 collapse scenarios and get a personalised survival plan in minutes.",
|
||||
page_title: "Plan-B — AI-Assisted Preparedness for Urban Households",
|
||||
meta_description: "A calm, costed preparedness plan for your household in about 10 minutes. Check your readiness across 6 areas like water, energy and food, built on economics not fear.",
|
||||
og_locale: "en_US",
|
||||
hero_eyebrow: "Crisis Preparedness Advisor",
|
||||
hero_sub: "AI-Assisted Preparedness for Urban Households",
|
||||
@@ -713,8 +713,8 @@ const T = {
|
||||
},
|
||||
de: {
|
||||
brand: "Plan-B",
|
||||
page_title: "Plan-B — KI-gestützter Krisenvorsorge-Berater",
|
||||
meta_description: "Kostenloser KI-gestützter Krisenvorsorge-Berater für urbane Haushalte. Bewerte deine Vorsorge über 9 Krisenszenarien und erhalte in Minuten einen personalisierten Survival-Plan.",
|
||||
page_title: "Plan-B — KI-gestützte Vorsorge für urbane Haushalte",
|
||||
meta_description: "Ein besonnener, kalkulierter Vorsorgeplan für Ihren Haushalt in rund 10 Minuten. Prüfen Sie Ihre Vorsorge in 6 Bereichen wie Wasser, Energie und Lebensmitteln, ökonomisch fundiert statt angstgetrieben.",
|
||||
og_locale: "de_DE",
|
||||
hero_eyebrow: "⚡ Krisenvorsorge-Berater",
|
||||
hero_sub: "KI-gestützte Vorsorge für urbane Haushalte",
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
Static SEO snapshot — injected into a <noscript> in the served HTML by
|
||||
vite-plugin-seo-snapshot.js. Browsers with JavaScript enabled IGNORE this
|
||||
block entirely, so it has ZERO effect on the live app/UX. It exists so that
|
||||
`curl` and non-rendering crawlers receive real <h1> + content + <a href>
|
||||
links instead of an empty shell. Keep copy on-brand (preparedness, not
|
||||
crisis/survival) and factually aligned with the app.
|
||||
-->
|
||||
<main class="seo-snapshot" role="main">
|
||||
<h1>Plan-B — AI-Assisted Preparedness for Urban Households</h1>
|
||||
|
||||
<p>Plan-B builds a calm, costed preparedness plan for your household in about
|
||||
ten minutes. It checks where your home is exposed and gives you a clear,
|
||||
prioritised 90-day plan — built on economics, not fear. Made for city
|
||||
apartments.</p>
|
||||
|
||||
<h2>What Plan-B checks</h2>
|
||||
<p>Answer a short set of questions about your household and get a readiness
|
||||
picture across the core areas that keep a home running through a power outage,
|
||||
a supply disruption, or a service interruption:</p>
|
||||
<ul>
|
||||
<li><strong>Water</strong> — storage, filtration and backup supply.</li>
|
||||
<li><strong>Food</strong> — reserves, shelf-stable staples and rotation.</li>
|
||||
<li><strong>Growing</strong> — fresh food you can produce at home.</li>
|
||||
<li><strong>Energy</strong> — power and heat backup for an apartment.</li>
|
||||
<li><strong>Medical</strong> — first aid and essential supplies.</li>
|
||||
<li><strong>Sanitation</strong> — hygiene and waste when services pause.</li>
|
||||
</ul>
|
||||
|
||||
<h2>How it works</h2>
|
||||
<ol>
|
||||
<li>Answer a few questions about your household and location.</li>
|
||||
<li>See where you are exposed across each readiness area.</li>
|
||||
<li>Get a costed, prioritised 90-day plan you can act on.</li>
|
||||
</ol>
|
||||
|
||||
<nav aria-label="Site">
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/#about-section">About Plan-B</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<p>Plan-B is available in English and German (Kammergut).</p>
|
||||
</main>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
// Injects a static, crawlable SEO snapshot into a <noscript> block in the
|
||||
// served index.html. Browsers with JS enabled ignore <noscript>, so the live
|
||||
// app and its UX are completely unaffected. Non-rendering crawlers and `curl`
|
||||
// receive a real <h1>, descriptive content and <a href> links instead of an
|
||||
// empty SPA shell.
|
||||
export default function seoSnapshot() {
|
||||
const snapshotPath = fileURLToPath(new URL('./src/seo-snapshot.html', import.meta.url))
|
||||
return {
|
||||
name: 'seo-snapshot',
|
||||
transformIndexHtml(html) {
|
||||
const snapshot = readFileSync(snapshotPath, 'utf8')
|
||||
.replace(/<!--[\s\S]*?-->/g, '') // strip authoring comments from served HTML
|
||||
.trim()
|
||||
const block = `<noscript>\n${snapshot}\n</noscript>`
|
||||
// Place it immediately after the app mount point.
|
||||
return html.replace(
|
||||
'<div id="app"></div>',
|
||||
`<div id="app"></div>\n${block}`,
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import seoSnapshot from './vite-plugin-seo-snapshot.js'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
plugins: [vue(), seoSnapshot()],
|
||||
server: { port: 5173 }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user