Skip to content

Commit

Permalink
feat(core): fetch locales at build time
Browse files Browse the repository at this point in the history
  • Loading branch information
deini committed Nov 12, 2024
1 parent 72a30a8 commit c65541a
Show file tree
Hide file tree
Showing 24 changed files with 138 additions and 96 deletions.
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
3 changes: 1 addition & 2 deletions core/app/[locale]/(default)/(faceted)/brand/[slug]/page.tsx
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
5 changes: 2 additions & 3 deletions core/app/[locale]/store-selector/_components/locale-link.tsx
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

0 comments on commit c65541a

Please sign in to comment.