diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..7464590e --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,28 @@ +name: Build +on: + pull_request: +jobs: + build: + runs-on: self-hosted + strategy: + matrix: + project: [backend, frontend] + defaults: + run: + working-directory: ${{ matrix.project }} + steps: + - uses: actions/checkout@v5 + - uses: pnpm/action-setup@v6 + with: + package_json_file: ${{ matrix.project }}/package.json + cache: true + cache_dependency_path: ${{ matrix.project }}/pnpm-lock.yaml + - uses: actions/setup-node@v5 + with: + node-version: 24 + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Typecheck + run: pnpm typecheck + - name: Build + run: pnpm build diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml new file mode 100644 index 00000000..98e8a18b --- /dev/null +++ b/.github/workflows/eslint.yml @@ -0,0 +1,30 @@ +name: ESLint +on: + pull_request: +jobs: + eslint: + runs-on: self-hosted + strategy: + matrix: + project: [backend, frontend] + defaults: + run: + working-directory: ${{ matrix.project }} + steps: + - uses: actions/checkout@v5 + - uses: pnpm/action-setup@v6 + with: + package_json_file: ${{ matrix.project }}/package.json + cache: true + cache_dependency_path: ${{ matrix.project }}/pnpm-lock.yaml + - uses: actions/setup-node@v5 + with: + node-version: 24 + - name: Install dependencies + run: pnpm install --frozen-lockfile + # We need to generate React Router types for lint rules that use types + - name: Generate React Router types + if: matrix.project == 'frontend' + run: pnpm exec react-router typegen + - name: Lint with ESLint + run: pnpm lint diff --git a/.github/workflows/frontend-test.yml b/.github/workflows/frontend-test.yml new file mode 100644 index 00000000..cd9f9b39 --- /dev/null +++ b/.github/workflows/frontend-test.yml @@ -0,0 +1,23 @@ +name: Frontend Tests +on: + pull_request: +jobs: + test: + runs-on: self-hosted + defaults: + run: + working-directory: frontend + steps: + - uses: actions/checkout@v5 + - uses: pnpm/action-setup@v6 + with: + package_json_file: frontend/package.json + cache: true + cache_dependency_path: frontend/pnpm-lock.yaml + - uses: actions/setup-node@v5 + with: + node-version: 24 + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Run tests + run: pnpm test diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 00000000..8c8a99f3 --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,26 @@ +name: Prettier +on: + pull_request: +jobs: + prettier: + runs-on: self-hosted + strategy: + matrix: + project: [backend, frontend] + defaults: + run: + working-directory: ${{ matrix.project }} + steps: + - uses: actions/checkout@v5 + - uses: pnpm/action-setup@v6 + with: + package_json_file: ${{ matrix.project }}/package.json + cache: true + cache_dependency_path: ${{ matrix.project }}/pnpm-lock.yaml + - uses: actions/setup-node@v5 + with: + node-version: 24 + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Check formatting with Prettier + run: pnpm format:check diff --git a/backend/package.json b/backend/package.json index 34431cd8..f7f0dac0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,7 @@ "type": "module", "engines": { "node": ">=24.6.0", - "pnpm": ">=12.4.2" + "pnpm": ">=12.8.1" }, "description": "", "main": "dist/index.js", @@ -13,6 +13,8 @@ "typecheck": "tsc", "lint": "eslint .", "codegen": "graphql-codegen", + "format": "prettier --write .", + "format:check": "prettier --check .", "dev": "pnpm codegen && tsx watch src/index.ts", "build": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node24 --packages=external --outfile=dist/index.js", "start": "pnpm build && node dist/index.js", diff --git a/frontend/app/routes/profile.tsx b/frontend/app/routes/profile.tsx index 44b78381..438e7157 100644 --- a/frontend/app/routes/profile.tsx +++ b/frontend/app/routes/profile.tsx @@ -151,7 +151,9 @@ export default function ProfilePage() { ))} ) : error || !books ? ( -

Could not load your favorite books. Please try again later.

+

+ Could not load your favorite books. Please try again later. +

) : books.length === 0 ? (

You have no favorite books yet.{" "} diff --git a/frontend/package.json b/frontend/package.json index 765acfcc..62301935 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,7 +5,7 @@ "type": "module", "engines": { "node": ">=24.6.0", - "pnpm": ">=12.4.2" + "pnpm": ">=12.8.1" }, "scripts": { "codegen": "graphql-codegen", @@ -14,6 +14,8 @@ "start": "react-router-serve ./build/server/index.js", "typecheck": "react-router typegen && tsc", "lint": "eslint .", + "format": "prettier --write .", + "format:check": "prettier --check .", "preview": "vite preview", "test": "vitest run", "test:watch": "vitest", @@ -21,6 +23,14 @@ "e2e:watch": "playwright test --watch", "e2e:ui": "playwright test --ui" }, + "devEngines": { + "packageManager": { + "name": "pnpm", + "version": "12.8.1", + "onFail": "download" + } + }, + "packageManager": "pnpm@12.8.1", "dependencies": { "@apollo/client": "^4.3.7", "@base-ui/react": "^1.9.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 941f58e6..1e9d2bbc 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -1,3 +1,161 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.8.1 + version: 12.8.1 + +packages: + + '@pnpm/exe.android-arm64@12.8.1': + resolution: {integrity: sha512-siDxcidfz5eM6L2tJfik+i2bbUEw8UC7MZ3Yl2HYdRmqbdhHGAOQACtOOu6QVW4Jul4/w5wVKm+wM8ACtgh1Pg==} + cpu: [arm64] + os: [android] + + '@pnpm/exe.android-x64@12.8.1': + resolution: {integrity: sha512-0F/uqPRc3aN4vLHsa0f+6fTtmsigFQ9/yxEU43fionF//pNHEd5e97b3w/nKIgqYMCUQCJphmBRMA9Q8/wlo9Q==} + cpu: [x64] + os: [android] + + '@pnpm/exe.darwin-arm64@12.8.1': + resolution: {integrity: sha512-/rwavvMQJsl2RIxOHBnDF+4Gk+fhQFAY4M3PQZoKskJOEnaJcygG9p1xby6wcQcbS4d9dIubv09CQhufTgfmpw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.8.1': + resolution: {integrity: sha512-htYt2gN7zqJKLhC99Ft6EUiO7myjZAZXfmGCTJUL1v+o7vE9hZdE1+fAkk4Oj1ZHNRti6b2woOxEkRkAusi8OQ==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.freebsd-x64@12.8.1': + resolution: {integrity: sha512-PB5BULNgytxvExoOXUUeseS/DcpPENs+cg3iA6xHHeaLT4ctEGKSGXEbuj7USeqvo8JBpAx039hivnbzGqDwnA==} + cpu: [x64] + os: [freebsd] + + '@pnpm/exe.linux-arm64-musl@12.8.1': + resolution: {integrity: sha512-kKeyC/ArjrgdciQRBrOL0j9HtOI4gDRgoyP+eJ1CQKyadpKlf/DhDDyspXe07R3B9KagTVLUF2nlt6Dr5quFIg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.8.1': + resolution: {integrity: sha512-q4s9a9X2O3N9aeUFooW24orNrxvu20+jyVUFR5RanPCqOKKPBrgs2iywMkBBx1aXkkzdWT54/mfz8Be8sJlWEw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-ppc64@12.8.1': + resolution: {integrity: sha512-F7XSjKJthwCh/L6abZiMpAfo2Cm61SZIT3e4dfgiIjESndlWaXCnfLKngSgZ/SEMsdmHT6dud3+kWIB3tw0/xQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-riscv64@12.8.1': + resolution: {integrity: sha512-z1ecpIK/ZY08Unk69KWWE9867oaKUytGUjrHnBoGFAAhsK+q+0cfvBH+f5fWobJ7FX97leMlLO+gkJolddM45Q==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-s390x@12.8.1': + resolution: {integrity: sha512-KU6tnrBfu4uKXTq6N5qJwSm8FSrs9Wc8b5AUEsHK9bRXwNdsZ1/lRxG1++X29SKRW9OSNuDSjxASv5mAvGo1gA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.8.1': + resolution: {integrity: sha512-eUE8BWJkDr2tJb/yrk0yVnszlmNqFarVxUlqiZ9BAkct28Nq/5g2H0tvMDmPA7fAQW3STNj+omJPPRVT+4s5sQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.8.1': + resolution: {integrity: sha512-8bDZ0lZlCdi2rvnFul7EYgcC7zKeWSe76y8JV2oXobaS8p9i9d6PSf6LgLtTM0fI7R9Oh4eLCbveXyNDMmvZ+g==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.8.1': + resolution: {integrity: sha512-MsT0dlrRxnCRCHCaosNbhWpEiAFnFuMEu3KuDJDU41BXgTwiINxU8goloUSUDejWZUqKaUqNC/qUTihl9nFtqg==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.8.1': + resolution: {integrity: sha512-SWtPe0PsbTTk//gJND10tMdOfCYjrkq5+qV49hzVhY7xz2iJ339Rc+xwASlcvbwTfrvH39YNqpKYibD+CRGLwA==} + cpu: [x64] + os: [win32] + + pnpm@12.8.1: + resolution: {integrity: sha512-9kupB1B/XOr+BsjTjmBS0BeURFgOwSed3Vv8EctIqoomRLZlmOB+dh2oSHKp/FfV+QK4f6SdAkGY1VhhKqu+RQ==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.android-arm64@12.8.1': + optional: true + + '@pnpm/exe.android-x64@12.8.1': + optional: true + + '@pnpm/exe.darwin-arm64@12.8.1': + optional: true + + '@pnpm/exe.darwin-x64@12.8.1': + optional: true + + '@pnpm/exe.freebsd-x64@12.8.1': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.8.1': + optional: true + + '@pnpm/exe.linux-arm64@12.8.1': + optional: true + + '@pnpm/exe.linux-ppc64@12.8.1': + optional: true + + '@pnpm/exe.linux-riscv64@12.8.1': + optional: true + + '@pnpm/exe.linux-s390x@12.8.1': + optional: true + + '@pnpm/exe.linux-x64-musl@12.8.1': + optional: true + + '@pnpm/exe.linux-x64@12.8.1': + optional: true + + '@pnpm/exe.win32-arm64@12.8.1': + optional: true + + '@pnpm/exe.win32-x64@12.8.1': + optional: true + + pnpm@12.8.1: + optionalDependencies: + '@pnpm/exe.android-arm64': 12.8.1 + '@pnpm/exe.android-x64': 12.8.1 + '@pnpm/exe.darwin-arm64': 12.8.1 + '@pnpm/exe.darwin-x64': 12.8.1 + '@pnpm/exe.freebsd-x64': 12.8.1 + '@pnpm/exe.linux-arm64': 12.8.1 + '@pnpm/exe.linux-arm64-musl': 12.8.1 + '@pnpm/exe.linux-ppc64': 12.8.1 + '@pnpm/exe.linux-riscv64': 12.8.1 + '@pnpm/exe.linux-s390x': 12.8.1 + '@pnpm/exe.linux-x64': 12.8.1 + '@pnpm/exe.linux-x64-musl': 12.8.1 + '@pnpm/exe.win32-arm64': 12.8.1 + '@pnpm/exe.win32-x64': 12.8.1 + +--- lockfileVersion: '9.0' settings: