fix(website): improve seo foundations - #470
Conversation
Improve social previews, changelog navigation, canonical routing, delivery headers, and static-site SEO regressions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
WalkthroughCloudFront delivery policies add response headers and caching rules. Website localization gains route-aware trailing-slash handling. Changelog rendering, homepage content, metadata, deferred video loading, SEO validation, and package metadata are updated. ChangesCloudFront delivery configuration
Website localization and SEO
Node.js package metadata
Estimated code review effort: 4 (Complex) | ~50 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR strengthens Envilder’s website SEO baseline and regression protection by (1) making localized routing/canonical/hreflang behavior explicit, (2) improving share previews and media loading, and (3) tightening CDN caching + security headers, while adding build-output checks to catch SEO regressions pre-deploy.
Changes:
- Add Open Graph/Twitter image metadata (absolute URLs) + JSON-LD screenshot, and defer demo video loading with poster + transcript.
- Normalize i18n routing (trailing slashes, supported localized routes) and harden changelog navigation (stable fragment IDs, noindex localized changelog shells, sitemap filtering).
- Add CloudFront cache/security header policies + HTTP/2+HTTP/3, with new static-site + infra regression tests.
Reviewed changes
Copilot reviewed 27 out of 36 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/website/utils/markdown.test.ts | Updates changelog HTML tests for prefixed fragment IDs and doc-title rendering. |
| tests/website/static-site.test.ts | Adds dist-based SEO regression tests (canonical/hreflang/anchors/OG/Twitter/robots/sitemap/video). |
| tests/website/i18n/utils.test.ts | Updates localizedPath expectations (trailing slash, unsupported localization fallback, fragment preservation). |
| tests/iac/lib/stacks/staticWebsiteStack.test.ts | Extends CDK assertions for error caching TTL, HTTP version, and response header policies. |
| tests/iac/lib/stacks/snapshots/staticWebsiteStack.test.ts.snap | Updates synthesized template snapshot for new CloudFront behaviors/policies. |
| src/website/src/utils/markdown.ts | Adds changelog heading id prefixing and renders markdown document titles as <h2> for changelog pages. |
| src/website/src/pages/changelog.astro | Uses prefixed changelog anchors and adds language notice copy. |
| src/website/src/pages/ca/changelog.astro | Adds noindex for localized changelog shell + prefix-based anchors + language notice. |
| src/website/src/pages/es/changelog.astro | Adds noindex for localized changelog shell + prefix-based anchors + language notice. |
| src/website/src/layouts/BaseLayout.astro | Implements canonical localized routing, OG/Twitter image metadata, and hreflang clusters driven by localized route support. |
| src/website/src/i18n/utils.ts | Adds path normalization, locale stripping, supported-language lookup, and localizedPath fragment preservation. |
| src/website/src/i18n/types.ts | Extends translation types for new SEO/media/changelog strings. |
| src/website/src/i18n/localized-routes.ts | Defines indexable routes and their supported locales as the source of truth for localization. |
| src/website/src/i18n/en.ts | Adds/updates SEO and UI copy (OG alt, positioning, transcript, release language notice, docs title). |
| src/website/src/i18n/ca.ts | Adds/updates SEO and UI copy (OG alt, positioning, transcript, release language notice, docs title). |
| src/website/src/i18n/es.ts | Adds/updates SEO and UI copy (OG alt, positioning, transcript, release language notice, docs title). |
| src/website/src/components/Sponsors.astro | Marks sponsor link as sponsored and adds image perf attributes (dimensions/lazy/async). |
| src/website/src/components/Sdks.astro | Normalizes docs links to trailing-slash routes. |
| src/website/src/components/Navbar.astro | Normalizes changelog/docs links to trailing-slash routes. |
| src/website/src/components/HowItWorks.astro | Normalizes docs links to trailing-slash routes. |
| src/website/src/components/Hero.astro | Adds positioning copy under the main hero description. |
| src/website/src/components/Footer.astro | Updates language switcher to only show supported locales per route; normalizes links with trailing slashes and fragments. |
| src/website/src/components/DemoVideo.astro | Defers loading demo MP4 until near-viewport, adds poster + transcript, respects reduced motion. |
| src/website/astro.config.mjs | Filters localized changelog shells out of sitemap; enforces trailing slashes. |
| src/website/public/localstack-logo-vertical-Light.svg | Removes unused asset. |
| src/website/public/localstack-logo-vertical-dark.svg | Removes unused asset. |
| src/website/public/localstack-logo-vertical-color.svg | Removes unused asset. |
| src/website/public/localstack-logo-icon-light.svg | Removes unused asset. |
| src/website/public/localstack-logo-icon-dark.svg | Removes unused asset. |
| src/website/public/localstack-logo-icon-color.svg | Removes unused asset. |
| src/website/public/localstack-logo-horizontal-color.svg | Removes unused asset. |
| src/sdks/nodejs/package.json | Adds homepage metadata. |
| src/iac/lib/stacks/staticWebsiteStack.ts | Adds CloudFront response header policies, asset behavior, longer error TTL, and HTTP/2+HTTP/3. |
| package.json | Updates root package description and adjusts test script to build website before running Vitest. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/iac/lib/stacks/staticWebsiteStack.test.ts (1)
81-142: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAssertions don't verify which behavior references which policy.
The test checks that a
_assets/*CacheBehavior exists and that two ResponseHeadersPolicy resources exist with the expected Cache-Control values, but never links the two — ifdefaultResponseHeadersPolicyandassetResponseHeadersPolicywere accidentally swapped betweendefaultBehaviorand_assets/*, this test would still pass.♻️ Example using Capture to link behavior → policy id
+ const assetPolicyIdCapture = new Capture(); actual.hasResourceProperties("AWS::CloudFront::Distribution", { DistributionConfig: Match.objectLike({ HttpVersion: "http2and3", CacheBehaviors: Match.arrayWith([ Match.objectLike({ PathPattern: "_assets/*", + ResponseHeadersPolicyId: { Ref: assetPolicyIdCapture }, }), ]), }), }); + // then assert assetPolicyIdCapture.asString() matches the asset (not default) policy's logical id🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/iac/lib/stacks/staticWebsiteStack.test.ts` around lines 81 - 142, Update Should_ApplyCacheAndSecurityHeaders_When_WebsiteIsCreated to capture the CloudFront ResponseHeadersPolicy resource IDs and assert that the defaultBehavior and the _assets/* CacheBehavior reference the correct policy IDs. Keep the existing policy-content assertions, but explicitly link each CacheBehavior’s ResponseHeadersPolicyId to its corresponding defaultResponseHeadersPolicy or assetResponseHeadersPolicy.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/website/src/layouts/BaseLayout.astro`:
- Around line 128-138: Update the og:locale meta tag in BaseLayout to emit a
full Open Graph locale with language and territory, rather than the bare lang
code. Reuse the existing locale or language-region mapping used by the layout so
values resolve to forms such as en_US, ca_ES, and es_ES.
---
Nitpick comments:
In `@tests/iac/lib/stacks/staticWebsiteStack.test.ts`:
- Around line 81-142: Update
Should_ApplyCacheAndSecurityHeaders_When_WebsiteIsCreated to capture the
CloudFront ResponseHeadersPolicy resource IDs and assert that the
defaultBehavior and the _assets/* CacheBehavior reference the correct policy
IDs. Keep the existing policy-content assertions, but explicitly link each
CacheBehavior’s ResponseHeadersPolicyId to its corresponding
defaultResponseHeadersPolicy or assetResponseHeadersPolicy.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 506dbbbd-c696-462a-97fd-626ab8234eba
⛔ Files ignored due to path filters (10)
package.jsonis excluded by none and included by nonesrc/website/public/localstack-logo-horizontal-color.svgis excluded by!**/*.svgand included bysrc/**src/website/public/localstack-logo-icon-color.svgis excluded by!**/*.svgand included bysrc/**src/website/public/localstack-logo-icon-dark.svgis excluded by!**/*.svgand included bysrc/**src/website/public/localstack-logo-icon-light.svgis excluded by!**/*.svgand included bysrc/**src/website/public/localstack-logo-vertical-Light.svgis excluded by!**/*.svgand included bysrc/**src/website/public/localstack-logo-vertical-color.svgis excluded by!**/*.svgand included bysrc/**src/website/public/localstack-logo-vertical-dark.svgis excluded by!**/*.svgand included bysrc/**src/website/public/og-image.pngis excluded by!**/*.pngand included bysrc/**tests/iac/lib/stacks/__snapshots__/staticWebsiteStack.test.ts.snapis excluded by!**/*.snapand included bytests/**
📒 Files selected for processing (26)
src/iac/lib/stacks/staticWebsiteStack.tssrc/sdks/nodejs/package.jsonsrc/website/astro.config.mjssrc/website/public/Envilder-demo-poster.webpsrc/website/src/components/DemoVideo.astrosrc/website/src/components/Footer.astrosrc/website/src/components/Hero.astrosrc/website/src/components/HowItWorks.astrosrc/website/src/components/Navbar.astrosrc/website/src/components/Sdks.astrosrc/website/src/components/Sponsors.astrosrc/website/src/i18n/ca.tssrc/website/src/i18n/en.tssrc/website/src/i18n/es.tssrc/website/src/i18n/localized-routes.tssrc/website/src/i18n/types.tssrc/website/src/i18n/utils.tssrc/website/src/layouts/BaseLayout.astrosrc/website/src/pages/ca/changelog.astrosrc/website/src/pages/changelog.astrosrc/website/src/pages/es/changelog.astrosrc/website/src/utils/markdown.tstests/iac/lib/stacks/staticWebsiteStack.test.tstests/website/i18n/utils.test.tstests/website/static-site.test.tstests/website/utils/markdown.test.ts
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 36 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
package.json:34
test:ciis used in CI (.github/workflows/tests.yml) but it does not build the website first. Since this PR adds tests that readsrc/website/dist(e.g., tests/website/static-site.test.ts), CI runs will fail unless the website build happens before Vitest (as it now does forpnpm test).
"test": "pnpm --filter @envilder/website build && vitest run --reporter=verbose --coverage",
"test:ci": "vitest run --reporter=verbose --reporter=junit --coverage --outputFile=coverage/junit/test-results.xml",
tests/website/static-site.test.ts:89
JSON.parse(jsonLd)can throw, which would crash the test run instead of reporting a readable SEO failure. This makes the regression test less actionable (a parse error stacktrace rather than${file}: invalid JSON-LD).
Build the website before static output tests run in CI. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Return an actionable SEO issue instead of an uncaught parse error. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Emit language and territory codes that Open Graph consumers recognize. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Prevent default and asset response header policies from being swapped. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 36 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/website/src/i18n/localized-routes.ts:5
localizedRoutesis typed asRecord<string, readonly string[]>, andlocalizedLanguages()later casts these strings toLang. That means a typo like 'eng' would compile and only fail at runtime (e.g., producing invalidhreflang/routes). Tighten the type here so only the supported locale codes are allowed.
export const localizedRoutes = {
'/': ['en', 'ca', 'es'],
'/docs/': ['en', 'ca', 'es'],
'/changelog/': ['en'],
} as const satisfies Record<string, readonly string[]>;
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/website/src/styles/global.css`:
- Around line 166-167: Update the global caret-color rule near the existing
transparent caret declaration so editable elements retain a visible insertion
caret. Scope the transparent styling to non-editable selectable text, or add a
reset covering inputs, textareas, and [contenteditable] elements without
changing the intended Chromium/Firefox behavior elsewhere.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 31ed3269-23ae-452a-adeb-4e5564e65563
📒 Files selected for processing (1)
src/website/src/styles/global.css
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 37 changed files in this pull request and generated no new comments.
Suppressed comments (2)
tests/website/static-site.test.ts:137
getHreflangIssues()only compares thehrefvalues of<link rel="alternate">tags. That can miss regressions where the set/order ofhreflangattributes is wrong (e.g., missingx-defaultor incorrect locale codes) whilehrefs still match. Consider asserting bothhrefandhreflangsequences.
tests/website/static-site.test.ts:54getExpectedAlternateUrls()hard-codes locale prefixes (ca|es) and will throw a non-actionableCannot read properties of undefined (reading 'map')if a new indexable route is added but not listed inlocalizedRoutes. Deriving supported locale prefixes fromlocalizedRoutesand throwing a clearer error for missing routes will make these regression tests easier to maintain as locales/routes evolve.
Summary
Improves share previews, changelog navigation, canonical localized routing, delivery caching, and static SEO safeguards without changing Envilder product behavior.
Adds build-output regression checks so broken anchors, hreflang clusters, metadata, and eager media loading are caught before deployment.
Changes
Testing
pnpm lintpassespnpm testpasses (Docker/Testcontainers unavailable; 349 tests passed, 18 skipped, and 4 suites were blocked)pnpm --filter @envilder/website-tests testpassespnpm --filter @envilder/iac-tests testpassesRelated
N/A
Summary by CodeRabbit
New Features
Bug Fixes