feat: add monorepo to template

This commit is contained in:
eric
2026-03-16 17:42:50 +01:00
parent 71c7fe09cd
commit ece1dffb39
43 changed files with 1636 additions and 263 deletions

18
template/.env.schema Normal file
View File

@@ -0,0 +1,18 @@
# @currentEnv=$REPO_ENVIRONMENT
# ---
# Canonical repo environment used by Varlock.
# @type=enum(development,ci,production)
REPO_ENVIRONMENT=development
# Safe starter values for local development.
# @type=string
APP_NAME=typescript-monorepo
# @type=port
APP_PORT=3000
# Optional example secret resolved from OpenBao.
# Replace the namespace and secret path with values for your repo before use.
# @optional @sensitive
EXAMPLE_API_TOKEN=exec('bao kv get -mount=kv -namespace="${BAO_NAMESPACE:+$BAO_NAMESPACE/}template" -field=EXAMPLE_API_TOKEN "$REPO_ENVIRONMENT/shared" 2>/dev/null || true')

3
template/.gitignore vendored
View File

@@ -1,9 +1,12 @@
.direnv/
.moon/cache/
.pre-commit-config.yaml
lefthook.yml
.tools/
bazel-*
build/
dist/
node_modules/
.env.sh

View File

@@ -0,0 +1,11 @@
$schema: "./cache/schemas/workspace.json"
projects:
globs:
- "apps/*"
- "packages/*"
sources:
root: "."
vcs:
defaultBranch: "main"

32
template/README.md Normal file
View File

@@ -0,0 +1,32 @@
# TypeScript Monorepo Template
This template gives you a Bun-only monorepo with:
- Moonrepo at the workspace root
- strict shared TypeScript configs
- Varlock with a committed `.env.schema`
- a Nix flake shell built through `repo-lib`
## First Run
1. Enter the shell with `direnv allow` or `nix develop`.
2. Install workspace dependencies with `bun install`.
3. Review and customize `.env.schema` for your repo.
4. Run `bun run env:check`.
5. Run `moon run :typecheck`.
## Layout
- `apps/` for applications
- `packages/` for shared libraries
- `tsconfig/` for shared TypeScript profiles
- `moon.yml` for root Moonrepo tasks
## Varlock
`bunfig.toml` preloads `varlock/auto-load`, and the root scripts expose:
- `bun run env:check`
- `bun run env:scan`
If you use OpenBao locally, set `OPENBAO_ADDR`, `OPENBAO_NAMESPACE`, and `OPENBAO_CACERT` in your shell or an ignored `.env.sh` file before running those commands.

1
template/apps/.gitkeep Normal file
View File

@@ -0,0 +1 @@

2
template/bunfig.toml Normal file
View File

@@ -0,0 +1,2 @@
env = false
preload = ["varlock/auto-load"]

159
template/flake.lock generated
View File

@@ -1,159 +0,0 @@
{
"nodes": {
"devshell-lib": {
"inputs": {
"git-hooks": "git-hooks",
"nixpkgs": [
"nixpkgs"
],
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1772603902,
"narHash": "sha256-GN5EC9m0flWDuc6qaB6QoIBD73yFnhl2PBIYXzSTGeQ=",
"ref": "v0.0.2",
"rev": "db4ed150e01e2f9245e668077245447d0089163f",
"revCount": 15,
"type": "git",
"url": "https://git.dgren.dev/eric/nix-flake-lib"
},
"original": {
"ref": "v0.0.2",
"type": "git",
"url": "https://git.dgren.dev/eric/nix-flake-lib"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1772024342,
"narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devshell-lib",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1770073757,
"narHash": "sha256-Vy+G+F+3E/Tl+GMNgiHl9Pah2DgShmIUBJXmbiQPHbI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "47472570b1e607482890801aeaf29bfb749884f6",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1770107345,
"narHash": "sha256-tbS0Ebx2PiA1FRW8mt8oejR0qMXmziJmPaU1d4kYY9g=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "4533d9293756b63904b7238acb84ac8fe4c8c2c4",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1772542754,
"narHash": "sha256-WGV2hy+VIeQsYXpsLjdr4GvHv5eECMISX1zKLTedhdg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "8c809a146a140c5c8806f13399592dbcb1bb5dc4",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devshell-lib": "devshell-lib",
"nixpkgs": "nixpkgs_3"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1770228511,
"narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "337a4fe074be1042a35086f15481d763b8ddc0e7",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,6 +1,5 @@
# flake.nix — product repo template
{
description = "my-product";
description = "typescript-monorepo";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
@@ -20,99 +19,61 @@
src = ./.;
config = {
# includeStandardPackages = false;
shell = {
env = {
# FOO = "bar";
banner = {
style = "pretty";
icon = "";
title = "Moonrepo shell ready";
titleColor = "GREEN";
subtitle = "Bun + TypeScript + Varlock";
subtitleColor = "GRAY";
borderColor = "BLUE";
};
extraShellText = ''
# any repo-specific shell setup here
export PATH="$PWD/node_modules/.bin:$PATH"
'';
# Impure bootstrap is available as an explicit escape hatch.
# bootstrap = ''
# export GOBIN="$PWD/.tools/bin"
# export PATH="$GOBIN:$PATH"
# '';
# allowImpureBootstrap = true;
bootstrap = ''
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
export BUN_INSTALL_GLOBAL_DIR="$repo_root/.tools/bun/install/global"
export BUN_INSTALL_BIN="$repo_root/.tools/bun/bin"
export PATH="$BUN_INSTALL_BIN:$PATH"
mkdir -p "$BUN_INSTALL_GLOBAL_DIR" "$BUN_INSTALL_BIN"
if [ ! -x "$BUN_INSTALL_BIN/moon" ]; then
bun add -g @moonrepo/cli
fi
'';
allowImpureBootstrap = true;
};
formatting = {
# nixfmt is enabled by default and wired into lefthook.
programs = {
# shfmt.enable = true;
# gofmt.enable = true;
oxfmt.enable = true;
};
settings = {
# shfmt.options = [ "-i" "2" "-s" "-w" ];
oxfmt.excludes = [
"*.css"
"*.graphql"
"*.hbs"
"*.html"
"*.md"
"*.mdx"
"*.mustache"
"*.scss"
"*.vue"
"*.yaml"
"*.yml"
];
};
};
# These checks become lefthook commands in the generated `lefthook.yml`.
# repo-lib runs `pre-commit` and `pre-push` hook commands in parallel.
# It also sets `output = [ "failure" "summary" ]` by default.
checks = {
tests = {
command = "echo 'No tests defined yet.'";
stage = "pre-push";
passFilenames = false;
};
# fmt = {
# command = "nix fmt";
# stage = "pre-commit";
# passFilenames = false;
# };
};
# For advanced Lefthook fields like `stage_fixed`, use raw passthrough.
# repo-lib merges this after generated checks.
# lefthook.pre-push.commands.tests.stage_fixed = true;
# lefthook.commit-msg.commands.commitlint = {
# run = "pnpm commitlint --edit {1}";
# stage_fixed = true;
# };
# repo-lib also installs built-in hooks for:
# - treefmt / nixfmt on `pre-commit`
# - gitleaks on `pre-commit`
# - gitlint on `commit-msg`
# release = null;
release = {
steps = [
# Write a generated version file during release.
# {
# writeFile = {
# path = "src/version.ts";
# text = ''
# export const APP_VERSION = "$FULL_VERSION" as const;
# '';
# };
# }
# Replace a version string while preserving surrounding captures.
# {
# replace = {
# path = "README.md";
# regex = ''^(version = ")[^"]*(")$'';
# replacement = ''\1$FULL_VERSION\2'';
# };
# }
# Run any extra release step with declared runtime inputs.
# {
# run = {
# runtimeInputs = [ pkgs.git ];
# script = ''
# git status --short
# '';
# };
# }
];
steps = [ ];
};
};
@@ -137,37 +98,58 @@
};
})
# (repo-lib.lib.tools.fromPackage {
# name = "Go";
# package = pkgs.go;
# version.args = [ "version" ];
# banner.color = "CYAN";
# })
(repo-lib.lib.tools.fromPackage {
name = "Bun";
package = pkgs.bun;
version.args = [ "--version" ];
banner = {
color = "YELLOW";
icon = "";
};
})
];
shell.packages = [
self.packages.${system}.release
# pkgs.go
# pkgs.bun
pkgs.bun
pkgs.openbao
pkgs.oxfmt
pkgs.oxlint
];
# checks.lint = {
# command = "bun test";
# stage = "pre-push";
# passFilenames = false;
# runtimeInputs = [ pkgs.bun ];
# };
checks.format = {
command = "oxfmt --check .";
stage = "pre-commit";
passFilenames = false;
runtimeInputs = [ pkgs.oxfmt ];
};
# checks.generated = {
# command = "git diff --exit-code";
# stage = "pre-commit";
# passFilenames = false;
# };
checks.typecheck = {
command = "bun run typecheck";
stage = "pre-push";
passFilenames = false;
runtimeInputs = [ pkgs.bun ];
};
# packages.my-tool = pkgs.writeShellApplication {
# name = "my-tool";
# text = ''echo hello'';
# };
checks.env-check = {
command = "bun run env:check";
stage = "pre-push";
passFilenames = false;
runtimeInputs = [
pkgs.bun
pkgs.openbao
];
};
checks.env-scan = {
command = "bun run env:scan";
stage = "pre-commit";
passFilenames = false;
runtimeInputs = [
pkgs.bun
pkgs.openbao
];
};
};
};
}

45
template/moon.yml Normal file
View File

@@ -0,0 +1,45 @@
$schema: "./.moon/cache/schemas/project.json"
tasks:
typecheck:
command: "bun"
args:
- "run"
- "typecheck"
inputs:
- "package.json"
- "tsconfig.json"
- "tsconfig.options.json"
- "tsconfig/**/*"
options:
cache: false
runFromWorkspaceRoot: true
env-check:
command: "bun"
args:
- "run"
- "env:check"
inputs:
- ".env.schema"
- "package.json"
- "bunfig.toml"
toolchains: "system"
options:
cache: false
runFromWorkspaceRoot: true
env-scan:
command: "bun"
args:
- "run"
- "env:scan"
inputs:
- ".env.schema"
- "package.json"
- "bunfig.toml"
toolchains: "system"
options:
cache: false
runFromWorkspaceRoot: true
runInCI: "skip"

23
template/package.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "typescript-monorepo",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"type": "module",
"scripts": {
"typecheck": "bunx tsc -p tsconfig.json --pretty false",
"env:check": "sh -ec 'export BAO_ADDR=\"${BAO_ADDR:-${OPENBAO_ADDR:-}}\"; export BAO_NAMESPACE=\"${BAO_NAMESPACE:-${OPENBAO_NAMESPACE:-}}\"; export BAO_CACERT=\"${BAO_CACERT:-${OPENBAO_CACERT:-}}\"; exec varlock load --show-all'",
"env:scan": "sh -ec 'export BAO_ADDR=\"${BAO_ADDR:-${OPENBAO_ADDR:-}}\"; export BAO_NAMESPACE=\"${BAO_NAMESPACE:-${OPENBAO_NAMESPACE:-}}\"; export BAO_CACERT=\"${BAO_CACERT:-${OPENBAO_CACERT:-}}\"; exec varlock scan --staged'",
"check": "bun run typecheck && bun run env:check"
},
"dependencies": {
"@moonrepo/cli": "^2.0.4"
},
"devDependencies": {
"@types/bun": "latest",
"typescript": "^6.0.0-beta",
"varlock": "0.5.0"
}
}

View File

@@ -0,0 +1 @@

4
template/tsconfig.json Normal file
View File

@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.options.json",
"files": []
}

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": ["ESNext"],
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"noEmit": true
}
}

View File

@@ -0,0 +1,6 @@
{
"extends": "./runtime.json",
"compilerOptions": {
"types": ["vite/client"]
}
}

View File

@@ -0,0 +1,6 @@
{
"extends": "./runtime.json",
"compilerOptions": {
"types": ["@types/bun"]
}
}

View File

@@ -0,0 +1,6 @@
{
"compilerOptions": {
"types": []
},
"extends": "../tsconfig.options.json"
}

View File

@@ -0,0 +1,6 @@
{
"extends": "../tsconfig.options.json",
"compilerOptions": {
"lib": ["ESNext", "DOM", "DOM.Iterable"]
}
}