Skip to content

Centralize store config into src/lib/store.ts#108

Merged
damianlegawiec merged 6 commits into
mainfrom
refactor/centralize-store-config
Apr 9, 2026
Merged

Centralize store config into src/lib/store.ts#108
damianlegawiec merged 6 commits into
mainfrom
refactor/centralize-store-config

Conversation

@Cichorek
Copy link
Copy Markdown
Contributor

@Cichorek Cichorek commented Apr 3, 2026

Summary

  • Extracts all store-level env var access (NEXT_PUBLIC_STORE_NAME, NEXT_PUBLIC_STORE_DESCRIPTION, NEXT_PUBLIC_SITE_URL, NEXT_PUBLIC_DEFAULT_COUNTRY, NEXT_PUBLIC_DEFAULT_LOCALE, EMAIL_FROM) into a single src/lib/store.ts module with consistent fallback values
  • Removes duplicated process.env access across 12+ files — only src/lib/store.ts reads these vars now
  • Builds ensureProtocol into getStoreUrl() so callers no longer need to wrap it manually
  • Removes re-exports from seo.ts — store config has a single import path (@/lib/store)
  • Adds console.warn when EMAIL_FROM fallback is used with Resend (would silently fail with 403/422)
  • Adds console.warn when SITE_URL is empty in production (email links would be broken)

Test plan

  • Verify npm run check (Biome) passes
  • Verify npm run build completes without errors
  • Verify storefront renders correctly (header/footer show store name)
  • Verify sitemap generation works (/sitemap/0.xml)
  • Verify email sending in dev mode (trigger webhook, check .next/emails/)
  • Verify no process.env.NEXT_PUBLIC_STORE_* access outside src/lib/store.ts via grep

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor

    • Centralized store configuration so name, description, locale/country and site URL are sourced consistently across site, metadata, routing, sitemap/robots, and emails.
    • Email templates now derive store info at runtime (store name/URL become optional props); email link rendering is conditional when site URL is absent.
  • Chores

    • Deduplicated and standardized NEXT_PUBLIC_SITE_URL in example config.
    • Sitemap/robots only publish sitemap/host when a site URL is configured; sender fallback/warning for emails standardized.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
storefront Ready Ready Preview, Comment Apr 8, 2026 0:32am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Centralizes store configuration into a new src/lib/store.ts, replaces scattered env reads with unified getters across layouts, metadata, emails, webhooks, sitemap/robots, middleware, components, and deduplicates NEXT_PUBLIC_SITE_URL in .env.example.

Changes

Cohort / File(s) Summary
Environment config
\.env.example
Moved NEXT_PUBLIC_SITE_URL into "Store defaults", removed duplicate definitions and related comment.
New store helpers
src/lib/store.ts
Added centralized helpers: ensureProtocol, getStoreUrl, getStoreName, getStoreDescription, getDefaultCountry, getDefaultLocale, getStoreSeoTitle, getStoreMetaDescription, getStoreEmailFrom, isStoreEmailFromFallback.
SEO utilities
src/lib/seo.ts
Removed local store helper implementations and now import those helpers from @/lib/store; several previously-exported helper functions were removed.
Metadata generators
src/lib/metadata/...
src/lib/metadata/home.ts, .../store.ts, .../categories.ts, .../category.ts, .../product.ts, .../products.ts
Switched imports to @/lib/store for store URL/name/description/SEO values; removed some ensureProtocol wrappings; core metadata logic retained.
Layouts & root
src/app/layout.tsx, src/app/[country]/[locale]/layout.tsx, src/app/[country]/[locale]/(checkout)/layout.tsx
Replaced direct process.env reads and inline DEFAULT_* constants with calls to getStoreName(), getStoreDescription(), getDefaultCountry(), getDefaultLocale().
Storefront pages
src/app/[country]/[locale]/(storefront)/page.tsx, .../c/[...permalink]/page.tsx, .../products/[slug]/page.tsx, .../policies/[slug]/page.tsx
Updated imports to use store helpers from @/lib/store; behavior unchanged beyond import/source changes.
Sitemap & robots
src/app/sitemap.ts, src/app/robots.ts
Now source defaults from @/lib/store; removed ensureProtocol in some paths; robots now omits sitemap/host when baseUrl is absent.
Layout components & context
src/components/layout/Header.tsx, src/components/layout/Footer.tsx, src/contexts/StoreContext.tsx
Now obtain store name/description from @/lib/store; context/module-level initializers use helpers.
Emails & webhooks
src/lib/emails/send.ts, src/lib/webhooks/handlers.ts, emails/* previews, src/lib/emails/*
Email from prefers passed value or getStoreEmailFrom(); added conditional warnings when fallbacks are used; webhook handlers use getStoreUrl() and warn in production; email components made storeName/storeUrl optional and default to store helpers; previews updated to omit explicit store props.
Proxy / middleware
src/proxy.ts
Use getDefaultCountry() / getDefaultLocale() at init instead of precomputed env-derived constants.
Misc imports updated
src/app/robots.ts, src/lib/..., src/components/..., src/lib/metadata/...
Multiple files updated to import store helpers from @/lib/store instead of local/seo helpers; logic largely preserved.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • damianlegawiec

"🐰 I hopped through envs, tidy and spry,
One helper burrow where defaults lie.
No stray URLs, no scattered name,
A neat little module — tidy code's my game! 🥕"

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: centralizing store configuration into a new src/lib/store.ts module, which is the main refactoring objective throughout the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/centralize-store-config

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Extract all store-level env var access (NEXT_PUBLIC_STORE_NAME,
NEXT_PUBLIC_STORE_DESCRIPTION, NEXT_PUBLIC_SITE_URL,
NEXT_PUBLIC_DEFAULT_COUNTRY, NEXT_PUBLIC_DEFAULT_LOCALE, EMAIL_FROM)
into a single module with consistent fallback values.

- Remove duplicated process.env access across 12+ files
- Build ensureProtocol into getStoreUrl() so callers don't double-wrap
- Remove re-exports from seo.ts — store config lives in store.ts only
- Add console.warn when EMAIL_FROM fallback is used with Resend
- Add console.warn when SITE_URL is empty (broken email links)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/lib/metadata/home.ts (1)

14-16: Consider centralizing SEO-specific overrides.

The code still directly accesses process.env.STORE_SEO_TITLE and process.env.STORE_META_DESCRIPTION while using the centralized helpers as fallbacks. While this pattern allows SEO-specific title/description overrides, it introduces inconsistency with the PR's centralization goal.

Consider adding getStoreSeoTitle() and getStoreMetaDescription() helpers in @/lib/store that encapsulate this override logic, keeping all environment variable access in one place.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/metadata/home.ts` around lines 14 - 16, The code reads
process.env.STORE_SEO_TITLE and process.env.STORE_META_DESCRIPTION directly when
building storeName and description; centralize this override logic by adding new
helpers getStoreSeoTitle() and getStoreMetaDescription() in the existing
'@/lib/store' module that return the env override or fall back to
getStoreName()/getStoreDescription(), then replace direct env access in
src/lib/metadata/home.ts so storeName uses getStoreSeoTitle() and description
uses getStoreMetaDescription().
src/lib/webhooks/handlers.ts (1)

16-20: Scope this warning to production only.

Current condition can spam logs in test/CI environments. Restricting it to production aligns better with the stated intent.

💡 Proposed fix
-if (!SITE_URL) {
+if (!SITE_URL && process.env.NODE_ENV === "production") {
   console.warn(
-    "[webhooks] NEXT_PUBLIC_SITE_URL is not set — email links will be broken",
+    "[webhooks] NEXT_PUBLIC_SITE_URL / NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL is not set — email links will be broken",
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/webhooks/handlers.ts` around lines 16 - 20, The console warning
currently triggered by SITE_URL being unset should only run in production to
avoid noisy CI/test logs; update the conditional that checks SITE_URL (the
SITE_URL constant/variable in handlers.ts) to also require that
process.env.NODE_ENV === 'production' (or your existing isProduction helper)
before calling console.warn so the message only appears in production
environments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/robots.ts`:
- Around line 6-7: Guard against empty baseUrl returned by getStoreUrl() before
building sitemap URLs and the host: if baseUrl is falsy, avoid producing
relative sitemap locations and leave host undefined or set to null. Update the
logic around baseUrl, the call to generateSitemaps(), and where sitemap
URLs/host are composed (references: baseUrl, getStoreUrl, generateSitemaps,
sitemap/host) so that when baseUrl === "" you skip prefixing sitemap paths with
an empty host and emit absolute URLs only when baseUrl is present; apply the
same guard where sitemap/host are used later (the other occurrence around
sitemap handling noted in the diff).

In `@src/lib/store.ts`:
- Around line 52-54: getDefaultCountry currently returns
process.env.NEXT_PUBLIC_DEFAULT_COUNTRY as-is which violates the JSDoc
"lowercase" promise; update the getDefaultCountry function to normalize the env
value to lowercase (e.g., call .toLowerCase() on the env value) while safely
handling undefined/null (fall back to "us") so the function always returns a
lowercase string; ensure you apply this change in the getDefaultCountry
implementation.

---

Nitpick comments:
In `@src/lib/metadata/home.ts`:
- Around line 14-16: The code reads process.env.STORE_SEO_TITLE and
process.env.STORE_META_DESCRIPTION directly when building storeName and
description; centralize this override logic by adding new helpers
getStoreSeoTitle() and getStoreMetaDescription() in the existing '@/lib/store'
module that return the env override or fall back to
getStoreName()/getStoreDescription(), then replace direct env access in
src/lib/metadata/home.ts so storeName uses getStoreSeoTitle() and description
uses getStoreMetaDescription().

In `@src/lib/webhooks/handlers.ts`:
- Around line 16-20: The console warning currently triggered by SITE_URL being
unset should only run in production to avoid noisy CI/test logs; update the
conditional that checks SITE_URL (the SITE_URL constant/variable in handlers.ts)
to also require that process.env.NODE_ENV === 'production' (or your existing
isProduction helper) before calling console.warn so the message only appears in
production environments.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1ef6ab06-fbdd-4638-9763-95c38cf7d45d

📥 Commits

Reviewing files that changed from the base of the PR and between d1ae860 and 542934f.

📒 Files selected for processing (20)
  • .env.example
  • src/app/[country]/[locale]/(checkout)/layout.tsx
  • src/app/[country]/[locale]/(storefront)/c/[...permalink]/page.tsx
  • src/app/[country]/[locale]/(storefront)/page.tsx
  • src/app/[country]/[locale]/(storefront)/policies/[slug]/page.tsx
  • src/app/[country]/[locale]/(storefront)/products/[slug]/page.tsx
  • src/app/[country]/[locale]/layout.tsx
  • src/app/layout.tsx
  • src/app/robots.ts
  • src/app/sitemap.ts
  • src/components/layout/Footer.tsx
  • src/components/layout/Header.tsx
  • src/contexts/StoreContext.tsx
  • src/lib/emails/send.ts
  • src/lib/metadata/home.ts
  • src/lib/metadata/store.ts
  • src/lib/seo.ts
  • src/lib/store.ts
  • src/lib/webhooks/handlers.ts
  • src/proxy.ts

Comment thread src/app/robots.ts Outdated
Comment thread src/lib/store.ts
- robots.ts: skip sitemap/host fields when baseUrl is empty
- store.ts: normalize getDefaultCountry() to lowercase as JSDoc promises
- store.ts: add getStoreSeoTitle() and getStoreMetaDescription() helpers
- metadata/home.ts, metadata/store.ts: use new helpers instead of direct env access
- handlers.ts: only warn about missing SITE_URL in production

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Line 12: Move the environment key NEXT_PUBLIC_SITE_URL so it appears above the
NEXT_PUBLIC_STORE_* keys in the .env.example to satisfy dotenv-linter's
UnorderedKey rule; locate the NEXT_PUBLIC_SITE_URL entry and cut/paste it into
the NEXT_PUBLIC_* block immediately before the NEXT_PUBLIC_STORE_* entries so
all NEXT_PUBLIC_* keys are grouped together and the linter warning disappears.

In `@src/lib/emails/send.ts`:
- Around line 63-69: The code in send.ts reads process.env.EMAIL_FROM directly;
replace that check with a store helper to centralize env access: add a function
isStoreEmailFromFallback() to src/lib/store.ts (returns boolean for missing
EMAIL_FROM) and update send.ts to call isStoreEmailFromFallback() instead of
reading process.env.EMAIL_FROM, keeping the existing logic that logs the warning
when getStoreEmailFrom() is being used as a fallback.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33ed9f18-191b-4c84-9a16-c5e2d4654ccb

📥 Commits

Reviewing files that changed from the base of the PR and between 542934f and 8434f25.

📒 Files selected for processing (24)
  • .env.example
  • src/app/[country]/[locale]/(checkout)/layout.tsx
  • src/app/[country]/[locale]/(storefront)/c/[...permalink]/page.tsx
  • src/app/[country]/[locale]/(storefront)/page.tsx
  • src/app/[country]/[locale]/(storefront)/policies/[slug]/page.tsx
  • src/app/[country]/[locale]/(storefront)/products/[slug]/page.tsx
  • src/app/[country]/[locale]/layout.tsx
  • src/app/layout.tsx
  • src/app/robots.ts
  • src/app/sitemap.ts
  • src/components/layout/Footer.tsx
  • src/components/layout/Header.tsx
  • src/contexts/StoreContext.tsx
  • src/lib/emails/send.ts
  • src/lib/metadata/categories.ts
  • src/lib/metadata/category.ts
  • src/lib/metadata/home.ts
  • src/lib/metadata/product.ts
  • src/lib/metadata/products.ts
  • src/lib/metadata/store.ts
  • src/lib/seo.ts
  • src/lib/store.ts
  • src/lib/webhooks/handlers.ts
  • src/proxy.ts
✅ Files skipped from review due to trivial changes (13)
  • src/app/[country]/[locale]/(storefront)/policies/[slug]/page.tsx
  • src/lib/metadata/product.ts
  • src/lib/metadata/category.ts
  • src/lib/metadata/products.ts
  • src/app/[country]/[locale]/(storefront)/products/[slug]/page.tsx
  • src/app/[country]/[locale]/(storefront)/c/[...permalink]/page.tsx
  • src/lib/metadata/categories.ts
  • src/proxy.ts
  • src/lib/metadata/home.ts
  • src/lib/webhooks/handlers.ts
  • src/lib/store.ts
  • src/app/[country]/[locale]/(storefront)/page.tsx
  • src/app/[country]/[locale]/(checkout)/layout.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/app/robots.ts
  • src/app/layout.tsx
  • src/components/layout/Footer.tsx
  • src/components/layout/Header.tsx
  • src/contexts/StoreContext.tsx
  • src/lib/metadata/store.ts
  • src/app/[country]/[locale]/layout.tsx

Comment thread .env.example Outdated
Comment thread src/lib/emails/send.ts
Move NEXT_PUBLIC_SITE_URL before NEXT_PUBLIC_STORE_* in .env.example to
satisfy dotenv-linter's UnorderedKey rule, and add isStoreEmailFromFallback()
to src/lib/store.ts so send.ts no longer reads process.env.EMAIL_FROM directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/lib/webhooks/handlers.ts (1)

11-14: Consider adding explicit type annotations for module constants.

Per coding guidelines on strict TypeScript, consider adding explicit types:

-const STORE_NAME = getStoreName();
-const SITE_URL =
-  getStoreUrl() ||
+const STORE_NAME: string = getStoreName();
+const SITE_URL: string =
+  getStoreUrl() ||
   (process.env.NODE_ENV === "development" ? "http://localhost:3001" : "");

This is a minor refinement since TypeScript infers the types correctly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/webhooks/handlers.ts` around lines 11 - 14, Add explicit TypeScript
type annotations to the module constants: annotate STORE_NAME and SITE_URL with
string types (e.g. declare STORE_NAME: string and SITE_URL: string) where they
are initialized using getStoreName() and the getStoreUrl() || ... expression to
satisfy strict typing; ensure the annotations match the runtime fallback
behavior so SITE_URL is typed as string even when getStoreUrl() may return
undefined.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/lib/webhooks/handlers.ts`:
- Around line 11-14: Add explicit TypeScript type annotations to the module
constants: annotate STORE_NAME and SITE_URL with string types (e.g. declare
STORE_NAME: string and SITE_URL: string) where they are initialized using
getStoreName() and the getStoreUrl() || ... expression to satisfy strict typing;
ensure the annotations match the runtime fallback behavior so SITE_URL is typed
as string even when getStoreUrl() may return undefined.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 732d50df-abd6-4420-b99f-3c3e9b8d666b

📥 Commits

Reviewing files that changed from the base of the PR and between 8434f25 and 8ac9d11.

📒 Files selected for processing (7)
  • .env.example
  • src/app/robots.ts
  • src/lib/emails/send.ts
  • src/lib/metadata/home.ts
  • src/lib/metadata/store.ts
  • src/lib/store.ts
  • src/lib/webhooks/handlers.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/app/robots.ts
  • .env.example
  • src/lib/metadata/home.ts

Make storeName and storeUrl optional props with defaults from
getStoreName()/getStoreUrl() in all four email components, removing
the need for callers to pass them explicitly. Clean up handlers.ts
and email preview files accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/emails/order-confirmation.tsx`:
- Around line 60-61: The current assignment reintroduces a production-wide
hardcoded fallback for storeUrl; change the storeUrl assignment (where
getStoreUrl() is used) so the 'http://localhost:3001' fallback is only applied
in development. Replace the unconditional fallback with a conditional that uses
getStoreUrl() if present, otherwise returns the localhost only when
process.env.NODE_ENV === 'development' (and in production leave it undefined or
throw/raise an error so misconfiguration surfaces). Update any downstream uses
that assume storeUrl is always defined to handle the possibly undefined value
(or throw early) so production emails never contain the localhost link.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0f3caf5-b795-4612-9baf-ac47746263be

📥 Commits

Reviewing files that changed from the base of the PR and between 8ac9d11 and 8d1566c.

📒 Files selected for processing (9)
  • emails/order-canceled.tsx
  • emails/order-confirmation.tsx
  • emails/password-reset.tsx
  • emails/shipment-shipped.tsx
  • src/lib/emails/order-canceled.tsx
  • src/lib/emails/order-confirmation.tsx
  • src/lib/emails/password-reset.tsx
  • src/lib/emails/shipment-shipped.tsx
  • src/lib/webhooks/handlers.ts
💤 Files with no reviewable changes (3)
  • emails/order-confirmation.tsx
  • emails/order-canceled.tsx
  • emails/shipment-shipped.tsx

Comment thread src/lib/emails/order-confirmation.tsx Outdated
getStoreUrl() now returns http://localhost:3001 only in development;
in production it returns undefined so misconfiguration surfaces.
Email templates and handlers.ts no longer carry their own fallback.
Footer links and product links render conditionally when storeUrl is
undefined.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/emails/shipment-shipped.tsx`:
- Around line 113-119: The product link construction in the Link rendering (in
src/lib/emails/shipment-shipped.tsx) can produce malformed URLs when storeUrl
ends with '/' or item.slug starts with '/'; update the Link href logic to
normalize both parts before concatenation: trim trailing slashes from storeUrl
and leading slashes from item.slug (use the normalized storeUrl alone when
item.slug is falsy) so the final URL always has exactly one slash between host
and path.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7e7bb959-6038-46e8-87dc-32334e855f0c

📥 Commits

Reviewing files that changed from the base of the PR and between 8d1566c and f9462b1.

📒 Files selected for processing (6)
  • src/lib/emails/order-canceled.tsx
  • src/lib/emails/order-confirmation.tsx
  • src/lib/emails/password-reset.tsx
  • src/lib/emails/shipment-shipped.tsx
  • src/lib/store.ts
  • src/lib/webhooks/handlers.ts
✅ Files skipped from review due to trivial changes (1)
  • src/lib/store.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/emails/password-reset.tsx

Comment on lines +113 to +119
{storeUrl ? (
<Link
href={
item.slug
? `${storeUrl}/products/${item.slug}`
: storeUrl
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize product URL construction before rendering email links.

Current concatenation can emit malformed URLs when storeUrl ends with / or item.slug starts with /.

Proposed fix
-                          href={
-                            item.slug
-                              ? `${storeUrl}/products/${item.slug}`
-                              : storeUrl
-                          }
+                          href={
+                            item.slug
+                              ? new URL(
+                                  `/products/${item.slug.replace(/^\/+/, "")}`,
+                                  storeUrl,
+                                ).toString()
+                              : storeUrl
+                          }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{storeUrl ? (
<Link
href={
item.slug
? `${storeUrl}/products/${item.slug}`
: storeUrl
}
{storeUrl ? (
<Link
href={
item.slug
? new URL(
`/products/${item.slug.replace(/^\/+/, "")}`,
storeUrl,
).toString()
: storeUrl
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/emails/shipment-shipped.tsx` around lines 113 - 119, The product link
construction in the Link rendering (in src/lib/emails/shipment-shipped.tsx) can
produce malformed URLs when storeUrl ends with '/' or item.slug starts with '/';
update the Link href logic to normalize both parts before concatenation: trim
trailing slashes from storeUrl and leading slashes from item.slug (use the
normalized storeUrl alone when item.slug is falsy) so the final URL always has
exactly one slash between host and path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Product slugs, are creating on our backend - we trust format is good.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Cichorek, understood — since slugs are backend-generated and their format is trusted, no normalization is needed here. I'll keep this in mind for future reviews.

(╯°□°)╯


✏️ Learnings added
Learnt from: Cichorek
Repo: spree/storefront PR: 108
File: src/lib/emails/shipment-shipped.tsx:113-119
Timestamp: 2026-04-07T09:42:19.503Z
Learning: In spree/storefront, product slugs in email templates (e.g. src/lib/emails/shipment-shipped.tsx, order-confirmation.tsx) are generated by the backend and their format (no leading slashes, no double slashes) is trusted. Do not flag URL concatenation like `${storeUrl}/products/${item.slug}` as potentially malformed — no slug normalization is required.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: Cichorek
Repo: spree/storefront PR: 43
File: src/components/navigation/megaMenu/MegaMenu.tsx:186-204
Timestamp: 2026-03-04T13:40:29.672Z
Learning: In the spree/storefront repository, do not flag useEffect-based data fetching in client components (e.g., MegaMenu.tsx, Header.tsx) as violations. The project currently uses client-side data fetching in useEffect; server components data fetching is a future consideration. Treat useEffect fetching in client components as acceptable for now and focus reviews on other issues (dependencies, side effects, rendering correctness).

Resolve conflicts in 5 files, keeping centralized @/lib/store imports
while adopting main's i18n (next-intl) and SDK 0.21.0 changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@damianlegawiec damianlegawiec merged commit 3c123f7 into main Apr 9, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants