Pragmatic template for a
TypeScriptmonorepo withpnpm,turborepo, andvitest.
The development toolchain is pinned in .mise.toml, with tool artifacts locked in mise.lock.
- Getting Started
- What's Included
- Available Scripts
- Publishing (OIDC)
- Test Structure
- FAQ
- Author
- Show your support
- License
Use this repository as a template when you want a fresh TypeScript monorepo with pnpm, turborepo, vitest, oxfmt, oxlint, Changesets, and GitHub Actions already wired together.
There are two common ways to create your own repository from it.
- Open
jkomyno/pnpm-monorepo-template. - Click Use this template.
- Choose Create a new repository.
- Pick the owner, repository name, visibility, and whether you want to include all branches.
- Click Create repository.
- Clone your new repository:
git clone git@github.com:<your-github-user-or-org>/<your-new-repo>.git
cd <your-new-repo>If you prefer the GitHub CLI, create and clone the new repository in one command:
gh repo create <your-github-user-or-org>/<your-new-repo> \
--template jkomyno/pnpm-monorepo-template \
--private \
--clone
cd <your-new-repo>Use --public instead of --private if the repository should be public.
This template pins Node.js and pnpm in .mise.toml. Install mise, then let it provision the repository toolchain:
mise trust
mise install
pnpm install
pnpm build
pnpm testUse mise as the source of truth when changing tool versions, and run mise lock after an update to refresh the locked tool artifacts. The packageManager field in package.json mirrors the pnpm pin for compatibility with package-manager-aware tooling.
After that, make the template yours:
- Replace the
@jkomyno/*package scope in the localpackage.jsonfiles with your own npm scope. - Update the
name,description,repository,author, and package visibility fields in the root and package-levelpackage.jsonfiles. - Update README badges, author details, trusted-publisher examples, and links so they point to your new GitHub repository.
- Remove, rename, or replace the example packages in
packages/once you know what your monorepo should contain.
At this point the repository is ready for normal development. Use pnpm build, pnpm test, pnpm lint:ci, and pnpm changeset as the main day-to-day commands.
pnpmworkspace, whose configuration is stored inpnpm-workspace.yaml. Two example packages are included,common-utilsandexample, with the latter importingcommon-utilsas a dependency. All local packages are decorated with a@jkomyno/*scope (you may want to substitute these instances in thenameentries of anypackage.jsonwith yours or your company's name).tsdownbundler, whose configuration is stored intsdown.config.base.ts.turborepo, whose configuration is stored inturbo.json- centralized dependency versions through the
pnpmcatalog inpnpm-workspace.yaml, plus install hardening with release-age gating and targeted transitive dependency overrides. - the
vitesttest engine, whose shared configuration is stored invitest.config.base.ts. Workspace imports in tests resolve to TypeScript sources through the@jkomyno/sourceexport condition (mirroring thecustomConditionstsconfig setting), so running tests never requires building dependency packages first. - opinionated formatting and linting setups via
oxfmtandoxlint, whose configurations are defined in the.oxfmtrc.jsoncand.oxlintrc.jsoncfiles.Lefthookruns thelint-stagedchecks before each commit. - Changesets for versioning and changelogs; the Release workflow opens a "Version Packages" PR when changesets land on
main, and publishes to npm when that PR is merged using npm trusted publishing (OIDC)—no long-lived tokens. See Publishing (OIDC) below. - pkg.pr.new for continuous preview releases: each PR gets installable preview packages (install the GitHub App on the repo first).
- shared AI-agent guidance through
AGENTS.mdand itsCLAUDE.mdsymlink, plus repository-local TypeScript and testing skills under.agents/skills.
pnpm install: install the dependencies needed for each package.pnpm build: typecheck and transpile the local TypeScript packages to JavaScript.pnpm build:watch: transpile the local TypeScript packages to JavaScript, and watch for changes.pnpm check:exports: check that theexportsfield in thepackage.jsonfiles of each exported package is correctly set, using@arethetypeswrong/cli.pnpm typecheck: run type checks for packages that define atypecheckscript.pnpm format:ci: check that the code follows theoxfmtguidelines.pnpm format: check that the code follows theoxfmtguidelines, and override it to follow them if possible.pnpm lint:ci: check that the code follows theoxfmtandoxlintguidelines.pnpm lint: check that the code follows theoxfmtandoxlintguidelines, and override it to follow them if possible.pnpm lint:fix: same aspnpm lint; included as a clearer alias.pnpm test:unit: run unit tests.pnpm test:integration: run integration tests.pnpm test: run all tests.pnpm bench: run benchmarks for packages that define abenchscript.pnpm bench:watch: run benchmarks in watch mode for packages that definebench:watch.pnpm changeset: add a new changeset (version bump + changelog entry).pnpm version-packages: apply changesets (bump versions, update changelogs, thenpnpm install). Used by the Release workflow.
Releases use npm trusted publishing (OIDC), so you do not need an NPM_TOKEN secret. You configure npm to trust this repo's workflow once per package:
Template note: the release workflow is scaffolded but disabled by default in
release.yaml. After you configure npm trusted publishing (OIDC), removeif: falseand uncomment the Changesets step in that file.
- Publish the package manually once (if it has never been published), so it exists on npm.
- On npmjs.com, open the package → Package settings → Trusted Publisher.
- Choose GitHub Actions and set:
- Organization or user: your GitHub org or username (e.g.
jkomyno) - Repository: this repo name (e.g.
pnpm-monorepo-template) - Workflow filename:
release.yaml(must match exactly, including extension)
- Organization or user: your GitHub org or username (e.g.
- Save. Future publishes from the Release workflow will use OIDC; no tokens required.
Repeat for each publishable package in the monorepo. Optional: under Publishing access, enable “Require two-factor authentication and disallow tokens” so only the trusted workflow can publish.
Tests live in each package's __tests__ directory. Put isolated, deterministic tests in __tests__/unit and package-boundary or adapter tests in __tests__/integration.
pnpm test runs both groups locally and in CI. Keep required integration tests hermetic; put tests that need external infrastructure behind an explicit, opt-in script or workflow instead of the default verification path.
- How do I add a new package to the local workspace?
- Create a folder in
packages/with apackage.jsonandtsconfig.json. Extendtsconfig.src.jsonfor publishable source builds andtsconfig.test.jsonfor packages that typecheck source and tests together. Usecommon-utilsas the publishable-library example andexampleas the private-package example.
- How do I add a new dependency that should be available to each package in the local workspace?
pnpm add -w $dependencyName
Hi, I'm Alberto Schiabel, you can follow me on:
Give a ⭐️ if this project helped or inspired you!
Built with ❤️ by Alberto Schiabel.
This project is MIT licensed.