Opinionated monorepo powered by moon and proto with code generation templates, unified build pipelines, and dependency version management.
-
Primary focus: TypeScript (Node 26, TypeScript 6)
-
Secondary support: Go (Go 1.26), Rust (Rust 1.96)
- Clone this repository
- IMPORTANT: Update internal template variables (see Customization)
- IMPORTANT: Update or replace the LICENSE file with your own
- Install dependencies
proto installpnpm install
- Sync configs:
moon sync config-schemas - Generate your first project:
- TypeScript:
moon generate node-appormoon generate node-package - Go:
moon generate go-appormoon generate go-package - Rust:
moon generate rust-appormoon generate rust-package
- TypeScript:
- Sync apps and packages:
moon sync
apps/ # Applications (entry points, deployable artifacts)
packages/ # Shared libraries (consumed by apps and other packages)
infra/ # Infrastructure tooling
tests/ # Test apps, toy projects, and POCs
templates/ # Moon code generation templates
.moon/ # Moon workspace and task configuration
TypeScript tasks are split across three files to support swappable runtimes:
javascript.yml: shared build tooling (tsdown, tsc, biome) inherited by all JS/TS projectsnode.yml: runtime tasks (dev,start,start-prod) vianode/tsx, taggednode_runtimebun.yml: same runtime tasks viabun, taggedbun_runtime
Source files live in src/, build output goes to dist/.
| Task | Command | Source | Purpose |
|---|---|---|---|
typecheck |
tsc --build |
javascript.yml |
Type checking with project references |
build |
tsdown --sourcemap |
javascript.yml |
Build with sourcemaps and type declarations |
bundle |
tsdown --sourcemap --minify --no-dts |
javascript.yml |
Minified production bundle, no type declarations |
check |
biome check --write |
javascript.yml |
Lint and format |
dev |
tsx src/index.ts / bun src/index.ts |
node.yml / bun.yml |
Run from source with typechecking |
start |
node dist/index.mjs / bun dist/index.mjs |
node.yml / bun.yml |
Run the build output |
start-prod |
node dist/index.mjs / bun dist/index.mjs |
node.yml / bun.yml |
Run the bundle output |
start and start-prod automatically build workspace dependencies (^:build) before running.
Projects default to Node via the node_runtime tag in templates. To switch to Bun, change the tag in the project's moon.yml:
tags:
- bun_runtime # instead of node_runtimeBuild tooling (tsdown, tsc, biome) is shared, only the runtime commands (dev, start, start-prod) change.
Packages produce importable libraries. Their build output includes type declarations (.d.mts) and externalizes dependencies by default. Packages exclude the bundle task since they are not standalone executables.
Apps produce runnable artifacts. build is the same as packages. bundle creates a single-file minified output with all dependencies inlined (alwaysBundle: ["**"] in package.json).
Apps bundle all dependencies by default via the tsdown config in package.json:
{
"tsdown": {
"deps": {
"alwaysBundle": ["**"]
}
}
}For native packages that cannot be bundled (e.g. better-sqlite3), exclude them per-app:
{
"dependencies": {
"better-sqlite3": "^11.0.0"
},
"tsdown": {
"deps": {
"alwaysBundle": ["**"],
"neverBundle": ["better-sqlite3"]
}
}
}Externalized packages must be declared in the app's dependencies so they are available in node_modules at runtime.
Packages do not need any tsdown config. Dependencies listed in package.json dependencies are externalized by default.
When deploying a bundled app, only externalized (native) packages need node_modules at runtime. To keep images small:
- Externalize native packages via
neverBundle - Create a
production.package.jsonlisting only externalized packages pnpm install --prodfrom that file in your Dockerfile
The tsconfig chain is:
tsconfig-moon/tsconfig.projects.json (base: strict, composite, declarations)
-> tsconfig.options.json (overrides: es2025, nodenext, types: ["node"])
-> <project>/tsconfig.json (project-specific: rootDir, outDir)
@types/node is installed at the root and made available to all projects via types: ["node"] in tsconfig.options.json. Projects that need different types (e.g. React, Next.js) can override types in their own tsconfig.json, the field fully replaces the parent.
syncpack enforces consistent dependency versions across all package.json files.
| Task | Command | Purpose |
|---|---|---|
syncpack-check |
syncpack lint |
Check for version mismatches (CI) |
syncpack-fix |
syncpack fix |
Auto-fix version mismatches |
syncpack-update |
syncpack update --target minor |
Safe minor/patch updates |
syncpack-upgrade |
syncpack update --target latest |
Full upgrade including majors |
Run from root: moon run root:syncpack-check
Configuration (.syncpackrc.json):
- Enforces
^semver ranges across all packages - Uses
workspace:*protocol for local package references - Sources auto-discovered from
pnpm-workspace.yaml
| Template | Command | Creates |
|---|---|---|
node-app |
moon generate node-app |
Application with bundling, start tasks, and tsdown config |
node-package |
moon generate node-package |
Library with type declarations and package exports |
| Template | Command | Creates |
|---|---|---|
go-app |
moon generate go-app |
Go application with module config |
go-package |
moon generate go-package |
Go library with module config |
After generating a Go project, add it to the workspace:
go work use apps/yourgoappname
# or
go work use packages/yourgopackagename| Template | Command | Creates |
|---|---|---|
rust-app |
moon generate rust-app |
Binary crate with main.rs |
rust-package |
moon generate rust-package |
Library crate with lib.rs and tests |
After generating a Rust project, add it to the members list in the root Cargo.toml:
[workspace]
members = [
"apps/my_app",
"packages/my_lib",
]Cargo workspace globs are not used because not all directories under apps/ or packages/ contain a Cargo.toml — members must be registered manually.
Use underscores for crate names (my_app, not my-app).
To use a local package from an app, add it to apps/my_app/Cargo.toml:
[dependencies]
my_lib = { path = "../../packages/my_lib" }| Task | Command | Purpose |
|---|---|---|
dev |
cargo run |
Run from source (debug) |
build |
cargo build |
Debug build |
start |
cargo run --release |
Optimized release run |
typecheck |
cargo check |
Fast type/borrow checking |
lint |
cargo clippy |
Linting |
format |
cargo fmt --check |
Format checking |
check |
clippy + fmt | Combined lint and format |
test |
cargo test |
Run tests |
No bundle task. cargo build --release already produces a single statically linked binary. Packages exclude build, dev, and start.
- Author Information: Update
author.defaultin alltemplates/*/template.ymlfiles - License: Update
license.defaultin template files if needed - LICENSE File: Replace root and template LICENSE files with your own