create-brainerce-store 1.47.0 → 1.48.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
CHANGED
|
@@ -31,7 +31,7 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "create-brainerce-store",
|
|
34
|
-
version: "1.
|
|
34
|
+
version: "1.48.0",
|
|
35
35
|
description: "Scaffold a production-ready e-commerce storefront connected to Brainerce",
|
|
36
36
|
bin: {
|
|
37
37
|
"create-brainerce-store": "dist/index.js"
|
package/package.json
CHANGED
|
@@ -13,6 +13,24 @@ type Props = {
|
|
|
13
13
|
params: Promise<{ slug: string; locale?: string }>;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
function buildHreflang(
|
|
17
|
+
baseUrl: string,
|
|
18
|
+
baseSlug: string,
|
|
19
|
+
localeSlugs: Record<string, string>,
|
|
20
|
+
locales: string[],
|
|
21
|
+
defaultLoc: string
|
|
22
|
+
): Record<string, string> {
|
|
23
|
+
const langs: Record<string, string> = {};
|
|
24
|
+
for (const loc of locales) {
|
|
25
|
+
const locSlug = localeSlugs[loc] || baseSlug;
|
|
26
|
+
const path = loc === defaultLoc ? `/products/${locSlug}` : `/${loc}/products/${locSlug}`;
|
|
27
|
+
langs[loc] = `${baseUrl}${path}`;
|
|
28
|
+
}
|
|
29
|
+
// x-default points to the default-locale canonical (no prefix)
|
|
30
|
+
langs['x-default'] = `${baseUrl}/products/${localeSlugs[defaultLoc] || baseSlug}`;
|
|
31
|
+
return langs;
|
|
32
|
+
}
|
|
33
|
+
|
|
16
34
|
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|
17
35
|
const { slug: rawSlug, locale } = await params;
|
|
18
36
|
const slug = decodeSlug(rawSlug);
|
|
@@ -46,11 +64,32 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|
|
46
64
|
const inStock = product.inventory?.canPurchase !== false;
|
|
47
65
|
const brandName = (product as { brand?: { name?: string } | null }).brand?.name;
|
|
48
66
|
|
|
67
|
+
// Multilingual SEO: hreflang tags + correct canonical per locale.
|
|
68
|
+
// Locales come from storeInfo.i18n — already fetched above, zero extra cost.
|
|
69
|
+
const supportedLocales = storeInfo?.i18n?.supportedLocales ?? [];
|
|
70
|
+
const defaultLoc = storeInfo?.i18n?.defaultLocale ?? storeInfo?.language ?? '';
|
|
71
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || '';
|
|
72
|
+
const baseSlug = product.slug || slug;
|
|
73
|
+
const localeSlugs = product.localeSlugs ?? {};
|
|
74
|
+
|
|
75
|
+
// Canonical: use the locale-specific slug for this locale when available.
|
|
76
|
+
const canonicalSlug = (locale && localeSlugs[locale]) || baseSlug;
|
|
77
|
+
const canonicalPath =
|
|
78
|
+
locale && locale !== defaultLoc
|
|
79
|
+
? `/${locale}/products/${canonicalSlug}`
|
|
80
|
+
: `/products/${canonicalSlug}`;
|
|
81
|
+
|
|
82
|
+
const hreflangLanguages =
|
|
83
|
+
supportedLocales.length > 1
|
|
84
|
+
? buildHreflang(baseUrl, baseSlug, localeSlugs, supportedLocales, defaultLoc)
|
|
85
|
+
: undefined;
|
|
86
|
+
|
|
49
87
|
return {
|
|
50
88
|
title: seoTitle,
|
|
51
89
|
description: seoDescription,
|
|
52
90
|
alternates: {
|
|
53
|
-
canonical:
|
|
91
|
+
canonical: canonicalPath,
|
|
92
|
+
...(hreflangLanguages ? { languages: hreflangLanguages } : {}),
|
|
54
93
|
},
|
|
55
94
|
openGraph: {
|
|
56
95
|
title: seoTitle,
|
|
@@ -1,25 +1,49 @@
|
|
|
1
|
-
import type { MetadataRoute } from 'next';
|
|
2
|
-
import { getServerClient } from '@/lib/brainerce';
|
|
3
|
-
|
|
4
|
-
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
5
|
-
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
import type { MetadataRoute } from 'next';
|
|
2
|
+
import { getServerClient } from '@/lib/brainerce';
|
|
3
|
+
|
|
4
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
5
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';
|
|
6
|
+
|
|
7
|
+
const client = getServerClient();
|
|
8
|
+
const storeInfo = await client.getStoreInfo().catch(() => null);
|
|
9
|
+
const supportedLocales = storeInfo?.i18n?.supportedLocales ?? [];
|
|
10
|
+
const defaultLoc = storeInfo?.i18n?.defaultLocale ?? storeInfo?.language ?? '';
|
|
11
|
+
const isMultiLocale = supportedLocales.length > 1;
|
|
12
|
+
|
|
13
|
+
const staticPages: MetadataRoute.Sitemap = isMultiLocale
|
|
14
|
+
? supportedLocales.flatMap((loc) => {
|
|
15
|
+
const prefix = loc === defaultLoc ? '' : `/${loc}`;
|
|
16
|
+
return [
|
|
17
|
+
{ url: `${baseUrl}${prefix || '/'}`, lastModified: new Date(), priority: 1 },
|
|
18
|
+
{ url: `${baseUrl}${prefix}/products`, lastModified: new Date(), priority: 0.9 },
|
|
19
|
+
];
|
|
20
|
+
})
|
|
21
|
+
: [
|
|
22
|
+
{ url: baseUrl, lastModified: new Date(), priority: 1 },
|
|
23
|
+
{ url: `${baseUrl}/products`, lastModified: new Date(), priority: 0.9 },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const { data: products } = await client.getProducts({ limit: 1000 });
|
|
28
|
+
|
|
29
|
+
const productPages: MetadataRoute.Sitemap = products.flatMap((product) => {
|
|
30
|
+
const baseSlug = product.slug || product.id;
|
|
31
|
+
const localeSlugs = product.localeSlugs ?? {};
|
|
32
|
+
const lastMod = product.updatedAt ? new Date(product.updatedAt) : new Date();
|
|
33
|
+
|
|
34
|
+
if (!isMultiLocale) {
|
|
35
|
+
return [{ url: `${baseUrl}/products/${baseSlug}`, lastModified: lastMod, priority: 0.8 }];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return supportedLocales.map((loc) => {
|
|
39
|
+
const locSlug = localeSlugs[loc] || baseSlug;
|
|
40
|
+
const path = loc === defaultLoc ? `/products/${locSlug}` : `/${loc}/products/${locSlug}`;
|
|
41
|
+
return { url: `${baseUrl}${path}`, lastModified: lastMod, priority: 0.8 };
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return [...staticPages, ...productPages];
|
|
46
|
+
} catch {
|
|
47
|
+
return staticPages;
|
|
48
|
+
}
|
|
49
|
+
}
|