fix(seo): locale edge cases manifest and structured data polish #34
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/seo/locale-edge-cases-manifest-polish"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Что сделано
src/middleware.ts) —/RU,/En,/EN/foo→ 308 redirect на lowercase-вариант с сохранением path. Matcher regex не менялся (uppercase не матчитru|enв lookahead → middleware срабатывает и делает case-correction). 8 новых тестов вmiddleware.test.ts.manifest.tsstart_url—"/"→"/ru"(default locale, PWA launch без redirect hop).lang: "ru"оставлен (single manifest, locale-aware — out of scope).[slug]/page.tsxog:type —"website"→"article"+publishedTime: repo.createdAt+authors: ["Sergey"]для rich social cards.lastmod—DiscoveredRepoиForgejoRepoDetailполучилиupdatedAt/updated_atполе, проброшено черезbuildDiscoveredRepo/makeErrorRepo/discoverShowcaseRepos. Sitemap:lastmod = updatedAt ?? createdAt(fallback на createdAt если updated_at null).switchLocalePath— JSDoc-документация инварианта same-slug-both-locales (функция осталась sync pure, async validation — out of scope).layout.tsxmetadataBase—new URL(baseUrl)для rootnot-found.tsxmetadata URL resolution.Почему
Issue #31 — остаточные SEO/UX edge cases после PR#32 (routing/404) и PR#33 (hreflang/OG/robots). Uppercase locale bypass, hardcoded manifest lang/start_url, og:type=website для detail-страниц, sitemap lastmod=createdAt (не обновляется), switchLocalePath без валидации, root metadataBase missing. По отдельности low-severity, но для цели «ни одного косяка» — все закрыты.
Watch out
pathname.slice(first.length)не учитывала ведущий/— давало/enN/fooвместо/en/foo). Исправлено: redirect target собирается из segments (/${lowerFirst}/${rest}), не из slice.ForgejoRepoListItemтип получилupdated_at: string | null— mock-объекты в 5 тестах обновлены (updatedAt: null), mockRepoDetailвshowcase-single-repo.test.tsиспользуетas-каст и не требует обновления (undefined → fallback null).Pending
—
Closes #31
Code Review Summary
Качественный SEO-polish PR: case-insensitive locale redirect в middleware, manifest start_url, og:type=article для detail-страниц, sitemap lastmod с fallback на createdAt, root metadataBase, JSDoc для switchLocalePath. Все 197 тестов зелёные, tsc --noEmit чисто, 5 mock-объектов обновлены с
updatedAt: null. Исправление бага спеки (segment slicing → join) корректно и задокументировано в Watch out.Positives
ru|enlookahead → middleware срабатывает), redirect target собирается из segments (не slice), 308 permanent. 8 тестов покрывают /RU, /En, /Ru, /RU/apps, /En/foo, /EN/foo + negative cases (already-lowercase passes through).DiscoveredRepo.updatedAtпроброшен консистентно через весь стек:ForgejoRepoListItem→RepoMeta→buildDiscoveredRepo/makeErrorRepo→discoverShowcaseRepos. Sitemap fallbackupdatedAt ?? createdAtс NaN-guard — корректный.switchLocalePathдокументирует нетривиальный инвариант (same-slug-both-locales) — соответствует code-standards §4 (public API contract warrants docstring).layout.tsxmetadataBaseне дублирует[locale]/layout.tsx— Next.js использует nearest, root применяется только кnot-found.tsxвне[locale]segment. Корректное решение.## Что сделанодетально,## Почемуссылается на issue #31,## Watch outописывает fix бага спеки + mock-обновления.Suggestions (info, not blocking)
src/lib/readme-discovery.ts:217,237 [consistency]
buildDiscoveredRepoставитupdatedAt: repo.updated_atбез?? null, тогда какmakeErrorRepo(line 186) иdiscoverShowcaseRepos(line 313) нормализуют?? null. ПутьdiscoverSingleRepo(line 277) передаётdetailсupdated_at: undefinedв рантайме (моки вshowcase-single-repo.test.ts:49-60иgenerate-metadata.test.ts:80-89не включаютupdated_at) →updatedAt: undefinedвместоnull, нарушая типstring | null. Runtime-безопасно (sitemap трактует falsy), но нарушает паттерн из того же PR. Fix:updatedAt: repo.updated_at ?? null.src/app/manifest.ts:8 [DRY]
start_url: "/ru"хардкод вместоDEFAULT_LOCALE. ЕслиDEFAULT_LOCALEсменится на"en",start_urlустареет. Fix:import { DEFAULT_LOCALE } from "@/lib/i18n"→start_url: \/${DEFAULT_LOCALE}``.src/tests/middleware.test.ts [test coverage] Нет теста, что matcher regex всё ещё матчит uppercase пути (
/RU,/En). Если кто-то изменит matcher, чтобы исключать uppercase, case-correction станет dead code, а существующие 8 тестов пройдут (они вызывают middleware напрямую, минуя matcher). Fix: добавитьexpect(matches("/RU")).toBe(true),expect(matches("/En")).toBe(true)в matcher describe block.src/app/[locale]/[slug]/page.tsx:57-58 [suggestion] Добавлен
publishedTime: repo.createdAt, но неmodifiedTime: repo.updatedAt. Для og:articlemodifiedTimeулучшает social card caching. Fix:modifiedTime: repo.updatedAt ?? repo.createdAt.Verdict: APPROVE