fix(spa): prerender-build laeuft + meta/json-ld korrekt
Drei build-blocker beim ersten prerender-versuch identifiziert und gefixt:
1. svelte.config.js: handleHttpError + handleMissingId fuer den
prerender-crawler. Der crawler folgt zur build-zeit allen hrefs/srcs
im HTML — sieht dabei
- __SITE_URL__-platzhalter in canonical/hreflang (werden im deploy
per sed ersetzt, sind keine echten routes)
- relative bild-paths in alten posts (z.B. h01-json-import.png)
- anchor-links auf headings ohne id-attribute (#ACF-JSON-Export)
Alle drei sind keine echten 404s — handlers ignorieren sie.
2. +page.svelte: <script type="application/ld+json">{jsonLd}</script>
in <svelte:head> rendert {jsonLd} als literalen string, weil svelte
den script-tag-inhalt nicht als expression evaluiert. Zurueck zu
{@html ...} mit </script>-escape-hardening, damit titel oder
beschreibungen mit </script> den output nicht aufbrechen koennen.
3. app.html behaelt seine homepage-defaults fuer og:title/og:url/
og:description/canonical — der prerender-crawler rendert nur
detail-routen (/<slug>/), die homepage bleibt SPA-only und braucht
die defaults im app.html-template, weil dort kein svelte:head greift.
Detail-routen ueberschreiben per <svelte:head>; last-wins greift bei
LinkedIn/Mastodon/Browser. Facebook/Twitter (first-wins) haetten
einen homepage-prerender-schritt noetig — folge-aufgabe.
Plus snapshot/deno.lock committed — deno empfiehlt lockfile-commit fuer
reproduzierbare CI-builds, analog package-lock.json.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2ad27adf1f
commit
3e31caacef
|
|
@ -10,6 +10,13 @@
|
|||
<meta property="og:description" content="Jörg Lohrer – Blog (Nostr-basiert)" />
|
||||
<link rel="canonical" href="__SITE_URL__/" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<!--
|
||||
Detail-seiten (prerender=true) hängen via %sveltekit.head% ihre
|
||||
eigenen og:title/description/url/canonical hinten an. Last-wins
|
||||
gilt fuer LinkedIn/Mastodon/Browser; Facebook/Twitter nehmen
|
||||
tendenziell first-wins — fuer perfekte OG-tags muesste die
|
||||
homepage auch prerendered werden (separate aufgabe).
|
||||
-->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@
|
|||
<link rel="alternate" hreflang={alt.lang} href={`${siteUrl}/${alt.slug}/`} />
|
||||
{/each}
|
||||
<link rel="alternate" hreflang="x-default" href={canonical} />
|
||||
<script type="application/ld+json">{jsonLd}</script>
|
||||
{@html `<script type="application/ld+json">${jsonLd.replace(/<\/script>/gi, '<\\/script>')}</script>`}
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,29 @@ const config = {
|
|||
}),
|
||||
alias: {
|
||||
$lib: 'src/lib'
|
||||
},
|
||||
prerender: {
|
||||
// Der Crawler folgt zur Build-Zeit href/src-attributen im HTML. Zwei
|
||||
// faelle, in denen 404er kein echter fehler sind:
|
||||
//
|
||||
// 1. canonical/hreflang enthalten den `__SITE_URL__`-platzhalter, der
|
||||
// erst beim deploy per sed durch die echte SITE_URL ersetzt wird.
|
||||
// Pfade wie `/<slug>/__SITE_URL__/` sind also pseudo-pfade.
|
||||
// 2. Bild-references mit relativen pfaden (z.B. `h01-json-import.png`)
|
||||
// in alten posts, die nicht zu Blossom-URLs migriert wurden — die
|
||||
// sind im post-body als <img src="..."> und vom crawler verfolgte
|
||||
// pseudo-routes. Die SPA selbst rendert die <img>-tags zwar, aber
|
||||
// eine 404-route gibt es dafuer nicht.
|
||||
handleHttpError: ({ path, message }) => {
|
||||
if (path.includes('__SITE_URL__')) return;
|
||||
if (/\.(png|jpe?g|gif|webp|svg|avif)\/?$/i.test(path)) return;
|
||||
throw new Error(message);
|
||||
},
|
||||
// Markdown-headings bekommen ohne slugify-plugin keine id-attribute.
|
||||
// Anchor-links in alten posts (z.B. [link](#ACF-JSON-Export)) sind
|
||||
// damit zur build-zeit unauffindbar. Kein render-fehler — die SPA
|
||||
// scrollt im browser entweder zum element oder garnicht.
|
||||
handleMissingId: 'ignore'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,172 @@
|
|||
{
|
||||
"version": "5",
|
||||
"specifiers": {
|
||||
"jsr:@std/assert@^1.0.6": "1.0.19",
|
||||
"jsr:@std/cli@^1.0.6": "1.0.28",
|
||||
"jsr:@std/fs@^1.0.4": "1.0.23",
|
||||
"jsr:@std/internal@^1.0.12": "1.0.12",
|
||||
"jsr:@std/path@^1.0.6": "1.1.4",
|
||||
"jsr:@std/path@^1.1.4": "1.1.4",
|
||||
"npm:applesauce-relay@2": "2.3.0",
|
||||
"npm:nostr-tools@^2.10.4": "2.23.3",
|
||||
"npm:rxjs@^7.8.1": "7.8.2"
|
||||
},
|
||||
"jsr": {
|
||||
"@std/assert@1.0.19": {
|
||||
"integrity": "eaada96ee120cb980bc47e040f82814d786fe8162ecc53c91d8df60b8755991e",
|
||||
"dependencies": [
|
||||
"jsr:@std/internal"
|
||||
]
|
||||
},
|
||||
"@std/cli@1.0.28": {
|
||||
"integrity": "74ef9b976db59ca6b23a5283469c9072be6276853807a83ec6c7ce412135c70a",
|
||||
"dependencies": [
|
||||
"jsr:@std/internal"
|
||||
]
|
||||
},
|
||||
"@std/fs@1.0.23": {
|
||||
"integrity": "3ecbae4ce4fee03b180fa710caff36bb5adb66631c46a6460aaad49515565a37",
|
||||
"dependencies": [
|
||||
"jsr:@std/internal",
|
||||
"jsr:@std/path@^1.1.4"
|
||||
]
|
||||
},
|
||||
"@std/internal@1.0.12": {
|
||||
"integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027"
|
||||
},
|
||||
"@std/path@1.1.4": {
|
||||
"integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5",
|
||||
"dependencies": [
|
||||
"jsr:@std/internal"
|
||||
]
|
||||
}
|
||||
},
|
||||
"npm": {
|
||||
"@noble/ciphers@2.1.1": {
|
||||
"integrity": "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw=="
|
||||
},
|
||||
"@noble/curves@2.0.1": {
|
||||
"integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==",
|
||||
"dependencies": [
|
||||
"@noble/hashes@2.0.1"
|
||||
]
|
||||
},
|
||||
"@noble/hashes@1.8.0": {
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="
|
||||
},
|
||||
"@noble/hashes@2.0.1": {
|
||||
"integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="
|
||||
},
|
||||
"@scure/base@1.1.1": {
|
||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="
|
||||
},
|
||||
"@scure/base@1.2.6": {
|
||||
"integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="
|
||||
},
|
||||
"@scure/base@2.0.0": {
|
||||
"integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w=="
|
||||
},
|
||||
"@scure/bip32@2.0.1": {
|
||||
"integrity": "sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA==",
|
||||
"dependencies": [
|
||||
"@noble/curves",
|
||||
"@noble/hashes@2.0.1",
|
||||
"@scure/base@2.0.0"
|
||||
]
|
||||
},
|
||||
"@scure/bip39@2.0.1": {
|
||||
"integrity": "sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg==",
|
||||
"dependencies": [
|
||||
"@noble/hashes@2.0.1",
|
||||
"@scure/base@2.0.0"
|
||||
]
|
||||
},
|
||||
"applesauce-core@2.3.0": {
|
||||
"integrity": "sha512-rMVrwGMgHxXAHZfrq3ibtMjljAxeEfT95nl5VYLl5mSMmOHXnwjbiPTccJ2UDd6GP+INdHfkPgeB8AOUf5DFog==",
|
||||
"dependencies": [
|
||||
"@noble/hashes@1.8.0",
|
||||
"@scure/base@1.2.6",
|
||||
"debug",
|
||||
"fast-deep-equal",
|
||||
"hash-sum",
|
||||
"light-bolt11-decoder",
|
||||
"nanoid",
|
||||
"nostr-tools",
|
||||
"rxjs"
|
||||
]
|
||||
},
|
||||
"applesauce-relay@2.3.0": {
|
||||
"integrity": "sha512-tOijiN1yVyORS5jT5mXe8MTzqc1IVq/AdJXOzTe3uQgeDYhJzQ9lNYgqejDBXW1ahUThsRZgX2RybkOHVjBuHA==",
|
||||
"dependencies": [
|
||||
"@noble/hashes@1.8.0",
|
||||
"applesauce-core",
|
||||
"nanoid",
|
||||
"nostr-tools",
|
||||
"rxjs"
|
||||
]
|
||||
},
|
||||
"debug@4.4.3": {
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"dependencies": [
|
||||
"ms"
|
||||
]
|
||||
},
|
||||
"fast-deep-equal@3.1.3": {
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
},
|
||||
"hash-sum@2.0.0": {
|
||||
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
|
||||
},
|
||||
"light-bolt11-decoder@3.2.0": {
|
||||
"integrity": "sha512-3QEofgiBOP4Ehs9BI+RkZdXZNtSys0nsJ6fyGeSiAGCBsMwHGUDS/JQlY/sTnWs91A2Nh0S9XXfA8Sy9g6QpuQ==",
|
||||
"dependencies": [
|
||||
"@scure/base@1.1.1"
|
||||
]
|
||||
},
|
||||
"ms@2.1.3": {
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"nanoid@5.1.7": {
|
||||
"integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==",
|
||||
"bin": true
|
||||
},
|
||||
"nostr-tools@2.23.3": {
|
||||
"integrity": "sha512-AALyt9k8xPdF4UV2mlLJ2mgCn4kpTB0DZ8t2r6wjdUh6anfx2cTVBsHUlo9U0EY/cKC5wcNyiMAmRJV5OVEalA==",
|
||||
"dependencies": [
|
||||
"@noble/ciphers",
|
||||
"@noble/curves",
|
||||
"@noble/hashes@2.0.1",
|
||||
"@scure/base@2.0.0",
|
||||
"@scure/bip32",
|
||||
"@scure/bip39",
|
||||
"nostr-wasm"
|
||||
]
|
||||
},
|
||||
"nostr-wasm@0.1.0": {
|
||||
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA=="
|
||||
},
|
||||
"rxjs@7.8.2": {
|
||||
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
|
||||
"dependencies": [
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"tslib@2.8.1": {
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@^1.0.6",
|
||||
"jsr:@std/cli@^1.0.6",
|
||||
"jsr:@std/encoding@^1.0.5",
|
||||
"jsr:@std/fs@^1.0.4",
|
||||
"jsr:@std/path@^1.0.6",
|
||||
"jsr:@std/testing@^1.0.3",
|
||||
"jsr:@std/yaml@^1.0.5",
|
||||
"npm:applesauce-relay@2",
|
||||
"npm:nostr-tools@^2.10.4",
|
||||
"npm:rxjs@^7.8.1"
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue