create-next-style-guide 0.1.6 → 0.1.8

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 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 path31 from "path";
6
+ import path32 from "path";
7
7
 
8
8
  // src/constants.ts
9
9
  import path from "path";
@@ -59,6 +59,10 @@ function ciInstallCommand(id) {
59
59
  }
60
60
  }
61
61
 
62
+ // src/helpers/project-target.ts
63
+ import fs from "fs-extra";
64
+ import path2 from "path";
65
+
62
66
  // src/helpers/slugify.ts
63
67
  function slugify(name) {
64
68
  const slug = name.trim().toLowerCase().replace(/[\s.-]+/g, "_").replace(/[^a-z0-9_]/g, "").replace(/_+/g, "_").replace(/^_|_$/g, "");
@@ -71,6 +75,60 @@ function slugify(name) {
71
75
  return slug;
72
76
  }
73
77
 
78
+ // src/helpers/project-target.ts
79
+ var IGNORABLE_ENTRIES = /* @__PURE__ */ new Set([
80
+ ".DS_Store",
81
+ ".git",
82
+ ".gitattributes",
83
+ ".gitignore",
84
+ ".gitlab-ci.yml",
85
+ ".hg",
86
+ ".hgcheck",
87
+ ".hgignore",
88
+ ".idea",
89
+ ".npmignore",
90
+ ".travis.yml",
91
+ ".vscode",
92
+ "LICENSE",
93
+ "LICENSE.md",
94
+ "LICENSE.txt",
95
+ "README.md",
96
+ "Thumbs.db"
97
+ ]);
98
+ function isInPlaceTarget(target) {
99
+ const t = target.trim();
100
+ return t === "." || t === "./";
101
+ }
102
+ function resolveProjectTarget(target, cwd = process.cwd()) {
103
+ const trimmed = target.trim();
104
+ if (!trimmed) {
105
+ throw new Error("Project name is required.");
106
+ }
107
+ const inPlace = isInPlaceTarget(trimmed);
108
+ const outDir = path2.resolve(cwd, inPlace ? "." : trimmed);
109
+ const base = path2.basename(outDir);
110
+ const name = inPlace && (!base || base === "." || base === path2.sep) ? "app" : inPlace ? base : trimmed;
111
+ return {
112
+ outDir,
113
+ name,
114
+ slug: slugify(name),
115
+ inPlace
116
+ };
117
+ }
118
+ async function assertDirScaffoldable(outDir) {
119
+ if (!await fs.pathExists(outDir)) return;
120
+ const entries = await fs.readdir(outDir);
121
+ const blockers = entries.filter((entry) => !IGNORABLE_ENTRIES.has(entry));
122
+ if (blockers.length === 0) return;
123
+ throw new Error(
124
+ [
125
+ `Target directory is not empty: ${outDir}`,
126
+ `Conflicting entries: ${blockers.slice(0, 8).join(", ")}${blockers.length > 8 ? ", \u2026" : ""}`,
127
+ `Use an empty folder, or run with \`.\` only in a fresh directory (".git" / README / LICENSE are OK).`
128
+ ].join("\n")
129
+ );
130
+ }
131
+
74
132
  // src/presets.ts
75
133
  var PRESET_SEEDS = {
76
134
  minimal: {
@@ -524,23 +582,27 @@ async function promptCustomOptions(base) {
524
582
  }
525
583
  async function runPrompts(initialName) {
526
584
  p.intro(color.bgCyan(color.black(` ${TITLE} `)));
527
- const name = assertNotCancel(
585
+ const rawName = assertNotCancel(
528
586
  await p.text({
529
- message: "Project name?",
587
+ message: "Project name? (use . for current directory)",
530
588
  placeholder: "my-app",
531
589
  initialValue: initialName ?? "my-app",
532
590
  validate(value) {
533
591
  const next = value ?? "";
534
592
  if (!next.trim()) return "Name is required.";
535
593
  try {
536
- slugify(next);
594
+ resolveProjectTarget(next);
537
595
  } catch (error) {
538
596
  return error instanceof Error ? error.message : "Invalid name.";
539
597
  }
540
598
  }
541
599
  })
542
600
  );
543
- const slug = slugify(name);
601
+ const target = resolveProjectTarget(rawName);
602
+ const { name, slug, outDir } = target;
603
+ if (target.inPlace) {
604
+ p.log.info(`Scaffolding into current directory \u2192 ${outDir}`);
605
+ }
544
606
  const packageManager = assertNotCancel(
545
607
  await p.select({
546
608
  message: "Package manager?",
@@ -603,6 +665,7 @@ async function runPrompts(initialName) {
603
665
  const draft = normalizeOptions({
604
666
  name: name.trim(),
605
667
  slug,
668
+ outDir,
606
669
  ...optionSeed,
607
670
  packageManager,
608
671
  noInstall: false
@@ -613,7 +676,7 @@ async function runPrompts(initialName) {
613
676
  }
614
677
  const confirmed = assertNotCancel(
615
678
  await p.confirm({
616
- message: `Create ${color.cyan(name)} (${color.dim(slug)})?`,
679
+ message: target.inPlace ? `Create ${color.cyan(name)} (${color.dim(slug)}) in ${color.dim(".")}?` : `Create ${color.cyan(name)} (${color.dim(slug)})?`,
617
680
  initialValue: true
618
681
  })
619
682
  );
@@ -625,8 +688,8 @@ async function runPrompts(initialName) {
625
688
  }
626
689
 
627
690
  // src/scaffold.ts
628
- import fs28 from "fs-extra";
629
- import path29 from "path";
691
+ import fs29 from "fs-extra";
692
+ import path30 from "path";
630
693
  import { execFileSync } from "child_process";
631
694
  import * as p2 from "@clack/prompts";
632
695
  import color2 from "picocolors";
@@ -656,8 +719,8 @@ function yieldUi() {
656
719
  }
657
720
 
658
721
  // src/helpers/replace-tokens.ts
659
- import fs from "fs-extra";
660
- import path2 from "path";
722
+ import fs2 from "fs-extra";
723
+ import path3 from "path";
661
724
  var TEXT_EXT = /* @__PURE__ */ new Set([
662
725
  ".ts",
663
726
  ".tsx",
@@ -681,9 +744,9 @@ var TEXT_EXT = /* @__PURE__ */ new Set([
681
744
  ".npmrc"
682
745
  ]);
683
746
  function shouldRewrite(filePath) {
684
- const base = path2.basename(filePath);
747
+ const base = path3.basename(filePath);
685
748
  if (base === ".env.example" || base === "LICENSE") return true;
686
- const ext = path2.extname(filePath);
749
+ const ext = path3.extname(filePath);
687
750
  return TEXT_EXT.has(ext) || base.startsWith(".");
688
751
  }
689
752
  function applyTokens(contents, tokens) {
@@ -694,26 +757,26 @@ function applyTokens(contents, tokens) {
694
757
  return out;
695
758
  }
696
759
  async function replaceTokensInTree(root, tokens) {
697
- const entries = await fs.readdir(root, { withFileTypes: true });
760
+ const entries = await fs2.readdir(root, { withFileTypes: true });
698
761
  for (const entry of entries) {
699
- const full = path2.join(root, entry.name);
762
+ const full = path3.join(root, entry.name);
700
763
  if (entry.isDirectory()) {
701
764
  if (entry.name === "node_modules" || entry.name === ".git") continue;
702
765
  await replaceTokensInTree(full, tokens);
703
766
  continue;
704
767
  }
705
768
  if (!shouldRewrite(full)) continue;
706
- const raw = await fs.readFile(full, "utf8");
769
+ const raw = await fs2.readFile(full, "utf8");
707
770
  const next = applyTokens(raw, tokens);
708
771
  if (next !== raw) {
709
- await fs.writeFile(full, next, "utf8");
772
+ await fs2.writeFile(full, next, "utf8");
710
773
  }
711
774
  }
712
775
  }
713
776
 
714
777
  // src/installers/api-envelope.ts
715
- import fs2 from "fs-extra";
716
- import path3 from "path";
778
+ import fs3 from "fs-extra";
779
+ import path4 from "path";
717
780
  var HTTP_ENVELOPE = `import { env } from "@/lib/env";
718
781
 
719
782
  type ApiEnvelope<T> = {
@@ -775,18 +838,18 @@ var apiEnvelopeInstaller = {
775
838
  id: "api-envelope",
776
839
  active: (o) => o.apiEnvelope,
777
840
  async run({ projectDir, options }) {
778
- await fs2.writeFile(
779
- path3.join(projectDir, "src", "lib", "http.ts"),
841
+ await fs3.writeFile(
842
+ path4.join(projectDir, "src", "lib", "http.ts"),
780
843
  HTTP_ENVELOPE,
781
844
  "utf8"
782
845
  );
783
- await fs2.writeFile(
784
- path3.join(projectDir, ".cursor", "rules", "api-envelope.mdc"),
846
+ await fs3.writeFile(
847
+ path4.join(projectDir, ".cursor", "rules", "api-envelope.mdc"),
785
848
  RULE,
786
849
  "utf8"
787
850
  );
788
- await fs2.outputFile(
789
- path3.join(projectDir, "docs", "style-guide", "http", "envelope.md"),
851
+ await fs3.outputFile(
852
+ path4.join(projectDir, "docs", "style-guide", "http", "envelope.md"),
790
853
  `# API envelope
791
854
 
792
855
  \`src/lib/http.ts\` \u2192 \`apiFetch\` unwraps \`result\` from standard envelopes.
@@ -797,8 +860,8 @@ ${options.endpoints ? "Endpoints/axios: use `unwrapApiResult` from `@/utils/api-
797
860
  };
798
861
 
799
862
  // src/helpers/app-providers.ts
800
- import fs3 from "fs-extra";
801
- import path4 from "path";
863
+ import fs4 from "fs-extra";
864
+ import path5 from "path";
802
865
  function needsAppProviders(options) {
803
866
  return options.data === "tanstack-query" || options.store === "redux" || options.styling === "mui" || options.i18n || options.darkMode;
804
867
  }
@@ -868,17 +931,17 @@ ${closers.join("\n")}
868
931
  }
869
932
  async function writeAppProviders(projectDir, options) {
870
933
  if (!needsAppProviders(options)) return;
871
- const providersDir = path4.join(projectDir, "src", "providers");
872
- await fs3.ensureDir(providersDir);
873
- await fs3.remove(path4.join(providersDir, ".gitkeep"));
874
- await fs3.writeFile(
875
- path4.join(providersDir, "app-providers.tsx"),
934
+ const providersDir = path5.join(projectDir, "src", "providers");
935
+ await fs4.ensureDir(providersDir);
936
+ await fs4.remove(path5.join(providersDir, ".gitkeep"));
937
+ await fs4.writeFile(
938
+ path5.join(providersDir, "app-providers.tsx"),
876
939
  generateAppProvidersSource(options),
877
940
  "utf8"
878
941
  );
879
942
  }
880
943
  async function wireRootLayout(projectDir, options) {
881
- const layoutPath = path4.join(projectDir, "src", "app", "layout.tsx");
944
+ const layoutPath = path5.join(projectDir, "src", "app", "layout.tsx");
882
945
  const useProviders = needsAppProviders(options);
883
946
  const dark = options.darkMode;
884
947
  const i18n = options.i18n;
@@ -937,8 +1000,8 @@ ${fnBody}
937
1000
  }
938
1001
  `;
939
1002
  let final = source;
940
- if (await fs3.pathExists(layoutPath)) {
941
- const existing = await fs3.readFile(layoutPath, "utf8");
1003
+ if (await fs4.pathExists(layoutPath)) {
1004
+ const existing = await fs4.readFile(layoutPath, "utf8");
942
1005
  const titleMatch = existing.match(/title:\s*"([^"]+)"/);
943
1006
  if (titleMatch?.[1] && titleMatch[1] !== "__PROJECT_NAME__") {
944
1007
  final = final.replace(
@@ -947,7 +1010,7 @@ ${fnBody}
947
1010
  );
948
1011
  }
949
1012
  }
950
- await fs3.writeFile(layoutPath, final, "utf8");
1013
+ await fs4.writeFile(layoutPath, final, "utf8");
951
1014
  }
952
1015
 
953
1016
  // src/installers/app-providers.ts
@@ -961,15 +1024,15 @@ var appProvidersInstaller = {
961
1024
  };
962
1025
 
963
1026
  // src/installers/auth-authjs.ts
964
- import fs5 from "fs-extra";
965
- import path6 from "path";
1027
+ import fs6 from "fs-extra";
1028
+ import path7 from "path";
966
1029
 
967
1030
  // src/helpers/package-json.ts
968
- import fs4 from "fs-extra";
969
- import path5 from "path";
1031
+ import fs5 from "fs-extra";
1032
+ import path6 from "path";
970
1033
  async function readPkg(projectDir) {
971
- const pkgPath = path5.join(projectDir, "package.json");
972
- const pkg = await fs4.readJson(pkgPath);
1034
+ const pkgPath = path6.join(projectDir, "package.json");
1035
+ const pkg = await fs5.readJson(pkgPath);
973
1036
  return { pkgPath, pkg };
974
1037
  }
975
1038
  function sortRecord(record) {
@@ -983,7 +1046,7 @@ async function addDependencies(projectDir, deps, kind = "dependencies") {
983
1046
  ...pkg[kind] ?? {},
984
1047
  ...deps
985
1048
  });
986
- await fs4.writeJson(pkgPath, pkg, { spaces: 2 });
1049
+ await fs5.writeJson(pkgPath, pkg, { spaces: 2 });
987
1050
  }
988
1051
  async function addScripts(projectDir, scripts) {
989
1052
  const { pkgPath, pkg } = await readPkg(projectDir);
@@ -991,12 +1054,12 @@ async function addScripts(projectDir, scripts) {
991
1054
  ...pkg.scripts ?? {},
992
1055
  ...scripts
993
1056
  };
994
- await fs4.writeJson(pkgPath, pkg, { spaces: 2 });
1057
+ await fs5.writeJson(pkgPath, pkg, { spaces: 2 });
995
1058
  }
996
1059
  async function mergePackageField(projectDir, field, value) {
997
1060
  const { pkgPath, pkg } = await readPkg(projectDir);
998
1061
  pkg[field] = value;
999
- await fs4.writeJson(pkgPath, pkg, { spaces: 2 });
1062
+ await fs5.writeJson(pkgPath, pkg, { spaces: 2 });
1000
1063
  }
1001
1064
  async function removeDependencies(projectDir, names, kind = "dependencies") {
1002
1065
  const { pkgPath, pkg } = await readPkg(projectDir);
@@ -1005,7 +1068,7 @@ async function removeDependencies(projectDir, names, kind = "dependencies") {
1005
1068
  delete pkg[kind][name];
1006
1069
  }
1007
1070
  pkg[kind] = sortRecord(pkg[kind]);
1008
- await fs4.writeJson(pkgPath, pkg, { spaces: 2 });
1071
+ await fs5.writeJson(pkgPath, pkg, { spaces: 2 });
1009
1072
  }
1010
1073
 
1011
1074
  // src/installers/auth-authjs.ts
@@ -1166,13 +1229,13 @@ var authAuthjsInstaller = {
1166
1229
  id: "auth-authjs",
1167
1230
  active: (o) => o.auth === "authjs",
1168
1231
  async run({ projectDir }) {
1169
- await fs5.writeFile(
1170
- path6.join(projectDir, "src", "lib", "auth.ts"),
1232
+ await fs6.writeFile(
1233
+ path7.join(projectDir, "src", "lib", "auth.ts"),
1171
1234
  AUTH_TS,
1172
1235
  "utf8"
1173
1236
  );
1174
- await fs5.outputFile(
1175
- path6.join(
1237
+ await fs6.outputFile(
1238
+ path7.join(
1176
1239
  projectDir,
1177
1240
  "src",
1178
1241
  "app",
@@ -1183,17 +1246,17 @@ var authAuthjsInstaller = {
1183
1246
  ),
1184
1247
  ROUTE
1185
1248
  );
1186
- await fs5.writeFile(
1187
- path6.join(projectDir, "src", "middleware.ts"),
1249
+ await fs6.writeFile(
1250
+ path7.join(projectDir, "src", "middleware.ts"),
1188
1251
  MIDDLEWARE,
1189
1252
  "utf8"
1190
1253
  );
1191
- await fs5.outputFile(
1192
- path6.join(projectDir, "src", "app", "login", "page.tsx"),
1254
+ await fs6.outputFile(
1255
+ path7.join(projectDir, "src", "app", "login", "page.tsx"),
1193
1256
  LOGIN_PAGE
1194
1257
  );
1195
- await fs5.outputFile(
1196
- path6.join(
1258
+ await fs6.outputFile(
1259
+ path7.join(
1197
1260
  projectDir,
1198
1261
  "src",
1199
1262
  "app",
@@ -1204,13 +1267,13 @@ var authAuthjsInstaller = {
1204
1267
  ),
1205
1268
  LOGIN_FORM
1206
1269
  );
1207
- await fs5.writeFile(
1208
- path6.join(projectDir, ".cursor", "rules", "auth.mdc"),
1270
+ await fs6.writeFile(
1271
+ path7.join(projectDir, ".cursor", "rules", "auth.mdc"),
1209
1272
  RULE2,
1210
1273
  "utf8"
1211
1274
  );
1212
- await fs5.outputFile(
1213
- path6.join(projectDir, "docs", "style-guide", "auth", "authjs.md"),
1275
+ await fs6.outputFile(
1276
+ path7.join(projectDir, "docs", "style-guide", "auth", "authjs.md"),
1214
1277
  `# Auth.js
1215
1278
 
1216
1279
  - \`src/lib/auth.ts\` \u2014 Credentials demo (replace \`authorize\`)
@@ -1220,15 +1283,15 @@ var authAuthjsInstaller = {
1220
1283
  - Env: \`AUTH_SECRET\`
1221
1284
  `
1222
1285
  );
1223
- const envPath = path6.join(projectDir, ".env.example");
1224
- if (await fs5.pathExists(envPath)) {
1225
- let env = await fs5.readFile(envPath, "utf8");
1286
+ const envPath = path7.join(projectDir, ".env.example");
1287
+ if (await fs6.pathExists(envPath)) {
1288
+ let env = await fs6.readFile(envPath, "utf8");
1226
1289
  if (!env.includes("AUTH_SECRET")) {
1227
1290
  env += `
1228
1291
  # Auth.js \u2014 generate with: openssl rand -base64 32
1229
1292
  AUTH_SECRET=
1230
1293
  `;
1231
- await fs5.writeFile(envPath, env, "utf8");
1294
+ await fs6.writeFile(envPath, env, "utf8");
1232
1295
  }
1233
1296
  }
1234
1297
  await addDependencies(projectDir, {
@@ -1238,8 +1301,8 @@ AUTH_SECRET=
1238
1301
  };
1239
1302
 
1240
1303
  // src/installers/auth-jwt.ts
1241
- import fs6 from "fs-extra";
1242
- import path7 from "path";
1304
+ import fs7 from "fs-extra";
1305
+ import path8 from "path";
1243
1306
  function tokenStorage(slug) {
1244
1307
  const accessKey = `${slug}.accessToken`;
1245
1308
  const refreshKey = `${slug}.refreshToken`;
@@ -1392,27 +1455,27 @@ var authJwtInstaller = {
1392
1455
  id: "auth-jwt",
1393
1456
  active: (o) => o.auth === "jwt",
1394
1457
  async run({ projectDir, options }) {
1395
- await fs6.ensureDir(path7.join(projectDir, "src", "utils"));
1396
- await fs6.remove(path7.join(projectDir, "src", "utils", ".gitkeep"));
1397
- await fs6.writeFile(
1398
- path7.join(projectDir, "src", "utils", "token-storage.ts"),
1458
+ await fs7.ensureDir(path8.join(projectDir, "src", "utils"));
1459
+ await fs7.remove(path8.join(projectDir, "src", "utils", ".gitkeep"));
1460
+ await fs7.writeFile(
1461
+ path8.join(projectDir, "src", "utils", "token-storage.ts"),
1399
1462
  tokenStorage(options.slug),
1400
1463
  "utf8"
1401
1464
  );
1402
1465
  if (options.endpoints) {
1403
- await fs6.writeFile(
1404
- path7.join(projectDir, "src", "utils", "axios-instance.ts"),
1466
+ await fs7.writeFile(
1467
+ path8.join(projectDir, "src", "utils", "axios-instance.ts"),
1405
1468
  AXIOS_JWT,
1406
1469
  "utf8"
1407
1470
  );
1408
1471
  }
1409
- await fs6.writeFile(
1410
- path7.join(projectDir, ".cursor", "rules", "auth.mdc"),
1472
+ await fs7.writeFile(
1473
+ path8.join(projectDir, ".cursor", "rules", "auth.mdc"),
1411
1474
  RULE3,
1412
1475
  "utf8"
1413
1476
  );
1414
- await fs6.outputFile(
1415
- path7.join(projectDir, "docs", "style-guide", "auth", "jwt.md"),
1477
+ await fs7.outputFile(
1478
+ path8.join(projectDir, "docs", "style-guide", "auth", "jwt.md"),
1416
1479
  `# JWT auth
1417
1480
 
1418
1481
  - \`src/utils/token-storage.ts\` \u2014 access/refresh in localStorage
@@ -1423,8 +1486,8 @@ var authJwtInstaller = {
1423
1486
  };
1424
1487
 
1425
1488
  // src/installers/ci.ts
1426
- import fs7 from "fs-extra";
1427
- import path8 from "path";
1489
+ import fs8 from "fs-extra";
1490
+ import path9 from "path";
1428
1491
  function workflowYaml(options) {
1429
1492
  const pm = getPackageManager(options.packageManager);
1430
1493
  const run = (script) => pm.runScript(script);
@@ -1502,25 +1565,25 @@ var ciInstaller = {
1502
1565
  id: "ci",
1503
1566
  active: (o) => o.ci,
1504
1567
  async run({ projectDir, options }) {
1505
- const addon = path8.join(ADDONS_DIR, "ci");
1506
- await fs7.outputFile(
1507
- path8.join(projectDir, ".github", "workflows", "ci.yml"),
1568
+ const addon = path9.join(ADDONS_DIR, "ci");
1569
+ await fs8.outputFile(
1570
+ path9.join(projectDir, ".github", "workflows", "ci.yml"),
1508
1571
  workflowYaml(options)
1509
1572
  );
1510
- await fs7.copy(
1511
- path8.join(addon, ".cursor", "rules", "ci.mdc"),
1512
- path8.join(projectDir, ".cursor", "rules", "ci.mdc")
1573
+ await fs8.copy(
1574
+ path9.join(addon, ".cursor", "rules", "ci.mdc"),
1575
+ path9.join(projectDir, ".cursor", "rules", "ci.mdc")
1513
1576
  );
1514
- await fs7.copy(
1515
- path8.join(addon, "docs", "style-guide", "ops", "ci.md"),
1516
- path8.join(projectDir, "docs", "style-guide", "ops", "ci.md")
1577
+ await fs8.copy(
1578
+ path9.join(addon, "docs", "style-guide", "ops", "ci.md"),
1579
+ path9.join(projectDir, "docs", "style-guide", "ops", "ci.md")
1517
1580
  );
1518
1581
  }
1519
1582
  };
1520
1583
 
1521
1584
  // src/installers/dark-mode.ts
1522
- import fs8 from "fs-extra";
1523
- import path9 from "path";
1585
+ import fs9 from "fs-extra";
1586
+ import path10 from "path";
1524
1587
  var THEME_PROVIDER = `"use client";
1525
1588
 
1526
1589
  import { ThemeProvider as NextThemesProvider } from "next-themes";
@@ -1550,30 +1613,30 @@ var darkModeInstaller = {
1550
1613
  id: "dark-mode",
1551
1614
  active: (o) => o.darkMode && o.styling === "tailwind",
1552
1615
  async run({ projectDir }) {
1553
- const providersDir = path9.join(projectDir, "src", "providers");
1554
- await fs8.ensureDir(providersDir);
1555
- await fs8.remove(path9.join(providersDir, ".gitkeep"));
1556
- await fs8.writeFile(
1557
- path9.join(providersDir, "theme-provider.tsx"),
1616
+ const providersDir = path10.join(projectDir, "src", "providers");
1617
+ await fs9.ensureDir(providersDir);
1618
+ await fs9.remove(path10.join(providersDir, ".gitkeep"));
1619
+ await fs9.writeFile(
1620
+ path10.join(providersDir, "theme-provider.tsx"),
1558
1621
  THEME_PROVIDER,
1559
1622
  "utf8"
1560
1623
  );
1561
- await fs8.writeFile(
1562
- path9.join(projectDir, ".cursor", "rules", "dark-mode.mdc"),
1624
+ await fs9.writeFile(
1625
+ path10.join(projectDir, ".cursor", "rules", "dark-mode.mdc"),
1563
1626
  RULE4,
1564
1627
  "utf8"
1565
1628
  );
1566
- await fs8.outputFile(
1567
- path9.join(projectDir, "docs", "style-guide", "ui", "dark-mode.md"),
1629
+ await fs9.outputFile(
1630
+ path10.join(projectDir, "docs", "style-guide", "ui", "dark-mode.md"),
1568
1631
  `# Dark mode
1569
1632
 
1570
1633
  next-themes + Tailwind \`dark:\` variants.
1571
1634
  Provider: \`src/providers/theme-provider.tsx\` (via \`AppProviders\`).
1572
1635
  `
1573
1636
  );
1574
- const globalsPath = path9.join(projectDir, "src", "app", "globals.css");
1575
- if (await fs8.pathExists(globalsPath)) {
1576
- let css = await fs8.readFile(globalsPath, "utf8");
1637
+ const globalsPath = path10.join(projectDir, "src", "app", "globals.css");
1638
+ if (await fs9.pathExists(globalsPath)) {
1639
+ let css = await fs9.readFile(globalsPath, "utf8");
1577
1640
  if (!css.includes("@custom-variant dark")) {
1578
1641
  css = css.replace(
1579
1642
  `@import "tailwindcss";
@@ -1583,7 +1646,7 @@ Provider: \`src/providers/theme-provider.tsx\` (via \`AppProviders\`).
1583
1646
  @custom-variant dark (&:where(.dark, .dark *));
1584
1647
  `
1585
1648
  );
1586
- await fs8.writeFile(globalsPath, css, "utf8");
1649
+ await fs9.writeFile(globalsPath, css, "utf8");
1587
1650
  }
1588
1651
  }
1589
1652
  await addDependencies(projectDir, {
@@ -1593,8 +1656,8 @@ Provider: \`src/providers/theme-provider.tsx\` (via \`AppProviders\`).
1593
1656
  };
1594
1657
 
1595
1658
  // src/installers/docker-compose.ts
1596
- import fs9 from "fs-extra";
1597
- import path10 from "path";
1659
+ import fs10 from "fs-extra";
1660
+ import path11 from "path";
1598
1661
  var COMPOSE = `services:
1599
1662
  api:
1600
1663
  image: nginx:alpine
@@ -1635,22 +1698,22 @@ var dockerComposeInstaller = {
1635
1698
  id: "docker-compose",
1636
1699
  active: (o) => o.auth === "jwt" && o.endpoints,
1637
1700
  async run({ projectDir }) {
1638
- await fs9.writeFile(
1639
- path10.join(projectDir, "docker-compose.yml"),
1701
+ await fs10.writeFile(
1702
+ path11.join(projectDir, "docker-compose.yml"),
1640
1703
  COMPOSE,
1641
1704
  "utf8"
1642
1705
  );
1643
- await fs9.outputFile(
1644
- path10.join(projectDir, "docker", "api-mock", "README.md"),
1706
+ await fs10.outputFile(
1707
+ path11.join(projectDir, "docker", "api-mock", "README.md"),
1645
1708
  MOCK_README
1646
1709
  );
1647
- await fs9.writeFile(
1648
- path10.join(projectDir, ".cursor", "rules", "docker-compose.mdc"),
1710
+ await fs10.writeFile(
1711
+ path11.join(projectDir, ".cursor", "rules", "docker-compose.mdc"),
1649
1712
  RULE5,
1650
1713
  "utf8"
1651
1714
  );
1652
- await fs9.outputFile(
1653
- path10.join(projectDir, "docs", "style-guide", "ops", "docker-compose.md"),
1715
+ await fs10.outputFile(
1716
+ path11.join(projectDir, "docs", "style-guide", "ops", "docker-compose.md"),
1654
1717
  `# Docker Compose (local API)
1655
1718
 
1656
1719
  \`\`\`bash
@@ -1664,8 +1727,8 @@ Stub listens on \`http://localhost:8000\`. Align with \`NEXT_PUBLIC_API_URL\`.
1664
1727
  };
1665
1728
 
1666
1729
  // src/installers/docker.ts
1667
- import fs10 from "fs-extra";
1668
- import path11 from "path";
1730
+ import fs11 from "fs-extra";
1731
+ import path12 from "path";
1669
1732
  var STANDALONE_CONFIG = `import type { NextConfig } from "next";
1670
1733
 
1671
1734
  const nextConfig: NextConfig = {
@@ -1846,33 +1909,33 @@ var dockerInstaller = {
1846
1909
  id: "docker",
1847
1910
  active: (o) => o.deploy === "docker",
1848
1911
  async run({ projectDir, options }) {
1849
- const addon = path11.join(ADDONS_DIR, "docker");
1912
+ const addon = path12.join(ADDONS_DIR, "docker");
1850
1913
  const pm = getPackageManager(options.packageManager);
1851
- await fs10.ensureDir(path11.join(projectDir, "docker"));
1852
- await fs10.writeFile(
1853
- path11.join(projectDir, "docker", "production.Dockerfile"),
1914
+ await fs11.ensureDir(path12.join(projectDir, "docker"));
1915
+ await fs11.writeFile(
1916
+ path12.join(projectDir, "docker", "production.Dockerfile"),
1854
1917
  dockerfileFor(options.packageManager),
1855
1918
  "utf8"
1856
1919
  );
1857
- await fs10.copy(
1858
- path11.join(addon, ".dockerignore"),
1859
- path11.join(projectDir, ".dockerignore")
1920
+ await fs11.copy(
1921
+ path12.join(addon, ".dockerignore"),
1922
+ path12.join(projectDir, ".dockerignore")
1860
1923
  );
1861
- await fs10.copy(
1862
- path11.join(addon, ".cursor", "rules", "docker.mdc"),
1863
- path11.join(projectDir, ".cursor", "rules", "docker.mdc")
1924
+ await fs11.copy(
1925
+ path12.join(addon, ".cursor", "rules", "docker.mdc"),
1926
+ path12.join(projectDir, ".cursor", "rules", "docker.mdc")
1864
1927
  );
1865
- let doc = await fs10.readFile(
1866
- path11.join(addon, "docs", "style-guide", "ops", "docker.md"),
1928
+ let doc = await fs11.readFile(
1929
+ path12.join(addon, "docs", "style-guide", "ops", "docker.md"),
1867
1930
  "utf8"
1868
1931
  );
1869
1932
  doc = doc.replaceAll("__PROJECT_SLUG__", options.slug).replaceAll("__PACKAGE_MANAGER__", pm.id);
1870
- await fs10.outputFile(
1871
- path11.join(projectDir, "docs", "style-guide", "ops", "docker.md"),
1933
+ await fs11.outputFile(
1934
+ path12.join(projectDir, "docs", "style-guide", "ops", "docker.md"),
1872
1935
  doc
1873
1936
  );
1874
- await fs10.writeFile(
1875
- path11.join(projectDir, "next.config.ts"),
1937
+ await fs11.writeFile(
1938
+ path12.join(projectDir, "next.config.ts"),
1876
1939
  STANDALONE_CONFIG,
1877
1940
  "utf8"
1878
1941
  );
@@ -1880,20 +1943,20 @@ var dockerInstaller = {
1880
1943
  };
1881
1944
 
1882
1945
  // src/installers/endpoints.ts
1883
- import fs11 from "fs-extra";
1884
- import path12 from "path";
1946
+ import fs12 from "fs-extra";
1947
+ import path13 from "path";
1885
1948
  var endpointsInstaller = {
1886
1949
  id: "endpoints",
1887
1950
  active: (o) => o.endpoints,
1888
1951
  async run({ projectDir }) {
1889
- const addon = path12.join(ADDONS_DIR, "endpoints");
1890
- await fs11.copy(
1891
- path12.join(addon, "scripts"),
1892
- path12.join(projectDir, "scripts")
1952
+ const addon = path13.join(ADDONS_DIR, "endpoints");
1953
+ await fs12.copy(
1954
+ path13.join(addon, "scripts"),
1955
+ path13.join(projectDir, "scripts")
1893
1956
  );
1894
- await fs11.copy(
1895
- path12.join(addon, "src", "endpoints"),
1896
- path12.join(projectDir, "src", "endpoints")
1957
+ await fs12.copy(
1958
+ path13.join(addon, "src", "endpoints"),
1959
+ path13.join(projectDir, "src", "endpoints")
1897
1960
  );
1898
1961
  for (const file of [
1899
1962
  "axios-instance.ts",
@@ -1902,19 +1965,19 @@ var endpointsInstaller = {
1902
1965
  "search-params.ts",
1903
1966
  "form-data.ts"
1904
1967
  ]) {
1905
- await fs11.copy(
1906
- path12.join(addon, "src", "utils", file),
1907
- path12.join(projectDir, "src", "utils", file)
1968
+ await fs12.copy(
1969
+ path13.join(addon, "src", "utils", file),
1970
+ path13.join(projectDir, "src", "utils", file)
1908
1971
  );
1909
1972
  }
1910
- await fs11.remove(path12.join(projectDir, "src", "utils", ".gitkeep"));
1911
- await fs11.copy(
1912
- path12.join(addon, ".cursor", "rules", "endpoints.mdc"),
1913
- path12.join(projectDir, ".cursor", "rules", "endpoints.mdc")
1973
+ await fs12.remove(path13.join(projectDir, "src", "utils", ".gitkeep"));
1974
+ await fs12.copy(
1975
+ path13.join(addon, ".cursor", "rules", "endpoints.mdc"),
1976
+ path13.join(projectDir, ".cursor", "rules", "endpoints.mdc")
1914
1977
  );
1915
- await fs11.copy(
1916
- path12.join(addon, "docs", "style-guide", "http", "endpoints.md"),
1917
- path12.join(projectDir, "docs", "style-guide", "http", "endpoints.md")
1978
+ await fs12.copy(
1979
+ path13.join(addon, "docs", "style-guide", "http", "endpoints.md"),
1980
+ path13.join(projectDir, "docs", "style-guide", "http", "endpoints.md")
1918
1981
  );
1919
1982
  await addDependencies(projectDir, { axios: "^1.7.9" });
1920
1983
  await addDependencies(
@@ -1929,24 +1992,24 @@ var endpointsInstaller = {
1929
1992
  };
1930
1993
 
1931
1994
  // src/installers/eslint.ts
1932
- import fs12 from "fs-extra";
1933
- import path13 from "path";
1995
+ import fs13 from "fs-extra";
1996
+ import path14 from "path";
1934
1997
  var eslintInstaller = {
1935
1998
  id: "eslint",
1936
1999
  active: (o) => o.eslint,
1937
2000
  async run({ projectDir }) {
1938
- const addon = path13.join(ADDONS_DIR, "eslint");
1939
- await fs12.copy(
1940
- path13.join(addon, "eslint.config.mjs"),
1941
- path13.join(projectDir, "eslint.config.mjs")
2001
+ const addon = path14.join(ADDONS_DIR, "eslint");
2002
+ await fs13.copy(
2003
+ path14.join(addon, "eslint.config.mjs"),
2004
+ path14.join(projectDir, "eslint.config.mjs")
1942
2005
  );
1943
- await fs12.copy(
1944
- path13.join(addon, ".cursor", "rules", "eslint.mdc"),
1945
- path13.join(projectDir, ".cursor", "rules", "eslint.mdc")
2006
+ await fs13.copy(
2007
+ path14.join(addon, ".cursor", "rules", "eslint.mdc"),
2008
+ path14.join(projectDir, ".cursor", "rules", "eslint.mdc")
1946
2009
  );
1947
- await fs12.copy(
1948
- path13.join(addon, "docs", "style-guide", "tooling", "eslint.md"),
1949
- path13.join(projectDir, "docs", "style-guide", "tooling", "eslint.md")
2010
+ await fs13.copy(
2011
+ path14.join(addon, "docs", "style-guide", "tooling", "eslint.md"),
2012
+ path14.join(projectDir, "docs", "style-guide", "tooling", "eslint.md")
1950
2013
  );
1951
2014
  await addDependencies(
1952
2015
  projectDir,
@@ -1965,20 +2028,20 @@ var eslintInstaller = {
1965
2028
  };
1966
2029
 
1967
2030
  // src/installers/husky.ts
1968
- import fs13 from "fs-extra";
1969
- import path14 from "path";
2031
+ import fs14 from "fs-extra";
2032
+ import path15 from "path";
1970
2033
  var huskyInstaller = {
1971
2034
  id: "husky",
1972
2035
  active: (o) => o.husky,
1973
2036
  async run({ projectDir, options }) {
1974
- const addon = path14.join(ADDONS_DIR, "husky");
1975
- await fs13.copy(path14.join(addon, ".husky"), path14.join(projectDir, ".husky"));
1976
- await fs13.copy(
1977
- path14.join(addon, "docs", "style-guide", "tooling", "husky.md"),
1978
- path14.join(projectDir, "docs", "style-guide", "tooling", "husky.md")
2037
+ const addon = path15.join(ADDONS_DIR, "husky");
2038
+ await fs14.copy(path15.join(addon, ".husky"), path15.join(projectDir, ".husky"));
2039
+ await fs14.copy(
2040
+ path15.join(addon, "docs", "style-guide", "tooling", "husky.md"),
2041
+ path15.join(projectDir, "docs", "style-guide", "tooling", "husky.md")
1979
2042
  );
1980
2043
  try {
1981
- await fs13.chmod(path14.join(projectDir, ".husky", "pre-commit"), 493);
2044
+ await fs14.chmod(path15.join(projectDir, ".husky", "pre-commit"), 493);
1982
2045
  } catch {
1983
2046
  }
1984
2047
  await addDependencies(
@@ -2005,8 +2068,8 @@ var huskyInstaller = {
2005
2068
  };
2006
2069
 
2007
2070
  // src/installers/i18n.ts
2008
- import fs14 from "fs-extra";
2009
- import path15 from "path";
2071
+ import fs15 from "fs-extra";
2072
+ import path16 from "path";
2010
2073
  var I18N_REQUEST = `import { getRequestConfig } from "next-intl/server";
2011
2074
 
2012
2075
  export default getRequestConfig(async () => {
@@ -2041,30 +2104,30 @@ var i18nInstaller = {
2041
2104
  id: "i18n",
2042
2105
  active: (o) => o.i18n,
2043
2106
  async run({ projectDir }) {
2044
- await fs14.outputFile(
2045
- path15.join(projectDir, "src", "i18n", "request.ts"),
2107
+ await fs15.outputFile(
2108
+ path16.join(projectDir, "src", "i18n", "request.ts"),
2046
2109
  I18N_REQUEST
2047
2110
  );
2048
- await fs14.outputFile(
2049
- path15.join(projectDir, "messages", "en.json"),
2111
+ await fs15.outputFile(
2112
+ path16.join(projectDir, "messages", "en.json"),
2050
2113
  MESSAGES_EN
2051
2114
  );
2052
- await fs14.writeFile(
2053
- path15.join(projectDir, ".cursor", "rules", "i18n.mdc"),
2115
+ await fs15.writeFile(
2116
+ path16.join(projectDir, ".cursor", "rules", "i18n.mdc"),
2054
2117
  RULE6,
2055
2118
  "utf8"
2056
2119
  );
2057
- await fs14.outputFile(
2058
- path15.join(projectDir, "docs", "style-guide", "i18n", "overview.md"),
2120
+ await fs15.outputFile(
2121
+ path16.join(projectDir, "docs", "style-guide", "i18n", "overview.md"),
2059
2122
  `# i18n
2060
2123
 
2061
2124
  next-intl: \`src/i18n/request.ts\` + \`messages/en.json\`
2062
2125
  Plugin wired in \`next.config.ts\`. Provider via \`AppProviders\`.
2063
2126
  `
2064
2127
  );
2065
- const nextConfigPath = path15.join(projectDir, "next.config.ts");
2066
- if (await fs14.pathExists(nextConfigPath)) {
2067
- let nextConfig = await fs14.readFile(nextConfigPath, "utf8");
2128
+ const nextConfigPath = path16.join(projectDir, "next.config.ts");
2129
+ if (await fs15.pathExists(nextConfigPath)) {
2130
+ let nextConfig = await fs15.readFile(nextConfigPath, "utf8");
2068
2131
  if (!nextConfig.includes("next-intl")) {
2069
2132
  if (!nextConfig.includes("createNextIntlPlugin")) {
2070
2133
  nextConfig = `import createNextIntlPlugin from "next-intl/plugin";
@@ -2077,7 +2140,7 @@ ${nextConfig}`;
2077
2140
  "export default nextConfig;",
2078
2141
  "export default withNextIntl(nextConfig);"
2079
2142
  );
2080
- await fs14.writeFile(nextConfigPath, nextConfig, "utf8");
2143
+ await fs15.writeFile(nextConfigPath, nextConfig, "utf8");
2081
2144
  }
2082
2145
  }
2083
2146
  await addDependencies(projectDir, {
@@ -2087,8 +2150,8 @@ ${nextConfig}`;
2087
2150
  };
2088
2151
 
2089
2152
  // src/installers/icons.ts
2090
- import fs15 from "fs-extra";
2091
- import path16 from "path";
2153
+ import fs16 from "fs-extra";
2154
+ import path17 from "path";
2092
2155
  var RULE7 = `---
2093
2156
  description: Icon library conventions
2094
2157
  globs: "src/**/*.{ts,tsx}"
@@ -2112,10 +2175,10 @@ var iconsInstaller = {
2112
2175
  if (options.icons === "none") return;
2113
2176
  const pkg = ICON_PACKAGES[options.icons];
2114
2177
  await addDependencies(projectDir, { [pkg.name]: pkg.version });
2115
- const ruleDir = path16.join(projectDir, ".cursor", "rules");
2116
- await fs15.ensureDir(ruleDir);
2117
- await fs15.writeFile(
2118
- path16.join(ruleDir, "icons.mdc"),
2178
+ const ruleDir = path17.join(projectDir, ".cursor", "rules");
2179
+ await fs16.ensureDir(ruleDir);
2180
+ await fs16.writeFile(
2181
+ path17.join(ruleDir, "icons.mdc"),
2119
2182
  RULE7.replace("__ICONS_BODY__", BODIES[options.icons]),
2120
2183
  "utf8"
2121
2184
  );
@@ -2123,8 +2186,8 @@ var iconsInstaller = {
2123
2186
  };
2124
2187
 
2125
2188
  // src/installers/mui.ts
2126
- import fs16 from "fs-extra";
2127
- import path17 from "path";
2189
+ import fs17 from "fs-extra";
2190
+ import path18 from "path";
2128
2191
  var THEME_PROVIDER2 = `"use client";
2129
2192
 
2130
2193
  import { type ReactNode } from "react";
@@ -2168,16 +2231,16 @@ var muiInstaller = {
2168
2231
  id: "mui",
2169
2232
  active: (o) => o.styling === "mui",
2170
2233
  async run({ projectDir }) {
2171
- const providersDir = path17.join(projectDir, "src", "providers");
2172
- await fs16.ensureDir(providersDir);
2173
- await fs16.remove(path17.join(providersDir, ".gitkeep"));
2174
- await fs16.writeFile(
2175
- path17.join(providersDir, "mui-provider.tsx"),
2234
+ const providersDir = path18.join(projectDir, "src", "providers");
2235
+ await fs17.ensureDir(providersDir);
2236
+ await fs17.remove(path18.join(providersDir, ".gitkeep"));
2237
+ await fs17.writeFile(
2238
+ path18.join(providersDir, "mui-provider.tsx"),
2176
2239
  THEME_PROVIDER2,
2177
2240
  "utf8"
2178
2241
  );
2179
- await fs16.writeFile(
2180
- path17.join(projectDir, ".cursor", "rules", "mui.mdc"),
2242
+ await fs17.writeFile(
2243
+ path18.join(projectDir, ".cursor", "rules", "mui.mdc"),
2181
2244
  RULE8,
2182
2245
  "utf8"
2183
2246
  );
@@ -2192,43 +2255,43 @@ var muiInstaller = {
2192
2255
  };
2193
2256
 
2194
2257
  // src/installers/package-manager.ts
2195
- import fs17 from "fs-extra";
2196
- import path18 from "path";
2258
+ import fs18 from "fs-extra";
2259
+ import path19 from "path";
2197
2260
  var packageManagerInstaller = {
2198
2261
  id: "package-manager",
2199
2262
  active: () => true,
2200
2263
  async run({ projectDir, options }) {
2201
2264
  const meta = getPackageManager(options.packageManager);
2202
- const pkgPath = path18.join(projectDir, "package.json");
2203
- const pkg = await fs17.readJson(pkgPath);
2265
+ const pkgPath = path19.join(projectDir, "package.json");
2266
+ const pkg = await fs18.readJson(pkgPath);
2204
2267
  if (meta.packageManagerField) {
2205
2268
  pkg.packageManager = meta.packageManagerField;
2206
2269
  } else {
2207
2270
  delete pkg.packageManager;
2208
2271
  }
2209
- await fs17.writeJson(pkgPath, pkg, { spaces: 2 });
2272
+ await fs18.writeJson(pkgPath, pkg, { spaces: 2 });
2210
2273
  }
2211
2274
  };
2212
2275
 
2213
2276
  // src/installers/prettier.ts
2214
- import fs18 from "fs-extra";
2215
- import path19 from "path";
2277
+ import fs19 from "fs-extra";
2278
+ import path20 from "path";
2216
2279
  var prettierInstaller = {
2217
2280
  id: "prettier",
2218
2281
  active: (o) => o.prettier,
2219
2282
  async run({ projectDir }) {
2220
- const addon = path19.join(ADDONS_DIR, "prettier");
2221
- await fs18.copy(
2222
- path19.join(addon, ".prettierrc"),
2223
- path19.join(projectDir, ".prettierrc")
2283
+ const addon = path20.join(ADDONS_DIR, "prettier");
2284
+ await fs19.copy(
2285
+ path20.join(addon, ".prettierrc"),
2286
+ path20.join(projectDir, ".prettierrc")
2224
2287
  );
2225
- await fs18.copy(
2226
- path19.join(addon, ".cursor", "rules", "prettier.mdc"),
2227
- path19.join(projectDir, ".cursor", "rules", "prettier.mdc")
2288
+ await fs19.copy(
2289
+ path20.join(addon, ".cursor", "rules", "prettier.mdc"),
2290
+ path20.join(projectDir, ".cursor", "rules", "prettier.mdc")
2228
2291
  );
2229
- await fs18.copy(
2230
- path19.join(addon, "docs", "style-guide", "tooling", "prettier.md"),
2231
- path19.join(projectDir, "docs", "style-guide", "tooling", "prettier.md")
2292
+ await fs19.copy(
2293
+ path20.join(addon, "docs", "style-guide", "tooling", "prettier.md"),
2294
+ path20.join(projectDir, "docs", "style-guide", "tooling", "prettier.md")
2232
2295
  );
2233
2296
  await addDependencies(
2234
2297
  projectDir,
@@ -2243,8 +2306,8 @@ var prettierInstaller = {
2243
2306
  };
2244
2307
 
2245
2308
  // src/installers/seo.ts
2246
- import fs19 from "fs-extra";
2247
- import path20 from "path";
2309
+ import fs20 from "fs-extra";
2310
+ import path21 from "path";
2248
2311
  var SEO_TS = `import type { Metadata } from "next";
2249
2312
 
2250
2313
  import { env } from "@/lib/env";
@@ -2393,29 +2456,29 @@ var seoInstaller = {
2393
2456
  active: (o) => o.seo,
2394
2457
  async run({ projectDir, options }) {
2395
2458
  let seo = SEO_TS.replaceAll("__PROJECT_NAME__", options.name);
2396
- await fs19.writeFile(path20.join(projectDir, "src", "lib", "seo.ts"), seo, "utf8");
2397
- await fs19.writeFile(
2398
- path20.join(projectDir, "src", "lib", "public-routes.ts"),
2459
+ await fs20.writeFile(path21.join(projectDir, "src", "lib", "seo.ts"), seo, "utf8");
2460
+ await fs20.writeFile(
2461
+ path21.join(projectDir, "src", "lib", "public-routes.ts"),
2399
2462
  PUBLIC_ROUTES,
2400
2463
  "utf8"
2401
2464
  );
2402
- await fs19.writeFile(
2403
- path20.join(projectDir, "src", "app", "sitemap.ts"),
2465
+ await fs20.writeFile(
2466
+ path21.join(projectDir, "src", "app", "sitemap.ts"),
2404
2467
  SITEMAP,
2405
2468
  "utf8"
2406
2469
  );
2407
- await fs19.writeFile(
2408
- path20.join(projectDir, "src", "app", "robots.ts"),
2470
+ await fs20.writeFile(
2471
+ path21.join(projectDir, "src", "app", "robots.ts"),
2409
2472
  ROBOTS,
2410
2473
  "utf8"
2411
2474
  );
2412
- await fs19.writeFile(
2413
- path20.join(projectDir, ".cursor", "rules", "seo.mdc"),
2475
+ await fs20.writeFile(
2476
+ path21.join(projectDir, ".cursor", "rules", "seo.mdc"),
2414
2477
  RULE9,
2415
2478
  "utf8"
2416
2479
  );
2417
- await fs19.outputFile(
2418
- path20.join(projectDir, "docs", "style-guide", "seo", "overview.md"),
2480
+ await fs20.outputFile(
2481
+ path21.join(projectDir, "docs", "style-guide", "seo", "overview.md"),
2419
2482
  `# SEO
2420
2483
 
2421
2484
  - Registry: \`src/lib/public-routes.ts\`
@@ -2429,8 +2492,8 @@ Add a page \u2192 register the path \u2192 metadata/sitemap update automatically
2429
2492
  };
2430
2493
 
2431
2494
  // src/installers/shadcn.ts
2432
- import fs20 from "fs-extra";
2433
- import path21 from "path";
2495
+ import fs21 from "fs-extra";
2496
+ import path22 from "path";
2434
2497
  function componentsJson(iconLibrary) {
2435
2498
  return `${JSON.stringify(
2436
2499
  {
@@ -2665,28 +2728,28 @@ var shadcnInstaller = {
2665
2728
  async run({ projectDir, options }) {
2666
2729
  const iconLibrary = options.icons === "tabler" ? "tabler" : "lucide";
2667
2730
  const set = options.shadcnSet;
2668
- const uiDir = path21.join(projectDir, "src", "components", "ui");
2669
- await fs20.writeFile(
2670
- path21.join(projectDir, "components.json"),
2731
+ const uiDir = path22.join(projectDir, "src", "components", "ui");
2732
+ await fs21.writeFile(
2733
+ path22.join(projectDir, "components.json"),
2671
2734
  componentsJson(iconLibrary),
2672
2735
  "utf8"
2673
2736
  );
2674
- await fs20.ensureDir(uiDir);
2675
- await fs20.writeFile(path21.join(uiDir, "button.tsx"), BUTTON, "utf8");
2737
+ await fs21.ensureDir(uiDir);
2738
+ await fs21.writeFile(path22.join(uiDir, "button.tsx"), BUTTON, "utf8");
2676
2739
  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");
2740
+ await fs21.writeFile(path22.join(uiDir, "input.tsx"), INPUT, "utf8");
2741
+ await fs21.writeFile(path22.join(uiDir, "card.tsx"), CARD, "utf8");
2742
+ await fs21.writeFile(path22.join(uiDir, "dialog.tsx"), DIALOG, "utf8");
2680
2743
  }
2681
- await fs20.remove(path21.join(uiDir, ".gitkeep"));
2682
- await fs20.remove(path21.join(projectDir, "src", "components", ".gitkeep"));
2683
- await fs20.writeFile(
2684
- path21.join(projectDir, ".cursor", "rules", "shadcn.mdc"),
2744
+ await fs21.remove(path22.join(uiDir, ".gitkeep"));
2745
+ await fs21.remove(path22.join(projectDir, "src", "components", ".gitkeep"));
2746
+ await fs21.writeFile(
2747
+ path22.join(projectDir, ".cursor", "rules", "shadcn.mdc"),
2685
2748
  ruleFor(set),
2686
2749
  "utf8"
2687
2750
  );
2688
- await fs20.outputFile(
2689
- path21.join(projectDir, "docs", "style-guide", "ui", "shadcn.md"),
2751
+ await fs21.outputFile(
2752
+ path22.join(projectDir, "docs", "style-guide", "ui", "shadcn.md"),
2690
2753
  `# shadcn/ui
2691
2754
 
2692
2755
  Set: **${set}**
@@ -2710,8 +2773,8 @@ Add more: \`npx shadcn@latest add \u2026\`
2710
2773
  };
2711
2774
 
2712
2775
  // src/installers/store.ts
2713
- import fs21 from "fs-extra";
2714
- import path22 from "path";
2776
+ import fs22 from "fs-extra";
2777
+ import path23 from "path";
2715
2778
  var ZUSTAND_STORE = `import { create } from "zustand";
2716
2779
 
2717
2780
  type AppState = {
@@ -2812,21 +2875,21 @@ var storeInstaller = {
2812
2875
  id: "store",
2813
2876
  active: (o) => o.store !== "none",
2814
2877
  async run({ projectDir, options }) {
2815
- const storesDir = path22.join(projectDir, "src", "stores");
2816
- await fs21.ensureDir(storesDir);
2878
+ const storesDir = path23.join(projectDir, "src", "stores");
2879
+ await fs22.ensureDir(storesDir);
2817
2880
  if (options.store === "zustand") {
2818
- await fs21.writeFile(
2819
- path22.join(storesDir, "app-store.ts"),
2881
+ await fs22.writeFile(
2882
+ path23.join(storesDir, "app-store.ts"),
2820
2883
  ZUSTAND_STORE,
2821
2884
  "utf8"
2822
2885
  );
2823
- await fs21.writeFile(
2824
- path22.join(projectDir, ".cursor", "rules", "store.mdc"),
2886
+ await fs22.writeFile(
2887
+ path23.join(projectDir, ".cursor", "rules", "store.mdc"),
2825
2888
  ZUSTAND_RULE,
2826
2889
  "utf8"
2827
2890
  );
2828
- await fs21.outputFile(
2829
- path22.join(projectDir, "docs", "style-guide", "state", "zustand.md"),
2891
+ await fs22.outputFile(
2892
+ path23.join(projectDir, "docs", "style-guide", "state", "zustand.md"),
2830
2893
  `# Zustand
2831
2894
 
2832
2895
  - Store: \`src/stores/app-store.ts\`
@@ -2836,23 +2899,23 @@ var storeInstaller = {
2836
2899
  await addDependencies(projectDir, { zustand: "^5.0.3" });
2837
2900
  return;
2838
2901
  }
2839
- await fs21.writeFile(path22.join(storesDir, "store.ts"), REDUX_STORE, "utf8");
2840
- await fs21.writeFile(path22.join(storesDir, "hooks.ts"), REDUX_HOOKS, "utf8");
2841
- const providersDir = path22.join(projectDir, "src", "providers");
2842
- await fs21.ensureDir(providersDir);
2843
- await fs21.remove(path22.join(providersDir, ".gitkeep"));
2844
- await fs21.writeFile(
2845
- path22.join(providersDir, "store-provider.tsx"),
2902
+ await fs22.writeFile(path23.join(storesDir, "store.ts"), REDUX_STORE, "utf8");
2903
+ await fs22.writeFile(path23.join(storesDir, "hooks.ts"), REDUX_HOOKS, "utf8");
2904
+ const providersDir = path23.join(projectDir, "src", "providers");
2905
+ await fs22.ensureDir(providersDir);
2906
+ await fs22.remove(path23.join(providersDir, ".gitkeep"));
2907
+ await fs22.writeFile(
2908
+ path23.join(providersDir, "store-provider.tsx"),
2846
2909
  REDUX_PROVIDER,
2847
2910
  "utf8"
2848
2911
  );
2849
- await fs21.writeFile(
2850
- path22.join(projectDir, ".cursor", "rules", "store.mdc"),
2912
+ await fs22.writeFile(
2913
+ path23.join(projectDir, ".cursor", "rules", "store.mdc"),
2851
2914
  REDUX_RULE,
2852
2915
  "utf8"
2853
2916
  );
2854
- await fs21.outputFile(
2855
- path22.join(projectDir, "docs", "style-guide", "state", "redux.md"),
2917
+ await fs22.outputFile(
2918
+ path23.join(projectDir, "docs", "style-guide", "state", "redux.md"),
2856
2919
  `# Redux Toolkit
2857
2920
 
2858
2921
  - Store: \`src/stores/store.ts\`
@@ -2868,8 +2931,8 @@ var storeInstaller = {
2868
2931
  };
2869
2932
 
2870
2933
  // src/installers/surface.ts
2871
- import fs22 from "fs-extra";
2872
- import path23 from "path";
2934
+ import fs23 from "fs-extra";
2935
+ import path24 from "path";
2873
2936
  function stackLines(options) {
2874
2937
  const stylingLabel = options.styling === "tailwind" ? options.ui === "shadcn" ? "Tailwind + shadcn/ui" : "Tailwind CSS" : options.styling === "mui" ? "MUI" : "Plain CSS";
2875
2938
  return [
@@ -3054,8 +3117,8 @@ var surfaceInstaller = {
3054
3117
  id: "surface",
3055
3118
  active: () => true,
3056
3119
  async run({ projectDir, options }) {
3057
- await fs22.writeFile(
3058
- path23.join(
3120
+ await fs23.writeFile(
3121
+ path24.join(
3059
3122
  projectDir,
3060
3123
  "src",
3061
3124
  "app",
@@ -3066,15 +3129,15 @@ var surfaceInstaller = {
3066
3129
  welcomeCardSource(options),
3067
3130
  "utf8"
3068
3131
  );
3069
- await fs22.writeFile(
3070
- path23.join(projectDir, "src", "app", "page.tsx"),
3132
+ await fs23.writeFile(
3133
+ path24.join(projectDir, "src", "app", "page.tsx"),
3071
3134
  homePageSource(options),
3072
3135
  "utf8"
3073
3136
  );
3074
3137
  if (options.data === "tanstack-query") {
3075
- await fs22.copy(
3076
- path23.join(ADDONS_DIR, "docs", "data", "tanstack-query.md"),
3077
- path23.join(
3138
+ await fs23.copy(
3139
+ path24.join(ADDONS_DIR, "docs", "data", "tanstack-query.md"),
3140
+ path24.join(
3078
3141
  projectDir,
3079
3142
  "docs",
3080
3143
  "style-guide",
@@ -3084,27 +3147,27 @@ var surfaceInstaller = {
3084
3147
  );
3085
3148
  }
3086
3149
  if (options.icons !== "none") {
3087
- await fs22.copy(
3088
- path23.join(ADDONS_DIR, "docs", "ui", "icons.md"),
3089
- path23.join(projectDir, "docs", "style-guide", "ui", "icons.md")
3150
+ await fs23.copy(
3151
+ path24.join(ADDONS_DIR, "docs", "ui", "icons.md"),
3152
+ path24.join(projectDir, "docs", "style-guide", "ui", "icons.md")
3090
3153
  );
3091
3154
  }
3092
3155
  if (options.auth === "jwt" && options.endpoints) {
3093
- const exampleSrc = path23.join(
3156
+ const exampleSrc = path24.join(
3094
3157
  ADDONS_DIR,
3095
3158
  "docs",
3096
3159
  "examples",
3097
3160
  "api-app-crud.md"
3098
3161
  );
3099
- if (await fs22.pathExists(exampleSrc)) {
3100
- await fs22.copy(
3162
+ if (await fs23.pathExists(exampleSrc)) {
3163
+ await fs23.copy(
3101
3164
  exampleSrc,
3102
- path23.join(projectDir, "docs", "examples", "api-app-crud.md")
3165
+ path24.join(projectDir, "docs", "examples", "api-app-crud.md")
3103
3166
  );
3104
3167
  }
3105
3168
  }
3106
- await fs22.writeFile(
3107
- path23.join(projectDir, "docs", "style-guide", "README.md"),
3169
+ await fs23.writeFile(
3170
+ path24.join(projectDir, "docs", "style-guide", "README.md"),
3108
3171
  docsIndex(options),
3109
3172
  "utf8"
3110
3173
  );
@@ -3112,8 +3175,8 @@ var surfaceInstaller = {
3112
3175
  };
3113
3176
 
3114
3177
  // src/installers/tailwind.ts
3115
- import fs23 from "fs-extra";
3116
- import path24 from "path";
3178
+ import fs24 from "fs-extra";
3179
+ import path25 from "path";
3117
3180
  var POSTCSS = `const config = {
3118
3181
  plugins: {
3119
3182
  "@tailwindcss/postcss": {},
@@ -3239,23 +3302,23 @@ var tailwindInstaller = {
3239
3302
  id: "tailwind",
3240
3303
  active: (o) => o.styling === "tailwind",
3241
3304
  async run({ projectDir }) {
3242
- await fs23.writeFile(
3243
- path24.join(projectDir, "postcss.config.mjs"),
3305
+ await fs24.writeFile(
3306
+ path25.join(projectDir, "postcss.config.mjs"),
3244
3307
  POSTCSS,
3245
3308
  "utf8"
3246
3309
  );
3247
- await fs23.writeFile(
3248
- path24.join(projectDir, "src", "app", "globals.css"),
3310
+ await fs24.writeFile(
3311
+ path25.join(projectDir, "src", "app", "globals.css"),
3249
3312
  GLOBALS,
3250
3313
  "utf8"
3251
3314
  );
3252
- await fs23.writeFile(
3253
- path24.join(projectDir, "src", "lib", "utils.ts"),
3315
+ await fs24.writeFile(
3316
+ path25.join(projectDir, "src", "lib", "utils.ts"),
3254
3317
  UTILS,
3255
3318
  "utf8"
3256
3319
  );
3257
- await fs23.writeFile(
3258
- path24.join(projectDir, ".cursor", "rules", "tailwind.mdc"),
3320
+ await fs24.writeFile(
3321
+ path25.join(projectDir, ".cursor", "rules", "tailwind.mdc"),
3259
3322
  RULE10,
3260
3323
  "utf8"
3261
3324
  );
@@ -3275,8 +3338,8 @@ var tailwindInstaller = {
3275
3338
  };
3276
3339
 
3277
3340
  // src/installers/tanstack-query.ts
3278
- import fs24 from "fs-extra";
3279
- import path25 from "path";
3341
+ import fs25 from "fs-extra";
3342
+ import path26 from "path";
3280
3343
  var PROVIDER = `"use client";
3281
3344
 
3282
3345
  import { type ReactNode, useState } from "react";
@@ -3325,18 +3388,18 @@ var tanstackQueryInstaller = {
3325
3388
  await addDependencies(projectDir, {
3326
3389
  "@tanstack/react-query": "^5.62.8"
3327
3390
  });
3328
- const providersDir = path25.join(projectDir, "src", "providers");
3329
- await fs24.ensureDir(providersDir);
3330
- await fs24.remove(path25.join(providersDir, ".gitkeep"));
3331
- await fs24.writeFile(
3332
- path25.join(providersDir, "query-provider.tsx"),
3391
+ const providersDir = path26.join(projectDir, "src", "providers");
3392
+ await fs25.ensureDir(providersDir);
3393
+ await fs25.remove(path26.join(providersDir, ".gitkeep"));
3394
+ await fs25.writeFile(
3395
+ path26.join(providersDir, "query-provider.tsx"),
3333
3396
  PROVIDER,
3334
3397
  "utf8"
3335
3398
  );
3336
- const ruleDir = path25.join(projectDir, ".cursor", "rules");
3337
- await fs24.ensureDir(ruleDir);
3338
- await fs24.writeFile(
3339
- path25.join(ruleDir, "tanstack-query.mdc"),
3399
+ const ruleDir = path26.join(projectDir, ".cursor", "rules");
3400
+ await fs25.ensureDir(ruleDir);
3401
+ await fs25.writeFile(
3402
+ path26.join(ruleDir, "tanstack-query.mdc"),
3340
3403
  RULE11,
3341
3404
  "utf8"
3342
3405
  );
@@ -3344,8 +3407,8 @@ var tanstackQueryInstaller = {
3344
3407
  };
3345
3408
 
3346
3409
  // src/installers/testing.ts
3347
- import fs25 from "fs-extra";
3348
- import path26 from "path";
3410
+ import fs26 from "fs-extra";
3411
+ import path27 from "path";
3349
3412
  var VITEST_CONFIG = `import react from "@vitejs/plugin-react";
3350
3413
  import { resolve } from "node:path";
3351
3414
  import { defineConfig } from "vitest/config";
@@ -3423,13 +3486,13 @@ alwaysApply: false
3423
3486
  - Prefer \`renderToStaticMarkup\` for simple server component smoke.
3424
3487
  - Run \`${testCmd}\`. Playwright (if enabled): \`${e2eCmd}\`.
3425
3488
  `;
3426
- await fs25.writeFile(
3427
- path26.join(projectDir, "vitest.config.mts"),
3489
+ await fs26.writeFile(
3490
+ path27.join(projectDir, "vitest.config.mts"),
3428
3491
  VITEST_CONFIG,
3429
3492
  "utf8"
3430
3493
  );
3431
- await fs25.outputFile(
3432
- path26.join(
3494
+ await fs26.outputFile(
3495
+ path27.join(
3433
3496
  projectDir,
3434
3497
  "src",
3435
3498
  "app",
@@ -3439,13 +3502,13 @@ alwaysApply: false
3439
3502
  ),
3440
3503
  SAMPLE_TEST
3441
3504
  );
3442
- await fs25.writeFile(
3443
- path26.join(projectDir, ".cursor", "rules", "testing.mdc"),
3505
+ await fs26.writeFile(
3506
+ path27.join(projectDir, ".cursor", "rules", "testing.mdc"),
3444
3507
  rule,
3445
3508
  "utf8"
3446
3509
  );
3447
- await fs25.outputFile(
3448
- path26.join(projectDir, "docs", "style-guide", "tooling", "testing.md"),
3510
+ await fs26.outputFile(
3511
+ path27.join(projectDir, "docs", "style-guide", "tooling", "testing.md"),
3449
3512
  `# Testing
3450
3513
 
3451
3514
  Vitest: \`${testCmd}\`
@@ -3470,12 +3533,12 @@ Playwright: \`${e2eCmd}\` \xB7 config \`playwright.config.ts\` \xB7 specs in \`e
3470
3533
  "test:watch": "vitest"
3471
3534
  });
3472
3535
  if (options.playwright) {
3473
- await fs25.writeFile(
3474
- path26.join(projectDir, "playwright.config.ts"),
3536
+ await fs26.writeFile(
3537
+ path27.join(projectDir, "playwright.config.ts"),
3475
3538
  playwrightConfig(pm.runScript("dev")),
3476
3539
  "utf8"
3477
3540
  );
3478
- await fs25.outputFile(path26.join(projectDir, "e2e", "home.spec.ts"), E2E_SMOKE);
3541
+ await fs26.outputFile(path27.join(projectDir, "e2e", "home.spec.ts"), E2E_SMOKE);
3479
3542
  await addDependencies(
3480
3543
  projectDir,
3481
3544
  { "@playwright/test": "^1.50.1" },
@@ -3489,8 +3552,8 @@ Playwright: \`${e2eCmd}\` \xB7 config \`playwright.config.ts\` \xB7 specs in \`e
3489
3552
  };
3490
3553
 
3491
3554
  // src/installers/validation.ts
3492
- import fs26 from "fs-extra";
3493
- import path27 from "path";
3555
+ import fs27 from "fs-extra";
3556
+ import path28 from "path";
3494
3557
  var ENV_ZOD = `import { z } from "zod";
3495
3558
 
3496
3559
  /**
@@ -3572,37 +3635,37 @@ var validationInstaller = {
3572
3635
  id: "validation",
3573
3636
  active: () => true,
3574
3637
  async run({ projectDir, options }) {
3575
- const validationsDir = path27.join(projectDir, "src", "lib", "validations");
3576
- await fs26.ensureDir(validationsDir);
3577
- await fs26.writeFile(path27.join(validationsDir, ".gitkeep"), "", "utf8");
3638
+ const validationsDir = path28.join(projectDir, "src", "lib", "validations");
3639
+ await fs27.ensureDir(validationsDir);
3640
+ await fs27.writeFile(path28.join(validationsDir, ".gitkeep"), "", "utf8");
3578
3641
  if (options.validation === "yup") {
3579
- await fs26.writeFile(
3580
- path27.join(projectDir, "src", "lib", "env.ts"),
3642
+ await fs27.writeFile(
3643
+ path28.join(projectDir, "src", "lib", "env.ts"),
3581
3644
  ENV_YUP,
3582
3645
  "utf8"
3583
3646
  );
3584
3647
  await removeDependencies(projectDir, ["zod"]);
3585
3648
  await addDependencies(projectDir, { yup: "^1.6.1" });
3586
3649
  } else {
3587
- await fs26.writeFile(
3588
- path27.join(projectDir, "src", "lib", "env.ts"),
3650
+ await fs27.writeFile(
3651
+ path28.join(projectDir, "src", "lib", "env.ts"),
3589
3652
  ENV_ZOD,
3590
3653
  "utf8"
3591
3654
  );
3592
3655
  await addDependencies(projectDir, { zod: "^3.24.1" });
3593
3656
  }
3594
- await fs26.writeFile(
3595
- path27.join(projectDir, ".cursor", "rules", "forms.mdc"),
3657
+ await fs27.writeFile(
3658
+ path28.join(projectDir, ".cursor", "rules", "forms.mdc"),
3596
3659
  formsRule(options.validation),
3597
3660
  "utf8"
3598
3661
  );
3599
- await fs26.writeFile(
3600
- path27.join(projectDir, ".cursor", "rules", "validation.mdc"),
3662
+ await fs27.writeFile(
3663
+ path28.join(projectDir, ".cursor", "rules", "validation.mdc"),
3601
3664
  validationRule(options.validation),
3602
3665
  "utf8"
3603
3666
  );
3604
- await fs26.outputFile(
3605
- path27.join(
3667
+ await fs27.outputFile(
3668
+ path28.join(
3606
3669
  projectDir,
3607
3670
  "docs",
3608
3671
  "style-guide",
@@ -3620,8 +3683,8 @@ Forms: \`@hookform/resolvers/${options.validation}\` when using RHF.
3620
3683
  };
3621
3684
 
3622
3685
  // src/installers/vercel.ts
3623
- import fs27 from "fs-extra";
3624
- import path28 from "path";
3686
+ import fs28 from "fs-extra";
3687
+ import path29 from "path";
3625
3688
  var VERCEL_JSON = `{
3626
3689
  "framework": "nextjs",
3627
3690
  "regions": ["iad1"]
@@ -3644,18 +3707,18 @@ var vercelInstaller = {
3644
3707
  id: "vercel",
3645
3708
  active: (o) => o.deploy === "vercel",
3646
3709
  async run({ projectDir }) {
3647
- await fs27.writeFile(
3648
- path28.join(projectDir, "vercel.json"),
3710
+ await fs28.writeFile(
3711
+ path29.join(projectDir, "vercel.json"),
3649
3712
  VERCEL_JSON,
3650
3713
  "utf8"
3651
3714
  );
3652
- await fs27.writeFile(
3653
- path28.join(projectDir, ".cursor", "rules", "vercel.mdc"),
3715
+ await fs28.writeFile(
3716
+ path29.join(projectDir, ".cursor", "rules", "vercel.mdc"),
3654
3717
  RULE12,
3655
3718
  "utf8"
3656
3719
  );
3657
- await fs27.outputFile(
3658
- path28.join(projectDir, "docs", "style-guide", "ops", "vercel.md"),
3720
+ await fs28.outputFile(
3721
+ path29.join(projectDir, "docs", "style-guide", "ops", "vercel.md"),
3659
3722
  `# Vercel
3660
3723
 
3661
3724
  - \`vercel.json\` \u2014 Next.js framework hint
@@ -3714,33 +3777,29 @@ async function runInstallers(ctx, onProgress) {
3714
3777
 
3715
3778
  // src/scaffold.ts
3716
3779
  async function scaffold(options) {
3717
- const projectDir = path29.resolve(process.cwd(), options.name);
3780
+ const projectDir = options.outDir ?? resolveProjectTarget(options.name).outDir;
3718
3781
  const pm = getPackageManager(options.packageManager);
3719
- if (await fs28.pathExists(projectDir)) {
3720
- const entries = await fs28.readdir(projectDir);
3721
- if (entries.length > 0) {
3722
- throw new Error(
3723
- `Target directory already exists and is not empty: ${projectDir}`
3724
- );
3725
- }
3726
- }
3782
+ await assertDirScaffoldable(projectDir);
3727
3783
  const spin = p2.spinner();
3728
- spin.start("Copying template");
3784
+ spin.start(
3785
+ projectDir === path30.resolve(process.cwd()) ? "Copying template into current directory" : "Copying template"
3786
+ );
3729
3787
  await yieldUi();
3730
- if (!await fs28.pathExists(TEMPLATE_DIR)) {
3788
+ if (!await fs29.pathExists(TEMPLATE_DIR)) {
3731
3789
  spin.stop("Template missing");
3732
3790
  throw new Error(`Template directory not found at ${TEMPLATE_DIR}`);
3733
3791
  }
3734
- await fs28.copy(TEMPLATE_DIR, projectDir, {
3792
+ await fs29.ensureDir(projectDir);
3793
+ await fs29.copy(TEMPLATE_DIR, projectDir, {
3735
3794
  filter: (src) => {
3736
- const base = path29.basename(src);
3795
+ const base = path30.basename(src);
3737
3796
  return base !== "node_modules" && base !== ".next" && base !== "dist";
3738
3797
  }
3739
3798
  });
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 });
3799
+ const gitignoreSrc = path30.join(projectDir, "gitignore");
3800
+ const gitignoreDest = path30.join(projectDir, ".gitignore");
3801
+ if (await fs29.pathExists(gitignoreSrc)) {
3802
+ await fs29.move(gitignoreSrc, gitignoreDest, { overwrite: true });
3744
3803
  }
3745
3804
  spin.message("Applying project tokens");
3746
3805
  await yieldUi();
@@ -3797,8 +3856,8 @@ async function scaffold(options) {
3797
3856
  }
3798
3857
 
3799
3858
  // src/upgrade.ts
3800
- import fs29 from "fs-extra";
3801
- import path30 from "path";
3859
+ import fs30 from "fs-extra";
3860
+ import path31 from "path";
3802
3861
  import * as p3 from "@clack/prompts";
3803
3862
  import color3 from "picocolors";
3804
3863
  var TRACKED = [
@@ -3808,17 +3867,17 @@ var TRACKED = [
3808
3867
  ".cursor/rules/forms.mdc"
3809
3868
  ];
3810
3869
  function rel(projectDir, file) {
3811
- return path30.join(projectDir, file);
3870
+ return path31.join(projectDir, file);
3812
3871
  }
3813
3872
  function templatePath(file) {
3814
- return path30.join(TEMPLATE_DIR, file);
3873
+ return path31.join(TEMPLATE_DIR, file);
3815
3874
  }
3816
3875
  async function runUpgrade(options) {
3817
3876
  const { projectDir, apply } = options;
3818
- if (!await fs29.pathExists(path30.join(projectDir, "package.json"))) {
3877
+ if (!await fs30.pathExists(path31.join(projectDir, "package.json"))) {
3819
3878
  throw new Error(`Not an app directory (missing package.json): ${projectDir}`);
3820
3879
  }
3821
- if (!await fs29.pathExists(TEMPLATE_DIR)) {
3880
+ if (!await fs30.pathExists(TEMPLATE_DIR)) {
3822
3881
  throw new Error(`Template missing at ${TEMPLATE_DIR}`);
3823
3882
  }
3824
3883
  p3.intro(color3.bgCyan(color3.black(" next-style-guide upgrade ")));
@@ -3831,14 +3890,14 @@ async function runUpgrade(options) {
3831
3890
  for (const file of TRACKED) {
3832
3891
  const from = templatePath(file);
3833
3892
  const to = rel(projectDir, file);
3834
- if (!await fs29.pathExists(from)) {
3893
+ if (!await fs30.pathExists(from)) {
3835
3894
  p3.log.warn(`Template missing ${file}`);
3836
3895
  continue;
3837
3896
  }
3838
- const next = await fs29.readFile(from, "utf8");
3897
+ const next = await fs30.readFile(from, "utf8");
3839
3898
  let outgoing = next;
3840
- if (file === "AGENTS.md" && await fs29.pathExists(to)) {
3841
- const current2 = await fs29.readFile(to, "utf8");
3899
+ if (file === "AGENTS.md" && await fs30.pathExists(to)) {
3900
+ const current2 = await fs30.readFile(to, "utf8");
3842
3901
  const nameMatch = current2.match(/^# Agents \((.+)\)$/m);
3843
3902
  if (nameMatch?.[1]) {
3844
3903
  outgoing = outgoing.replace("__PROJECT_NAME__", nameMatch[1]);
@@ -3851,18 +3910,18 @@ async function runUpgrade(options) {
3851
3910
  }
3852
3911
  }
3853
3912
  }
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)) {
3913
+ outgoing = outgoing.replaceAll("__PROJECT_NAME__", path31.basename(projectDir)).replaceAll("__PACKAGE_MANAGER__", "pnpm").replaceAll("__PROJECT_SLUG__", path31.basename(projectDir));
3914
+ if (!await fs30.pathExists(to)) {
3856
3915
  missing += 1;
3857
3916
  p3.log.warn(`missing ${file}`);
3858
3917
  if (apply) {
3859
- await fs29.ensureDir(path30.dirname(to));
3860
- await fs29.writeFile(to, outgoing, "utf8");
3918
+ await fs30.ensureDir(path31.dirname(to));
3919
+ await fs30.writeFile(to, outgoing, "utf8");
3861
3920
  p3.log.success(`wrote ${file}`);
3862
3921
  }
3863
3922
  continue;
3864
3923
  }
3865
- const current = await fs29.readFile(to, "utf8");
3924
+ const current = await fs30.readFile(to, "utf8");
3866
3925
  if (current === outgoing) {
3867
3926
  same += 1;
3868
3927
  p3.log.step(color3.dim(`same ${file}`));
@@ -3871,7 +3930,7 @@ async function runUpgrade(options) {
3871
3930
  changed += 1;
3872
3931
  p3.log.warn(`differs ${file}`);
3873
3932
  if (apply) {
3874
- await fs29.writeFile(to, outgoing, "utf8");
3933
+ await fs30.writeFile(to, outgoing, "utf8");
3875
3934
  p3.log.success(`updated ${file}`);
3876
3935
  }
3877
3936
  }
@@ -4078,6 +4137,7 @@ ${TITLE} \u2014 ${PKG_NAME}
4078
4137
 
4079
4138
  Usage:
4080
4139
  npx create-next-style-guide [name] [options]
4140
+ npx create-next-style-guide . [options] # scaffold into current directory
4081
4141
  npx create-next-style-guide upgrade [--apply] [dir]
4082
4142
  npx create-next-style-guide --diff [dir]
4083
4143
 
@@ -4150,18 +4210,19 @@ function applyFlagOverrides(options, flags) {
4150
4210
  async function main() {
4151
4211
  const flags = parseArgs(process.argv);
4152
4212
  if (flags.upgrade) {
4153
- const projectDir2 = path31.resolve(process.cwd(), flags.name ?? ".");
4213
+ const projectDir2 = path32.resolve(process.cwd(), flags.name ?? ".");
4154
4214
  await runUpgrade({ projectDir: projectDir2, apply: flags.apply });
4155
4215
  return;
4156
4216
  }
4157
4217
  let options;
4158
4218
  if (flags.yes) {
4159
- const name = (flags.name ?? "my-app").trim();
4219
+ const target = resolveProjectTarget(flags.name ?? "my-app");
4160
4220
  const presetSeed = flags.preset && flags.preset !== "custom" ? PRESET_SEEDS[flags.preset] : DEFAULT_OPTIONS;
4161
4221
  options = applyFlagOverrides(
4162
4222
  {
4163
- name,
4164
- slug: slugify(name),
4223
+ name: target.name,
4224
+ slug: target.slug,
4225
+ outDir: target.outDir,
4165
4226
  ...presetSeed,
4166
4227
  noInstall: flags.noInstall
4167
4228
  },
@@ -4171,6 +4232,7 @@ async function main() {
4171
4232
  p4.log.info(
4172
4233
  [
4173
4234
  `Non-interactive: ${options.name} (${options.slug})`,
4235
+ target.inPlace ? `out=. (${target.outDir})` : null,
4174
4236
  flags.preset ? `preset=${flags.preset}` : null,
4175
4237
  `pm=${options.packageManager}`,
4176
4238
  `styling=${options.styling} ui=${options.ui}${options.ui === "shadcn" ? `:${options.shadcnSet}` : ""} auth=${options.auth} store=${options.store}`,
@@ -4189,7 +4251,7 @@ async function main() {
4189
4251
  const projectDir = await scaffold(options);
4190
4252
  const pm = getPackageManager(options.packageManager);
4191
4253
  const nextSteps = [
4192
- `cd ${path31.relative(process.cwd(), projectDir) || "."}`,
4254
+ `cd ${path32.relative(process.cwd(), projectDir) || "."}`,
4193
4255
  options.noInstall ? `${pm.id} ${pm.installArgs.filter((a) => a !== "--ignore-workspace").join(" ")}`.trim() : null,
4194
4256
  options.ui === "shadcn" && options.shadcnSet === "minimal" ? "npx shadcn@latest add input # or re-scaffold with --shadcn-set starter" : null,
4195
4257
  options.endpoints ? `${pm.runScript("endpoints:generate")} # when API schema is up` : null,