run.sh for the KCD2 Dice Solver
+How one script turns "clone and pray" into "clone and run" for a Vite/React app.
+ + + +Background
+
+ kcd2_dice_solver is a small Vite + React + TypeScript app: you tell it
+ which dice and badges you own in Kingdom Come: Deliverance II, and it
+ searches every loadout your inventory allows to recommend the best one. It's a
+ normal Node project — a package.json with scripts for dev,
+ build, test, and lint, run through pnpm.
+
+ Until now, running it meant reading the README and typing two commands by hand: +
+pnpm install
+pnpm dev
+
+ That's fine if you already have the right Node version and pnpm installed and you
+ remember the exact commands. It's not fine as a "just run it" entry point — which
+ is what this change adds: a run.sh that does the checking and installing
+ for you, then starts the dev server.
+
Intuition
+
+ The interesting part of run.sh isn't the two commands above — it's
+ everything that has to happen before them so those two commands are safe to
+ run unconditionally. The script has to answer two questions about the machine it's
+ running on:
+
nvm, install/switch to the LTS releasepnpm on the PATH?
+ Only once both are true does it do the boring part: pnpm install, then
+ pnpm dev. The project's own package.json already states the
+ requirement ("engines": {"node": ">=22", "pnpm": ">=10"}) — the
+ script's job is to make that requirement true rather than just documented.
+
run.sh would print and do.
+ Code
+ +Guarding Node's version
+
+ ensure_node only reaches for nvm when it has to. If
+ node resolves and its major version already clears the bar, it returns
+ immediately — no network call, no reinstall, no surprise version switch on a
+ machine that's already fine.
+
ensure_node() {
+ if command -v node >/dev/null 2>&1; then
+ local major
+ major="$(node --version | sed -E 's/^v([0-9]+).*/\1/')"
+ if (( major >= NODE_MIN_MAJOR )); then
+ return
+ fi
+ echo "Node $(node --version) is older than required v${NODE_MIN_MAJOR}; installing a newer version via nvm..."
+ else
+ echo "Node not found; installing via nvm..."
+ fi
+
+ if [[ ! -s "${NVM_DIR}/nvm.sh" ]]; then
+ echo "Error: nvm not found at ${NVM_DIR}/nvm.sh; install nvm first." >&2
+ exit 1
+ fi
+ source "${NVM_DIR}/nvm.sh"
+ nvm install --lts
+ nvm use --lts
+}
+
+ Two things worth noticing: it fails with a clear error rather than trying to
+ bootstrap nvm itself (installing a version manager is a bigger, riskier
+ step than the script should take silently), and it treats "too old" and "missing"
+ as the same recovery path — both end up at nvm install --lts.
+
Guarding pnpm
+
+ ensure_pnpm is the same shape, one level simpler: pnpm ships inside Node
+ via Corepack, so there's no version manager to bootstrap — just corepack
+ enable followed by pulling the latest pnpm release.
+
ensure_pnpm() {
+ if command -v pnpm >/dev/null 2>&1; then
+ return
+ fi
+ echo "pnpm not found; enabling via corepack..."
+ corepack enable
+ corepack prepare pnpm@latest --activate
+}
+
+ Loading nvm before checking, not just before installing
+
+ One subtlety lives in main, not in ensure_node: nvm.sh
+ is sourced before ensure_node is even called.
+
main() {
+ if [[ -s "${NVM_DIR}/nvm.sh" ]]; then
+ source "${NVM_DIR}/nvm.sh"
+ fi
+
+ ensure_node
+ ensure_pnpm
+
+ cd "$SCRIPT_DIR"
+ echo "Installing dependencies..."
+ pnpm install
+
+ echo "Starting dev server (http://localhost:5174)..."
+ exec pnpm dev
+}
+
+ A fresh, non-interactive shell (like the one a script runs in) doesn't source
+ ~/.bashrc, so nvm's shims for switching Node versions
+ wouldn't otherwise be in scope at all. Sourcing it up front means that if a later
+ run needs nvm use --lts inside ensure_node, the function
+ to call already exists.
+
+ The last line, exec pnpm dev, replaces the shell process with pnpm
+ instead of running it as a child. That means Ctrl-C in the terminal goes straight
+ to Vite's dev server rather than being caught by an intermediate run.sh
+ process — one less layer between you and the thing you're trying to stop.
+
| Function | Checks | Recovery |
|---|---|---|
ensure_node | node present and ≥ v22 | nvm install --lts + nvm use --lts |
ensure_pnpm | pnpm present | corepack enable + corepack prepare pnpm@latest --activate |