Objective
To build a monorepo using a high-performance, modern tech stack, designed to deliver a better developer experience and enhance efficiency across the board.
Historically, developer tooling has lagged behind in terms of both performance and usability. Many of these tools have been JavaScript-based, limiting the potential for significant speed improvements. There's a ceiling to the performance achievable when JavaScript is used to handle tasks like building, linting, and formatting… until now.
Enter Rust-based tooling. With its exceptional performance and system-level capabilities, Rust has rapidly gained popularity for powering
new, high-performant tools like rsbuild, biome, and turbo. These tools outperform their predecessors, providing greater customization
and performance that developers need.
In this monorepo, we'll be using pnpm as our package manager, taking advantage of its workspace capabilities to manage multiple projects
seamlessly. While other package managers like yarn and npm also support workspaces, pnpm stands out by not hoisting packages to the
root and keeping workspace packages isolated. This isolation also optimizes installation by symbolically linking dependencies, minimizing
redundancy and improving efficiency.
Tech stack
In this monorepo, we’ll be using a carefully selected tech stack that prioritizes speed, efficiency, and developer experience. While I recommend following along with these specific tools, you can substitute them based on your own preferences or requirements later.
- Pnpm: A fast, efficient package manager with robust workspace features.
- TypeScript: A typed superset of JavaScript that provides static type checking for improved reliability.
- BiomeJS: A high-performance formatter and linter for JavaScript, TypeScript, and more.
- Simple Git Hooks: A lightweight tool for managing Git hooks with zero dependencies.
- Lint-Staged: Enables linting and formatting of staged files before committing to ensure clean, consistent code.
- CommitLint: Ensures commit messages adhere to a defined convention for better Git history.
- Rsbuild: A Rust-based, high-speed build tool optimized for performance.
- Rslib: A build tool for libraries, based on
rsbuild. - TurboRepo: An incremental build system written in Rust, optimized for JavaScript and TypeScript.
Prerequisites
To follow along, ensure that you have the following installed on your machine:
- Git
- Node.js: Use Node.js v20, as we'll create an AWS Lambda function that requires Node.js v20.x for compatibility with AWS runtimes.
Installing PNPM
You can install and enable PNPM using Corepack:
corepack enable
corepack prepare pnpm@latest --activateCaution
Note: Node.js is moving toward removing Corepack from its distribution in a future major release. If this happens, you'll need to install Corepack manually before enabling PNPM. More details can be found here.
Initial setup
Let's start by creating a new folder named monorepo. Inside this folder, run the following commands to initialize PNPM and Git:
# Initialize PNPM and Git
pnpm init
git initAfter running the these commands, a package.json configuration file will appear in your project's root directory. Open this file to make a
few adjustments.
{
"name": "monorepo",
"version": "1.0.0",
"description": "",
"type": "module",
"private": true,
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}Code formatting and linting
To ensure consistent code styling across the team and reduce stylistic changes during Git pull request reviews, we'll set up biome for
code linting and formatting. Biome's fast, Rust-based performance makes it an ideal choice for keeping our codebase clean and
developer-friendly.
Installing and setting up Biome
Let's start by installing Biome as a development dependency:
pnpm add -D @biomejs/biomeNext, initialize Biome in the project:
pnpm biome initAfter running the init command, a biome.json configuration file will appear in your project's root directory. Open this file to make a few adjustments to better suit our team's coding standards.
Biome is designed to align closely with Prettier's style defaults, so if you've used Prettier, much will feel familiar. However, for modern, larger monitors, I find a maximum line length of 140 characters more practical than the traditional 80 characters. This allows for readable code without excessive line wrapping, especially on high-resolution displays.
Here's the configuration we'll use in biome.json, with adjustments to the formatter and JavaScript settings:
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": false,
"ignore": []
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 140,
"attributePosition": "auto"
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "single"
}
}
}Automating Biome in our workflow
While you can run Biome manually using commands from its documentation, we want it integrated into our development workflow to automatically lint and format code before each commit. This approach minimizes code style discrepancies in the codebase.
For IDE integration, I'll be using Visual Studio Code (VSCode) as my primary editor in this monorepo. I'll walk through some recommended extensions and workspace settings that make Biome's linting and formatting visible directly within the editor, enhancing the development experience and catching issues in real-time.
Let's configure some workspace settings for VSCode by creating a new file at ./.vscode/settings.json with the following content:
{
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit",
"quickfix.biome": "explicit"
},
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.tabSize": 2,
"editor.autoIndent": "advanced",
"editor.insertSpaces": true,
"editor.detectIndentation": false,
"editor.rulers": [
{
"column": 140,
"color": "#ff000066"
}
],
"html.format.wrapLineLength": 140
}This configuration sets VSCode to use 2 spaces for indentation instead of tabs, enables advanced auto-indentation, and explicitly designates
biome as the default formatter. It also establishes a maximum line length of 140 characters and adds a subtle red ruler as a visual guide
to indicate where the maximum line length ends.
Next, we'll add simple-git-hooks and lint-staged to automatically run biome checks and fixes whenever we attempt to commit our code.
Installing required packages
Start by installing the necessary packages as development dependencies:
pnpm add -D simple-git-hooks lint-stagedThen, make the following updates to the package.json file in the root of your repository:
{
"name": "monorepo",
"version": "1.0.0",
"description": "",
"type": "module",
"private": true,
"scripts": {
"format": "biome format --write",
"lint": "biome lint --write",
"check": "biome check --write"
},
"packageManager": "pnpm@9.5.0",
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"simple-git-hooks": "^2.11.1"
},
"simple-git-hooks": {
"pre-commit": "pnpm dlx lint-staged"
},
"lint-staged": {
"*.{js,cjs,mjs,ts,mts,json,jsonc}": "pnpm dlx biome --check --write"
}
}With this setup, whenever you run git commit, the biome check command will automatically lint and format any modified files included in
that commit. Any files that receive automatic fixes will be added to the commit for you.
If you wish to run biome linting and formatting manually at any time, you can use the following commands:
# Formats files and directories using the format command with the --write option
pnpm format
# Lint and apply "safe fixes" to files and directories using the lint command with the --write option
pnpm lint
# Runs both format and lint commands with the --write option
pnpm checkBefore we start making commits, it's also a good idea to install the Biome VSCode extension. This extension enhances your development experience by providing integrated linting and formatting directly within the editor.
To install the Biome extension, go to the Extensions view in VSCode (you can access it by clicking the Extensions icon in the sidebar or
using the shortcut Ctrl+Shift+X). Search for “Biome” and install the extension by BiomeJS.
Additionally, you can add Biome as a recommended extension for your workspace. Create or update the ./.vscode/extensions.json file with
the following content:
{
"recommendations": [
"biomejs.biome"
]
}This ensures that anyone who clones the repository will be prompted to install the Biome extension, keeping the development environment consistent across the team.
Before we make our first Git commit, we need to create a .gitignore file to specify which files and folders Git should exclude from
tracking and committing. Create a new file in the root of the repository at ./.gitignore and add the following content:
# Build output
dist/
build/
# Test output
coverage/
test-reports/
# Generated outputs / caches
.rsbuild/
.rslib/
.turbo/
.pnpm_store/
# Dependencies
node_modules/
# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# IDE settings
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Local .env files
.env*.local
# Miscellaneous
.DS_Store
*.local
*.log*
Thumbs.dbThis basic .gitignore file will exclude generated folders and files that don't need to be committed to Git. Instead, these should be
generated by each engineer who clones the repository or when running as part of a CI/CD pipeline.
Now we can proceed to make our first commit. Run the following commands:
# Stage all changed files
git add .
# Commit the changed files with a descriptive commit message
git commit -m "feat: initial setup with biome and pnpm"Note
- Next add instructions to setup CommitLint with conventional commits
- Afterwards add instructions to convert out repo into a monorepo and begin by adding a new apps/lambda/
