diff --git a/.gitignore b/.gitignore index 61a20a6..4b30b1a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ yarn.lock node_modules +.react-router/ + # Editor Configs .idea .vscode diff --git a/bun.lockb b/bun.lockb index 40c4f73..180b0ce 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 33636b7..fa49fc4 100644 --- a/package.json +++ b/package.json @@ -22,5 +22,5 @@ "@biomejs/biome": "^1.9.4", "typescript": "^5.7.2" }, - "trustedDependencies": ["@biomejs/biome"] + "trustedDependencies": ["@biomejs/biome", "esbuild"] } diff --git a/packages/www/app/components/misc/client-hints.tsx b/packages/www/app/components/misc/client-hints.tsx index 06d48c3..322b4d7 100644 --- a/packages/www/app/components/misc/client-hints.tsx +++ b/packages/www/app/components/misc/client-hints.tsx @@ -1,5 +1,5 @@ import { useEffect } from 'react' -import { useRevalidator } from '@remix-run/react' +import { useRevalidator } from 'react-router'; import { subscribeToSchemeChange } from '@epic-web/client-hints/color-scheme' import { hintsUtils } from '#app/utils/hooks/use-hints' diff --git a/packages/www/app/components/misc/error-boundary.tsx b/packages/www/app/components/misc/error-boundary.tsx index 98c129b..6e29f90 100644 --- a/packages/www/app/components/misc/error-boundary.tsx +++ b/packages/www/app/components/misc/error-boundary.tsx @@ -1,5 +1,5 @@ -import type { ErrorResponse } from '@remix-run/router' -import { isRouteErrorResponse, useParams, useRouteError } from '@remix-run/react' +import type { ErrorResponse } from 'react-router'; +import { isRouteErrorResponse, useParams, useRouteError } from 'react-router'; type StatusHandler = (info: { error: ErrorResponse diff --git a/packages/www/app/components/misc/language-switcher.tsx b/packages/www/app/components/misc/language-switcher.tsx index dee2a77..2895b21 100644 --- a/packages/www/app/components/misc/language-switcher.tsx +++ b/packages/www/app/components/misc/language-switcher.tsx @@ -1,4 +1,4 @@ -import { useNavigate } from '@remix-run/react' +import { useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next' import { LucideLanguages } from 'lucide-react' import { diff --git a/packages/www/app/components/misc/theme-switcher.tsx b/packages/www/app/components/misc/theme-switcher.tsx index 03eb4dd..394c041 100644 --- a/packages/www/app/components/misc/theme-switcher.tsx +++ b/packages/www/app/components/misc/theme-switcher.tsx @@ -1,5 +1,5 @@ import type { Theme, ThemeExtended } from '#app/utils/hooks/use-theme' -import { useSubmit } from '@remix-run/react' +import { useSubmit } from 'react-router'; import { LucideSun, LucideMoon, LucideMonitor } from 'lucide-react' import { useOptimisticThemeMode } from '#app/utils/hooks/use-theme' import { cn } from '#app/utils/misc' diff --git a/packages/www/app/components/navigation.tsx b/packages/www/app/components/navigation.tsx index ae4b27b..f857cd4 100644 --- a/packages/www/app/components/navigation.tsx +++ b/packages/www/app/components/navigation.tsx @@ -1,4 +1,4 @@ -import { Link, useLocation, useNavigate, useSubmit } from '@remix-run/react' +import { Link, useLocation, useNavigate, useSubmit } from 'react-router'; import { LucideCheck, LucideChevronDown, diff --git a/packages/www/app/entry.client.tsx b/packages/www/app/entry.client.tsx index 7a880f6..c62c264 100644 --- a/packages/www/app/entry.client.tsx +++ b/packages/www/app/entry.client.tsx @@ -1,4 +1,4 @@ -import { RemixBrowser } from '@remix-run/react' +import { HydratedRouter } from 'react-router/dom'; import i18next from 'i18next' import I18nextBrowserLanguageDetector from 'i18next-browser-languagedetector' import { startTransition } from 'react' @@ -27,7 +27,7 @@ async function main() { hydrateRoot( document, - + , ) }) diff --git a/packages/www/app/entry.server.tsx b/packages/www/app/entry.server.tsx index 7c4daba..0b1034f 100644 --- a/packages/www/app/entry.server.tsx +++ b/packages/www/app/entry.server.tsx @@ -1,9 +1,9 @@ -import type { AppLoadContext, EntryContext } from '@remix-run/node' +import type { AppLoadContext, EntryContext } from 'react-router'; import { isbot } from 'isbot' import { PassThrough } from 'node:stream' import crypto from 'node:crypto' -import { RemixServer } from '@remix-run/react' -import { createReadableStreamFromReadable } from '@remix-run/node' +import { ServerRouter } from 'react-router'; +import { createReadableStreamFromReadable } from '@react-router/node'; import { renderToPipeableStream } from 'react-dom/server' import { createInstance } from 'i18next' import { I18nextProvider, initReactI18next } from 'react-i18next' @@ -17,7 +17,7 @@ export default async function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, - remixContext: EntryContext, + reactRouterContext: EntryContext, _: AppLoadContext, ) { const callbackName = isbot(request.headers.get('user-agent')) @@ -40,7 +40,7 @@ export default async function handleRequest( */ const instance = createInstance() const lng = await i18nServer.getLocale(request) - const ns = i18nServer.getRouteNamespaces(remixContext) + const ns = i18nServer.getRouteNamespaces(reactRouterContext) await instance.use(initReactI18next).init({ ...i18n, @@ -54,7 +54,7 @@ export default async function handleRequest( const { pipe, abort } = renderToPipeableStream( - + , { @@ -91,5 +91,5 @@ export default async function handleRequest( ) setTimeout(abort, streamTimeout + 1000) - }) + }); } diff --git a/packages/www/app/modules/auth/auth-session.server.ts b/packages/www/app/modules/auth/auth-session.server.ts index 43edf9d..c442586 100644 --- a/packages/www/app/modules/auth/auth-session.server.ts +++ b/packages/www/app/modules/auth/auth-session.server.ts @@ -1,4 +1,4 @@ -import { createCookieSessionStorage } from '@remix-run/node' +import { createCookieSessionStorage } from 'react-router'; import { Resource } from 'sst' export const AUTH_SESSION_KEY = '_auth' diff --git a/packages/www/app/modules/auth/auth.server.ts b/packages/www/app/modules/auth/auth.server.ts index f7c826e..212aede 100644 --- a/packages/www/app/modules/auth/auth.server.ts +++ b/packages/www/app/modules/auth/auth.server.ts @@ -1,6 +1,6 @@ import { db, schema } from '@company/core/src/drizzle/index' import { Email } from '@company/core/src/email/index' -import { redirect } from '@remix-run/node' +import { redirect } from 'react-router'; import { eq } from 'drizzle-orm' import { Authenticator } from 'remix-auth' import { GitHubStrategy } from 'remix-auth-github' diff --git a/packages/www/app/modules/i18n/i18n.server.ts b/packages/www/app/modules/i18n/i18n.server.ts index 91eda75..14ae638 100644 --- a/packages/www/app/modules/i18n/i18n.server.ts +++ b/packages/www/app/modules/i18n/i18n.server.ts @@ -1,4 +1,4 @@ -import { createCookie } from '@remix-run/node' +import { createCookie } from 'react-router'; import { RemixI18Next } from 'remix-i18next/server' import * as i18n from '#app/modules/i18n/i18n' diff --git a/packages/www/app/root.tsx b/packages/www/app/root.tsx index 20c54c8..2763bcd 100644 --- a/packages/www/app/root.tsx +++ b/packages/www/app/root.tsx @@ -1,14 +1,7 @@ -import type { MetaFunction, LinksFunction, LoaderFunctionArgs } from '@remix-run/node' +import type { MetaFunction, LinksFunction, LoaderFunctionArgs } from 'react-router'; import type { Theme } from '#app/utils/hooks/use-theme' -import { - Links, - Meta, - Outlet, - Scripts, - ScrollRestoration, - useLoaderData, -} from '@remix-run/react' -import { data } from '@remix-run/node' +import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from 'react-router'; +import { data } from 'react-router'; import { useChangeLanguage } from 'remix-i18next/react' import { AuthenticityTokenProvider } from 'remix-utils/csrf/react' import { HoneypotProvider } from 'remix-utils/honeypot/react' diff --git a/packages/www/app/routes.ts b/packages/www/app/routes.ts index 46a09d6..0ed3fc9 100644 --- a/packages/www/app/routes.ts +++ b/packages/www/app/routes.ts @@ -1,5 +1,5 @@ -import type { RouteConfig } from '@remix-run/route-config' -import { remixRoutesOptionAdapter } from '@remix-run/routes-option-adapter' +import type { RouteConfig } from '@react-router/dev/routes'; +import { remixRoutesOptionAdapter } from '@react-router/remix-routes-option-adapter'; import { flatRoutes } from 'remix-flat-routes' export default remixRoutesOptionAdapter((defineRoutes) => diff --git a/packages/www/app/routes/$.tsx b/packages/www/app/routes/$.tsx index 4874f7e..c4d6d6a 100644 --- a/packages/www/app/routes/$.tsx +++ b/packages/www/app/routes/$.tsx @@ -1,5 +1,5 @@ -import type { MetaFunction } from '@remix-run/node' -import { Link } from '@remix-run/react' +import type { MetaFunction } from 'react-router'; +import { Link } from 'react-router'; import { LucideHelpCircle, LucideExternalLink } from 'lucide-react' import { siteConfig } from '#app/utils/constants/brand' import { GenericErrorBoundary } from '#app/components/misc/error-boundary' diff --git a/packages/www/app/routes/_home+/_index.tsx b/packages/www/app/routes/_home+/_index.tsx index 90f934f..b8f2add 100644 --- a/packages/www/app/routes/_home+/_index.tsx +++ b/packages/www/app/routes/_home+/_index.tsx @@ -1,5 +1,5 @@ -import type { MetaFunction, LoaderFunctionArgs } from '@remix-run/node' -import { Link, useLoaderData } from '@remix-run/react' +import type { MetaFunction, LoaderFunctionArgs } from 'react-router'; +import { Link, useLoaderData } from 'react-router'; import Markdown from 'react-markdown' import rehypeSlug from 'rehype-slug' import { authenticator } from '#app/modules/auth/auth.server' diff --git a/packages/www/app/routes/_home+/_layout.tsx b/packages/www/app/routes/_home+/_layout.tsx index aa99a0a..c6deb4c 100644 --- a/packages/www/app/routes/_home+/_layout.tsx +++ b/packages/www/app/routes/_home+/_layout.tsx @@ -1,4 +1,4 @@ -import { Outlet } from '@remix-run/react' +import { Outlet } from 'react-router'; export const ROUTE_PATH = '/' as const diff --git a/packages/www/app/routes/admin+/_index.tsx b/packages/www/app/routes/admin+/_index.tsx index 28b46b2..eaa0332 100644 --- a/packages/www/app/routes/admin+/_index.tsx +++ b/packages/www/app/routes/admin+/_index.tsx @@ -1,4 +1,4 @@ -import type { LoaderFunctionArgs } from '@remix-run/node' +import type { LoaderFunctionArgs } from 'react-router'; import { requireUserWithRole } from '#app/utils/permissions.server' export async function loader({ request }: LoaderFunctionArgs) { diff --git a/packages/www/app/routes/admin+/_layout.tsx b/packages/www/app/routes/admin+/_layout.tsx index d05b2c7..f9fcc9f 100644 --- a/packages/www/app/routes/admin+/_layout.tsx +++ b/packages/www/app/routes/admin+/_layout.tsx @@ -1,5 +1,5 @@ -import type { MetaFunction, LoaderFunctionArgs } from '@remix-run/node' -import { useLoaderData } from '@remix-run/react' +import type { MetaFunction, LoaderFunctionArgs } from 'react-router'; +import { useLoaderData } from 'react-router'; import { LucideShoppingBasket, LucideExternalLink } from 'lucide-react' import { requireUserWithRole } from '#app/utils/permissions.server' import { cn } from '#app/utils/misc.js' diff --git a/packages/www/app/routes/auth+/$provider.callback.tsx b/packages/www/app/routes/auth+/$provider.callback.tsx index ffd2aef..a0074e9 100644 --- a/packages/www/app/routes/auth+/$provider.callback.tsx +++ b/packages/www/app/routes/auth+/$provider.callback.tsx @@ -1,4 +1,4 @@ -import type { LoaderFunctionArgs } from '@remix-run/node' +import type { LoaderFunctionArgs } from 'react-router'; import { authenticator } from '#app/modules/auth/auth.server' import { ROUTE_PATH as LOGIN_PATH } from '#app/routes/auth+/login' import { ROUTE_PATH as DASHBOARD_PATH } from '#app/routes/dashboard+/_layout' diff --git a/packages/www/app/routes/auth+/$provider.tsx b/packages/www/app/routes/auth+/$provider.tsx index d63a3ae..0c6ae24 100644 --- a/packages/www/app/routes/auth+/$provider.tsx +++ b/packages/www/app/routes/auth+/$provider.tsx @@ -1,5 +1,5 @@ -import type { ActionFunctionArgs } from '@remix-run/node' -import { redirect } from '@remix-run/node' +import type { ActionFunctionArgs } from 'react-router'; +import { redirect } from 'react-router'; import { authenticator } from '#app/modules/auth/auth.server' import { ROUTE_PATH as LOGIN_PATH } from '#app/routes/auth+/login' diff --git a/packages/www/app/routes/auth+/_layout.tsx b/packages/www/app/routes/auth+/_layout.tsx index 7807b2d..55f1f31 100644 --- a/packages/www/app/routes/auth+/_layout.tsx +++ b/packages/www/app/routes/auth+/_layout.tsx @@ -1,6 +1,6 @@ -import type { LoaderFunctionArgs } from '@remix-run/node' -import { Link, Outlet } from '@remix-run/react' -import { redirect } from '@remix-run/node' +import type { LoaderFunctionArgs } from 'react-router'; +import { Link, Outlet } from 'react-router'; +import { redirect } from 'react-router'; import { authenticator } from '#app/modules/auth/auth.server' import { getDomainPathname } from '#app/utils/misc.server' import { ROUTE_PATH as HOME_PATH } from '#app/routes/_home+/_layout' diff --git a/packages/www/app/routes/auth+/login.tsx b/packages/www/app/routes/auth+/login.tsx index fcc6211..dbc8a7a 100644 --- a/packages/www/app/routes/auth+/login.tsx +++ b/packages/www/app/routes/auth+/login.tsx @@ -1,11 +1,7 @@ -import type { - MetaFunction, - LoaderFunctionArgs, - ActionFunctionArgs, -} from '@remix-run/node' +import type { MetaFunction, LoaderFunctionArgs, ActionFunctionArgs } from 'react-router'; import { useRef, useEffect } from 'react' -import { Form, useLoaderData } from '@remix-run/react' -import { data } from '@remix-run/node' +import { Form, useLoaderData } from 'react-router'; +import { data } from 'react-router'; import { useHydrated } from 'remix-utils/use-hydrated' import { AuthenticityTokenInput } from 'remix-utils/csrf/react' import { HoneypotInputs } from 'remix-utils/honeypot/react' diff --git a/packages/www/app/routes/auth+/logout.tsx b/packages/www/app/routes/auth+/logout.tsx index e632951..c9fba32 100644 --- a/packages/www/app/routes/auth+/logout.tsx +++ b/packages/www/app/routes/auth+/logout.tsx @@ -1,4 +1,4 @@ -import type { LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node' +import type { LoaderFunctionArgs, ActionFunctionArgs } from 'react-router'; import { authenticator } from '#app/modules/auth/auth.server' export const ROUTE_PATH = '/auth/logout' as const diff --git a/packages/www/app/routes/auth+/magic-link.tsx b/packages/www/app/routes/auth+/magic-link.tsx index 1045dde..0cb336e 100644 --- a/packages/www/app/routes/auth+/magic-link.tsx +++ b/packages/www/app/routes/auth+/magic-link.tsx @@ -1,4 +1,4 @@ -import type { LoaderFunctionArgs } from '@remix-run/node' +import type { LoaderFunctionArgs } from 'react-router'; import { authenticator } from '#app/modules/auth/auth.server' import { ROUTE_PATH as DASHBOARD_PATH } from '#app/routes/dashboard+/_layout' diff --git a/packages/www/app/routes/auth+/verify.tsx b/packages/www/app/routes/auth+/verify.tsx index 4ab7fce..c873d73 100644 --- a/packages/www/app/routes/auth+/verify.tsx +++ b/packages/www/app/routes/auth+/verify.tsx @@ -1,11 +1,7 @@ -import type { - MetaFunction, - LoaderFunctionArgs, - ActionFunctionArgs, -} from '@remix-run/node' +import type { MetaFunction, LoaderFunctionArgs, ActionFunctionArgs } from 'react-router'; import { useRef, useEffect } from 'react' -import { Form, useLoaderData } from '@remix-run/react' -import { data, redirect } from '@remix-run/node' +import { Form, useLoaderData } from 'react-router'; +import { data, redirect } from 'react-router'; import { useHydrated } from 'remix-utils/use-hydrated' import { AuthenticityTokenInput } from 'remix-utils/csrf/react' import { HoneypotInputs } from 'remix-utils/honeypot/react' diff --git a/packages/www/app/routes/dashboard+/_index.tsx b/packages/www/app/routes/dashboard+/_index.tsx index 5886a60..11b0652 100644 --- a/packages/www/app/routes/dashboard+/_index.tsx +++ b/packages/www/app/routes/dashboard+/_index.tsx @@ -1,4 +1,4 @@ -import type { MetaFunction, LoaderFunctionArgs } from '@remix-run/node' +import type { MetaFunction, LoaderFunctionArgs } from 'react-router'; import { LucidePlus, LucideExternalLink } from 'lucide-react' import { useTranslation } from 'react-i18next' import { requireUser } from '#app/modules/auth/auth.server' diff --git a/packages/www/app/routes/dashboard+/_layout.tsx b/packages/www/app/routes/dashboard+/_layout.tsx index fc6389f..9b8ef45 100644 --- a/packages/www/app/routes/dashboard+/_layout.tsx +++ b/packages/www/app/routes/dashboard+/_layout.tsx @@ -1,6 +1,6 @@ -import type { LoaderFunctionArgs } from '@remix-run/node' -import { Outlet, useLoaderData } from '@remix-run/react' -import { redirect } from '@remix-run/node' +import type { LoaderFunctionArgs } from 'react-router'; +import { Outlet, useLoaderData } from 'react-router'; +import { redirect } from 'react-router'; import { requireUser } from '#app/modules/auth/auth.server' import { ROUTE_PATH as ONBOARDING_USERNAME_PATH } from '#app/routes/onboarding+/username' import { Navigation } from '#app/components/navigation' diff --git a/packages/www/app/routes/dashboard+/checkout.tsx b/packages/www/app/routes/dashboard+/checkout.tsx index f8f1dbd..622eb29 100644 --- a/packages/www/app/routes/dashboard+/checkout.tsx +++ b/packages/www/app/routes/dashboard+/checkout.tsx @@ -1,7 +1,7 @@ -import type { MetaFunction, LoaderFunctionArgs } from '@remix-run/node' +import type { MetaFunction, LoaderFunctionArgs } from 'react-router'; import { useState } from 'react' -import { Link, useLoaderData, useRevalidator } from '@remix-run/react' -import { redirect } from '@remix-run/node' +import { Link, useLoaderData, useRevalidator } from 'react-router'; +import { redirect } from 'react-router'; import { LucideLoader2, LucideBadgeCheck, diff --git a/packages/www/app/routes/dashboard+/settings.billing.tsx b/packages/www/app/routes/dashboard+/settings.billing.tsx index a70986d..72a71ba 100644 --- a/packages/www/app/routes/dashboard+/settings.billing.tsx +++ b/packages/www/app/routes/dashboard+/settings.billing.tsx @@ -1,12 +1,8 @@ -import type { - MetaFunction, - LoaderFunctionArgs, - ActionFunctionArgs, -} from '@remix-run/node' +import type { MetaFunction, LoaderFunctionArgs, ActionFunctionArgs } from 'react-router'; import type { Interval, Plan as PlanEnum } from '@company/core/src/constants' import { useState } from 'react' -import { Form, useLoaderData } from '@remix-run/react' -import { redirect } from '@remix-run/node' +import { Form, useLoaderData } from 'react-router'; +import { redirect } from 'react-router'; import { requireSessionUser } from '#app/modules/auth/auth.server' import { PLANS, PRICING_PLANS, INTERVALS, CURRENCIES } from '@company/core/src/constants' import { getLocaleCurrency } from '#app/utils/misc.server' diff --git a/packages/www/app/routes/dashboard+/settings.index.tsx b/packages/www/app/routes/dashboard+/settings.index.tsx index ee40c8f..e26996c 100644 --- a/packages/www/app/routes/dashboard+/settings.index.tsx +++ b/packages/www/app/routes/dashboard+/settings.index.tsx @@ -1,7 +1,7 @@ -import type { LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node' +import type { LoaderFunctionArgs, ActionFunctionArgs } from 'react-router'; import { useRef, useState } from 'react' -import { Form, useFetcher, useLoaderData, useActionData } from '@remix-run/react' -import { data, redirect } from '@remix-run/node' +import { Form, useFetcher, useLoaderData, useActionData } from 'react-router'; +import { data, redirect } from 'react-router'; import { z } from 'zod' import { getZodConstraint, parseWithZod } from '@conform-to/zod' import { getFormProps, getInputProps, useForm } from '@conform-to/react' diff --git a/packages/www/app/routes/dashboard+/settings.tsx b/packages/www/app/routes/dashboard+/settings.tsx index c41e9de..907ea89 100644 --- a/packages/www/app/routes/dashboard+/settings.tsx +++ b/packages/www/app/routes/dashboard+/settings.tsx @@ -1,5 +1,5 @@ -import type { MetaFunction, LoaderFunctionArgs } from '@remix-run/node' -import { Link, Outlet, useLocation } from '@remix-run/react' +import type { MetaFunction, LoaderFunctionArgs } from 'react-router'; +import { Link, Outlet, useLocation } from 'react-router'; import { z } from 'zod' import { requireUser } from '#app/modules/auth/auth.server' import { cn } from '#app/utils/misc' diff --git a/packages/www/app/routes/onboarding+/_layout.tsx b/packages/www/app/routes/onboarding+/_layout.tsx index cdd87c5..8b1a5b9 100644 --- a/packages/www/app/routes/onboarding+/_layout.tsx +++ b/packages/www/app/routes/onboarding+/_layout.tsx @@ -1,6 +1,6 @@ -import type { LoaderFunctionArgs } from '@remix-run/node' -import { Outlet } from '@remix-run/react' -import { json, redirect } from '@remix-run/node' +import type { LoaderFunctionArgs } from 'react-router' +import { Outlet } from 'react-router' +import { redirect } from 'react-router' import { requireUser } from '#app/modules/auth/auth.server' import { getDomainPathname } from '#app/utils/misc.server' import { ROUTE_PATH as DASHBOARD_PATH } from '#app/routes/dashboard+/_layout' diff --git a/packages/www/app/routes/onboarding+/username.tsx b/packages/www/app/routes/onboarding+/username.tsx index d8a86d2..44eac4f 100644 --- a/packages/www/app/routes/onboarding+/username.tsx +++ b/packages/www/app/routes/onboarding+/username.tsx @@ -1,11 +1,7 @@ -import type { - MetaFunction, - LoaderFunctionArgs, - ActionFunctionArgs, -} from '@remix-run/node' +import type { MetaFunction, LoaderFunctionArgs, ActionFunctionArgs } from 'react-router'; import { useRef, useEffect } from 'react' -import { Form, useActionData } from '@remix-run/react' -import { data, redirect } from '@remix-run/node' +import { Form, useActionData } from 'react-router'; +import { data, redirect } from 'react-router'; import { useHydrated } from 'remix-utils/use-hydrated' import { AuthenticityTokenInput } from 'remix-utils/csrf/react' import { HoneypotInputs } from 'remix-utils/honeypot/react' diff --git a/packages/www/app/routes/resources+/reset-image.ts b/packages/www/app/routes/resources+/reset-image.ts index 3eac677..fd51061 100644 --- a/packages/www/app/routes/resources+/reset-image.ts +++ b/packages/www/app/routes/resources+/reset-image.ts @@ -1,4 +1,4 @@ -import type { ActionFunctionArgs } from '@remix-run/router' +import type { ActionFunctionArgs } from 'react-router'; import { requireUser } from '#app/modules/auth/auth.server' import { db, schema } from '@company/core/src/drizzle/index' import { eq } from 'drizzle-orm' diff --git a/packages/www/app/routes/resources+/update-theme.ts b/packages/www/app/routes/resources+/update-theme.ts index 14218f3..99103d4 100644 --- a/packages/www/app/routes/resources+/update-theme.ts +++ b/packages/www/app/routes/resources+/update-theme.ts @@ -1,5 +1,5 @@ -import type { ActionFunctionArgs } from '@remix-run/node' -import { redirect } from '@remix-run/node' +import type { ActionFunctionArgs } from 'react-router'; +import { redirect } from 'react-router'; import { safeRedirect } from 'remix-utils/safe-redirect' import { ThemeSchema, setTheme } from '#app/utils/hooks/use-theme' diff --git a/packages/www/app/routes/resources+/upload-image.ts b/packages/www/app/routes/resources+/upload-image.ts index 557a892..0926ee0 100644 --- a/packages/www/app/routes/resources+/upload-image.ts +++ b/packages/www/app/routes/resources+/upload-image.ts @@ -1,10 +1,5 @@ -import type { ActionFunctionArgs } from '@remix-run/router' -import { - unstable_createMemoryUploadHandler, - unstable_parseMultipartFormData, - MaxPartSizeExceededError, - data, -} from '@remix-run/node' +import { data, type ActionFunctionArgs } from 'react-router' +import { parseFormData, type FileUpload } from '@mjackson/form-data-parser' import { z } from 'zod' import { parseWithZod } from '@conform-to/zod' import type { SubmissionResult } from '@conform-to/react' @@ -13,6 +8,12 @@ import { createToastHeaders } from '#app/utils/toast.server' import { db, schema } from '@company/core/src/drizzle/index' import { eq } from 'drizzle-orm' +export class MaxPartSizeExceededError extends Error { + constructor() { + super('File size exceeded the maximum allowed size') + } +} + export const ROUTE_PATH = '/resources/upload-image' as const export const MAX_FILE_SIZE = 1024 * 1024 * 3 // 3MB @@ -20,14 +21,20 @@ export const ImageSchema = z.object({ imageFile: z.instanceof(File).refine((file) => file.size > 0, 'Image is required.'), }) -export async function action({ request, context }: ActionFunctionArgs) { +export async function action({ request }: ActionFunctionArgs) { try { const user = await requireUser(request) - const formData = await unstable_parseMultipartFormData( - request, - unstable_createMemoryUploadHandler({ maxPartSize: MAX_FILE_SIZE }), - ) + const formData = await parseFormData(request, async (fileUpload: FileUpload) => { + const buffer = await fileUpload.arrayBuffer() + if (buffer.byteLength > MAX_FILE_SIZE) { + throw new MaxPartSizeExceededError() + } + return new File([buffer], fileUpload.name, { + type: fileUpload.type, + }) + }) + const submission = await parseWithZod(formData, { schema: ImageSchema.transform(async (data) => { return { @@ -39,6 +46,7 @@ export async function action({ request, context }: ActionFunctionArgs) { }), async: true, }) + if (submission.status !== 'success') { return data(submission.reply(), { status: submission.status === 'error' ? 400 : 200, diff --git a/packages/www/app/routes/resources+/user-images.$imageId.ts b/packages/www/app/routes/resources+/user-images.$imageId.ts index 366d48b..75efadc 100644 --- a/packages/www/app/routes/resources+/user-images.$imageId.ts +++ b/packages/www/app/routes/resources+/user-images.$imageId.ts @@ -1,4 +1,4 @@ -import type { LoaderFunctionArgs } from '@remix-run/node' +import type { LoaderFunctionArgs } from 'react-router'; import { db, schema } from '@company/core/src/drizzle/index' import { eq } from 'drizzle-orm' diff --git a/packages/www/app/utils/csrf.server.ts b/packages/www/app/utils/csrf.server.ts index eb24542..27f9c35 100644 --- a/packages/www/app/utils/csrf.server.ts +++ b/packages/www/app/utils/csrf.server.ts @@ -2,7 +2,7 @@ * Learn more about CSRF protection: * @see https://github.com/sergiodxa/remix-utils?tab=readme-ov-file#csrf */ -import { createCookie } from '@remix-run/node' +import { createCookie } from 'react-router'; import { CSRF, CSRFError } from 'remix-utils/csrf/server' import { Resource } from 'sst' diff --git a/packages/www/app/utils/hooks/use-request-info.ts b/packages/www/app/utils/hooks/use-request-info.ts index 77f669b..09775e1 100644 --- a/packages/www/app/utils/hooks/use-request-info.ts +++ b/packages/www/app/utils/hooks/use-request-info.ts @@ -1,5 +1,5 @@ import type { loader as rootLoader } from '#app/root' -import { useRouteLoaderData } from '@remix-run/react' +import { useRouteLoaderData } from 'react-router'; /** * Returns the request info from the Root loader. diff --git a/packages/www/app/utils/hooks/use-theme.ts b/packages/www/app/utils/hooks/use-theme.ts index 6fe357b..4f81cfc 100644 --- a/packages/www/app/utils/hooks/use-theme.ts +++ b/packages/www/app/utils/hooks/use-theme.ts @@ -3,7 +3,7 @@ */ import * as cookie from 'cookie' import { z } from 'zod' -import { useFetcher } from '@remix-run/react' +import { useFetcher } from 'react-router'; import { useHints } from '#app/utils/hooks/use-hints' import { useRequestInfo } from '#app/utils/hooks/use-request-info' diff --git a/packages/www/app/utils/misc.ts b/packages/www/app/utils/misc.ts index 82c351a..ec80af5 100644 --- a/packages/www/app/utils/misc.ts +++ b/packages/www/app/utils/misc.ts @@ -1,7 +1,6 @@ -import type { SerializeFrom } from '@remix-run/node' import type { ClassValue } from 'clsx' import type { loader as rootLoader } from '#app/root' -import { useFormAction, useNavigation, useRouteLoaderData } from '@remix-run/react' +import { useFormAction, useNavigation, useRouteLoaderData } from 'react-router' import { clsx } from 'clsx' import { twMerge } from 'tailwind-merge' @@ -18,7 +17,7 @@ export function cn(...inputs: ClassValue[]) { */ // biome-ignore lint/suspicious/noExplicitAny: -function isUser(user: any): user is SerializeFrom['user'] { +function isUser(user: any): boolean { return user && typeof user === 'object' && typeof user.id === 'string' } diff --git a/packages/www/app/utils/permissions.server.ts b/packages/www/app/utils/permissions.server.ts index c1ecf2a..5cc5591 100644 --- a/packages/www/app/utils/permissions.server.ts +++ b/packages/www/app/utils/permissions.server.ts @@ -2,7 +2,7 @@ * Permissions and Roles. * Implementation based on github.com/epicweb-dev/epic-stack */ -import { data } from '@remix-run/node' +import { data } from 'react-router'; import { requireUser } from '#app/modules/auth/auth.server' import { userHasRole } from '#app/utils/misc' import { ROUTE_PATH as LOGIN_PATH } from '#app/routes/auth+/login' diff --git a/packages/www/app/utils/toast.server.ts b/packages/www/app/utils/toast.server.ts index 134e974..01c5ce6 100644 --- a/packages/www/app/utils/toast.server.ts +++ b/packages/www/app/utils/toast.server.ts @@ -2,7 +2,7 @@ * Server-Side Toasts. * Implementation based on github.com/epicweb-dev/epic-stack */ -import { redirect, createCookieSessionStorage } from '@remix-run/node' +import { redirect, createCookieSessionStorage } from 'react-router'; import { z } from 'zod' import { combineHeaders } from '#app/utils/misc.server' import { Resource } from 'sst' diff --git a/packages/www/package.json b/packages/www/package.json index 90486cf..998d4ed 100644 --- a/packages/www/package.json +++ b/packages/www/package.json @@ -7,25 +7,24 @@ "#*": "./*" }, "scripts": { - "build": "remix vite:build", - "dev": "remix vite:dev", + "build": "react-router build", + "dev": "react-router dev", "start": "sst shell remix vite:start", "test": "vitest", - "typecheck": "tsc" + "typecheck": "react-router typegen && tsc" }, "dependencies": { "@conform-to/react": "1.1.4", "@conform-to/zod": "1.1.3", "@epic-web/client-hints": "^1.3.2", + "@mjackson/form-data-parser": "^0.5.1", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", "@react-email/components": "^0.0.28", "@react-email/render": "^1.0.2", - "@remix-run/node": "^2.15.0", - "@remix-run/react": "^2.15.0", - "@remix-run/router": "^1.21.0", + "@react-router/node": "^7.1.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cookie": "^1.0.2", @@ -39,10 +38,12 @@ "react-dom": "^18.3.1", "react-i18next": "^15.1.1", "react-markdown": "^9.0.1", + "react-router": "^7.1.1", + "react-router-dom": "^7.1.1", "remix-auth": "^3.6.0", "remix-auth-github": "^1.7.0", "remix-auth-totp": "^3.3.0", - "remix-i18next": "^6.1.0", + "remix-i18next": "^7.0.1", "remix-utils": "^7.6.0", "sonner": "^1.4.41", "sst": "^3.3.27", @@ -51,8 +52,8 @@ "zod": "^3.23.6" }, "devDependencies": { - "@remix-run/dev": "^2.15.0", - "@remix-run/routes-option-adapter": "^2.15.0", + "@react-router/dev": "^7.0.0", + "@react-router/remix-routes-option-adapter": "^7.0.0", "@tailwindcss/typography": "^0.5.15", "@types/cookie": "^1.0.0", "@types/react": "^18.3.2", @@ -60,7 +61,6 @@ "autoprefixer": "^10.4.19", "postcss": "^8.4.38", "rehype-slug": "^6.0.0", - "remix-development-tools": "^4.1.6", "remix-flat-routes": "^0.6.5", "tailwindcss": "^3.4.3", "typescript": "^5.7.2", diff --git a/packages/www/tests/setup-test-env.ts b/packages/www/tests/setup-test-env.ts deleted file mode 100644 index 50d3111..0000000 --- a/packages/www/tests/setup-test-env.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { installGlobals } from '@remix-run/node' - -installGlobals() - -// Handle `beforeEach` | `afterEach` and other setup/teardown logic here. -// afterEach(() => resetHandlers()) -// afterEach(() => cleanup()) diff --git a/packages/www/tsconfig.json b/packages/www/tsconfig.json index 49c63fc..2c9d09b 100644 --- a/packages/www/tsconfig.json +++ b/packages/www/tsconfig.json @@ -8,8 +8,16 @@ "**/.client/**/*.tsx" ], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["@remix-run/node", "vite/client", "vitest/globals"], + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], + "types": [ + "@react-router/node", + "vite/client", + "vitest/globals" + ], "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", @@ -26,7 +34,9 @@ "noEmit": true, "baseUrl": ".", "paths": { - "#*": ["./*"] + "#*": [ + "./*" + ] } } -} +} \ No newline at end of file diff --git a/packages/www/vite.config.ts b/packages/www/vite.config.ts index 55a4ea3..edcbf9c 100644 --- a/packages/www/vite.config.ts +++ b/packages/www/vite.config.ts @@ -1,31 +1,10 @@ -import { vitePlugin as remix } from '@remix-run/dev' +import { reactRouter } from '@react-router/dev/vite' import { defineConfig } from 'vite' -import { remixDevTools } from 'remix-development-tools' import tsconfigPaths from 'vite-tsconfig-paths' -declare module '@remix-run/node' { - // or cloudflare, deno, etc. - interface Future { - v3_singleFetch: true - } -} - export default defineConfig({ build: { target: 'ES2022', }, - plugins: [ - remixDevTools(), - remix({ - future: { - v3_fetcherPersist: true, - v3_relativeSplatPath: true, - v3_throwAbortReason: true, - v3_lazyRouteDiscovery: true, - v3_singleFetch: true, - v3_routeConfig: true, - }, - }), - tsconfigPaths(), - ], + plugins: [reactRouter(), tsconfigPaths()], }) diff --git a/packages/www/vitest.config.ts b/packages/www/vitest.config.ts index a52fbf8..bbd1644 100644 --- a/packages/www/vitest.config.ts +++ b/packages/www/vitest.config.ts @@ -7,9 +7,6 @@ import tsconfigPaths from 'vite-tsconfig-paths' export default defineConfig({ plugins: [tsconfigPaths()], test: { - // Path to setup file that runs before your tests. - setupFiles: ['./tests/setup-test-env.ts'], - // Path to your test files. include: ['./tests/integration/*.test.ts'], }, }) diff --git a/readme.md b/readme.md index 582e3a4..cc6d56c 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -![](./.github/cover.png) +![](https://github.com/Murderlon/the-startup-stack/blob/main/.github/cover.png?raw=true) # The Startup Stack @@ -6,7 +6,7 @@ Get independence from expensive SaaS without losing its developer experience, the infra primitives to adapt to any future requirement, and the tools to build delightful, secure user experiences. -Check it live 👉 https://stack.merlijn.site +Check it live 👉 ## Contents @@ -25,6 +25,7 @@ Check it live 👉 https://stack.merlijn.site - [Stripe](#stripe) - [Secrets](#secrets) - [Use](#use) + - [Commands](#commands) - [Authentication](#authentication) - [Subscriptions](#subscriptions) - [Internationalization](#internationalization) @@ -35,7 +36,7 @@ Check it live 👉 https://stack.merlijn.site ## Features -- **[Remix][]** as the full-stack **[React][]** framework. +- **[React Router][]** (formerly known as [Remix][]) as the full-stack **[React][]** framework. - **[SST][]** for infrastructure as code on AWS and Cloudflare. - **[Hono][]** API on [AWS Lambda][]. - **[Postgres][]** database through **[Neon][]**. @@ -400,6 +401,7 @@ Usage is as simple as it can be, as everything is already set up for you. [Remix]: https://remix.run +[React Router]: https://reactrouter.com [React]: https://react.dev [SST]: https://sst.dev [Postgres]: https://postgresql.org