An open API service indexing awesome lists of open source software.

https://github.com/neg4n/good-typescript-libraries-template

Production ready NX monorepo template for developing and releasing TypeScript libraries, including automated GitHub repository setup. And believe me, it is good.
https://github.com/neg4n/good-typescript-libraries-template

biomejs github github-actions husky-hooks javascript-template nx nx-workspace pnpm project-template rollup template typescript typescript-library typescript-template

Last synced: about 1 month ago
JSON representation

Production ready NX monorepo template for developing and releasing TypeScript libraries, including automated GitHub repository setup. And believe me, it is good.

Awesome Lists containing this project

README

          

# Good TypeScript Libraries Template

An opinionated production-ready TypeScript **monorepo** template with automated builds, testing, and releases powered by Nx.

And believe me. **It is good.**

> [!IMPORTANT]
> Looking for a simpler, non-monorepo template without **nx** but same features?
> [Check out `good-typescript-library-template`!](https://github.com/neg4n/good-typescript-library-template)

## Features

- ๐Ÿงญ **Nx-powered Monorepo** โ€“ multiple libraries under `packages/*` with intelligent task orchestration
- โšก **Affected-only CI** โ€“ lint, typecheck, test, build run only for changed packages
- ๐Ÿ’พ **Local & remote caching** โ€“ skip redundant work across runs
- ๐Ÿš€ **Independent releases** โ€“ per-package versioning with conventional commits
- ๐Ÿ“Š **Project graph awareness** โ€“ understands package dependencies
- ๐Ÿ“ฆ **Dual Package Support** โ€“ Rollup outputs CommonJS and ESM builds per package
- ๐Ÿ›ก๏ธ **Type Safety** โ€“ Extremely strict TypeScript configuration (shared `tsconfig.base.json`)
- ๐Ÿ” **Always up-to-date deps** โ€“ [Renovate](https://github.com/renovatebot/renovate) bot for CVE-aware automatic dependency updates
- โœ… **Build Validation** โ€“ Uses `@arethetypeswrong/cli` to check package exports
- ๐Ÿงช **Automated Testing** โ€“ Vitest workspace with per-package coverage PR comments
- ๐ŸŽจ **Code Quality** โ€“ Biome linting/formatting with pre-commit hooks
- โš™๏ธ **CI/CD Pipeline** โ€“ GitHub Actions with workflow chaining (CI โ†’ Version โ†’ Publish)
- ๐Ÿ”ง **One-Click Setup** โ€“ Automated repository configuration with `init.sh` script
- ๐Ÿ›๏ธ **Repository rulesets** - Branch protection with linear history and PR reviews
- ๐Ÿšท **Feature cleanup** - Disable wikis, projects, squash/merge commits
- ๐Ÿ”„ **Merge restrictions** - Rebase-only workflow at repository and ruleset levels
- ๐Ÿ‘‘ **Admin bypass** - Repository administrators can bypass protection rules
- ๐Ÿ” **Actions verification** - Ensure GitHub Actions are enabled
- ๐Ÿ—๏ธ **Secrets validation** - Check and guide setup of required secrets

## Tech Stack

- **Nx** - Orchestrates tasks, caching, and releases across packages
- **TypeScript** - Strict configuration for type safety
- **Rollup** - Builds both CommonJS and ESM formats per package
- **Biome** - Fast linting and formatting
- **Vitest** - Testing with coverage reports (workspace config)
- **Husky** - Pre-commit hooks for code quality
- **Nx Release** - Automated versioning/changelog + npm publishing
- **pnpm** - Fast package management with Corepack
- **GitHub Actions** - CI/CD pipeline (validation, versioning, publishing)

## Setup

### 1. Use the template

Run this in your terminal _[GitHub CLI](https://cli.github.com) required_

```bash
gh repo create my-typescript-libraries --clone --template neg4n/good-typescript-libraries-template --private && cd my-typescript-libraries
```

> [!NOTE]
> Replace `my-typescript-libraries` with your new library name, you can also change the visibility of the newly created repo by passing `--public` instead of `--private`! Read more about possible options in [GitHub CLI documentation](https://cli.github.com/manual/gh_repo_create)

#### Setup via GitHub web interface

If for some reason you can't run the mentioned commands in your terminal, click the "Use this template โ–พ" button below (or in the top right corner of the repository page)



### 2. Minimal Setup

Run the initialization script to automatically configure your repository:

```bash
# One-command setup
./init.sh
```

This script will:
- ๐Ÿ”’ **Create repository rulesets** for branch protection (linear history, PR reviews)
- ๐Ÿšซ **Disable unnecessary features** (wikis, projects, squash/merge commits)
- โš™๏ธ **Configure merge settings** (rebase-only workflow at repository and ruleset levels)
- ๐Ÿ‘ค **Grant admin bypass** permissions for repository administrators
- ๐Ÿ”ง **Verify GitHub Actions** and validate repository configuration
- ๐Ÿ”‘ **Check required secrets** and provide setup instructions

### 3. Required Secrets

The script will guide you to set up these secrets if missing:

**NPM_TOKEN** (for publishing):
```bash
# Generate NPM token with OTP for enhanced security
pnpm token create --otp= --registry=https://registry.npmjs.org/

# Set the token as repository secret
gh secret set NPM_TOKEN --body "your-npm-token-here"
```

**ACTIONS_BRANCH_PROTECTION_BYPASS** (for automated pushes from versioning workflow):
```bash
# Create Personal Access Token with 'repo' permissions
# Visit: https://github.com/settings/personal-access-tokens/new

# Set the PAT as repository secret
gh secret set ACTIONS_BRANCH_PROTECTION_BYPASS --body "your-pat-token-here"
```

## Scripts

| Command | Description |
|---------|-------------|
| `pnpm lint` | Biome lint across all packages |
| `pnpm lint:fix` | Auto-fix lint issues |
| `pnpm test` | Run Vitest across the workspace |
| `pnpm test:coverage` | Vitest with coverage |
| `pnpm typecheck` | TypeScript `--noEmit` across packages |
| `pnpm build` | Rollup builds for every package |
| `pnpm attw` | Run AreTheTypesWrong for all packages |
| `pnpm release:dry` | Nx Release dry run |
| `pnpm release:version` | Bump versions & changelogs, create tags |
| `pnpm release:publish` | Publish tagged packages to npm |

Per-package targets are also available, e.g. `pnpm nx run core:build`.

## Adding a new package

Use the built-in generator to scaffold a new package with all the template's features:

```bash
pnpm nx g @good-typescript-libraries/tools:library --name=my-utils
```

### What the generator does

1. **Creates package** at `packages//` with:
- `project.json` with Nx targets (build, lint, test, typecheck, attw)
- `package.json` with dual CJS/ESM exports and publishConfig
- `rollup.config.js` producing ESM, CJS, minified ESM, and bundled types
- `tsconfig.json` and `tsconfig.build.json`
- Placeholder `src/index.ts` and `test/index.test.ts`

2. **Updates `vitest.config.ts`** โ€” adds workspace entry with test config and alias

3. **Updates CI workflow** โ€” adds coverage report step to `.github/workflows/ci.yml`

### Generator options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `name` | string | โ€” | Package name (required) |
| `--description` | string | `""` | Package description for package.json |
| `--skipCoverage` | boolean | `false` | Skip adding coverage report to CI |

### Examples

```bash
pnpm nx g @good-typescript-libraries/tools:library --name=utils --description="Utility functions"

pnpm nx g @good-typescript-libraries/tools:library --name=internal-tools --skipCoverage
```

After generating, add your exports to `packages//src/index.ts` and run `pnpm nx build ` to verify.

## Changing the npm scope
The default scope is `@good-typescript-libraries`. To rename it (e.g. to `@my-libraries`), update these places:
1. **nx.json** โ€“ set `"npmScope": "my-libraries"`.
2. **tsconfig.base.json** โ€“ change the path alias to `"@my-libraries/*": ["packages/*/src/index.ts"]`.
3. **Package names/import paths** โ€“ in each `packages/*/package.json` update `"name"` and `importPath` (if present) to the new scope; adjust any imports in code/tests (e.g. `import {...} from '@my-libraries/core'`).
4. **Vitest aliases** โ€“ in `vitest.config.ts` update the alias map to the new scope.
5. **Tools package** โ€“ in `tools/package.json` update `"name"` to the new scope (e.g. `@my-libraries/tools`).
6. **Docs & examples** โ€“ replace `@my-libraries/` occurrences in README/CONTRIBUTING and commands.

> [!NOTE]
> After changing the scope, generator commands will also change. For example:
> ```bash
> pnpm nx g @my-libraries/tools:library --name=utils
> ```

After those edits, run `pnpm install`, then `pnpm lint && pnpm test && pnpm build` to ensure everything resolves correctly.

## Release model (Nx Release)
- Conventional commits determine bumps; packages release **independently**.
- Version workflow (triggered after CI succeeds on `main`) runs `nx release version --yes` to update versions/changelogs and create tags (`{projectName}@{version}`).
- Publish workflow (tag push) runs `nx release publish --projects ` with npm provenance.
- Required secret: `NPM_TOKEN`; optional `ACTIONS_BRANCH_PROTECTION_BYPASS` if your repo blocks CI pushes.

## CI/CD (GitHub Actions)
- `CI` (push/PR): runs `nx affected` for lint โ†’ typecheck โ†’ tests + coverage โ†’ build on Node 20 & 22.
- `Release Version` (after CI succeeds on `main`): bumps versions/changelogs, creates tags, pushes back.
- `Release Publish` (tag push): rebuilds tagged package(s) and publishes to npm with provenance.

## Renovate

`renovate.json5` already turns onboarding off, so Renovate will start opening update PRs as soon as the GitHub App is installed. Enable it like this:

1. Visit https://github.com/apps/renovate and click **Install**.
2. Choose your personal account or organization, then pick **All repos** or **Only select repos** (include this one).
3. Approve the requested permissions to finish installation. Renovate will run shortly after and open PRs based on `renovate.json5`.

Notes:
- Want to stop it? Uninstall the app or set `"enabled": false` in `renovate.json5`.
- Need custom rules (schedules, groups, automerge)? Extend `renovate.json5` - no extra onboarding PR is required.

## FAQ

#### How do I modify the merging methods?

`good-typescript-libraries-template` sets **rebase-only** at both repository and main branch levels. Here's how to modify this:

##### **Current Setup**
- **Repository**: Rebase merging only (squash/merge disabled)
- **Main branch ruleset**: Requires rebase merging

##### **To Change Merge Methods**

**For repository-wide changes:**
- **Settings > General > Pull Requests** - toggle merge methods

**For branch-specific changes:**
- **Settings > Rules** - edit the main branch ruleset's "Require merge type"

##### **Precedence Rules**
1. Repository settings define what's **available**
2. Rulesets add **restrictions** on top
3. **Most restrictive wins** - if repository disallows a method but ruleset requires it, merging is **blocked**

##### **Common Modifications**
- **Allow all methods**: Enable squash/merge in repo settings + remove "Require merge type" from ruleset
- **Squash-only**: Change repo settings to squash-only OR keep current repo settings + change ruleset to require squash
- **Different rules per branch**: Create additional rulesets for other branch patterns

> [!TIP]
> Since `good-typescript-libraries-template` is rebase-only, you must enable other methods in repository settings before rulesets can use them.

#### How to solve pnpm lockfile error on my CI/CD?

If you're seeing this error in your CI/CD (GitHub Actions) pipeline:

```
ERR_PNPM_OUTDATED_LOCKFILE Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with /package.json
```

##### **Why This Happens**
This template uses `--frozen-lockfile` to ensure consistent installations in CI/CD. The error occurs when your `package.json` has been modified but the `pnpm-lock.yaml` hasn't been updated to match.

##### **Solution**
Run the following command locally:
```bash
pnpm install
```

Then commit the updated lockfile:
```bash
git add pnpm-lock.yaml
git commit -m "chore: update pnpm lockfile"
```

#### Why Linear History?

Linear history provides several benefits for library releases:

- **Clean commit history** - Easy to track changes and debug issues
- **Simplified releases** - Nx Release and conventional commits work best with linear commits
- **Clear changelog** - Each commit represents a complete change
- **Better debugging** - `git bisect` works more effectively
- **Consistent workflow** - Forces proper PR review process