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 95ba7a7d9..553e95dc6 100644 --- a/apps/cli/src/constants.ts +++ b/apps/cli/src/constants.ts @@ -195,5 +195,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 d6543b4d8..9c61994b7 100644 --- a/apps/cli/src/helpers/core/template-manager.ts +++ b/apps/cli/src/helpers/core/template-manager.ts @@ -36,6 +36,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 fb92e165c..ffa7304d4 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..43f639bb8 --- /dev/null +++ b/apps/cli/templates/addons/nix-flake/flake.nix.hbs @@ -0,0 +1,77 @@ +{ + 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 = [ + # package manager +{{#if (eq packageManager "bun")}} + pkgs.bun +{{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 [ -n "$PS1" ]; then +{{#if (eq packageManager "bun")}} + echo "bun: $(bun --version)" +{{else}} + echo "node: $(node --version)" +{{/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"; + }; +} diff --git a/apps/cli/test/addons.test.ts b/apps/cli/test/addons.test.ts index 862763603..83a759e88 100644 --- a/apps/cli/test/addons.test.ts +++ b/apps/cli/test/addons.test.ts @@ -6,7 +6,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 () => { @@ -290,6 +290,7 @@ describe("Addon Configurations", () => { "husky", "turborepo", "oxlint", + "nix-flake", // Note: starlight, ultracite, ruler, fumadocs are prompt-controlled only ]; diff --git a/apps/web/content/docs/cli/options.mdx b/apps/web/content/docs/cli/options.mdx index 824087d38..a8037896f 100644 --- a/apps/web/content/docs/cli/options.mdx +++ b/apps/web/content/docs/cli/options.mdx @@ -296,6 +296,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 - `opentui`: OpenTUI components - `wxt`: WXT browser extension framework diff --git a/apps/web/content/docs/index.mdx b/apps/web/content/docs/index.mdx index f6377d381..99e8b8e62 100644 --- a/apps/web/content/docs/index.mdx +++ b/apps/web/content/docs/index.mdx @@ -311,7 +311,7 @@ See the full list in the [CLI Reference](/docs/cli). Key flags: - `--api`: trpc, orpc, none - `--auth`: better-auth, clerk, none - `--payments`: polar, none -- `--addons`: turborepo, pwa, tauri, biome, husky, starlight, fumadocs, ultracite, oxlint, ruler, opentui, wxt, none +- `--addons`: turborepo, pwa, tauri, biome, husky, starlight, fumadocs, ultracite, oxlint, ruler, opentui, wxt, nix-flake, none - `--examples`: todo, ai, none ## Next Steps diff --git a/apps/web/src/lib/constant.ts b/apps/web/src/lib/constant.ts index 776c7ac2e..0c7f1980a 100644 --- a/apps/web/src/lib/constant.ts +++ b/apps/web/src/lib/constant.ts @@ -557,6 +557,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", 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 c6462ec48..f707e1cc6 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");