@mars-stack/cli 2.0.0 → 2.0.1
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 +430 -332
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/template/package.json +4 -4
- package/template/src/app/(auth)/forgotten-password/page.tsx +18 -0
- package/template/src/app/(auth)/register/page.tsx +3 -0
- package/template/src/app/(auth)/verify/page.tsx +19 -0
- package/template/src/app/(protected)/layout.tsx +18 -0
- package/template/src/app/api/auth/forgot/route.ts +7 -1
- package/template/src/app/api/auth/signup/route.ts +9 -0
- package/template/src/config/app.config.ts +0 -8
- package/template/src/config/routes.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -45,7 +45,7 @@ var init_logger = __esm({
|
|
|
45
45
|
|
|
46
46
|
// src/utils/rollback.ts
|
|
47
47
|
import fs3 from "fs-extra";
|
|
48
|
-
import
|
|
48
|
+
import path4 from "path";
|
|
49
49
|
import os from "os";
|
|
50
50
|
function createRollbackContext() {
|
|
51
51
|
const manifest = {
|
|
@@ -53,14 +53,14 @@ function createRollbackContext() {
|
|
|
53
53
|
filesModified: [],
|
|
54
54
|
depsInstalled: []
|
|
55
55
|
};
|
|
56
|
-
const backupDir =
|
|
56
|
+
const backupDir = path4.join(os.tmpdir(), `mars-rollback-${Date.now()}`);
|
|
57
57
|
function trackCreatedFile(filePath) {
|
|
58
58
|
manifest.filesCreated.push(filePath);
|
|
59
59
|
}
|
|
60
60
|
async function trackModifiedFile(filePath) {
|
|
61
61
|
if (!await fs3.pathExists(filePath)) return;
|
|
62
62
|
await fs3.ensureDir(backupDir);
|
|
63
|
-
const backupPath =
|
|
63
|
+
const backupPath = path4.join(backupDir, `${manifest.filesModified.length}-${path4.basename(filePath)}`);
|
|
64
64
|
await fs3.copy(filePath, backupPath);
|
|
65
65
|
manifest.filesModified.push({ path: filePath, backup: backupPath });
|
|
66
66
|
}
|
|
@@ -109,9 +109,9 @@ var init_rollback = __esm({
|
|
|
109
109
|
|
|
110
110
|
// src/utils/dependencies.ts
|
|
111
111
|
import fs4 from "fs-extra";
|
|
112
|
-
import
|
|
112
|
+
import path5 from "path";
|
|
113
113
|
async function addDependencies(projectRoot, deps, dev = false) {
|
|
114
|
-
const pkgPath =
|
|
114
|
+
const pkgPath = path5.join(projectRoot, "package.json");
|
|
115
115
|
if (!await fs4.pathExists(pkgPath)) return;
|
|
116
116
|
const pkg = await fs4.readJson(pkgPath);
|
|
117
117
|
const key = dev ? "devDependencies" : "dependencies";
|
|
@@ -124,16 +124,40 @@ var init_dependencies = __esm({
|
|
|
124
124
|
}
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
+
// src/utils/routes.ts
|
|
128
|
+
import fs5 from "fs-extra";
|
|
129
|
+
import path6 from "path";
|
|
130
|
+
async function registerRoute(projectRoot, key, routePath, ctx) {
|
|
131
|
+
const routesFile = path6.join(projectRoot, "src", "config", "routes.ts");
|
|
132
|
+
if (!await fs5.pathExists(routesFile)) return;
|
|
133
|
+
let content = await fs5.readFile(routesFile, "utf-8");
|
|
134
|
+
if (content.includes(`${key}:`)) return;
|
|
135
|
+
if (ctx) {
|
|
136
|
+
await ctx.trackModifiedFile(routesFile);
|
|
137
|
+
}
|
|
138
|
+
content = content.replace(
|
|
139
|
+
/} as const;/,
|
|
140
|
+
` ${key}: '${routePath}',
|
|
141
|
+
} as const;`
|
|
142
|
+
);
|
|
143
|
+
await fs5.writeFile(routesFile, content);
|
|
144
|
+
}
|
|
145
|
+
var init_routes = __esm({
|
|
146
|
+
"src/utils/routes.ts"() {
|
|
147
|
+
"use strict";
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
127
151
|
// src/generators/features/blog.ts
|
|
128
152
|
var blog_exports = {};
|
|
129
153
|
__export(blog_exports, {
|
|
130
154
|
generateBlog: () => generateBlog
|
|
131
155
|
});
|
|
132
|
-
import
|
|
133
|
-
import
|
|
156
|
+
import fs6 from "fs-extra";
|
|
157
|
+
import path7 from "path";
|
|
134
158
|
async function generateBlog(projectRoot) {
|
|
135
|
-
const featureDir =
|
|
136
|
-
if (await
|
|
159
|
+
const featureDir = path7.join(projectRoot, "src", "features", "blog");
|
|
160
|
+
if (await fs6.pathExists(featureDir)) {
|
|
137
161
|
log.error("Blog feature already exists at src/features/blog/");
|
|
138
162
|
return;
|
|
139
163
|
}
|
|
@@ -154,10 +178,10 @@ async function generateBlog(projectRoot) {
|
|
|
154
178
|
};
|
|
155
179
|
let count = 0;
|
|
156
180
|
for (const [filePath, content] of Object.entries(files)) {
|
|
157
|
-
const fullPath =
|
|
181
|
+
const fullPath = path7.join(projectRoot, filePath);
|
|
158
182
|
ctx.trackCreatedFile(fullPath);
|
|
159
|
-
await
|
|
160
|
-
await
|
|
183
|
+
await fs6.ensureDir(path7.dirname(fullPath));
|
|
184
|
+
await fs6.writeFile(fullPath, content);
|
|
161
185
|
count++;
|
|
162
186
|
}
|
|
163
187
|
await addDependencies(projectRoot, {
|
|
@@ -168,6 +192,7 @@ async function generateBlog(projectRoot) {
|
|
|
168
192
|
"remark-gfm": "^4.0.0"
|
|
169
193
|
});
|
|
170
194
|
await addDependencies(projectRoot, { "@types/mdx": "^2.0.0" }, true);
|
|
195
|
+
await registerRoute(projectRoot, "blog", "/blog", ctx);
|
|
171
196
|
await setConfigFlag(projectRoot, ctx);
|
|
172
197
|
await ctx.commit();
|
|
173
198
|
log.success(`Generated blog feature with ${count} files`);
|
|
@@ -183,12 +208,12 @@ async function generateBlog(projectRoot) {
|
|
|
183
208
|
}
|
|
184
209
|
}
|
|
185
210
|
async function setConfigFlag(projectRoot, ctx) {
|
|
186
|
-
const configPath =
|
|
187
|
-
if (!await
|
|
211
|
+
const configPath = path7.join(projectRoot, "src", "config", "app.config.ts");
|
|
212
|
+
if (!await fs6.pathExists(configPath)) return;
|
|
188
213
|
await ctx.trackModifiedFile(configPath);
|
|
189
|
-
const content = await
|
|
214
|
+
const content = await fs6.readFile(configPath, "utf-8");
|
|
190
215
|
const updated = content.replace(/blog:\s*false/, "blog: true");
|
|
191
|
-
await
|
|
216
|
+
await fs6.writeFile(configPath, updated);
|
|
192
217
|
}
|
|
193
218
|
function schemas() {
|
|
194
219
|
return `${STAMP}
|
|
@@ -681,6 +706,7 @@ var init_blog = __esm({
|
|
|
681
706
|
init_logger();
|
|
682
707
|
init_rollback();
|
|
683
708
|
init_dependencies();
|
|
709
|
+
init_routes();
|
|
684
710
|
GENERATOR_VERSION = "0.1.0";
|
|
685
711
|
STAMP = `// @mars-generated blog@${GENERATOR_VERSION}`;
|
|
686
712
|
}
|
|
@@ -706,11 +732,11 @@ var dark_mode_exports = {};
|
|
|
706
732
|
__export(dark_mode_exports, {
|
|
707
733
|
generateDarkMode: () => generateDarkMode
|
|
708
734
|
});
|
|
709
|
-
import
|
|
710
|
-
import
|
|
735
|
+
import fs7 from "fs-extra";
|
|
736
|
+
import path8 from "path";
|
|
711
737
|
async function generateDarkMode(projectRoot) {
|
|
712
|
-
const featureDir =
|
|
713
|
-
if (await
|
|
738
|
+
const featureDir = path8.join(projectRoot, "src", "features", "dark-mode");
|
|
739
|
+
if (await fs7.pathExists(featureDir)) {
|
|
714
740
|
log.error("Dark mode feature already exists at src/features/dark-mode/");
|
|
715
741
|
return;
|
|
716
742
|
}
|
|
@@ -725,10 +751,10 @@ async function generateDarkMode(projectRoot) {
|
|
|
725
751
|
};
|
|
726
752
|
let count = 0;
|
|
727
753
|
for (const [filePath, content] of Object.entries(files)) {
|
|
728
|
-
const fullPath =
|
|
754
|
+
const fullPath = path8.join(projectRoot, filePath);
|
|
729
755
|
ctx.trackCreatedFile(fullPath);
|
|
730
|
-
await
|
|
731
|
-
await
|
|
756
|
+
await fs7.ensureDir(path8.dirname(fullPath));
|
|
757
|
+
await fs7.writeFile(fullPath, content);
|
|
732
758
|
count++;
|
|
733
759
|
}
|
|
734
760
|
await wireLayout(projectRoot, ctx);
|
|
@@ -758,18 +784,18 @@ async function generateDarkMode(projectRoot) {
|
|
|
758
784
|
}
|
|
759
785
|
}
|
|
760
786
|
async function setConfigFlag2(projectRoot, ctx) {
|
|
761
|
-
const configPath =
|
|
762
|
-
if (!await
|
|
787
|
+
const configPath = path8.join(projectRoot, "src", "config", "app.config.ts");
|
|
788
|
+
if (!await fs7.pathExists(configPath)) return;
|
|
763
789
|
await ctx.trackModifiedFile(configPath);
|
|
764
|
-
const content = await
|
|
790
|
+
const content = await fs7.readFile(configPath, "utf-8");
|
|
765
791
|
const updated = content.replace(/darkMode:\s*false/, "darkMode: true");
|
|
766
|
-
await
|
|
792
|
+
await fs7.writeFile(configPath, updated);
|
|
767
793
|
}
|
|
768
794
|
async function wireLayout(projectRoot, ctx) {
|
|
769
|
-
const layoutPath =
|
|
770
|
-
if (!await
|
|
795
|
+
const layoutPath = path8.join(projectRoot, "src", "app", "layout.tsx");
|
|
796
|
+
if (!await fs7.pathExists(layoutPath)) return;
|
|
771
797
|
await ctx.trackModifiedFile(layoutPath);
|
|
772
|
-
let content = await
|
|
798
|
+
let content = await fs7.readFile(layoutPath, "utf-8");
|
|
773
799
|
if (!content.includes("getThemeScript")) {
|
|
774
800
|
content = `import { getThemeScript } from '@/features/dark-mode';
|
|
775
801
|
${content}`;
|
|
@@ -783,13 +809,13 @@ ${content}`;
|
|
|
783
809
|
"$1\n <head>\n <script dangerouslySetInnerHTML={{ __html: getThemeScript() }} />\n </head>\n$2"
|
|
784
810
|
);
|
|
785
811
|
}
|
|
786
|
-
await
|
|
812
|
+
await fs7.writeFile(layoutPath, content);
|
|
787
813
|
}
|
|
788
814
|
async function wireProviders(projectRoot, ctx) {
|
|
789
|
-
const providersPath =
|
|
790
|
-
if (!await
|
|
815
|
+
const providersPath = path8.join(projectRoot, "src", "app", "providers.tsx");
|
|
816
|
+
if (!await fs7.pathExists(providersPath)) return;
|
|
791
817
|
await ctx.trackModifiedFile(providersPath);
|
|
792
|
-
let content = await
|
|
818
|
+
let content = await fs7.readFile(providersPath, "utf-8");
|
|
793
819
|
if (content.includes("ThemeProvider")) return;
|
|
794
820
|
content = insertImportAfterDirectives(
|
|
795
821
|
content,
|
|
@@ -808,13 +834,13 @@ async function wireProviders(projectRoot, ctx) {
|
|
|
808
834
|
);`;
|
|
809
835
|
}
|
|
810
836
|
);
|
|
811
|
-
await
|
|
837
|
+
await fs7.writeFile(providersPath, content);
|
|
812
838
|
}
|
|
813
839
|
async function wireProtectedNav(projectRoot, ctx) {
|
|
814
|
-
const navPath =
|
|
815
|
-
if (!await
|
|
840
|
+
const navPath = path8.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
|
|
841
|
+
if (!await fs7.pathExists(navPath)) return;
|
|
816
842
|
await ctx.trackModifiedFile(navPath);
|
|
817
|
-
let content = await
|
|
843
|
+
let content = await fs7.readFile(navPath, "utf-8");
|
|
818
844
|
if (content.includes("ThemeToggleSimple")) return;
|
|
819
845
|
content = insertImportAfterDirectives(
|
|
820
846
|
content,
|
|
@@ -829,19 +855,19 @@ async function wireProtectedNav(projectRoot, ctx) {
|
|
|
829
855
|
/(<div className="border-t border-border-default px-4 py-3">\s*\n\s*<UserMenu \/>)/,
|
|
830
856
|
'$1\n <div className="mt-2">\n <ThemeToggleSimple />\n </div>'
|
|
831
857
|
);
|
|
832
|
-
await
|
|
858
|
+
await fs7.writeFile(navPath, content);
|
|
833
859
|
}
|
|
834
860
|
async function wireTailwindDarkMode(projectRoot, ctx) {
|
|
835
|
-
const globalsPath =
|
|
836
|
-
if (!await
|
|
861
|
+
const globalsPath = path8.join(projectRoot, "src", "styles", "globals.css");
|
|
862
|
+
if (!await fs7.pathExists(globalsPath)) return;
|
|
837
863
|
await ctx.trackModifiedFile(globalsPath);
|
|
838
|
-
let content = await
|
|
864
|
+
let content = await fs7.readFile(globalsPath, "utf-8");
|
|
839
865
|
if (content.includes("@custom-variant dark")) return;
|
|
840
866
|
content = content.replace(
|
|
841
867
|
/@import 'tailwindcss';/,
|
|
842
868
|
"@import 'tailwindcss';\n@custom-variant dark (&:where(.dark, .dark *));"
|
|
843
869
|
);
|
|
844
|
-
await
|
|
870
|
+
await fs7.writeFile(globalsPath, content);
|
|
845
871
|
}
|
|
846
872
|
function themeProvider() {
|
|
847
873
|
return `${STAMP2}
|
|
@@ -1121,19 +1147,19 @@ var init_dark_mode = __esm({
|
|
|
1121
1147
|
});
|
|
1122
1148
|
|
|
1123
1149
|
// src/utils/prisma.ts
|
|
1124
|
-
import
|
|
1125
|
-
import
|
|
1150
|
+
import fs8 from "fs-extra";
|
|
1151
|
+
import path9 from "path";
|
|
1126
1152
|
async function addUserRelation(projectRoot, field, ctx) {
|
|
1127
|
-
const authPath =
|
|
1128
|
-
if (!await
|
|
1153
|
+
const authPath = path9.join(projectRoot, "prisma", "schema", "auth.prisma");
|
|
1154
|
+
if (!await fs8.pathExists(authPath)) return;
|
|
1129
1155
|
await ctx.trackModifiedFile(authPath);
|
|
1130
|
-
let content = await
|
|
1156
|
+
let content = await fs8.readFile(authPath, "utf-8");
|
|
1131
1157
|
if (content.includes(field)) return;
|
|
1132
1158
|
const insertBefore = " @@index([email])";
|
|
1133
1159
|
content = content.replace(insertBefore, ` ${field}
|
|
1134
1160
|
|
|
1135
1161
|
${insertBefore}`);
|
|
1136
|
-
await
|
|
1162
|
+
await fs8.writeFile(authPath, content);
|
|
1137
1163
|
}
|
|
1138
1164
|
var init_prisma = __esm({
|
|
1139
1165
|
"src/utils/prisma.ts"() {
|
|
@@ -1146,11 +1172,11 @@ var notifications_exports = {};
|
|
|
1146
1172
|
__export(notifications_exports, {
|
|
1147
1173
|
generateNotifications: () => generateNotifications
|
|
1148
1174
|
});
|
|
1149
|
-
import
|
|
1150
|
-
import
|
|
1175
|
+
import fs9 from "fs-extra";
|
|
1176
|
+
import path10 from "path";
|
|
1151
1177
|
async function generateNotifications(projectRoot) {
|
|
1152
|
-
const featureDir =
|
|
1153
|
-
if (await
|
|
1178
|
+
const featureDir = path10.join(projectRoot, "src", "features", "notifications");
|
|
1179
|
+
if (await fs9.pathExists(featureDir)) {
|
|
1154
1180
|
log.error("Notifications feature already exists at src/features/notifications/");
|
|
1155
1181
|
return;
|
|
1156
1182
|
}
|
|
@@ -1173,10 +1199,10 @@ async function generateNotifications(projectRoot) {
|
|
|
1173
1199
|
};
|
|
1174
1200
|
let count = 0;
|
|
1175
1201
|
for (const [filePath, content] of Object.entries(files)) {
|
|
1176
|
-
const fullPath =
|
|
1202
|
+
const fullPath = path10.join(projectRoot, filePath);
|
|
1177
1203
|
ctx.trackCreatedFile(fullPath);
|
|
1178
|
-
await
|
|
1179
|
-
await
|
|
1204
|
+
await fs9.ensureDir(path10.dirname(fullPath));
|
|
1205
|
+
await fs9.writeFile(fullPath, content);
|
|
1180
1206
|
count++;
|
|
1181
1207
|
}
|
|
1182
1208
|
await addUserRelation(projectRoot, "notifications Notification[]", ctx);
|
|
@@ -1203,10 +1229,10 @@ async function generateNotifications(projectRoot) {
|
|
|
1203
1229
|
}
|
|
1204
1230
|
}
|
|
1205
1231
|
async function wireProtectedNav2(projectRoot, ctx) {
|
|
1206
|
-
const navPath =
|
|
1207
|
-
if (!await
|
|
1232
|
+
const navPath = path10.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
|
|
1233
|
+
if (!await fs9.pathExists(navPath)) return;
|
|
1208
1234
|
await ctx.trackModifiedFile(navPath);
|
|
1209
|
-
let content = await
|
|
1235
|
+
let content = await fs9.readFile(navPath, "utf-8");
|
|
1210
1236
|
if (content.includes("NotificationBell")) return;
|
|
1211
1237
|
content = insertImportAfterDirectives(
|
|
1212
1238
|
content,
|
|
@@ -1217,15 +1243,15 @@ async function wireProtectedNav2(projectRoot, ctx) {
|
|
|
1217
1243
|
/(<div className="flex items-center gap-3">)\s*\n(\s*)(<div className="hidden md:block">)/,
|
|
1218
1244
|
"$1\n$2<NotificationBell />\n$2$3"
|
|
1219
1245
|
);
|
|
1220
|
-
await
|
|
1246
|
+
await fs9.writeFile(navPath, content);
|
|
1221
1247
|
}
|
|
1222
1248
|
async function setConfigFlag3(projectRoot, ctx) {
|
|
1223
|
-
const configPath =
|
|
1224
|
-
if (!await
|
|
1249
|
+
const configPath = path10.join(projectRoot, "src", "config", "app.config.ts");
|
|
1250
|
+
if (!await fs9.pathExists(configPath)) return;
|
|
1225
1251
|
await ctx.trackModifiedFile(configPath);
|
|
1226
|
-
const content = await
|
|
1252
|
+
const content = await fs9.readFile(configPath, "utf-8");
|
|
1227
1253
|
const updated = content.replace(/notifications:\s*false/, "notifications: true");
|
|
1228
|
-
await
|
|
1254
|
+
await fs9.writeFile(configPath, updated);
|
|
1229
1255
|
}
|
|
1230
1256
|
function prismaSchema() {
|
|
1231
1257
|
return `// Generated by mars generate notifications (notifications@${GENERATOR_VERSION3})
|
|
@@ -1821,11 +1847,11 @@ var analytics_exports = {};
|
|
|
1821
1847
|
__export(analytics_exports, {
|
|
1822
1848
|
generateAnalytics: () => generateAnalytics
|
|
1823
1849
|
});
|
|
1824
|
-
import
|
|
1825
|
-
import
|
|
1850
|
+
import fs10 from "fs-extra";
|
|
1851
|
+
import path11 from "path";
|
|
1826
1852
|
async function generateAnalytics(projectRoot) {
|
|
1827
|
-
const featureDir =
|
|
1828
|
-
if (await
|
|
1853
|
+
const featureDir = path11.join(projectRoot, "src", "features", "analytics");
|
|
1854
|
+
if (await fs10.pathExists(featureDir)) {
|
|
1829
1855
|
log.error("Analytics feature already exists at src/features/analytics/");
|
|
1830
1856
|
return;
|
|
1831
1857
|
}
|
|
@@ -1840,10 +1866,10 @@ async function generateAnalytics(projectRoot) {
|
|
|
1840
1866
|
};
|
|
1841
1867
|
let count = 0;
|
|
1842
1868
|
for (const [filePath, content] of Object.entries(files)) {
|
|
1843
|
-
const fullPath =
|
|
1869
|
+
const fullPath = path11.join(projectRoot, filePath);
|
|
1844
1870
|
ctx.trackCreatedFile(fullPath);
|
|
1845
|
-
await
|
|
1846
|
-
await
|
|
1871
|
+
await fs10.ensureDir(path11.dirname(fullPath));
|
|
1872
|
+
await fs10.writeFile(fullPath, content);
|
|
1847
1873
|
count++;
|
|
1848
1874
|
}
|
|
1849
1875
|
await wireProviders2(projectRoot, ctx);
|
|
@@ -1874,10 +1900,10 @@ async function generateAnalytics(projectRoot) {
|
|
|
1874
1900
|
}
|
|
1875
1901
|
}
|
|
1876
1902
|
async function wireProviders2(projectRoot, ctx) {
|
|
1877
|
-
const providersPath =
|
|
1878
|
-
if (!await
|
|
1903
|
+
const providersPath = path11.join(projectRoot, "src", "app", "providers.tsx");
|
|
1904
|
+
if (!await fs10.pathExists(providersPath)) return;
|
|
1879
1905
|
await ctx.trackModifiedFile(providersPath);
|
|
1880
|
-
let content = await
|
|
1906
|
+
let content = await fs10.readFile(providersPath, "utf-8");
|
|
1881
1907
|
if (content.includes("AnalyticsProvider")) return;
|
|
1882
1908
|
content = insertImportAfterDirectives(
|
|
1883
1909
|
content,
|
|
@@ -1892,8 +1918,8 @@ async function wireProviders2(projectRoot, ctx) {
|
|
|
1892
1918
|
);`;
|
|
1893
1919
|
}
|
|
1894
1920
|
);
|
|
1895
|
-
await
|
|
1896
|
-
const written = await
|
|
1921
|
+
await fs10.writeFile(providersPath, content);
|
|
1922
|
+
const written = await fs10.readFile(providersPath, "utf-8");
|
|
1897
1923
|
if (!written.includes("AnalyticsProvider")) {
|
|
1898
1924
|
throw new Error(
|
|
1899
1925
|
"wireProviders: AnalyticsProvider was not inserted into providers.tsx \u2014 the return statement pattern did not match. Review the template file structure."
|
|
@@ -1901,12 +1927,12 @@ async function wireProviders2(projectRoot, ctx) {
|
|
|
1901
1927
|
}
|
|
1902
1928
|
}
|
|
1903
1929
|
async function setConfigFlag4(projectRoot, ctx) {
|
|
1904
|
-
const configPath =
|
|
1905
|
-
if (!await
|
|
1930
|
+
const configPath = path11.join(projectRoot, "src", "config", "app.config.ts");
|
|
1931
|
+
if (!await fs10.pathExists(configPath)) return;
|
|
1906
1932
|
await ctx.trackModifiedFile(configPath);
|
|
1907
|
-
const content = await
|
|
1933
|
+
const content = await fs10.readFile(configPath, "utf-8");
|
|
1908
1934
|
const updated = content.replace(/analytics:\s*false/, "analytics: true");
|
|
1909
|
-
await
|
|
1935
|
+
await fs10.writeFile(configPath, updated);
|
|
1910
1936
|
}
|
|
1911
1937
|
function types3() {
|
|
1912
1938
|
return `${STAMP4}
|
|
@@ -2265,11 +2291,11 @@ var command_palette_exports = {};
|
|
|
2265
2291
|
__export(command_palette_exports, {
|
|
2266
2292
|
generateCommandPalette: () => generateCommandPalette
|
|
2267
2293
|
});
|
|
2268
|
-
import
|
|
2269
|
-
import
|
|
2294
|
+
import fs11 from "fs-extra";
|
|
2295
|
+
import path12 from "path";
|
|
2270
2296
|
async function generateCommandPalette(projectRoot) {
|
|
2271
|
-
const featureDir =
|
|
2272
|
-
if (await
|
|
2297
|
+
const featureDir = path12.join(projectRoot, "src", "features", "command-palette");
|
|
2298
|
+
if (await fs11.pathExists(featureDir)) {
|
|
2273
2299
|
log.error("Command palette feature already exists at src/features/command-palette/");
|
|
2274
2300
|
return;
|
|
2275
2301
|
}
|
|
@@ -2287,10 +2313,10 @@ async function generateCommandPalette(projectRoot) {
|
|
|
2287
2313
|
};
|
|
2288
2314
|
let count = 0;
|
|
2289
2315
|
for (const [filePath, content] of Object.entries(files)) {
|
|
2290
|
-
const fullPath =
|
|
2316
|
+
const fullPath = path12.join(projectRoot, filePath);
|
|
2291
2317
|
ctx.trackCreatedFile(fullPath);
|
|
2292
|
-
await
|
|
2293
|
-
await
|
|
2318
|
+
await fs11.ensureDir(path12.dirname(fullPath));
|
|
2319
|
+
await fs11.writeFile(fullPath, content);
|
|
2294
2320
|
count++;
|
|
2295
2321
|
}
|
|
2296
2322
|
await addDependencies(projectRoot, { cmdk: "^1.0.0" });
|
|
@@ -2313,26 +2339,26 @@ async function generateCommandPalette(projectRoot) {
|
|
|
2313
2339
|
}
|
|
2314
2340
|
}
|
|
2315
2341
|
async function wireLayout2(projectRoot, ctx) {
|
|
2316
|
-
const layoutPath =
|
|
2317
|
-
if (!await
|
|
2318
|
-
await
|
|
2319
|
-
let content = await fs10.readFile(layoutPath, "utf-8");
|
|
2342
|
+
const layoutPath = path12.join(projectRoot, "src", "app", "layout.tsx");
|
|
2343
|
+
if (!await fs11.pathExists(layoutPath)) return;
|
|
2344
|
+
let content = await fs11.readFile(layoutPath, "utf-8");
|
|
2320
2345
|
if (content.includes("CommandPalette")) return;
|
|
2346
|
+
await ctx.trackModifiedFile(layoutPath);
|
|
2321
2347
|
content = `import { CommandPalette } from '@/features/command-palette';
|
|
2322
2348
|
${content}`;
|
|
2323
2349
|
content = content.replace(
|
|
2324
|
-
/(\s*)(<Providers
|
|
2350
|
+
/(\s*)(<Providers>[\s\S]*?<\/Providers>)/,
|
|
2325
2351
|
"$1$2\n$1<CommandPalette />"
|
|
2326
2352
|
);
|
|
2327
|
-
await
|
|
2353
|
+
await fs11.writeFile(layoutPath, content);
|
|
2328
2354
|
}
|
|
2329
2355
|
async function setConfigFlag5(projectRoot, ctx) {
|
|
2330
|
-
const configPath =
|
|
2331
|
-
if (!await
|
|
2356
|
+
const configPath = path12.join(projectRoot, "src", "config", "app.config.ts");
|
|
2357
|
+
if (!await fs11.pathExists(configPath)) return;
|
|
2332
2358
|
await ctx.trackModifiedFile(configPath);
|
|
2333
|
-
const content = await
|
|
2359
|
+
const content = await fs11.readFile(configPath, "utf-8");
|
|
2334
2360
|
const updated = content.replace(/commandPalette:\s*false/, "commandPalette: true");
|
|
2335
|
-
await
|
|
2361
|
+
await fs11.writeFile(configPath, updated);
|
|
2336
2362
|
}
|
|
2337
2363
|
function types4() {
|
|
2338
2364
|
return `${STAMP5}
|
|
@@ -2748,11 +2774,11 @@ var onboarding_exports = {};
|
|
|
2748
2774
|
__export(onboarding_exports, {
|
|
2749
2775
|
generateOnboarding: () => generateOnboarding
|
|
2750
2776
|
});
|
|
2751
|
-
import
|
|
2752
|
-
import
|
|
2777
|
+
import fs12 from "fs-extra";
|
|
2778
|
+
import path13 from "path";
|
|
2753
2779
|
async function generateOnboarding(projectRoot) {
|
|
2754
|
-
const featureDir =
|
|
2755
|
-
if (await
|
|
2780
|
+
const featureDir = path13.join(projectRoot, "src", "features", "onboarding");
|
|
2781
|
+
if (await fs12.pathExists(featureDir)) {
|
|
2756
2782
|
log.error("Onboarding feature already exists at src/features/onboarding/");
|
|
2757
2783
|
return;
|
|
2758
2784
|
}
|
|
@@ -2774,13 +2800,14 @@ async function generateOnboarding(projectRoot) {
|
|
|
2774
2800
|
};
|
|
2775
2801
|
let count = 0;
|
|
2776
2802
|
for (const [filePath, content] of Object.entries(files)) {
|
|
2777
|
-
const fullPath =
|
|
2803
|
+
const fullPath = path13.join(projectRoot, filePath);
|
|
2778
2804
|
ctx.trackCreatedFile(fullPath);
|
|
2779
|
-
await
|
|
2780
|
-
await
|
|
2805
|
+
await fs12.ensureDir(path13.dirname(fullPath));
|
|
2806
|
+
await fs12.writeFile(fullPath, content);
|
|
2781
2807
|
count++;
|
|
2782
2808
|
}
|
|
2783
2809
|
await addUserRelation(projectRoot, "onboardingProgress OnboardingProgress?", ctx);
|
|
2810
|
+
await registerRoute(projectRoot, "onboarding", "/onboarding", ctx);
|
|
2784
2811
|
await setConfigFlag6(projectRoot, ctx);
|
|
2785
2812
|
await ctx.commit();
|
|
2786
2813
|
log.success(`Generated onboarding feature with ${count} files`);
|
|
@@ -2801,12 +2828,12 @@ async function generateOnboarding(projectRoot) {
|
|
|
2801
2828
|
}
|
|
2802
2829
|
}
|
|
2803
2830
|
async function setConfigFlag6(projectRoot, ctx) {
|
|
2804
|
-
const configPath =
|
|
2805
|
-
if (!await
|
|
2831
|
+
const configPath = path13.join(projectRoot, "src", "config", "app.config.ts");
|
|
2832
|
+
if (!await fs12.pathExists(configPath)) return;
|
|
2806
2833
|
await ctx.trackModifiedFile(configPath);
|
|
2807
|
-
const content = await
|
|
2834
|
+
const content = await fs12.readFile(configPath, "utf-8");
|
|
2808
2835
|
const updated = content.replace(/onboarding:\s*false/, "onboarding: true");
|
|
2809
|
-
await
|
|
2836
|
+
await fs12.writeFile(configPath, updated);
|
|
2810
2837
|
}
|
|
2811
2838
|
function prismaSchema2() {
|
|
2812
2839
|
return `// Generated by mars generate onboarding (onboarding@${GENERATOR_VERSION6})
|
|
@@ -3322,6 +3349,7 @@ var init_onboarding = __esm({
|
|
|
3322
3349
|
init_logger();
|
|
3323
3350
|
init_rollback();
|
|
3324
3351
|
init_prisma();
|
|
3352
|
+
init_routes();
|
|
3325
3353
|
GENERATOR_VERSION6 = "0.1.0";
|
|
3326
3354
|
STAMP6 = `// @mars-generated onboarding@${GENERATOR_VERSION6}`;
|
|
3327
3355
|
}
|
|
@@ -3332,11 +3360,11 @@ var search_exports = {};
|
|
|
3332
3360
|
__export(search_exports, {
|
|
3333
3361
|
generateSearch: () => generateSearch
|
|
3334
3362
|
});
|
|
3335
|
-
import
|
|
3336
|
-
import
|
|
3363
|
+
import fs13 from "fs-extra";
|
|
3364
|
+
import path14 from "path";
|
|
3337
3365
|
async function generateSearch(projectRoot) {
|
|
3338
|
-
const featureDir =
|
|
3339
|
-
if (await
|
|
3366
|
+
const featureDir = path14.join(projectRoot, "src", "features", "search");
|
|
3367
|
+
if (await fs13.pathExists(featureDir)) {
|
|
3340
3368
|
log.error("Search feature already exists at src/features/search/");
|
|
3341
3369
|
return;
|
|
3342
3370
|
}
|
|
@@ -3349,25 +3377,30 @@ async function generateSearch(projectRoot) {
|
|
|
3349
3377
|
"src/features/search/server/index.ts": serverIndex(),
|
|
3350
3378
|
"src/features/search/hooks/use-search.ts": useSearchHook(),
|
|
3351
3379
|
"src/features/search/components/SearchInput.tsx": searchInput(),
|
|
3380
|
+
"src/features/search/components/NavSearch.tsx": navSearch(),
|
|
3352
3381
|
"src/features/search/components/index.ts": componentIndex5(),
|
|
3353
3382
|
"src/features/search/index.ts": featureIndex4(),
|
|
3354
3383
|
"src/app/api/protected/search/route.ts": searchRoute()
|
|
3355
3384
|
};
|
|
3356
3385
|
let count = 0;
|
|
3357
3386
|
for (const [filePath, content] of Object.entries(files)) {
|
|
3358
|
-
const fullPath =
|
|
3387
|
+
const fullPath = path14.join(projectRoot, filePath);
|
|
3359
3388
|
ctx.trackCreatedFile(fullPath);
|
|
3360
|
-
await
|
|
3361
|
-
await
|
|
3389
|
+
await fs13.ensureDir(path14.dirname(fullPath));
|
|
3390
|
+
await fs13.writeFile(fullPath, content);
|
|
3362
3391
|
count++;
|
|
3363
3392
|
}
|
|
3393
|
+
await wireProtectedNav3(projectRoot, ctx);
|
|
3364
3394
|
await setConfigFlag7(projectRoot, ctx);
|
|
3365
3395
|
await ctx.commit();
|
|
3366
|
-
log.success(`Generated search feature
|
|
3396
|
+
log.success(`Generated and wired search feature (${count} files created)`);
|
|
3367
3397
|
log.blank();
|
|
3368
3398
|
log.step("src/features/search/ \u2014 types, validation, server logic, hooks, components");
|
|
3369
3399
|
log.step("src/app/api/protected/search/ \u2014 authenticated search endpoint");
|
|
3370
3400
|
log.blank();
|
|
3401
|
+
log.step("Wired automatically:");
|
|
3402
|
+
log.step(" \u2713 NavSearch added to navigation bar");
|
|
3403
|
+
log.blank();
|
|
3371
3404
|
log.warn("Search uses Postgres full-text search by default (no extra setup).");
|
|
3372
3405
|
log.blank();
|
|
3373
3406
|
log.step("For Algolia: yarn add algoliasearch + set ALGOLIA_APP_ID, ALGOLIA_API_KEY env vars");
|
|
@@ -3381,13 +3414,30 @@ async function generateSearch(projectRoot) {
|
|
|
3381
3414
|
throw error;
|
|
3382
3415
|
}
|
|
3383
3416
|
}
|
|
3417
|
+
async function wireProtectedNav3(projectRoot, ctx) {
|
|
3418
|
+
const navPath = path14.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
|
|
3419
|
+
if (!await fs13.pathExists(navPath)) return;
|
|
3420
|
+
let content = await fs13.readFile(navPath, "utf-8");
|
|
3421
|
+
if (content.includes("NavSearch")) return;
|
|
3422
|
+
await ctx.trackModifiedFile(navPath);
|
|
3423
|
+
content = insertImportAfterDirectives(
|
|
3424
|
+
content,
|
|
3425
|
+
`import { NavSearch } from '@/features/search';
|
|
3426
|
+
`
|
|
3427
|
+
);
|
|
3428
|
+
content = content.replace(
|
|
3429
|
+
/(<div className="flex items-center gap-3">)\s*\n(\s*)(<)/,
|
|
3430
|
+
"$1\n$2<NavSearch />\n$2$3"
|
|
3431
|
+
);
|
|
3432
|
+
await fs13.writeFile(navPath, content);
|
|
3433
|
+
}
|
|
3384
3434
|
async function setConfigFlag7(projectRoot, ctx) {
|
|
3385
|
-
const configPath =
|
|
3386
|
-
if (!await
|
|
3435
|
+
const configPath = path14.join(projectRoot, "src", "config", "app.config.ts");
|
|
3436
|
+
if (!await fs13.pathExists(configPath)) return;
|
|
3387
3437
|
await ctx.trackModifiedFile(configPath);
|
|
3388
|
-
const content = await
|
|
3438
|
+
const content = await fs13.readFile(configPath, "utf-8");
|
|
3389
3439
|
const updated = content.replace(/search:\s*false/, "search: true");
|
|
3390
|
-
await
|
|
3440
|
+
await fs13.writeFile(configPath, updated);
|
|
3391
3441
|
}
|
|
3392
3442
|
function types6() {
|
|
3393
3443
|
return `${STAMP7}
|
|
@@ -3657,9 +3707,31 @@ export function SearchInput({
|
|
|
3657
3707
|
}
|
|
3658
3708
|
`;
|
|
3659
3709
|
}
|
|
3710
|
+
function navSearch() {
|
|
3711
|
+
return `${STAMP7}
|
|
3712
|
+
'use client';
|
|
3713
|
+
|
|
3714
|
+
import { useState } from 'react';
|
|
3715
|
+
import { SearchInput } from './SearchInput';
|
|
3716
|
+
|
|
3717
|
+
export function NavSearch() {
|
|
3718
|
+
const [query, setQuery] = useState('');
|
|
3719
|
+
|
|
3720
|
+
return (
|
|
3721
|
+
<SearchInput
|
|
3722
|
+
value={query}
|
|
3723
|
+
onQueryChange={setQuery}
|
|
3724
|
+
placeholder="Search\u2026"
|
|
3725
|
+
className="w-48 lg:w-64"
|
|
3726
|
+
/>
|
|
3727
|
+
);
|
|
3728
|
+
}
|
|
3729
|
+
`;
|
|
3730
|
+
}
|
|
3660
3731
|
function componentIndex5() {
|
|
3661
3732
|
return `${STAMP7}
|
|
3662
3733
|
export { SearchInput } from './SearchInput';
|
|
3734
|
+
export { NavSearch } from './NavSearch';
|
|
3663
3735
|
`;
|
|
3664
3736
|
}
|
|
3665
3737
|
function featureIndex4() {
|
|
@@ -3667,7 +3739,7 @@ function featureIndex4() {
|
|
|
3667
3739
|
export type { SearchResult, SearchOptions, SearchProvider } from './types';
|
|
3668
3740
|
export { searchParamsSchema } from './validation/schemas';
|
|
3669
3741
|
export type { SearchParams } from './validation/schemas';
|
|
3670
|
-
export { SearchInput } from './components';
|
|
3742
|
+
export { SearchInput, NavSearch } from './components';
|
|
3671
3743
|
export { useSearch } from './hooks/use-search';
|
|
3672
3744
|
`;
|
|
3673
3745
|
}
|
|
@@ -3706,6 +3778,7 @@ var GENERATOR_VERSION7, STAMP7;
|
|
|
3706
3778
|
var init_search = __esm({
|
|
3707
3779
|
"src/generators/features/search.ts"() {
|
|
3708
3780
|
"use strict";
|
|
3781
|
+
init_client_file_patch();
|
|
3709
3782
|
init_logger();
|
|
3710
3783
|
init_rollback();
|
|
3711
3784
|
GENERATOR_VERSION7 = "0.1.0";
|
|
@@ -3718,11 +3791,11 @@ var realtime_exports = {};
|
|
|
3718
3791
|
__export(realtime_exports, {
|
|
3719
3792
|
generateRealtime: () => generateRealtime
|
|
3720
3793
|
});
|
|
3721
|
-
import
|
|
3722
|
-
import
|
|
3794
|
+
import fs14 from "fs-extra";
|
|
3795
|
+
import path15 from "path";
|
|
3723
3796
|
async function generateRealtime(projectRoot) {
|
|
3724
|
-
const featureDir =
|
|
3725
|
-
if (await
|
|
3797
|
+
const featureDir = path15.join(projectRoot, "src", "features", "realtime");
|
|
3798
|
+
if (await fs14.pathExists(featureDir)) {
|
|
3726
3799
|
log.error("Realtime feature already exists at src/features/realtime/");
|
|
3727
3800
|
return;
|
|
3728
3801
|
}
|
|
@@ -3739,10 +3812,10 @@ async function generateRealtime(projectRoot) {
|
|
|
3739
3812
|
};
|
|
3740
3813
|
let count = 0;
|
|
3741
3814
|
for (const [filePath, content] of Object.entries(files)) {
|
|
3742
|
-
const fullPath =
|
|
3815
|
+
const fullPath = path15.join(projectRoot, filePath);
|
|
3743
3816
|
ctx.trackCreatedFile(fullPath);
|
|
3744
|
-
await
|
|
3745
|
-
await
|
|
3817
|
+
await fs14.ensureDir(path15.dirname(fullPath));
|
|
3818
|
+
await fs14.writeFile(fullPath, content);
|
|
3746
3819
|
count++;
|
|
3747
3820
|
}
|
|
3748
3821
|
await setConfigFlag8(projectRoot, ctx);
|
|
@@ -3767,12 +3840,12 @@ async function generateRealtime(projectRoot) {
|
|
|
3767
3840
|
}
|
|
3768
3841
|
}
|
|
3769
3842
|
async function setConfigFlag8(projectRoot, ctx) {
|
|
3770
|
-
const configPath =
|
|
3771
|
-
if (!await
|
|
3843
|
+
const configPath = path15.join(projectRoot, "src", "config", "app.config.ts");
|
|
3844
|
+
if (!await fs14.pathExists(configPath)) return;
|
|
3772
3845
|
await ctx.trackModifiedFile(configPath);
|
|
3773
|
-
const content = await
|
|
3846
|
+
const content = await fs14.readFile(configPath, "utf-8");
|
|
3774
3847
|
const updated = content.replace(/realtime:\s*false/, "realtime: true");
|
|
3775
|
-
await
|
|
3848
|
+
await fs14.writeFile(configPath, updated);
|
|
3776
3849
|
}
|
|
3777
3850
|
function types7() {
|
|
3778
3851
|
return `${STAMP8}
|
|
@@ -4081,11 +4154,11 @@ var ai_exports = {};
|
|
|
4081
4154
|
__export(ai_exports, {
|
|
4082
4155
|
generateAI: () => generateAI
|
|
4083
4156
|
});
|
|
4084
|
-
import
|
|
4085
|
-
import
|
|
4157
|
+
import fs15 from "fs-extra";
|
|
4158
|
+
import path16 from "path";
|
|
4086
4159
|
async function generateAI(projectRoot) {
|
|
4087
|
-
const featureDir =
|
|
4088
|
-
if (await
|
|
4160
|
+
const featureDir = path16.join(projectRoot, "src", "features", "ai");
|
|
4161
|
+
if (await fs15.pathExists(featureDir)) {
|
|
4089
4162
|
log.error("AI feature already exists at src/features/ai/");
|
|
4090
4163
|
return;
|
|
4091
4164
|
}
|
|
@@ -4103,10 +4176,10 @@ async function generateAI(projectRoot) {
|
|
|
4103
4176
|
};
|
|
4104
4177
|
let count = 0;
|
|
4105
4178
|
for (const [filePath, content] of Object.entries(files)) {
|
|
4106
|
-
const fullPath =
|
|
4179
|
+
const fullPath = path16.join(projectRoot, filePath);
|
|
4107
4180
|
ctx.trackCreatedFile(fullPath);
|
|
4108
|
-
await
|
|
4109
|
-
await
|
|
4181
|
+
await fs15.ensureDir(path16.dirname(fullPath));
|
|
4182
|
+
await fs15.writeFile(fullPath, content);
|
|
4110
4183
|
count++;
|
|
4111
4184
|
}
|
|
4112
4185
|
await addDependencies(projectRoot, {
|
|
@@ -4132,12 +4205,12 @@ async function generateAI(projectRoot) {
|
|
|
4132
4205
|
}
|
|
4133
4206
|
}
|
|
4134
4207
|
async function setConfigFlag9(projectRoot, ctx) {
|
|
4135
|
-
const configPath =
|
|
4136
|
-
if (!await
|
|
4208
|
+
const configPath = path16.join(projectRoot, "src", "config", "app.config.ts");
|
|
4209
|
+
if (!await fs15.pathExists(configPath)) return;
|
|
4137
4210
|
await ctx.trackModifiedFile(configPath);
|
|
4138
|
-
const content = await
|
|
4211
|
+
const content = await fs15.readFile(configPath, "utf-8");
|
|
4139
4212
|
const updated = content.replace(/ai:\s*false/, "ai: true");
|
|
4140
|
-
await
|
|
4213
|
+
await fs15.writeFile(configPath, updated);
|
|
4141
4214
|
}
|
|
4142
4215
|
function types8() {
|
|
4143
4216
|
return `${STAMP9}
|
|
@@ -4576,11 +4649,11 @@ var cookie_consent_exports = {};
|
|
|
4576
4649
|
__export(cookie_consent_exports, {
|
|
4577
4650
|
generateCookieConsent: () => generateCookieConsent
|
|
4578
4651
|
});
|
|
4579
|
-
import
|
|
4580
|
-
import
|
|
4652
|
+
import fs16 from "fs-extra";
|
|
4653
|
+
import path17 from "path";
|
|
4581
4654
|
async function generateCookieConsent(projectRoot) {
|
|
4582
|
-
const featureDir =
|
|
4583
|
-
if (await
|
|
4655
|
+
const featureDir = path17.join(projectRoot, "src", "features", "cookie-consent");
|
|
4656
|
+
if (await fs16.pathExists(featureDir)) {
|
|
4584
4657
|
log.error("Cookie consent feature already exists at src/features/cookie-consent/");
|
|
4585
4658
|
return;
|
|
4586
4659
|
}
|
|
@@ -4596,10 +4669,10 @@ async function generateCookieConsent(projectRoot) {
|
|
|
4596
4669
|
};
|
|
4597
4670
|
let count = 0;
|
|
4598
4671
|
for (const [filePath, content] of Object.entries(files)) {
|
|
4599
|
-
const fullPath =
|
|
4672
|
+
const fullPath = path17.join(projectRoot, filePath);
|
|
4600
4673
|
ctx.trackCreatedFile(fullPath);
|
|
4601
|
-
await
|
|
4602
|
-
await
|
|
4674
|
+
await fs16.ensureDir(path17.dirname(fullPath));
|
|
4675
|
+
await fs16.writeFile(fullPath, content);
|
|
4603
4676
|
count++;
|
|
4604
4677
|
}
|
|
4605
4678
|
await wireLayout3(projectRoot, ctx);
|
|
@@ -4624,26 +4697,26 @@ async function generateCookieConsent(projectRoot) {
|
|
|
4624
4697
|
}
|
|
4625
4698
|
}
|
|
4626
4699
|
async function wireLayout3(projectRoot, ctx) {
|
|
4627
|
-
const layoutPath =
|
|
4628
|
-
if (!await
|
|
4629
|
-
await
|
|
4630
|
-
let content = await fs15.readFile(layoutPath, "utf-8");
|
|
4700
|
+
const layoutPath = path17.join(projectRoot, "src", "app", "layout.tsx");
|
|
4701
|
+
if (!await fs16.pathExists(layoutPath)) return;
|
|
4702
|
+
let content = await fs16.readFile(layoutPath, "utf-8");
|
|
4631
4703
|
if (content.includes("CookieConsentBanner")) return;
|
|
4704
|
+
await ctx.trackModifiedFile(layoutPath);
|
|
4632
4705
|
content = `import { CookieConsentBanner } from '@/features/cookie-consent';
|
|
4633
4706
|
${content}`;
|
|
4634
4707
|
content = content.replace(
|
|
4635
|
-
/(\s*)(<Providers
|
|
4708
|
+
/(\s*)(<Providers>[\s\S]*?<\/Providers>)/,
|
|
4636
4709
|
"$1$2\n$1<CookieConsentBanner />"
|
|
4637
4710
|
);
|
|
4638
|
-
await
|
|
4711
|
+
await fs16.writeFile(layoutPath, content);
|
|
4639
4712
|
}
|
|
4640
4713
|
async function setConfigFlag10(projectRoot, ctx) {
|
|
4641
|
-
const configPath =
|
|
4642
|
-
if (!await
|
|
4714
|
+
const configPath = path17.join(projectRoot, "src", "config", "app.config.ts");
|
|
4715
|
+
if (!await fs16.pathExists(configPath)) return;
|
|
4643
4716
|
await ctx.trackModifiedFile(configPath);
|
|
4644
|
-
const content = await
|
|
4717
|
+
const content = await fs16.readFile(configPath, "utf-8");
|
|
4645
4718
|
const updated = content.replace(/cookieConsent:\s*false/, "cookieConsent: true");
|
|
4646
|
-
await
|
|
4719
|
+
await fs16.writeFile(configPath, updated);
|
|
4647
4720
|
}
|
|
4648
4721
|
function types9() {
|
|
4649
4722
|
return `${STAMP10}
|
|
@@ -4928,11 +5001,11 @@ var coming_soon_exports = {};
|
|
|
4928
5001
|
__export(coming_soon_exports, {
|
|
4929
5002
|
generateComingSoon: () => generateComingSoon
|
|
4930
5003
|
});
|
|
4931
|
-
import
|
|
4932
|
-
import
|
|
5004
|
+
import fs17 from "fs-extra";
|
|
5005
|
+
import path18 from "path";
|
|
4933
5006
|
async function generateComingSoon(projectRoot) {
|
|
4934
|
-
const featureDir =
|
|
4935
|
-
if (await
|
|
5007
|
+
const featureDir = path18.join(projectRoot, "src", "features", "coming-soon");
|
|
5008
|
+
if (await fs17.pathExists(featureDir)) {
|
|
4936
5009
|
log.error("Coming soon feature already exists at src/features/coming-soon/");
|
|
4937
5010
|
return;
|
|
4938
5011
|
}
|
|
@@ -4949,12 +5022,13 @@ async function generateComingSoon(projectRoot) {
|
|
|
4949
5022
|
};
|
|
4950
5023
|
let count = 0;
|
|
4951
5024
|
for (const [filePath, content] of Object.entries(files)) {
|
|
4952
|
-
const fullPath =
|
|
5025
|
+
const fullPath = path18.join(projectRoot, filePath);
|
|
4953
5026
|
ctx.trackCreatedFile(fullPath);
|
|
4954
|
-
await
|
|
4955
|
-
await
|
|
5027
|
+
await fs17.ensureDir(path18.dirname(fullPath));
|
|
5028
|
+
await fs17.writeFile(fullPath, content);
|
|
4956
5029
|
count++;
|
|
4957
5030
|
}
|
|
5031
|
+
await registerRoute(projectRoot, "comingSoon", "/coming-soon", ctx);
|
|
4958
5032
|
await setConfigFlag11(projectRoot, ctx);
|
|
4959
5033
|
await ctx.commit();
|
|
4960
5034
|
log.success(`Generated coming soon feature with ${count} files`);
|
|
@@ -4973,12 +5047,12 @@ async function generateComingSoon(projectRoot) {
|
|
|
4973
5047
|
}
|
|
4974
5048
|
}
|
|
4975
5049
|
async function setConfigFlag11(projectRoot, ctx) {
|
|
4976
|
-
const configPath =
|
|
4977
|
-
if (!await
|
|
5050
|
+
const configPath = path18.join(projectRoot, "src", "config", "app.config.ts");
|
|
5051
|
+
if (!await fs17.pathExists(configPath)) return;
|
|
4978
5052
|
await ctx.trackModifiedFile(configPath);
|
|
4979
|
-
const content = await
|
|
5053
|
+
const content = await fs17.readFile(configPath, "utf-8");
|
|
4980
5054
|
const updated = content.replace(/comingSoon:\s*false/, "comingSoon: true");
|
|
4981
|
-
await
|
|
5055
|
+
await fs17.writeFile(configPath, updated);
|
|
4982
5056
|
}
|
|
4983
5057
|
function types10() {
|
|
4984
5058
|
return `${STAMP11}
|
|
@@ -5155,6 +5229,7 @@ var init_coming_soon = __esm({
|
|
|
5155
5229
|
"use strict";
|
|
5156
5230
|
init_logger();
|
|
5157
5231
|
init_rollback();
|
|
5232
|
+
init_routes();
|
|
5158
5233
|
GENERATOR_VERSION11 = "0.1.0";
|
|
5159
5234
|
STAMP11 = `// @mars-generated coming-soon@${GENERATOR_VERSION11}`;
|
|
5160
5235
|
}
|
|
@@ -5165,11 +5240,11 @@ var sentry_exports = {};
|
|
|
5165
5240
|
__export(sentry_exports, {
|
|
5166
5241
|
generateSentry: () => generateSentry
|
|
5167
5242
|
});
|
|
5168
|
-
import
|
|
5169
|
-
import
|
|
5243
|
+
import fs18 from "fs-extra";
|
|
5244
|
+
import path19 from "path";
|
|
5170
5245
|
async function generateSentry(projectRoot) {
|
|
5171
|
-
const featureDir =
|
|
5172
|
-
if (await
|
|
5246
|
+
const featureDir = path19.join(projectRoot, "src", "features", "sentry");
|
|
5247
|
+
if (await fs18.pathExists(featureDir)) {
|
|
5173
5248
|
log.error("Sentry feature already exists at src/features/sentry/");
|
|
5174
5249
|
return;
|
|
5175
5250
|
}
|
|
@@ -5185,24 +5260,27 @@ async function generateSentry(projectRoot) {
|
|
|
5185
5260
|
};
|
|
5186
5261
|
let count = 0;
|
|
5187
5262
|
for (const [filePath, content] of Object.entries(files)) {
|
|
5188
|
-
const fullPath =
|
|
5263
|
+
const fullPath = path19.join(projectRoot, filePath);
|
|
5189
5264
|
ctx.trackCreatedFile(fullPath);
|
|
5190
|
-
await
|
|
5191
|
-
await
|
|
5265
|
+
await fs18.ensureDir(path19.dirname(fullPath));
|
|
5266
|
+
await fs18.writeFile(fullPath, content);
|
|
5192
5267
|
count++;
|
|
5193
5268
|
}
|
|
5194
5269
|
await addDependencies(projectRoot, { "@sentry/nextjs": "^8.0.0" });
|
|
5270
|
+
await wireRootLayout(projectRoot, ctx);
|
|
5195
5271
|
await setConfigFlag12(projectRoot, ctx);
|
|
5196
5272
|
await ctx.commit();
|
|
5197
|
-
log.success(`Generated sentry feature
|
|
5273
|
+
log.success(`Generated and wired sentry feature (${count} files created)`);
|
|
5198
5274
|
log.blank();
|
|
5199
5275
|
log.step("src/features/sentry/ \u2014 types, client-init, server-init, ErrorBoundary");
|
|
5200
5276
|
log.blank();
|
|
5277
|
+
log.step("Wired automatically:");
|
|
5278
|
+
log.step(" \u2713 ErrorBoundary wrapping app content in root layout");
|
|
5279
|
+
log.blank();
|
|
5201
5280
|
log.warn("Optional: run npx @sentry/wizard@latest -i nextjs for full setup");
|
|
5202
5281
|
log.blank();
|
|
5203
5282
|
log.warn("Next steps:");
|
|
5204
5283
|
log.step("Set NEXT_PUBLIC_SENTRY_DSN and SENTRY_DSN environment variables");
|
|
5205
|
-
log.step("Wrap pages or layouts with <ErrorBoundary> for graceful error handling");
|
|
5206
5284
|
log.step("Import client-init and server-init in your instrumentation files");
|
|
5207
5285
|
log.blank();
|
|
5208
5286
|
} catch (error) {
|
|
@@ -5210,13 +5288,27 @@ async function generateSentry(projectRoot) {
|
|
|
5210
5288
|
throw error;
|
|
5211
5289
|
}
|
|
5212
5290
|
}
|
|
5291
|
+
async function wireRootLayout(projectRoot, ctx) {
|
|
5292
|
+
const layoutPath = path19.join(projectRoot, "src", "app", "layout.tsx");
|
|
5293
|
+
if (!await fs18.pathExists(layoutPath)) return;
|
|
5294
|
+
let content = await fs18.readFile(layoutPath, "utf-8");
|
|
5295
|
+
if (content.includes("ErrorBoundary")) return;
|
|
5296
|
+
await ctx.trackModifiedFile(layoutPath);
|
|
5297
|
+
content = `import { ErrorBoundary } from '@/features/sentry';
|
|
5298
|
+
${content}`;
|
|
5299
|
+
content = content.replace(
|
|
5300
|
+
/(<Providers>)\{children\}(<\/Providers>)/,
|
|
5301
|
+
"$1<ErrorBoundary>{children}</ErrorBoundary>$2"
|
|
5302
|
+
);
|
|
5303
|
+
await fs18.writeFile(layoutPath, content);
|
|
5304
|
+
}
|
|
5213
5305
|
async function setConfigFlag12(projectRoot, ctx) {
|
|
5214
|
-
const configPath =
|
|
5215
|
-
if (!await
|
|
5306
|
+
const configPath = path19.join(projectRoot, "src", "config", "app.config.ts");
|
|
5307
|
+
if (!await fs18.pathExists(configPath)) return;
|
|
5216
5308
|
await ctx.trackModifiedFile(configPath);
|
|
5217
|
-
const content = await
|
|
5309
|
+
const content = await fs18.readFile(configPath, "utf-8");
|
|
5218
5310
|
const updated = content.replace(/sentry:\s*false/, "sentry: true");
|
|
5219
|
-
await
|
|
5311
|
+
await fs18.writeFile(configPath, updated);
|
|
5220
5312
|
}
|
|
5221
5313
|
function types11() {
|
|
5222
5314
|
return `${STAMP12}
|
|
@@ -5361,11 +5453,11 @@ var feature_flags_exports = {};
|
|
|
5361
5453
|
__export(feature_flags_exports, {
|
|
5362
5454
|
generateFeatureFlags: () => generateFeatureFlags
|
|
5363
5455
|
});
|
|
5364
|
-
import
|
|
5365
|
-
import
|
|
5456
|
+
import fs19 from "fs-extra";
|
|
5457
|
+
import path20 from "path";
|
|
5366
5458
|
async function generateFeatureFlags(projectRoot) {
|
|
5367
|
-
const featureDir =
|
|
5368
|
-
if (await
|
|
5459
|
+
const featureDir = path20.join(projectRoot, "src", "features", "feature-flags");
|
|
5460
|
+
if (await fs19.pathExists(featureDir)) {
|
|
5369
5461
|
log.error("Feature flags feature already exists at src/features/feature-flags/");
|
|
5370
5462
|
return;
|
|
5371
5463
|
}
|
|
@@ -5384,10 +5476,10 @@ async function generateFeatureFlags(projectRoot) {
|
|
|
5384
5476
|
};
|
|
5385
5477
|
let count = 0;
|
|
5386
5478
|
for (const [filePath, content] of Object.entries(files)) {
|
|
5387
|
-
const fullPath =
|
|
5479
|
+
const fullPath = path20.join(projectRoot, filePath);
|
|
5388
5480
|
ctx.trackCreatedFile(fullPath);
|
|
5389
|
-
await
|
|
5390
|
-
await
|
|
5481
|
+
await fs19.ensureDir(path20.dirname(fullPath));
|
|
5482
|
+
await fs19.writeFile(fullPath, content);
|
|
5391
5483
|
count++;
|
|
5392
5484
|
}
|
|
5393
5485
|
await setConfigFlag13(projectRoot, ctx);
|
|
@@ -5407,12 +5499,12 @@ async function generateFeatureFlags(projectRoot) {
|
|
|
5407
5499
|
}
|
|
5408
5500
|
}
|
|
5409
5501
|
async function setConfigFlag13(projectRoot, ctx) {
|
|
5410
|
-
const configPath =
|
|
5411
|
-
if (!await
|
|
5502
|
+
const configPath = path20.join(projectRoot, "src", "config", "app.config.ts");
|
|
5503
|
+
if (!await fs19.pathExists(configPath)) return;
|
|
5412
5504
|
await ctx.trackModifiedFile(configPath);
|
|
5413
|
-
const content = await
|
|
5505
|
+
const content = await fs19.readFile(configPath, "utf-8");
|
|
5414
5506
|
const updated = content.replace(/featureFlags:\s*false/, "featureFlags: true");
|
|
5415
|
-
await
|
|
5507
|
+
await fs19.writeFile(configPath, updated);
|
|
5416
5508
|
}
|
|
5417
5509
|
function types12() {
|
|
5418
5510
|
return `${STAMP13}
|
|
@@ -5650,27 +5742,43 @@ var init_feature_flags = __esm({
|
|
|
5650
5742
|
// src/index.ts
|
|
5651
5743
|
import { Command } from "commander";
|
|
5652
5744
|
|
|
5745
|
+
// src/utils/version.ts
|
|
5746
|
+
import { createRequire } from "module";
|
|
5747
|
+
import path from "path";
|
|
5748
|
+
import { fileURLToPath } from "url";
|
|
5749
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5750
|
+
function readVersionFromPackageJson(packageDir) {
|
|
5751
|
+
const require2 = createRequire(import.meta.url);
|
|
5752
|
+
const pkg = require2(path.join(packageDir, "package.json"));
|
|
5753
|
+
const v = pkg.version?.trim();
|
|
5754
|
+
return v ? v : "0.0.0";
|
|
5755
|
+
}
|
|
5756
|
+
function getCliVersion() {
|
|
5757
|
+
const packageRoot = path.resolve(__dirname, "..", "..");
|
|
5758
|
+
return readVersionFromPackageJson(packageRoot);
|
|
5759
|
+
}
|
|
5760
|
+
|
|
5653
5761
|
// src/commands/create.ts
|
|
5654
5762
|
init_logger();
|
|
5655
|
-
import
|
|
5656
|
-
import
|
|
5763
|
+
import fs21 from "fs-extra";
|
|
5764
|
+
import path22 from "path";
|
|
5657
5765
|
import os3 from "os";
|
|
5658
5766
|
import ora from "ora";
|
|
5659
5767
|
import pc2 from "picocolors";
|
|
5660
5768
|
import prompts5 from "prompts";
|
|
5661
5769
|
|
|
5662
5770
|
// src/utils/template.ts
|
|
5663
|
-
import
|
|
5664
|
-
import { fileURLToPath } from "url";
|
|
5771
|
+
import path2 from "path";
|
|
5772
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5665
5773
|
import fs from "fs-extra";
|
|
5666
|
-
var
|
|
5774
|
+
var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
|
|
5667
5775
|
function getTemplatePath() {
|
|
5668
5776
|
const candidates = [
|
|
5669
5777
|
// Built bundle: __dirname = packages/cli/dist/
|
|
5670
|
-
|
|
5671
|
-
|
|
5778
|
+
path2.resolve(__dirname2, "..", "template"),
|
|
5779
|
+
path2.resolve(__dirname2, "..", "..", "..", "template"),
|
|
5672
5780
|
// Source (vitest): __dirname = packages/cli/src/utils/
|
|
5673
|
-
|
|
5781
|
+
path2.resolve(__dirname2, "..", "..", "..", "..", "template")
|
|
5674
5782
|
];
|
|
5675
5783
|
for (const candidate of candidates) {
|
|
5676
5784
|
if (fs.existsSync(candidate)) {
|
|
@@ -5682,7 +5790,7 @@ function getTemplatePath() {
|
|
|
5682
5790
|
);
|
|
5683
5791
|
}
|
|
5684
5792
|
function resolveProjectPath(projectName) {
|
|
5685
|
-
return
|
|
5793
|
+
return path2.resolve(process.cwd(), projectName);
|
|
5686
5794
|
}
|
|
5687
5795
|
|
|
5688
5796
|
// src/prompts/project-info.ts
|
|
@@ -6032,7 +6140,6 @@ var DESIGN_DIRECTIONS = [
|
|
|
6032
6140
|
function getDefaultTheme() {
|
|
6033
6141
|
return {
|
|
6034
6142
|
primaryColor: COLOR_PRESETS[0].value,
|
|
6035
|
-
secondaryColor: "amber-400",
|
|
6036
6143
|
font: FONT_CHOICES[0].value,
|
|
6037
6144
|
designDirection: DESIGN_DIRECTIONS[0].value
|
|
6038
6145
|
};
|
|
@@ -6073,7 +6180,6 @@ async function promptTheme() {
|
|
|
6073
6180
|
if (response.primaryColor === void 0) return null;
|
|
6074
6181
|
return {
|
|
6075
6182
|
primaryColor: response.primaryColor,
|
|
6076
|
-
secondaryColor: "amber-400",
|
|
6077
6183
|
font: response.font,
|
|
6078
6184
|
designDirection: response.designDirection
|
|
6079
6185
|
};
|
|
@@ -6082,7 +6188,7 @@ async function promptTheme() {
|
|
|
6082
6188
|
// src/generators/scaffold.ts
|
|
6083
6189
|
init_logger();
|
|
6084
6190
|
import fs2 from "fs-extra";
|
|
6085
|
-
import
|
|
6191
|
+
import path3 from "path";
|
|
6086
6192
|
import { generateBrandCss } from "@mars-stack/ui/utils";
|
|
6087
6193
|
|
|
6088
6194
|
// src/generators/app-config.ts
|
|
@@ -6125,15 +6231,7 @@ function generateAppConfig(config) {
|
|
|
6125
6231
|
address: '',
|
|
6126
6232
|
},
|
|
6127
6233
|
theme: {
|
|
6128
|
-
primaryColor: '${config.theme.primaryColor}' as string,
|
|
6129
|
-
secondaryColor: '${config.theme.secondaryColor}' as string,
|
|
6130
6234
|
font: '${config.theme.font}' as string,
|
|
6131
|
-
designDirection: '${config.theme.designDirection}' as
|
|
6132
|
-
| 'modern-saas'
|
|
6133
|
-
| 'minimal'
|
|
6134
|
-
| 'enterprise'
|
|
6135
|
-
| 'creative'
|
|
6136
|
-
| 'dashboard',
|
|
6137
6235
|
},
|
|
6138
6236
|
features: {
|
|
6139
6237
|
${featureEntries}
|
|
@@ -6276,8 +6374,8 @@ async function copyTemplateFiles(templateDir, targetDir) {
|
|
|
6276
6374
|
async function walkAndCopy(src, dest) {
|
|
6277
6375
|
const entries = await fs2.readdir(src, { withFileTypes: true });
|
|
6278
6376
|
for (const entry of entries) {
|
|
6279
|
-
const srcPath =
|
|
6280
|
-
const destPath =
|
|
6377
|
+
const srcPath = path3.join(src, entry.name);
|
|
6378
|
+
const destPath = path3.join(dest, entry.name);
|
|
6281
6379
|
if (entry.isDirectory()) {
|
|
6282
6380
|
if (IGNORED_DIRS.includes(entry.name)) continue;
|
|
6283
6381
|
await fs2.ensureDir(destPath);
|
|
@@ -6295,8 +6393,8 @@ async function copyTemplateFiles(templateDir, targetDir) {
|
|
|
6295
6393
|
function resolveMarsPackageRange(packageName, templateDir) {
|
|
6296
6394
|
const shortName = packageName.replace("@mars-stack/", "");
|
|
6297
6395
|
const candidates = [
|
|
6298
|
-
|
|
6299
|
-
|
|
6396
|
+
path3.resolve(templateDir, "..", "packages", shortName, "package.json"),
|
|
6397
|
+
path3.resolve(templateDir, "..", "..", "packages", shortName, "package.json")
|
|
6300
6398
|
];
|
|
6301
6399
|
for (const candidate of candidates) {
|
|
6302
6400
|
if (fs2.existsSync(candidate)) {
|
|
@@ -6307,7 +6405,7 @@ function resolveMarsPackageRange(packageName, templateDir) {
|
|
|
6307
6405
|
}
|
|
6308
6406
|
}
|
|
6309
6407
|
}
|
|
6310
|
-
const templatePkg = fs2.readJsonSync(
|
|
6408
|
+
const templatePkg = fs2.readJsonSync(path3.join(templateDir, "package.json"));
|
|
6311
6409
|
const existing = templatePkg.dependencies?.[packageName];
|
|
6312
6410
|
if (existing && existing !== "*") {
|
|
6313
6411
|
if (/^[\^~>=]/.test(existing)) return existing;
|
|
@@ -6318,7 +6416,7 @@ function resolveMarsPackageRange(packageName, templateDir) {
|
|
|
6318
6416
|
}
|
|
6319
6417
|
function generatePackageJson(config) {
|
|
6320
6418
|
const templateDir = getTemplatePath();
|
|
6321
|
-
const templatePkg =
|
|
6419
|
+
const templatePkg = path3.join(templateDir, "package.json");
|
|
6322
6420
|
const pkg = fs2.readJsonSync(templatePkg);
|
|
6323
6421
|
pkg.name = config.name;
|
|
6324
6422
|
pkg.version = "0.1.0";
|
|
@@ -6518,7 +6616,7 @@ async function pruneDisabledFeatures(features, targetDir) {
|
|
|
6518
6616
|
const flagValue = features[feature];
|
|
6519
6617
|
if (flagValue !== false) continue;
|
|
6520
6618
|
for (const relativePath of paths) {
|
|
6521
|
-
const fullPath =
|
|
6619
|
+
const fullPath = path3.join(targetDir, relativePath);
|
|
6522
6620
|
if (await fs2.pathExists(fullPath)) {
|
|
6523
6621
|
await fs2.remove(fullPath);
|
|
6524
6622
|
log.step(`Pruned disabled feature path: ${relativePath}`);
|
|
@@ -6529,7 +6627,7 @@ async function pruneDisabledFeatures(features, targetDir) {
|
|
|
6529
6627
|
}
|
|
6530
6628
|
}
|
|
6531
6629
|
if (relationsToRemove.length > 0) {
|
|
6532
|
-
const authPath =
|
|
6630
|
+
const authPath = path3.join(targetDir, "prisma", "schema", "auth.prisma");
|
|
6533
6631
|
if (await fs2.pathExists(authPath)) {
|
|
6534
6632
|
let content = await fs2.readFile(authPath, "utf-8");
|
|
6535
6633
|
for (const field of relationsToRemove) {
|
|
@@ -6547,22 +6645,22 @@ async function scaffoldProject(targetDir, config) {
|
|
|
6547
6645
|
await fs2.ensureDir(targetDir);
|
|
6548
6646
|
const fileCount = await copyTemplateFiles(templateDir, targetDir);
|
|
6549
6647
|
await pruneDisabledFeatures(config.features, targetDir);
|
|
6550
|
-
await fs2.writeFile(
|
|
6648
|
+
await fs2.writeFile(path3.join(targetDir, "package.json"), generatePackageJson(config));
|
|
6551
6649
|
await fs2.writeFile(
|
|
6552
|
-
|
|
6650
|
+
path3.join(targetDir, "src", "config", "app.config.ts"),
|
|
6553
6651
|
generateAppConfig(config)
|
|
6554
6652
|
);
|
|
6555
|
-
await fs2.writeFile(
|
|
6556
|
-
await fs2.writeFile(
|
|
6557
|
-
await fs2.writeFile(
|
|
6558
|
-
await fs2.writeFile(
|
|
6559
|
-
await fs2.writeFile(
|
|
6653
|
+
await fs2.writeFile(path3.join(targetDir, "docker-compose.yml"), generateDockerCompose(config));
|
|
6654
|
+
await fs2.writeFile(path3.join(targetDir, "src", "app", "layout.tsx"), generateLayout(config));
|
|
6655
|
+
await fs2.writeFile(path3.join(targetDir, ".env"), generateEnvFile(config));
|
|
6656
|
+
await fs2.writeFile(path3.join(targetDir, ".env.example"), generateEnvExample(config));
|
|
6657
|
+
await fs2.writeFile(path3.join(targetDir, ".gitignore"), generateGitignore());
|
|
6560
6658
|
await patchGlobalsCssForScaffoldedProject(targetDir, config);
|
|
6561
6659
|
await generateBrandCssForProject(targetDir, config);
|
|
6562
6660
|
return { fileCount };
|
|
6563
6661
|
}
|
|
6564
6662
|
async function generateBrandCssForProject(targetDir, config) {
|
|
6565
|
-
const brandPath =
|
|
6663
|
+
const brandPath = path3.join(targetDir, "src", "styles", "brand.css");
|
|
6566
6664
|
if (!await fs2.pathExists(brandPath)) return;
|
|
6567
6665
|
const css = generateBrandCss(config.theme.primaryColor);
|
|
6568
6666
|
await fs2.writeFile(brandPath, css);
|
|
@@ -6575,7 +6673,7 @@ var VALID_DESIGN_DIRECTIONS = [
|
|
|
6575
6673
|
"dashboard"
|
|
6576
6674
|
];
|
|
6577
6675
|
async function patchGlobalsCssForScaffoldedProject(targetDir, config) {
|
|
6578
|
-
const globalsPath =
|
|
6676
|
+
const globalsPath = path3.join(targetDir, "src", "styles", "globals.css");
|
|
6579
6677
|
if (!await fs2.pathExists(globalsPath)) return;
|
|
6580
6678
|
let content = await fs2.readFile(globalsPath, "utf-8");
|
|
6581
6679
|
content = content.replace(
|
|
@@ -6712,13 +6810,13 @@ async function generateSelectedFeatures(projectRoot, features) {
|
|
|
6712
6810
|
}
|
|
6713
6811
|
|
|
6714
6812
|
// src/utils/telemetry.ts
|
|
6715
|
-
import
|
|
6716
|
-
import
|
|
6813
|
+
import fs20 from "fs-extra";
|
|
6814
|
+
import path21 from "path";
|
|
6717
6815
|
import os2 from "os";
|
|
6718
|
-
var RC_PATH =
|
|
6816
|
+
var RC_PATH = path21.join(os2.homedir(), ".marsrc");
|
|
6719
6817
|
function isTelemetryEnabled() {
|
|
6720
6818
|
try {
|
|
6721
|
-
const config =
|
|
6819
|
+
const config = fs20.readJsonSync(RC_PATH);
|
|
6722
6820
|
return config.enabled === true;
|
|
6723
6821
|
} catch {
|
|
6724
6822
|
return false;
|
|
@@ -6727,16 +6825,16 @@ function isTelemetryEnabled() {
|
|
|
6727
6825
|
function enableTelemetry() {
|
|
6728
6826
|
const config = loadOrCreateConfig();
|
|
6729
6827
|
config.enabled = true;
|
|
6730
|
-
|
|
6828
|
+
fs20.writeJsonSync(RC_PATH, config, { spaces: 2 });
|
|
6731
6829
|
}
|
|
6732
6830
|
function disableTelemetry() {
|
|
6733
6831
|
const config = loadOrCreateConfig();
|
|
6734
6832
|
config.enabled = false;
|
|
6735
|
-
|
|
6833
|
+
fs20.writeJsonSync(RC_PATH, config, { spaces: 2 });
|
|
6736
6834
|
}
|
|
6737
6835
|
function loadOrCreateConfig() {
|
|
6738
6836
|
try {
|
|
6739
|
-
return
|
|
6837
|
+
return fs20.readJsonSync(RC_PATH);
|
|
6740
6838
|
} catch {
|
|
6741
6839
|
return { enabled: false, anonymousId: crypto.randomUUID() };
|
|
6742
6840
|
}
|
|
@@ -6756,13 +6854,13 @@ function trackEvent(event, properties) {
|
|
|
6756
6854
|
},
|
|
6757
6855
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6758
6856
|
};
|
|
6759
|
-
const logPath =
|
|
6760
|
-
|
|
6857
|
+
const logPath = path21.join(os2.homedir(), ".mars-telemetry.log");
|
|
6858
|
+
fs20.appendFile(logPath, JSON.stringify(payload) + "\n").catch(() => {
|
|
6761
6859
|
});
|
|
6762
6860
|
}
|
|
6763
6861
|
|
|
6764
6862
|
// src/commands/create.ts
|
|
6765
|
-
var RC_PATH2 =
|
|
6863
|
+
var RC_PATH2 = path22.join(os3.homedir(), ".marsrc");
|
|
6766
6864
|
var PROJECT_NAME_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
6767
6865
|
var MAX_PROJECT_NAME_LENGTH = 214;
|
|
6768
6866
|
async function createCommand(projectName, options) {
|
|
@@ -6789,8 +6887,8 @@ async function createCommand(projectName, options) {
|
|
|
6789
6887
|
const projectInfo = useDefaults ? getDefaultProjectInfo(projectName) : await promptProjectInfo(projectName);
|
|
6790
6888
|
if (!projectInfo) return;
|
|
6791
6889
|
const targetDir = resolveProjectPath(projectInfo.name);
|
|
6792
|
-
if (await
|
|
6793
|
-
const entries = await
|
|
6890
|
+
if (await fs21.pathExists(targetDir)) {
|
|
6891
|
+
const entries = await fs21.readdir(targetDir);
|
|
6794
6892
|
if (entries.length > 0) {
|
|
6795
6893
|
log.error(`Directory "${projectInfo.name}" already exists and is not empty.`);
|
|
6796
6894
|
return;
|
|
@@ -6831,7 +6929,7 @@ async function createCommand(projectName, options) {
|
|
|
6831
6929
|
log.info("Scaffolding cancelled.");
|
|
6832
6930
|
process.exit(0);
|
|
6833
6931
|
}
|
|
6834
|
-
if (!
|
|
6932
|
+
if (!fs21.pathExistsSync(RC_PATH2)) {
|
|
6835
6933
|
const { telemetryOptIn } = await prompts5(
|
|
6836
6934
|
{
|
|
6837
6935
|
type: "confirm",
|
|
@@ -6870,10 +6968,10 @@ async function createCommand(projectName, options) {
|
|
|
6870
6968
|
} catch (err) {
|
|
6871
6969
|
spinner.fail("Failed to scaffold project");
|
|
6872
6970
|
log.error(err instanceof Error ? err.message : String(err));
|
|
6873
|
-
if (await
|
|
6971
|
+
if (await fs21.pathExists(targetDir)) {
|
|
6874
6972
|
const cleanupSpinner = ora("Cleaning up...").start();
|
|
6875
6973
|
try {
|
|
6876
|
-
await
|
|
6974
|
+
await fs21.remove(targetDir);
|
|
6877
6975
|
cleanupSpinner.succeed("Cleaned up partial scaffold");
|
|
6878
6976
|
} catch {
|
|
6879
6977
|
cleanupSpinner.warn(`Could not clean up ${targetDir}. You may need to remove it manually.`);
|
|
@@ -6888,8 +6986,8 @@ function countEnabled(flags) {
|
|
|
6888
6986
|
|
|
6889
6987
|
// src/commands/doctor.ts
|
|
6890
6988
|
init_logger();
|
|
6891
|
-
import
|
|
6892
|
-
import
|
|
6989
|
+
import fs22 from "fs-extra";
|
|
6990
|
+
import path23 from "path";
|
|
6893
6991
|
import { execSync } from "child_process";
|
|
6894
6992
|
function commandExists(cmd) {
|
|
6895
6993
|
try {
|
|
@@ -6909,12 +7007,12 @@ function getVersion(cmd) {
|
|
|
6909
7007
|
async function checkForUpgrades() {
|
|
6910
7008
|
log.blank();
|
|
6911
7009
|
log.title("Upgrade Check");
|
|
6912
|
-
const packageJsonPath =
|
|
6913
|
-
if (!
|
|
7010
|
+
const packageJsonPath = path23.join(process.cwd(), "package.json");
|
|
7011
|
+
if (!fs22.pathExistsSync(packageJsonPath)) {
|
|
6914
7012
|
log.warn("No package.json found \u2014 skipping upgrade check.");
|
|
6915
7013
|
return;
|
|
6916
7014
|
}
|
|
6917
|
-
const packageJson =
|
|
7015
|
+
const packageJson = fs22.readJsonSync(packageJsonPath);
|
|
6918
7016
|
const deps = packageJson.dependencies ?? {};
|
|
6919
7017
|
const devDeps = packageJson.devDependencies ?? {};
|
|
6920
7018
|
const currentRaw = deps["@mars-stack/core"] ?? devDeps["@mars-stack/core"];
|
|
@@ -6976,25 +7074,25 @@ async function doctorCommand(options) {
|
|
|
6976
7074
|
},
|
|
6977
7075
|
{
|
|
6978
7076
|
name: "package.json exists",
|
|
6979
|
-
check: () =>
|
|
7077
|
+
check: () => fs22.pathExistsSync(path23.join(process.cwd(), "package.json"))
|
|
6980
7078
|
},
|
|
6981
7079
|
{
|
|
6982
7080
|
name: ".env file exists",
|
|
6983
|
-
check: () =>
|
|
7081
|
+
check: () => fs22.pathExistsSync(path23.join(process.cwd(), ".env"))
|
|
6984
7082
|
},
|
|
6985
7083
|
{
|
|
6986
7084
|
name: "Prisma schema exists",
|
|
6987
|
-
check: () =>
|
|
7085
|
+
check: () => fs22.pathExistsSync(path23.join(process.cwd(), "prisma", "schema")) || fs22.pathExistsSync(path23.join(process.cwd(), "prisma", "schema.prisma"))
|
|
6988
7086
|
},
|
|
6989
7087
|
{
|
|
6990
7088
|
name: "node_modules installed",
|
|
6991
|
-
check: () =>
|
|
7089
|
+
check: () => fs22.pathExistsSync(path23.join(process.cwd(), "node_modules"))
|
|
6992
7090
|
},
|
|
6993
7091
|
{
|
|
6994
7092
|
name: "DATABASE_URL set",
|
|
6995
7093
|
check: () => {
|
|
6996
7094
|
try {
|
|
6997
|
-
const envContent =
|
|
7095
|
+
const envContent = fs22.readFileSync(path23.join(process.cwd(), ".env"), "utf-8");
|
|
6998
7096
|
const match = envContent.match(/^DATABASE_URL=(.+)$/m);
|
|
6999
7097
|
return match ? match[1] !== "" : false;
|
|
7000
7098
|
} catch {
|
|
@@ -7006,7 +7104,7 @@ async function doctorCommand(options) {
|
|
|
7006
7104
|
name: "JWT_SECRET set",
|
|
7007
7105
|
check: () => {
|
|
7008
7106
|
try {
|
|
7009
|
-
const envContent =
|
|
7107
|
+
const envContent = fs22.readFileSync(path23.join(process.cwd(), ".env"), "utf-8");
|
|
7010
7108
|
const match = envContent.match(/^JWT_SECRET=(.+)$/m);
|
|
7011
7109
|
if (!match || !match[1]) return false;
|
|
7012
7110
|
return match[1].length >= 32 ? true : "set but too short (need >=32 chars)";
|
|
@@ -7051,16 +7149,16 @@ async function doctorCommand(options) {
|
|
|
7051
7149
|
|
|
7052
7150
|
// src/commands/add.ts
|
|
7053
7151
|
init_logger();
|
|
7054
|
-
import
|
|
7055
|
-
import
|
|
7152
|
+
import fs24 from "fs-extra";
|
|
7153
|
+
import path25 from "path";
|
|
7056
7154
|
import pc3 from "picocolors";
|
|
7057
7155
|
|
|
7058
7156
|
// src/utils/doc-updater.ts
|
|
7059
|
-
import
|
|
7060
|
-
import
|
|
7157
|
+
import fs23 from "fs-extra";
|
|
7158
|
+
import path24 from "path";
|
|
7061
7159
|
async function appendToDbSchema(projectDir, modelName, fields) {
|
|
7062
|
-
const filePath =
|
|
7063
|
-
if (!await
|
|
7160
|
+
const filePath = path24.join(projectDir, "docs", "generated", "db-schema.md");
|
|
7161
|
+
if (!await fs23.pathExists(filePath)) return;
|
|
7064
7162
|
const entry = [
|
|
7065
7163
|
"",
|
|
7066
7164
|
`### ${modelName}`,
|
|
@@ -7070,12 +7168,12 @@ async function appendToDbSchema(projectDir, modelName, fields) {
|
|
|
7070
7168
|
...fields.map((f) => `| ${f} | |`),
|
|
7071
7169
|
""
|
|
7072
7170
|
].join("\n");
|
|
7073
|
-
await
|
|
7171
|
+
await fs23.appendFile(filePath, entry);
|
|
7074
7172
|
}
|
|
7075
7173
|
async function updateQualityScore(projectDir, domain, grade) {
|
|
7076
|
-
const filePath =
|
|
7077
|
-
if (!await
|
|
7078
|
-
const content = await
|
|
7174
|
+
const filePath = path24.join(projectDir, "docs", "QUALITY_SCORE.md");
|
|
7175
|
+
if (!await fs23.pathExists(filePath)) return;
|
|
7176
|
+
const content = await fs23.readFile(filePath, "utf-8");
|
|
7079
7177
|
const pattern = new RegExp(`^\\|\\s*${escapeRegex(domain)}\\s*\\|`, "m");
|
|
7080
7178
|
if (pattern.test(content)) {
|
|
7081
7179
|
const updated = content.replace(pattern, (match) => {
|
|
@@ -7083,11 +7181,11 @@ async function updateQualityScore(projectDir, domain, grade) {
|
|
|
7083
7181
|
parts[2] = ` ${grade} `;
|
|
7084
7182
|
return parts.join("|");
|
|
7085
7183
|
});
|
|
7086
|
-
await
|
|
7184
|
+
await fs23.writeFile(filePath, updated);
|
|
7087
7185
|
} else {
|
|
7088
7186
|
const row = `| ${domain} | ${grade} | |
|
|
7089
7187
|
`;
|
|
7090
|
-
await
|
|
7188
|
+
await fs23.appendFile(filePath, row);
|
|
7091
7189
|
}
|
|
7092
7190
|
}
|
|
7093
7191
|
function escapeRegex(str) {
|
|
@@ -7098,8 +7196,8 @@ function escapeRegex(str) {
|
|
|
7098
7196
|
init_rollback();
|
|
7099
7197
|
function ensureInProject() {
|
|
7100
7198
|
const cwd = process.cwd();
|
|
7101
|
-
const configPath =
|
|
7102
|
-
if (!
|
|
7199
|
+
const configPath = path25.join(cwd, "src", "config", "app.config.ts");
|
|
7200
|
+
if (!fs24.pathExistsSync(configPath)) {
|
|
7103
7201
|
log.error("Not inside a MARS project. Run this command from the project root.");
|
|
7104
7202
|
process.exit(1);
|
|
7105
7203
|
}
|
|
@@ -7132,8 +7230,8 @@ async function addFeatureCommand(name) {
|
|
|
7132
7230
|
const kebab = toKebab(name);
|
|
7133
7231
|
const pascal = toPascal(name);
|
|
7134
7232
|
const camel = toCamel(name);
|
|
7135
|
-
const featureDir =
|
|
7136
|
-
if (await
|
|
7233
|
+
const featureDir = path25.join(root, "src", "features", kebab);
|
|
7234
|
+
if (await fs24.pathExists(featureDir)) {
|
|
7137
7235
|
log.error(`Feature "${kebab}" already exists at src/features/${kebab}/`);
|
|
7138
7236
|
return;
|
|
7139
7237
|
}
|
|
@@ -7198,9 +7296,9 @@ export type Update${pascal}Input = z.infer<typeof ${camel}Schemas.update>;
|
|
|
7198
7296
|
ctx.trackCreatedFile(featureDir);
|
|
7199
7297
|
let count = 0;
|
|
7200
7298
|
for (const [filePath, content] of Object.entries(files)) {
|
|
7201
|
-
const fullPath =
|
|
7202
|
-
await
|
|
7203
|
-
await
|
|
7299
|
+
const fullPath = path25.join(featureDir, filePath);
|
|
7300
|
+
await fs24.ensureDir(path25.dirname(fullPath));
|
|
7301
|
+
await fs24.writeFile(fullPath, content);
|
|
7204
7302
|
count++;
|
|
7205
7303
|
}
|
|
7206
7304
|
await ctx.commit();
|
|
@@ -7221,8 +7319,8 @@ async function addPageCommand(routePath, options) {
|
|
|
7221
7319
|
const root = ensureInProject();
|
|
7222
7320
|
const cleanPath = routePath.startsWith("/") ? routePath.slice(1) : routePath;
|
|
7223
7321
|
const group = options.protected ? "(protected)" : "(public)";
|
|
7224
|
-
const pageDir =
|
|
7225
|
-
if (await
|
|
7322
|
+
const pageDir = path25.join(root, "src", "app", group, cleanPath);
|
|
7323
|
+
if (await fs24.pathExists(path25.join(pageDir, "page.tsx"))) {
|
|
7226
7324
|
log.error(`Page already exists at src/app/${group}/${cleanPath}/page.tsx`);
|
|
7227
7325
|
return;
|
|
7228
7326
|
}
|
|
@@ -7270,9 +7368,9 @@ export default function Loading() {
|
|
|
7270
7368
|
const ctx = createRollbackContext();
|
|
7271
7369
|
try {
|
|
7272
7370
|
ctx.trackCreatedFile(pageDir);
|
|
7273
|
-
await
|
|
7274
|
-
await
|
|
7275
|
-
await
|
|
7371
|
+
await fs24.ensureDir(pageDir);
|
|
7372
|
+
await fs24.writeFile(path25.join(pageDir, "page.tsx"), pageContent);
|
|
7373
|
+
await fs24.writeFile(path25.join(pageDir, "loading.tsx"), loadingContent);
|
|
7276
7374
|
await ctx.commit();
|
|
7277
7375
|
log.success(`Created page at ${pc3.bold(`src/app/${group}/${cleanPath}/`)}`);
|
|
7278
7376
|
trackEvent("add", { type: "page" });
|
|
@@ -7289,9 +7387,9 @@ async function addModelCommand(name) {
|
|
|
7289
7387
|
const pascal = toPascal(name);
|
|
7290
7388
|
const camel = toCamel(name);
|
|
7291
7389
|
const kebab = toKebab(name);
|
|
7292
|
-
const schemaDir =
|
|
7293
|
-
const schemaFile =
|
|
7294
|
-
if (await
|
|
7390
|
+
const schemaDir = path25.join(root, "prisma", "schema");
|
|
7391
|
+
const schemaFile = path25.join(schemaDir, `${kebab}.prisma`);
|
|
7392
|
+
if (await fs24.pathExists(schemaFile)) {
|
|
7295
7393
|
log.error(`Schema file already exists: prisma/schema/${kebab}.prisma`);
|
|
7296
7394
|
return;
|
|
7297
7395
|
}
|
|
@@ -7309,8 +7407,8 @@ async function addModelCommand(name) {
|
|
|
7309
7407
|
const ctx = createRollbackContext();
|
|
7310
7408
|
try {
|
|
7311
7409
|
ctx.trackCreatedFile(schemaFile);
|
|
7312
|
-
await
|
|
7313
|
-
await
|
|
7410
|
+
await fs24.ensureDir(schemaDir);
|
|
7411
|
+
await fs24.writeFile(schemaFile, content);
|
|
7314
7412
|
await ctx.commit();
|
|
7315
7413
|
log.success(`Created model ${pc3.bold(pascal)} at prisma/schema/${kebab}.prisma`);
|
|
7316
7414
|
trackEvent("add", { type: "model" });
|
|
@@ -7332,9 +7430,9 @@ async function addEmailCommand(name) {
|
|
|
7332
7430
|
const kebab = toKebab(name);
|
|
7333
7431
|
const pascal = toPascal(name);
|
|
7334
7432
|
const camel = toCamel(name);
|
|
7335
|
-
const templatesDir =
|
|
7336
|
-
const filePath =
|
|
7337
|
-
if (await
|
|
7433
|
+
const templatesDir = path25.join(root, "src", "lib", "core", "email", "templates");
|
|
7434
|
+
const filePath = path25.join(templatesDir, `${kebab}-email.ts`);
|
|
7435
|
+
if (await fs24.pathExists(filePath)) {
|
|
7338
7436
|
log.error(`Email template already exists: src/lib/core/email/templates/${kebab}-email.ts`);
|
|
7339
7437
|
return;
|
|
7340
7438
|
}
|
|
@@ -7378,25 +7476,25 @@ export function ${functionName}({ appName, actionUrl, userName }: ${pascal}Email
|
|
|
7378
7476
|
}
|
|
7379
7477
|
`;
|
|
7380
7478
|
const ctx = createRollbackContext();
|
|
7381
|
-
const indexPath =
|
|
7479
|
+
const indexPath = path25.join(templatesDir, "index.ts");
|
|
7382
7480
|
try {
|
|
7383
7481
|
ctx.trackCreatedFile(filePath);
|
|
7384
|
-
if (await
|
|
7482
|
+
if (await fs24.pathExists(indexPath)) {
|
|
7385
7483
|
await ctx.trackModifiedFile(indexPath);
|
|
7386
7484
|
} else {
|
|
7387
7485
|
ctx.trackCreatedFile(indexPath);
|
|
7388
7486
|
}
|
|
7389
|
-
await
|
|
7390
|
-
await
|
|
7487
|
+
await fs24.ensureDir(templatesDir);
|
|
7488
|
+
await fs24.writeFile(filePath, content);
|
|
7391
7489
|
const exportLine = `export { ${functionName} } from './${kebab}-email';
|
|
7392
7490
|
`;
|
|
7393
|
-
if (await
|
|
7394
|
-
const existing = await
|
|
7491
|
+
if (await fs24.pathExists(indexPath)) {
|
|
7492
|
+
const existing = await fs24.readFile(indexPath, "utf-8");
|
|
7395
7493
|
if (!existing.includes(functionName)) {
|
|
7396
|
-
await
|
|
7494
|
+
await fs24.appendFile(indexPath, exportLine);
|
|
7397
7495
|
}
|
|
7398
7496
|
} else {
|
|
7399
|
-
await
|
|
7497
|
+
await fs24.writeFile(indexPath, exportLine);
|
|
7400
7498
|
}
|
|
7401
7499
|
await ctx.commit();
|
|
7402
7500
|
log.success(`Created email template ${pc3.bold(kebab)} at src/lib/core/email/templates/${kebab}-email.ts`);
|
|
@@ -7419,10 +7517,10 @@ async function addComponentCommand(name, options) {
|
|
|
7419
7517
|
log.error(`Invalid type "${type}". Use: ${validTypes.join(", ")}`);
|
|
7420
7518
|
return;
|
|
7421
7519
|
}
|
|
7422
|
-
const dir = type === "primitive" ?
|
|
7423
|
-
await
|
|
7424
|
-
const filePath =
|
|
7425
|
-
if (await
|
|
7520
|
+
const dir = type === "primitive" ? path25.join(root, "src", "components", "primitives") : path25.join(root, "src", "components", "patterns");
|
|
7521
|
+
await fs24.ensureDir(dir);
|
|
7522
|
+
const filePath = path25.join(dir, `${pascal}.tsx`);
|
|
7523
|
+
if (await fs24.pathExists(filePath)) {
|
|
7426
7524
|
log.error(`Component already exists: ${pascal}.tsx`);
|
|
7427
7525
|
return;
|
|
7428
7526
|
}
|
|
@@ -7471,9 +7569,9 @@ export function ${pascal}({ children, className }: ${pascal}Props) {
|
|
|
7471
7569
|
const ctx = createRollbackContext();
|
|
7472
7570
|
try {
|
|
7473
7571
|
ctx.trackCreatedFile(filePath);
|
|
7474
|
-
await
|
|
7572
|
+
await fs24.writeFile(filePath, content);
|
|
7475
7573
|
await ctx.commit();
|
|
7476
|
-
log.success(`Created ${type} component ${pc3.bold(pascal)} at ${
|
|
7574
|
+
log.success(`Created ${type} component ${pc3.bold(pascal)} at ${path25.relative(root, filePath)}`);
|
|
7477
7575
|
trackEvent("add", { type: "component", componentType: type });
|
|
7478
7576
|
log.blank();
|
|
7479
7577
|
log.warn(`Remember to add the export to the barrel file:`);
|
|
@@ -7488,14 +7586,14 @@ export function ${pascal}({ children, className }: ${pascal}Props) {
|
|
|
7488
7586
|
// src/commands/configure.ts
|
|
7489
7587
|
init_logger();
|
|
7490
7588
|
import { execSync as execSync2 } from "child_process";
|
|
7491
|
-
import
|
|
7492
|
-
import
|
|
7589
|
+
import fs25 from "fs-extra";
|
|
7590
|
+
import path26 from "path";
|
|
7493
7591
|
import pc4 from "picocolors";
|
|
7494
7592
|
import prompts6 from "prompts";
|
|
7495
7593
|
function ensureInProject2() {
|
|
7496
7594
|
const cwd = process.cwd();
|
|
7497
|
-
const configPath =
|
|
7498
|
-
if (!
|
|
7595
|
+
const configPath = path26.join(cwd, "src", "config", "app.config.ts");
|
|
7596
|
+
if (!fs25.pathExistsSync(configPath)) {
|
|
7499
7597
|
log.error("Not inside a MARS project. Run this command from the project root.");
|
|
7500
7598
|
process.exit(1);
|
|
7501
7599
|
}
|
|
@@ -7509,8 +7607,8 @@ var PROVIDER_DEPENDENCIES = {
|
|
|
7509
7607
|
"storage:s3": ["@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner"]
|
|
7510
7608
|
};
|
|
7511
7609
|
function updateAppConfig(projectDir, serviceKey, provider, featureKey) {
|
|
7512
|
-
const configPath =
|
|
7513
|
-
let content =
|
|
7610
|
+
const configPath = path26.join(projectDir, "src", "config", "app.config.ts");
|
|
7611
|
+
let content = fs25.readFileSync(configPath, "utf-8");
|
|
7514
7612
|
if (serviceKey === "auth") {
|
|
7515
7613
|
const boolValue = provider === "google" ? "true" : "false";
|
|
7516
7614
|
const featureRegex = new RegExp(`(googleOAuth\\s*:\\s*)(?:true|false)`);
|
|
@@ -7525,10 +7623,10 @@ function updateAppConfig(projectDir, serviceKey, provider, featureKey) {
|
|
|
7525
7623
|
content = content.replace(featureRegex, `$1true`);
|
|
7526
7624
|
}
|
|
7527
7625
|
}
|
|
7528
|
-
|
|
7626
|
+
fs25.writeFileSync(configPath, content);
|
|
7529
7627
|
}
|
|
7530
7628
|
function detectPackageManager(projectDir) {
|
|
7531
|
-
if (
|
|
7629
|
+
if (fs25.existsSync(path26.join(projectDir, "yarn.lock"))) return "yarn";
|
|
7532
7630
|
return "npm";
|
|
7533
7631
|
}
|
|
7534
7632
|
function installDependencies(projectDir, deps) {
|
|
@@ -7638,15 +7736,15 @@ async function configureCommand(service) {
|
|
|
7638
7736
|
log.step(`Manually set ${pc4.bold(`services.${serviceConfig.configKey}.provider`)} to ${pc4.bold(`'${provider}'`)} in ${pc4.dim("src/config/app.config.ts")}`);
|
|
7639
7737
|
}
|
|
7640
7738
|
if (selectedProvider.envVars.length > 0) {
|
|
7641
|
-
const envPath =
|
|
7642
|
-
if (await
|
|
7643
|
-
const envContent = await
|
|
7739
|
+
const envPath = path26.join(root, ".env");
|
|
7740
|
+
if (await fs25.pathExists(envPath)) {
|
|
7741
|
+
const envContent = await fs25.readFile(envPath, "utf-8");
|
|
7644
7742
|
const missingVars = selectedProvider.envVars.filter(
|
|
7645
7743
|
(v) => !envContent.includes(v)
|
|
7646
7744
|
);
|
|
7647
7745
|
if (missingVars.length > 0) {
|
|
7648
7746
|
const additions = missingVars.map((v) => `${v}=""`).join("\n");
|
|
7649
|
-
await
|
|
7747
|
+
await fs25.appendFile(envPath, `
|
|
7650
7748
|
# ${selectedService} (${provider})
|
|
7651
7749
|
${additions}
|
|
7652
7750
|
`);
|
|
@@ -7675,15 +7773,15 @@ ${additions}
|
|
|
7675
7773
|
|
|
7676
7774
|
// src/commands/deploy.ts
|
|
7677
7775
|
init_logger();
|
|
7678
|
-
import
|
|
7679
|
-
import
|
|
7776
|
+
import fs26 from "fs-extra";
|
|
7777
|
+
import path27 from "path";
|
|
7680
7778
|
import { execSync as execSync3 } from "child_process";
|
|
7681
7779
|
import pc5 from "picocolors";
|
|
7682
7780
|
import prompts7 from "prompts";
|
|
7683
7781
|
function ensureInProject3() {
|
|
7684
7782
|
const cwd = process.cwd();
|
|
7685
|
-
const configPath =
|
|
7686
|
-
if (!
|
|
7783
|
+
const configPath = path27.join(cwd, "src", "config", "app.config.ts");
|
|
7784
|
+
if (!fs26.pathExistsSync(configPath)) {
|
|
7687
7785
|
log.error("Not inside a MARS project. Run this command from the project root.");
|
|
7688
7786
|
process.exit(1);
|
|
7689
7787
|
}
|
|
@@ -7722,8 +7820,8 @@ async function deployCommand() {
|
|
|
7722
7820
|
return;
|
|
7723
7821
|
}
|
|
7724
7822
|
}
|
|
7725
|
-
const vercelDir =
|
|
7726
|
-
if (!
|
|
7823
|
+
const vercelDir = path27.join(root, ".vercel");
|
|
7824
|
+
if (!fs26.existsSync(vercelDir)) {
|
|
7727
7825
|
log.step("Linking project to Vercel...");
|
|
7728
7826
|
try {
|
|
7729
7827
|
execSync3("vercel link", { cwd: root, stdio: "inherit" });
|
|
@@ -7740,8 +7838,8 @@ async function deployCommand() {
|
|
|
7740
7838
|
log.blank();
|
|
7741
7839
|
const requiredVars = ["JWT_SECRET", "DATABASE_URL"];
|
|
7742
7840
|
log.step(`Core: ${pc5.dim(requiredVars.join(", "))}`);
|
|
7743
|
-
const configPath =
|
|
7744
|
-
const configContent = await
|
|
7841
|
+
const configPath = path27.join(root, "src", "config", "app.config.ts");
|
|
7842
|
+
const configContent = await fs26.readFile(configPath, "utf-8");
|
|
7745
7843
|
if (configContent.includes("email: { provider: 'sendgrid'")) {
|
|
7746
7844
|
log.step(`Email (SendGrid): ${pc5.dim("SENDGRID_API_KEY, SENDGRID_FROM_EMAIL")}`);
|
|
7747
7845
|
} else if (configContent.includes("email: { provider: 'resend'")) {
|
|
@@ -7802,8 +7900,8 @@ async function deployCommand() {
|
|
|
7802
7900
|
|
|
7803
7901
|
// src/commands/upgrade.ts
|
|
7804
7902
|
init_logger();
|
|
7805
|
-
import
|
|
7806
|
-
import
|
|
7903
|
+
import fs27 from "fs-extra";
|
|
7904
|
+
import path28 from "path";
|
|
7807
7905
|
import { execSync as execSync4 } from "child_process";
|
|
7808
7906
|
var MARS_PACKAGES = ["@mars-stack/core", "@mars-stack/ui"];
|
|
7809
7907
|
var CHANGELOG_URL = "https://github.com/greaveselliott/mars/blob/main/CHANGELOG.md";
|
|
@@ -7826,7 +7924,7 @@ function readCurrentVersion(packageJson, packageName) {
|
|
|
7826
7924
|
return version ? version.replace(/^[\^~]/, "") : null;
|
|
7827
7925
|
}
|
|
7828
7926
|
function detectPackageManager2(projectDir) {
|
|
7829
|
-
if (
|
|
7927
|
+
if (fs27.pathExistsSync(path28.join(projectDir, "yarn.lock"))) {
|
|
7830
7928
|
return "yarn";
|
|
7831
7929
|
}
|
|
7832
7930
|
return "npm";
|
|
@@ -7890,7 +7988,7 @@ function updatePackageJsonVersions(packageJsonPath, packageJson, versions) {
|
|
|
7890
7988
|
if (updated.length > 0) {
|
|
7891
7989
|
packageJson.dependencies = deps;
|
|
7892
7990
|
packageJson.devDependencies = devDeps;
|
|
7893
|
-
|
|
7991
|
+
fs27.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
|
|
7894
7992
|
}
|
|
7895
7993
|
return updated;
|
|
7896
7994
|
}
|
|
@@ -7900,15 +7998,15 @@ function upgradeCommand(program2) {
|
|
|
7900
7998
|
).option("--dry-run", "Show what would be updated without making changes").action(async (options) => {
|
|
7901
7999
|
log.title("MARS Upgrade");
|
|
7902
8000
|
const projectDir = process.cwd();
|
|
7903
|
-
const packageJsonPath =
|
|
7904
|
-
if (!
|
|
8001
|
+
const packageJsonPath = path28.join(projectDir, "package.json");
|
|
8002
|
+
if (!fs27.pathExistsSync(packageJsonPath)) {
|
|
7905
8003
|
log.error(
|
|
7906
8004
|
"No package.json found. Are you in a Mars project directory?"
|
|
7907
8005
|
);
|
|
7908
8006
|
process.exitCode = 1;
|
|
7909
8007
|
return;
|
|
7910
8008
|
}
|
|
7911
|
-
const packageJson =
|
|
8009
|
+
const packageJson = fs27.readJsonSync(packageJsonPath);
|
|
7912
8010
|
const hasMarsPackage = MARS_PACKAGES.some(
|
|
7913
8011
|
(name) => readCurrentVersion(packageJson, name) !== null
|
|
7914
8012
|
);
|
|
@@ -8004,7 +8102,7 @@ init_sentry();
|
|
|
8004
8102
|
init_feature_flags();
|
|
8005
8103
|
init_logger();
|
|
8006
8104
|
var program = new Command();
|
|
8007
|
-
program.name("mars").description("MARS CLI: scaffold, configure, and maintain SaaS apps").version(
|
|
8105
|
+
program.name("mars").description("MARS CLI: scaffold, configure, and maintain SaaS apps").version(getCliVersion()).option("-v, --verbose", "Enable verbose output for debugging");
|
|
8008
8106
|
function isVerbose() {
|
|
8009
8107
|
return program.opts().verbose === true;
|
|
8010
8108
|
}
|