Technology and its tooling evolves overtime, the aim of this project is to provide a modern Typescript monorepo example for today and for the future. Watch this space as time progresses to be kept up to date with changes within this area.
Feel free to ask any questions or raise any issues.
This project uses the following technologies and tools:
- PNPM - Package management
- Turborepo - High performance build system
- Husky - Git hooks
- Typescript - Type-safe codebase
- Prettier - Code formatter
- Eslint - Code linter
- Nodemon - Development runtime (script monitor)
- Vitest - Frontend & backend test suite
- GitHub Actions - CI/CD workflow automation
- Conventional Commits - Commit message standard
A Fast, disk space efficient package manager with native workspace support. PNPM is a drop-in replacement for NPM and Yarn (v1
& v2
). It's faster than both and uses less disk space. It has a lockfile that is compatible with both NPM and Yarn. With regard to a monorepo, in most cases, it also serves as a replacement for Lerna.
A high-performance build system for monorepos. Turborepo is a replacement for Lerna and it is mildly faster than Lerna's integrated counterpart Nx. It also requires less configuration and has less of a learning curve compared to Nx if used independently.
It is worth mentioning, along side Nodemon, you can get the same development experience as if you were working with Concurrently to run multiple development scripts or packages local to the repository.
A modern Git hooks manager.
A superset of JavaScript that compiles to clean JavaScript code. A type-safe coding language and a great tool for large codebases as it helps to prevent bugs and improves code quality.
You will notice 3 tsconfig.ts
file variants in the root of the project.
tsconfig.base.json
- This is the base configuration for all packages within the monorepo. It is worth pointing out that we extend the recommended rules for the current Node LTS version and for strict type-checking from@tsconfig/node-lts-strictest
(tsconfig/bases)tsconfig.build.json
- This is the configuration for the build process. It extends the base configuration and configures where the compiled codebase should be outputted to and what should be compiled.tsconfig.json
- This is the configuration for the root of the monorepo mainly for the IDE to use and other libraries that may need it such as Eslint (@typescript-eslint
). It also extends the base configuration.
Within each packages/*
directory, you will notice a tsconfig.json
and tsconfig.build.json
file. This is for package specific Typescript configuration. It is important in some aspects to treat each package independently from each other as they may have different requirements.
For example, the tsconfig.build.json
file within a packages/api
directory may have its module
option set to commonjs
. Whereas the tsconfig.build.json
file within a packages/frontend
directory might have its module
option set to esnext
.
It is worth mentioning, to improve performance, the incremental option within the tsconfig.base.json
has been set to true
. This will cache the results of the last successful compilation and use it to speed up the next compilation.
Another configuration that is worth mentioning, is that the declaration option has also been set to true
. This will generate .d.ts
files for each file within the built dist
directory. These files separate out the type information from the compiled code resulting in cleaner code output. This is also faster for the packages that depend on them as the compile doesn't have to sift through the code to find the types.
An opinionated code formatter.
A pluggable and configurable linting tool that statically analyses your code to quickly find problems and can be used to enforce code style.
A monitoring tool that restarts the configured executable when file changes in the configured directory are detected.
Within the packages/*
directories, you will notice a nodemode.json
that has an executable script of exec: pnpm typecheck && pnpm build
. This is to ensure that the codebase is fully type-checked and built - ready for dependants to import. Remember, that the built configuration is only intended for the final built code and not the source code. This form of double Type-checking also quite performant as the Typescript compilation is cached in the form a generate tsconfig.tsbuildinfo
file thanks to the incremental: true
Typescript configuration option.
A fast and feature-rich Vite-native testing framework. Vitest provides a seamless testing experience with native TypeScript support, built-in code coverage, and a simple, Jest-compatible API.
Unlike the previous Jest setup, Vitest leverages Vite's native ESM support and provides faster test execution with minimal configuration.
A CI/CD workflow automation tool that is built into GitHub. It is a great tool for automating your workflow and can be used to build, test and deploy your codebase.
It is worth pointing out the .github/workflows/pr.yaml
file. This workflow runs on every pull_request
and validates the PR title follows the Conventional Commits specification.
A specification for adding human and machine readable meaning to commit messages. It is a great tool for automating your workflow and can be used to build, test and deploy your codebase.
The project follows the Conventional Commits specification with the following accepted prefixes:
-
feat:
- A new feature (minor version bump) -
fix:
- A bug fix (patch version bump) -
docs:
- Documentation changes -
style:
- Code formatting, missing semicolons, etc. (no functional changes) -
refactor:
- Code refactoring without introducing new features or fixing bugs -
test:
- Adding or modifying tests -
chore:
- Maintenance tasks, updates to build processes, etc. -
perf:
- Performance improvements -
ci:
- Changes to CI configuration files and scripts -
build:
- Changes that affect the build system or external dependencies Optional scopes can be added in parentheses to provide additional context: -
feat(auth): add two-factor authentication
-
fix(api): resolve connection timeout issue
Breaking changes should be indicated by adding an exclamation mark after the type/scope:
feat!: major API redesign
refactor!(core): complete system architecture change
A modern static site generator powered by Vue 3 and Vite. VitePress enables the creation of fast, lightweight documentation sites with a focus on developer experience and performance.
Project documentation scripts:
pnpm docs:dev
: Start local development serverpnpm docs:build
: Build production documentationpnpm docs:preview
: Preview production build locally
The documentation is located in the docs/
directory and uses VitePress and supports Mermaid diagrams.
A comprehensive template for software and system architecture documentation. Arc42 provides a structured approach to documenting software architectures, making complex systems more understandable and maintainable.
The project uses VitePress to render Arc42-style documentation, allowing for easy navigation and maintenance of architectural documentation.
This is an example of how you may give instructions on setting up your project locally. To get a local copy up and running follow these simple example steps.
Here's a list of technologies that you will need in order to run this project. We're going to assume that you already have Node.js installed, however, you will need the required version (LTS or v18+) as stated in the package.json:engines.node
configuration.
If your computer doesn't already have PNPM installed, you can install it by visiting the PNPM installation page.
If you're using MacOS, you can install it using Homebrew.
brew install pnpm
No you have PNPM installed, you can install the required Node version by running the following command.
pnpm add -g n
n lts
To install the monorepo and all of its dependancies, simply run the following command at the root of the project.
pnpm install
To run the monorepo and all of its packages, simply run the following command at the root of the project.
pnpm dev
Turborepo and Nodemon will run each package in parallel and watch for file changes.
There are several ways to deploy this project. Depending on your requirements, here are some examples of some popular methods.
Making use of the pnpm deploy command, we can create a Docker image that only contains the production dependencies for a specific package within the monorepo. This essential bundles the given package and all of its local and external dependencies.
FROM workspace as pruned
RUN pnpm --filter <PACKAGE_NAME> --prod deploy <TARGET_DIRECTORY>
FROM node:18-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=pruned /app/pruned .
ENTRYPOINT ["node", "index.js"]
Distributed under the MIT License. See the local LICENSE
file for more information.