create-brainerce-store 1.66.0 → 1.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/nextjs/base/.env.local.ejs +45 -24
- package/templates/nextjs/base/.eslintrc.json +51 -57
- package/templates/nextjs/base/AGENTS.md.ejs +107 -81
- package/templates/nextjs/base/CLAUDE.md.ejs +118 -92
- package/templates/nextjs/base/next.config.ts +103 -86
- package/templates/nextjs/base/scripts/fetch-store-info.mjs +104 -97
- package/templates/nextjs/base/src/app/agents.md/route.ts +4 -3
- package/templates/nextjs/base/src/app/api/auth/me/route.ts +65 -59
- package/templates/nextjs/base/src/app/api/store/[...path]/route.ts +255 -242
- package/templates/nextjs/base/src/app/blog/[slug]/page.tsx.ejs +311 -308
- package/templates/nextjs/base/src/app/blog/page.tsx.ejs +277 -276
- package/templates/nextjs/base/src/app/blog/rss.xml/route.ts +4 -3
- package/templates/nextjs/base/src/app/category/[slug]/page.tsx +6 -5
- package/templates/nextjs/base/src/app/faq/page.tsx.ejs +46 -46
- package/templates/nextjs/base/src/app/indexnow-key.txt/route.ts +25 -26
- package/templates/nextjs/base/src/app/layout.tsx.ejs +273 -257
- package/templates/nextjs/base/src/app/llms.txt/route.ts +4 -3
- package/templates/nextjs/base/src/app/opengraph-image.tsx +1 -1
- package/templates/nextjs/base/src/app/page.tsx +65 -64
- package/templates/nextjs/base/src/app/pages/[slug]/page.tsx.ejs +92 -92
- package/templates/nextjs/base/src/app/products/[slug]/page.tsx +6 -5
- package/templates/nextjs/base/src/app/robots.ts +3 -2
- package/templates/nextjs/base/src/app/sitemap.ts +4 -3
- package/templates/nextjs/base/src/components/checkout/custom-fields-step.tsx +294 -292
- package/templates/nextjs/base/src/components/seo/article-json-ld.tsx +60 -59
- package/templates/nextjs/base/src/components/seo/category-json-ld.tsx +60 -61
- package/templates/nextjs/base/src/components/seo/product-json-ld.tsx +2 -1
- package/templates/nextjs/base/src/core/lib/brainerce.server.ts +81 -0
- package/templates/nextjs/base/src/core/lib/brainerce.ts.ejs +60 -110
- package/templates/nextjs/base/src/core/lib/site-url.ts +197 -0
- package/templates/nextjs/base/src/ui/product/review-form.tsx +294 -294
- package/templates/nextjs/base/src/ui/product/reviews-section.tsx.ejs +108 -108
- package/templates/nextjs/designs/atelier/app-overlay/layout.tsx.ejs +301 -285
- package/templates/nextjs/designs/atelier/ui/layout/faq-section.tsx +97 -96
- package/templates/nextjs/designs/atelier/ui/layout/site-footer.tsx.ejs +142 -141
- package/templates/nextjs/designs/atelier/ui/layout/site-header.tsx.ejs +139 -138
- package/templates/nextjs/designs/atelier/ui/product/review-form.tsx +295 -267
- package/templates/nextjs/designs/atelier/ui/product/reviews-section.tsx.ejs +148 -148
- package/templates/nextjs/ui-canvas/layout/faq-section.tsx.ejs +72 -71
- package/templates/nextjs/ui-canvas/layout/site-footer.tsx.ejs +83 -82
- package/templates/nextjs/ui-canvas/layout/site-header.tsx.ejs +120 -119
- package/templates/nextjs/ui-canvas/product/faq-section.tsx.ejs +54 -0
- package/templates/nextjs/ui-canvas/product/review-form.tsx +267 -266
- package/templates/nextjs/ui-canvas/product/reviews-section.tsx.ejs +96 -96
|
@@ -1,64 +1,65 @@
|
|
|
1
|
-
import type { Metadata } from 'next';
|
|
2
|
-
import { getServerClient } from '@/core/lib/brainerce';
|
|
3
|
-
import { buildMetaDescription } from '@/core/lib/seo';
|
|
4
|
-
import { OrganizationJsonLd } from '@/components/seo/organization-json-ld';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
client.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
1
|
+
import type { Metadata } from 'next';
|
|
2
|
+
import { getServerClient } from '@/core/lib/brainerce.server';
|
|
3
|
+
import { buildMetaDescription } from '@/core/lib/seo';
|
|
4
|
+
import { OrganizationJsonLd } from '@/components/seo/organization-json-ld';
|
|
5
|
+
import { getCanonicalSiteUrl } from '@/core/lib/site-url';
|
|
6
|
+
import { RichTextBlock } from '@/ui/layout/rich-text-block';
|
|
7
|
+
import { HomeClient } from '@/ui/home/home-client';
|
|
8
|
+
|
|
9
|
+
// Build homepage metadata server-side so Google, Facebook, WhatsApp etc.
|
|
10
|
+
// see a real <meta name="description"> on first request. Falls back to the
|
|
11
|
+
// store name when the merchant hasn't authored a meta description yet.
|
|
12
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
13
|
+
try {
|
|
14
|
+
const storeInfo = await (await getServerClient()).getStoreInfo();
|
|
15
|
+
const description = buildMetaDescription(storeInfo.metaDescription) || undefined;
|
|
16
|
+
return {
|
|
17
|
+
title: storeInfo.name,
|
|
18
|
+
description,
|
|
19
|
+
openGraph: {
|
|
20
|
+
title: storeInfo.name,
|
|
21
|
+
description,
|
|
22
|
+
type: 'website',
|
|
23
|
+
},
|
|
24
|
+
twitter: {
|
|
25
|
+
card: 'summary_large_image',
|
|
26
|
+
title: storeInfo.name,
|
|
27
|
+
description,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
} catch {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default async function HomePage() {
|
|
36
|
+
// Fetch store info server-side so the JSON-LD is in the initial HTML —
|
|
37
|
+
// crawlers and AI Overviews see it without needing to execute JavaScript.
|
|
38
|
+
// Failure here is non-fatal: the client component re-fetches via the
|
|
39
|
+
// StoreProvider so the page still renders.
|
|
40
|
+
//
|
|
41
|
+
// Also fetch the merchant's homepage intro (RICH_TEXT key='main'). When
|
|
42
|
+
// present, it renders as a sanitized HTML block above the product grid —
|
|
43
|
+
// typical use is brand introduction, value-prop copy, or a seasonal
|
|
44
|
+
// announcement that needs more formatting than the AnnouncementBar.
|
|
45
|
+
// When absent, RichTextBlock returns null cleanly so the layout collapses
|
|
46
|
+
// to just the product grid.
|
|
47
|
+
const client = await getServerClient();
|
|
48
|
+
const [storeInfo, intro] = await Promise.all([
|
|
49
|
+
client.getStoreInfo().catch(() => null),
|
|
50
|
+
client.content.richText.get('main').catch(() => null),
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
const baseUrl = await getCanonicalSiteUrl();
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<>
|
|
57
|
+
{storeInfo && baseUrl ? <OrganizationJsonLd storeInfo={storeInfo} baseUrl={baseUrl} /> : null}
|
|
58
|
+
<RichTextBlock
|
|
59
|
+
block={intro}
|
|
60
|
+
className="prose prose-sm dark:prose-invert mx-auto max-w-3xl px-4 py-8 sm:px-6 lg:px-8"
|
|
61
|
+
/>
|
|
62
|
+
<HomeClient />
|
|
63
|
+
</>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Static page route — fully driven by the merchant's dashboard.
|
|
3
|
-
*
|
|
4
|
-
* The merchant creates pages in the Brainerce dashboard under
|
|
5
|
-
* Sell → Content → Page (one row per page, each with its own `slug`)
|
|
6
|
-
*
|
|
7
|
-
* This catch-all renders any PAGE by its `data.slug`. `/pages/about` →
|
|
8
|
-
* fetches PAGE with slug='about'. Mounting under `/pages/` (rather than
|
|
9
|
-
* the root) keeps the route from clashing with existing static routes
|
|
10
|
-
* like /cart, /login, /products.
|
|
11
|
-
*
|
|
12
|
-
* Security: page HTML is sanitized via `sanitizeHtml` before injecting.
|
|
13
|
-
*/
|
|
14
|
-
import * as React from 'react';
|
|
15
|
-
import type { Metadata } from 'next';
|
|
16
|
-
import { notFound } from 'next/navigation';
|
|
17
|
-
|
|
18
|
-
import { getServerClient } from '@/core/lib/brainerce';
|
|
19
|
-
import { sanitizeHtml } from '@/core/lib/sanitize';
|
|
20
|
-
import { decodeSlug } from '@/core/lib/utils';
|
|
21
|
-
|
|
22
|
-
<% if (i18nEnabled) { %>
|
|
23
|
-
type PageProps = {
|
|
24
|
-
params: Promise<{ locale: string; slug: string }>;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
|
28
|
-
const { locale, slug: rawSlug } = await params;
|
|
29
|
-
const slug = decodeSlug(rawSlug);
|
|
30
|
-
const page = await getServerClient(locale).content.page.getBySlug(slug, locale).catch(() => null);
|
|
31
|
-
if (!page) return { title: slug };
|
|
32
|
-
const seo = page.data.seo ?? {};
|
|
33
|
-
return {
|
|
34
|
-
title: seo.title ?? page.data.title,
|
|
35
|
-
description: seo.description,
|
|
36
|
-
openGraph: seo.ogImage ? { images: [{ url: seo.ogImage }] } : undefined,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export default async function StaticPage({ params }: PageProps) {
|
|
41
|
-
const { locale, slug: rawSlug } = await params;
|
|
42
|
-
const slug = decodeSlug(rawSlug);
|
|
43
|
-
const page = await getServerClient(locale).content.page.getBySlug(slug, locale).catch(() => null);
|
|
44
|
-
if (!page) notFound();
|
|
45
|
-
return (
|
|
46
|
-
<article className="mx-auto max-w-3xl px-4 py-10 sm:px-6 lg:px-8">
|
|
47
|
-
<h1 className="text-foreground mb-6 text-2xl font-semibold sm:text-3xl">
|
|
48
|
-
{page.data.title}
|
|
49
|
-
</h1>
|
|
50
|
-
<div
|
|
51
|
-
className="prose prose-sm dark:prose-invert max-w-none"
|
|
52
|
-
dangerouslySetInnerHTML={{ __html: sanitizeHtml(page.data.html) }}
|
|
53
|
-
/>
|
|
54
|
-
</article>
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
<% } else { %>
|
|
58
|
-
type PageProps = {
|
|
59
|
-
params: Promise<{ slug: string }>;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
|
63
|
-
const { slug: rawSlug } = await params;
|
|
64
|
-
const slug = decodeSlug(rawSlug);
|
|
65
|
-
const page = await getServerClient().content.page.getBySlug(slug).catch(() => null);
|
|
66
|
-
if (!page) return { title: slug };
|
|
67
|
-
const seo = page.data.seo ?? {};
|
|
68
|
-
return {
|
|
69
|
-
title: seo.title ?? page.data.title,
|
|
70
|
-
description: seo.description,
|
|
71
|
-
openGraph: seo.ogImage ? { images: [{ url: seo.ogImage }] } : undefined,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export default async function StaticPage({ params }: PageProps) {
|
|
76
|
-
const { slug: rawSlug } = await params;
|
|
77
|
-
const slug = decodeSlug(rawSlug);
|
|
78
|
-
const page = await getServerClient().content.page.getBySlug(slug).catch(() => null);
|
|
79
|
-
if (!page) notFound();
|
|
80
|
-
return (
|
|
81
|
-
<article className="mx-auto max-w-3xl px-4 py-10 sm:px-6 lg:px-8">
|
|
82
|
-
<h1 className="text-foreground mb-6 text-2xl font-semibold sm:text-3xl">
|
|
83
|
-
{page.data.title}
|
|
84
|
-
</h1>
|
|
85
|
-
<div
|
|
86
|
-
className="prose prose-sm dark:prose-invert max-w-none"
|
|
87
|
-
dangerouslySetInnerHTML={{ __html: sanitizeHtml(page.data.html) }}
|
|
88
|
-
/>
|
|
89
|
-
</article>
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
<% } %>
|
|
1
|
+
/**
|
|
2
|
+
* Static page route — fully driven by the merchant's dashboard.
|
|
3
|
+
*
|
|
4
|
+
* The merchant creates pages in the Brainerce dashboard under
|
|
5
|
+
* Sell → Content → Page (one row per page, each with its own `slug`)
|
|
6
|
+
*
|
|
7
|
+
* This catch-all renders any PAGE by its `data.slug`. `/pages/about` →
|
|
8
|
+
* fetches PAGE with slug='about'. Mounting under `/pages/` (rather than
|
|
9
|
+
* the root) keeps the route from clashing with existing static routes
|
|
10
|
+
* like /cart, /login, /products.
|
|
11
|
+
*
|
|
12
|
+
* Security: page HTML is sanitized via `sanitizeHtml` before injecting.
|
|
13
|
+
*/
|
|
14
|
+
import * as React from 'react';
|
|
15
|
+
import type { Metadata } from 'next';
|
|
16
|
+
import { notFound } from 'next/navigation';
|
|
17
|
+
|
|
18
|
+
import { getServerClient } from '@/core/lib/brainerce.server';
|
|
19
|
+
import { sanitizeHtml } from '@/core/lib/sanitize';
|
|
20
|
+
import { decodeSlug } from '@/core/lib/utils';
|
|
21
|
+
|
|
22
|
+
<% if (i18nEnabled) { %>
|
|
23
|
+
type PageProps = {
|
|
24
|
+
params: Promise<{ locale: string; slug: string }>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
|
28
|
+
const { locale, slug: rawSlug } = await params;
|
|
29
|
+
const slug = decodeSlug(rawSlug);
|
|
30
|
+
const page = await (await getServerClient(locale)).content.page.getBySlug(slug, locale).catch(() => null);
|
|
31
|
+
if (!page) return { title: slug };
|
|
32
|
+
const seo = page.data.seo ?? {};
|
|
33
|
+
return {
|
|
34
|
+
title: seo.title ?? page.data.title,
|
|
35
|
+
description: seo.description,
|
|
36
|
+
openGraph: seo.ogImage ? { images: [{ url: seo.ogImage }] } : undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default async function StaticPage({ params }: PageProps) {
|
|
41
|
+
const { locale, slug: rawSlug } = await params;
|
|
42
|
+
const slug = decodeSlug(rawSlug);
|
|
43
|
+
const page = await (await getServerClient(locale)).content.page.getBySlug(slug, locale).catch(() => null);
|
|
44
|
+
if (!page) notFound();
|
|
45
|
+
return (
|
|
46
|
+
<article className="mx-auto max-w-3xl px-4 py-10 sm:px-6 lg:px-8">
|
|
47
|
+
<h1 className="text-foreground mb-6 text-2xl font-semibold sm:text-3xl">
|
|
48
|
+
{page.data.title}
|
|
49
|
+
</h1>
|
|
50
|
+
<div
|
|
51
|
+
className="prose prose-sm dark:prose-invert max-w-none"
|
|
52
|
+
dangerouslySetInnerHTML={{ __html: sanitizeHtml(page.data.html) }}
|
|
53
|
+
/>
|
|
54
|
+
</article>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
<% } else { %>
|
|
58
|
+
type PageProps = {
|
|
59
|
+
params: Promise<{ slug: string }>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
|
63
|
+
const { slug: rawSlug } = await params;
|
|
64
|
+
const slug = decodeSlug(rawSlug);
|
|
65
|
+
const page = await (await getServerClient()).content.page.getBySlug(slug).catch(() => null);
|
|
66
|
+
if (!page) return { title: slug };
|
|
67
|
+
const seo = page.data.seo ?? {};
|
|
68
|
+
return {
|
|
69
|
+
title: seo.title ?? page.data.title,
|
|
70
|
+
description: seo.description,
|
|
71
|
+
openGraph: seo.ogImage ? { images: [{ url: seo.ogImage }] } : undefined,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default async function StaticPage({ params }: PageProps) {
|
|
76
|
+
const { slug: rawSlug } = await params;
|
|
77
|
+
const slug = decodeSlug(rawSlug);
|
|
78
|
+
const page = await (await getServerClient()).content.page.getBySlug(slug).catch(() => null);
|
|
79
|
+
if (!page) notFound();
|
|
80
|
+
return (
|
|
81
|
+
<article className="mx-auto max-w-3xl px-4 py-10 sm:px-6 lg:px-8">
|
|
82
|
+
<h1 className="text-foreground mb-6 text-2xl font-semibold sm:text-3xl">
|
|
83
|
+
{page.data.title}
|
|
84
|
+
</h1>
|
|
85
|
+
<div
|
|
86
|
+
className="prose prose-sm dark:prose-invert max-w-none"
|
|
87
|
+
dangerouslySetInnerHTML={{ __html: sanitizeHtml(page.data.html) }}
|
|
88
|
+
/>
|
|
89
|
+
</article>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
<% } %>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Metadata } from 'next';
|
|
2
2
|
import { notFound, permanentRedirect } from 'next/navigation';
|
|
3
3
|
import { getProductPriceInfo } from 'brainerce';
|
|
4
|
-
import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce';
|
|
4
|
+
import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce.server';
|
|
5
5
|
import { resolveCurrency } from '@/core/lib/resolve-currency';
|
|
6
6
|
import { buildMetaDescription } from '@/core/lib/seo';
|
|
7
7
|
import { decodeSlug } from '@/core/lib/utils';
|
|
8
8
|
import { ProductJsonLd } from '@/components/seo/product-json-ld';
|
|
9
|
+
import { getCanonicalSiteUrl } from '@/core/lib/site-url';
|
|
9
10
|
import { Breadcrumbs } from '@/components/seo/breadcrumbs';
|
|
10
11
|
import { ReviewsSection } from '@/ui/product/reviews-section';
|
|
11
12
|
import { ProductFaqSection } from '@/ui/product/faq-section';
|
|
@@ -38,7 +39,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|
|
38
39
|
const slug = decodeSlug(rawSlug);
|
|
39
40
|
|
|
40
41
|
try {
|
|
41
|
-
const client = getServerClient(locale);
|
|
42
|
+
const client = await getServerClient(locale);
|
|
42
43
|
// Fetch product + store info in parallel; storeInfo gives us the live
|
|
43
44
|
// currency for OG price tags (with env-var + USD fallbacks so we never
|
|
44
45
|
// silently emit a wrong currency when the API is briefly unreachable).
|
|
@@ -70,7 +71,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|
|
70
71
|
// Locales come from storeInfo.i18n — already fetched above, zero extra cost.
|
|
71
72
|
const supportedLocales = storeInfo?.i18n?.supportedLocales ?? [];
|
|
72
73
|
const defaultLoc = storeInfo?.i18n?.defaultLocale ?? storeInfo?.language ?? '';
|
|
73
|
-
const baseUrl =
|
|
74
|
+
const baseUrl = await getCanonicalSiteUrl();
|
|
74
75
|
const baseSlug = product.slug || slug;
|
|
75
76
|
const localeSlugs = product.localeSlugs ?? {};
|
|
76
77
|
|
|
@@ -133,7 +134,7 @@ export default async function ProductDetailPage({ params }: Props) {
|
|
|
133
134
|
const { slug: rawSlug, locale } = await params;
|
|
134
135
|
const slug = decodeSlug(rawSlug);
|
|
135
136
|
|
|
136
|
-
const client = getServerClient(locale);
|
|
137
|
+
const client = await getServerClient(locale);
|
|
137
138
|
let product;
|
|
138
139
|
try {
|
|
139
140
|
product = await client.getProductBySlug(slug);
|
|
@@ -153,7 +154,7 @@ export default async function ProductDetailPage({ params }: Props) {
|
|
|
153
154
|
notFound();
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
const baseUrl =
|
|
157
|
+
const baseUrl = await getCanonicalSiteUrl();
|
|
157
158
|
const productUrl = `${baseUrl}/products/${slug}`;
|
|
158
159
|
// Reuse the cached storeInfo from generateMetadata's call — React cache()
|
|
159
160
|
// collapses both into one backend request per render. resolveCurrency owns
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MetadataRoute } from 'next';
|
|
2
|
+
import { getCanonicalSiteUrl } from '@/core/lib/site-url';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* robots.txt with an explicit AI-crawler policy.
|
|
@@ -47,8 +48,8 @@ const AI_TRAINING_BOTS = [
|
|
|
47
48
|
'Applebot-Extended',
|
|
48
49
|
];
|
|
49
50
|
|
|
50
|
-
export default function robots(): MetadataRoute.Robots {
|
|
51
|
-
const baseUrl =
|
|
51
|
+
export default async function robots(): Promise<MetadataRoute.Robots> {
|
|
52
|
+
const baseUrl = await getCanonicalSiteUrl();
|
|
52
53
|
|
|
53
54
|
return {
|
|
54
55
|
rules: [
|
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
getCategorySitemapEntries,
|
|
5
5
|
getProductSitemapEntries,
|
|
6
6
|
} from 'brainerce';
|
|
7
|
-
import { getServerClient } from '@/core/lib/brainerce';
|
|
7
|
+
import { getServerClient } from '@/core/lib/brainerce.server';
|
|
8
|
+
import { getCanonicalSiteUrl } from '@/core/lib/site-url';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Storefront sitemap: static pages + every published product, category,
|
|
@@ -16,9 +17,9 @@ import { getServerClient } from '@/core/lib/brainerce';
|
|
|
16
17
|
* The helper uses a dedicated lightweight endpoint with a 5000-product cap.
|
|
17
18
|
*/
|
|
18
19
|
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
19
|
-
const baseUrl =
|
|
20
|
+
const baseUrl = await getCanonicalSiteUrl();
|
|
20
21
|
|
|
21
|
-
const client = getServerClient();
|
|
22
|
+
const client = await getServerClient();
|
|
22
23
|
const storeInfo = await client.getStoreInfo().catch(() => null);
|
|
23
24
|
const supportedLocales = storeInfo?.i18n?.supportedLocales ?? [];
|
|
24
25
|
const defaultLoc = storeInfo?.i18n?.defaultLocale ?? storeInfo?.language ?? '';
|