Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): fetch locales at build time #1631

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cuddly-boats-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": minor
---

fetch available locales at build time
3 changes: 3 additions & 0 deletions core/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ client/generated

# secrets
.catalyst

# Build config
build-config.json
4 changes: 2 additions & 2 deletions core/app/[locale]/(default)/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getTranslations, setRequestLocale } from 'next-intl/server';

import { Link } from '~/components/link';
import { Button } from '~/components/ui/button';
import { locales, LocaleType } from '~/i18n/routing';
import { locales } from '~/i18n/routing';

import { LoginForm } from './_components/login-form';

Expand All @@ -19,7 +19,7 @@ export async function generateMetadata({ params }: Props) {
}

interface Props {
params: Promise<{ locale: LocaleType }>;
params: Promise<{ locale: string }>;
}

export default async function Login({ params }: Props) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { getTranslations, setRequestLocale } from 'next-intl/server';

import { ProductCard } from '~/components/product-card';
import { Pagination } from '~/components/ui/pagination';
import { LocaleType } from '~/i18n/routing';

import { FacetedSearch } from '../../_components/faceted-search';
import { MobileSideNav } from '../../_components/mobile-side-nav';
Expand All @@ -16,7 +15,7 @@ import { getBrand } from './page-data';
interface Props {
params: Promise<{
slug: string;
locale: LocaleType;
locale: string;
}>;
searchParams: Promise<Record<string, string | string[] | undefined>>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { getTranslations, setRequestLocale } from 'next-intl/server';
import { Breadcrumbs } from '~/components/breadcrumbs';
import { ProductCard } from '~/components/product-card';
import { Pagination } from '~/components/ui/pagination';
import { LocaleType } from '~/i18n/routing';

import { FacetedSearch } from '../../_components/faceted-search';
import { MobileSideNav } from '../../_components/mobile-side-nav';
Expand All @@ -20,7 +19,7 @@ import { getCategoryPageData } from './page-data';
interface Props {
params: Promise<{
slug: string;
locale: LocaleType;
locale: string;
}>;
searchParams: Promise<Record<string, string | string[] | undefined>>;
}
Expand Down
4 changes: 1 addition & 3 deletions core/app/[locale]/(default)/account/(tabs)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { getTranslations, setRequestLocale } from 'next-intl/server';
import { PropsWithChildren } from 'react';

import { LocaleType } from '~/i18n/routing';

import { TabNavigation, TabType } from './_components/tab-navigation';

interface Props extends PropsWithChildren {
params: Promise<{ locale: LocaleType; tab?: TabType }>;
params: Promise<{ locale: string; tab?: TabType }>;
}

export default async function AccountTabLayout({ children, params }: Props) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { getTranslations, setRequestLocale } from 'next-intl/server';

import { LocaleType } from '~/i18n/routing';

import { TabHeading } from '../../_components/tab-heading';

import { ChangePasswordForm } from './_components/change-password-form';
Expand All @@ -15,7 +13,7 @@ export async function generateMetadata() {
}

interface Props {
params: Promise<{ locale: LocaleType }>;
params: Promise<{ locale: string }>;
}

export default async function ChangePassword({ params }: Props) {
Expand Down
3 changes: 1 addition & 2 deletions core/app/[locale]/(default)/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import { getTranslations } from 'next-intl/server';

import { BlogPostCard } from '~/components/blog-post-card';
import { Pagination } from '~/components/ui/pagination';
import { LocaleType } from '~/i18n/routing';

import { getBlogPosts } from './page-data';

interface Props {
params: Promise<{ locale: LocaleType }>;
params: Promise<{ locale: string }>;
searchParams: Promise<Record<string, string | string[] | undefined>>;
}

Expand Down
3 changes: 1 addition & 2 deletions core/app/[locale]/(default)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import { PropsWithChildren, Suspense } from 'react';
import { Footer } from '~/components/footer/footer';
import { Header, HeaderSkeleton } from '~/components/header';
import { Cart } from '~/components/header/cart';
import { LocaleType } from '~/i18n/routing';

interface Props extends PropsWithChildren {
params: Promise<{ locale: LocaleType }>;
params: Promise<{ locale: string }>;
}

export default async function DefaultLayout({ params, children }: Props) {
Expand Down
3 changes: 1 addition & 2 deletions core/app/[locale]/(default)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { revalidate } from '~/client/revalidate-target';
import { ProductCardCarousel } from '~/components/product-card-carousel';
import { ProductCardCarouselFragment } from '~/components/product-card-carousel/fragment';
import { Slideshow } from '~/components/slideshow';
import { LocaleType } from '~/i18n/routing';

const HomePageQuery = graphql(
`
Expand All @@ -35,7 +34,7 @@ const HomePageQuery = graphql(
);

interface Props {
params: Promise<{ locale: LocaleType }>;
params: Promise<{ locale: string }>;
}

export default async function Home({ params }: Props) {
Expand Down
3 changes: 1 addition & 2 deletions core/app/[locale]/(default)/product/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { getTranslations, setRequestLocale } from 'next-intl/server';
import { Suspense } from 'react';

import { Breadcrumbs } from '~/components/breadcrumbs';
import { LocaleType } from '~/i18n/routing';

import { Description } from './_components/description';
import { Details } from './_components/details';
Expand All @@ -17,7 +16,7 @@ import { Warranty } from './_components/warranty';
import { getProduct } from './page-data';

interface Props {
params: Promise<{ slug: string; locale: LocaleType }>;
params: Promise<{ slug: string; locale: string }>;
searchParams: Promise<Record<string, string | string[] | undefined>>;
}

Expand Down
4 changes: 2 additions & 2 deletions core/app/[locale]/(default)/product/[slug]/static/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getChannelIdFromLocale } from '~/channels.config';
import { client } from '~/client';
import { graphql } from '~/client/graphql';
import { revalidate as revalidateTarget } from '~/client/revalidate-target';
import { locales, LocaleType } from '~/i18n/routing';
import { locales } from '~/i18n/routing';

import ProductPage from '../page';
import { getProduct } from '../page-data';
Expand Down Expand Up @@ -60,7 +60,7 @@ export async function generateStaticParams() {
}

interface Props {
params: Promise<{ slug: string; locale: LocaleType }>;
params: Promise<{ slug: string; locale: string }>;
}

export async function generateMetadata(props: Props): Promise<Metadata> {
Expand Down
3 changes: 1 addition & 2 deletions core/app/[locale]/maintenance/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { client } from '~/client';
import { graphql } from '~/client/graphql';
import { StoreLogo } from '~/components/store-logo';
import { StoreLogoFragment } from '~/components/store-logo/fragment';
import { LocaleType } from '~/i18n/routing';

const MaintenancePageQuery = graphql(
`
Expand Down Expand Up @@ -38,7 +37,7 @@ const Container = ({ children }: { children: ReactNode }) => (
);

interface Props {
params: Promise<{ locale: LocaleType }>;
params: Promise<{ locale: string }>;
}

export default async function Maintenance({ params }: Props) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Link } from '~/components/link';
import { localeLanguageRegionMap, LocaleType } from '~/i18n/routing';
import { localeLanguageRegionMap } from '~/i18n/routing';
import { cn } from '~/lib/utils';

export const LocaleLink = ({ locale, selected }: { locale: string; selected: boolean }) => {
Expand All @@ -16,8 +16,7 @@ export const LocaleLink = ({ locale, selected }: { locale: string; selected: boo
selected && 'border-black',
)}
href="/"
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
locale={locale as LocaleType}
locale={locale}
>
<div className="flex h-full items-center gap-2">
<div className="text-2xl">{selectedLocale.flag}</div>
Expand Down
5 changes: 2 additions & 3 deletions core/app/[locale]/store-selector/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { graphql } from '~/client/graphql';
import { Link } from '~/components/link';
import { StoreLogo } from '~/components/store-logo';
import { StoreLogoFragment } from '~/components/store-logo/fragment';
import { locales, LocaleType } from '~/i18n/routing';
import { locales } from '~/i18n/routing';

import { LocaleLink } from './_components/locale-link';

Expand All @@ -31,7 +31,7 @@ export async function generateMetadata() {
}

interface Props {
params: Promise<{ locale: LocaleType }>;
params: Promise<{ locale: string }>;
}

export default async function StoreSelector({ params }: Props) {
Expand Down Expand Up @@ -61,7 +61,6 @@ export default async function StoreSelector({ params }: Props) {

<div className="grid grid-cols-1 gap-6 py-6 md:grid-cols-3 md:gap-11 lg:grid-cols-4 lg:gap-8">
{locales.map((locale) => (
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
<LocaleLink key={locale} locale={locale} selected={selectedLocale === locale} />
))}
</div>
Expand Down
16 changes: 16 additions & 0 deletions core/build-config/reader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import rawBuildConfig from './build-config.json';
import { buildConfigSchema, BuildConfigSchema } from './schema';

class BuildConfig {
private config = buildConfigSchema.parse(rawBuildConfig);

get<K extends keyof BuildConfigSchema>(key: K): BuildConfigSchema[K] {
if (key in this.config) {
return this.config[key];
}

throw new Error(`Key "${key}" not found in BuildConfig`);
}
}

export const buildConfig = new BuildConfig();
12 changes: 12 additions & 0 deletions core/build-config/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { z } from 'zod';

export const buildConfigSchema = z.object({
locales: z.array(
z.object({
code: z.string(),
isDefault: z.boolean(),
}),
),
});

export type BuildConfigSchema = z.infer<typeof buildConfigSchema>;
27 changes: 27 additions & 0 deletions core/build-config/writer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable no-console */
import { writeFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { z } from 'zod';

import { buildConfigSchema } from './schema';

const destinationPath = dirname(fileURLToPath(import.meta.url));
const CONFIG_FILE = join(destinationPath, 'build-config.json');

// This fn is only intended to be used in the build process (next.config.ts)
export async function writeBuildConfig(data: unknown) {
try {
buildConfigSchema.parse(data);

await writeFile(CONFIG_FILE, JSON.stringify(data), 'utf8');
} catch (error) {
if (error instanceof z.ZodError) {
console.error('Data validation failed:', error.errors);
} else {
console.error('Error writing build-config.json:', error);
}

throw error;
}
}
13 changes: 3 additions & 10 deletions core/channels.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
import { type LocaleType } from './i18n/routing';

export type RecordFromLocales = {
[K in LocaleType]: string;
};

// Set overrides per locale
const localeToChannelsMappings: Partial<RecordFromLocales> = {
const localeToChannelsMappings: Record<string, string> = {
// es: '12345',
};

function getChannelIdFromLocale(locale?: string) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return localeToChannelsMappings[locale as LocaleType] ?? process.env.BIGCOMMERCE_CHANNEL_ID;
function getChannelIdFromLocale(locale = '') {
return localeToChannelsMappings[locale] ?? process.env.BIGCOMMERCE_CHANNEL_ID;
}

export { getChannelIdFromLocale };
3 changes: 1 addition & 2 deletions core/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { createClient } from '@bigcommerce/catalyst-client';
import { headers } from 'next/headers';
import { getLocale } from 'next-intl/server';

import { getChannelIdFromLocale } from '~/channels.config';

import { getChannelIdFromLocale } from '../channels.config';
import { backendUserAgent } from '../userAgent';

export const client = createClient({
Expand Down
3 changes: 1 addition & 2 deletions core/components/ui/header/locale-switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useTranslations } from 'next-intl';
import { useMemo, useState } from 'react';

import { Link } from '~/components/link';
import { LocaleType } from '~/i18n/routing';

import { Button } from '../button';
import { Select } from '../form';
Expand All @@ -19,7 +18,7 @@ type LanguagesByRegionMap = Record<
>;

interface Locale {
id: LocaleType;
id: string;
region: string;
language: string;
flag: string;
Expand Down
5 changes: 2 additions & 3 deletions core/i18n/request.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { notFound } from 'next/navigation';
import { getRequestConfig } from 'next-intl/server';

import { locales, LocaleType } from './routing';
import { locales } from './routing';

export default getRequestConfig(async ({ requestLocale }) => {
const locale = await requestLocale;

// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
if (!locales.includes(locale as LocaleType)) {
if (!locale || !locales.includes(locale)) {
notFound();
}

Expand Down
Loading
Loading