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 +11 -0
- package/README.md +10 -9
- package/dist/cli.js +121 -21
- package/package.json +1 -1
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** -
|
|
27
|
-
-
|
|
28
|
-
- โก **Supabase** -
|
|
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
|
|
72
|
-
- *
|
|
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
|
-
| `--
|
|
95
|
-
| `--
|
|
96
|
-
| `--
|
|
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("--
|
|
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
|
|
2841
|
-
|
|
2842
|
-
|
|
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 (
|
|
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
|
|
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 (
|
|
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(`
|