create-next-style-guide 0.1.3 → 0.1.5
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/dist/index.js +453 -93
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/template/.env.example +12 -2
- package/template/.env.production.example +11 -0
- package/template/.env.staging.example +12 -0
- package/template/README.md +3 -0
- package/template/docs/style-guide/ops/env.md +23 -0
- package/template/docs/style-guide/overview/architecture.md +1 -1
- package/template/gitignore +2 -0
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import * as p4 from "@clack/prompts";
|
|
5
5
|
import color4 from "picocolors";
|
|
6
|
-
import
|
|
6
|
+
import path31 from "path";
|
|
7
7
|
|
|
8
8
|
// src/constants.ts
|
|
9
9
|
import path from "path";
|
|
@@ -78,6 +78,7 @@ var PRESET_SEEDS = {
|
|
|
78
78
|
auth: "none",
|
|
79
79
|
styling: "none",
|
|
80
80
|
ui: "none",
|
|
81
|
+
shadcnSet: "minimal",
|
|
81
82
|
data: "rsc",
|
|
82
83
|
store: "none",
|
|
83
84
|
icons: "none",
|
|
@@ -90,7 +91,7 @@ var PRESET_SEEDS = {
|
|
|
90
91
|
eslint: false,
|
|
91
92
|
prettier: false,
|
|
92
93
|
husky: false,
|
|
93
|
-
|
|
94
|
+
deploy: "none",
|
|
94
95
|
ci: false,
|
|
95
96
|
testing: false,
|
|
96
97
|
playwright: false
|
|
@@ -100,6 +101,7 @@ var PRESET_SEEDS = {
|
|
|
100
101
|
auth: "jwt",
|
|
101
102
|
styling: "tailwind",
|
|
102
103
|
ui: "shadcn",
|
|
104
|
+
shadcnSet: "starter",
|
|
103
105
|
data: "tanstack-query",
|
|
104
106
|
store: "zustand",
|
|
105
107
|
icons: "lucide",
|
|
@@ -112,7 +114,7 @@ var PRESET_SEEDS = {
|
|
|
112
114
|
eslint: true,
|
|
113
115
|
prettier: true,
|
|
114
116
|
husky: true,
|
|
115
|
-
|
|
117
|
+
deploy: "none",
|
|
116
118
|
ci: true,
|
|
117
119
|
testing: true,
|
|
118
120
|
playwright: false
|
|
@@ -122,6 +124,7 @@ var PRESET_SEEDS = {
|
|
|
122
124
|
auth: "none",
|
|
123
125
|
styling: "tailwind",
|
|
124
126
|
ui: "shadcn",
|
|
127
|
+
shadcnSet: "minimal",
|
|
125
128
|
data: "rsc",
|
|
126
129
|
store: "none",
|
|
127
130
|
icons: "lucide",
|
|
@@ -134,7 +137,7 @@ var PRESET_SEEDS = {
|
|
|
134
137
|
eslint: true,
|
|
135
138
|
prettier: true,
|
|
136
139
|
husky: true,
|
|
137
|
-
|
|
140
|
+
deploy: "vercel",
|
|
138
141
|
ci: true,
|
|
139
142
|
testing: true,
|
|
140
143
|
playwright: false
|
|
@@ -144,6 +147,7 @@ var PRESET_SEEDS = {
|
|
|
144
147
|
auth: "none",
|
|
145
148
|
styling: "tailwind",
|
|
146
149
|
ui: "shadcn",
|
|
150
|
+
shadcnSet: "starter",
|
|
147
151
|
data: "tanstack-query",
|
|
148
152
|
store: "zustand",
|
|
149
153
|
icons: "lucide",
|
|
@@ -156,7 +160,7 @@ var PRESET_SEEDS = {
|
|
|
156
160
|
eslint: true,
|
|
157
161
|
prettier: true,
|
|
158
162
|
husky: true,
|
|
159
|
-
|
|
163
|
+
deploy: "none",
|
|
160
164
|
ci: true,
|
|
161
165
|
testing: true,
|
|
162
166
|
playwright: false
|
|
@@ -166,19 +170,20 @@ function describePreset(id) {
|
|
|
166
170
|
const seed = PRESET_SEEDS[id];
|
|
167
171
|
return [
|
|
168
172
|
`styling=${seed.styling}`,
|
|
169
|
-
seed.ui === "shadcn" ?
|
|
173
|
+
seed.ui === "shadcn" ? `shadcn:${seed.shadcnSet}` : null,
|
|
170
174
|
`auth=${seed.auth}`,
|
|
171
175
|
seed.store !== "none" ? `store=${seed.store}` : null,
|
|
172
176
|
`validation=${seed.validation}`,
|
|
173
177
|
seed.endpoints ? "endpoints" : null,
|
|
174
178
|
seed.data === "tanstack-query" ? "query" : null,
|
|
175
179
|
seed.darkMode ? "dark" : null,
|
|
180
|
+
seed.deploy !== "none" ? `deploy=${seed.deploy}` : null,
|
|
176
181
|
seed.seo ? "seo" : null,
|
|
177
182
|
seed.i18n ? "i18n" : null,
|
|
178
183
|
seed.testing ? "vitest" : null
|
|
179
184
|
].filter(Boolean).join(" \xB7 ");
|
|
180
185
|
}
|
|
181
|
-
function collectOptionWarnings(options) {
|
|
186
|
+
function collectOptionWarnings(options, { verbose = false } = {}) {
|
|
182
187
|
const warnings = [];
|
|
183
188
|
if (options.auth === "jwt" && !options.endpoints) {
|
|
184
189
|
warnings.push(
|
|
@@ -199,9 +204,9 @@ function collectOptionWarnings(options) {
|
|
|
199
204
|
if (options.playwright && !options.testing) {
|
|
200
205
|
warnings.push("Playwright was requested without Vitest \u2014 enabling testing.");
|
|
201
206
|
}
|
|
202
|
-
if (options.store !== "none" && options.data === "tanstack-query") {
|
|
207
|
+
if (verbose && options.store !== "none" && options.data === "tanstack-query") {
|
|
203
208
|
warnings.push(
|
|
204
|
-
"
|
|
209
|
+
"Tip: Query = server cache; store = UI/session only. See docs/style-guide/overview/architecture.md"
|
|
205
210
|
);
|
|
206
211
|
}
|
|
207
212
|
return warnings;
|
|
@@ -217,6 +222,7 @@ var DEFAULT_OPTIONS = {
|
|
|
217
222
|
auth: "none",
|
|
218
223
|
styling: "none",
|
|
219
224
|
ui: "none",
|
|
225
|
+
shadcnSet: "minimal",
|
|
220
226
|
data: "rsc",
|
|
221
227
|
store: "none",
|
|
222
228
|
icons: "none",
|
|
@@ -230,7 +236,7 @@ var DEFAULT_OPTIONS = {
|
|
|
230
236
|
eslint: true,
|
|
231
237
|
prettier: true,
|
|
232
238
|
husky: true,
|
|
233
|
-
|
|
239
|
+
deploy: "none",
|
|
234
240
|
ci: true,
|
|
235
241
|
testing: true,
|
|
236
242
|
playwright: false,
|
|
@@ -245,6 +251,9 @@ function normalizeOptions(options) {
|
|
|
245
251
|
if (next.styling !== "tailwind" && next.ui === "shadcn") {
|
|
246
252
|
next = { ...next, ui: "none" };
|
|
247
253
|
}
|
|
254
|
+
if (next.ui !== "shadcn") {
|
|
255
|
+
next = { ...next, shadcnSet: "minimal" };
|
|
256
|
+
}
|
|
248
257
|
if (next.styling !== "tailwind" && next.darkMode) {
|
|
249
258
|
next = { ...next, darkMode: false };
|
|
250
259
|
}
|
|
@@ -283,6 +292,7 @@ async function promptCustomOptions(base) {
|
|
|
283
292
|
})
|
|
284
293
|
);
|
|
285
294
|
let ui = "none";
|
|
295
|
+
let shadcnSet = "minimal";
|
|
286
296
|
let darkMode = false;
|
|
287
297
|
if (styling === "tailwind") {
|
|
288
298
|
const useShadcn = assertNotCancel(
|
|
@@ -292,6 +302,26 @@ async function promptCustomOptions(base) {
|
|
|
292
302
|
})
|
|
293
303
|
);
|
|
294
304
|
ui = useShadcn ? "shadcn" : "none";
|
|
305
|
+
if (useShadcn) {
|
|
306
|
+
shadcnSet = assertNotCancel(
|
|
307
|
+
await p.select({
|
|
308
|
+
message: "shadcn component set?",
|
|
309
|
+
options: [
|
|
310
|
+
{
|
|
311
|
+
value: "minimal",
|
|
312
|
+
label: "Minimal",
|
|
313
|
+
hint: "Button only"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
value: "starter",
|
|
317
|
+
label: "Starter",
|
|
318
|
+
hint: "Button \xB7 Input \xB7 Card \xB7 Dialog"
|
|
319
|
+
}
|
|
320
|
+
],
|
|
321
|
+
initialValue: base.shadcnSet
|
|
322
|
+
})
|
|
323
|
+
);
|
|
324
|
+
}
|
|
295
325
|
darkMode = assertNotCancel(
|
|
296
326
|
await p.confirm({
|
|
297
327
|
message: "Add dark mode (next-themes)?",
|
|
@@ -439,10 +469,27 @@ async function promptCustomOptions(base) {
|
|
|
439
469
|
initialValue: base.husky
|
|
440
470
|
})
|
|
441
471
|
);
|
|
442
|
-
const
|
|
443
|
-
await p.
|
|
444
|
-
message: "
|
|
445
|
-
|
|
472
|
+
const deploy = assertNotCancel(
|
|
473
|
+
await p.select({
|
|
474
|
+
message: "Deployment target?",
|
|
475
|
+
options: [
|
|
476
|
+
{
|
|
477
|
+
value: "none",
|
|
478
|
+
label: "None",
|
|
479
|
+
hint: "no deploy files"
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
value: "vercel",
|
|
483
|
+
label: "Vercel",
|
|
484
|
+
hint: "vercel.json + docs"
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
value: "docker",
|
|
488
|
+
label: "Docker",
|
|
489
|
+
hint: "standalone production image"
|
|
490
|
+
}
|
|
491
|
+
],
|
|
492
|
+
initialValue: base.deploy
|
|
446
493
|
})
|
|
447
494
|
);
|
|
448
495
|
const ci = assertNotCancel(
|
|
@@ -455,6 +502,7 @@ async function promptCustomOptions(base) {
|
|
|
455
502
|
...base,
|
|
456
503
|
styling,
|
|
457
504
|
ui,
|
|
505
|
+
shadcnSet,
|
|
458
506
|
darkMode,
|
|
459
507
|
icons,
|
|
460
508
|
validation,
|
|
@@ -470,7 +518,7 @@ async function promptCustomOptions(base) {
|
|
|
470
518
|
eslint,
|
|
471
519
|
prettier,
|
|
472
520
|
husky,
|
|
473
|
-
|
|
521
|
+
deploy,
|
|
474
522
|
ci
|
|
475
523
|
};
|
|
476
524
|
}
|
|
@@ -482,9 +530,10 @@ async function runPrompts(initialName) {
|
|
|
482
530
|
placeholder: "my-app",
|
|
483
531
|
initialValue: initialName ?? "my-app",
|
|
484
532
|
validate(value) {
|
|
485
|
-
|
|
533
|
+
const next = value ?? "";
|
|
534
|
+
if (!next.trim()) return "Name is required.";
|
|
486
535
|
try {
|
|
487
|
-
slugify(
|
|
536
|
+
slugify(next);
|
|
488
537
|
} catch (error) {
|
|
489
538
|
return error instanceof Error ? error.message : "Invalid name.";
|
|
490
539
|
}
|
|
@@ -576,12 +625,36 @@ async function runPrompts(initialName) {
|
|
|
576
625
|
}
|
|
577
626
|
|
|
578
627
|
// src/scaffold.ts
|
|
579
|
-
import
|
|
580
|
-
import
|
|
628
|
+
import fs28 from "fs-extra";
|
|
629
|
+
import path29 from "path";
|
|
630
|
+
import { execFileSync } from "child_process";
|
|
581
631
|
import * as p2 from "@clack/prompts";
|
|
582
|
-
import { execSync } from "child_process";
|
|
583
632
|
import color2 from "picocolors";
|
|
584
633
|
|
|
634
|
+
// src/helpers/run-package-install.ts
|
|
635
|
+
import { spawn } from "child_process";
|
|
636
|
+
function runPackageInstall(projectDir, pm) {
|
|
637
|
+
return new Promise((resolve, reject) => {
|
|
638
|
+
const child = spawn(pm.id, pm.installArgs, {
|
|
639
|
+
cwd: projectDir,
|
|
640
|
+
stdio: "inherit",
|
|
641
|
+
env: process.env,
|
|
642
|
+
shell: process.platform === "win32"
|
|
643
|
+
});
|
|
644
|
+
child.on("error", reject);
|
|
645
|
+
child.on("close", (code) => {
|
|
646
|
+
if (code === 0) {
|
|
647
|
+
resolve();
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
reject(new Error(`${pm.id} ${pm.installArgs.join(" ")} exited with ${code}`));
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
function yieldUi() {
|
|
655
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
656
|
+
}
|
|
657
|
+
|
|
585
658
|
// src/helpers/replace-tokens.ts
|
|
586
659
|
import fs from "fs-extra";
|
|
587
660
|
import path2 from "path";
|
|
@@ -1771,7 +1844,7 @@ CMD ["node", "server.js"]
|
|
|
1771
1844
|
}
|
|
1772
1845
|
var dockerInstaller = {
|
|
1773
1846
|
id: "docker",
|
|
1774
|
-
active: (o) => o.docker,
|
|
1847
|
+
active: (o) => o.deploy === "docker",
|
|
1775
1848
|
async run({ projectDir, options }) {
|
|
1776
1849
|
const addon = path11.join(ADDONS_DIR, "docker");
|
|
1777
1850
|
const pm = getPackageManager(options.packageManager);
|
|
@@ -1916,7 +1989,7 @@ var huskyInstaller = {
|
|
|
1916
1989
|
},
|
|
1917
1990
|
"devDependencies"
|
|
1918
1991
|
);
|
|
1919
|
-
await addScripts(projectDir, { prepare: "husky" });
|
|
1992
|
+
await addScripts(projectDir, { prepare: "husky || true" });
|
|
1920
1993
|
const tsHooks = [];
|
|
1921
1994
|
if (options.prettier) tsHooks.push("prettier --write");
|
|
1922
1995
|
if (options.eslint) tsHooks.push("eslint --fix");
|
|
@@ -2424,7 +2497,154 @@ export function Button({
|
|
|
2424
2497
|
);
|
|
2425
2498
|
}
|
|
2426
2499
|
`;
|
|
2427
|
-
var
|
|
2500
|
+
var INPUT = `import type { ComponentProps } from "react";
|
|
2501
|
+
|
|
2502
|
+
import { cn } from "@/lib/utils";
|
|
2503
|
+
|
|
2504
|
+
export function Input({ className, type, ...props }: ComponentProps<"input">) {
|
|
2505
|
+
return (
|
|
2506
|
+
<input
|
|
2507
|
+
type={type}
|
|
2508
|
+
className={cn(
|
|
2509
|
+
"flex h-9 w-full rounded-md border border-neutral-300 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-neutral-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50",
|
|
2510
|
+
className,
|
|
2511
|
+
)}
|
|
2512
|
+
{...props}
|
|
2513
|
+
/>
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
`;
|
|
2517
|
+
var CARD = `import type { ComponentProps } from "react";
|
|
2518
|
+
|
|
2519
|
+
import { cn } from "@/lib/utils";
|
|
2520
|
+
|
|
2521
|
+
export function Card({ className, ...props }: ComponentProps<"div">) {
|
|
2522
|
+
return (
|
|
2523
|
+
<div
|
|
2524
|
+
className={cn(
|
|
2525
|
+
"rounded-xl border border-neutral-200 bg-white text-neutral-950 shadow-sm",
|
|
2526
|
+
className,
|
|
2527
|
+
)}
|
|
2528
|
+
{...props}
|
|
2529
|
+
/>
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
export function CardHeader({ className, ...props }: ComponentProps<"div">) {
|
|
2534
|
+
return (
|
|
2535
|
+
<div className={cn("flex flex-col gap-1.5 p-6", className)} {...props} />
|
|
2536
|
+
);
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
export function CardTitle({ className, ...props }: ComponentProps<"h3">) {
|
|
2540
|
+
return (
|
|
2541
|
+
<h3
|
|
2542
|
+
className={cn("font-semibold leading-none tracking-tight", className)}
|
|
2543
|
+
{...props}
|
|
2544
|
+
/>
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
export function CardDescription({ className, ...props }: ComponentProps<"p">) {
|
|
2549
|
+
return (
|
|
2550
|
+
<p className={cn("text-sm text-neutral-500", className)} {...props} />
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
export function CardContent({ className, ...props }: ComponentProps<"div">) {
|
|
2555
|
+
return <div className={cn("p-6 pt-0", className)} {...props} />;
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
export function CardFooter({ className, ...props }: ComponentProps<"div">) {
|
|
2559
|
+
return (
|
|
2560
|
+
<div className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
|
2561
|
+
);
|
|
2562
|
+
}
|
|
2563
|
+
`;
|
|
2564
|
+
var DIALOG = `"use client";
|
|
2565
|
+
|
|
2566
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
2567
|
+
import type { ComponentProps } from "react";
|
|
2568
|
+
|
|
2569
|
+
import { cn } from "@/lib/utils";
|
|
2570
|
+
|
|
2571
|
+
export const Dialog = DialogPrimitive.Root;
|
|
2572
|
+
export const DialogTrigger = DialogPrimitive.Trigger;
|
|
2573
|
+
export const DialogClose = DialogPrimitive.Close;
|
|
2574
|
+
export const DialogPortal = DialogPrimitive.Portal;
|
|
2575
|
+
|
|
2576
|
+
export function DialogOverlay({
|
|
2577
|
+
className,
|
|
2578
|
+
...props
|
|
2579
|
+
}: ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
2580
|
+
return (
|
|
2581
|
+
<DialogPrimitive.Overlay
|
|
2582
|
+
className={cn(
|
|
2583
|
+
"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
2584
|
+
className,
|
|
2585
|
+
)}
|
|
2586
|
+
{...props}
|
|
2587
|
+
/>
|
|
2588
|
+
);
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
export function DialogContent({
|
|
2592
|
+
className,
|
|
2593
|
+
children,
|
|
2594
|
+
...props
|
|
2595
|
+
}: ComponentProps<typeof DialogPrimitive.Content>) {
|
|
2596
|
+
return (
|
|
2597
|
+
<DialogPortal>
|
|
2598
|
+
<DialogOverlay />
|
|
2599
|
+
<DialogPrimitive.Content
|
|
2600
|
+
className={cn(
|
|
2601
|
+
"fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border border-neutral-200 bg-white p-6 shadow-lg duration-200 sm:rounded-xl",
|
|
2602
|
+
className,
|
|
2603
|
+
)}
|
|
2604
|
+
{...props}
|
|
2605
|
+
>
|
|
2606
|
+
{children}
|
|
2607
|
+
</DialogPrimitive.Content>
|
|
2608
|
+
</DialogPortal>
|
|
2609
|
+
);
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
export function DialogHeader({ className, ...props }: ComponentProps<"div">) {
|
|
2613
|
+
return (
|
|
2614
|
+
<div
|
|
2615
|
+
className={cn("flex flex-col gap-1.5 text-center sm:text-left", className)}
|
|
2616
|
+
{...props}
|
|
2617
|
+
/>
|
|
2618
|
+
);
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
export function DialogTitle({
|
|
2622
|
+
className,
|
|
2623
|
+
...props
|
|
2624
|
+
}: ComponentProps<typeof DialogPrimitive.Title>) {
|
|
2625
|
+
return (
|
|
2626
|
+
<DialogPrimitive.Title
|
|
2627
|
+
className={cn("text-lg font-semibold leading-none", className)}
|
|
2628
|
+
{...props}
|
|
2629
|
+
/>
|
|
2630
|
+
);
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
export function DialogDescription({
|
|
2634
|
+
className,
|
|
2635
|
+
...props
|
|
2636
|
+
}: ComponentProps<typeof DialogPrimitive.Description>) {
|
|
2637
|
+
return (
|
|
2638
|
+
<DialogPrimitive.Description
|
|
2639
|
+
className={cn("text-sm text-neutral-500", className)}
|
|
2640
|
+
{...props}
|
|
2641
|
+
/>
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2644
|
+
`;
|
|
2645
|
+
function ruleFor(set) {
|
|
2646
|
+
const setLine = set === "starter" ? "- Starter set: `button`, `input`, `card`, `dialog`." : "- Minimal set: `button` only.";
|
|
2647
|
+
return `---
|
|
2428
2648
|
description: shadcn/ui conventions
|
|
2429
2649
|
globs: components.json,src/components/ui/**,src/lib/utils.ts
|
|
2430
2650
|
alwaysApply: false
|
|
@@ -2433,41 +2653,59 @@ alwaysApply: false
|
|
|
2433
2653
|
# shadcn
|
|
2434
2654
|
|
|
2435
2655
|
- Primitives live flat in \`src/components/ui/*\` (CLI-compatible).
|
|
2436
|
-
|
|
2656
|
+
${setLine}
|
|
2657
|
+
- Add more with \`npx shadcn@latest add \u2026\`.
|
|
2437
2658
|
- \`cn()\` from \`@/lib/utils\` for class merging.
|
|
2438
2659
|
- Config: \`components.json\`. Requires Tailwind.
|
|
2439
2660
|
`;
|
|
2661
|
+
}
|
|
2440
2662
|
var shadcnInstaller = {
|
|
2441
2663
|
id: "shadcn",
|
|
2442
2664
|
active: (o) => o.ui === "shadcn" && o.styling === "tailwind",
|
|
2443
2665
|
async run({ projectDir, options }) {
|
|
2444
2666
|
const iconLibrary = options.icons === "tabler" ? "tabler" : "lucide";
|
|
2667
|
+
const set = options.shadcnSet;
|
|
2668
|
+
const uiDir = path21.join(projectDir, "src", "components", "ui");
|
|
2445
2669
|
await fs20.writeFile(
|
|
2446
2670
|
path21.join(projectDir, "components.json"),
|
|
2447
2671
|
componentsJson(iconLibrary),
|
|
2448
2672
|
"utf8"
|
|
2449
2673
|
);
|
|
2450
|
-
await fs20.ensureDir(
|
|
2451
|
-
await fs20.writeFile(
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
"utf8"
|
|
2455
|
-
|
|
2456
|
-
|
|
2674
|
+
await fs20.ensureDir(uiDir);
|
|
2675
|
+
await fs20.writeFile(path21.join(uiDir, "button.tsx"), BUTTON, "utf8");
|
|
2676
|
+
if (set === "starter") {
|
|
2677
|
+
await fs20.writeFile(path21.join(uiDir, "input.tsx"), INPUT, "utf8");
|
|
2678
|
+
await fs20.writeFile(path21.join(uiDir, "card.tsx"), CARD, "utf8");
|
|
2679
|
+
await fs20.writeFile(path21.join(uiDir, "dialog.tsx"), DIALOG, "utf8");
|
|
2680
|
+
}
|
|
2681
|
+
await fs20.remove(path21.join(uiDir, ".gitkeep"));
|
|
2457
2682
|
await fs20.remove(path21.join(projectDir, "src", "components", ".gitkeep"));
|
|
2458
2683
|
await fs20.writeFile(
|
|
2459
2684
|
path21.join(projectDir, ".cursor", "rules", "shadcn.mdc"),
|
|
2460
|
-
|
|
2685
|
+
ruleFor(set),
|
|
2461
2686
|
"utf8"
|
|
2462
2687
|
);
|
|
2688
|
+
await fs20.outputFile(
|
|
2689
|
+
path21.join(projectDir, "docs", "style-guide", "ui", "shadcn.md"),
|
|
2690
|
+
`# shadcn/ui
|
|
2691
|
+
|
|
2692
|
+
Set: **${set}**
|
|
2693
|
+
${set === "starter" ? "- `button` \xB7 `input` \xB7 `card` \xB7 `dialog`\n" : "- `button`\n"}
|
|
2694
|
+
Add more: \`npx shadcn@latest add \u2026\`
|
|
2695
|
+
`
|
|
2696
|
+
);
|
|
2463
2697
|
if (options.icons === "none") {
|
|
2464
2698
|
await addDependencies(projectDir, {
|
|
2465
2699
|
"lucide-react": "^0.469.0"
|
|
2466
2700
|
});
|
|
2467
2701
|
}
|
|
2468
|
-
|
|
2702
|
+
const deps = {
|
|
2469
2703
|
"class-variance-authority": "^0.7.1"
|
|
2470
|
-
}
|
|
2704
|
+
};
|
|
2705
|
+
if (set === "starter") {
|
|
2706
|
+
deps["@radix-ui/react-dialog"] = "^1.1.4";
|
|
2707
|
+
}
|
|
2708
|
+
await addDependencies(projectDir, deps);
|
|
2471
2709
|
}
|
|
2472
2710
|
};
|
|
2473
2711
|
|
|
@@ -2651,9 +2889,10 @@ function stackLines(options) {
|
|
|
2651
2889
|
options.eslint ? "Enhanced ESLint" : "Minimal ESLint",
|
|
2652
2890
|
options.prettier ? "Prettier" : "No Prettier",
|
|
2653
2891
|
options.husky ? "Husky + lint-staged" : "No git hooks",
|
|
2654
|
-
options.
|
|
2892
|
+
options.ui === "shadcn" ? `shadcn set: ${options.shadcnSet}` : null,
|
|
2893
|
+
options.deploy === "docker" ? "Deploy: Docker standalone image" : options.deploy === "vercel" ? "Deploy: Vercel" : "No deploy target",
|
|
2655
2894
|
options.ci ? "GitHub Actions CI" : "No CI workflow"
|
|
2656
|
-
];
|
|
2895
|
+
].filter((line) => line !== null);
|
|
2657
2896
|
}
|
|
2658
2897
|
function welcomeCardSource(options) {
|
|
2659
2898
|
const items = stackLines(options).map((line) => ` <li>${escapeJsx(line)}</li>`).join("\n");
|
|
@@ -2712,6 +2951,7 @@ function docsIndex(options) {
|
|
|
2712
2951
|
"Layers: app / components / lib / utils"
|
|
2713
2952
|
],
|
|
2714
2953
|
["[Project structure](overview/project-structure.md)", "Folder map"],
|
|
2954
|
+
["[Environment](ops/env.md)", "`.env.example` \xB7 staging \xB7 production"],
|
|
2715
2955
|
["[HTTP client](http/client.md)", "`apiFetch` helper"],
|
|
2716
2956
|
[
|
|
2717
2957
|
`[Validation](validation/${options.validation}.md)`,
|
|
@@ -2755,7 +2995,10 @@ function docsIndex(options) {
|
|
|
2755
2995
|
rows.push(["Tailwind", "See `.cursor/rules/tailwind.mdc`"]);
|
|
2756
2996
|
}
|
|
2757
2997
|
if (options.ui === "shadcn") {
|
|
2758
|
-
rows.push([
|
|
2998
|
+
rows.push([
|
|
2999
|
+
"[shadcn/ui](ui/shadcn.md)",
|
|
3000
|
+
`set=${options.shadcnSet}`
|
|
3001
|
+
]);
|
|
2759
3002
|
}
|
|
2760
3003
|
if (options.styling === "mui") {
|
|
2761
3004
|
rows.push(["MUI", "See `.cursor/rules/mui.mdc`"]);
|
|
@@ -2784,9 +3027,12 @@ function docsIndex(options) {
|
|
|
2784
3027
|
if (options.husky) {
|
|
2785
3028
|
rows.push(["[Husky](tooling/husky.md)", "Pre-commit hooks"]);
|
|
2786
3029
|
}
|
|
2787
|
-
if (options.docker) {
|
|
3030
|
+
if (options.deploy === "docker") {
|
|
2788
3031
|
rows.push(["[Docker](ops/docker.md)", "Standalone production image"]);
|
|
2789
3032
|
}
|
|
3033
|
+
if (options.deploy === "vercel") {
|
|
3034
|
+
rows.push(["[Vercel](ops/vercel.md)", "vercel.json + env notes"]);
|
|
3035
|
+
}
|
|
2790
3036
|
if (options.ci) {
|
|
2791
3037
|
rows.push(["[CI](ops/ci.md)", "GitHub Actions"]);
|
|
2792
3038
|
}
|
|
@@ -2977,7 +3223,7 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
2977
3223
|
return twMerge(clsx(inputs));
|
|
2978
3224
|
}
|
|
2979
3225
|
`;
|
|
2980
|
-
var
|
|
3226
|
+
var RULE10 = `---
|
|
2981
3227
|
description: Tailwind CSS v4 conventions
|
|
2982
3228
|
globs: src/app/globals.css,src/**/*.{tsx,css},postcss.config.mjs
|
|
2983
3229
|
alwaysApply: false
|
|
@@ -3010,7 +3256,7 @@ var tailwindInstaller = {
|
|
|
3010
3256
|
);
|
|
3011
3257
|
await fs23.writeFile(
|
|
3012
3258
|
path24.join(projectDir, ".cursor", "rules", "tailwind.mdc"),
|
|
3013
|
-
|
|
3259
|
+
RULE10,
|
|
3014
3260
|
"utf8"
|
|
3015
3261
|
);
|
|
3016
3262
|
await addDependencies(projectDir, {
|
|
@@ -3058,7 +3304,7 @@ export function QueryProvider({ children }: { children: ReactNode }) {
|
|
|
3058
3304
|
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
|
|
3059
3305
|
}
|
|
3060
3306
|
`;
|
|
3061
|
-
var
|
|
3307
|
+
var RULE11 = `---
|
|
3062
3308
|
description: TanStack Query conventions
|
|
3063
3309
|
globs: src/providers/**,src/hooks/**,src/endpoints/**,src/**/*query*.{ts,tsx},src/**/use-*.{ts,tsx}
|
|
3064
3310
|
alwaysApply: false
|
|
@@ -3091,7 +3337,7 @@ var tanstackQueryInstaller = {
|
|
|
3091
3337
|
await fs24.ensureDir(ruleDir);
|
|
3092
3338
|
await fs24.writeFile(
|
|
3093
3339
|
path25.join(ruleDir, "tanstack-query.mdc"),
|
|
3094
|
-
|
|
3340
|
+
RULE11,
|
|
3095
3341
|
"utf8"
|
|
3096
3342
|
);
|
|
3097
3343
|
}
|
|
@@ -3373,6 +3619,53 @@ Forms: \`@hookform/resolvers/${options.validation}\` when using RHF.
|
|
|
3373
3619
|
}
|
|
3374
3620
|
};
|
|
3375
3621
|
|
|
3622
|
+
// src/installers/vercel.ts
|
|
3623
|
+
import fs27 from "fs-extra";
|
|
3624
|
+
import path28 from "path";
|
|
3625
|
+
var VERCEL_JSON = `{
|
|
3626
|
+
"framework": "nextjs",
|
|
3627
|
+
"regions": ["iad1"]
|
|
3628
|
+
}
|
|
3629
|
+
`;
|
|
3630
|
+
var RULE12 = `---
|
|
3631
|
+
description: Vercel deployment conventions
|
|
3632
|
+
globs: vercel.json
|
|
3633
|
+
alwaysApply: false
|
|
3634
|
+
---
|
|
3635
|
+
|
|
3636
|
+
# Vercel
|
|
3637
|
+
|
|
3638
|
+
- Config: \`vercel.json\` (framework Next.js).
|
|
3639
|
+
- Set \`NEXT_PUBLIC_APP_URL\` / \`NEXT_PUBLIC_API_URL\` (and \`AUTH_SECRET\` if Auth.js) in the Vercel project env.
|
|
3640
|
+
- Prefer Preview env for staging; Production for live.
|
|
3641
|
+
- Do not commit \`.vercel\` or production secrets.
|
|
3642
|
+
`;
|
|
3643
|
+
var vercelInstaller = {
|
|
3644
|
+
id: "vercel",
|
|
3645
|
+
active: (o) => o.deploy === "vercel",
|
|
3646
|
+
async run({ projectDir }) {
|
|
3647
|
+
await fs27.writeFile(
|
|
3648
|
+
path28.join(projectDir, "vercel.json"),
|
|
3649
|
+
VERCEL_JSON,
|
|
3650
|
+
"utf8"
|
|
3651
|
+
);
|
|
3652
|
+
await fs27.writeFile(
|
|
3653
|
+
path28.join(projectDir, ".cursor", "rules", "vercel.mdc"),
|
|
3654
|
+
RULE12,
|
|
3655
|
+
"utf8"
|
|
3656
|
+
);
|
|
3657
|
+
await fs27.outputFile(
|
|
3658
|
+
path28.join(projectDir, "docs", "style-guide", "ops", "vercel.md"),
|
|
3659
|
+
`# Vercel
|
|
3660
|
+
|
|
3661
|
+
- \`vercel.json\` \u2014 Next.js framework hint
|
|
3662
|
+
- Link the repo in the Vercel dashboard (or \`npx vercel\`)
|
|
3663
|
+
- Env: copy from \`.env.production.example\` into Project \u2192 Settings \u2192 Environment Variables
|
|
3664
|
+
`
|
|
3665
|
+
);
|
|
3666
|
+
}
|
|
3667
|
+
};
|
|
3668
|
+
|
|
3376
3669
|
// src/installers/index.ts
|
|
3377
3670
|
var installers = [
|
|
3378
3671
|
packageManagerInstaller,
|
|
@@ -3396,14 +3689,23 @@ var installers = [
|
|
|
3396
3689
|
prettierInstaller,
|
|
3397
3690
|
huskyInstaller,
|
|
3398
3691
|
dockerInstaller,
|
|
3692
|
+
vercelInstaller,
|
|
3399
3693
|
ciInstaller,
|
|
3400
3694
|
appProvidersInstaller,
|
|
3401
3695
|
surfaceInstaller
|
|
3402
3696
|
];
|
|
3403
|
-
async function runInstallers(ctx) {
|
|
3697
|
+
async function runInstallers(ctx, onProgress) {
|
|
3698
|
+
const active = installers.filter(
|
|
3699
|
+
(installer) => installer.active(ctx.options)
|
|
3700
|
+
);
|
|
3404
3701
|
const ran = [];
|
|
3405
|
-
for (
|
|
3406
|
-
|
|
3702
|
+
for (let i = 0; i < active.length; i += 1) {
|
|
3703
|
+
const installer = active[i];
|
|
3704
|
+
await onProgress?.({
|
|
3705
|
+
id: installer.id,
|
|
3706
|
+
index: i + 1,
|
|
3707
|
+
total: active.length
|
|
3708
|
+
});
|
|
3407
3709
|
await installer.run(ctx);
|
|
3408
3710
|
ran.push(installer.id);
|
|
3409
3711
|
}
|
|
@@ -3412,10 +3714,10 @@ async function runInstallers(ctx) {
|
|
|
3412
3714
|
|
|
3413
3715
|
// src/scaffold.ts
|
|
3414
3716
|
async function scaffold(options) {
|
|
3415
|
-
const projectDir =
|
|
3717
|
+
const projectDir = path29.resolve(process.cwd(), options.name);
|
|
3416
3718
|
const pm = getPackageManager(options.packageManager);
|
|
3417
|
-
if (await
|
|
3418
|
-
const entries = await
|
|
3719
|
+
if (await fs28.pathExists(projectDir)) {
|
|
3720
|
+
const entries = await fs28.readdir(projectDir);
|
|
3419
3721
|
if (entries.length > 0) {
|
|
3420
3722
|
throw new Error(
|
|
3421
3723
|
`Target directory already exists and is not empty: ${projectDir}`
|
|
@@ -3424,46 +3726,68 @@ async function scaffold(options) {
|
|
|
3424
3726
|
}
|
|
3425
3727
|
const spin = p2.spinner();
|
|
3426
3728
|
spin.start("Copying template");
|
|
3427
|
-
|
|
3729
|
+
await yieldUi();
|
|
3730
|
+
if (!await fs28.pathExists(TEMPLATE_DIR)) {
|
|
3428
3731
|
spin.stop("Template missing");
|
|
3429
3732
|
throw new Error(`Template directory not found at ${TEMPLATE_DIR}`);
|
|
3430
3733
|
}
|
|
3431
|
-
await
|
|
3734
|
+
await fs28.copy(TEMPLATE_DIR, projectDir, {
|
|
3432
3735
|
filter: (src) => {
|
|
3433
|
-
const base =
|
|
3736
|
+
const base = path29.basename(src);
|
|
3434
3737
|
return base !== "node_modules" && base !== ".next" && base !== "dist";
|
|
3435
3738
|
}
|
|
3436
3739
|
});
|
|
3437
|
-
const gitignoreSrc =
|
|
3438
|
-
const gitignoreDest =
|
|
3439
|
-
if (await
|
|
3440
|
-
await
|
|
3740
|
+
const gitignoreSrc = path29.join(projectDir, "gitignore");
|
|
3741
|
+
const gitignoreDest = path29.join(projectDir, ".gitignore");
|
|
3742
|
+
if (await fs28.pathExists(gitignoreSrc)) {
|
|
3743
|
+
await fs28.move(gitignoreSrc, gitignoreDest, { overwrite: true });
|
|
3441
3744
|
}
|
|
3442
3745
|
spin.message("Applying project tokens");
|
|
3746
|
+
await yieldUi();
|
|
3443
3747
|
await replaceTokensInTree(projectDir, {
|
|
3444
3748
|
__PROJECT_NAME__: options.name,
|
|
3445
3749
|
__PROJECT_SLUG__: options.slug,
|
|
3446
3750
|
__PACKAGE_MANAGER__: pm.id
|
|
3447
3751
|
});
|
|
3448
|
-
|
|
3449
|
-
|
|
3752
|
+
const ran = await runInstallers({ projectDir, options }, async (step) => {
|
|
3753
|
+
spin.message(`Installer (${step.index}/${step.total}): ${step.id}`);
|
|
3754
|
+
await yieldUi();
|
|
3755
|
+
});
|
|
3756
|
+
spin.stop(
|
|
3757
|
+
ran.length ? `Scaffold ready \xB7 ${ran.length} installer${ran.length === 1 ? "" : "s"}` : "Scaffold ready \xB7 base template"
|
|
3758
|
+
);
|
|
3450
3759
|
if (!options.noInstall) {
|
|
3451
|
-
|
|
3760
|
+
if (options.husky) {
|
|
3761
|
+
try {
|
|
3762
|
+
execFileSync("git", ["init"], {
|
|
3763
|
+
cwd: projectDir,
|
|
3764
|
+
stdio: "ignore"
|
|
3765
|
+
});
|
|
3766
|
+
} catch {
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
p2.log.step(
|
|
3770
|
+
`Installing dependencies with ${color2.cyan(pm.id)} \u2014 live output below\u2026`
|
|
3771
|
+
);
|
|
3452
3772
|
try {
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
});
|
|
3457
|
-
} catch {
|
|
3773
|
+
await runPackageInstall(projectDir, pm);
|
|
3774
|
+
p2.log.success("Dependencies installed");
|
|
3775
|
+
} catch (error) {
|
|
3458
3776
|
if (pm.id !== "npm") {
|
|
3459
|
-
|
|
3460
|
-
|
|
3777
|
+
p2.log.warn(`${pm.id} failed \u2014 retrying with npm\u2026`);
|
|
3778
|
+
const npm = getPackageManager("npm");
|
|
3779
|
+
await runPackageInstall(projectDir, npm);
|
|
3780
|
+
p2.log.success("Dependencies installed (npm)");
|
|
3461
3781
|
} else {
|
|
3462
|
-
|
|
3782
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3783
|
+
throw new Error(`Dependency install failed: ${message}`);
|
|
3463
3784
|
}
|
|
3464
3785
|
}
|
|
3786
|
+
} else {
|
|
3787
|
+
p2.log.info(
|
|
3788
|
+
color2.dim(`Skipped install \xB7 run ${pm.id} install in the project folder`)
|
|
3789
|
+
);
|
|
3465
3790
|
}
|
|
3466
|
-
spin.stop("Project ready");
|
|
3467
3791
|
if (ran.length) {
|
|
3468
3792
|
p2.log.info(`Installers: ${ran.join(", ")}`);
|
|
3469
3793
|
} else {
|
|
@@ -3473,8 +3797,8 @@ async function scaffold(options) {
|
|
|
3473
3797
|
}
|
|
3474
3798
|
|
|
3475
3799
|
// src/upgrade.ts
|
|
3476
|
-
import
|
|
3477
|
-
import
|
|
3800
|
+
import fs29 from "fs-extra";
|
|
3801
|
+
import path30 from "path";
|
|
3478
3802
|
import * as p3 from "@clack/prompts";
|
|
3479
3803
|
import color3 from "picocolors";
|
|
3480
3804
|
var TRACKED = [
|
|
@@ -3484,17 +3808,17 @@ var TRACKED = [
|
|
|
3484
3808
|
".cursor/rules/forms.mdc"
|
|
3485
3809
|
];
|
|
3486
3810
|
function rel(projectDir, file) {
|
|
3487
|
-
return
|
|
3811
|
+
return path30.join(projectDir, file);
|
|
3488
3812
|
}
|
|
3489
3813
|
function templatePath(file) {
|
|
3490
|
-
return
|
|
3814
|
+
return path30.join(TEMPLATE_DIR, file);
|
|
3491
3815
|
}
|
|
3492
3816
|
async function runUpgrade(options) {
|
|
3493
3817
|
const { projectDir, apply } = options;
|
|
3494
|
-
if (!await
|
|
3818
|
+
if (!await fs29.pathExists(path30.join(projectDir, "package.json"))) {
|
|
3495
3819
|
throw new Error(`Not an app directory (missing package.json): ${projectDir}`);
|
|
3496
3820
|
}
|
|
3497
|
-
if (!await
|
|
3821
|
+
if (!await fs29.pathExists(TEMPLATE_DIR)) {
|
|
3498
3822
|
throw new Error(`Template missing at ${TEMPLATE_DIR}`);
|
|
3499
3823
|
}
|
|
3500
3824
|
p3.intro(color3.bgCyan(color3.black(" next-style-guide upgrade ")));
|
|
@@ -3507,14 +3831,14 @@ async function runUpgrade(options) {
|
|
|
3507
3831
|
for (const file of TRACKED) {
|
|
3508
3832
|
const from = templatePath(file);
|
|
3509
3833
|
const to = rel(projectDir, file);
|
|
3510
|
-
if (!await
|
|
3834
|
+
if (!await fs29.pathExists(from)) {
|
|
3511
3835
|
p3.log.warn(`Template missing ${file}`);
|
|
3512
3836
|
continue;
|
|
3513
3837
|
}
|
|
3514
|
-
const next = await
|
|
3838
|
+
const next = await fs29.readFile(from, "utf8");
|
|
3515
3839
|
let outgoing = next;
|
|
3516
|
-
if (file === "AGENTS.md" && await
|
|
3517
|
-
const current2 = await
|
|
3840
|
+
if (file === "AGENTS.md" && await fs29.pathExists(to)) {
|
|
3841
|
+
const current2 = await fs29.readFile(to, "utf8");
|
|
3518
3842
|
const nameMatch = current2.match(/^# Agents \((.+)\)$/m);
|
|
3519
3843
|
if (nameMatch?.[1]) {
|
|
3520
3844
|
outgoing = outgoing.replace("__PROJECT_NAME__", nameMatch[1]);
|
|
@@ -3527,18 +3851,18 @@ async function runUpgrade(options) {
|
|
|
3527
3851
|
}
|
|
3528
3852
|
}
|
|
3529
3853
|
}
|
|
3530
|
-
outgoing = outgoing.replaceAll("__PROJECT_NAME__",
|
|
3531
|
-
if (!await
|
|
3854
|
+
outgoing = outgoing.replaceAll("__PROJECT_NAME__", path30.basename(projectDir)).replaceAll("__PACKAGE_MANAGER__", "pnpm").replaceAll("__PROJECT_SLUG__", path30.basename(projectDir));
|
|
3855
|
+
if (!await fs29.pathExists(to)) {
|
|
3532
3856
|
missing += 1;
|
|
3533
3857
|
p3.log.warn(`missing ${file}`);
|
|
3534
3858
|
if (apply) {
|
|
3535
|
-
await
|
|
3536
|
-
await
|
|
3859
|
+
await fs29.ensureDir(path30.dirname(to));
|
|
3860
|
+
await fs29.writeFile(to, outgoing, "utf8");
|
|
3537
3861
|
p3.log.success(`wrote ${file}`);
|
|
3538
3862
|
}
|
|
3539
3863
|
continue;
|
|
3540
3864
|
}
|
|
3541
|
-
const current = await
|
|
3865
|
+
const current = await fs29.readFile(to, "utf8");
|
|
3542
3866
|
if (current === outgoing) {
|
|
3543
3867
|
same += 1;
|
|
3544
3868
|
p3.log.step(color3.dim(`same ${file}`));
|
|
@@ -3547,7 +3871,7 @@ async function runUpgrade(options) {
|
|
|
3547
3871
|
changed += 1;
|
|
3548
3872
|
p3.log.warn(`differs ${file}`);
|
|
3549
3873
|
if (apply) {
|
|
3550
|
-
await
|
|
3874
|
+
await fs29.writeFile(to, outgoing, "utf8");
|
|
3551
3875
|
p3.log.success(`updated ${file}`);
|
|
3552
3876
|
}
|
|
3553
3877
|
}
|
|
@@ -3569,6 +3893,8 @@ var VALIDATION_VALUES = /* @__PURE__ */ new Set(["zod", "yup"]);
|
|
|
3569
3893
|
var AUTH_VALUES = /* @__PURE__ */ new Set(["none", "jwt", "authjs"]);
|
|
3570
3894
|
var STORE_VALUES = /* @__PURE__ */ new Set(["none", "zustand", "redux"]);
|
|
3571
3895
|
var PM_VALUES = /* @__PURE__ */ new Set(["pnpm", "npm", "yarn"]);
|
|
3896
|
+
var SHADCN_SET_VALUES = /* @__PURE__ */ new Set(["minimal", "starter"]);
|
|
3897
|
+
var DEPLOY_VALUES = /* @__PURE__ */ new Set(["none", "vercel", "docker"]);
|
|
3572
3898
|
var PRESET_VALUES = /* @__PURE__ */ new Set([
|
|
3573
3899
|
"custom",
|
|
3574
3900
|
"minimal",
|
|
@@ -3604,7 +3930,8 @@ function parseArgs(argv) {
|
|
|
3604
3930
|
noInstall: false,
|
|
3605
3931
|
yes: false,
|
|
3606
3932
|
upgrade: false,
|
|
3607
|
-
apply: false
|
|
3933
|
+
apply: false,
|
|
3934
|
+
verbose: false
|
|
3608
3935
|
};
|
|
3609
3936
|
for (let i = 0; i < args.length; i += 1) {
|
|
3610
3937
|
const arg = args[i];
|
|
@@ -3621,6 +3948,10 @@ function parseArgs(argv) {
|
|
|
3621
3948
|
out.apply = false;
|
|
3622
3949
|
continue;
|
|
3623
3950
|
}
|
|
3951
|
+
if (arg === "--verbose" || arg === "-V") {
|
|
3952
|
+
out.verbose = true;
|
|
3953
|
+
continue;
|
|
3954
|
+
}
|
|
3624
3955
|
if (arg === "--no-install") {
|
|
3625
3956
|
out.noInstall = true;
|
|
3626
3957
|
continue;
|
|
@@ -3645,7 +3976,6 @@ function parseArgs(argv) {
|
|
|
3645
3976
|
["eslint", "--eslint", "--no-eslint"],
|
|
3646
3977
|
["prettier", "--prettier", "--no-prettier"],
|
|
3647
3978
|
["husky", "--husky", "--no-husky"],
|
|
3648
|
-
["docker", "--docker", "--no-docker"],
|
|
3649
3979
|
["ci", "--ci", "--no-ci"],
|
|
3650
3980
|
["shadcn", "--shadcn", "--no-shadcn"]
|
|
3651
3981
|
];
|
|
@@ -3713,6 +4043,29 @@ function parseArgs(argv) {
|
|
|
3713
4043
|
i = icons.nextIndex;
|
|
3714
4044
|
continue;
|
|
3715
4045
|
}
|
|
4046
|
+
const shadcnSet = readChoice(arg, "--shadcn-set", SHADCN_SET_VALUES, args, i);
|
|
4047
|
+
if (shadcnSet) {
|
|
4048
|
+
out.shadcnSet = shadcnSet.value;
|
|
4049
|
+
out.shadcn = true;
|
|
4050
|
+
i = shadcnSet.nextIndex;
|
|
4051
|
+
continue;
|
|
4052
|
+
}
|
|
4053
|
+
const deploy = readChoice(arg, "--deploy", DEPLOY_VALUES, args, i);
|
|
4054
|
+
if (deploy) {
|
|
4055
|
+
out.deploy = deploy.value;
|
|
4056
|
+
i = deploy.nextIndex;
|
|
4057
|
+
continue;
|
|
4058
|
+
}
|
|
4059
|
+
if (arg === "--docker") {
|
|
4060
|
+
out.deploy = "docker";
|
|
4061
|
+
continue;
|
|
4062
|
+
}
|
|
4063
|
+
if (arg === "--no-docker") {
|
|
4064
|
+
if (out.deploy === void 0 || out.deploy === "docker") {
|
|
4065
|
+
out.deploy = "none";
|
|
4066
|
+
}
|
|
4067
|
+
continue;
|
|
4068
|
+
}
|
|
3716
4069
|
if (!arg.startsWith("-") && !out.name) {
|
|
3717
4070
|
out.name = arg;
|
|
3718
4071
|
}
|
|
@@ -3736,6 +4089,7 @@ Options:
|
|
|
3736
4089
|
--store none|zustand|redux
|
|
3737
4090
|
--styling none|tailwind|mui
|
|
3738
4091
|
--shadcn | --no-shadcn
|
|
4092
|
+
--shadcn-set minimal|starter
|
|
3739
4093
|
--dark-mode | --no-dark-mode
|
|
3740
4094
|
--validation zod|yup
|
|
3741
4095
|
--auth none|jwt|authjs
|
|
@@ -3750,8 +4104,10 @@ Options:
|
|
|
3750
4104
|
--eslint | --no-eslint
|
|
3751
4105
|
--prettier | --no-prettier
|
|
3752
4106
|
--husky | --no-husky
|
|
3753
|
-
--
|
|
4107
|
+
--deploy none|vercel|docker
|
|
4108
|
+
--docker | --no-docker (alias for --deploy docker / none)
|
|
3754
4109
|
--ci | --no-ci
|
|
4110
|
+
--verbose, -V advisory tips (e.g. Query vs store)
|
|
3755
4111
|
--help, -h
|
|
3756
4112
|
|
|
3757
4113
|
Upgrade:
|
|
@@ -3767,6 +4123,10 @@ function applyFlagOverrides(options, flags) {
|
|
|
3767
4123
|
if (flags.icons !== void 0) next.icons = flags.icons;
|
|
3768
4124
|
if (flags.styling !== void 0) next.styling = flags.styling;
|
|
3769
4125
|
if (flags.shadcn !== void 0) next.ui = flags.shadcn ? "shadcn" : "none";
|
|
4126
|
+
if (flags.shadcnSet !== void 0) {
|
|
4127
|
+
next.shadcnSet = flags.shadcnSet;
|
|
4128
|
+
next.ui = "shadcn";
|
|
4129
|
+
}
|
|
3770
4130
|
if (flags.validation !== void 0) next.validation = flags.validation;
|
|
3771
4131
|
if (flags.auth !== void 0) next.auth = flags.auth;
|
|
3772
4132
|
if (flags.store !== void 0) next.store = flags.store;
|
|
@@ -3783,14 +4143,14 @@ function applyFlagOverrides(options, flags) {
|
|
|
3783
4143
|
if (flags.eslint !== void 0) next.eslint = flags.eslint;
|
|
3784
4144
|
if (flags.prettier !== void 0) next.prettier = flags.prettier;
|
|
3785
4145
|
if (flags.husky !== void 0) next.husky = flags.husky;
|
|
3786
|
-
if (flags.
|
|
4146
|
+
if (flags.deploy !== void 0) next.deploy = flags.deploy;
|
|
3787
4147
|
if (flags.ci !== void 0) next.ci = flags.ci;
|
|
3788
4148
|
return normalizeOptions(next);
|
|
3789
4149
|
}
|
|
3790
4150
|
async function main() {
|
|
3791
4151
|
const flags = parseArgs(process.argv);
|
|
3792
4152
|
if (flags.upgrade) {
|
|
3793
|
-
const projectDir2 =
|
|
4153
|
+
const projectDir2 = path31.resolve(process.cwd(), flags.name ?? ".");
|
|
3794
4154
|
await runUpgrade({ projectDir: projectDir2, apply: flags.apply });
|
|
3795
4155
|
return;
|
|
3796
4156
|
}
|
|
@@ -3813,8 +4173,8 @@ async function main() {
|
|
|
3813
4173
|
`Non-interactive: ${options.name} (${options.slug})`,
|
|
3814
4174
|
flags.preset ? `preset=${flags.preset}` : null,
|
|
3815
4175
|
`pm=${options.packageManager}`,
|
|
3816
|
-
`styling=${options.styling} ui=${options.ui} auth=${options.auth} store=${options.store}`,
|
|
3817
|
-
`endpoints=${options.endpoints} seo=${options.seo} i18n=${options.i18n} testing=${options.testing}`
|
|
4176
|
+
`styling=${options.styling} ui=${options.ui}${options.ui === "shadcn" ? `:${options.shadcnSet}` : ""} auth=${options.auth} store=${options.store}`,
|
|
4177
|
+
`deploy=${options.deploy} endpoints=${options.endpoints} seo=${options.seo} i18n=${options.i18n} testing=${options.testing}`
|
|
3818
4178
|
].filter(Boolean).join(" \xB7 ")
|
|
3819
4179
|
);
|
|
3820
4180
|
} else {
|
|
@@ -3822,16 +4182,16 @@ async function main() {
|
|
|
3822
4182
|
options.noInstall = flags.noInstall;
|
|
3823
4183
|
options = applyFlagOverrides(options, flags);
|
|
3824
4184
|
}
|
|
3825
|
-
const warnings = collectOptionWarnings(options);
|
|
4185
|
+
const warnings = collectOptionWarnings(options, { verbose: flags.verbose });
|
|
3826
4186
|
for (const warning of warnings) {
|
|
3827
4187
|
p4.log.warn(warning);
|
|
3828
4188
|
}
|
|
3829
4189
|
const projectDir = await scaffold(options);
|
|
3830
4190
|
const pm = getPackageManager(options.packageManager);
|
|
3831
4191
|
const nextSteps = [
|
|
3832
|
-
`cd ${
|
|
4192
|
+
`cd ${path31.relative(process.cwd(), projectDir) || "."}`,
|
|
3833
4193
|
options.noInstall ? `${pm.id} ${pm.installArgs.filter((a) => a !== "--ignore-workspace").join(" ")}`.trim() : null,
|
|
3834
|
-
options.ui === "shadcn" ? "npx shadcn@latest add input #
|
|
4194
|
+
options.ui === "shadcn" && options.shadcnSet === "minimal" ? "npx shadcn@latest add input # or re-scaffold with --shadcn-set starter" : null,
|
|
3835
4195
|
options.endpoints ? `${pm.runScript("endpoints:generate")} # when API schema is up` : null,
|
|
3836
4196
|
options.testing ? pm.runScript("test") : null,
|
|
3837
4197
|
pm.runScript("dev")
|