A minimal TypeScript project template with a complete build, format, lint, and test toolchain. The code does nothing useful — it’s a starting point for new projects.
| Tool | Purpose |
|---|---|
| pnpm | Package manager |
| TypeScript | Type checking (tsc --noEmit) |
| Biome | Primary formatter and linter |
| oxlint | Secondary type-aware linter |
| ast-grep | Structural lint/format rules |
| convert-to-arrow | Codemod: function → arrow consts |
| Vitest | Test runner (unit + integration) |
| @vitest/coverage-v8 | Coverage provider + 80% threshold |
| jscpd | Code-duplication detection |
| tsx | Dev-time TypeScript execution |
| npm-run-all2 | Orchestrates multi-step scripts |
| pandoc | Markdown formatter (GFM) |
pnpm install
pnpm build # type-check with tsc
pnpm test # run unit tests (coverage gated)| Script | Description |
|---|---|
pnpm build |
Type-check the project with tsc (no output files) |
The lint script runs all linters in sequence via npm-run-all:
| Script | Description |
|---|---|
pnpm lint |
Run all lint steps |
pnpm lint:biome |
Biome check: format + lint + import order |
pnpm lint:oxlint |
oxlint with type-aware rules |
pnpm lint:exports |
ast-grep: no inline exports |
pnpm lint:functions |
ast-grep: no function declarations |
pnpm lint:md |
pandoc: Markdown must be GFM-formatted |
pnpm lint:peer-deps |
pnpm: no peer dependency conflicts |
pnpm lint:audit |
pnpm audit: production dependency vulns |
pnpm lint:duplicates |
jscpd: code duplication (5% threshold) |
The format script runs all formatters in sequence:
| Script | Description |
|---|---|
pnpm format |
Run all format steps |
pnpm format:arrows |
convert-to-arrow — rewrite function to arrow consts |
pnpm format:braces |
ast-grep strip single-statement braces |
pnpm format:biome |
Biome format with auto-fix |
pnpm format:check |
Biome check (lint + format auto-fix) |
pnpm format:md |
pandoc: reformat Markdown to canonical GFM |
| Script | Description |
|---|---|
pnpm test |
Run unit tests with coverage (80% threshold) |
pnpm test:watch |
Watch mode |
pnpm test enforces an 80% coverage threshold (statements, branches,
functions, lines) via @vitest/coverage-v8; pnpm test:watch runs
without coverage.
These are enforced by the toolchain, not just preferences:
- Arrow functions only — no
functiondeclarations (convert-to-arrow+ ast-grep rule) - Separate exports — no inline
exportkeywords (ast-grep rule) - Single-statement brace stripping —
if/for/whilewith one body line drop braces (ast-grep rule) - Double quotes, 2-space indent, 80-char width, trailing commas, semicolons (Biome)
- ESM only (
"type": "module") - Markdown via pandoc — all
.mdformatted withpandoc -t gfm(lint:md/format:md)
├── .ast-grep/rules/ # Structural lint/format rules
├── .github/workflows/ # CI
├── scripts/ # Tooling scripts (pandoc-md)
├── src/
│ ├── index.ts # Trivial module (replace with your code)
│ └── tests/ # Unit and integration tests
├── biome.json # Biome formatter + linter config
├── .oxlintrc.json # oxlint type-aware rules
├── tsconfig.json # TypeScript config
└── vitest.config.ts # Test config