Skip to content

Commit 3d714c9

Browse files
Brooooooklynclaude
andauthored
feat(seo): source-level SEO overhaul (canonical, hreflang, JSON-LD, per-page OG, sitemap) (#480)
* docs(seo): add SEO overhaul design spec Design doc for the source-level SEO overhaul: canonical + hreflang via the existing head-rewrite middleware fed by a baked route->locales map, meta-description fallback, JSON-LD, sitemap lastmod/alternates, per-page OG images, and head polish. Deployment/migration explicitly out of scope. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(seo): add SEO overhaul implementation plan 11 TDD tasks: pure seo/ url+hreflang+jsonld helpers, baked route-map.gen.ts, head-middleware injection, sitemap lastmod/alternates, content descriptions, blog dates, per-page OG images, and head polish. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(seo): add pure canonical/locale URL helpers * docs(seo): fix plan neutralPath to match splitLocale (en not stripped) * feat(seo): generate committed route->locales + blog-date map * feat(seo): add hreflang alternate link builder * feat(seo): add JSON-LD builder (WebSite/Org/TechArticle/BlogPosting) * docs(seo): fix plan JSON-LD breadcrumb existsByPage to per-locale Record * feat(seo): inject canonical, hreflang, JSON-LD, desc fallback in head middleware * feat(seo): sitemap git lastmod + xhtml:link hreflang alternates Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(seo): add meta descriptions to concept + function + module-init docs * docs(seo): Task 8 guard - maintain blog dates in pages/, never run convert-content * feat(seo): add blog dates + descriptions (fixes RSS + BlogPosting date) * feat(seo): generate per-page OG images at build (satori + resvg) Docs + blog routes get a branded 1200x630 title card at dist/client/og/<route>.png via satori (React node -> SVG) + resvg (SVG -> PNG). Islands keep the static og-v2.png. Fonts come from the static TTFs shipped by @expo-google-fonts (Manrope 700 title, Inter 600 wordmark, Noto Sans SC 700 as CJK fallback so cn titles render). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(seo): move OG build deps to devDependencies; lazy-load OG fonts satori + @resvg/resvg-js are build-only (imported only by scripts/generate-og-images.mjs via vite.config.ts); move them from dependencies to devDependencies so all five build-only packages (incl the three @expo-google-fonts/*) live together. Read the three font TTFs (~11 MB) lazily + memoized in a getFonts() accessor instead of at module top level, so importing the module (dev server, vp test) no longer reads them; only renderOg does. Output PNGs are byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(seo): per-page og:image + twitter card; drop static image duplicates * fix(seo): use en OG image on i18n-fallback pages (avoid 404 cn/pt-BR og:image) * feat(seo): localized h1s, PWA manifest, theme-color, apple-touch-icon, img alt * fix(seo): escape head/JSON-LD, argv git, fallback JSON-LD locale, trailing-slash canonical Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(seo): decode pre-encoded head values before re-escaping (no double-encode) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(seo): normalize /en prefix in og:image path (avoid /og/en/*.png 404) The deploy prerenderer dispatches pages by their internal `/en/docs|blog/…` route, bypassing the edge `/en/* -> /:splat` redirect, so a non-fallback en page reaches ogImageUrl with a raw `en/` prefix. og-image.ts derived the served path from the raw publicPath, emitting `https://napi.rs/og/en/docs/….png` — a PNG the generator never writes (en cards are unprefixed), so crawlers of the baked HTML got a 404 og:image. canonical/og:url were already correct (selfCanonical normalizes en -> root); only og:image leaked the prefix. Rebuild `served` from (locale + neutral) to mirror fileToRoute's shape: default locale drops its prefix, cn/pt-BR keep theirs. Adds regression tests for the /en/ (+ trailing slash) and pt-BR cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent e4ccc55 commit 3d714c9

74 files changed

Lines changed: 3515 additions & 36 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

components/landing/features/optimized-performance.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export const OptimizedPerformanceCard: React.FC = ({
143143
</svg>
144144

145145
<span className="brand-text">
146-
<img src={logo} width={20} height={20} /> NAPI-RS
146+
<img src={logo} alt="NAPI-RS logo" width={20} height={20} /> NAPI-RS
147147
</span>
148148

149149
<div className="checkmark-container">

components/link-preview.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export function LinkPreview({ href, data }: { href: string; data?: string }) {
6666
const logo = href.includes('mozilla') ? (
6767
<img
6868
src={mdnLogo}
69+
alt="MDN"
6970
style={{ verticalAlign: 'text-bottom', marginRight: '4px' }}
7071
width={16}
7172
height={16}

components/title.pt-BR.jsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { NodeJS, Rust } from './lang'
22

3+
// Root is an `<h1>` (was a `<p>`) so the localized landing has exactly one
4+
// top-level heading, matching the en hero. `.page-landing-locale h1` in
5+
// landing.css resets the browser-default h1 sizing/margins back to the prose
6+
// `p` values, so this stays visually identical to the previous paragraph.
37
export const TitlePTBR = () => (
4-
<p>
8+
<h1>
59
<span style={{ fontSize: '36px', fontWeight: 'bold' }}>NAPI-RS</span> é um
610
framework para construir addons pré-compilados para <NodeJS /> em <Rust />.
7-
</p>
11+
</h1>
812
)

components/title.zh.jsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { NodeJS, Rust } from './lang'
22

3+
// Root is an `<h1>` (was a `<p>`) so the localized landing has exactly one
4+
// top-level heading, matching the en hero. `.page-landing-locale h1` in
5+
// landing.css resets the browser-default h1 sizing/margins back to the prose
6+
// `p` values, so this stays visually identical to the previous paragraph.
37
export const TitleZH = () => (
4-
<p>
8+
<h1>
59
<span style={{ fontSize: '36px', fontWeight: 'bold' }}>NAPI-RS</span>{' '}
610
是一个使用 <Rust /> 构建预编译 <NodeJS /> 原生扩展的框架
7-
</p>
11+
</h1>
812
)

content/docs/concepts/class.cn.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: ''
3+
description: 使用 NAPI-RS 将 Rust 结构体定义并导出为 JavaScript 类。
34
---
45

56
#

content/docs/concepts/class.en.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: 'Class'
3+
description: Define and export Rust structs as JavaScript classes with NAPI-RS.
34
---
45

56
# Class

content/docs/concepts/class.pt-BR.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: 'Class'
3+
description: Defina e exporte structs de Rust como classes JavaScript com NAPI-RS.
34
---
45

56
# Classe

content/docs/concepts/enum.cn.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: '枚举'
3+
description: 使用 NAPI-RS 将 Rust 枚举映射为 JavaScript 字符串联合类型和数字枚举。
34
---
45

56
# 枚举

content/docs/concepts/enum.en.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: 'Enum'
3+
description: Map Rust enums to JavaScript string unions and numeric enums in NAPI-RS.
34
---
45

56
# Enum

content/docs/concepts/enum.pt-BR.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: 'Enum'
3+
description: Mapeie enums de Rust para uniões de strings e enums numéricos em JavaScript com NAPI-RS.
34
---
45

56
# Enum

0 commit comments

Comments
 (0)