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/README.md +4 -0
- package/dist/index.js +439 -377
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/template/tsconfig.json +8 -2
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 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
|
|
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
|
-
|
|
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
|
|
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
|
|
629
|
-
import
|
|
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
|
|
660
|
-
import
|
|
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 =
|
|
747
|
+
const base = path3.basename(filePath);
|
|
685
748
|
if (base === ".env.example" || base === "LICENSE") return true;
|
|
686
|
-
const ext =
|
|
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
|
|
760
|
+
const entries = await fs2.readdir(root, { withFileTypes: true });
|
|
698
761
|
for (const entry of entries) {
|
|
699
|
-
const full =
|
|
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
|
|
769
|
+
const raw = await fs2.readFile(full, "utf8");
|
|
707
770
|
const next = applyTokens(raw, tokens);
|
|
708
771
|
if (next !== raw) {
|
|
709
|
-
await
|
|
772
|
+
await fs2.writeFile(full, next, "utf8");
|
|
710
773
|
}
|
|
711
774
|
}
|
|
712
775
|
}
|
|
713
776
|
|
|
714
777
|
// src/installers/api-envelope.ts
|
|
715
|
-
import
|
|
716
|
-
import
|
|
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
|
|
779
|
-
|
|
841
|
+
await fs3.writeFile(
|
|
842
|
+
path4.join(projectDir, "src", "lib", "http.ts"),
|
|
780
843
|
HTTP_ENVELOPE,
|
|
781
844
|
"utf8"
|
|
782
845
|
);
|
|
783
|
-
await
|
|
784
|
-
|
|
846
|
+
await fs3.writeFile(
|
|
847
|
+
path4.join(projectDir, ".cursor", "rules", "api-envelope.mdc"),
|
|
785
848
|
RULE,
|
|
786
849
|
"utf8"
|
|
787
850
|
);
|
|
788
|
-
await
|
|
789
|
-
|
|
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
|
|
801
|
-
import
|
|
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 =
|
|
872
|
-
await
|
|
873
|
-
await
|
|
874
|
-
await
|
|
875
|
-
|
|
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 =
|
|
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
|
|
941
|
-
const existing = await
|
|
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
|
|
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
|
|
965
|
-
import
|
|
1027
|
+
import fs6 from "fs-extra";
|
|
1028
|
+
import path7 from "path";
|
|
966
1029
|
|
|
967
1030
|
// src/helpers/package-json.ts
|
|
968
|
-
import
|
|
969
|
-
import
|
|
1031
|
+
import fs5 from "fs-extra";
|
|
1032
|
+
import path6 from "path";
|
|
970
1033
|
async function readPkg(projectDir) {
|
|
971
|
-
const pkgPath =
|
|
972
|
-
const pkg = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1170
|
-
|
|
1232
|
+
await fs6.writeFile(
|
|
1233
|
+
path7.join(projectDir, "src", "lib", "auth.ts"),
|
|
1171
1234
|
AUTH_TS,
|
|
1172
1235
|
"utf8"
|
|
1173
1236
|
);
|
|
1174
|
-
await
|
|
1175
|
-
|
|
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
|
|
1187
|
-
|
|
1249
|
+
await fs6.writeFile(
|
|
1250
|
+
path7.join(projectDir, "src", "middleware.ts"),
|
|
1188
1251
|
MIDDLEWARE,
|
|
1189
1252
|
"utf8"
|
|
1190
1253
|
);
|
|
1191
|
-
await
|
|
1192
|
-
|
|
1254
|
+
await fs6.outputFile(
|
|
1255
|
+
path7.join(projectDir, "src", "app", "login", "page.tsx"),
|
|
1193
1256
|
LOGIN_PAGE
|
|
1194
1257
|
);
|
|
1195
|
-
await
|
|
1196
|
-
|
|
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
|
|
1208
|
-
|
|
1270
|
+
await fs6.writeFile(
|
|
1271
|
+
path7.join(projectDir, ".cursor", "rules", "auth.mdc"),
|
|
1209
1272
|
RULE2,
|
|
1210
1273
|
"utf8"
|
|
1211
1274
|
);
|
|
1212
|
-
await
|
|
1213
|
-
|
|
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 =
|
|
1224
|
-
if (await
|
|
1225
|
-
let env = await
|
|
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
|
|
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
|
|
1242
|
-
import
|
|
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
|
|
1396
|
-
await
|
|
1397
|
-
await
|
|
1398
|
-
|
|
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
|
|
1404
|
-
|
|
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
|
|
1410
|
-
|
|
1472
|
+
await fs7.writeFile(
|
|
1473
|
+
path8.join(projectDir, ".cursor", "rules", "auth.mdc"),
|
|
1411
1474
|
RULE3,
|
|
1412
1475
|
"utf8"
|
|
1413
1476
|
);
|
|
1414
|
-
await
|
|
1415
|
-
|
|
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
|
|
1427
|
-
import
|
|
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 =
|
|
1506
|
-
await
|
|
1507
|
-
|
|
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
|
|
1511
|
-
|
|
1512
|
-
|
|
1573
|
+
await fs8.copy(
|
|
1574
|
+
path9.join(addon, ".cursor", "rules", "ci.mdc"),
|
|
1575
|
+
path9.join(projectDir, ".cursor", "rules", "ci.mdc")
|
|
1513
1576
|
);
|
|
1514
|
-
await
|
|
1515
|
-
|
|
1516
|
-
|
|
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
|
|
1523
|
-
import
|
|
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 =
|
|
1554
|
-
await
|
|
1555
|
-
await
|
|
1556
|
-
await
|
|
1557
|
-
|
|
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
|
|
1562
|
-
|
|
1624
|
+
await fs9.writeFile(
|
|
1625
|
+
path10.join(projectDir, ".cursor", "rules", "dark-mode.mdc"),
|
|
1563
1626
|
RULE4,
|
|
1564
1627
|
"utf8"
|
|
1565
1628
|
);
|
|
1566
|
-
await
|
|
1567
|
-
|
|
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 =
|
|
1575
|
-
if (await
|
|
1576
|
-
let css = await
|
|
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
|
|
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
|
|
1597
|
-
import
|
|
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
|
|
1639
|
-
|
|
1701
|
+
await fs10.writeFile(
|
|
1702
|
+
path11.join(projectDir, "docker-compose.yml"),
|
|
1640
1703
|
COMPOSE,
|
|
1641
1704
|
"utf8"
|
|
1642
1705
|
);
|
|
1643
|
-
await
|
|
1644
|
-
|
|
1706
|
+
await fs10.outputFile(
|
|
1707
|
+
path11.join(projectDir, "docker", "api-mock", "README.md"),
|
|
1645
1708
|
MOCK_README
|
|
1646
1709
|
);
|
|
1647
|
-
await
|
|
1648
|
-
|
|
1710
|
+
await fs10.writeFile(
|
|
1711
|
+
path11.join(projectDir, ".cursor", "rules", "docker-compose.mdc"),
|
|
1649
1712
|
RULE5,
|
|
1650
1713
|
"utf8"
|
|
1651
1714
|
);
|
|
1652
|
-
await
|
|
1653
|
-
|
|
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
|
|
1668
|
-
import
|
|
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 =
|
|
1912
|
+
const addon = path12.join(ADDONS_DIR, "docker");
|
|
1850
1913
|
const pm = getPackageManager(options.packageManager);
|
|
1851
|
-
await
|
|
1852
|
-
await
|
|
1853
|
-
|
|
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
|
|
1858
|
-
|
|
1859
|
-
|
|
1920
|
+
await fs11.copy(
|
|
1921
|
+
path12.join(addon, ".dockerignore"),
|
|
1922
|
+
path12.join(projectDir, ".dockerignore")
|
|
1860
1923
|
);
|
|
1861
|
-
await
|
|
1862
|
-
|
|
1863
|
-
|
|
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
|
|
1866
|
-
|
|
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
|
|
1871
|
-
|
|
1933
|
+
await fs11.outputFile(
|
|
1934
|
+
path12.join(projectDir, "docs", "style-guide", "ops", "docker.md"),
|
|
1872
1935
|
doc
|
|
1873
1936
|
);
|
|
1874
|
-
await
|
|
1875
|
-
|
|
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
|
|
1884
|
-
import
|
|
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 =
|
|
1890
|
-
await
|
|
1891
|
-
|
|
1892
|
-
|
|
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
|
|
1895
|
-
|
|
1896
|
-
|
|
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
|
|
1906
|
-
|
|
1907
|
-
|
|
1968
|
+
await fs12.copy(
|
|
1969
|
+
path13.join(addon, "src", "utils", file),
|
|
1970
|
+
path13.join(projectDir, "src", "utils", file)
|
|
1908
1971
|
);
|
|
1909
1972
|
}
|
|
1910
|
-
await
|
|
1911
|
-
await
|
|
1912
|
-
|
|
1913
|
-
|
|
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
|
|
1916
|
-
|
|
1917
|
-
|
|
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
|
|
1933
|
-
import
|
|
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 =
|
|
1939
|
-
await
|
|
1940
|
-
|
|
1941
|
-
|
|
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
|
|
1944
|
-
|
|
1945
|
-
|
|
2006
|
+
await fs13.copy(
|
|
2007
|
+
path14.join(addon, ".cursor", "rules", "eslint.mdc"),
|
|
2008
|
+
path14.join(projectDir, ".cursor", "rules", "eslint.mdc")
|
|
1946
2009
|
);
|
|
1947
|
-
await
|
|
1948
|
-
|
|
1949
|
-
|
|
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
|
|
1969
|
-
import
|
|
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 =
|
|
1975
|
-
await
|
|
1976
|
-
await
|
|
1977
|
-
|
|
1978
|
-
|
|
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
|
|
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
|
|
2009
|
-
import
|
|
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
|
|
2045
|
-
|
|
2107
|
+
await fs15.outputFile(
|
|
2108
|
+
path16.join(projectDir, "src", "i18n", "request.ts"),
|
|
2046
2109
|
I18N_REQUEST
|
|
2047
2110
|
);
|
|
2048
|
-
await
|
|
2049
|
-
|
|
2111
|
+
await fs15.outputFile(
|
|
2112
|
+
path16.join(projectDir, "messages", "en.json"),
|
|
2050
2113
|
MESSAGES_EN
|
|
2051
2114
|
);
|
|
2052
|
-
await
|
|
2053
|
-
|
|
2115
|
+
await fs15.writeFile(
|
|
2116
|
+
path16.join(projectDir, ".cursor", "rules", "i18n.mdc"),
|
|
2054
2117
|
RULE6,
|
|
2055
2118
|
"utf8"
|
|
2056
2119
|
);
|
|
2057
|
-
await
|
|
2058
|
-
|
|
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 =
|
|
2066
|
-
if (await
|
|
2067
|
-
let nextConfig = await
|
|
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
|
|
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
|
|
2091
|
-
import
|
|
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 =
|
|
2116
|
-
await
|
|
2117
|
-
await
|
|
2118
|
-
|
|
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
|
|
2127
|
-
import
|
|
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 =
|
|
2172
|
-
await
|
|
2173
|
-
await
|
|
2174
|
-
await
|
|
2175
|
-
|
|
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
|
|
2180
|
-
|
|
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
|
|
2196
|
-
import
|
|
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 =
|
|
2203
|
-
const pkg = await
|
|
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
|
|
2272
|
+
await fs18.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
2210
2273
|
}
|
|
2211
2274
|
};
|
|
2212
2275
|
|
|
2213
2276
|
// src/installers/prettier.ts
|
|
2214
|
-
import
|
|
2215
|
-
import
|
|
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 =
|
|
2221
|
-
await
|
|
2222
|
-
|
|
2223
|
-
|
|
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
|
|
2226
|
-
|
|
2227
|
-
|
|
2288
|
+
await fs19.copy(
|
|
2289
|
+
path20.join(addon, ".cursor", "rules", "prettier.mdc"),
|
|
2290
|
+
path20.join(projectDir, ".cursor", "rules", "prettier.mdc")
|
|
2228
2291
|
);
|
|
2229
|
-
await
|
|
2230
|
-
|
|
2231
|
-
|
|
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
|
|
2247
|
-
import
|
|
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
|
|
2397
|
-
await
|
|
2398
|
-
|
|
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
|
|
2403
|
-
|
|
2465
|
+
await fs20.writeFile(
|
|
2466
|
+
path21.join(projectDir, "src", "app", "sitemap.ts"),
|
|
2404
2467
|
SITEMAP,
|
|
2405
2468
|
"utf8"
|
|
2406
2469
|
);
|
|
2407
|
-
await
|
|
2408
|
-
|
|
2470
|
+
await fs20.writeFile(
|
|
2471
|
+
path21.join(projectDir, "src", "app", "robots.ts"),
|
|
2409
2472
|
ROBOTS,
|
|
2410
2473
|
"utf8"
|
|
2411
2474
|
);
|
|
2412
|
-
await
|
|
2413
|
-
|
|
2475
|
+
await fs20.writeFile(
|
|
2476
|
+
path21.join(projectDir, ".cursor", "rules", "seo.mdc"),
|
|
2414
2477
|
RULE9,
|
|
2415
2478
|
"utf8"
|
|
2416
2479
|
);
|
|
2417
|
-
await
|
|
2418
|
-
|
|
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
|
|
2433
|
-
import
|
|
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 =
|
|
2669
|
-
await
|
|
2670
|
-
|
|
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
|
|
2675
|
-
await
|
|
2737
|
+
await fs21.ensureDir(uiDir);
|
|
2738
|
+
await fs21.writeFile(path22.join(uiDir, "button.tsx"), BUTTON, "utf8");
|
|
2676
2739
|
if (set === "starter") {
|
|
2677
|
-
await
|
|
2678
|
-
await
|
|
2679
|
-
await
|
|
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
|
|
2682
|
-
await
|
|
2683
|
-
await
|
|
2684
|
-
|
|
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
|
|
2689
|
-
|
|
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
|
|
2714
|
-
import
|
|
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 =
|
|
2816
|
-
await
|
|
2878
|
+
const storesDir = path23.join(projectDir, "src", "stores");
|
|
2879
|
+
await fs22.ensureDir(storesDir);
|
|
2817
2880
|
if (options.store === "zustand") {
|
|
2818
|
-
await
|
|
2819
|
-
|
|
2881
|
+
await fs22.writeFile(
|
|
2882
|
+
path23.join(storesDir, "app-store.ts"),
|
|
2820
2883
|
ZUSTAND_STORE,
|
|
2821
2884
|
"utf8"
|
|
2822
2885
|
);
|
|
2823
|
-
await
|
|
2824
|
-
|
|
2886
|
+
await fs22.writeFile(
|
|
2887
|
+
path23.join(projectDir, ".cursor", "rules", "store.mdc"),
|
|
2825
2888
|
ZUSTAND_RULE,
|
|
2826
2889
|
"utf8"
|
|
2827
2890
|
);
|
|
2828
|
-
await
|
|
2829
|
-
|
|
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
|
|
2840
|
-
await
|
|
2841
|
-
const providersDir =
|
|
2842
|
-
await
|
|
2843
|
-
await
|
|
2844
|
-
await
|
|
2845
|
-
|
|
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
|
|
2850
|
-
|
|
2912
|
+
await fs22.writeFile(
|
|
2913
|
+
path23.join(projectDir, ".cursor", "rules", "store.mdc"),
|
|
2851
2914
|
REDUX_RULE,
|
|
2852
2915
|
"utf8"
|
|
2853
2916
|
);
|
|
2854
|
-
await
|
|
2855
|
-
|
|
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
|
|
2872
|
-
import
|
|
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
|
|
3058
|
-
|
|
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
|
|
3070
|
-
|
|
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
|
|
3076
|
-
|
|
3077
|
-
|
|
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
|
|
3088
|
-
|
|
3089
|
-
|
|
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 =
|
|
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
|
|
3100
|
-
await
|
|
3162
|
+
if (await fs23.pathExists(exampleSrc)) {
|
|
3163
|
+
await fs23.copy(
|
|
3101
3164
|
exampleSrc,
|
|
3102
|
-
|
|
3165
|
+
path24.join(projectDir, "docs", "examples", "api-app-crud.md")
|
|
3103
3166
|
);
|
|
3104
3167
|
}
|
|
3105
3168
|
}
|
|
3106
|
-
await
|
|
3107
|
-
|
|
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
|
|
3116
|
-
import
|
|
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
|
|
3243
|
-
|
|
3305
|
+
await fs24.writeFile(
|
|
3306
|
+
path25.join(projectDir, "postcss.config.mjs"),
|
|
3244
3307
|
POSTCSS,
|
|
3245
3308
|
"utf8"
|
|
3246
3309
|
);
|
|
3247
|
-
await
|
|
3248
|
-
|
|
3310
|
+
await fs24.writeFile(
|
|
3311
|
+
path25.join(projectDir, "src", "app", "globals.css"),
|
|
3249
3312
|
GLOBALS,
|
|
3250
3313
|
"utf8"
|
|
3251
3314
|
);
|
|
3252
|
-
await
|
|
3253
|
-
|
|
3315
|
+
await fs24.writeFile(
|
|
3316
|
+
path25.join(projectDir, "src", "lib", "utils.ts"),
|
|
3254
3317
|
UTILS,
|
|
3255
3318
|
"utf8"
|
|
3256
3319
|
);
|
|
3257
|
-
await
|
|
3258
|
-
|
|
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
|
|
3279
|
-
import
|
|
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 =
|
|
3329
|
-
await
|
|
3330
|
-
await
|
|
3331
|
-
await
|
|
3332
|
-
|
|
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 =
|
|
3337
|
-
await
|
|
3338
|
-
await
|
|
3339
|
-
|
|
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
|
|
3348
|
-
import
|
|
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
|
|
3427
|
-
|
|
3489
|
+
await fs26.writeFile(
|
|
3490
|
+
path27.join(projectDir, "vitest.config.mts"),
|
|
3428
3491
|
VITEST_CONFIG,
|
|
3429
3492
|
"utf8"
|
|
3430
3493
|
);
|
|
3431
|
-
await
|
|
3432
|
-
|
|
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
|
|
3443
|
-
|
|
3505
|
+
await fs26.writeFile(
|
|
3506
|
+
path27.join(projectDir, ".cursor", "rules", "testing.mdc"),
|
|
3444
3507
|
rule,
|
|
3445
3508
|
"utf8"
|
|
3446
3509
|
);
|
|
3447
|
-
await
|
|
3448
|
-
|
|
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
|
|
3474
|
-
|
|
3536
|
+
await fs26.writeFile(
|
|
3537
|
+
path27.join(projectDir, "playwright.config.ts"),
|
|
3475
3538
|
playwrightConfig(pm.runScript("dev")),
|
|
3476
3539
|
"utf8"
|
|
3477
3540
|
);
|
|
3478
|
-
await
|
|
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
|
|
3493
|
-
import
|
|
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 =
|
|
3576
|
-
await
|
|
3577
|
-
await
|
|
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
|
|
3580
|
-
|
|
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
|
|
3588
|
-
|
|
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
|
|
3595
|
-
|
|
3657
|
+
await fs27.writeFile(
|
|
3658
|
+
path28.join(projectDir, ".cursor", "rules", "forms.mdc"),
|
|
3596
3659
|
formsRule(options.validation),
|
|
3597
3660
|
"utf8"
|
|
3598
3661
|
);
|
|
3599
|
-
await
|
|
3600
|
-
|
|
3662
|
+
await fs27.writeFile(
|
|
3663
|
+
path28.join(projectDir, ".cursor", "rules", "validation.mdc"),
|
|
3601
3664
|
validationRule(options.validation),
|
|
3602
3665
|
"utf8"
|
|
3603
3666
|
);
|
|
3604
|
-
await
|
|
3605
|
-
|
|
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
|
|
3624
|
-
import
|
|
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
|
|
3648
|
-
|
|
3710
|
+
await fs28.writeFile(
|
|
3711
|
+
path29.join(projectDir, "vercel.json"),
|
|
3649
3712
|
VERCEL_JSON,
|
|
3650
3713
|
"utf8"
|
|
3651
3714
|
);
|
|
3652
|
-
await
|
|
3653
|
-
|
|
3715
|
+
await fs28.writeFile(
|
|
3716
|
+
path29.join(projectDir, ".cursor", "rules", "vercel.mdc"),
|
|
3654
3717
|
RULE12,
|
|
3655
3718
|
"utf8"
|
|
3656
3719
|
);
|
|
3657
|
-
await
|
|
3658
|
-
|
|
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 =
|
|
3780
|
+
const projectDir = options.outDir ?? resolveProjectTarget(options.name).outDir;
|
|
3718
3781
|
const pm = getPackageManager(options.packageManager);
|
|
3719
|
-
|
|
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(
|
|
3784
|
+
spin.start(
|
|
3785
|
+
projectDir === path30.resolve(process.cwd()) ? "Copying template into current directory" : "Copying template"
|
|
3786
|
+
);
|
|
3729
3787
|
await yieldUi();
|
|
3730
|
-
if (!await
|
|
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
|
|
3792
|
+
await fs29.ensureDir(projectDir);
|
|
3793
|
+
await fs29.copy(TEMPLATE_DIR, projectDir, {
|
|
3735
3794
|
filter: (src) => {
|
|
3736
|
-
const base =
|
|
3795
|
+
const base = path30.basename(src);
|
|
3737
3796
|
return base !== "node_modules" && base !== ".next" && base !== "dist";
|
|
3738
3797
|
}
|
|
3739
3798
|
});
|
|
3740
|
-
const gitignoreSrc =
|
|
3741
|
-
const gitignoreDest =
|
|
3742
|
-
if (await
|
|
3743
|
-
await
|
|
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
|
|
3801
|
-
import
|
|
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
|
|
3870
|
+
return path31.join(projectDir, file);
|
|
3812
3871
|
}
|
|
3813
3872
|
function templatePath(file) {
|
|
3814
|
-
return
|
|
3873
|
+
return path31.join(TEMPLATE_DIR, file);
|
|
3815
3874
|
}
|
|
3816
3875
|
async function runUpgrade(options) {
|
|
3817
3876
|
const { projectDir, apply } = options;
|
|
3818
|
-
if (!await
|
|
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
|
|
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
|
|
3893
|
+
if (!await fs30.pathExists(from)) {
|
|
3835
3894
|
p3.log.warn(`Template missing ${file}`);
|
|
3836
3895
|
continue;
|
|
3837
3896
|
}
|
|
3838
|
-
const next = await
|
|
3897
|
+
const next = await fs30.readFile(from, "utf8");
|
|
3839
3898
|
let outgoing = next;
|
|
3840
|
-
if (file === "AGENTS.md" && await
|
|
3841
|
-
const current2 = await
|
|
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__",
|
|
3855
|
-
if (!await
|
|
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
|
|
3860
|
-
await
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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:
|
|
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 ${
|
|
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,
|