create-esa-stack 0.1.13 โ†’ 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.0] - 2026-02-07
9
+
10
+ ### Added
11
+ - **Backend Selection**: Introduced a primary choice between different backend technologies.
12
+ - **Jazz.tools**: Added support for Jazz.tools (local-first sync and auth).
13
+ - **Bundled Email Tools**: Combined Resend and React Email into a single "Email" option for a smoother setup.
14
+
15
+ ### Changed
16
+ - **Supabase**: Now part of the Backend Selection prompt instead of being a simple optional package.
17
+
18
+
8
19
  ## [0.1.13] - 2026-02-06
9
20
 
10
21
  ### Added
package/README.md CHANGED
@@ -23,9 +23,10 @@ During setup, you can interactively choose to add:
23
23
  - ๐ŸŽจ **shadcn/ui** - Beautiful, accessible, copy-paste components.
24
24
  - ๐Ÿ“ก **TanStack Query** - Powerful asynchronous state management.
25
25
  - ๐Ÿšฆ **@next/third-parties** - Optimized loading for third-party scripts.
26
- - ๐Ÿ“ง **Resend** - The best API for sending emails.
27
- - โœ‰๏ธ **React Email** - Build emails using React components.
28
- - โšก **Supabase** - Open Source Firebase alternative (Auth, DB, Realtime).
26
+ - ๐Ÿ“ง **Email (Resend + React Email)** - Transactional emails with React components.
27
+ - ๐Ÿ“ฆ **Backend Choice** - Choose your primary backend infrastructure:
28
+ - โšก **Supabase** - Auth, DB, Realtime.
29
+ - ๐ŸŽท **Jazz.tools** - Local-first sync and auth.
29
30
  - ๐Ÿงฉ **Zod** - TypeScript-first schema validation with static type inference.
30
31
  - ๐Ÿงช **Testing Tools** - Vitest & React Testing Library (Unit/Integration testing).
31
32
  - ๐Ÿ“š **Storybook** - Frontend workshop for UI development (Includes testing setup).
@@ -68,9 +69,8 @@ The CLI will guide you through the setup:
68
69
  - *Install shadcn/ui?* (Default: Yes)
69
70
  - *Install TanStack Query?* (Default: Yes)
70
71
  - *Install @next/third-parties?* (Default: Yes)
71
- - *Install Resend?* (Default: No)
72
- - *Install React Email?* (Default: No)
73
- - *Install Supabase?* (Default: Yes)
72
+ - *Install Email tools? (Resend + React Email)* (Default: No)
73
+ - *Choose Backend?* (Supabase, Jazz, or None)
74
74
  - *Install Zod?* (Default: Yes)
75
75
  - *Install Storybook?* (Default: No)
76
76
  - *If Yes*: Sets up Storybook environment.
@@ -91,9 +91,10 @@ pnpm create esa-stack <project-name> [options]
91
91
  | `--shadcn` / `--no-shadcn` | Install shadcn/ui | Yes |
92
92
  | `--tanstack-query` / `--no-tanstack-query` | Install TanStack Query | Yes |
93
93
  | `--next-third-parties` / `--no-next-third-parties` | Install @next/third-parties | Yes |
94
- | `--resend` / `--no-resend` | Install Resend | No |
95
- | `--react-email` / `--no-react-email` | Install React Email | No |
96
- | `--supabase` / `--no-supabase` | Install Supabase | Yes |
94
+ | `--email` / `--no-email` | Install Resend + React Email | No |
95
+ | `--supabase` | Install Supabase (Backend) | * |
96
+ | `--jazz` | Install Jazz.tools (Backend) | - |
97
+ | `--no-backend` | Do not install any backend | - |
97
98
  | `--zod` / `--no-zod` | Install Zod | Yes |
98
99
  | `--storybook` / `--no-storybook` | Install Storybook | No |
99
100
  | `--testing` / `--no-testing` | Install Vitest + RTL | Yes |
package/dist/cli.js CHANGED
@@ -2568,6 +2568,32 @@ class BD extends x {
2568
2568
  });
2569
2569
  }
2570
2570
  }
2571
+ var wD = Object.defineProperty;
2572
+ var yD = (e, u, F) => (u in e) ? wD(e, u, { enumerable: true, configurable: true, writable: true, value: F }) : e[u] = F;
2573
+ var Z = (e, u, F) => (yD(e, typeof u != "symbol" ? u + "" : u, F), F);
2574
+ var $D = class extends x {
2575
+ constructor(u) {
2576
+ super(u, false), Z(this, "options"), Z(this, "cursor", 0), this.options = u.options, this.cursor = this.options.findIndex(({ value: F }) => F === u.initialValue), this.cursor === -1 && (this.cursor = 0), this.changeValue(), this.on("cursor", (F) => {
2577
+ switch (F) {
2578
+ case "left":
2579
+ case "up":
2580
+ this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
2581
+ break;
2582
+ case "down":
2583
+ case "right":
2584
+ this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
2585
+ break;
2586
+ }
2587
+ this.changeValue();
2588
+ });
2589
+ }
2590
+ get _value() {
2591
+ return this.options[this.cursor];
2592
+ }
2593
+ changeValue() {
2594
+ this.value = this._value.value;
2595
+ }
2596
+ };
2571
2597
  var TD = Object.defineProperty;
2572
2598
  var jD = (e, u, F) => (u in e) ? TD(e, u, { enumerable: true, configurable: true, writable: true, value: F }) : e[u] = F;
2573
2599
  var MD = (e, u, F) => (jD(e, typeof u != "symbol" ? u + "" : u, F), F);
@@ -2655,6 +2681,16 @@ var y2 = (s) => {
2655
2681
  return import_picocolors2.default.green(M2);
2656
2682
  }
2657
2683
  };
2684
+ var E = (s) => {
2685
+ const { cursor: n, options: t, style: i } = s, r2 = s.maxItems ?? 1 / 0, o = Math.max(process.stdout.rows - 4, 0), c2 = Math.min(o, Math.max(r2, 5));
2686
+ let l2 = 0;
2687
+ n >= l2 + c2 - 3 ? l2 = Math.max(Math.min(n - c2 + 3, t.length - c2), 0) : n < l2 + 2 && (l2 = Math.max(n - 2, 0));
2688
+ const d2 = c2 < t.length && l2 > 0, p = c2 < t.length && l2 + c2 < t.length;
2689
+ return t.slice(l2, l2 + c2).map((S2, f2, x2) => {
2690
+ const g2 = f2 === 0 && d2, m2 = f2 === x2.length - 1 && p;
2691
+ return g2 || m2 ? import_picocolors2.default.dim("...") : i(S2, f2 + l2 === n);
2692
+ });
2693
+ };
2658
2694
  var ae = (s) => new PD({ validate: s.validate, placeholder: s.placeholder, defaultValue: s.defaultValue, initialValue: s.initialValue, render() {
2659
2695
  const n = `${import_picocolors2.default.gray(a2)}
2660
2696
  ${y2(this.state)} ${s.message}
@@ -2695,6 +2731,38 @@ ${import_picocolors2.default.cyan($2)}
2695
2731
  }
2696
2732
  } }).prompt();
2697
2733
  };
2734
+ var le = (s) => {
2735
+ const n = (t, i) => {
2736
+ const r2 = t.label ?? String(t.value);
2737
+ switch (i) {
2738
+ case "selected":
2739
+ return `${import_picocolors2.default.dim(r2)}`;
2740
+ case "active":
2741
+ return `${import_picocolors2.default.green(I2)} ${r2} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}`;
2742
+ case "cancelled":
2743
+ return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r2))}`;
2744
+ default:
2745
+ return `${import_picocolors2.default.dim(T2)} ${import_picocolors2.default.dim(r2)}`;
2746
+ }
2747
+ };
2748
+ return new $D({ options: s.options, initialValue: s.initialValue, render() {
2749
+ const t = `${import_picocolors2.default.gray(a2)}
2750
+ ${y2(this.state)} ${s.message}
2751
+ `;
2752
+ switch (this.state) {
2753
+ case "submit":
2754
+ return `${t}${import_picocolors2.default.gray(a2)} ${n(this.options[this.cursor], "selected")}`;
2755
+ case "cancel":
2756
+ return `${t}${import_picocolors2.default.gray(a2)} ${n(this.options[this.cursor], "cancelled")}
2757
+ ${import_picocolors2.default.gray(a2)}`;
2758
+ default:
2759
+ return `${t}${import_picocolors2.default.cyan(a2)} ${E({ cursor: this.cursor, options: this.options, maxItems: s.maxItems, style: (i, r2) => n(i, r2 ? "active" : "inactive") }).join(`
2760
+ ${import_picocolors2.default.cyan(a2)} `)}
2761
+ ${import_picocolors2.default.cyan($2)}
2762
+ `;
2763
+ }
2764
+ } }).prompt();
2765
+ };
2698
2766
  var he = (s = "") => {
2699
2767
  process.stdout.write(`${import_picocolors2.default.gray($2)} ${import_picocolors2.default.red(s)}
2700
2768
 
@@ -2757,7 +2825,7 @@ var {
2757
2825
 
2758
2826
  // src/cli.ts
2759
2827
  import { execSync } from "node:child_process";
2760
- import { existsSync, readFileSync } from "node:fs";
2828
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2761
2829
  import { join, dirname } from "node:path";
2762
2830
  import { fileURLToPath } from "node:url";
2763
2831
  var __filename2 = fileURLToPath(import.meta.url);
@@ -2775,7 +2843,7 @@ function getVersion() {
2775
2843
  async function main() {
2776
2844
  const program2 = new Command;
2777
2845
  const version = getVersion();
2778
- program2.name("create-esa-stack").description("CLI tool to scaffold Next.js projects with ESA's preferred tech stack").version(version).argument("[project-name]", "Name of the project directory").option("--shadcn", "Install shadcn/ui").option("--no-shadcn", "Do not install shadcn/ui").option("--tanstack-query", "Install TanStack Query").option("--no-tanstack-query", "Do not install TanStack Query").option("--next-third-parties", "Install @next/third-parties").option("--no-next-third-parties", "Do not install @next/third-parties").option("--resend", "Install Resend").option("--no-resend", "Do not install Resend").option("--react-email", "Install React Email").option("--no-react-email", "Do not install React Email").option("--supabase", "Install Supabase").option("--no-supabase", "Do not install Supabase").option("--storybook", "Install Storybook").option("--no-storybook", "Do not install Storybook").option("--testing", "Install Testing tools (Vitest + RTL)").option("--no-testing", "Do not install Testing tools").option("--zod", "Install Zod").option("--no-zod", "Do not install Zod").option("-y, --default", "Skip prompts and use default values").parse(process.argv);
2846
+ program2.name("create-esa-stack").description("CLI tool to scaffold Next.js projects with ESA's preferred tech stack").version(version).argument("[project-name]", "Name of the project directory").option("--shadcn", "Install shadcn/ui").option("--no-shadcn", "Do not install shadcn/ui").option("--tanstack-query", "Install TanStack Query").option("--no-tanstack-query", "Do not install TanStack Query").option("--next-third-parties", "Install @next/third-parties").option("--no-next-third-parties", "Do not install @next/third-parties").option("--email", "Install Resend + React Email").option("--no-email", "Do not install email tools").option("--supabase", "Install Supabase (Backend)").option("--jazz", "Install Jazz.tools (Backend)").option("--no-backend", "Do not install any backend").option("--storybook", "Install Storybook").option("--no-storybook", "Do not install Storybook").option("--testing", "Install Testing tools (Vitest + RTL)").option("--no-testing", "Do not install Testing tools").option("--zod", "Install Zod").option("--no-zod", "Do not install Zod").option("-y, --default", "Skip prompts and use default values").parse(process.argv);
2779
2847
  const options = program2.opts();
2780
2848
  const [cliProjectName] = program2.args;
2781
2849
  const useDefaults = !!options.default;
@@ -2837,9 +2905,32 @@ async function main() {
2837
2905
  const installShadcn = await resolveOption(options.shadcn, true, "Do you want to install shadcn/ui?");
2838
2906
  const installTanstackQuery = await resolveOption(options.tanstackQuery, true, "Do you want to install TanStack Query?");
2839
2907
  const installNextThirdParties = await resolveOption(options.nextThirdParties, true, "Do you want to install @next/third-parties?");
2840
- const installResend = await resolveOption(options.resend, false, "Do you want to install Resend?");
2841
- const installReactEmail = await resolveOption(options.reactEmail, false, "Do you want to install React Email?");
2842
- const installSupabase = await resolveOption(options.supabase, true, "Do you want to install Supabase?");
2908
+ const installEmail = await resolveOption(options.email, false, "Do you want to install Email tools? (Resend + React Email)");
2909
+ let backend = "none";
2910
+ if (options.supabase) {
2911
+ backend = "supabase";
2912
+ } else if (options.jazz) {
2913
+ backend = "jazz";
2914
+ } else if (options.noBackend) {
2915
+ backend = "none";
2916
+ } else if (useDefaults) {
2917
+ backend = "supabase";
2918
+ } else {
2919
+ const result = await le({
2920
+ message: "Which backend tool do you want to use?",
2921
+ options: [
2922
+ { value: "supabase", label: "Supabase (Auth, DB, Realtime)", hint: "recommended" },
2923
+ { value: "jazz", label: "Jazz.tools (Local-first, Sync, Auth)" },
2924
+ { value: "none", label: "None (Pure Next.js)" }
2925
+ ],
2926
+ initialValue: "supabase"
2927
+ });
2928
+ if (typeof result === "symbol") {
2929
+ he("Operation cancelled.");
2930
+ process.exit(0);
2931
+ }
2932
+ backend = result;
2933
+ }
2843
2934
  const installZod = await resolveOption(options.zod, true, "Do you want to install Zod?");
2844
2935
  const installStorybook = await resolveOption(options.storybook, false, "Do you want to install Storybook?");
2845
2936
  let installTesting = false;
@@ -2888,31 +2979,20 @@ Setting up @next/third-parties...`);
2888
2979
  console.log("Installing @next/third-parties...");
2889
2980
  execSync("pnpm add @next/third-parties", { stdio: "inherit", cwd: projectPath });
2890
2981
  }
2891
- if (installResend) {
2892
- console.log(`
2893
- Setting up Resend...`);
2894
- const hasNodeModules = existsSync(join(projectPath, "node_modules"));
2895
- if (!hasNodeModules) {
2896
- console.log("Installing dependencies...");
2897
- execSync("pnpm install", { stdio: "inherit", cwd: projectPath });
2898
- }
2899
- console.log("Installing resend...");
2900
- execSync("pnpm add resend", { stdio: "inherit", cwd: projectPath });
2901
- }
2902
- if (installReactEmail) {
2982
+ if (installEmail) {
2903
2983
  console.log(`
2904
- Setting up React Email...`);
2984
+ Setting up Email (Resend + React Email)...`);
2905
2985
  const hasNodeModules = existsSync(join(projectPath, "node_modules"));
2906
2986
  if (!hasNodeModules) {
2907
2987
  console.log("Installing dependencies...");
2908
2988
  execSync("pnpm install", { stdio: "inherit", cwd: projectPath });
2909
2989
  }
2910
- console.log("Installing react-email and @react-email/preview-server...");
2911
- execSync("pnpm add react-email @react-email/preview-server -D -E", { stdio: "inherit", cwd: projectPath });
2990
+ console.log("Installing resend and react-email...");
2991
+ execSync("pnpm add resend react-email @react-email/preview-server -D -E", { stdio: "inherit", cwd: projectPath });
2912
2992
  console.log("Installing @react-email/components...");
2913
2993
  execSync("pnpm add @react-email/components -E", { stdio: "inherit", cwd: projectPath });
2914
2994
  }
2915
- if (installSupabase) {
2995
+ if (backend === "supabase") {
2916
2996
  console.log(`
2917
2997
  Setting up Supabase...`);
2918
2998
  const hasNodeModules = existsSync(join(projectPath, "node_modules"));
@@ -2924,6 +3004,26 @@ Setting up Supabase...`);
2924
3004
  execSync("pnpm add @supabase/supabase-js @supabase/ssr -E", { stdio: "inherit", cwd: projectPath });
2925
3005
  console.log("Installing supabase CLI...");
2926
3006
  execSync("pnpm add supabase -D -E", { stdio: "inherit", cwd: projectPath });
3007
+ console.log("Creating .env.example...");
3008
+ const envContent = `NEXT_PUBLIC_SUPABASE_URL=""
3009
+ NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=""
3010
+ `;
3011
+ writeFileSync(join(projectPath, ".env.example"), envContent);
3012
+ }
3013
+ if (backend === "jazz") {
3014
+ console.log(`
3015
+ Setting up Jazz.tools...`);
3016
+ const hasNodeModules = existsSync(join(projectPath, "node_modules"));
3017
+ if (!hasNodeModules) {
3018
+ console.log("Installing dependencies...");
3019
+ execSync("pnpm install", { stdio: "inherit", cwd: projectPath });
3020
+ }
3021
+ console.log("Installing jazz-tools...");
3022
+ execSync("pnpm add jazz-tools", { stdio: "inherit", cwd: projectPath });
3023
+ console.log("Creating .env.example...");
3024
+ const envContent = `NEXT_PUBLIC_JAZZ_API_KEY="" # or your API key
3025
+ `;
3026
+ writeFileSync(join(projectPath, ".env.example"), envContent);
2927
3027
  }
2928
3028
  if (installZod) {
2929
3029
  console.log(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-esa-stack",
3
- "version": "0.1.13",
3
+ "version": "0.2.0",
4
4
  "description": "CLI tool to scaffold Next.js projects with esa's preferred tech stack",
5
5
  "type": "module",
6
6
  "bin": {