From a28a6a854340aef46ad5e9297cca0cf6b34b2c8a Mon Sep 17 00:00:00 2001 From: Karol Broda Date: Mon, 22 Dec 2025 14:54:34 +0100 Subject: [PATCH 1/4] feat: add nix-flake addon support and related configurations --- .envrc | 1 + apps/cli/src/constants.ts | 1 + apps/cli/src/helpers/core/template-manager.ts | 2 + apps/cli/src/prompts/addons.ts | 6 ++- .../cli/templates/addons/nix-flake/_envrc.hbs | 2 + .../templates/addons/nix-flake/flake.nix.hbs | 48 +++++++++++++++++++ apps/cli/test/addons.test.ts | 3 +- flake.lock | 27 +++++++++++ flake.nix | 37 ++++++++++++++ packages/types/src/schemas.ts | 1 + 10 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 .envrc create mode 100644 apps/cli/templates/addons/nix-flake/_envrc.hbs create mode 100644 apps/cli/templates/addons/nix-flake/flake.nix.hbs create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..cffc922b0 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake . --impure diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts index 0fd10de98..06c16bbac 100644 --- a/apps/cli/src/constants.ts +++ b/apps/cli/src/constants.ts @@ -189,5 +189,6 @@ export const ADDON_COMPATIBILITY = { fumadocs: [], opentui: [], wxt: [], + "nix-flake": [], none: [], } as const; diff --git a/apps/cli/src/helpers/core/template-manager.ts b/apps/cli/src/helpers/core/template-manager.ts index 82de793b6..6059df7c8 100644 --- a/apps/cli/src/helpers/core/template-manager.ts +++ b/apps/cli/src/helpers/core/template-manager.ts @@ -35,6 +35,8 @@ export async function processAndCopyFiles( relativeDestPath = path.join(path.dirname(relativeDestPath), ".gitignore"); } else if (basename === "_npmrc") { relativeDestPath = path.join(path.dirname(relativeDestPath), ".npmrc"); + } else if (basename === "_envrc") { + relativeDestPath = path.join(path.dirname(relativeDestPath), ".envrc"); } const destPath = path.join(destDir, relativeDestPath); diff --git a/apps/cli/src/prompts/addons.ts b/apps/cli/src/prompts/addons.ts index 747892f53..5c94b7192 100644 --- a/apps/cli/src/prompts/addons.ts +++ b/apps/cli/src/prompts/addons.ts @@ -63,6 +63,10 @@ function getAddonDisplay(addon: Addons): { label: string; hint: string } { label = "WXT"; hint = "Build browser extensions"; break; + case "nix-flake": + label = "Nix Flake"; + hint = "Reproducible dev environment with Nix"; + break; default: label = addon; hint = `Add ${addon}`; @@ -74,7 +78,7 @@ function getAddonDisplay(addon: Addons): { label: string; hint: string } { const ADDON_GROUPS = { Documentation: ["starlight", "fumadocs"], Linting: ["biome", "oxlint", "ultracite"], - Other: ["ruler", "pwa", "tauri", "husky", "opentui", "wxt", "turborepo"], + Other: ["ruler", "pwa", "tauri", "husky", "opentui", "wxt", "turborepo", "nix-flake"], }; export async function getAddonsChoice(addons?: Addons[], frontends?: Frontend[], auth?: Auth) { diff --git a/apps/cli/templates/addons/nix-flake/_envrc.hbs b/apps/cli/templates/addons/nix-flake/_envrc.hbs new file mode 100644 index 000000000..e3fecb324 --- /dev/null +++ b/apps/cli/templates/addons/nix-flake/_envrc.hbs @@ -0,0 +1,2 @@ +use flake + diff --git a/apps/cli/templates/addons/nix-flake/flake.nix.hbs b/apps/cli/templates/addons/nix-flake/flake.nix.hbs new file mode 100644 index 000000000..12037e5fd --- /dev/null +++ b/apps/cli/templates/addons/nix-flake/flake.nix.hbs @@ -0,0 +1,48 @@ +\{ + description = "\{{projectName}} devshell"; + + inputs = \{ + nixpkgs = \{ + url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + }; + + outputs = \{ self, nixpkgs }: + let + mkShellFor = system: + let + pkgs = nixpkgs.legacyPackages.$\{system}; + in + pkgs.mkShell \{ + packages = [ +{{#if (eq runtime "bun")}} + pkgs.bun +{{else if (eq runtime "node")}} + pkgs.nodejs_22 +{{/if}} +{{#if (eq dbSetup "docker")}} + pkgs.docker-compose +{{/if}} + ]; + + shellHook = '' +{{#if (eq runtime "bun")}} + if [ -n "$PS1" ]; then + echo "bun: $(bun --version)" + fi +{{else if (eq runtime "node")}} + if [ -n "$PS1" ]; then + echo "node: $(node --version)" + fi +{{/if}} + ''; + }; + in + \{ + devShells.x86_64-linux.default = mkShellFor "x86_64-linux"; + devShells.aarch64-linux.default = mkShellFor "aarch64-linux"; + devShells.x86_64-darwin.default = mkShellFor "x86_64-darwin"; + devShells.aarch64-darwin.default = mkShellFor "aarch64-darwin"; + }; +} + diff --git a/apps/cli/test/addons.test.ts b/apps/cli/test/addons.test.ts index 043e845ce..fe05b2385 100644 --- a/apps/cli/test/addons.test.ts +++ b/apps/cli/test/addons.test.ts @@ -4,7 +4,7 @@ import { expectError, expectSuccess, runTRPCTest, type TestConfig } from "./test describe("Addon Configurations", () => { describe("Universal Addons (no frontend restrictions)", () => { - const universalAddons = ["biome", "husky", "turborepo", "oxlint"]; + const universalAddons = ["biome", "husky", "turborepo", "oxlint", "nix-flake"]; for (const addon of universalAddons) { it(`should work with ${addon} addon on any frontend`, async () => { @@ -288,6 +288,7 @@ describe("Addon Configurations", () => { "husky", "turborepo", "oxlint", + "nix-flake", // Note: starlight, ultracite, ruler, fumadocs are prompt-controlled only ]; diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..8e486ee8c --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1766201043, + "narHash": "sha256-eplAP+rorKKd0gNjV3rA6+0WMzb1X1i16F5m5pASnjA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b3aad468604d3e488d627c0b43984eb60e75e782", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..6f6db71bd --- /dev/null +++ b/flake.nix @@ -0,0 +1,37 @@ +{ + description = "create-t-stack devshell flake"; + + inputs = { + nixpkgs = { + url = "github:NixOS/nixpkgs/nixos-25.11"; + }; + }; + + outputs = { self, nixpkgs }: + let + mkShellFor = system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + pkgs.mkShell { + packages = [ + pkgs.bun + pkgs.nodejs_22 + ]; + + shellHook = '' + if [ -n "$PS1" ]; then + echo "bun: $(bun --version)" + fi + ''; + }; + in + { + # explicit nested outputs per system + devShells.x86_64-linux.default = mkShellFor "x86_64-linux"; + devShells.aarch64-linux.default = mkShellFor "aarch64-linux"; + devShells.x86_64-darwin.default = mkShellFor "x86_64-darwin"; + devShells.aarch64-darwin.default = mkShellFor "aarch64-darwin"; + }; +} + diff --git a/packages/types/src/schemas.ts b/packages/types/src/schemas.ts index f262ee6f8..44ac0caf5 100644 --- a/packages/types/src/schemas.ts +++ b/packages/types/src/schemas.ts @@ -44,6 +44,7 @@ export const AddonsSchema = z "oxlint", "opentui", "wxt", + "nix-flake", "none", ]) .describe("Additional addons"); From f2fc3f96a02b8bb93148c12fad7b5f4627afc880 Mon Sep 17 00:00:00 2001 From: Karol Broda Date: Mon, 22 Dec 2025 16:23:19 +0100 Subject: [PATCH 2/4] feat: add nix-flake to web builder constants --- apps/web/src/lib/constant.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/web/src/lib/constant.ts b/apps/web/src/lib/constant.ts index 8e16d493f..45fb9dd7d 100644 --- a/apps/web/src/lib/constant.ts +++ b/apps/web/src/lib/constant.ts @@ -559,6 +559,14 @@ export const TECH_OPTIONS: Record< color: "from-emerald-500 to-emerald-700", default: false, }, + { + id: "nix-flake", + name: "Nix Flake", + description: "Reproducible dev environment with Nix", + icon: "", + color: "from-sky-500 to-sky-700", + default: false, + }, { id: "turborepo", name: "Turborepo", From 1c73aa304d13e058851299259f518a4e35db6885 Mon Sep 17 00:00:00 2001 From: Karol Broda Date: Mon, 22 Dec 2025 16:38:55 +0100 Subject: [PATCH 3/4] docs: add nix-flake to addon documentation --- apps/web/content/docs/cli/options.mdx | 1 + apps/web/content/docs/index.mdx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/content/docs/cli/options.mdx b/apps/web/content/docs/cli/options.mdx index 1413a8cb4..68799040e 100644 --- a/apps/web/content/docs/cli/options.mdx +++ b/apps/web/content/docs/cli/options.mdx @@ -257,6 +257,7 @@ Additional features to include: - `ultracite`: Ultracite configuration - `oxlint`: Oxlint + Oxfmt (linting & formatting) - `ruler`: Centralize your AI rules with Ruler +- `nix-flake`: Reproducible dev environment with Nix ```bash create-better-t-stack --addons pwa biome husky diff --git a/apps/web/content/docs/index.mdx b/apps/web/content/docs/index.mdx index 669320275..cbc2703e9 100644 --- a/apps/web/content/docs/index.mdx +++ b/apps/web/content/docs/index.mdx @@ -310,7 +310,7 @@ See the full list in the [CLI Reference](/docs/cli). Key flags: - `--orm`: drizzle, prisma, mongoose, none - `--api`: trpc, orpc, none - `--auth`: better-auth, clerk, none -- `--addons`: turborepo, pwa, tauri, biome, husky, starlight, fumadocs, ultracite, oxlint, ruler, none +- `--addons`: turborepo, pwa, tauri, biome, husky, starlight, fumadocs, ultracite, oxlint, ruler, nix-flake, none - `--examples`: todo, ai, none ## Next Steps From 9e6642dac9e8ee2644b5966915a2a3fc335bea13 Mon Sep 17 00:00:00 2001 From: Karol Broda Date: Mon, 22 Dec 2025 19:36:44 +0100 Subject: [PATCH 4/4] feat: enhance nix-flake template with package manager and database support --- .../templates/addons/nix-flake/flake.nix.hbs | 61 ++++++++++++++----- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/apps/cli/templates/addons/nix-flake/flake.nix.hbs b/apps/cli/templates/addons/nix-flake/flake.nix.hbs index 12037e5fd..43f639bb8 100644 --- a/apps/cli/templates/addons/nix-flake/flake.nix.hbs +++ b/apps/cli/templates/addons/nix-flake/flake.nix.hbs @@ -1,48 +1,77 @@ -\{ - description = "\{{projectName}} devshell"; +{ + description = "{{projectName}} devshell"; - inputs = \{ - nixpkgs = \{ + inputs = { + nixpkgs = { url = "github:NixOS/nixpkgs/nixos-unstable"; }; }; - outputs = \{ self, nixpkgs }: + outputs = { self, nixpkgs }: let mkShellFor = system: let - pkgs = nixpkgs.legacyPackages.$\{system}; + pkgs = nixpkgs.legacyPackages.${system}; in - pkgs.mkShell \{ + pkgs.mkShell { packages = [ -{{#if (eq runtime "bun")}} + # package manager +{{#if (eq packageManager "bun")}} pkgs.bun -{{else if (eq runtime "node")}} +{{else}} pkgs.nodejs_22 {{/if}} +{{#if (eq packageManager "pnpm")}} + pkgs.pnpm +{{/if}} + + # backend runtime (if different from package manager) +{{#if (and (eq runtime "bun") (ne packageManager "bun"))}} + pkgs.bun +{{/if}} +{{#if (and (eq runtime "node") (eq packageManager "bun"))}} + pkgs.nodejs_22 +{{/if}} +{{#if (eq runtime "workers")}} + pkgs.wrangler +{{/if}} + + # database tooling {{#if (eq dbSetup "docker")}} pkgs.docker-compose +{{/if}} +{{#if (and (eq database "postgres") (eq dbSetup "docker"))}} + pkgs.postgresql +{{/if}} +{{#if (and (eq database "mysql") (eq dbSetup "docker"))}} + pkgs.mariadb +{{/if}} +{{#if (and (eq database "mongodb") (eq dbSetup "docker"))}} + pkgs.mongosh +{{/if}} +{{#if (eq dbSetup "turso")}} + pkgs.turso-cli +{{/if}} +{{#if (eq dbSetup "supabase")}} + pkgs.supabase-cli {{/if}} ]; shellHook = '' -{{#if (eq runtime "bun")}} if [ -n "$PS1" ]; then +{{#if (eq packageManager "bun")}} echo "bun: $(bun --version)" - fi -{{else if (eq runtime "node")}} - if [ -n "$PS1" ]; then +{{else}} echo "node: $(node --version)" - fi {{/if}} + fi ''; }; in - \{ + { devShells.x86_64-linux.default = mkShellFor "x86_64-linux"; devShells.aarch64-linux.default = mkShellFor "aarch64-linux"; devShells.x86_64-darwin.default = mkShellFor "x86_64-darwin"; devShells.aarch64-darwin.default = mkShellFor "aarch64-darwin"; }; } -