mirror of
				https://github.com/theoludwig/theoludwig.git
				synced 2025-10-14 20:23:25 +02:00 
			
		
		
		
	build(deps): update Next.js to v15 and ESLint to v9
This commit is contained in:
		
							
								
								
									
										4
									
								
								.github/workflows/chromatic.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/chromatic.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,14 +11,14 @@ jobs: | ||||
|     timeout-minutes: 30 | ||||
|     runs-on: "ubuntu-latest" | ||||
|     steps: | ||||
|       - uses: "actions/checkout@v4.2.1" | ||||
|       - uses: "actions/checkout@v4.2.2" | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|  | ||||
|       - uses: "pnpm/action-setup@v4.0.0" | ||||
|  | ||||
|       - name: "Setup Node.js" | ||||
|         uses: "actions/setup-node@v4.0.4" | ||||
|         uses: "actions/setup-node@v4.1.0" | ||||
|         with: | ||||
|           node-version: "22.x" | ||||
|           cache: "pnpm" | ||||
|   | ||||
							
								
								
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,12 +11,12 @@ jobs: | ||||
|     timeout-minutes: 30 | ||||
|     runs-on: "ubuntu-latest" | ||||
|     steps: | ||||
|       - uses: "actions/checkout@v4.2.1" | ||||
|       - uses: "actions/checkout@v4.2.2" | ||||
|  | ||||
|       - uses: "pnpm/action-setup@v4.0.0" | ||||
|  | ||||
|       - name: "Setup Node.js" | ||||
|         uses: "actions/setup-node@v4.0.4" | ||||
|         uses: "actions/setup-node@v4.1.0" | ||||
|         with: | ||||
|           node-version: "22.x" | ||||
|           cache: "pnpm" | ||||
| @@ -38,6 +38,6 @@ jobs: | ||||
|   commitlint: | ||||
|     runs-on: "ubuntu-latest" | ||||
|     steps: | ||||
|       - uses: "actions/checkout@v4.2.1" | ||||
|       - uses: "actions/checkout@v4.2.2" | ||||
|  | ||||
|       - uses: "wagoid/commitlint-github-action@v6.1.2" | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -14,7 +14,7 @@ jobs: | ||||
|       pull-requests: "write" | ||||
|       id-token: "write" | ||||
|     steps: | ||||
|       - uses: "actions/checkout@v4.2.1" | ||||
|       - uses: "actions/checkout@v4.2.2" | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|           persist-credentials: false | ||||
| @@ -29,7 +29,7 @@ jobs: | ||||
|       - uses: "pnpm/action-setup@v4.0.0" | ||||
|  | ||||
|       - name: "Setup Node.js" | ||||
|         uses: "actions/setup-node@v4.0.4" | ||||
|         uses: "actions/setup-node@v4.1.0" | ||||
|         with: | ||||
|           node-version: "22.x" | ||||
|           cache: "pnpm" | ||||
|   | ||||
							
								
								
									
										6
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -7,12 +7,8 @@ | ||||
|   "editor.formatOnSave": true, | ||||
|   "editor.codeActionsOnSave": { | ||||
|     "source.fixAll": "explicit", | ||||
|     "source.organizeImports": "explicit" | ||||
|     "source.organizeImports": "never" | ||||
|   }, | ||||
|   "eslint.options": { | ||||
|     "ignorePath": ".gitignore" | ||||
|   }, | ||||
|   "prettier.ignorePath": ".gitignore", | ||||
|   "tailwindCSS.experimental.classRegex": [ | ||||
|     ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], | ||||
|     ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] | ||||
|   | ||||
| @@ -32,7 +32,7 @@ The commit message guidelines adheres to [Conventional Commits](https://www.conv | ||||
| ### Prerequisites | ||||
|  | ||||
| - [Node.js](https://nodejs.org/) >= 22.0.0 | ||||
| - [pnpm](https://pnpm.io/) >= 9.12.1 [(`corepack enable`)](https://nodejs.org/docs/latest-v22.x/api/corepack.html) | ||||
| - [pnpm](https://pnpm.io/) >= 9.12.3 [(`corepack enable`)](https://nodejs.org/docs/latest-v22.x/api/corepack.html) | ||||
| - [Docker](https://www.docker.com/) | ||||
|  | ||||
| ### Installation | ||||
|   | ||||
| @@ -1,4 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "extends": ["@repo/eslint-config"] | ||||
| } | ||||
| @@ -13,11 +13,10 @@ const config: StorybookConfig = { | ||||
|     "../stories/*.mdx", | ||||
|   ], | ||||
|   addons: [ | ||||
|     "@chromatic-com/storybook", | ||||
|     "@storybook/addon-essentials", | ||||
|     "@storybook/addon-storysource", | ||||
|     "@storybook/addon-a11y", | ||||
|     "@storybook/addon-links", | ||||
|     "@chromatic-com/storybook", | ||||
|     "@storybook/addon-interactions", | ||||
|     "storybook-dark-mode", | ||||
|   ], | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import "@repo/config-tailwind/styles.css" | ||||
| import { defaultTranslationValues } from "@repo/i18n/config" | ||||
| import i18nMessagesEnglish from "@repo/i18n/translations/en-US.json" | ||||
| import { LOCALE_DEFAULT } from "@repo/utils/constants" | ||||
| import i18nMessages from "@repo/i18n/translations/en-US.json" | ||||
| import { LOCALE_DEFAULT, TIMEZONE } from "@repo/utils/constants" | ||||
| import type { Preview } from "@storybook/react" | ||||
| import { NextIntlClientProvider } from "next-intl" | ||||
| import { ThemeProvider as NextThemeProvider } from "next-themes" | ||||
| @@ -28,7 +27,7 @@ const preview: Preview = { | ||||
|       disableSaveFromUI: true, | ||||
|       matchers: { | ||||
|         color: /(background|color)$/i, | ||||
|         date: /Date$/i, | ||||
|         date: /date$/i, | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
| @@ -37,10 +36,9 @@ const preview: Preview = { | ||||
|       return ( | ||||
|         <NextThemeProvider enableColorScheme={false}> | ||||
|           <NextIntlClientProvider | ||||
|             messages={i18nMessagesEnglish} | ||||
|             messages={i18nMessages} | ||||
|             locale={LOCALE_DEFAULT} | ||||
|             defaultTranslationValues={defaultTranslationValues} | ||||
|             timeZone={process.env["TZ"] ?? "UTC"} | ||||
|             timeZone={TIMEZONE} | ||||
|           > | ||||
|             <Story /> | ||||
|           </NextIntlClientProvider> | ||||
|   | ||||
							
								
								
									
										13
									
								
								apps/storybook/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								apps/storybook/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import config from "@repo/eslint-config" | ||||
|  | ||||
| export default typescriptESLint.config(...config, { | ||||
|   files: ["**/*.ts", "**/*.tsx"], | ||||
|   languageOptions: { | ||||
|     parser: typescriptESLint.parser, | ||||
|     parserOptions: { | ||||
|       projectService: true, | ||||
|       tsconfigRootDir: import.meta.dirname, | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
| @@ -26,13 +26,13 @@ | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@repo/eslint-config": "workspace:*", | ||||
|     "@repo/config-typescript": "workspace:*", | ||||
|     "@chromatic-com/storybook": "catalog:", | ||||
|     "@playwright/test": "catalog:", | ||||
|     "@storybook/addon-a11y": "catalog:", | ||||
|     "@storybook/addon-essentials": "catalog:", | ||||
|     "@storybook/addon-interactions": "catalog:", | ||||
|     "@storybook/addon-links": "catalog:", | ||||
|     "@storybook/addon-storysource": "catalog:", | ||||
|     "@storybook/addon-a11y": "catalog:", | ||||
|     "@storybook/addon-interactions": "catalog:", | ||||
|     "@storybook/addon-themes": "catalog:", | ||||
|     "@storybook/blocks": "catalog:", | ||||
|     "@storybook/nextjs": "catalog:", | ||||
| @@ -51,6 +51,7 @@ | ||||
|     "storybook-dark-mode": "catalog:", | ||||
|     "postcss": "catalog:", | ||||
|     "tailwindcss": "catalog:", | ||||
|     "typescript-eslint": "catalog:", | ||||
|     "typescript": "catalog:" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										7
									
								
								apps/storybook/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								apps/storybook/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "extends": "@repo/config-typescript/tsconfig.json", | ||||
|   "compilerOptions": { | ||||
|     "lib": ["DOM", "DOM.Iterable", "ESNext"] | ||||
|   }, | ||||
|   "include": ["./.storybook/**/*.ts", "./.storybook/**/*.tsx"] | ||||
| } | ||||
| @@ -1,14 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "extends": ["@repo/eslint-config/nextjs/.eslintrc.json"], | ||||
|   "overrides": [ | ||||
|     { | ||||
|       "files": ["*.ts", "*.tsx"], | ||||
|       "plugins": ["@typescript-eslint"], | ||||
|       "parser": "@typescript-eslint/parser", | ||||
|       "parserOptions": { | ||||
|         "projectService": true | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| FROM node:22.4.1-slim AS node-pnpm | ||||
| FROM node:22.11.0-slim AS node-pnpm | ||||
| ENV PNPM_HOME="/pnpm" | ||||
| ENV PATH="$PNPM_HOME:$PATH" | ||||
| RUN corepack enable | ||||
| @@ -8,7 +8,7 @@ WORKDIR /usr/src/app | ||||
|  | ||||
| FROM node-pnpm AS builder | ||||
| COPY ./ ./ | ||||
| RUN pnpm install --global turbo@2.1.3 | ||||
| RUN pnpm install --global turbo@2.2.3 | ||||
| RUN turbo prune @repo/website --docker | ||||
|  | ||||
| FROM node-pnpm AS installer | ||||
|   | ||||
| @@ -4,19 +4,20 @@ import { notFound } from "next/navigation" | ||||
| import { getBlogPostBySlug, getBlogPosts } from "@repo/blog" | ||||
| import { BlogPostUI } from "@repo/blog/BlogPostUI" | ||||
| import type { Locale } from "@repo/utils/constants" | ||||
| import { unstable_setRequestLocale } from "next-intl/server" | ||||
| import { setRequestLocale } from "next-intl/server" | ||||
|  | ||||
| interface BlogPostPageProps { | ||||
|   params: { | ||||
|   params: Promise<{ | ||||
|     slug: string | ||||
|     locale: Locale | ||||
|   } | ||||
|   }> | ||||
| } | ||||
|  | ||||
| export const generateMetadata = async ( | ||||
|   props: BlogPostPageProps, | ||||
| ): Promise<Metadata> => { | ||||
|   const blogPost = await getBlogPostBySlug(props.params.slug) | ||||
|   const { slug } = await props.params | ||||
|   const blogPost = await getBlogPostBySlug(slug) | ||||
|   if (blogPost == null) { | ||||
|     return notFound() | ||||
|   } | ||||
| @@ -50,10 +51,11 @@ export const generateStaticParams = async (): Promise< | ||||
| const BlogPostPage: React.FC<BlogPostPageProps> = async (props) => { | ||||
|   const { params } = props | ||||
|  | ||||
|   const { locale, slug } = await params | ||||
|   // Enable static rendering | ||||
|   unstable_setRequestLocale(params.locale) | ||||
|   setRequestLocale(locale) | ||||
|  | ||||
|   const blogPost = await getBlogPostBySlug(params.slug) | ||||
|   const blogPost = await getBlogPostBySlug(slug) | ||||
|   if (blogPost == null) { | ||||
|     return notFound() | ||||
|   } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { getBlogPosts } from "@repo/blog" | ||||
| import { BlogPosts } from "@repo/blog/BlogPosts" | ||||
| import type { LocaleProps } from "@repo/i18n/config" | ||||
| import type { LocaleProps } from "@repo/i18n/routing" | ||||
| import { MainLayout } from "@repo/ui/Layout/MainLayout" | ||||
| import { | ||||
|   Section, | ||||
| @@ -9,7 +9,7 @@ import { | ||||
| } from "@repo/ui/Layout/Section" | ||||
| import { LOCALE_DEFAULT } from "@repo/utils/constants" | ||||
| import type { Metadata } from "next" | ||||
| import { unstable_setRequestLocale } from "next-intl/server" | ||||
| import { setRequestLocale } from "next-intl/server" | ||||
|  | ||||
| const title = "Blog | Théo LUDWIG" | ||||
| const description = | ||||
| @@ -36,8 +36,9 @@ interface BlogPageProps extends LocaleProps {} | ||||
| const BlogPage: React.FC<BlogPageProps> = async (props) => { | ||||
|   const { params } = props | ||||
|  | ||||
|   const { locale } = await params | ||||
|   // Enable static rendering | ||||
|   unstable_setRequestLocale(params.locale) | ||||
|   setRequestLocale(locale) | ||||
|  | ||||
|   const posts = await getBlogPosts() | ||||
|  | ||||
|   | ||||
| @@ -1,18 +1,19 @@ | ||||
| import "@repo/config-tailwind/styles.css" | ||||
| import type { LocaleProps } from "@repo/i18n/config" | ||||
| import type { LocaleProps } from "@repo/i18n/routing" | ||||
| import { Footer } from "@repo/ui/Layout/Footer" | ||||
| import { Header } from "@repo/ui/Layout/Header" | ||||
| import { ThemeProvider } from "@repo/ui/Layout/Header/SwitchTheme" | ||||
| import { VERSION } from "@repo/utils/constants" | ||||
| import { unstable_setRequestLocale } from "next-intl/server" | ||||
| import { setRequestLocale } from "next-intl/server" | ||||
|  | ||||
| interface MainLayoutProps extends React.PropsWithChildren, LocaleProps {} | ||||
|  | ||||
| const MainLayout: React.FC<MainLayoutProps> = async (props) => { | ||||
|   const { children, params } = props | ||||
|  | ||||
|   const { locale } = await params | ||||
|   // Enable static rendering | ||||
|   unstable_setRequestLocale(params.locale) | ||||
|   setRequestLocale(locale) | ||||
|  | ||||
|   return ( | ||||
|     <ThemeProvider> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { LocaleProps } from "@repo/i18n/config" | ||||
| import type { LocaleProps } from "@repo/i18n/routing" | ||||
| import { About } from "@repo/ui/Home/About" | ||||
| import { Interests } from "@repo/ui/Home/Interests" | ||||
| import { OpenSource } from "@repo/ui/Home/OpenSource" | ||||
| @@ -6,15 +6,16 @@ import { Portfolio } from "@repo/ui/Home/Portfolio" | ||||
| import { Skills } from "@repo/ui/Home/Skills" | ||||
| import { MainLayout } from "@repo/ui/Layout/MainLayout" | ||||
| import { RevealFade } from "@repo/ui/Layout/Section" | ||||
| import { unstable_setRequestLocale } from "next-intl/server" | ||||
| import { setRequestLocale } from "next-intl/server" | ||||
|  | ||||
| interface HomePageProps extends LocaleProps {} | ||||
|  | ||||
| const HomePage: React.FC<HomePageProps> = (props) => { | ||||
| const HomePage: React.FC<HomePageProps> = async (props) => { | ||||
|   const { params } = props | ||||
|  | ||||
|   const { locale } = await params | ||||
|   // Enable static rendering | ||||
|   unstable_setRequestLocale(params.locale) | ||||
|   setRequestLocale(locale) | ||||
|  | ||||
|   return ( | ||||
|     <MainLayout> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import "@repo/config-tailwind/styles.css" | ||||
| import type { LocaleProps } from "@repo/i18n/config" | ||||
| import type { LocaleProps } from "@repo/i18n/routing" | ||||
| import { ThemeProvider } from "@repo/ui/Layout/Header/SwitchTheme" | ||||
| import { unstable_setRequestLocale } from "next-intl/server" | ||||
| import { setRequestLocale } from "next-intl/server" | ||||
|  | ||||
| interface CurriculumVitaeLayoutProps | ||||
|   extends React.PropsWithChildren, | ||||
| @@ -12,8 +12,9 @@ const CurriculumVitaeLayout: React.FC<CurriculumVitaeLayoutProps> = async ( | ||||
| ) => { | ||||
|   const { children, params } = props | ||||
|  | ||||
|   const { locale } = await params | ||||
|   // Enable static rendering | ||||
|   unstable_setRequestLocale(params.locale) | ||||
|   setRequestLocale(locale) | ||||
|  | ||||
|   return <ThemeProvider forcedTheme="light">{children}</ThemeProvider> | ||||
| } | ||||
|   | ||||
| @@ -1,14 +1,15 @@ | ||||
| import type { LocaleProps } from "@repo/i18n/config" | ||||
| import type { LocaleProps } from "@repo/i18n/routing" | ||||
| import { CurriculumVitae } from "@repo/ui/CurriculumVitae" | ||||
| import { unstable_setRequestLocale } from "next-intl/server" | ||||
| import { setRequestLocale } from "next-intl/server" | ||||
|  | ||||
| interface CurriculumVitaeProps extends LocaleProps {} | ||||
|  | ||||
| const CurriculumVitaePage: React.FC<CurriculumVitaeProps> = (props) => { | ||||
| const CurriculumVitaePage: React.FC<CurriculumVitaeProps> = async (props) => { | ||||
|   const { params } = props | ||||
|  | ||||
|   const { locale } = await params | ||||
|   // Enable static rendering | ||||
|   unstable_setRequestLocale(params.locale) | ||||
|   setRequestLocale(locale) | ||||
|  | ||||
|   return <CurriculumVitae /> | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import "@repo/config-tailwind/styles.css" | ||||
| import type { LocaleProps } from "@repo/i18n/config" | ||||
| import type { LocaleProps } from "@repo/i18n/routing" | ||||
| import type { Locale } from "@repo/utils/constants" | ||||
| import { LOCALES } from "@repo/utils/constants" | ||||
| import type { Metadata } from "next" | ||||
| @@ -7,18 +7,19 @@ import { NextIntlClientProvider } from "next-intl" | ||||
| import { | ||||
|   getMessages, | ||||
|   getTranslations, | ||||
|   unstable_setRequestLocale, | ||||
|   setRequestLocale, | ||||
| } from "next-intl/server" | ||||
|  | ||||
| export const generateMetadata = async ({ | ||||
|   params, | ||||
| }: LocaleProps): Promise<Metadata> => { | ||||
|   const t = await getTranslations({ locale: params.locale }) | ||||
|   const { locale } = await params | ||||
|   const t = await getTranslations({ locale }) | ||||
|   const title = t("meta.title") | ||||
|   const description = `${title} - ${t("meta.description")}` | ||||
|   const image = "/images/logo.webp" | ||||
|   const url = new URL("https://theoludwig.fr") | ||||
|   const locale = LOCALES.join(", ") | ||||
|   const locales = LOCALES.join(", ") | ||||
|  | ||||
|   return { | ||||
|     title, | ||||
| @@ -36,7 +37,7 @@ export const generateMetadata = async ({ | ||||
|           height: 96, | ||||
|         }, | ||||
|       ], | ||||
|       locale, | ||||
|       locale: locales, | ||||
|       type: "website", | ||||
|     }, | ||||
|     twitter: { | ||||
| @@ -61,13 +62,14 @@ interface LocaleLayoutProps extends React.PropsWithChildren, LocaleProps {} | ||||
| const LocaleLayout: React.FC<LocaleLayoutProps> = async (props) => { | ||||
|   const { children, params } = props | ||||
|  | ||||
|   const { locale } = await params | ||||
|   // Enable static rendering | ||||
|   unstable_setRequestLocale(params.locale) | ||||
|   setRequestLocale(locale) | ||||
|  | ||||
|   const messages = await getMessages() | ||||
|  | ||||
|   return ( | ||||
|     <html lang={params.locale} suppressHydrationWarning> | ||||
|     <html lang={locale} suppressHydrationWarning> | ||||
|       <body> | ||||
|         <NextIntlClientProvider messages={messages}> | ||||
|           {children} | ||||
|   | ||||
							
								
								
									
										13
									
								
								apps/website/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								apps/website/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import configNextjs from "@repo/eslint-config/nextjs" | ||||
|  | ||||
| export default typescriptESLint.config(...configNextjs, { | ||||
|   files: ["**/*.ts", "**/*.tsx"], | ||||
|   languageOptions: { | ||||
|     parser: typescriptESLint.parser, | ||||
|     parserOptions: { | ||||
|       projectService: true, | ||||
|       tsconfigRootDir: import.meta.dirname, | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
| @@ -1,3 +0,0 @@ | ||||
| import i18nConfig from "@repo/i18n/i18n" | ||||
|  | ||||
| export default i18nConfig | ||||
							
								
								
									
										3
									
								
								apps/website/i18n/request.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								apps/website/i18n/request.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| import i18nRequestConfig from "@repo/i18n/request" | ||||
|  | ||||
| export default i18nRequestConfig | ||||
| @@ -1,25 +1,26 @@ | ||||
| import { LOCALES, LOCALE_DEFAULT, LOCALE_PREFIX } from "@repo/utils/constants" | ||||
| import createMiddleware from "next-intl/middleware" | ||||
| import { routing } from "@repo/i18n/routing" | ||||
| import createIntlMiddleware from "next-intl/middleware" | ||||
|  | ||||
| export default createMiddleware({ | ||||
|   locales: LOCALES, | ||||
|   defaultLocale: LOCALE_DEFAULT, | ||||
|   localePrefix: LOCALE_PREFIX, | ||||
| }) | ||||
| const intlMiddleware = createIntlMiddleware(routing) | ||||
|  | ||||
| export default intlMiddleware | ||||
|  | ||||
| export const config = { | ||||
|   matcher: [ | ||||
|     // Enable a redirect to a matching locale at the root | ||||
|     "/", | ||||
|  | ||||
|     // Set a cookie to remember the previous locale for | ||||
|     // all requests that have a locale prefix | ||||
|     // Next.js issue, middleware matcher should support template literals: | ||||
|     // https://github.com/vercel/next.js/issues/56398 | ||||
|     "/(en-US|fr-FR)/:path*", | ||||
|  | ||||
|     // Enable redirects that add missing locales | ||||
|     // (e.g. `/pathnames` -> `/en/pathnames`) | ||||
|     "/((?!_next|_vercel|.*\\..*).*)", | ||||
|     /* | ||||
|      * Match all request paths except for the ones starting with: | ||||
|      * - api (API routes) | ||||
|      * - _next/static (static files) | ||||
|      * - _next/image (image optimization files) | ||||
|      * - favicon.ico (favicon file) | ||||
|      */ | ||||
|     "/((?!api|_next/static|_next/image|images|favicon.ico).*)", | ||||
|   ], | ||||
| } | ||||
|   | ||||
| @@ -7,10 +7,10 @@ | ||||
|     "#*": "./*" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "dev": "next dev --port 3000 --turbo", | ||||
|     "dev": "next dev --port 3000 --turbopack", | ||||
|     "build": "next build", | ||||
|     "start": "next start --port 3000", | ||||
|     "lint:eslint": "eslint . --max-warnings 0 --report-unused-disable-directives", | ||||
|     "lint:eslint": "eslint . --max-warnings 0", | ||||
|     "lint:typescript": "tsc --noEmit" | ||||
|   }, | ||||
|   "dependencies": { | ||||
| @@ -34,6 +34,7 @@ | ||||
|     "eslint": "catalog:", | ||||
|     "postcss": "catalog:", | ||||
|     "tailwindcss": "catalog:", | ||||
|     "typescript-eslint": "catalog:", | ||||
|     "typescript": "catalog:" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -3,10 +3,10 @@ | ||||
|   "version": "4.0.0", | ||||
|   "private": true, | ||||
|   "type": "module", | ||||
|   "packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4", | ||||
|   "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee", | ||||
|   "engines": { | ||||
|     "node": ">=22.0.0", | ||||
|     "pnpm": ">=9.12.1" | ||||
|     "pnpm": ">=9.12.3" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "build": "turbo run build", | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "extends": ["@repo/eslint-config/nextjs/.eslintrc.json"], | ||||
|   "overrides": [ | ||||
|     { | ||||
|       "files": ["*.ts", "*.tsx"], | ||||
|       "plugins": ["@typescript-eslint"], | ||||
|       "parser": "@typescript-eslint/parser", | ||||
|       "parserOptions": { | ||||
|         "projectService": true | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										13
									
								
								packages/blog/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/blog/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import configNextjs from "@repo/eslint-config/nextjs" | ||||
|  | ||||
| export default typescriptESLint.config(...configNextjs, { | ||||
|   files: ["**/*.ts", "**/*.tsx"], | ||||
|   languageOptions: { | ||||
|     parser: typescriptESLint.parser, | ||||
|     parserOptions: { | ||||
|       projectService: true, | ||||
|       tsconfigRootDir: import.meta.dirname, | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
| @@ -9,7 +9,7 @@ | ||||
|     "./BlogPostUI": "./src/BlogPostUI.tsx" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "lint:eslint": "eslint src --max-warnings 0 --report-unused-disable-directives", | ||||
|     "lint:eslint": "eslint src --max-warnings 0", | ||||
|     "lint:typescript": "tsc --noEmit" | ||||
|   }, | ||||
|   "dependencies": { | ||||
| @@ -49,6 +49,7 @@ | ||||
|     "eslint": "catalog:", | ||||
|     "postcss": "catalog:", | ||||
|     "tailwindcss": "catalog:", | ||||
|     "typescript-eslint": "catalog:", | ||||
|     "typescript": "catalog:" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import rehypeSlug from "rehype-slug" | ||||
| import remarkGfm from "remark-gfm" | ||||
| import remarkMath from "remark-math" | ||||
|  | ||||
| import { Link } from "@repo/i18n/navigation" | ||||
| import { Link } from "@repo/i18n/routing" | ||||
| import "katex/dist/katex.min.css" | ||||
| import { BlogPostComments } from "./BlogPostComments.tsx" | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { Link } from "@repo/i18n/navigation" | ||||
| import { Link } from "@repo/i18n/routing" | ||||
| import { Typography } from "@repo/ui/Design/Typography" | ||||
| import { Section, SectionContent } from "@repo/ui/Layout/Section" | ||||
| import { getISODate } from "@repo/utils/dates" | ||||
|   | ||||
| @@ -1,26 +0,0 @@ | ||||
| { | ||||
|   "extends": ["conventions"], | ||||
|   "plugins": ["import-x"], | ||||
|   "rules": { | ||||
|     "import-x/no-absolute-path": "error", | ||||
|     "import-x/no-webpack-loader-syntax": "error", | ||||
|     "import-x/no-self-import": "error", | ||||
|     "import-x/no-useless-path-segments": "error", | ||||
|     "import-x/export": "error", | ||||
|     "import-x/no-duplicates": "error", | ||||
|     "import-x/no-named-default": "error", | ||||
|     "import-x/no-empty-named-blocks": "error", | ||||
|     "import-x/no-anonymous-default-export": "error", | ||||
|     "import-x/extensions": [ | ||||
|       "error", | ||||
|       "ignorePackages", | ||||
|       { | ||||
|         "ts": "always", | ||||
|         "tsx": "always", | ||||
|         "js": "never", | ||||
|         "jsx": "never" | ||||
|       } | ||||
|     ], | ||||
|     "import-x/consistent-type-specifier-style": ["error", "prefer-top-level"] | ||||
|   } | ||||
| } | ||||
							
								
								
									
										40
									
								
								packages/config-eslint/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								packages/config-eslint/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import configConventions from "eslint-config-conventions" | ||||
| import importX from "eslint-plugin-import-x" | ||||
| import unicorn from "eslint-plugin-unicorn" | ||||
|  | ||||
| export default typescriptESLint.config( | ||||
|   { | ||||
|     ignores: [ | ||||
|       ".next", | ||||
|       "**/next.config.js", | ||||
|       "**/eslint.config.js", | ||||
|       "**/tailwind.config.js", | ||||
|       "**/postcss.config.js", | ||||
|       "**/vitest.config.ts", | ||||
|       "**/kysely.config.ts", | ||||
|     ], | ||||
|   }, | ||||
|   ...configConventions, | ||||
|   { | ||||
|     name: "config-eslint", | ||||
|     plugins: { | ||||
|       "import-x": importX, | ||||
|       unicorn, | ||||
|     }, | ||||
|     rules: { | ||||
|       "import-x/extensions": [ | ||||
|         "error", | ||||
|         "ignorePackages", | ||||
|         { | ||||
|           ts: "always", | ||||
|           tsx: "always", | ||||
|           js: "never", | ||||
|           jsx: "never", | ||||
|         }, | ||||
|       ], | ||||
|       "unicorn/explicit-length-check": "error", | ||||
|       "unicorn/consistent-destructuring": "off", | ||||
|     }, | ||||
|   }, | ||||
| ) | ||||
							
								
								
									
										7
									
								
								packages/config-eslint/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/config-eslint/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import type typescriptESLint from "typescript-eslint" | ||||
|  | ||||
| declare const eslintConfigConventions: ReturnType< | ||||
|   typeof typescriptESLint.config | ||||
| > | ||||
|  | ||||
| export default eslintConfigConventions | ||||
| @@ -1,57 +0,0 @@ | ||||
| { | ||||
|   "extends": [ | ||||
|     "../.eslintrc.json", | ||||
|     "next/core-web-vitals", | ||||
|     "plugin:tailwindcss/recommended", | ||||
|     "plugin:storybook/recommended" | ||||
|   ], | ||||
|   "ignorePatterns": [ | ||||
|     "next.config.js", | ||||
|     "tailwind.config.js", | ||||
|     "postcss.config.js", | ||||
|     "vitest.config.ts" | ||||
|   ], | ||||
|   "settings": { | ||||
|     "tailwindcss": { | ||||
|       "callees": ["classNames", "cva"] | ||||
|     }, | ||||
|     "react": { | ||||
|       "version": "detect" | ||||
|     } | ||||
|   }, | ||||
|   "rules": { | ||||
|     "tailwindcss/classnames-order": "off", | ||||
|     "tailwindcss/no-custom-classname": "off", | ||||
|     "@next/next/no-html-link-for-pages": "off", | ||||
|     "react/self-closing-comp": [ | ||||
|       "error", | ||||
|       { | ||||
|         "component": true, | ||||
|         "html": true | ||||
|       } | ||||
|     ], | ||||
|     "react/void-dom-elements-no-children": "error", | ||||
|     "react/jsx-boolean-value": "error", | ||||
|     "no-restricted-imports": [ | ||||
|       "error", | ||||
|       { | ||||
|         "paths": [ | ||||
|           { | ||||
|             "name": "next/link", | ||||
|             "message": "Please import from `@repo/i18n/navigation` instead." | ||||
|           }, | ||||
|           { | ||||
|             "name": "next/navigation", | ||||
|             "importNames": [ | ||||
|               "redirect", | ||||
|               "permanentRedirect", | ||||
|               "useRouter", | ||||
|               "usePathname" | ||||
|             ], | ||||
|             "message": "Please import from `@repo/i18n/navigation` instead." | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| } | ||||
							
								
								
									
										61
									
								
								packages/config-eslint/nextjs/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								packages/config-eslint/nextjs/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| import { FlatCompat } from "@eslint/eslintrc" | ||||
| import storybook from "eslint-plugin-storybook" | ||||
| import tailwind from "eslint-plugin-tailwindcss" | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import config from "../eslint.config.js" | ||||
|  | ||||
| const flatCompat = new FlatCompat() | ||||
|  | ||||
| export default typescriptESLint.config( | ||||
|   ...config, | ||||
|   ...flatCompat.extends("next/core-web-vitals"), | ||||
|   ...tailwind.configs["flat/recommended"], | ||||
|   ...storybook.configs["flat/recommended"], | ||||
|   { | ||||
|     name: "config-eslint/nextjs", | ||||
|     settings: { | ||||
|       tailwindcss: { | ||||
|         callees: ["classNames", "cva"], | ||||
|       }, | ||||
|       react: { | ||||
|         version: "detect", | ||||
|       }, | ||||
|     }, | ||||
|     rules: { | ||||
|       "tailwindcss/classnames-order": "off", | ||||
|       "tailwindcss/no-custom-classname": "off", | ||||
|       "@next/next/no-html-link-for-pages": "off", | ||||
|       "@next/next/no-img-element": "off", | ||||
|       "react/self-closing-comp": [ | ||||
|         "error", | ||||
|         { | ||||
|           component: true, | ||||
|           html: true, | ||||
|         }, | ||||
|       ], | ||||
|       "react/void-dom-elements-no-children": "error", | ||||
|       "react/jsx-boolean-value": "error", | ||||
|       "no-restricted-imports": [ | ||||
|         "error", | ||||
|         { | ||||
|           paths: [ | ||||
|             { | ||||
|               name: "next/link", | ||||
|               message: "Please import from `@repo/i18n/routing` instead.", | ||||
|             }, | ||||
|             { | ||||
|               name: "next/navigation", | ||||
|               importNames: [ | ||||
|                 "redirect", | ||||
|                 "permanentRedirect", | ||||
|                 "useRouter", | ||||
|                 "usePathname", | ||||
|               ], | ||||
|               message: "Please import from `@repo/i18n/routing` instead.", | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|   }, | ||||
| ) | ||||
| @@ -2,14 +2,24 @@ | ||||
|   "name": "@repo/eslint-config", | ||||
|   "version": "4.0.0", | ||||
|   "private": true, | ||||
|   "main": ".eslintrc.json", | ||||
|   "files": [ | ||||
|     ".eslintrc.json", | ||||
|     "nextjs/.eslintrc.json" | ||||
|   ], | ||||
|   "type": "module", | ||||
|   "exports": { | ||||
|     ".": { | ||||
|       "types": "./index.d.ts", | ||||
|       "import": "./eslint.config.js", | ||||
|       "require": "./eslint.config.js", | ||||
|       "default": "./eslint.config.js" | ||||
|     }, | ||||
|     "./nextjs": { | ||||
|       "types": "./index.d.ts", | ||||
|       "import": "./nextjs/eslint.config.js", | ||||
|       "require": "./nextjs/eslint.config.js", | ||||
|       "default": "./nextjs/eslint.config.js" | ||||
|     } | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@typescript-eslint/eslint-plugin": "catalog:", | ||||
|     "@typescript-eslint/parser": "catalog:", | ||||
|     "@eslint/eslintrc": "catalog:", | ||||
|     "typescript-eslint": "catalog:", | ||||
|     "eslint": "catalog:", | ||||
|     "eslint-config-conventions": "catalog:", | ||||
|     "eslint-plugin-promise": "catalog:", | ||||
| @@ -18,6 +28,7 @@ | ||||
|     "eslint-plugin-storybook": "catalog:", | ||||
|     "eslint-plugin-tailwindcss": "catalog:", | ||||
|     "eslint-plugin-import-x": "catalog:", | ||||
|     "typescript": "catalog:" | ||||
|     "typescript": "catalog:", | ||||
|     "globals": "catalog:" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "extends": ["@repo/eslint-config"], | ||||
|   "overrides": [ | ||||
|     { | ||||
|       "files": ["*.ts", "*.tsx"], | ||||
|       "plugins": ["@typescript-eslint"], | ||||
|       "parser": "@typescript-eslint/parser", | ||||
|       "parserOptions": { | ||||
|         "projectService": true | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										13
									
								
								packages/config-tailwind/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/config-tailwind/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import config from "@repo/eslint-config" | ||||
|  | ||||
| export default typescriptESLint.config(...config, { | ||||
|   files: ["**/*.ts", "**/*.tsx"], | ||||
|   languageOptions: { | ||||
|     parser: typescriptESLint.parser, | ||||
|     parserOptions: { | ||||
|       projectService: true, | ||||
|       tsconfigRootDir: import.meta.dirname, | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
| @@ -24,6 +24,7 @@ | ||||
|     "@repo/eslint-config": "workspace:*", | ||||
|     "@repo/config-typescript": "workspace:*", | ||||
|     "@tailwindcss/typography": "catalog:", | ||||
|     "typescript-eslint": "catalog:", | ||||
|     "eslint": "catalog:", | ||||
|     "postcss": "catalog:", | ||||
|     "tailwindcss": "catalog:" | ||||
|   | ||||
| @@ -24,9 +24,6 @@ | ||||
|     "target": "ESNext", | ||||
|     "module": "ESNext", | ||||
|     "moduleResolution": "Bundler", | ||||
|     "resolveJsonModule": true, | ||||
|  | ||||
|     "experimentalDecorators": true, | ||||
|     "emitDecoratorMetadata": true | ||||
|     "resolveJsonModule": true | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "extends": ["@repo/eslint-config"], | ||||
|   "overrides": [ | ||||
|     { | ||||
|       "files": ["*.ts", "*.tsx"], | ||||
|       "plugins": ["@typescript-eslint"], | ||||
|       "parser": "@typescript-eslint/parser", | ||||
|       "parserOptions": { | ||||
|         "projectService": true | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										13
									
								
								packages/i18n/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/i18n/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import config from "@repo/eslint-config" | ||||
|  | ||||
| export default typescriptESLint.config(...config, { | ||||
|   files: ["**/*.ts", "**/*.tsx"], | ||||
|   languageOptions: { | ||||
|     parser: typescriptESLint.parser, | ||||
|     parserOptions: { | ||||
|       projectService: true, | ||||
|       tsconfigRootDir: import.meta.dirname, | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
| @@ -5,13 +5,12 @@ | ||||
|   "type": "module", | ||||
|   "exports": { | ||||
|     "./translations/*.json": "./src/translations/*.json", | ||||
|     "./config": "./src/config.tsx", | ||||
|     "./i18n": "./src/i18n.ts", | ||||
|     "./messages.d.ts": "./src/messages.d.ts", | ||||
|     "./navigation": "./src/navigation.ts" | ||||
|     "./request": "./src/request.ts", | ||||
|     "./routing": "./src/routing.ts" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "lint:eslint": "eslint src --max-warnings 0 --report-unused-disable-directives", | ||||
|     "lint:eslint": "eslint src --max-warnings 0", | ||||
|     "lint:typescript": "tsc --noEmit", | ||||
|     "test": "vitest run" | ||||
|   }, | ||||
| @@ -30,6 +29,7 @@ | ||||
|     "@types/react-dom": "catalog:", | ||||
|     "@total-typescript/ts-reset": "catalog:", | ||||
|     "eslint": "catalog:", | ||||
|     "typescript-eslint": "catalog:", | ||||
|     "typescript": "catalog:", | ||||
|     "vitest": "catalog:" | ||||
|   } | ||||
|   | ||||
| @@ -1,26 +0,0 @@ | ||||
| import type { Locale } from "@repo/utils/constants" | ||||
| import type { RichTranslationValues } from "next-intl" | ||||
|  | ||||
| export interface LocaleProps { | ||||
|   params: { | ||||
|     locale: Locale | ||||
|   } | ||||
| } | ||||
|  | ||||
| export const defaultTranslationValues: RichTranslationValues = { | ||||
|   br: () => { | ||||
|     return <br /> | ||||
|   }, | ||||
|   strong: (children) => { | ||||
|     return <strong>{children}</strong> | ||||
|   }, | ||||
|   em: (children) => { | ||||
|     return <em>{children}</em> | ||||
|   }, | ||||
|   s: (children) => { | ||||
|     return <s>{children}</s> | ||||
|   }, | ||||
|   u: (children) => { | ||||
|     return <u>{children}</u> | ||||
|   }, | ||||
| } | ||||
| @@ -1,9 +0,0 @@ | ||||
| import { createSharedPathnamesNavigation } from "next-intl/navigation" | ||||
|  | ||||
| import { LOCALES, LOCALE_PREFIX } from "@repo/utils/constants" | ||||
|  | ||||
| export const { Link, redirect, usePathname, useRouter, permanentRedirect } = | ||||
|   createSharedPathnamesNavigation({ | ||||
|     locales: LOCALES, | ||||
|     localePrefix: LOCALE_PREFIX, | ||||
|   }) | ||||
| @@ -1,15 +1,14 @@ | ||||
| import deepmerge from "deepmerge" | ||||
| import type { AbstractIntlMessages } from "next-intl" | ||||
| import { getRequestConfig } from "next-intl/server" | ||||
| import { notFound } from "next/navigation" | ||||
| 
 | ||||
| import type { Locale } from "@repo/utils/constants" | ||||
| import { LOCALE_DEFAULT, LOCALES } from "@repo/utils/constants" | ||||
| import { defaultTranslationValues } from "./config.tsx" | ||||
| 
 | ||||
| export default getRequestConfig(async ({ locale }) => { | ||||
| export default getRequestConfig(async ({ requestLocale }) => { | ||||
|   let locale = await requestLocale | ||||
|   if (!LOCALES.includes(locale as Locale)) { | ||||
|     return notFound() | ||||
|     locale = LOCALE_DEFAULT | ||||
|   } | ||||
| 
 | ||||
|   const userMessages = (await import(`./translations/${locale}.json`)).default | ||||
| @@ -22,7 +21,7 @@ export default getRequestConfig(async ({ locale }) => { | ||||
|   ) | ||||
| 
 | ||||
|   return { | ||||
|     locale, | ||||
|     messages, | ||||
|     defaultTranslationValues, | ||||
|   } | ||||
| }) | ||||
							
								
								
									
										26
									
								
								packages/i18n/src/routing.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								packages/i18n/src/routing.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import { createNavigation } from "next-intl/navigation" | ||||
|  | ||||
| import { LOCALES, LOCALE_DEFAULT, LOCALE_PREFIX } from "@repo/utils/constants" | ||||
| import { defineRouting } from "next-intl/routing" | ||||
| import type { Locale } from "@repo/utils/constants" | ||||
|  | ||||
| export interface LocaleProps { | ||||
|   params: Promise<{ | ||||
|     locale: Locale | ||||
|   }> | ||||
| } | ||||
|  | ||||
| export const routing = defineRouting({ | ||||
|   locales: LOCALES, | ||||
|   defaultLocale: LOCALE_DEFAULT, | ||||
|   localePrefix: LOCALE_PREFIX, | ||||
| }) | ||||
|  | ||||
| export const { | ||||
|   Link, | ||||
|   redirect, | ||||
|   usePathname, | ||||
|   useRouter, | ||||
|   getPathname, | ||||
|   permanentRedirect, | ||||
| } = createNavigation(routing) | ||||
| @@ -10,10 +10,10 @@ | ||||
|         "institution": "Conservatoire National des Arts et Métiers (CNAM), in Eckbolsheim - UIMM Alsace - ITII Alsace", | ||||
|         "study-type": "Engineer in Computer Science and Information Systems (IS)", | ||||
|         "years": { | ||||
|           "2024-2025": { | ||||
|           "2024-2027": { | ||||
|             "courses": {}, | ||||
|             "description": "1st year", | ||||
|             "title": "2024 - 2025" | ||||
|             "description": "", | ||||
|             "title": "2024 - 2027" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|   | ||||
| @@ -10,10 +10,10 @@ | ||||
|         "institution": "Conservatoire National des Arts et Métiers (CNAM) à Eckbolsheim - UIMM Alsace - ITII Alsace", | ||||
|         "study-type": "Ingénieur en Informatique et Systèmes d'Information (SI)", | ||||
|         "years": { | ||||
|           "2024-2025": { | ||||
|           "2024-2027": { | ||||
|             "courses": {}, | ||||
|             "description": "1ère année", | ||||
|             "title": "2024 - 2025" | ||||
|             "description": "", | ||||
|             "title": "2024 - 2027" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "extends": ["@repo/eslint-config/nextjs/.eslintrc.json"], | ||||
|   "overrides": [ | ||||
|     { | ||||
|       "files": ["*.ts", "*.tsx"], | ||||
|       "plugins": ["@typescript-eslint"], | ||||
|       "parser": "@typescript-eslint/parser", | ||||
|       "parserOptions": { | ||||
|         "projectService": true | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										13
									
								
								packages/react-hooks/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/react-hooks/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import configNextjs from "@repo/eslint-config/nextjs" | ||||
|  | ||||
| export default typescriptESLint.config(...configNextjs, { | ||||
|   files: ["**/*.ts", "**/*.tsx"], | ||||
|   languageOptions: { | ||||
|     parser: typescriptESLint.parser, | ||||
|     parserOptions: { | ||||
|       projectService: true, | ||||
|       tsconfigRootDir: import.meta.dirname, | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
| @@ -8,7 +8,7 @@ | ||||
|     "./useIsMounted": "./src/useIsMounted.ts" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "lint:eslint": "eslint src --max-warnings 0 --report-unused-disable-directives", | ||||
|     "lint:eslint": "eslint src --max-warnings 0", | ||||
|     "lint:typescript": "tsc --noEmit", | ||||
|     "test": "vitest run --browser.headless", | ||||
|     "test:ui": "vitest --ui --no-open" | ||||
| @@ -25,10 +25,11 @@ | ||||
|     "@types/react-dom": "catalog:", | ||||
|     "@total-typescript/ts-reset": "catalog:", | ||||
|     "@vitest/browser": "catalog:", | ||||
|     "@vitest/coverage-istanbul": "catalog:", | ||||
|     "@vitest/coverage-v8": "catalog:", | ||||
|     "@vitest/ui": "catalog:", | ||||
|     "eslint": "catalog:", | ||||
|     "playwright": "catalog:", | ||||
|     "typescript-eslint": "catalog:", | ||||
|     "typescript": "catalog:", | ||||
|     "vitest": "catalog:" | ||||
|   } | ||||
|   | ||||
| @@ -1,15 +1,19 @@ | ||||
| import { defineConfig } from "vitest/config" | ||||
|  | ||||
| export default defineConfig({ | ||||
|   optimizeDeps: { | ||||
|     include: ["@vitest/coverage-v8/browser"], | ||||
|   }, | ||||
|   test: { | ||||
|     browser: { | ||||
|       provider: "playwright", | ||||
|       enabled: true, | ||||
|       name: "chromium", | ||||
|       screenshotFailures: false, | ||||
|     }, | ||||
|     coverage: { | ||||
|       enabled: true, | ||||
|       provider: "istanbul", | ||||
|       provider: "v8", | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "extends": ["@repo/eslint-config/nextjs/.eslintrc.json"], | ||||
|   "overrides": [ | ||||
|     { | ||||
|       "files": ["*.ts", "*.tsx"], | ||||
|       "plugins": ["@typescript-eslint"], | ||||
|       "parser": "@typescript-eslint/parser", | ||||
|       "parserOptions": { | ||||
|         "projectService": true | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										13
									
								
								packages/ui/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/ui/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import configNextjs from "@repo/eslint-config/nextjs" | ||||
|  | ||||
| export default typescriptESLint.config(...configNextjs, { | ||||
|   files: ["**/*.ts", "**/*.tsx"], | ||||
|   languageOptions: { | ||||
|     parser: typescriptESLint.parser, | ||||
|     parserOptions: { | ||||
|       projectService: true, | ||||
|       tsconfigRootDir: import.meta.dirname, | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
| @@ -23,7 +23,7 @@ | ||||
|     "./Layout/Section": "./src/Layout/Section/Section.tsx" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "lint:eslint": "eslint src --max-warnings 0 --report-unused-disable-directives", | ||||
|     "lint:eslint": "eslint src --max-warnings 0", | ||||
|     "lint:typescript": "tsc --noEmit" | ||||
|   }, | ||||
|   "dependencies": { | ||||
| @@ -51,6 +51,7 @@ | ||||
|     "eslint": "catalog:", | ||||
|     "postcss": "catalog:", | ||||
|     "tailwindcss": "catalog:", | ||||
|     "typescript-eslint": "catalog:", | ||||
|     "typescript": "catalog:" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -13,7 +13,13 @@ export const CurriculumVitaeAbout: React.FC<CurriculumVitaeAboutProps> = () => { | ||||
|       title={t("curriculum-vitae.about.title")} | ||||
|       icon={<FaUser size={24} />} | ||||
|     > | ||||
|       <p>{t.rich("curriculum-vitae.about.description")}</p> | ||||
|       <p> | ||||
|         {t.rich("curriculum-vitae.about.description", { | ||||
|           br: () => { | ||||
|             return <br /> | ||||
|           }, | ||||
|         })} | ||||
|       </p> | ||||
|     </CurriculumVitaeSection> | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -11,10 +11,10 @@ export const CurriculumVitaeEducation: React.FC< | ||||
|  | ||||
|   const educations = [ | ||||
|     { | ||||
|       years: t("curriculum-vitae.education.cnam.years.2024-2025.title"), | ||||
|       years: t("curriculum-vitae.education.cnam.years.2024-2027.title"), | ||||
|       studyType: t("curriculum-vitae.education.cnam.study-type"), | ||||
|       institution: t("curriculum-vitae.education.cnam.institution"), | ||||
|       score: t("curriculum-vitae.education.cnam.years.2024-2025.description"), | ||||
|       score: t("curriculum-vitae.education.cnam.years.2024-2027.description"), | ||||
|       courses: [], | ||||
|     }, | ||||
|     { | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { Link } from "@repo/i18n/navigation" | ||||
| import { Link } from "@repo/i18n/routing" | ||||
| import { useTranslations } from "next-intl" | ||||
| import Image from "next/image" | ||||
| import { BirthDate } from "../Home/About/AboutList/BirthDate.tsx" | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { classNames } from "@repo/config-tailwind/classNames" | ||||
| import { Link as NextLink } from "@repo/i18n/navigation" | ||||
| import { Link as NextLink } from "@repo/i18n/routing" | ||||
| import type { VariantProps } from "cva" | ||||
| import { cva } from "cva" | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { classNames } from "@repo/config-tailwind/classNames" | ||||
| import { Link as NextLink } from "@repo/i18n/navigation" | ||||
| import { Link as NextLink } from "@repo/i18n/routing" | ||||
| import { FiExternalLink } from "react-icons/fi" | ||||
|  | ||||
| export interface LinkProps extends React.ComponentProps<typeof NextLink> { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| "use client" | ||||
|  | ||||
| import { useRouter } from "@repo/i18n/navigation" | ||||
| import { useRouter } from "@repo/i18n/routing" | ||||
| import { useTranslations } from "next-intl" | ||||
| import { useEffect, useTransition } from "react" | ||||
| import { Button } from "../../Design/Button/Button.tsx" | ||||
|   | ||||
| @@ -10,7 +10,11 @@ export const AboutDescription: React.FC<AboutDescriptionProps> = () => { | ||||
|   return ( | ||||
|     <div className="dark:text-gray my-6 max-w-md text-center text-black"> | ||||
|       <Typography as="p" variant="text1" className="my-6"> | ||||
|         {t.rich("home.about.description")} | ||||
|         {t.rich("home.about.description", { | ||||
|           strong: (children) => { | ||||
|             return <strong>{children}</strong> | ||||
|           }, | ||||
|         })} | ||||
|       </Typography> | ||||
|  | ||||
|       <Button href="/curriculum-vitae" variant="outline"> | ||||
|   | ||||
| @@ -19,6 +19,12 @@ export const Interests: React.FC<InterestsProps> = () => { | ||||
|       id: "code", | ||||
|       title: t("home.interests.code.title"), | ||||
|       description: t.rich("home.interests.code.description", { | ||||
|         br: () => { | ||||
|           return <br /> | ||||
|         }, | ||||
|         strong: (children) => { | ||||
|           return <strong>{children}</strong> | ||||
|         }, | ||||
|         "abbr-ux": (children) => { | ||||
|           return <abbr title="User Experience">{children}</abbr> | ||||
|         }, | ||||
| @@ -29,6 +35,12 @@ export const Interests: React.FC<InterestsProps> = () => { | ||||
|       id: "open-source", | ||||
|       title: t("home.interests.open-source.title"), | ||||
|       description: t.rich("home.interests.open-source.description", { | ||||
|         br: () => { | ||||
|           return <br /> | ||||
|         }, | ||||
|         strong: (children) => { | ||||
|           return <strong>{children}</strong> | ||||
|         }, | ||||
|         "github-link": (children) => { | ||||
|           return ( | ||||
|             <Link href={GIT_REPO_LINK} target="_blank"> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| "use client" | ||||
|  | ||||
| import { classNames } from "@repo/config-tailwind/classNames" | ||||
| import { usePathname, useRouter } from "@repo/i18n/navigation" | ||||
| import { usePathname, useRouter } from "@repo/i18n/routing" | ||||
| import type { Locale } from "@repo/utils/constants" | ||||
| import { LOCALES } from "@repo/utils/constants" | ||||
| import { useLocale } from "next-intl" | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "extends": ["@repo/eslint-config"], | ||||
|   "overrides": [ | ||||
|     { | ||||
|       "files": ["*.ts", "*.tsx"], | ||||
|       "plugins": ["@typescript-eslint"], | ||||
|       "parser": "@typescript-eslint/parser", | ||||
|       "parserOptions": { | ||||
|         "projectService": true | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										13
									
								
								packages/utils/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/utils/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import typescriptESLint from "typescript-eslint" | ||||
| import config from "@repo/eslint-config" | ||||
|  | ||||
| export default typescriptESLint.config(...config, { | ||||
|   files: ["**/*.ts", "**/*.tsx"], | ||||
|   languageOptions: { | ||||
|     parser: typescriptESLint.parser, | ||||
|     parserOptions: { | ||||
|       projectService: true, | ||||
|       tsconfigRootDir: import.meta.dirname, | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
| @@ -9,7 +9,7 @@ | ||||
|     "./strings": "./src/strings.ts" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "lint:eslint": "eslint src --max-warnings 0 --report-unused-disable-directives", | ||||
|     "lint:eslint": "eslint src --max-warnings 0", | ||||
|     "lint:typescript": "tsc --noEmit", | ||||
|     "test": "vitest run", | ||||
|     "test:ui": "vitest --ui --no-open" | ||||
| @@ -19,9 +19,10 @@ | ||||
|     "@repo/config-typescript": "workspace:*", | ||||
|     "@types/node": "catalog:", | ||||
|     "@total-typescript/ts-reset": "catalog:", | ||||
|     "@vitest/coverage-istanbul": "catalog:", | ||||
|     "@vitest/coverage-v8": "catalog:", | ||||
|     "@vitest/ui": "catalog:", | ||||
|     "eslint": "catalog:", | ||||
|     "typescript-eslint": "catalog:", | ||||
|     "typescript": "catalog:", | ||||
|     "vitest": "catalog:" | ||||
|   } | ||||
|   | ||||
| @@ -1,10 +1,7 @@ | ||||
| import packageJSON from "../package.json" | ||||
|  | ||||
| export const VERSION = | ||||
|   process.env["NODE_ENV"] === "development" | ||||
|     ? "0.0.0-development" | ||||
|     : packageJSON.version | ||||
| import packageJSON from "../package.json" with { type: "json" } | ||||
|  | ||||
| export const IS_PRODUCTION = process.env["NODE_ENV"] === "production" | ||||
| export const VERSION = IS_PRODUCTION ? packageJSON.version : "0.0.0-development" | ||||
| export const GIT_REPO_LINK = "https://github.com/theoludwig/theoludwig" | ||||
|  | ||||
| export const LOCALES = ["en-US", "fr-FR"] as const | ||||
| @@ -16,6 +13,8 @@ export const THEMES = ["light", "dark"] as const | ||||
| export type Theme = (typeof THEMES)[number] | ||||
| export const THEME_DEFAULT = "light" as Theme | ||||
|  | ||||
| export const TIMEZONE = process.env["TZ"] ?? "UTC" | ||||
|  | ||||
| export const BIRTH_DATE_DAY = "31" | ||||
| export const BIRTH_DATE_MONTH = "03" | ||||
| export const BIRTH_DATE_YEAR = "2003" | ||||
|   | ||||
| @@ -4,7 +4,7 @@ export default defineConfig({ | ||||
|   test: { | ||||
|     coverage: { | ||||
|       enabled: true, | ||||
|       provider: "istanbul", | ||||
|       provider: "v8", | ||||
|     }, | ||||
|   }, | ||||
| }) | ||||
|   | ||||
							
								
								
									
										6086
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6086
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -4,7 +4,7 @@ packages: | ||||
|  | ||||
| catalog: | ||||
|   # Turborepo and Releases | ||||
|   "turbo": "2.1.3" | ||||
|   "turbo": "2.2.3" | ||||
|   "@saithodev/semantic-release-backmerge": "4.0.1" | ||||
|   "@semantic-release/exec": "6.0.3" | ||||
|   "@semantic-release/git": "10.0.1" | ||||
| @@ -15,13 +15,13 @@ catalog: | ||||
|   "deepmerge": "4.3.1" | ||||
|  | ||||
|   # React.js/Next.js | ||||
|   "next": "14.2.15" | ||||
|   "next-intl": "3.21.1" | ||||
|   "next-themes": "0.3.0" | ||||
|   "next": "15.0.3" | ||||
|   "next-intl": "3.25.0" | ||||
|   "next-themes": "0.4.3" | ||||
|   "react": "18.3.1" | ||||
|   "react-dom": "18.3.1" | ||||
|   "react-icons": "5.3.0" | ||||
|   "@types/react": "18.3.11" | ||||
|   "@types/react": "18.3.12" | ||||
|   "@types/react-dom": "18.3.1" | ||||
|  | ||||
|   # Blog | ||||
| @@ -29,76 +29,76 @@ catalog: | ||||
|   "gray-matter": "4.0.3" | ||||
|   "katex": "0.16.11" | ||||
|   "next-mdx-remote": "5.0.0" | ||||
|   "@mdx-js/mdx": "3.0.1" | ||||
|   "@mdx-js/mdx": "3.1.0" | ||||
|   "rehype-katex": "7.0.1" | ||||
|   "rehype-raw": "7.0.0" | ||||
|   "rehype-slug": "6.0.0" | ||||
|   "remark-gfm": "4.0.0" | ||||
|   "remark-math": "6.0.0" | ||||
|   "shiki": "1.22.0" | ||||
|   "@shikijs/rehype": "1.22.0" | ||||
|  | ||||
|   # TypeScript | ||||
|   "typescript": "5.5.4" | ||||
|   "@total-typescript/ts-reset": "0.6.1" | ||||
|   "@types/node": "22.7.5" | ||||
|  | ||||
|   # ESLint | ||||
|   "@typescript-eslint/eslint-plugin": "8.8.1" | ||||
|   "@typescript-eslint/parser": "8.8.1" | ||||
|   "eslint": "8.57.1" | ||||
|   "eslint-config-conventions": "16.0.1" | ||||
|   "eslint-plugin-promise": "7.1.0" | ||||
|   "eslint-plugin-unicorn": "55.0.0" | ||||
|   "eslint-config-next": "14.2.15" | ||||
|   "eslint-plugin-storybook": "0.9.0" | ||||
|   "eslint-plugin-tailwindcss": "3.17.5" | ||||
|   "eslint-plugin-import-x": "4.3.1" | ||||
|  | ||||
|   # Prettier | ||||
|   "prettier": "3.3.3" | ||||
|   "prettier-plugin-tailwindcss": "0.6.8" | ||||
|   "editorconfig-checker": "6.0.0" | ||||
|   "shiki": "1.22.2" | ||||
|   "@shikijs/rehype": "1.22.2" | ||||
|  | ||||
|   # Markdown Lint | ||||
|   "markdownlint-cli2": "0.14.0" | ||||
|   "markdownlint": "0.35.0" | ||||
|   "markdownlint-rule-relative-links": "3.0.0" | ||||
|  | ||||
|   # TypeScript | ||||
|   "typescript": "5.6.3" | ||||
|   "@total-typescript/ts-reset": "0.6.1" | ||||
|   "@types/node": "22.9.0" | ||||
|  | ||||
|   # ESLint | ||||
|   "globals": "15.12.0" | ||||
|   "typescript-eslint": "8.13.0" | ||||
|   "@eslint/eslintrc": "3.1.0" | ||||
|   "eslint": "9.14.0" | ||||
|   "eslint-config-conventions": "17.0.0" | ||||
|   "eslint-plugin-promise": "7.1.0" | ||||
|   "eslint-plugin-unicorn": "56.0.0" | ||||
|   "eslint-config-next": "15.0.3" | ||||
|   "eslint-plugin-storybook": "0.11.0" | ||||
|   "eslint-plugin-tailwindcss": "3.17.5" | ||||
|   "eslint-plugin-import-x": "4.4.0" | ||||
|  | ||||
|   # Prettier | ||||
|   "prettier": "3.3.3" | ||||
|   "prettier-plugin-tailwindcss": "0.6.8" | ||||
|   "editorconfig-checker": "6.0.0" | ||||
|  | ||||
|   # Storybook | ||||
|   "@chromatic-com/storybook": "2.0.2" | ||||
|   "@storybook/addon-a11y": "8.3.5" | ||||
|   "@storybook/addon-essentials": "8.3.5" | ||||
|   "@storybook/addon-interactions": "8.3.5" | ||||
|   "@storybook/addon-links": "8.3.5" | ||||
|   "@storybook/addon-storysource": "8.3.5" | ||||
|   "@storybook/addon-themes": "8.3.5" | ||||
|   "@storybook/blocks": "8.3.5" | ||||
|   "@storybook/nextjs": "8.3.5" | ||||
|   "@storybook/react": "8.3.5" | ||||
|   "@storybook/test": "8.3.5" | ||||
|   "@chromatic-com/storybook": "3.2.2" | ||||
|   "@storybook/addon-a11y": "8.4.2" | ||||
|   "@storybook/addon-essentials": "8.4.2" | ||||
|   "@storybook/addon-interactions": "8.4.2" | ||||
|   "@storybook/addon-storysource": "8.4.2" | ||||
|   "@storybook/addon-themes": "8.4.2" | ||||
|   "@storybook/blocks": "8.4.2" | ||||
|   "@storybook/nextjs": "8.4.2" | ||||
|   "@storybook/react": "8.4.2" | ||||
|   "@storybook/test": "8.4.2" | ||||
|   "@storybook/test-runner": "0.19.1" | ||||
|   "chromatic": "11.12.5" | ||||
|   "chromatic": "11.18.0" | ||||
|   "http-server": "14.1.1" | ||||
|   "storybook": "8.3.5" | ||||
|   "storybook": "8.4.2" | ||||
|   "storybook-dark-mode": "4.0.2" | ||||
|  | ||||
|   # Testing | ||||
|   "playwright": "1.48.0" | ||||
|   "@playwright/test": "1.48.0" | ||||
|   "playwright": "1.48.2" | ||||
|   "@playwright/test": "1.48.2" | ||||
|   "axe-playwright": "2.0.3" | ||||
|   "start-server-and-test": "2.0.8" | ||||
|   "@vitest/browser": "2.1.2" | ||||
|   "@vitest/coverage-istanbul": "2.1.2" | ||||
|   "@vitest/ui": "2.1.2" | ||||
|   "vitest": "2.1.2" | ||||
|   "@vitest/browser": "2.1.4" | ||||
|   "@vitest/coverage-v8": "2.1.4" | ||||
|   "@vitest/ui": "2.1.4" | ||||
|   "vitest": "2.1.4" | ||||
|   "@testing-library/react": "16.0.1" | ||||
|  | ||||
|   # CSS | ||||
|   "postcss": "8.4.47" | ||||
|   "tailwindcss": "3.4.13" | ||||
|   "tailwind-merge": "2.5.3" | ||||
|   "tailwindcss": "3.4.14" | ||||
|   "@tailwindcss/typography": "0.5.15" | ||||
|   "@fontsource/montserrat": "5.1.0" | ||||
|   "clsx": "2.1.0" | ||||
|   "tailwind-merge": "2.5.4" | ||||
|   "clsx": "2.0.0" | ||||
|   "cva": "1.0.0-beta.1" | ||||
|   "@fontsource/montserrat": "5.1.0" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user