i18n setup

This commit is contained in:
peintnermax
2024-10-08 14:32:06 +02:00
parent a5af0cae4d
commit 5dc1d8b0bc
29 changed files with 272 additions and 94 deletions

View File

@@ -1,7 +1,7 @@
const i18nConfig = {
locales: ["en", "de", "it"],
locales: ["en"],
defaultLocale: "en",
prefixDefault: false,
};
module.exports = i18nConfig;
export default i18nConfig;

View File

@@ -0,0 +1,4 @@
{
"title": "Welcome back!!",
"description": "Enter your login data."
}

View File

@@ -45,7 +45,7 @@
"i18next": "^23.15.2",
"i18next-resources-to-backend": "^1.2.1",
"moment": "^2.29.4",
"next": "14.2.10",
"next": "14.2.14",
"next-i18n-router": "^5.5.1",
"next-themes": "^0.2.1",
"nice-grpc": "2.0.1",
@@ -85,7 +85,7 @@
"prettier-plugin-tailwindcss": "0.6.6",
"sass": "^1.79.1",
"start-server-and-test": "^2.0.8",
"tailwindcss": "3.4.12",
"tailwindcss": "3.4.13",
"ts-proto": "^2.2.0",
"typescript": "^5.6.2",
"zitadel-tailwind-config": "workspace:*"

View File

@@ -10,14 +10,14 @@ import i18nConfig from "i18nConfig";
import { dir } from "i18next";
import { Lato } from "next/font/google";
import { ReactNode } from "react";
import initTranslations from "./i18n";
import initTranslations from "../i18n";
const lato = Lato({
weight: ["400", "700", "900"],
subsets: ["latin"],
});
export const revalidate = 60; // revalidate every minute
// export const revalidate = 60; // revalidate every minute
export function generateStaticParams() {
return i18nConfig.locales.map((locale) => ({ locale }));
@@ -30,7 +30,8 @@ export default async function RootLayout({
children: ReactNode;
params: { locale: string };
}) {
const i18nNamespaces = ["common", "footer"];
const i18nNamespaces = ["loginname"];
console.log("layout:", locale);
const { t, resources } = await initTranslations(locale, i18nNamespaces);
// later only shown with dev mode enabled

View File

@@ -1,3 +1,4 @@
import initTranslations from "@/app/i18n";
import { DynamicTheme } from "@/components/dynamic-theme";
import { SignInWithIdp } from "@/components/sign-in-with-idp";
import { UsernameForm } from "@/components/username-form";
@@ -19,9 +20,13 @@ function getIdentityProviders(orgId?: string) {
export default async function Page({
searchParams,
params: { locale },
}: {
searchParams: Record<string | number | symbol, string | undefined>;
params: { locale: string };
}) {
const { t } = await initTranslations(locale, ["loginname"]);
const loginName = searchParams?.loginName;
const authRequestId = searchParams?.authRequestId;
const organization = searchParams?.organization;
@@ -41,8 +46,8 @@ export default async function Page({
return (
<DynamicTheme branding={branding}>
<div className="flex flex-col items-center space-y-4">
<h1>Welcome back!</h1>
<p className="ztdl-p">Enter your login data.</p>
<h1>{t("title")}</h1>
<p className="ztdl-p">{t("description")}</p>
<UsernameForm
loginName={loginName}

View File

@@ -0,0 +1,133 @@
"use client";
import { Listbox, Transition } from "@headlessui/react";
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/24/outline";
import { usePathname, useRouter } from "next/navigation";
import { Fragment, useState } from "react";
import { useTranslation } from "react-i18next";
import i18nConfig from "../../i18nConfig";
interface Lang {
id: number;
name: string;
img: string;
code: string;
}
const LANGS: Lang[] = [
{
id: 1,
name: "Deutsch",
code: "de",
img: "/images/flags/de.png",
},
{
id: 2,
name: "Italiano",
code: "it",
img: "/images/flags/it.png",
},
{
id: 3,
name: "English",
code: "en",
img: "/images/flags/us.png",
},
];
type Props = {
locale: string;
};
export default function LanguageSwitcher({ locale }: Props) {
const { i18n } = useTranslation();
const currentLocale = locale || i18n.language || i18nConfig.defaultLocale;
const [selected, setSelected] = useState(
LANGS.find((l) => l.code === currentLocale) || LANGS[0],
);
const router = useRouter();
const currentPathname = usePathname();
const handleChange = (language: Lang) => {
setSelected(language);
const newLocale = language.code;
// set cookie for next-i18n-router
const days = 30;
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
const expires = date.toUTCString();
document.cookie = `NEXT_LOCALE=${newLocale};expires=${expires};path=/`;
// redirect to the new locale path
if (
currentLocale === i18nConfig.defaultLocale &&
!i18nConfig.prefixDefault
) {
router.push("/" + newLocale + currentPathname);
} else {
router.push(
currentPathname.replace(`/${currentLocale}`, `/${newLocale}`),
);
}
router.refresh();
};
return (
<div className="mb-4 w-32">
<Listbox value={selected} onChange={handleChange}>
<div className="relative mt-1">
<Listbox.Button className="relative w-full cursor-default rounded-lg border border-divider-light bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm">
<span className="block truncate">{selected.name}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronUpDownIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black/5 focus:outline-none sm:text-sm">
{LANGS.map((lang, index) => (
<Listbox.Option
key={lang.code}
className={({ active }) =>
`relative cursor-default select-none py-2 pl-10 pr-4 ${
active ? "bg-amber-100 text-amber-900" : "text-gray-900"
}`
}
value={lang}
>
{({ selected }) => (
<>
<span
className={`block truncate ${
selected ? "font-medium" : "font-normal"
}`}
>
{lang.name}
</span>
{selected ? (
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600">
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
</div>
);
}

View File

View File

@@ -1,42 +1,45 @@
import { i18nRouter } from "next-i18n-router";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
export const config = {
matcher: [
"/.well-known/:path*",
"/oauth/:path*",
"/oidc/:path*",
"/idps/callback/:path*",
],
};
import i18nConfig from "../i18nConfig";
const INSTANCE = process.env.ZITADEL_API_URL;
const SERVICE_USER_ID = process.env.ZITADEL_SERVICE_USER_ID as string;
export function middleware(request: NextRequest) {
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-zitadel-login-client", SERVICE_USER_ID);
// OIDC specific routes
if (
request.nextUrl.pathname.startsWith("/.well-known") ||
request.nextUrl.pathname.startsWith("/oauth") ||
request.nextUrl.pathname.startsWith("/oidc") ||
request.nextUrl.pathname.startsWith("/idps/callback")
) {
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-zitadel-login-client", SERVICE_USER_ID);
// this is a workaround for the next.js server not forwarding the host header
// requestHeaders.set("x-zitadel-forwarded", `host="${request.nextUrl.host}"`);
requestHeaders.set("x-zitadel-public-host", `${request.nextUrl.host}`);
// this is a workaround for the next.js server not forwarding the host header
// requestHeaders.set("x-zitadel-forwarded", `host="${request.nextUrl.host}"`);
requestHeaders.set("x-zitadel-public-host", `${request.nextUrl.host}`);
// this is a workaround for the next.js server not forwarding the host header
requestHeaders.set(
"x-zitadel-instance-host",
`${INSTANCE}`.replace("https://", ""),
);
// this is a workaround for the next.js server not forwarding the host header
requestHeaders.set(
"x-zitadel-instance-host",
`${INSTANCE}`.replace("https://", ""),
);
const responseHeaders = new Headers();
responseHeaders.set("Access-Control-Allow-Origin", "*");
responseHeaders.set("Access-Control-Allow-Headers", "*");
const responseHeaders = new Headers();
responseHeaders.set("Access-Control-Allow-Origin", "*");
responseHeaders.set("Access-Control-Allow-Headers", "*");
request.nextUrl.href = `${INSTANCE}${request.nextUrl.pathname}${request.nextUrl.search}`;
request.nextUrl.href = `${INSTANCE}${request.nextUrl.pathname}${request.nextUrl.search}`;
return NextResponse.rewrite(request.nextUrl, {
request: {
headers: requestHeaders,
},
headers: responseHeaders,
});
return NextResponse.rewrite(request.nextUrl, {
request: {
headers: requestHeaders,
},
headers: responseHeaders,
});
}
return i18nRouter(request, i18nConfig);
}

144
pnpm-lock.yaml generated
View File

@@ -58,10 +58,10 @@ importers:
version: 2.1.3(react@18.3.1)
'@tailwindcss/forms':
specifier: 0.5.7
version: 0.5.7(tailwindcss@3.4.12)
version: 0.5.7(tailwindcss@3.4.13)
'@vercel/analytics':
specifier: ^1.2.2
version: 1.3.1(next@14.2.10(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react@18.3.1)
version: 1.3.1(next@14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react@18.3.1)
'@zitadel/client':
specifier: workspace:*
version: link:../../packages/zitadel-client
@@ -87,14 +87,14 @@ importers:
specifier: ^2.29.4
version: 2.30.1
next:
specifier: 14.2.10
version: 14.2.10(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1)
specifier: 14.2.14
version: 14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1)
next-i18n-router:
specifier: ^5.5.1
version: 5.5.1
next-themes:
specifier: ^0.2.1
version: 0.2.1(next@14.2.10(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 0.2.1(next@14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nice-grpc:
specifier: 2.0.1
version: 2.0.1
@@ -202,8 +202,8 @@ importers:
specifier: ^2.0.8
version: 2.0.8
tailwindcss:
specifier: 3.4.12
version: 3.4.12
specifier: 3.4.13
version: 3.4.13
ts-proto:
specifier: ^2.2.0
version: 2.2.0
@@ -929,62 +929,62 @@ packages:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
hasBin: true
'@next/env@14.2.10':
resolution: {integrity: sha512-dZIu93Bf5LUtluBXIv4woQw2cZVZ2DJTjax5/5DOs3lzEOeKLy7GxRSr4caK9/SCPdaW6bCgpye6+n4Dh9oJPw==}
'@next/env@14.2.14':
resolution: {integrity: sha512-/0hWQfiaD5//LvGNgc8PjvyqV50vGK0cADYzaoOOGN8fxzBn3iAiaq3S0tCRnFBldq0LVveLcxCTi41ZoYgAgg==}
'@next/eslint-plugin-next@14.2.7':
resolution: {integrity: sha512-+7xh142AdhZGjY9/L0iFo7mqRBMJHe+q+uOL+hto1Lfo9DeWCGcR6no4StlFbVSVcA6fQLKEX6y6qhMsSKbgNQ==}
'@next/swc-darwin-arm64@14.2.10':
resolution: {integrity: sha512-V3z10NV+cvMAfxQUMhKgfQnPbjw+Ew3cnr64b0lr8MDiBJs3eLnM6RpGC46nhfMZsiXgQngCJKWGTC/yDcgrDQ==}
'@next/swc-darwin-arm64@14.2.14':
resolution: {integrity: sha512-bsxbSAUodM1cjYeA4o6y7sp9wslvwjSkWw57t8DtC8Zig8aG8V6r+Yc05/9mDzLKcybb6EN85k1rJDnMKBd9Gw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-x64@14.2.10':
resolution: {integrity: sha512-Y0TC+FXbFUQ2MQgimJ/7Ina2mXIKhE7F+GUe1SgnzRmwFY3hX2z8nyVCxE82I2RicspdkZnSWMn4oTjIKz4uzA==}
'@next/swc-darwin-x64@14.2.14':
resolution: {integrity: sha512-cC9/I+0+SK5L1k9J8CInahduTVWGMXhQoXFeNvF0uNs3Bt1Ub0Azb8JzTU9vNCr0hnaMqiWu/Z0S1hfKc3+dww==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-linux-arm64-gnu@14.2.10':
resolution: {integrity: sha512-ZfQ7yOy5zyskSj9rFpa0Yd7gkrBnJTkYVSya95hX3zeBG9E55Z6OTNPn1j2BTFWvOVVj65C3T+qsjOyVI9DQpA==}
'@next/swc-linux-arm64-gnu@14.2.14':
resolution: {integrity: sha512-RMLOdA2NU4O7w1PQ3Z9ft3PxD6Htl4uB2TJpocm+4jcllHySPkFaUIFacQ3Jekcg6w+LBaFvjSPthZHiPmiAUg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-musl@14.2.10':
resolution: {integrity: sha512-n2i5o3y2jpBfXFRxDREr342BGIQCJbdAUi/K4q6Env3aSx8erM9VuKXHw5KNROK9ejFSPf0LhoSkU/ZiNdacpQ==}
'@next/swc-linux-arm64-musl@14.2.14':
resolution: {integrity: sha512-WgLOA4hT9EIP7jhlkPnvz49iSOMdZgDJVvbpb8WWzJv5wBD07M2wdJXLkDYIpZmCFfo/wPqFsFR4JS4V9KkQ2A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-x64-gnu@14.2.10':
resolution: {integrity: sha512-GXvajAWh2woTT0GKEDlkVhFNxhJS/XdDmrVHrPOA83pLzlGPQnixqxD8u3bBB9oATBKB//5e4vpACnx5Vaxdqg==}
'@next/swc-linux-x64-gnu@14.2.14':
resolution: {integrity: sha512-lbn7svjUps1kmCettV/R9oAvEW+eUI0lo0LJNFOXoQM5NGNxloAyFRNByYeZKL3+1bF5YE0h0irIJfzXBq9Y6w==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-musl@14.2.10':
resolution: {integrity: sha512-opFFN5B0SnO+HTz4Wq4HaylXGFV+iHrVxd3YvREUX9K+xfc4ePbRrxqOuPOFjtSuiVouwe6uLeDtabjEIbkmDA==}
'@next/swc-linux-x64-musl@14.2.14':
resolution: {integrity: sha512-7TcQCvLQ/hKfQRgjxMN4TZ2BRB0P7HwrGAYL+p+m3u3XcKTraUFerVbV3jkNZNwDeQDa8zdxkKkw2els/S5onQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-win32-arm64-msvc@14.2.10':
resolution: {integrity: sha512-9NUzZuR8WiXTvv+EiU/MXdcQ1XUvFixbLIMNQiVHuzs7ZIFrJDLJDaOF1KaqttoTujpcxljM/RNAOmw1GhPPQQ==}
'@next/swc-win32-arm64-msvc@14.2.14':
resolution: {integrity: sha512-8i0Ou5XjTLEje0oj0JiI0Xo9L/93ghFtAUYZ24jARSeTMXLUx8yFIdhS55mTExq5Tj4/dC2fJuaT4e3ySvXU1A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-ia32-msvc@14.2.10':
resolution: {integrity: sha512-fr3aEbSd1GeW3YUMBkWAu4hcdjZ6g4NBl1uku4gAn661tcxd1bHs1THWYzdsbTRLcCKLjrDZlNp6j2HTfrw+Bg==}
'@next/swc-win32-ia32-msvc@14.2.14':
resolution: {integrity: sha512-2u2XcSaDEOj+96eXpyjHjtVPLhkAFw2nlaz83EPeuK4obF+HmtDJHqgR1dZB7Gb6V/d55FL26/lYVd0TwMgcOQ==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
'@next/swc-win32-x64-msvc@14.2.10':
resolution: {integrity: sha512-UjeVoRGKNL2zfbcQ6fscmgjBAS/inHBh63mjIlfPg/NG8Yn2ztqylXt5qilYb6hoHIwaU2ogHknHWWmahJjgZQ==}
'@next/swc-win32-x64-msvc@14.2.14':
resolution: {integrity: sha512-MZom+OvZ1NZxuRovKt1ApevjiUJTcU2PmdJKL66xUPaJeRywnbGGRWUlaAOwunD6dX+pm83vj979NTC8QXjGWg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -3141,8 +3141,8 @@ packages:
react: '*'
react-dom: '*'
next@14.2.10:
resolution: {integrity: sha512-sDDExXnh33cY3RkS9JuFEKaS4HmlWmDKP1VJioucCG6z5KuA008DPsDZOzi8UfqEk3Ii+2NCQSJrfbEWtZZfww==}
next@14.2.14:
resolution: {integrity: sha512-Q1coZG17MW0Ly5x76shJ4dkC23woLAhhnDnw+DfTc7EpZSGuWrlsZ3bZaO8t6u1Yu8FVfhkqJE+U8GC7E0GLPQ==}
engines: {node: '>=18.17.0'}
hasBin: true
peerDependencies:
@@ -4059,6 +4059,11 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
tailwindcss@3.4.13:
resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==}
engines: {node: '>=14.0.0'}
hasBin: true
tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
@@ -5218,37 +5223,37 @@ snapshots:
- encoding
- supports-color
'@next/env@14.2.10': {}
'@next/env@14.2.14': {}
'@next/eslint-plugin-next@14.2.7':
dependencies:
glob: 10.3.10
'@next/swc-darwin-arm64@14.2.10':
'@next/swc-darwin-arm64@14.2.14':
optional: true
'@next/swc-darwin-x64@14.2.10':
'@next/swc-darwin-x64@14.2.14':
optional: true
'@next/swc-linux-arm64-gnu@14.2.10':
'@next/swc-linux-arm64-gnu@14.2.14':
optional: true
'@next/swc-linux-arm64-musl@14.2.10':
'@next/swc-linux-arm64-musl@14.2.14':
optional: true
'@next/swc-linux-x64-gnu@14.2.10':
'@next/swc-linux-x64-gnu@14.2.14':
optional: true
'@next/swc-linux-x64-musl@14.2.10':
'@next/swc-linux-x64-musl@14.2.14':
optional: true
'@next/swc-win32-arm64-msvc@14.2.10':
'@next/swc-win32-arm64-msvc@14.2.14':
optional: true
'@next/swc-win32-ia32-msvc@14.2.10':
'@next/swc-win32-ia32-msvc@14.2.14':
optional: true
'@next/swc-win32-x64-msvc@14.2.10':
'@next/swc-win32-x64-msvc@14.2.14':
optional: true
'@nodelib/fs.scandir@2.1.5':
@@ -5361,10 +5366,10 @@ snapshots:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.4.12
'@tailwindcss/forms@0.5.7(tailwindcss@3.4.12)':
'@tailwindcss/forms@0.5.7(tailwindcss@3.4.13)':
dependencies:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.4.12
tailwindcss: 3.4.13
'@tanstack/react-virtual@3.10.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
@@ -5512,11 +5517,11 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@vercel/analytics@1.3.1(next@14.2.10(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react@18.3.1)':
'@vercel/analytics@1.3.1(next@14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react@18.3.1)':
dependencies:
server-only: 0.0.1
optionalDependencies:
next: 14.2.10(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1)
next: 14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1)
react: 18.3.1
'@vercel/git-hooks@1.0.0': {}
@@ -7651,15 +7656,15 @@ snapshots:
'@formatjs/intl-localematcher': 0.5.4
negotiator: 0.6.3
next-themes@0.2.1(next@14.2.10(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
next-themes@0.2.1(next@14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
next: 14.2.10(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1)
next: 14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
next@14.2.10(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1):
next@14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1):
dependencies:
'@next/env': 14.2.10
'@next/env': 14.2.14
'@swc/helpers': 0.5.5
busboy: 1.6.0
caniuse-lite: 1.0.30001666
@@ -7669,15 +7674,15 @@ snapshots:
react-dom: 18.3.1(react@18.3.1)
styled-jsx: 5.1.1(@babel/core@7.25.2)(react@18.3.1)
optionalDependencies:
'@next/swc-darwin-arm64': 14.2.10
'@next/swc-darwin-x64': 14.2.10
'@next/swc-linux-arm64-gnu': 14.2.10
'@next/swc-linux-arm64-musl': 14.2.10
'@next/swc-linux-x64-gnu': 14.2.10
'@next/swc-linux-x64-musl': 14.2.10
'@next/swc-win32-arm64-msvc': 14.2.10
'@next/swc-win32-ia32-msvc': 14.2.10
'@next/swc-win32-x64-msvc': 14.2.10
'@next/swc-darwin-arm64': 14.2.14
'@next/swc-darwin-x64': 14.2.14
'@next/swc-linux-arm64-gnu': 14.2.14
'@next/swc-linux-arm64-musl': 14.2.14
'@next/swc-linux-x64-gnu': 14.2.14
'@next/swc-linux-x64-musl': 14.2.14
'@next/swc-win32-arm64-msvc': 14.2.14
'@next/swc-win32-ia32-msvc': 14.2.14
'@next/swc-win32-x64-msvc': 14.2.14
sass: 1.79.1
transitivePeerDependencies:
- '@babel/core'
@@ -8564,6 +8569,33 @@ snapshots:
transitivePeerDependencies:
- ts-node
tailwindcss@3.4.13:
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
chokidar: 3.6.0
didyoumean: 1.2.2
dlv: 1.1.3
fast-glob: 3.3.2
glob-parent: 6.0.2
is-glob: 4.0.3
jiti: 1.21.6
lilconfig: 2.1.0
micromatch: 4.0.8
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.1.0
postcss: 8.4.47
postcss-import: 15.1.0(postcss@8.4.47)
postcss-js: 4.0.1(postcss@8.4.47)
postcss-load-config: 4.0.2(postcss@8.4.47)
postcss-nested: 6.2.0(postcss@8.4.47)
postcss-selector-parser: 6.1.2
resolve: 1.22.8
sucrase: 3.35.0
transitivePeerDependencies:
- ts-node
tapable@2.2.1: {}
tar@6.2.1: