A monorepo for all our frontend apps, designed to simplify sharing of code like components, styles, utils, and configs between different applications.
- Turborepo: For monorepo management and build tooling
- PNPM: Our package manager
- TypeScript: Our main language with shared, extendable config
- NextJS: The framework for all our frontend apps
- Tailwind CSS: For styling
- shadcn/ui: Our UI component base library to extend from
- Trunk CLI: Metalinter and formatter (ESLint, Prettier, Markdown, YAML, Shell, Commitlint)
- Vercel: For deployments and turborepo build remote caching
- GitHub Actions: For CI/CD (with Turborepo caching for builds via Vercel)
frontend-monorepo/
├── apps/ # Frontend applications
│ ├── app.mento.org/ # Mento Exchange UI
│ ├── governance.mento.org/ # Governance UI
│ ├── minipay.mento.org/ # MiniPay DApp
│ ├── reserve.mento.org/ # Reserve UI
│ └── ui.mento.org/ # Component Library Showcase
│
├── packages/ # Shared packages
│ ├── eslint-config/ # Shared ESLint configuration
│ ├── typescript-config/ # Shared TypeScript configuration
│ ├── ui/ # Shared UI library with tailwind styles and shadcn/ui components
│ └── web3/ # Shared library with web3-specific components and hooks
│
├── .github/ # GitHub workflows
│ └── workflows/ # CI/CD workflows
├── .trunk/ # Trunk CLI configuration and cache
├── turbo.json # Turborepo configuration
└── pnpm-workspace.yaml # PNPM workspace configuration- Node.js (v22 or later)
- PNPM (v10 or later)
- Trunk CLI (automatically installed during development)
-
Clone the repository:
git clone https://github.com/mento-protocol/frontend-monorepo && cd frontend-monorepo
-
Install dependencies:
pnpm install
-
Build all packages:
turbo build
-
Start the development server for all applications:
turbo dev
We use Trunk CLI as our universal linter and formatter. It combines ESLint, Prettier, Markdown linting, YAML linting, and more into a single, fast tool.
# Lint all files (comprehensive check)
pnpm lint
# Lint with auto-fix
pnpm lint:fix
# Format all files
pnpm format
# Check formatting without making changes
pnpm format:checkTo lint a specific application:
cd apps/<app-name>
pnpm lint # Lints only this appOr from the root directory:
trunk check apps/<app-name> # Direct Trunk usage- JavaScript/TypeScript: ESLint with your existing rules
- Code Formatting: Prettier (including Tailwind CSS class sorting)
- Markdown: Documentation formatting and best practices
- YAML: Configuration file formatting
- Shell Scripts: shellcheck and shfmt
- Git: Pre-commit and pre-push hooks
The workspace is configured to use Trunk for:
- Auto-formatting on save for JS/TS files
- Lint-on-type feedback
- Code actions for quick fixes
To run a specific application:
cd apps/<app-name>
pnpm devOr from the root directory:
pnpm dev --filter <app-name>
# i.e. pnpm dev --filter ui.mento.orgTo build a specific application:
pnpm build --filter <app-name>The UI package is located in packages/ui/ and contains reusable components built with shadcn/ui.
shadcn/ui is our component base layer we extend from.
- Install the shadcn/ui component you need:
pnpm dlx shadcn@latest add button - Customize it to your needs by simply editing
./packages/ui/src/components/ui/button.tsx - Export the new component from the main barrel file
./packages/ui/src/index.ts - Build the UI package:
pnpm build --filter @repo/ui
- Create a new component in
packages/ui/src/components - Export it from
packages/ui/src/index.ts - Build the UI package:
pnpm build --filter @repo/ui
Import components into an application:
// layout.tsx
import "@repo/ui/globals.css"; // Import once at the top of the app
import { Button } from "@repo/ui";We use Conventional Commits for standardized commit messages. This helps with automated versioning and generating changelogs.
Each commit message should follow this format:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]Types include:
feat: A new featurefix: A bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code changes that neither fix bugs nor add featurestest: Adding or fixing testschore: Changes to the build process or auxiliary toolsperf: Performance improvements
Example:
feat(ui): add new button componentGit Hooks: Trunk automatically manages git hooks that will:
- Pre-commit: Format and lint staged files
- Pre-push: Run comprehensive checks before pushing
- Commit-msg: Validate commit message format
The repository is set up with GitHub Actions for CI/CD:
- CI: On every PR, it runs linting (via Trunk), type checking, and builds all packages
- CD: On merges to main, it deploys applications to Vercel
The CI pipeline uses the Trunk GitHub Action to:
- Install Trunk CLI in the CI environment
- Run the same linting and formatting checks as local development
- Ensure consistent code quality across all environments
This repo utilizes Turborepo's Remote Caching, to speed up local development and CI/CD runs. It works by storing the outputs (build artifacts, logs) of tasks (like build, test, lint) in a shared remote cache on Vercel. Before running a task, Turborepo calculates a hash based on the input files, environment variables, and dependencies. If that hash exists in the remote cache, Turborepo downloads the stored output and logs instead of executing the task locally, saving a lot of time.
To connect your local machine to the remote cache:
-
Login to Vercel via Turbo CLI:
pnpm dlx turbo login
Follow the prompts to authenticate with your Vercel account.
-
Link the Repository:
pnpm dlx turbo link
This connects the local repository instance to your Vercel account/team's remote cache storage.
Once linked, turbo commands (like pnpm build, pnpm test) will automatically attempt to use the remote cache. You generally don't need to set TURBO_TOKEN or TURBO_TEAM locally after linking, as turbo stores the necessary credentials automatically.
The .github/workflows/ci.yml workflow is configured to automatically leverage remote caching:
- It uses the
TURBO_TOKEN(a Vercel Access Token) andTURBO_TEAM(your Vercel team slug/ID) environment variables. - These variables must be configured in the GitHub repository settings under Settings > Secrets and variables > Actions:
TURBO_TOKEN: As a Repository Secret.TURBO_TEAM: As a Repository Variable.
- With these variables set, the CI runner can authenticate with Vercel to read from and write to the remote cache.
This repository has Signed Remote Caching enabled ("signature": true in turbo.json) for enhanced security. This prevents cache poisoning by ensuring only trusted sources can write to the cache.
- How it works: Artifacts uploaded to the cache are signed using a secret key. Artifacts downloaded are verified against this signature.
- CI Requirement: The signing key must be provided to the CI environment via the
TURBO_REMOTE_CACHE_SIGNATURE_KEYenvironment variable. This should be configured as a Repository Secret in GitHub Actions settings. - Local Requirement: If you need to write to the cache locally (i.e., upload artifacts that weren't already there) with signing enabled, you would also need to set the
TURBO_REMOTE_CACHE_SIGNATURE_KEYenvironment variable in your local shell. Reading from the cache generally doesn't require the key.
- Add syncpack for consistent dependency versions across all monorepo packages
- Finetune builds. There's probably ways to make the builds of both packages and apps smaller and/or more performant.
- Make VS Code's "Go To Definition" on a component jump to the actual TypeScript source file instead of the compiled JS file in ./dist
- Enable additional Trunk linters for production CI (security scanning, image optimization)