♻️ Replace yarn with pnpm (#1693)

This commit is contained in:
Luke Vella 2025-04-27 15:16:38 +01:00 committed by GitHub
parent c8e78841f1
commit 765a97f3c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 17816 additions and 16954 deletions

View file

@ -11,3 +11,11 @@ docker-compose.yml
/docs
README.md
node_modules
# Build outputs
apps/*/.next
apps/*/dist
packages/*/dist
# Turbo prune output
out/

View file

@ -0,0 +1,8 @@
name: "Pnpm Install"
description: "Runs pnpm install with --frozen-lockfile"
runs:
using: "composite"
steps:
- name: Run pnpm install
run: pnpm install --frozen-lockfile
shell: bash

View file

@ -8,10 +8,13 @@ inputs:
cache:
description: "Package manager for caching"
required: false
default: "yarn"
default: "pnpm"
runs:
using: "composite"
steps:
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:

View file

@ -1,8 +0,0 @@
name: "Yarn Install"
description: "Runs yarn install with --frozen-lockfile"
runs:
using: "composite"
steps:
- name: Run yarn install
run: yarn install --frozen-lockfile
shell: bash

View file

@ -18,10 +18,13 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-node
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/pnpm-install
- name: Generate Prisma Client
run: pnpm db:generate
- name: Check types
run: yarn type-check
run: pnpm type-check
sherif:
name: Sherif
@ -31,7 +34,7 @@ jobs:
- uses: ./.github/actions/setup-node
- name: Run sherif
run: npx sherif@latest
run: pnpm dlx sherif@latest
linting:
name: Linting
@ -39,10 +42,10 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-node
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/pnpm-install
- name: Check linting rules
run: yarn lint
run: pnpm lint
unit-tests:
name: Unit tests
@ -50,10 +53,10 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-node
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/pnpm-install
- name: Run tests
run: yarn test:unit
run: pnpm test:unit
# Label of the container job
integration-tests:
@ -66,25 +69,28 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-node
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/pnpm-install
- name: Generate Prisma Client
run: pnpm db:generate
- name: Install system dependencies
run: sudo apt-get update
- name: Install playwright dependencies
run: yarn playwright install --with-deps chromium
run: pnpm playwright install --with-deps chromium
- name: Create production build
run: yarn turbo build:test --filter=@rallly/web
run: pnpm turbo build:test --filter=@rallly/web
- name: Start services
run: yarn docker:up
run: pnpm docker:up
- name: Setup database
run: yarn db:deploy
run: pnpm db:deploy
- name: Run tests
run: yarn turbo test:integration
run: pnpm turbo test:integration
- name: Upload artifact playwright-report
if: ${{ success() || failure() }}

2
.gitignore vendored
View file

@ -21,8 +21,6 @@ node_modules
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env

View file

@ -1,4 +1,4 @@
1. Use yarn for package management
1. Use pnpm for package management
2. Use dayjs for date handling
3. Use tailwindcss for styling
4. Use react-query for data fetching

View file

@ -38,7 +38,7 @@ The following instructions are for running the project locally for development.
2. Install dependencies
```bash
yarn
pnpm install
```
3. Setup environment variables
@ -54,7 +54,7 @@ The following instructions are for running the project locally for development.
4. Generate Prisma client
```bash
yarn db:generate
pnpm db:generate
```
5. Setup database
@ -64,13 +64,13 @@ The following instructions are for running the project locally for development.
To start the database, run:
```bash
yarn docker:up
pnpm docker:up
```
Next run the following command to setup the database:
```bash
yarn db:reset
pnpm db:reset
```
This will:
@ -82,7 +82,7 @@ The following instructions are for running the project locally for development.
6. Start the Next.js server
```bash
yarn dev
pnpm dev
```
## Contributors

View file

@ -13,7 +13,7 @@ To preview your changes locally, you can use the [mintlify cli](https://mintlify
Install the cli globally:
```bash
yarn global add mintlify
pnpm install --global mintlify
```
Navigate to this directory (where you can find `mint.json`):

View file

@ -3,6 +3,6 @@
"version": "0.0.0",
"private": true,
"devDependencies": {
"@rallly/tsconfig": "*"
"@rallly/tsconfig": "workspace:*"
}
}

View file

@ -12,33 +12,42 @@
"i18n:scan": "i18next-scanner --config i18next-scanner.config.js"
},
"dependencies": {
"@rallly/billing": "*",
"@rallly/icons": "*",
"@rallly/languages": "*",
"@rallly/tailwind-config": "*",
"@rallly/ui": "*",
"@rallly/utils": "*",
"@prisma/client": "^6.4.1",
"@rallly/billing": "workspace:*",
"@rallly/database": "workspace:*",
"@rallly/icons": "workspace:*",
"@rallly/languages": "workspace:*",
"@rallly/tailwind-config": "workspace:*",
"@rallly/ui": "workspace:*",
"@rallly/utils": "workspace:*",
"@svgr/webpack": "^6.5.1",
"@vercel/analytics": "^0.1.8",
"dayjs": "^1.11.10",
"dayjs": "^1.11.13",
"gray-matter": "^4.0.3",
"i18next": "^24.2.2",
"i18next-icu": "^2.3.0",
"i18next-resources-to-backend": "^1.2.1",
"intl-messageformat": "^10.7.15",
"lodash": "^4.17.21",
"lucide-react": "^0.479.0",
"motion": "^12.6.2",
"nanoid": "^5.0.9",
"next": "^14.2.25",
"next-mdx-remote": "^5.0.0",
"next-seo": "^6.1.0",
"react-i18next": "^15.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^15.5.1",
"react-use": "^17.4.0"
},
"devDependencies": {
"@next/bundle-analyzer": "^12.3.4",
"@rallly/eslint-config": "*",
"@rallly/tsconfig": "*",
"@next/bundle-analyzer": "^14.2.25",
"@rallly/eslint-config": "workspace:*",
"@rallly/tsconfig": "workspace:*",
"@types/color-hash": "^1.0.2",
"@types/lodash": "^4.14.178",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"cross-env": "^7.0.3",
"i18next-scanner": "^4.2.0",
"i18next-scanner-typescript": "^1.1.1"

View file

@ -19,7 +19,10 @@ export async function middleware(request: NextRequest) {
return;
}
const locale = getPreferredLocale(request);
const locale = getPreferredLocale({
acceptLanguageHeader: request.headers.get("accept-language") ?? undefined,
});
request.nextUrl.pathname = `/${locale}${pathname}`;
if (locale === "en") {

View file

@ -1,5 +1,5 @@
{
"installCommand": "yarn install",
"buildCommand": "cd ../.. && yarn db:generate && yarn build:landing",
"installCommand": "pnpm install",
"buildCommand": "cd ../.. && pnpm db:generate && pnpm build:landing",
"outputDirectory": ".next"
}

View file

@ -1,22 +1,35 @@
FROM node:20 AS builder
# Base stage with Node.js 20 and Corepack enabled
FROM node:20 AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
# Slim base stage with Node.js 20 and Corepack enabled
FROM node:20-slim AS base-slim
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
# --- Builder Stage ---
FROM base AS builder
WORKDIR /app
RUN yarn global add turbo
COPY package.json .
RUN pnpm add -g turbo
COPY . .
RUN turbo prune --scope=@rallly/web --docker
FROM node:20 AS installer
# --- Installer Stage ---
FROM base AS installer
WORKDIR /app
COPY .gitignore .gitignore
COPY package.json .
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/yarn.lock ./yarn.lock
RUN yarn --network-timeout 1000000
COPY --from=builder /app/out/pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
# Build the project
COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json
RUN yarn db:generate
RUN pnpm db:generate
ARG APP_VERSION
ENV NEXT_PUBLIC_APP_VERSION=$APP_VERSION
@ -24,11 +37,15 @@ ENV NEXT_PUBLIC_APP_VERSION=$APP_VERSION
ARG SELF_HOSTED
ENV NEXT_PUBLIC_SELF_HOSTED=$SELF_HOSTED
RUN SKIP_ENV_VALIDATION=1 yarn build
RUN SKIP_ENV_VALIDATION=1 pnpm build
FROM node:20-slim AS runner
# --- Runner Stage ---
FROM base-slim AS runner
# prisma requirements
# Disable Next.js telemetry for self-hosted instances
ENV NEXT_TELEMETRY_DISABLED=1
# Install required system packages for Prisma
# (see https://www.prisma.io/docs/orm/reference/system-requirements)
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
@ -40,26 +57,36 @@ RUN apt-get update \
WORKDIR /app
RUN yarn global add prisma
# Install prisma globally needed for runtime operations like migrations
RUN pnpm add -g prisma
# Don't run production as root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Set HOME env for non-root user
ENV HOME=/app
# Ensure /app is writable by nextjs user AND fix perms for global pnpm dir
RUN chown -R nextjs:nodejs /app /pnpm
USER nextjs
COPY --from=builder --chown=nextjs:nodejs /app/scripts/docker-start.sh ./
COPY --from=builder --chown=nextjs:nodejs /app/packages/database/prisma ./prisma
# Copy Corepack cache from installer stage to avoid runtime download
COPY --from=installer /root/.cache/node/corepack/ /app/.cache/node/corepack/
# Copy app files
COPY --from=builder /app/scripts/docker-start.sh ./
COPY --from=builder /app/packages/database/prisma ./prisma
COPY --from=installer /app/apps/web/next.config.js .
COPY --from=installer /app/apps/web/package.json .
ENV PORT=3000
EXPOSE 3000
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
# Copy Next.js standalone output
COPY --from=installer /app/apps/web/.next/standalone ./
COPY --from=installer /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=installer /app/apps/web/public ./apps/web/public
ARG SELF_HOSTED
ENV NEXT_PUBLIC_SELF_HOSTED=$SELF_HOSTED

View file

@ -8,10 +8,6 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});
require("dotenv").config({
path: "../../.env",
});
/** @type {import('next').NextConfig} */
const nextConfig = {
output:
@ -100,9 +96,10 @@ const sentryWebpackPluginOptions = {
};
const withBundleAnalyzerConfig = withBundleAnalyzer(nextConfig);
// Make sure adding Sentry options is the last code to run before exporting, to
// ensure that your source maps include changes from all other Webpack plugins
module.exports = withSentryConfig(
withBundleAnalyzerConfig,
sentryWebpackPluginOptions,
);
module.exports =
process.env.NEXT_PUBLIC_SELF_HOSTED === "true"
? withBundleAnalyzerConfig
: withSentryConfig(withBundleAnalyzerConfig, sentryWebpackPluginOptions);

View file

@ -3,7 +3,7 @@
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"dev": "dotenv -c development next dev",
"build": "next build",
"build:test": "NODE_ENV=test next build",
"analyze": "cross-env ANALYZE=true next build",
@ -13,7 +13,6 @@
"i18n:scan": "i18next-scanner --config i18next-scanner.config.js",
"test:integration": "NODE_ENV=test playwright test",
"test:unit": "vitest run",
"test": "yarn test:unit && yarn test:e2e",
"test:codegen": "playwright codegen http://localhost:3000",
"docker:start": "./scripts/docker-start.sh"
},
@ -23,20 +22,24 @@
"@aws-sdk/client-s3": "^3.645.0",
"@aws-sdk/s3-request-presigner": "^3.645.0",
"@hookform/resolvers": "^3.3.1",
"@next/bundle-analyzer": "^12.3.4",
"@next/bundle-analyzer": "^14.2.25",
"@next/env": "^15.3.1",
"@panva/hkdf": "^1.2.1",
"@prisma/client": "^6.4.1",
"@radix-ui/react-radio-group": "^1.2.3",
"@radix-ui/react-select": "^1.2.1",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.0.2",
"@rallly/billing": "*",
"@rallly/database": "*",
"@rallly/emails": "*",
"@rallly/icons": "*",
"@rallly/languages": "*",
"@rallly/posthog": "*",
"@rallly/tailwind-config": "*",
"@rallly/ui": "*",
"@sentry/nextjs": "*",
"@rallly/billing": "workspace:*",
"@rallly/database": "workspace:*",
"@rallly/emails": "workspace:*",
"@rallly/icons": "workspace:*",
"@rallly/languages": "workspace:*",
"@rallly/posthog": "workspace:*",
"@rallly/tailwind-config": "workspace:*",
"@rallly/ui": "workspace:*",
"@rallly/utils": "workspace:*",
"@sentry/nextjs": "^8.49.0",
"@svgr/webpack": "^6.5.1",
"@t3-oss/env-nextjs": "^0.11.0",
"@tanstack/react-query": "^4.0.0",
@ -46,7 +49,7 @@
"@trpc/server": "^10.13.0",
"@upstash/qstash": "^2.7.17",
"@upstash/ratelimit": "^1.2.1",
"@vercel/functions": "^1.5.2",
"@vercel/functions": "^2.0.0",
"@vercel/kv": "^2.0.0",
"ai": "^4.1.50",
"autoprefixer": "^10.4.13",
@ -55,7 +58,7 @@
"color-hash": "^2.0.2",
"cookie": "^0.7.0",
"crypto": "^1.0.1",
"dayjs": "^1.11.10",
"dayjs": "^1.11.13",
"i18next": "^24.2.2",
"i18next-http-backend": "^3.0.2",
"i18next-icu": "^2.3.0",
@ -72,33 +75,42 @@
"micro": "^10.0.1",
"motion": "^12.6.2",
"nanoid": "^5.0.9",
"next": "^14.2.25",
"next-auth": "^5.0.0-beta.25",
"php-serialize": "^4.1.1",
"postcss": "^8.4.31",
"react": "^18.2.0",
"react-big-calendar": "^1.8.1",
"react-dom": "^18.2.0",
"react-hook-form": "^7.42.1",
"react-hook-form-persist": "^3.0.0",
"react-i18next": "^15.4.1",
"react-i18next": "^15.5.1",
"react-remove-scroll": "^2.5.6",
"react-use": "^17.4.0",
"smoothscroll-polyfill": "^0.4.4",
"spacetime": "^7.4.7",
"superjson": "^2.0.0",
"timezone-soft": "^1.5.1"
"tailwindcss": "^3.4.17",
"timezone-soft": "^1.5.1",
"zod": "^3.23.8"
},
"devDependencies": {
"@babel/core": "^7.26.10",
"@playwright/test": "^1.49.1",
"@rallly/eslint-config": "*",
"@rallly/tsconfig": "*",
"@rallly/eslint-config": "workspace:*",
"@rallly/tsconfig": "workspace:*",
"@types/color-hash": "^1.0.2",
"@types/js-cookie": "^3.0.1",
"@types/lodash": "^4.14.178",
"@types/node": "^18.19.41",
"@types/react": "^18.2.0",
"@types/react-big-calendar": "^1.8.8",
"@types/react-dom": "^18.2.0",
"@types/smoothscroll-polyfill": "^0.3.1",
"cheerio": "^1.0.0-rc.12",
"cross-env": "^7.0.3",
"i18next-scanner": "^4.2.0",
"i18next-scanner-typescript": "^1.1.1",
"vitest": "^2.1.9",
"wait-on": "^6.0.1"
"vitest": "^2.1.9"
}
}

View file

@ -1,4 +1,5 @@
import { Tile, TileGrid, TileTitle } from "@rallly/ui/tile";
import Link from "next/link";
import type { Params } from "@/app/[locale]/types";
import {
@ -56,18 +57,22 @@ export default async function Page({ params }: { params: Params }) {
<Trans i18nKey="homeNavTitle" defaults="Navigation" />
</h2>
<TileGrid>
<Tile href="/polls">
<PollPageIcon />
<TileTitle>
<Trans i18nKey="polls" defaults="Polls" />
</TileTitle>
<Tile asChild>
<Link href="/polls">
<PollPageIcon />
<TileTitle>
<Trans i18nKey="polls" defaults="Polls" />
</TileTitle>
</Link>
</Tile>
<Tile href="/events">
<EventPageIcon />
<TileTitle>
<Trans i18nKey="events" defaults="Events" />
</TileTitle>
<Tile asChild>
<Link href="/events">
<EventPageIcon />
<TileTitle>
<Trans i18nKey="events" defaults="Events" />
</TileTitle>
</Link>
</Tile>
{/* <Tile href="/members">
@ -84,25 +89,31 @@ export default async function Page({ params }: { params: Params }) {
<Trans i18nKey="account" defaults="Account" />
</h2>
<TileGrid>
<Tile href="/settings/profile">
<ProfilePageIcon />
<TileTitle>
<Trans i18nKey="profile" defaults="Profile" />
</TileTitle>
<Tile asChild>
<Link href="/settings/profile">
<ProfilePageIcon />
<TileTitle>
<Trans i18nKey="profile" defaults="Profile" />
</TileTitle>
</Link>
</Tile>
<Tile href="/settings/preferences">
<PreferencesPageIcon />
<TileTitle>
<Trans i18nKey="preferences" defaults="Preferences" />
</TileTitle>
<Tile asChild>
<Link href="/settings/preferences">
<PreferencesPageIcon />
<TileTitle>
<Trans i18nKey="preferences" defaults="Preferences" />
</TileTitle>
</Link>
</Tile>
<Tile href="/settings/billing">
<BillingPageIcon />
<TileTitle>
<Trans i18nKey="billing" defaults="Billing" />
</TileTitle>
<Tile asChild>
<Link href="/settings/billing">
<BillingPageIcon />
<TileTitle>
<Trans i18nKey="billing" defaults="Billing" />
</TileTitle>
</Link>
</Tile>
</TileGrid>
</div>

View file

@ -1,8 +1,7 @@
import "tailwindcss/tailwind.css";
import "../../style.css";
import { defaultLocale, supportedLngs } from "@rallly/languages";
import { PostHogProvider } from "@rallly/posthog/client";
import { posthog, PostHogProvider } from "@rallly/posthog/client";
import { Toaster } from "@rallly/ui/toaster";
import { TooltipProvider } from "@rallly/ui/tooltip";
import { domAnimation, LazyMotion } from "motion/react";
@ -61,7 +60,7 @@ export default async function Root({
<I18nProvider locale={locale}>
<TRPCProvider>
<LazyMotion features={domAnimation}>
<PostHogProvider>
<PostHogProvider client={posthog}>
<PostHogPageView />
<TooltipProvider>
<UserProvider

View file

@ -1,6 +1,5 @@
import { withPosthog } from "@rallly/posthog/server";
import { handlers } from "@/next-auth";
import { withPosthog } from "@/utils/posthog";
export const GET = withPosthog(handlers.GET);
export const POST = withPosthog(handlers.POST);

View file

@ -1,10 +1,11 @@
import type { Stripe } from "@rallly/billing";
import { stripe } from "@rallly/billing";
import { withPosthog } from "@rallly/posthog/server";
import * as Sentry from "@sentry/nextjs";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { withPosthog } from "@/utils/posthog";
import { getEventHandler } from "./handlers";
export const POST = withPosthog(async (request: NextRequest) => {

View file

@ -19,7 +19,9 @@ const handler = async (req: NextRequest) => {
req,
router: appRouter,
createContext: async () => {
const locale = getPreferredLocale(req);
const locale = getPreferredLocale({
acceptLanguageHeader: req.headers.get("accept-language") ?? undefined,
});
const user = session?.user
? {
id: session.user.id,

View file

@ -1,6 +1,6 @@
"use client";
import { usePostHog } from "@rallly/posthog/client";
import { usePathname, useSearchParams } from "next/navigation";
import { usePostHog } from "posthog-js/react";
import { useEffect } from "react";
export function PostHogPageView() {

View file

@ -1,6 +1,6 @@
import languages from "@rallly/languages";
import { getPreferredLocale } from "@rallly/languages/get-preferred-locale";
import { withPostHog } from "@rallly/posthog/next/middleware";
import { getPosthogBootstrapCookie } from "@rallly/posthog/utils";
import { NextResponse } from "next/server";
import { withAuth } from "@/auth/edge";
@ -19,7 +19,12 @@ export const middleware = withAuth(async (req) => {
return NextResponse.redirect(newUrl);
}
const locale = req.auth?.user?.locale || getPreferredLocale(req);
const locale =
req.auth?.user?.locale ||
getPreferredLocale({
acceptLanguageHeader: req.headers.get("accept-language") ?? undefined,
});
if (supportedLocales.includes(locale)) {
newUrl.pathname = `/${locale}${pathname}`;
}
@ -29,7 +34,12 @@ export const middleware = withAuth(async (req) => {
res.headers.set("x-pathname", pathname);
if (req.auth?.user?.id) {
await withPostHog(res, { distinctID: req.auth.user.id });
const bootstrapCookie = getPosthogBootstrapCookie({
distinctID: req.auth.user.id,
});
if (bootstrapCookie) {
res.cookies.set(bootstrapCookie);
}
}
return res;

View file

@ -0,0 +1,15 @@
import { posthog } from "@rallly/posthog/server";
import { waitUntil } from "@vercel/functions";
import type { NextRequest } from "next/server";
export function withPosthog(handler: (req: NextRequest) => Promise<Response>) {
return async (req: NextRequest) => {
const res = await handler(req);
try {
waitUntil(Promise.all([posthog?.shutdown()]));
} catch (error) {
console.error("Failed to flush PostHog events:", error);
}
return res;
};
}

View file

@ -3,10 +3,15 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
"@/*": [
"src/*"
]
},
"strictNullChecks": true,
"types": ["vitest/globals"]
"types": [
"vitest/globals"
],
"target": "ES2017"
},
"include": [
"**/*.ts",
@ -15,5 +20,10 @@
".next/types/**/*.ts",
"vitest.config.mts"
],
"exclude": ["node_modules", ".next/**/*", "playwright-report", "test-results"]
"exclude": [
"node_modules",
".next/**/*",
"playwright-report",
"test-results"
]
}

View file

@ -1,5 +1,5 @@
{
"installCommand": "yarn install",
"buildCommand": "cd ../.. && yarn db:generate && yarn build:web && yarn db:deploy",
"installCommand": "pnpm install",
"buildCommand": "cd ../.. && pnpm db:generate && pnpm build:web && pnpm db:deploy",
"outputDirectory": ".next"
}

View file

@ -17,7 +17,7 @@
"db:migrate": "prisma migrate dev",
"db:reset": "prisma migrate reset",
"db:push": "prisma db push",
"docker:up": "docker compose -f docker-compose.dev.yml up -d && wait-on --timeout 60000 tcp:localhost:5450",
"docker:up": "docker compose -f docker-compose.dev.yml up -d && pnpm dlx wait-on --timeout 60000 tcp:localhost:5450",
"docker:down": "docker compose -f docker-compose.dev.yml down --volumes --remove-orphans",
"test:integration": "turbo test:integration",
"test:unit": "turbo test:unit",
@ -30,33 +30,22 @@
"sherif:fix": "npx sherif@latest --fix"
},
"prisma": {
"seed": "yarn workspace @rallly/database db:seed",
"seed": "pnpm --filter @rallly/database db:seed",
"schema": "./packages/database/prisma/schema.prisma"
},
"workspaces": [
"apps/*",
"packages/*"
],
"devDependencies": {
"@prisma/client": "^6.4.1",
"@sentry/nextjs": "^8.49.0",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"dotenv-cli": "^7.1.0",
"next": "^14.2.25",
"npm-run-all": "^4.1.5",
"@playwright/test": "^1.49.1",
"dotenv-cli": "^8.0.0",
"eslint": "^8.52.0",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tailwindcss": "^3.4.17",
"prisma": "^6.4.1",
"turbo": "^2.4.4",
"typescript": "^5.8.2",
"vitest": "^2.1.9",
"zod": "^3.23.8"
"vitest": "^2.1.9"
},
"engines": {
"node": "20.x"
},
"packageManager": "yarn@1.22.22"
"packageManager": "pnpm@10.9.0"
}

View file

@ -15,11 +15,12 @@
},
"dependencies": {
"@radix-ui/react-radio-group": "^1.2.3",
"@rallly/ui": "*",
"next": "*",
"@rallly/database": "workspace:*",
"@rallly/ui": "workspace:*",
"stripe": "^13.2.0"
},
"devDependencies": {
"@rallly/eslint-config": "*"
"@rallly/eslint-config": "workspace:*",
"@rallly/tsconfig": "workspace:*"
}
}

View file

@ -11,11 +11,15 @@
"type-check": "tsc --pretty --noEmit"
},
"exports": "./index.ts",
"dependencies": {
"@prisma/client": "^6.4.1",
"dayjs": "^1.11.13"
},
"devDependencies": {
"@faker-js/faker": "^7.6.0",
"@rallly/eslint-config": "*",
"@rallly/tsconfig": "*",
"@types/node": "^18.15.10",
"@rallly/eslint-config": "workspace:*",
"@rallly/tsconfig": "workspace:*",
"@types/node": "^18.19.41",
"prisma": "^6.4.1",
"tsx": "^4.6.2"
}

View file

@ -1,27 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@prisma/client@^4.10.1":
version "4.10.1"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.10.1.tgz#c47fd54661ee74b174cee63e9dc418ecf57a6ccd"
integrity sha512-VonXLJZybdt8e5XZH5vnIGCRNnIh6OMX1FS3H/yzMGLT3STj5TJ/OkMcednrvELgk8PK89Vo3aSh51MWNO0axA==
dependencies:
"@prisma/engines-version" "4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19"
"@prisma/engines-version@4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19":
version "4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19.tgz#312359d9d00e39e323136d0270876293d315658e"
integrity sha512-tsjTho7laDhf9EJ9EnDxAPEf7yrigSMDhniXeU4YoWc7azHAs4GPxRi2P9LTFonmHkJLMOLjR77J1oIP8Ife1w==
"@prisma/engines@4.10.1":
version "4.10.1"
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.10.1.tgz#c7062747f254e5d5fce98a8cae566c25f9f29fb2"
integrity sha512-B3tcTxjx196nuAu1GOTKO9cGPUgTFHYRdkPkTS4m5ptb2cejyBlH9X7GOfSt3xlI7p4zAJDshJP4JJivCg9ouA==
prisma@^4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.10.1.tgz#88084695d7b364ae6bebf93d5006f84439c4e7d1"
integrity sha512-0jDxgg+DruB1kHVNlcspXQB9au62IFfVg9drkhzXudszHNUAQn0lVuu+T8np0uC2z1nKD5S3qPeCyR8u5YFLnA==
dependencies:
"@prisma/engines" "4.10.1"

View file

@ -16,15 +16,21 @@
"@aws-sdk/client-ses": "^3.501.0",
"@aws-sdk/credential-provider-node": "^3.501.0",
"@react-email/components": "^0.0.14",
"@react-email/render": "^0.0.12",
"@vercel/functions": "*",
"@react-email/render": "^1.0.6",
"@vercel/functions": "^2.0.0",
"i18next": "^24.2.2",
"i18next-icu": "^2.3.0",
"i18next-resources-to-backend": "^1.2.1",
"nodemailer": "^6.9.9",
"react-email": "^4.0.2"
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-email": "^4.0.2",
"react-i18next": "^15.5.1"
},
"devDependencies": {
"@rallly/tailwind-config": "*",
"@rallly/tsconfig": "*",
"@rallly/utils": "*",
"@rallly/tailwind-config": "workspace:*",
"@rallly/tsconfig": "workspace:*",
"@rallly/utils": "workspace:*",
"@types/nodemailer": "^6.4.14"
}
}

View file

@ -1,27 +0,0 @@
# React Email Starter
A live preview right in your browser so you don't need to keep sending real emails during development.
## Getting Started
First, install the dependencies:
```sh
npm install
# or
yarn
```
Then, run the development server:
```sh
npm run dev
# or
yarn dev
```
Open [localhost:3000](http://localhost:3000) with your browser to see the result.
## License
MIT License

View file

@ -1,4 +1,4 @@
import { Section } from "@react-email/section";
import { Section } from "@react-email/components";
import { Trans } from "react-i18next/TransWithoutContext";
import type { EmailContext } from "../types";

View file

@ -1,11 +0,0 @@
import { Client } from "@upstash/qstash";
export function createQstashClient() {
if (!process.env.QSTASH_TOKEN) {
return null;
}
return new Client({
token: process.env.QSTASH_TOKEN,
});
}

View file

@ -1,4 +1,4 @@
import { Section } from "@react-email/section";
import { Section } from "@react-email/components";
import { Trans } from "react-i18next/TransWithoutContext";
import { EmailLayout } from "../components/email-layout";

View file

@ -1,4 +1,4 @@
import { Section } from "@react-email/section";
import { Section } from "@react-email/components";
import { Trans } from "react-i18next/TransWithoutContext";
import { EmailLayout } from "../components/email-layout";

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,6 @@
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"eslint": "^8.52.0",
"eslint-config-next": "^14.0.1",
"eslint-config-turbo": "^2.0.3",
"eslint-import-resolver-typescript": "^2.7.0",

View file

@ -7,5 +7,9 @@
"dependencies": {
"@heroicons/react": "^1.0.6",
"lucide-react": "^0.479.0"
},
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}

View file

@ -11,6 +11,7 @@
"negotiator": "^1.0.0"
},
"devDependencies": {
"@rallly/tsconfig": "workspace:*",
"@types/negotiator": "^0.6.3"
}
}

View file

@ -1,14 +1,21 @@
import languages, { defaultLocale } from "./index";
import Negotiator from "negotiator";
import { match } from "@formatjs/intl-localematcher";
import type { NextRequest } from "next/server";
const locales = Object.keys(languages);
export function getPreferredLocale(req: NextRequest) {
export function getPreferredLocale({
acceptLanguageHeader,
}: {
acceptLanguageHeader?: string;
}) {
if (!acceptLanguageHeader) {
return defaultLocale;
}
const preferredLanguages = new Negotiator({
headers: {
"accept-language": req.headers.get("accept-language") ?? "",
"accept-language": acceptLanguageHeader,
},
})
.languages()

View file

@ -1,5 +1,5 @@
{
"extends": "@rallly/tsconfig/react.json",
"include": ["**/*.ts"],
"extends": "@rallly/tsconfig/node.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}

View file

@ -4,23 +4,21 @@
"private": true,
"exports": {
"./server": "./src/server/index.ts",
"./client": "./src/client/index.ts",
"./next/middleware": "./src/next/middleware.ts"
"./client": "./src/client.ts",
"./utils": "./src/utils.ts"
},
"scripts": {
"lint": "eslint ./src",
"type-check": "tsc --noEmit"
},
"dependencies": {
"js-cookie": "^3.0.1",
"posthog-js": "^1.234.1",
"posthog-node": "^4.10.2"
},
"devDependencies": {
"@rallly/eslint-config": "*",
"@rallly/tsconfig": "*"
},
"peerDependencies": {
"next": "^14.2.13",
"react": "^18.2.0"
"@rallly/eslint-config": "workspace:*",
"@rallly/tsconfig": "workspace:*",
"@types/js-cookie": "^3.0.1"
}
}

View file

@ -1,10 +1,10 @@
"use client";
import Cookies from "js-cookie";
import posthog from "posthog-js";
import { PostHogProvider as Provider } from "posthog-js/react";
import React from "react";
import { POSTHOG_BOOTSTAP_DATA_COOKIE_NAME } from "../constants";
import { POSTHOG_BOOTSTAP_DATA_COOKIE_NAME } from "./constants";
export { PostHogProvider, usePostHog } from "posthog-js/react";
if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
let bootstrapData = {};
@ -31,6 +31,4 @@ if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
});
}
export function PostHogProvider(props: { children?: React.ReactNode }) {
return <Provider client={posthog}>{props.children}</Provider>;
}
export { posthog };

View file

@ -1,4 +0,0 @@
"use client";
export { PostHogProvider } from "./provider";
export { usePostHog } from "posthog-js/react";

View file

@ -1,23 +0,0 @@
import type { NextResponse } from "next/server";
import { POSTHOG_BOOTSTAP_DATA_COOKIE_NAME } from "../constants";
const posthogApiKey = process.env.NEXT_PUBLIC_POSTHOG_API_KEY;
export async function withPostHog(
res: NextResponse,
bootstrapData: { distinctID?: string },
) {
if (!posthogApiKey) {
return;
}
res.cookies.set({
name: POSTHOG_BOOTSTAP_DATA_COOKIE_NAME,
value: JSON.stringify(bootstrapData),
httpOnly: false,
secure: true,
sameSite: "lax",
path: "/",
});
}

View file

@ -1,5 +1,3 @@
import { waitUntil } from "@vercel/functions";
import type { NextRequest } from "next/server";
import { PostHog } from "posthog-node";
function PostHogClient() {
@ -14,15 +12,3 @@ function PostHogClient() {
}
export const posthog = PostHogClient();
export function withPosthog(handler: (req: NextRequest) => Promise<Response>) {
return async (req: NextRequest) => {
const res = await handler(req);
try {
waitUntil(Promise.all([posthog?.shutdown()]));
} catch (error) {
console.error("Failed to flush PostHog events:", error);
}
return res;
};
}

View file

@ -0,0 +1,20 @@
import { POSTHOG_BOOTSTAP_DATA_COOKIE_NAME } from "./constants";
const posthogApiKey = process.env.NEXT_PUBLIC_POSTHOG_API_KEY;
export function getPosthogBootstrapCookie(bootstrapData: {
distinctID?: string;
}) {
if (!posthogApiKey) {
return;
}
return {
name: POSTHOG_BOOTSTAP_DATA_COOKIE_NAME,
value: JSON.stringify(bootstrapData),
httpOnly: false,
secure: true,
sameSite: "lax" as const,
path: "/",
};
}

View file

@ -4,6 +4,9 @@
"private": true,
"main": "tailwind.config.js",
"types": "tailwind.config.d.ts",
"dependencies": {
"tailwindcss": "^3.4.17"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.13",
"autoprefixer": "^10.4.13",

View file

@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Node.js",
"compilerOptions": {
"lib": ["es2020"],
"module": "NodeNext",
"target": "es2020",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "NodeNext",
"noUncheckedIndexedAccess": true
}
}

View file

@ -24,6 +24,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.4",
"@radix-ui/react-label": "^2.0.1",
"@radix-ui/react-popover": "^1.0.5",
"@radix-ui/react-portal": "^1.1.6",
"@radix-ui/react-progress": "^1.1.2",
"@radix-ui/react-radio-group": "^1.2.3",
"@radix-ui/react-select": "^1.2.1",
@ -33,17 +34,22 @@
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.4",
"@radix-ui/react-tooltip": "^1.1.8",
"@rallly/icons": "*",
"@rallly/languages": "*",
"@rallly/tailwind-config": "*",
"@rallly/icons": "workspace:*",
"@rallly/languages": "workspace:*",
"@rallly/tailwind-config": "workspace:*",
"class-variance-authority": "^0.7.1",
"clsx": "^1.2.1",
"cmdk": "^0.2.1",
"lucide-react": "^0.479.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.42.1",
"tailwind-merge": "^1.12.0"
},
"devDependencies": {
"@rallly/eslint-config": "*",
"@rallly/tsconfig": "*"
"@rallly/eslint-config": "workspace:*",
"@rallly/tsconfig": "workspace:*",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0"
}
}

View file

@ -1,26 +1,32 @@
"use client";
import { Slot } from "@radix-ui/react-slot";
import Link from "next/link";
import * as React from "react";
import { cn } from "./lib/utils";
const Tile = React.forwardRef<
HTMLAnchorElement,
React.ComponentPropsWithoutRef<typeof Link>
>(({ className, children, ...props }, ref) => (
<Link
ref={ref}
className={cn(
"text-card-foreground bg-background flex flex-col justify-end rounded-xl border p-3 shadow-sm transition-shadow hover:bg-gray-50 active:shadow-none",
className,
)}
{...props}
>
{children}
</Link>
));
HTMLDivElement,
{
className?: string;
children?: React.ReactNode;
asChild?: boolean;
}
>(({ className, asChild, children, ...props }, ref) => {
const Comp = asChild ? Slot : "div";
return (
<Comp
ref={ref}
className={cn(
"text-card-foreground bg-background flex flex-col justify-end rounded-xl border p-3 shadow-sm transition-shadow hover:bg-gray-50 active:shadow-none",
className,
)}
{...props}
>
{children}
</Comp>
);
});
Tile.displayName = "Tile";
const TileIcon = React.forwardRef<

View file

@ -14,6 +14,8 @@
"nanoid": "^5.0.9"
},
"devDependencies": {
"@rallly/tsconfig": "workspace:*",
"@types/node": "^18.19.41",
"vitest": "^2.1.9"
}
}

View file

@ -1,7 +1,8 @@
{
"compilerOptions": {
"types": ["vitest/globals"]
"types": ["vitest/globals", "node"],
"lib": ["es2020", "dom"]
},
"extends": "@rallly/tsconfig/react.json",
"include": ["**/*.ts", "**/*.tsx"]
"extends": "@rallly/tsconfig/node.json",
"include": ["**/*.ts"]
}

17428
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

3
pnpm-workspace.yaml Normal file
View file

@ -0,0 +1,3 @@
packages:
- 'apps/*'
- 'packages/*'

View file

@ -4,5 +4,5 @@ set -e
export DIRECT_DATABASE_URL=$DATABASE_URL
export AUTH_URL=$NEXT_PUBLIC_BASE_URL
prisma migrate deploy --schema=./prisma/schema.prisma
pnpm prisma migrate deploy --schema=./prisma/schema.prisma
node apps/web/server.js

14644
yarn.lock

File diff suppressed because it is too large Load diff