@idevconn/create-icore 0.7.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -9
- package/dist/cli.js +689 -363
- package/dist/index.cjs +806 -358
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +798 -351
- package/dist/manifest/audit.js +122 -0
- package/package.json +1 -1
- package/templates/apps/api/src/app/app.module.ts +2 -6
- package/templates/apps/api/src/app/features.module.ts +9 -0
- package/templates/apps/api/src/app/gateway-services.ts +7 -0
- package/templates/apps/api/src/main.ts +1 -5
- package/templates/apps/microservices/auth/src/app/app.module.ts +4 -93
- package/templates/apps/microservices/auth/src/app/auth.provider.ts +9 -0
- package/templates/apps/microservices/notes/src/app/app.module.ts +4 -86
- package/templates/apps/microservices/notes/src/app/db.provider.ts +9 -0
- package/templates/apps/microservices/upload/src/app/app.module.ts +4 -140
- package/templates/apps/microservices/upload/src/app/storage.provider.ts +9 -0
- package/templates/apps/templates/client-antd/src/components/layout/LayoutSider.tsx +15 -23
- package/templates/apps/templates/client-antd/src/nav.config.ts +17 -0
- package/templates/apps/templates/client-mui/src/components/layout/LayoutSider.tsx +19 -20
- package/templates/apps/templates/client-mui/src/nav.config.ts +17 -0
- package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +20 -16
- package/templates/apps/templates/client-shadcn/src/nav.config.ts +17 -0
- package/templates/libs/auth-strategies/firebase/eslint.config.mjs +1 -0
- package/templates/libs/auth-strategies/firebase/package.json +4 -0
- package/templates/libs/auth-strategies/firebase/src/index.ts +1 -0
- package/templates/libs/auth-strategies/firebase/src/lib/__tests__/firebase-auth.module.unit.test.ts +49 -0
- package/templates/libs/auth-strategies/firebase/src/lib/firebase-auth.module.ts +41 -0
- package/templates/libs/auth-strategies/firebase/tsconfig.json +2 -0
- package/templates/libs/auth-strategies/mongodb/package.json +8 -1
- package/templates/libs/auth-strategies/mongodb/src/index.ts +1 -0
- package/templates/libs/auth-strategies/mongodb/src/lib/__tests__/mongodb-auth.module.unit.test.ts +16 -0
- package/templates/libs/auth-strategies/mongodb/src/lib/mongodb-auth.module.ts +45 -0
- package/templates/libs/auth-strategies/mongodb/tsconfig.json +2 -0
- package/templates/libs/auth-strategies/supabase/eslint.config.mjs +1 -0
- package/templates/libs/auth-strategies/supabase/package.json +3 -0
- package/templates/libs/auth-strategies/supabase/src/index.ts +1 -0
- package/templates/libs/auth-strategies/supabase/src/lib/__tests__/supabase-auth.module.unit.test.ts +43 -0
- package/templates/libs/auth-strategies/supabase/src/lib/supabase-auth.module.ts +41 -0
- package/templates/libs/auth-strategies/supabase/tsconfig.json +2 -0
- package/templates/libs/db-strategies/firestore/eslint.config.mjs +1 -1
- package/templates/libs/db-strategies/firestore/package.json +4 -0
- package/templates/libs/db-strategies/firestore/src/index.ts +1 -0
- package/templates/libs/db-strategies/firestore/src/lib/__tests__/firestore-db.module.unit.test.ts +37 -0
- package/templates/libs/db-strategies/firestore/src/lib/firestore-db.module.ts +41 -0
- package/templates/libs/db-strategies/firestore/tsconfig.json +2 -0
- package/templates/libs/db-strategies/mongodb/package.json +4 -1
- package/templates/libs/db-strategies/mongodb/src/index.ts +1 -0
- package/templates/libs/db-strategies/mongodb/src/lib/__tests__/mongodb-db.module.unit.test.ts +14 -0
- package/templates/libs/db-strategies/mongodb/src/lib/mongodb-db.module.ts +41 -0
- package/templates/libs/db-strategies/mongodb/tsconfig.json +2 -0
- package/templates/libs/db-strategies/supabase/eslint.config.mjs +6 -1
- package/templates/libs/db-strategies/supabase/package.json +3 -0
- package/templates/libs/db-strategies/supabase/src/index.ts +1 -0
- package/templates/libs/db-strategies/supabase/src/lib/__tests__/supabase-db.module.unit.test.ts +32 -0
- package/templates/libs/db-strategies/supabase/src/lib/supabase-db.module.ts +41 -0
- package/templates/libs/db-strategies/supabase/tsconfig.json +2 -0
- package/templates/libs/shared/src/strategies/__tests__/provide-strategy.unit.test.ts +73 -0
- package/templates/libs/shared/src/strategies/index.ts +1 -0
- package/templates/libs/shared/src/strategies/provide-strategy.ts +44 -0
- package/templates/libs/storage-strategies/cloudinary/eslint.config.mjs +1 -1
- package/templates/libs/storage-strategies/cloudinary/package.json +4 -0
- package/templates/libs/storage-strategies/cloudinary/src/index.ts +1 -0
- package/templates/libs/storage-strategies/cloudinary/src/lib/__tests__/cloudinary-storage.module.unit.test.ts +40 -0
- package/templates/libs/storage-strategies/cloudinary/src/lib/cloudinary-storage.module.ts +85 -0
- package/templates/libs/storage-strategies/cloudinary/tsconfig.json +2 -0
- package/templates/libs/storage-strategies/firebase/eslint.config.mjs +1 -1
- package/templates/libs/storage-strategies/firebase/package.json +4 -0
- package/templates/libs/storage-strategies/firebase/src/index.ts +1 -0
- package/templates/libs/storage-strategies/firebase/src/lib/__tests__/firebase-storage.module.unit.test.ts +42 -0
- package/templates/libs/storage-strategies/firebase/src/lib/firebase-storage.module.ts +46 -0
- package/templates/libs/storage-strategies/firebase/tsconfig.json +2 -0
- package/templates/libs/storage-strategies/mongodb/package.json +4 -1
- package/templates/libs/storage-strategies/mongodb/src/index.ts +1 -0
- package/templates/libs/storage-strategies/mongodb/src/lib/__tests__/mongodb-storage.module.unit.test.ts +14 -0
- package/templates/libs/storage-strategies/mongodb/src/lib/mongodb-storage.module.ts +41 -0
- package/templates/libs/storage-strategies/mongodb/tsconfig.json +2 -0
- package/templates/libs/storage-strategies/supabase/eslint.config.mjs +1 -0
- package/templates/libs/storage-strategies/supabase/package.json +3 -0
- package/templates/libs/storage-strategies/supabase/src/index.ts +1 -0
- package/templates/libs/storage-strategies/supabase/src/lib/__tests__/supabase-storage.module.unit.test.ts +46 -0
- package/templates/libs/storage-strategies/supabase/src/lib/supabase-storage.module.ts +46 -0
- package/templates/libs/storage-strategies/supabase/tsconfig.json +2 -0
- package/templates/package.json +1 -1
- package/templates/tools/create-icore/_template-shell/package.json +1 -1
- package/templates/tsconfig.base.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10,9 +10,98 @@ import * as p2 from "@clack/prompts";
|
|
|
10
10
|
// src/lib/prompts.ts
|
|
11
11
|
import * as p from "@clack/prompts";
|
|
12
12
|
import { resolve } from "path";
|
|
13
|
-
import { readFile } from "fs/promises";
|
|
13
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
14
14
|
import { dirname, join } from "path";
|
|
15
15
|
import { fileURLToPath } from "url";
|
|
16
|
+
|
|
17
|
+
// src/lib/config.ts
|
|
18
|
+
import { readFile } from "fs/promises";
|
|
19
|
+
var ConfigFileError = class extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = "ConfigFileError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var AUTH_PROVIDERS = ["supabase", "firebase", "mongodb", "none"];
|
|
26
|
+
var DB_PROVIDERS = ["supabase", "firebase", "mongodb", "none"];
|
|
27
|
+
var UPLOAD_PROVIDERS = [
|
|
28
|
+
"supabase",
|
|
29
|
+
"firebase",
|
|
30
|
+
"cloudinary",
|
|
31
|
+
"mongodb",
|
|
32
|
+
"none"
|
|
33
|
+
];
|
|
34
|
+
var PAYMENT_PROVIDERS = ["paypal", "none"];
|
|
35
|
+
var JOBS_PROVIDERS = ["bullmq", "none"];
|
|
36
|
+
var EXAMPLE_MODES = ["notes", "none"];
|
|
37
|
+
var UI_LIBRARIES = ["shadcn", "antd", "mui"];
|
|
38
|
+
var MS_TRANSPORTS = ["tcp", "redis", "nats", "mqtt", "rmq", "kafka"];
|
|
39
|
+
var PACKAGE_MANAGERS = ["yarn", "npm", "pnpm"];
|
|
40
|
+
function assertEnum(field, value, valid) {
|
|
41
|
+
if (typeof value !== "string" || !valid.includes(value)) {
|
|
42
|
+
throw new ConfigFileError(
|
|
43
|
+
`config field "${field}" got "${String(value)}", expected one of: ${valid.join(", ")}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
function assertBoolean(field, value) {
|
|
49
|
+
if (typeof value !== "boolean") {
|
|
50
|
+
throw new ConfigFileError(`config field "${field}" must be a boolean, got ${typeof value}`);
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
function validateConfig(raw) {
|
|
55
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
56
|
+
throw new ConfigFileError("config file must be a JSON object");
|
|
57
|
+
}
|
|
58
|
+
const obj = raw;
|
|
59
|
+
const result = {};
|
|
60
|
+
if ("projectName" in obj) {
|
|
61
|
+
const v = obj["projectName"];
|
|
62
|
+
if (typeof v !== "string" || !/^[a-z0-9-]+$/i.test(v)) {
|
|
63
|
+
throw new ConfigFileError(
|
|
64
|
+
`config field "projectName" must match /^[a-z0-9-]+$/i, got "${String(v)}"`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
result.projectName = v;
|
|
68
|
+
}
|
|
69
|
+
if ("authProvider" in obj)
|
|
70
|
+
result.authProvider = assertEnum("authProvider", obj["authProvider"], AUTH_PROVIDERS);
|
|
71
|
+
if ("dbProvider" in obj)
|
|
72
|
+
result.dbProvider = assertEnum("dbProvider", obj["dbProvider"], DB_PROVIDERS);
|
|
73
|
+
if ("upload" in obj) result.upload = assertEnum("upload", obj["upload"], UPLOAD_PROVIDERS);
|
|
74
|
+
if ("payment" in obj) result.payment = assertEnum("payment", obj["payment"], PAYMENT_PROVIDERS);
|
|
75
|
+
if ("jobs" in obj) result.jobs = assertEnum("jobs", obj["jobs"], JOBS_PROVIDERS);
|
|
76
|
+
if ("example" in obj) result.example = assertEnum("example", obj["example"], EXAMPLE_MODES);
|
|
77
|
+
if ("ui" in obj) result.ui = assertEnum("ui", obj["ui"], UI_LIBRARIES);
|
|
78
|
+
if ("transport" in obj)
|
|
79
|
+
result.transport = assertEnum("transport", obj["transport"], MS_TRANSPORTS);
|
|
80
|
+
if ("packageManager" in obj)
|
|
81
|
+
result.packageManager = assertEnum("packageManager", obj["packageManager"], PACKAGE_MANAGERS);
|
|
82
|
+
if ("initGit" in obj) result.initGit = assertBoolean("initGit", obj["initGit"]);
|
|
83
|
+
if ("install" in obj) result.install = assertBoolean("install", obj["install"]);
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
async function loadConfig(filePath) {
|
|
87
|
+
let raw;
|
|
88
|
+
try {
|
|
89
|
+
raw = await readFile(filePath, "utf8");
|
|
90
|
+
} catch {
|
|
91
|
+
throw new ConfigFileError(`config file not found: ${filePath}`);
|
|
92
|
+
}
|
|
93
|
+
let parsed;
|
|
94
|
+
try {
|
|
95
|
+
parsed = JSON.parse(raw);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
throw new ConfigFileError(
|
|
98
|
+
`config file is not valid JSON: ${e instanceof Error ? e.message : String(e)}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return validateConfig(parsed);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/lib/prompts.ts
|
|
16
105
|
function detectPackageManager() {
|
|
17
106
|
const ua = process.env["npm_config_user_agent"] ?? "";
|
|
18
107
|
if (ua.startsWith("yarn/")) return "yarn";
|
|
@@ -23,7 +112,7 @@ function detectPackageManager() {
|
|
|
23
112
|
async function readSelfVersion() {
|
|
24
113
|
try {
|
|
25
114
|
const here2 = dirname(fileURLToPath(import.meta.url));
|
|
26
|
-
const pkgRaw = await
|
|
115
|
+
const pkgRaw = await readFile2(join(here2, "..", "package.json"), "utf8");
|
|
27
116
|
const pkg = JSON.parse(pkgRaw);
|
|
28
117
|
return pkg.version ?? null;
|
|
29
118
|
} catch {
|
|
@@ -97,12 +186,21 @@ function parseFlags(argv) {
|
|
|
97
186
|
case "no-install":
|
|
98
187
|
out.install = false;
|
|
99
188
|
break;
|
|
189
|
+
case "config":
|
|
190
|
+
out._configPath = v;
|
|
191
|
+
break;
|
|
100
192
|
}
|
|
101
193
|
}
|
|
102
194
|
return out;
|
|
103
195
|
}
|
|
104
196
|
async function collectOptions({ argv, cwd }) {
|
|
105
197
|
const flags = parseFlags(argv);
|
|
198
|
+
const configPath = flags._configPath;
|
|
199
|
+
delete flags._configPath;
|
|
200
|
+
if (configPath) {
|
|
201
|
+
const configValues = await loadConfig(configPath);
|
|
202
|
+
Object.assign(flags, { ...configValues, ...flags });
|
|
203
|
+
}
|
|
106
204
|
const [selfVersion, latestVersion] = await Promise.all([readSelfVersion(), fetchLatestVersion()]);
|
|
107
205
|
const versionTag = selfVersion ? ` v${selfVersion}` : "";
|
|
108
206
|
p.intro(`iCore${versionTag} \u2014 bootstrap a new project`);
|
|
@@ -125,11 +223,12 @@ Re-run with @latest to refresh:
|
|
|
125
223
|
options: [
|
|
126
224
|
{ value: "supabase", label: "Supabase" },
|
|
127
225
|
{ value: "firebase", label: "Firebase" },
|
|
128
|
-
{ value: "mongodb", label: "MongoDB (Custom Auth)" }
|
|
226
|
+
{ value: "mongodb", label: "MongoDB (Custom Auth)" },
|
|
227
|
+
{ value: "none", label: "None \u2014 no login, open API (simple SPA)" }
|
|
129
228
|
]
|
|
130
229
|
});
|
|
131
230
|
if (p.isCancel(authProvider)) throw new Error("cancelled");
|
|
132
|
-
const dbProvider = flags.dbProvider ?? await p.select({
|
|
231
|
+
const dbProvider = authProvider === "none" ? "none" : flags.dbProvider ?? await p.select({
|
|
133
232
|
message: "Database backend",
|
|
134
233
|
options: [
|
|
135
234
|
{ value: "supabase", label: "Supabase Postgres" },
|
|
@@ -168,7 +267,7 @@ Re-run with @latest to refresh:
|
|
|
168
267
|
initialValue: "none"
|
|
169
268
|
});
|
|
170
269
|
if (p.isCancel(jobs)) throw new Error("cancelled");
|
|
171
|
-
const example = flags.example ?? await p.select({
|
|
270
|
+
const example = authProvider === "none" ? "none" : flags.example ?? await p.select({
|
|
172
271
|
message: "Include notes sample feature? (CRUD demo \u2014 remove before production)",
|
|
173
272
|
options: [
|
|
174
273
|
{ value: "notes", label: "Yes \u2014 include notes sample" },
|
|
@@ -193,7 +292,8 @@ Re-run with @latest to refresh:
|
|
|
193
292
|
initialValue: "shadcn"
|
|
194
293
|
});
|
|
195
294
|
if (p.isCancel(ui)) throw new Error("cancelled");
|
|
196
|
-
const
|
|
295
|
+
const noMicroservices = authProvider === "none" && upload === "none" && payment === "none";
|
|
296
|
+
const transport = flags.transport ?? (noMicroservices ? "tcp" : await p.select({
|
|
197
297
|
message: "Microservice transport",
|
|
198
298
|
options: [
|
|
199
299
|
{ value: "tcp", label: "TCP (default, no broker required)" },
|
|
@@ -204,7 +304,7 @@ Re-run with @latest to refresh:
|
|
|
204
304
|
{ value: "kafka", label: "Kafka" }
|
|
205
305
|
],
|
|
206
306
|
initialValue: "tcp"
|
|
207
|
-
});
|
|
307
|
+
}));
|
|
208
308
|
if (p.isCancel(transport)) throw new Error("cancelled");
|
|
209
309
|
const packageManager = flags.packageManager ?? detectPackageManager();
|
|
210
310
|
if (packageManager === "yarn") {
|
|
@@ -236,13 +336,13 @@ Re-run with @latest to refresh:
|
|
|
236
336
|
}
|
|
237
337
|
|
|
238
338
|
// src/lib/scaffold.ts
|
|
239
|
-
import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as
|
|
339
|
+
import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile8, stat, writeFile as writeFile8, rm as rm4 } from "fs/promises";
|
|
240
340
|
import { readFileSync } from "fs";
|
|
241
|
-
import { join as
|
|
341
|
+
import { join as join9 } from "path";
|
|
242
342
|
import { spawnSync } from "child_process";
|
|
243
343
|
|
|
244
344
|
// src/lib/scaffold-env.ts
|
|
245
|
-
import { readFile as
|
|
345
|
+
import { readFile as readFile3, writeFile } from "fs/promises";
|
|
246
346
|
import { join as join2 } from "path";
|
|
247
347
|
var TRANSPORT_ENV_TOKEN = {
|
|
248
348
|
redis: "REDIS",
|
|
@@ -271,7 +371,7 @@ function uncommentTransportEnv(text2, prefix, transport) {
|
|
|
271
371
|
async function stripGatewayTransport(targetDir, prefix) {
|
|
272
372
|
const gatewayEnv = join2(targetDir, "apps/api/.env");
|
|
273
373
|
try {
|
|
274
|
-
const env = await
|
|
374
|
+
const env = await readFile3(gatewayEnv, "utf8");
|
|
275
375
|
const next = env.split("\n").filter(
|
|
276
376
|
(line) => !line.startsWith(`${prefix}_`) && !line.startsWith(`# ${prefix}_`) && !line.includes(`${prefix} MS transport`)
|
|
277
377
|
).join("\n");
|
|
@@ -279,9 +379,34 @@ async function stripGatewayTransport(targetDir, prefix) {
|
|
|
279
379
|
} catch {
|
|
280
380
|
}
|
|
281
381
|
}
|
|
382
|
+
var ROOT_PROVIDER_SDKS = {
|
|
383
|
+
supabase: ["@supabase/supabase-js"],
|
|
384
|
+
cloudinary: ["cloudinary"],
|
|
385
|
+
mongodb: ["mongoose"],
|
|
386
|
+
firebase: ["firebase-admin"]
|
|
387
|
+
};
|
|
388
|
+
async function pruneRootProviderDeps(targetDir, opts) {
|
|
389
|
+
const chosen = /* @__PURE__ */ new Set([opts.authProvider, opts.dbProvider, opts.upload]);
|
|
390
|
+
const drop = /* @__PURE__ */ new Set();
|
|
391
|
+
for (const [provider, sdks] of Object.entries(ROOT_PROVIDER_SDKS)) {
|
|
392
|
+
if (!chosen.has(provider)) for (const sdk of sdks) drop.add(sdk);
|
|
393
|
+
}
|
|
394
|
+
if (drop.size === 0) return;
|
|
395
|
+
const pkgPath = join2(targetDir, "package.json");
|
|
396
|
+
try {
|
|
397
|
+
const pkg = JSON.parse(await readFile3(pkgPath, "utf8"));
|
|
398
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
399
|
+
const deps = pkg[field];
|
|
400
|
+
if (!deps) continue;
|
|
401
|
+
for (const sdk of drop) delete deps[sdk];
|
|
402
|
+
}
|
|
403
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
}
|
|
282
407
|
async function rewriteRootPackageJson(targetDir, opts) {
|
|
283
408
|
const pkgPath = join2(targetDir, "package.json");
|
|
284
|
-
const raw = await
|
|
409
|
+
const raw = await readFile3(pkgPath, "utf8");
|
|
285
410
|
const pkg = JSON.parse(raw);
|
|
286
411
|
pkg["name"] = opts.projectName;
|
|
287
412
|
pkg["version"] = "0.0.1";
|
|
@@ -296,11 +421,16 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
296
421
|
const deps = pkg["dependencies"] ??= {};
|
|
297
422
|
Object.assign(deps, MONGODB_DEPS);
|
|
298
423
|
}
|
|
424
|
+
if (opts.authProvider === "mongodb") {
|
|
425
|
+
const devDeps = pkg["devDependencies"] ??= {};
|
|
426
|
+
devDeps["@types/bcrypt"] = "^6.0.0";
|
|
427
|
+
devDeps["@types/jsonwebtoken"] = "^9.0.10";
|
|
428
|
+
}
|
|
299
429
|
if (opts.packageManager !== "yarn") {
|
|
300
430
|
delete pkg.packageManager;
|
|
301
431
|
} else {
|
|
302
432
|
try {
|
|
303
|
-
const yarnrc = await
|
|
433
|
+
const yarnrc = await readFile3(join2(targetDir, ".yarnrc.yml"), "utf8");
|
|
304
434
|
const match = yarnrc.match(/^yarnPath:\s*.+yarn-(\d+\.\d+\.\d+)\.cjs/m);
|
|
305
435
|
if (match?.[1]) {
|
|
306
436
|
pkg["packageManager"] = `yarn@${match[1]}`;
|
|
@@ -313,7 +443,7 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
313
443
|
}
|
|
314
444
|
async function writeAuthEnv(targetDir, opts) {
|
|
315
445
|
const envExample = join2(targetDir, "apps/microservices/auth/.env.example");
|
|
316
|
-
const env = await
|
|
446
|
+
const env = await readFile3(envExample, "utf8");
|
|
317
447
|
let next = env.replace(/^AUTH_PROVIDER=.*$/m, `AUTH_PROVIDER=${opts.authProvider}`).replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`);
|
|
318
448
|
next = uncommentTransportEnv(next, "AUTH", opts.transport);
|
|
319
449
|
if (opts.authProvider === "mongodb") {
|
|
@@ -324,7 +454,7 @@ async function writeAuthEnv(targetDir, opts) {
|
|
|
324
454
|
async function writeUploadEnv(targetDir, opts) {
|
|
325
455
|
if (opts.upload === "none") return;
|
|
326
456
|
const envExample = join2(targetDir, "apps/microservices/upload/.env.example");
|
|
327
|
-
const env = await
|
|
457
|
+
const env = await readFile3(envExample, "utf8");
|
|
328
458
|
let next = env.replace(/^STORAGE_PROVIDER=.*$/m, `STORAGE_PROVIDER=${opts.upload}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
|
|
329
459
|
next = uncommentTransportEnv(next, "UPLOAD", opts.transport);
|
|
330
460
|
if (opts.upload === "mongodb") {
|
|
@@ -336,7 +466,7 @@ async function writeNotesEnv(targetDir, opts) {
|
|
|
336
466
|
if (opts.example === "none") return;
|
|
337
467
|
const envExample = join2(targetDir, "apps/microservices/notes/.env.example");
|
|
338
468
|
try {
|
|
339
|
-
const env = await
|
|
469
|
+
const env = await readFile3(envExample, "utf8");
|
|
340
470
|
let next = env.replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`);
|
|
341
471
|
next = uncommentTransportEnv(next, "NOTES", opts.transport);
|
|
342
472
|
await writeFile(join2(targetDir, "apps/microservices/notes/.env"), next);
|
|
@@ -345,7 +475,7 @@ async function writeNotesEnv(targetDir, opts) {
|
|
|
345
475
|
}
|
|
346
476
|
async function writeGatewayEnv(targetDir, opts) {
|
|
347
477
|
const envExample = join2(targetDir, "apps/api/.env.example");
|
|
348
|
-
const env = await
|
|
478
|
+
const env = await readFile3(envExample, "utf8");
|
|
349
479
|
let next = env.replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`).replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
|
|
350
480
|
for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
|
|
351
481
|
next = uncommentTransportEnv(next, prefix, opts.transport);
|
|
@@ -368,7 +498,7 @@ async function writeRootEnv(targetDir, opts) {
|
|
|
368
498
|
async function writeClientEnv(targetDir) {
|
|
369
499
|
const envExample = join2(targetDir, "apps/client/.env.example");
|
|
370
500
|
try {
|
|
371
|
-
const env = await
|
|
501
|
+
const env = await readFile3(envExample, "utf8");
|
|
372
502
|
await writeFile(join2(targetDir, "apps/client/.env"), env);
|
|
373
503
|
} catch {
|
|
374
504
|
}
|
|
@@ -377,7 +507,7 @@ async function writePaymentEnv(targetDir, opts) {
|
|
|
377
507
|
if (opts.payment === "none") return;
|
|
378
508
|
const envExample = join2(targetDir, "apps/microservices/payment/.env.example");
|
|
379
509
|
try {
|
|
380
|
-
const env = await
|
|
510
|
+
const env = await readFile3(envExample, "utf8");
|
|
381
511
|
let next = env.replace(/^PAYMENT_PROVIDER=.*$/m, `PAYMENT_PROVIDER=${opts.payment}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
|
|
382
512
|
next = uncommentTransportEnv(next, "PAYMENT", opts.transport);
|
|
383
513
|
await writeFile(join2(targetDir, "apps/microservices/payment/.env"), next);
|
|
@@ -386,11 +516,11 @@ async function writePaymentEnv(targetDir, opts) {
|
|
|
386
516
|
}
|
|
387
517
|
|
|
388
518
|
// src/lib/scaffold-strip.ts
|
|
389
|
-
import { readFile as
|
|
519
|
+
import { readFile as readFile4, writeFile as writeFile2, rm } from "fs/promises";
|
|
390
520
|
import { join as join3 } from "path";
|
|
391
521
|
async function stripDeps(pkgPath, names) {
|
|
392
522
|
try {
|
|
393
|
-
const raw = await
|
|
523
|
+
const raw = await readFile4(pkgPath, "utf8");
|
|
394
524
|
const pkg = JSON.parse(raw);
|
|
395
525
|
for (const n of names) {
|
|
396
526
|
if (pkg.dependencies) delete pkg.dependencies[n];
|
|
@@ -403,7 +533,7 @@ async function stripDeps(pkgPath, names) {
|
|
|
403
533
|
async function stripTsconfigPath(targetDir, alias) {
|
|
404
534
|
const tsconfigPath = join3(targetDir, "tsconfig.base.json");
|
|
405
535
|
try {
|
|
406
|
-
const src = await
|
|
536
|
+
const src = await readFile4(tsconfigPath, "utf8");
|
|
407
537
|
const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
|
|
408
538
|
const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
|
|
409
539
|
if (pretty !== src) {
|
|
@@ -418,326 +548,75 @@ async function stripTsconfigPath(targetDir, alias) {
|
|
|
418
548
|
} catch {
|
|
419
549
|
}
|
|
420
550
|
}
|
|
421
|
-
async function
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
"
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
try {
|
|
433
|
-
const appModule = await readFile3(appModulePath, "utf8");
|
|
434
|
-
const next = appModule.replace(/^import \{ AdminModule \} from '\.\/admin\/admin\.module';\n/m, "").replace(/,\s*AdminModule/g, "");
|
|
435
|
-
await writeFile2(appModulePath, next);
|
|
436
|
-
} catch {
|
|
437
|
-
}
|
|
438
|
-
await stripDeps(join3(targetDir, "apps/api/package.json"), [
|
|
439
|
-
"@icore/jobs-client",
|
|
440
|
-
"@bull-board/api",
|
|
441
|
-
"@bull-board/express"
|
|
551
|
+
async function removeFirebaseAdminLib(targetDir) {
|
|
552
|
+
await rm(join3(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
|
|
553
|
+
await stripTsconfigPath(targetDir, "@icore/firebase-admin");
|
|
554
|
+
await stripDeps(join3(targetDir, "apps/microservices/auth/package.json"), [
|
|
555
|
+
"@icore/firebase-admin"
|
|
556
|
+
]);
|
|
557
|
+
await stripDeps(join3(targetDir, "apps/microservices/upload/package.json"), [
|
|
558
|
+
"@icore/firebase-admin"
|
|
559
|
+
]);
|
|
560
|
+
await stripDeps(join3(targetDir, "apps/microservices/notes/package.json"), [
|
|
561
|
+
"@icore/firebase-admin"
|
|
442
562
|
]);
|
|
443
|
-
const composePath = join3(targetDir, "docker-compose.yml");
|
|
444
|
-
try {
|
|
445
|
-
const compose = await readFile3(composePath, "utf8");
|
|
446
|
-
const next = compose.replace(/\n {2}jobs:[\s\S]+?(?=\n {2}\w+:|\nnetworks:)/m, "\n").replace(/\n {6}jobs:\n {8}condition: service_started/g, "").replace(/\n {6}JOBS_REDIS_URL:[^\n]*/g, "");
|
|
447
|
-
await writeFile2(composePath, next);
|
|
448
|
-
} catch {
|
|
449
|
-
}
|
|
450
563
|
}
|
|
451
|
-
async function
|
|
452
|
-
const
|
|
453
|
-
"apps/microservices/
|
|
454
|
-
"
|
|
455
|
-
"libs/
|
|
456
|
-
"
|
|
564
|
+
async function removeAuthStack(targetDir) {
|
|
565
|
+
const rmPaths = [
|
|
566
|
+
"apps/microservices/auth",
|
|
567
|
+
"libs/auth-strategies",
|
|
568
|
+
"libs/auth-client",
|
|
569
|
+
"Dockerfile.ms-auth",
|
|
570
|
+
"apps/api/src/app/auth",
|
|
571
|
+
"apps/api/src/app/profile",
|
|
572
|
+
"apps/api/src/app/abilities",
|
|
573
|
+
"apps/client/src/components/auth",
|
|
574
|
+
"apps/client/src/routes/login.tsx",
|
|
575
|
+
"apps/client/src/routes/auth.callback.tsx",
|
|
576
|
+
"apps/client/src/routes/auth.oauth.callback.tsx",
|
|
577
|
+
"apps/client/src/routes/_dashboard/profile.tsx"
|
|
457
578
|
];
|
|
458
|
-
for (const p3 of
|
|
579
|
+
for (const p3 of rmPaths) {
|
|
459
580
|
await rm(join3(targetDir, p3), { recursive: true, force: true });
|
|
460
581
|
}
|
|
461
582
|
const appModulePath = join3(targetDir, "apps/api/src/app/app.module.ts");
|
|
462
583
|
try {
|
|
463
|
-
const
|
|
464
|
-
const next =
|
|
584
|
+
const src = await readFile4(appModulePath, "utf8");
|
|
585
|
+
const next = src.replace(/^import \{ AuthModule \} from '\.\/auth\/auth\.module';\n/m, "").replace(/^import \{ ProfileModule \} from '\.\/profile\/profile\.module';\n/m, "").replace(/^import \{ AbilitiesModule \} from '\.\/abilities\/abilities\.module';\n/m, "").replace(/\bAuthModule,\s*/g, "").replace(/,\s*AuthModule\b/g, "").replace(/\bProfileModule,\s*/g, "").replace(/,\s*ProfileModule\b/g, "").replace(/\bAbilitiesModule,\s*/g, "").replace(/,\s*AbilitiesModule\b/g, "");
|
|
465
586
|
await writeFile2(appModulePath, next);
|
|
466
587
|
} catch {
|
|
467
588
|
}
|
|
468
|
-
|
|
469
|
-
"@icore/payment-client",
|
|
470
|
-
"@idevconn/payment"
|
|
471
|
-
]);
|
|
472
|
-
await stripGatewayTransport(targetDir, "PAYMENT");
|
|
473
|
-
const mainTsPath = join3(targetDir, "apps/api/src/main.ts");
|
|
589
|
+
const dashboardPath = join3(targetDir, "apps/client/src/routes/_dashboard.tsx");
|
|
474
590
|
try {
|
|
475
|
-
const src = await
|
|
476
|
-
const next = src.replace(
|
|
477
|
-
await writeFile2(
|
|
591
|
+
const src = await readFile4(dashboardPath, "utf8");
|
|
592
|
+
const next = src.replace(/^import \{ useAuthStore \} from '@icore\/template-shared';\n/m, "").replace(/, redirect/g, "").replace(/\n {2}beforeLoad: \(\) => \{[\s\S]*?\n {2}\},/m, "");
|
|
593
|
+
await writeFile2(dashboardPath, next);
|
|
478
594
|
} catch {
|
|
479
595
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
"
|
|
484
|
-
"
|
|
485
|
-
"libs/notes-client",
|
|
486
|
-
"apps/api/src/app/notes",
|
|
487
|
-
"apps/client/src/components/notes"
|
|
596
|
+
for (const alias of [
|
|
597
|
+
"@icore/auth-client",
|
|
598
|
+
"@icore/auth-supabase",
|
|
599
|
+
"@icore/auth-firebase",
|
|
600
|
+
"@icore/auth-mongodb"
|
|
488
601
|
]) {
|
|
489
|
-
await
|
|
490
|
-
}
|
|
491
|
-
await rm(join3(targetDir, "apps/client/src/routes/_dashboard/notes.tsx"), { force: true });
|
|
492
|
-
await rm(join3(targetDir, "apps/client/src/queries/notes.ts"), { force: true });
|
|
493
|
-
const appModulePath = join3(targetDir, "apps/api/src/app/app.module.ts");
|
|
494
|
-
try {
|
|
495
|
-
const src = await readFile3(appModulePath, "utf8");
|
|
496
|
-
const next = src.replace(/^import \{ NotesModule \} from '\.\/notes\/notes\.module';\n/m, "").replace(/,\s*NotesModule/g, "");
|
|
497
|
-
await writeFile2(appModulePath, next);
|
|
498
|
-
} catch {
|
|
499
|
-
}
|
|
500
|
-
await stripDeps(join3(targetDir, "apps/api/package.json"), [
|
|
501
|
-
"@icore/notes-client",
|
|
502
|
-
"@casl/ability"
|
|
503
|
-
]);
|
|
504
|
-
await stripGatewayTransport(targetDir, "NOTES");
|
|
505
|
-
const mainTsPath = join3(targetDir, "apps/api/src/main.ts");
|
|
506
|
-
try {
|
|
507
|
-
const src = await readFile3(mainTsPath, "utf8");
|
|
508
|
-
const next = src.replace(/\n\s*\{ name: 'notes', prefix: 'NOTES' \},/, "");
|
|
509
|
-
await writeFile2(mainTsPath, next);
|
|
510
|
-
} catch {
|
|
511
|
-
}
|
|
512
|
-
const tsconfigPath = join3(targetDir, "tsconfig.base.json");
|
|
513
|
-
try {
|
|
514
|
-
const src = await readFile3(tsconfigPath, "utf8");
|
|
515
|
-
const next = src.replace(/^\s*"@icore\/notes-client": \[[^\]]*\],?\n/m, "");
|
|
516
|
-
await writeFile2(tsconfigPath, next);
|
|
517
|
-
} catch {
|
|
518
|
-
}
|
|
519
|
-
const siderPath = join3(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
|
|
520
|
-
try {
|
|
521
|
-
const src = await readFile3(siderPath, "utf8");
|
|
522
|
-
const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
|
|
523
|
-
"const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
|
|
524
|
-
"const selectedKey = pathname.includes('/profile')"
|
|
525
|
-
).replace(
|
|
526
|
-
/\n {4}\{\n {6}key: 'notes',\n {6}icon: <FileTextOutlined \/>,\n {6}label: <Link to="\/(?:_dashboard\/)?notes">\{t\('notes\.title'\)\}<\/Link>,\n {4}\},/,
|
|
527
|
-
""
|
|
528
|
-
).replace("import NoteOutlinedIcon from '@mui/icons-material/NoteOutlined';\n", "").replace(
|
|
529
|
-
/\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
|
|
530
|
-
""
|
|
531
|
-
).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
|
|
532
|
-
await writeFile2(siderPath, next);
|
|
533
|
-
} catch {
|
|
602
|
+
await stripTsconfigPath(targetDir, alias);
|
|
534
603
|
}
|
|
535
|
-
|
|
604
|
+
await stripDeps(join3(targetDir, "apps/api/package.json"), ["@icore/auth-client"]);
|
|
605
|
+
const gatewayEnv = join3(targetDir, "apps/api/.env");
|
|
536
606
|
try {
|
|
537
|
-
const
|
|
538
|
-
const next =
|
|
539
|
-
await writeFile2(
|
|
607
|
+
const env = await readFile4(gatewayEnv, "utf8");
|
|
608
|
+
const next = env.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
609
|
+
await writeFile2(gatewayEnv, next);
|
|
540
610
|
} catch {
|
|
541
611
|
}
|
|
542
|
-
|
|
543
|
-
async function removeUnusedAuthStrategies(targetDir, authProvider) {
|
|
544
|
-
const modulePath = join3(targetDir, "apps/microservices/auth/src/app/app.module.ts");
|
|
545
|
-
const AUTH_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseAuth\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDbAuth\(connection, cfg\);\n\s*return makeFirebaseAuth\(cfg\);/m;
|
|
546
|
-
if (authProvider === "supabase") {
|
|
547
|
-
await rm(join3(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
|
|
548
|
-
await rm(join3(targetDir, "libs/auth-strategies/mongodb"), { recursive: true, force: true });
|
|
549
|
-
await stripDeps(join3(targetDir, "apps/microservices/auth/package.json"), [
|
|
550
|
-
"@icore/auth-firebase",
|
|
551
|
-
"@icore/firebase-admin",
|
|
552
|
-
"@icore/auth-mongodb"
|
|
553
|
-
]);
|
|
554
|
-
await stripTsconfigPath(targetDir, "@icore/auth-firebase");
|
|
555
|
-
await stripTsconfigPath(targetDir, "@icore/auth-mongodb");
|
|
556
|
-
try {
|
|
557
|
-
const src = await readFile3(modulePath, "utf8");
|
|
558
|
-
const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/gm, "").replace(/^import \{[^}]*MongoDbAuthStrategy[^}]*\} from '@icore\/auth-mongodb';\n/gm, "").replace(
|
|
559
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
560
|
-
""
|
|
561
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirebaseAuth[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDbAuth[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(AUTH_BRANCH, "return makeSupabaseAuth(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
562
|
-
await writeFile2(modulePath, next);
|
|
563
|
-
} catch {
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
if (authProvider === "firebase") {
|
|
567
|
-
await rm(join3(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
|
|
568
|
-
await rm(join3(targetDir, "libs/auth-strategies/mongodb"), { recursive: true, force: true });
|
|
569
|
-
await stripDeps(join3(targetDir, "apps/microservices/auth/package.json"), [
|
|
570
|
-
"@icore/auth-supabase",
|
|
571
|
-
"@icore/auth-mongodb"
|
|
572
|
-
]);
|
|
573
|
-
await stripTsconfigPath(targetDir, "@icore/auth-supabase");
|
|
574
|
-
await stripTsconfigPath(targetDir, "@icore/auth-mongodb");
|
|
575
|
-
try {
|
|
576
|
-
const src = await readFile3(modulePath, "utf8");
|
|
577
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/gm, "").replace(/^import \{[^}]*MongoDbAuthStrategy[^}]*\} from '@icore\/auth-mongodb';\n/gm, "").replace(
|
|
578
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
579
|
-
""
|
|
580
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseAuth[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDbAuth[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(AUTH_BRANCH, "return makeFirebaseAuth(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
581
|
-
await writeFile2(modulePath, next);
|
|
582
|
-
} catch {
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
if (authProvider === "mongodb") {
|
|
586
|
-
await rm(join3(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
|
|
587
|
-
await rm(join3(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
|
|
588
|
-
await stripDeps(join3(targetDir, "apps/microservices/auth/package.json"), [
|
|
589
|
-
"@icore/auth-supabase",
|
|
590
|
-
"@icore/auth-firebase",
|
|
591
|
-
"@icore/firebase-admin"
|
|
592
|
-
]);
|
|
593
|
-
await stripTsconfigPath(targetDir, "@icore/auth-supabase");
|
|
594
|
-
await stripTsconfigPath(targetDir, "@icore/auth-firebase");
|
|
595
|
-
try {
|
|
596
|
-
const src = await readFile3(modulePath, "utf8");
|
|
597
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/gm, "").replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseAuth[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeFirebaseAuth[\s\S]*?\n}\n/gm, "").replace(AUTH_BRANCH, "return makeMongoDbAuth(connection, cfg);");
|
|
598
|
-
await writeFile2(modulePath, next);
|
|
599
|
-
} catch {
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
|
|
604
|
-
if (uploadProvider === "none") return;
|
|
605
|
-
const modulePath = join3(targetDir, "apps/microservices/upload/src/app/app.module.ts");
|
|
606
|
-
const STORAGE_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseStorage\(cfg\);\n\s*if \(provider === 'firebase'\) return makeFirebaseStorage\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDbStorage\(connection\);\n\s*return makeCloudinaryStorage\(cfg\);/m;
|
|
607
|
-
if (uploadProvider !== "firebase") {
|
|
608
|
-
await rm(join3(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
|
|
609
|
-
await stripDeps(join3(targetDir, "apps/microservices/upload/package.json"), [
|
|
610
|
-
"@icore/storage-firebase",
|
|
611
|
-
"@icore/firebase-admin"
|
|
612
|
-
]);
|
|
613
|
-
await stripTsconfigPath(targetDir, "@icore/storage-firebase");
|
|
614
|
-
}
|
|
615
|
-
if (uploadProvider !== "cloudinary") {
|
|
616
|
-
await rm(join3(targetDir, "libs/storage-strategies/cloudinary"), {
|
|
617
|
-
recursive: true,
|
|
618
|
-
force: true
|
|
619
|
-
});
|
|
620
|
-
await stripDeps(join3(targetDir, "apps/microservices/upload/package.json"), [
|
|
621
|
-
"@icore/storage-cloudinary"
|
|
622
|
-
]);
|
|
623
|
-
await stripTsconfigPath(targetDir, "@icore/storage-cloudinary");
|
|
624
|
-
}
|
|
625
|
-
if (uploadProvider !== "supabase") {
|
|
626
|
-
await rm(join3(targetDir, "libs/storage-strategies/supabase"), { recursive: true, force: true });
|
|
627
|
-
await stripDeps(join3(targetDir, "apps/microservices/upload/package.json"), [
|
|
628
|
-
"@icore/storage-supabase"
|
|
629
|
-
]);
|
|
630
|
-
await stripTsconfigPath(targetDir, "@icore/storage-supabase");
|
|
631
|
-
}
|
|
632
|
-
if (uploadProvider !== "mongodb") {
|
|
633
|
-
await rm(join3(targetDir, "libs/storage-strategies/mongodb"), { recursive: true, force: true });
|
|
634
|
-
await stripDeps(join3(targetDir, "apps/microservices/upload/package.json"), [
|
|
635
|
-
"@icore/storage-mongodb"
|
|
636
|
-
]);
|
|
637
|
-
await stripTsconfigPath(targetDir, "@icore/storage-mongodb");
|
|
638
|
-
}
|
|
612
|
+
const composePath = join3(targetDir, "docker-compose.yml");
|
|
639
613
|
try {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
/^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/gm,
|
|
644
|
-
""
|
|
645
|
-
).replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirebaseStorage[\s\S]*?\n}\n/gm, "");
|
|
646
|
-
}
|
|
647
|
-
if (uploadProvider !== "cloudinary") {
|
|
648
|
-
src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/gm, "").replace(
|
|
649
|
-
/^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/gm,
|
|
650
|
-
""
|
|
651
|
-
).replace(/\nfunction makeCloudinaryStorage[\s\S]*?\n}\n/gm, "");
|
|
652
|
-
}
|
|
653
|
-
if (uploadProvider !== "supabase") {
|
|
654
|
-
src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(
|
|
655
|
-
/^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/gm,
|
|
656
|
-
""
|
|
657
|
-
).replace(/\nfunction makeSupabaseStorage[\s\S]*?\n}\n/gm, "");
|
|
658
|
-
}
|
|
659
|
-
if (uploadProvider !== "mongodb") {
|
|
660
|
-
src = src.replace(
|
|
661
|
-
/^import \{[^}]*MongoDbStorageStrategy[^}]*\} from '@icore\/storage-mongodb';\n/gm,
|
|
662
|
-
""
|
|
663
|
-
).replace(
|
|
664
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
665
|
-
""
|
|
666
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeMongoDbStorage[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "");
|
|
667
|
-
}
|
|
668
|
-
const chosenReturn = uploadProvider === "mongodb" ? `return makeMongoDbStorage(connection);` : `return make${uploadProvider.charAt(0).toUpperCase() + uploadProvider.slice(1)}Storage(cfg);`;
|
|
669
|
-
src = src.replace(STORAGE_BRANCH, chosenReturn);
|
|
670
|
-
if (uploadProvider !== "mongodb") {
|
|
671
|
-
src = src.replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
672
|
-
}
|
|
673
|
-
await writeFile2(modulePath, src);
|
|
614
|
+
const compose = await readFile4(composePath, "utf8");
|
|
615
|
+
const next = compose.replace(/\n {2}auth:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}auth:\n {8}condition: service_started/g, "").replace(/\n {6}AUTH_TRANSPORT:[^\n]*/g, "").replace(/\n {6}AUTH_REDIS_URL:[^\n]*/g, "");
|
|
616
|
+
await writeFile2(composePath, next);
|
|
674
617
|
} catch {
|
|
675
618
|
}
|
|
676
619
|
}
|
|
677
|
-
async function removeUnusedDbStrategies(targetDir, dbProvider) {
|
|
678
|
-
const modulePath = join3(targetDir, "apps/microservices/notes/src/app/app.module.ts");
|
|
679
|
-
const DB_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDb\(connection\);\n\s*return makeFirestoreDB\(cfg\);/m;
|
|
680
|
-
if (dbProvider === "supabase") {
|
|
681
|
-
await rm(join3(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
|
|
682
|
-
await rm(join3(targetDir, "libs/db-strategies/mongodb"), { recursive: true, force: true });
|
|
683
|
-
await stripDeps(join3(targetDir, "apps/microservices/notes/package.json"), [
|
|
684
|
-
"@icore/db-firestore",
|
|
685
|
-
"@icore/firebase-admin",
|
|
686
|
-
"@icore/db-mongodb"
|
|
687
|
-
]);
|
|
688
|
-
await stripTsconfigPath(targetDir, "@icore/db-firestore");
|
|
689
|
-
await stripTsconfigPath(targetDir, "@icore/db-mongodb");
|
|
690
|
-
try {
|
|
691
|
-
const src = await readFile3(modulePath, "utf8");
|
|
692
|
-
const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/gm, "").replace(/^import \{[^}]*MongoDbDBStrategy[^}]*\} from '@icore\/db-mongodb';\n/gm, "").replace(
|
|
693
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
694
|
-
""
|
|
695
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}firestore: \[[^\]]*\],\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirestoreDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDb[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(DB_BRANCH, "return makeSupabaseDB(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
696
|
-
await writeFile2(modulePath, next);
|
|
697
|
-
} catch {
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
if (dbProvider === "firebase") {
|
|
701
|
-
await rm(join3(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
|
|
702
|
-
await rm(join3(targetDir, "libs/db-strategies/mongodb"), { recursive: true, force: true });
|
|
703
|
-
await stripDeps(join3(targetDir, "apps/microservices/notes/package.json"), [
|
|
704
|
-
"@icore/db-supabase",
|
|
705
|
-
"@icore/db-mongodb"
|
|
706
|
-
]);
|
|
707
|
-
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
708
|
-
await stripTsconfigPath(targetDir, "@icore/db-mongodb");
|
|
709
|
-
try {
|
|
710
|
-
const src = await readFile3(modulePath, "utf8");
|
|
711
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/gm, "").replace(/^import \{[^}]*MongoDbDBStrategy[^}]*\} from '@icore\/db-mongodb';\n/gm, "").replace(
|
|
712
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
713
|
-
""
|
|
714
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDb[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(/\nfunction requireEnv[\s\S]*?\n}\n/gm, "").replace(DB_BRANCH, "return makeFirestoreDB(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
715
|
-
await writeFile2(modulePath, next);
|
|
716
|
-
} catch {
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
if (dbProvider === "mongodb") {
|
|
720
|
-
await rm(join3(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
|
|
721
|
-
await rm(join3(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
|
|
722
|
-
await stripDeps(join3(targetDir, "apps/microservices/notes/package.json"), [
|
|
723
|
-
"@icore/db-supabase",
|
|
724
|
-
"@icore/db-firestore",
|
|
725
|
-
"@icore/firebase-admin"
|
|
726
|
-
]);
|
|
727
|
-
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
728
|
-
await stripTsconfigPath(targetDir, "@icore/db-firestore");
|
|
729
|
-
try {
|
|
730
|
-
const src = await readFile3(modulePath, "utf8");
|
|
731
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/gm, "").replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}firestore: \[[^\]]*\],\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeFirestoreDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction requireEnv[\s\S]*?\n}\n/gm, "").replace(DB_BRANCH, "return makeMongoDb(connection);");
|
|
732
|
-
await writeFile2(modulePath, next);
|
|
733
|
-
} catch {
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
async function removeFirebaseAdminLib(targetDir) {
|
|
738
|
-
await rm(join3(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
|
|
739
|
-
await stripTsconfigPath(targetDir, "@icore/firebase-admin");
|
|
740
|
-
}
|
|
741
620
|
async function removeUploadStack(targetDir) {
|
|
742
621
|
const paths = [
|
|
743
622
|
"apps/microservices/upload",
|
|
@@ -751,14 +630,14 @@ async function removeUploadStack(targetDir) {
|
|
|
751
630
|
}
|
|
752
631
|
const appModulePath = join3(targetDir, "apps/api/src/app/app.module.ts");
|
|
753
632
|
try {
|
|
754
|
-
const appModule = await
|
|
633
|
+
const appModule = await readFile4(appModulePath, "utf8");
|
|
755
634
|
const next = appModule.replace(/^import \{ StorageModule \} from '\.\/storage\/storage\.module';\n/m, "").replace(/,\s*StorageModule/g, "");
|
|
756
635
|
await writeFile2(appModulePath, next);
|
|
757
636
|
} catch {
|
|
758
637
|
}
|
|
759
638
|
const gatewayEnv = join3(targetDir, "apps/api/.env");
|
|
760
639
|
try {
|
|
761
|
-
const env = await
|
|
640
|
+
const env = await readFile4(gatewayEnv, "utf8");
|
|
762
641
|
const next = env.split("\n").filter(
|
|
763
642
|
(line) => !line.startsWith("UPLOAD_") && !line.startsWith("# UPLOAD_") && !line.startsWith("MAX_FILE_SIZE_KB")
|
|
764
643
|
).join("\n");
|
|
@@ -771,9 +650,440 @@ async function removeUploadStack(targetDir) {
|
|
|
771
650
|
]);
|
|
772
651
|
}
|
|
773
652
|
|
|
774
|
-
// src/
|
|
775
|
-
import { readFile as
|
|
653
|
+
// src/manifest/wire-features.ts
|
|
654
|
+
import { readFile as readFile6, writeFile as writeFile4, rm as rm3 } from "fs/promises";
|
|
655
|
+
import { join as join5 } from "path";
|
|
656
|
+
|
|
657
|
+
// src/manifest/index.ts
|
|
658
|
+
var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
|
|
659
|
+
var MANIFEST = {
|
|
660
|
+
auth: {
|
|
661
|
+
supabase: {
|
|
662
|
+
libDirs: ["libs/auth-strategies/supabase"],
|
|
663
|
+
deps: { "@supabase/supabase-js": "^2.106.2" },
|
|
664
|
+
tsPaths: { "@icore/auth-supabase": ["libs/auth-strategies/supabase/src/index.ts"] },
|
|
665
|
+
nestModule: {
|
|
666
|
+
importFrom: "@icore/auth-supabase",
|
|
667
|
+
symbol: "SupabaseAuthModule",
|
|
668
|
+
into: "auth"
|
|
669
|
+
},
|
|
670
|
+
appTests: [
|
|
671
|
+
"apps/microservices/auth/src/app/__tests__/auth.controller.supabase.integration.unit.test.ts"
|
|
672
|
+
]
|
|
673
|
+
},
|
|
674
|
+
firebase: {
|
|
675
|
+
libDirs: ["libs/auth-strategies/firebase"],
|
|
676
|
+
deps: {},
|
|
677
|
+
tsPaths: { "@icore/auth-firebase": ["libs/auth-strategies/firebase/src/index.ts"] },
|
|
678
|
+
nestModule: {
|
|
679
|
+
importFrom: "@icore/auth-firebase",
|
|
680
|
+
symbol: "FirebaseAuthModule",
|
|
681
|
+
into: "auth"
|
|
682
|
+
},
|
|
683
|
+
appTests: [
|
|
684
|
+
"apps/microservices/auth/src/app/__tests__/auth.controller.firebase.integration.unit.test.ts"
|
|
685
|
+
]
|
|
686
|
+
},
|
|
687
|
+
mongodb: {
|
|
688
|
+
libDirs: ["libs/auth-strategies/mongodb"],
|
|
689
|
+
deps: { mongoose: "^9.6.3" },
|
|
690
|
+
tsPaths: { "@icore/auth-mongodb": ["libs/auth-strategies/mongodb/src/index.ts"] },
|
|
691
|
+
nestModule: { importFrom: "@icore/auth-mongodb", symbol: "MongoDbAuthModule", into: "auth" }
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
storage: {
|
|
695
|
+
supabase: {
|
|
696
|
+
libDirs: ["libs/storage-strategies/supabase"],
|
|
697
|
+
deps: { "@supabase/supabase-js": "^2.106.2" },
|
|
698
|
+
tsPaths: { "@icore/storage-supabase": ["libs/storage-strategies/supabase/src/index.ts"] },
|
|
699
|
+
nestModule: {
|
|
700
|
+
importFrom: "@icore/storage-supabase",
|
|
701
|
+
symbol: "SupabaseStorageModule",
|
|
702
|
+
into: "upload"
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
firebase: {
|
|
706
|
+
libDirs: ["libs/storage-strategies/firebase"],
|
|
707
|
+
deps: {},
|
|
708
|
+
tsPaths: { "@icore/storage-firebase": ["libs/storage-strategies/firebase/src/index.ts"] },
|
|
709
|
+
nestModule: {
|
|
710
|
+
importFrom: "@icore/storage-firebase",
|
|
711
|
+
symbol: "FirebaseStorageModule",
|
|
712
|
+
into: "upload"
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
cloudinary: {
|
|
716
|
+
libDirs: ["libs/storage-strategies/cloudinary"],
|
|
717
|
+
deps: { cloudinary: "^2.10.0" },
|
|
718
|
+
tsPaths: { "@icore/storage-cloudinary": ["libs/storage-strategies/cloudinary/src/index.ts"] },
|
|
719
|
+
nestModule: {
|
|
720
|
+
importFrom: "@icore/storage-cloudinary",
|
|
721
|
+
symbol: "CloudinaryStorageModule",
|
|
722
|
+
into: "upload"
|
|
723
|
+
}
|
|
724
|
+
},
|
|
725
|
+
mongodb: {
|
|
726
|
+
libDirs: ["libs/storage-strategies/mongodb"],
|
|
727
|
+
deps: { mongoose: "^9.6.3" },
|
|
728
|
+
tsPaths: { "@icore/storage-mongodb": ["libs/storage-strategies/mongodb/src/index.ts"] },
|
|
729
|
+
nestModule: {
|
|
730
|
+
importFrom: "@icore/storage-mongodb",
|
|
731
|
+
symbol: "MongoDbStorageModule",
|
|
732
|
+
into: "upload"
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
},
|
|
736
|
+
db: {
|
|
737
|
+
supabase: {
|
|
738
|
+
libDirs: ["libs/db-strategies/supabase"],
|
|
739
|
+
deps: { "@supabase/supabase-js": "^2.106.2" },
|
|
740
|
+
tsPaths: { "@icore/db-supabase": ["libs/db-strategies/supabase/src/index.ts"] },
|
|
741
|
+
nestModule: { importFrom: "@icore/db-supabase", symbol: "SupabaseDbModule", into: "notes" }
|
|
742
|
+
},
|
|
743
|
+
firebase: {
|
|
744
|
+
libDirs: ["libs/db-strategies/firestore"],
|
|
745
|
+
deps: {},
|
|
746
|
+
tsPaths: { "@icore/db-firestore": ["libs/db-strategies/firestore/src/index.ts"] },
|
|
747
|
+
nestModule: { importFrom: "@icore/db-firestore", symbol: "FirestoreDbModule", into: "notes" }
|
|
748
|
+
},
|
|
749
|
+
mongodb: {
|
|
750
|
+
libDirs: ["libs/db-strategies/mongodb"],
|
|
751
|
+
deps: { mongoose: "^9.6.3" },
|
|
752
|
+
tsPaths: { "@icore/db-mongodb": ["libs/db-strategies/mongodb/src/index.ts"] },
|
|
753
|
+
nestModule: { importFrom: "@icore/db-mongodb", symbol: "MongoDbDbModule", into: "notes" }
|
|
754
|
+
}
|
|
755
|
+
},
|
|
756
|
+
feature: {
|
|
757
|
+
notes: {
|
|
758
|
+
libDirs: [
|
|
759
|
+
"apps/microservices/notes",
|
|
760
|
+
"apps/microservices/notes-e2e",
|
|
761
|
+
"libs/notes-client",
|
|
762
|
+
"libs/db-strategies",
|
|
763
|
+
"apps/api/src/app/notes",
|
|
764
|
+
"apps/client/src/components/notes",
|
|
765
|
+
"apps/client/src/routes/_dashboard/notes.tsx",
|
|
766
|
+
"apps/client/src/queries/notes.ts"
|
|
767
|
+
],
|
|
768
|
+
deps: { "@icore/notes-client": "*", "@casl/ability": "^7.0.0" },
|
|
769
|
+
tsPaths: { "@icore/notes-client": ["libs/notes-client/src/index.ts"] },
|
|
770
|
+
gatewayModule: { importFrom: "./notes/notes.module", symbol: "NotesModule" },
|
|
771
|
+
gatewayService: { name: "notes", prefix: "NOTES" },
|
|
772
|
+
clientNav: { route: "/notes", labelKey: "nav.notes", iconName: "notes" }
|
|
773
|
+
},
|
|
774
|
+
payment: {
|
|
775
|
+
libDirs: [
|
|
776
|
+
"apps/microservices/payment",
|
|
777
|
+
"apps/microservices/payment-e2e",
|
|
778
|
+
"libs/payment-client",
|
|
779
|
+
"apps/api/src/app/payment"
|
|
780
|
+
],
|
|
781
|
+
deps: { "@icore/payment-client": "*", "@idevconn/payment": "^1.2.0" },
|
|
782
|
+
tsPaths: { "@icore/payment-client": ["libs/payment-client/src/index.ts"] },
|
|
783
|
+
gatewayModule: { importFrom: "./payment/payment.module", symbol: "PaymentModule" },
|
|
784
|
+
gatewayService: { name: "payment", prefix: "PAYMENT" }
|
|
785
|
+
},
|
|
786
|
+
jobs: {
|
|
787
|
+
libDirs: [
|
|
788
|
+
"apps/microservices/jobs",
|
|
789
|
+
"libs/jobs-client",
|
|
790
|
+
"apps/api/src/app/admin",
|
|
791
|
+
"Dockerfile.ms-jobs"
|
|
792
|
+
],
|
|
793
|
+
deps: {
|
|
794
|
+
"@icore/jobs-client": "*",
|
|
795
|
+
"@bull-board/api": "^7.1.5",
|
|
796
|
+
"@bull-board/express": "^7.1.5"
|
|
797
|
+
},
|
|
798
|
+
tsPaths: { "@icore/jobs-client": ["libs/jobs-client/src/index.ts"] },
|
|
799
|
+
gatewayModule: { importFrom: "./admin/admin.module", symbol: "AdminModule" },
|
|
800
|
+
dockerService: "jobs"
|
|
801
|
+
}
|
|
802
|
+
},
|
|
803
|
+
ui: { shadcn: EMPTY, antd: EMPTY, mui: EMPTY },
|
|
804
|
+
transport: { tcp: EMPTY, redis: EMPTY, nats: EMPTY, mqtt: EMPTY, rmq: EMPTY, kafka: EMPTY },
|
|
805
|
+
shared: {
|
|
806
|
+
firebaseAdmin: {
|
|
807
|
+
libDirs: ["libs/firebase-admin"],
|
|
808
|
+
deps: { "firebase-admin": "^13.10.0" },
|
|
809
|
+
tsPaths: { "@icore/firebase-admin": ["libs/firebase-admin/src/index.ts"] }
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
// src/manifest/wire-provider.ts
|
|
815
|
+
import { readFile as readFile5, writeFile as writeFile3, rm as rm2 } from "fs/promises";
|
|
776
816
|
import { join as join4 } from "path";
|
|
817
|
+
async function writeProvider(targetDir, axis, provider) {
|
|
818
|
+
const nestModule = axis.section[provider]?.nestModule;
|
|
819
|
+
if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
|
|
820
|
+
const { importFrom, symbol } = nestModule;
|
|
821
|
+
const content = `import { ${symbol} } from '${importFrom}';
|
|
822
|
+
|
|
823
|
+
const ENV_PATH = '${axis.envPath}';
|
|
824
|
+
|
|
825
|
+
export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
|
|
826
|
+
`;
|
|
827
|
+
await writeFile3(join4(targetDir, axis.providerFile), content);
|
|
828
|
+
}
|
|
829
|
+
async function stripJsonKeys(path, drop) {
|
|
830
|
+
try {
|
|
831
|
+
const pkg = JSON.parse(await readFile5(path, "utf8"));
|
|
832
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
833
|
+
const deps = pkg[field];
|
|
834
|
+
if (!deps) continue;
|
|
835
|
+
for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
|
|
836
|
+
}
|
|
837
|
+
await writeFile3(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
838
|
+
} catch {
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
async function stripTsconfigKeys(targetDir, aliases) {
|
|
842
|
+
const path = join4(targetDir, "tsconfig.base.json");
|
|
843
|
+
try {
|
|
844
|
+
const parsed = JSON.parse(await readFile5(path, "utf8"));
|
|
845
|
+
const paths = parsed.compilerOptions?.paths;
|
|
846
|
+
if (paths) for (const a of aliases) delete paths[a];
|
|
847
|
+
await writeFile3(path, JSON.stringify(parsed, null, 2) + "\n");
|
|
848
|
+
} catch {
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
async function cleanupUnusedAxis(targetDir, axis, chosen) {
|
|
852
|
+
for (const provider of Object.keys(axis.section)) {
|
|
853
|
+
if (provider === chosen) continue;
|
|
854
|
+
const unit = axis.section[provider];
|
|
855
|
+
for (const dir of unit.libDirs)
|
|
856
|
+
await rm2(join4(targetDir, dir), { recursive: true, force: true });
|
|
857
|
+
for (const t of unit.appTests ?? []) await rm2(join4(targetDir, t), { force: true });
|
|
858
|
+
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
859
|
+
await stripJsonKeys(join4(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
|
|
860
|
+
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// src/manifest/wire-features.ts
|
|
865
|
+
var FEATURES_MODULE = "apps/api/src/app/features.module.ts";
|
|
866
|
+
var GATEWAY_SERVICES = "apps/api/src/app/gateway-services.ts";
|
|
867
|
+
var API_PKG = "apps/api/package.json";
|
|
868
|
+
var FEATURES = MANIFEST.feature;
|
|
869
|
+
function selectedFeatures(opts) {
|
|
870
|
+
const out = [];
|
|
871
|
+
if (opts.example === "notes") out.push("notes");
|
|
872
|
+
if (opts.payment !== "none") out.push("payment");
|
|
873
|
+
if (opts.jobs !== "none") out.push("jobs");
|
|
874
|
+
return out;
|
|
875
|
+
}
|
|
876
|
+
async function writeFeaturesWiring(targetDir, opts) {
|
|
877
|
+
const chosen = selectedFeatures(opts);
|
|
878
|
+
const mods = chosen.map((k) => FEATURES[k].gatewayModule).filter((m) => !!m);
|
|
879
|
+
const imports = mods.map((m) => `import { ${m.symbol} } from '${m.importFrom}';`).join("\n");
|
|
880
|
+
const symbols = mods.map((m) => m.symbol).join(", ");
|
|
881
|
+
const featuresModule = `import { Module } from '@nestjs/common';
|
|
882
|
+
` + (imports ? imports + "\n" : "") + `
|
|
883
|
+
@Module({
|
|
884
|
+
imports: [${symbols}],
|
|
885
|
+
})
|
|
886
|
+
export class FeaturesModule {}
|
|
887
|
+
`;
|
|
888
|
+
await writeFile4(join5(targetDir, FEATURES_MODULE), featuresModule);
|
|
889
|
+
const services = [];
|
|
890
|
+
if (opts.authProvider !== "none") services.push({ name: "auth", prefix: "AUTH" });
|
|
891
|
+
if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
|
|
892
|
+
for (const k of chosen) {
|
|
893
|
+
const svc = FEATURES[k].gatewayService;
|
|
894
|
+
if (svc) services.push(svc);
|
|
895
|
+
}
|
|
896
|
+
const entries = services.map((s) => ` { name: '${s.name}', prefix: '${s.prefix}' },`).join("\n");
|
|
897
|
+
const gatewayServices = `/** Microservices the gateway proxies. Generated by create-icore. */
|
|
898
|
+
export const GATEWAY_SERVICES = [
|
|
899
|
+
${entries}
|
|
900
|
+
];
|
|
901
|
+
`;
|
|
902
|
+
await writeFile4(join5(targetDir, GATEWAY_SERVICES), gatewayServices);
|
|
903
|
+
}
|
|
904
|
+
async function stripJobsDockerCompose(targetDir) {
|
|
905
|
+
const composePath = join5(targetDir, "docker-compose.yml");
|
|
906
|
+
try {
|
|
907
|
+
const compose = await readFile6(composePath, "utf8");
|
|
908
|
+
const next = compose.replace(/\n {2}jobs:[\s\S]+?(?=\n {2}\w+:|\nnetworks:)/m, "\n").replace(/\n {6}jobs:\n {8}condition: service_started/g, "").replace(/\n {6}JOBS_REDIS_URL:[^\n]*/g, "");
|
|
909
|
+
await writeFile4(composePath, next);
|
|
910
|
+
} catch {
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
async function cleanupUnusedFeatures(targetDir, opts) {
|
|
914
|
+
const chosen = new Set(selectedFeatures(opts));
|
|
915
|
+
for (const key of ["notes", "payment", "jobs"]) {
|
|
916
|
+
if (chosen.has(key)) continue;
|
|
917
|
+
const unit = FEATURES[key];
|
|
918
|
+
for (const dir of unit.libDirs)
|
|
919
|
+
await rm3(join5(targetDir, dir), { recursive: true, force: true });
|
|
920
|
+
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
921
|
+
await stripJsonKeys(join5(targetDir, API_PKG), (k) => dropKeys.has(k));
|
|
922
|
+
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
923
|
+
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
924
|
+
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/manifest/wire-client.ts
|
|
929
|
+
import { writeFile as writeFile5 } from "fs/promises";
|
|
930
|
+
import { join as join6 } from "path";
|
|
931
|
+
var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
|
|
932
|
+
var BASE_NAV = [
|
|
933
|
+
{ to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
|
|
934
|
+
];
|
|
935
|
+
var PROFILE_NAV = { to: "/profile", labelKey: "nav.profile", iconName: "profile" };
|
|
936
|
+
function renderEntry(n) {
|
|
937
|
+
const parts = [`to: '${n.to}'`, `labelKey: '${n.labelKey}'`, `iconName: '${n.iconName}'`];
|
|
938
|
+
if (n.exact) parts.push("exact: true");
|
|
939
|
+
return ` { ${parts.join(", ")} },`;
|
|
940
|
+
}
|
|
941
|
+
async function writeNavConfig(targetDir, opts) {
|
|
942
|
+
const entries = [...BASE_NAV];
|
|
943
|
+
const notesNav = MANIFEST.feature.notes.clientNav;
|
|
944
|
+
if (opts.example === "notes" && notesNav) {
|
|
945
|
+
entries.push({
|
|
946
|
+
to: notesNav.route,
|
|
947
|
+
labelKey: notesNav.labelKey,
|
|
948
|
+
iconName: notesNav.iconName,
|
|
949
|
+
exact: notesNav.exact
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
entries.push(PROFILE_NAV);
|
|
953
|
+
const content = `/**
|
|
954
|
+
* Sidebar navigation. UI-agnostic: each LayoutSider maps \`iconName\` to its own
|
|
955
|
+
* icon library. Generated by create-icore.
|
|
956
|
+
*/
|
|
957
|
+
export interface NavItem {
|
|
958
|
+
to: string;
|
|
959
|
+
labelKey: string;
|
|
960
|
+
iconName: 'dashboard' | 'notes' | 'profile';
|
|
961
|
+
exact?: boolean;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
export const NAV_CONFIG: NavItem[] = [
|
|
965
|
+
` + entries.map(renderEntry).join("\n") + `
|
|
966
|
+
];
|
|
967
|
+
`;
|
|
968
|
+
await writeFile5(join6(targetDir, NAV_CONFIG_FILE), content);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// src/manifest/blueprint.ts
|
|
972
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
973
|
+
import { join as join7 } from "path";
|
|
974
|
+
async function writeBlueprintJson(targetDir, opts) {
|
|
975
|
+
const blueprint = {
|
|
976
|
+
schemaVersion: 1,
|
|
977
|
+
projectName: opts.projectName,
|
|
978
|
+
authProvider: opts.authProvider,
|
|
979
|
+
dbProvider: opts.dbProvider,
|
|
980
|
+
upload: opts.upload,
|
|
981
|
+
payment: opts.payment,
|
|
982
|
+
jobs: opts.jobs,
|
|
983
|
+
example: opts.example,
|
|
984
|
+
ui: opts.ui,
|
|
985
|
+
transport: opts.transport,
|
|
986
|
+
packageManager: opts.packageManager
|
|
987
|
+
};
|
|
988
|
+
await writeFile6(join7(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
|
|
989
|
+
}
|
|
990
|
+
async function writeJson(targetDir, rel, data) {
|
|
991
|
+
await writeFile6(join7(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
|
|
992
|
+
}
|
|
993
|
+
async function writeServiceBlueprints(targetDir, opts) {
|
|
994
|
+
const t = opts.transport;
|
|
995
|
+
if (opts.authProvider !== "none") {
|
|
996
|
+
await writeJson(targetDir, "apps/microservices/auth", {
|
|
997
|
+
schemaVersion: 1,
|
|
998
|
+
service: "auth",
|
|
999
|
+
authProvider: opts.authProvider,
|
|
1000
|
+
transport: t
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
if (opts.upload !== "none") {
|
|
1004
|
+
await writeJson(targetDir, "apps/microservices/upload", {
|
|
1005
|
+
schemaVersion: 1,
|
|
1006
|
+
service: "upload",
|
|
1007
|
+
storageProvider: opts.upload,
|
|
1008
|
+
transport: t
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
if (opts.example !== "none") {
|
|
1012
|
+
await writeJson(targetDir, "apps/microservices/notes", {
|
|
1013
|
+
schemaVersion: 1,
|
|
1014
|
+
service: "notes",
|
|
1015
|
+
dbProvider: opts.dbProvider,
|
|
1016
|
+
transport: t
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
if (opts.payment !== "none") {
|
|
1020
|
+
await writeJson(targetDir, "apps/microservices/payment", {
|
|
1021
|
+
schemaVersion: 1,
|
|
1022
|
+
service: "payment",
|
|
1023
|
+
paymentProvider: opts.payment,
|
|
1024
|
+
transport: t
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
if (opts.jobs !== "none") {
|
|
1028
|
+
await writeJson(targetDir, "apps/microservices/jobs", {
|
|
1029
|
+
schemaVersion: 1,
|
|
1030
|
+
service: "jobs",
|
|
1031
|
+
jobsProvider: opts.jobs
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
const features = [];
|
|
1035
|
+
if (opts.example !== "none") features.push("notes");
|
|
1036
|
+
if (opts.payment !== "none") features.push("payment");
|
|
1037
|
+
if (opts.jobs !== "none") features.push("jobs");
|
|
1038
|
+
await writeJson(targetDir, "apps/api", {
|
|
1039
|
+
schemaVersion: 1,
|
|
1040
|
+
service: "api",
|
|
1041
|
+
features,
|
|
1042
|
+
transport: t
|
|
1043
|
+
});
|
|
1044
|
+
await writeJson(targetDir, "apps/client", {
|
|
1045
|
+
schemaVersion: 1,
|
|
1046
|
+
service: "client",
|
|
1047
|
+
ui: opts.ui
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// src/manifest/wire-auth.ts
|
|
1052
|
+
var AUTH = {
|
|
1053
|
+
section: MANIFEST.auth,
|
|
1054
|
+
providerFile: "apps/microservices/auth/src/app/auth.provider.ts",
|
|
1055
|
+
exportConst: "AuthProviderModule",
|
|
1056
|
+
msPackageJson: "apps/microservices/auth/package.json",
|
|
1057
|
+
envPath: "apps/microservices/auth/.env"
|
|
1058
|
+
};
|
|
1059
|
+
var writeAuthProvider = (targetDir, provider) => writeProvider(targetDir, AUTH, provider);
|
|
1060
|
+
var cleanupUnusedAuth = (targetDir, chosen) => cleanupUnusedAxis(targetDir, AUTH, chosen);
|
|
1061
|
+
|
|
1062
|
+
// src/manifest/wire-storage.ts
|
|
1063
|
+
var STORAGE = {
|
|
1064
|
+
section: MANIFEST.storage,
|
|
1065
|
+
providerFile: "apps/microservices/upload/src/app/storage.provider.ts",
|
|
1066
|
+
exportConst: "StorageProviderModule",
|
|
1067
|
+
msPackageJson: "apps/microservices/upload/package.json",
|
|
1068
|
+
envPath: "apps/microservices/upload/.env"
|
|
1069
|
+
};
|
|
1070
|
+
var writeStorageProvider = (targetDir, provider) => writeProvider(targetDir, STORAGE, provider);
|
|
1071
|
+
var cleanupUnusedStorage = (targetDir, chosen) => cleanupUnusedAxis(targetDir, STORAGE, chosen);
|
|
1072
|
+
|
|
1073
|
+
// src/manifest/wire-db.ts
|
|
1074
|
+
var DB = {
|
|
1075
|
+
section: MANIFEST.db,
|
|
1076
|
+
providerFile: "apps/microservices/notes/src/app/db.provider.ts",
|
|
1077
|
+
exportConst: "DbProviderModule",
|
|
1078
|
+
msPackageJson: "apps/microservices/notes/package.json",
|
|
1079
|
+
envPath: "apps/microservices/notes/.env"
|
|
1080
|
+
};
|
|
1081
|
+
var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, provider);
|
|
1082
|
+
var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
|
|
1083
|
+
|
|
1084
|
+
// src/lib/scaffold-pkg.ts
|
|
1085
|
+
import { readFile as readFile7, writeFile as writeFile7, mkdir, readdir } from "fs/promises";
|
|
1086
|
+
import { join as join8 } from "path";
|
|
777
1087
|
|
|
778
1088
|
// src/lib/options.ts
|
|
779
1089
|
function pmRun(pm, script) {
|
|
@@ -782,8 +1092,8 @@ function pmRun(pm, script) {
|
|
|
782
1092
|
|
|
783
1093
|
// src/lib/scaffold-pkg.ts
|
|
784
1094
|
async function writePnpmWorkspace(targetDir) {
|
|
785
|
-
const pkgPath =
|
|
786
|
-
const pkg = JSON.parse(await
|
|
1095
|
+
const pkgPath = join8(targetDir, "package.json");
|
|
1096
|
+
const pkg = JSON.parse(await readFile7(pkgPath, "utf8"));
|
|
787
1097
|
const workspaces = pkg.workspaces ?? [];
|
|
788
1098
|
const packagesBlock = workspaces.map((p3) => ` - '${p3}'`).join("\n");
|
|
789
1099
|
const allowBuilds = [
|
|
@@ -792,7 +1102,9 @@ async function writePnpmWorkspace(targetDir) {
|
|
|
792
1102
|
"@parcel/watcher",
|
|
793
1103
|
"@scarf/scarf",
|
|
794
1104
|
"@swc/core",
|
|
1105
|
+
"bcrypt",
|
|
795
1106
|
"less",
|
|
1107
|
+
"mongodb-memory-server",
|
|
796
1108
|
"msgpackr-extract",
|
|
797
1109
|
"nx",
|
|
798
1110
|
"protobufjs",
|
|
@@ -804,7 +1116,7 @@ ${packagesBlock}
|
|
|
804
1116
|
allowBuilds:
|
|
805
1117
|
${allowBuilds}
|
|
806
1118
|
`;
|
|
807
|
-
await
|
|
1119
|
+
await writeFile7(join8(targetDir, "pnpm-workspace.yaml"), content);
|
|
808
1120
|
}
|
|
809
1121
|
async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
810
1122
|
async function walk(dir) {
|
|
@@ -817,9 +1129,9 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
817
1129
|
}
|
|
818
1130
|
for (const e of entries) {
|
|
819
1131
|
if (e.isDirectory() && e.name !== "node_modules") {
|
|
820
|
-
found.push(...await walk(
|
|
1132
|
+
found.push(...await walk(join8(dir, e.name)));
|
|
821
1133
|
} else if (e.isFile() && e.name === "package.json") {
|
|
822
|
-
found.push(
|
|
1134
|
+
found.push(join8(dir, e.name));
|
|
823
1135
|
}
|
|
824
1136
|
}
|
|
825
1137
|
return found;
|
|
@@ -827,17 +1139,17 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
827
1139
|
const pkgFiles = await walk(targetDir);
|
|
828
1140
|
for (const f of pkgFiles) {
|
|
829
1141
|
try {
|
|
830
|
-
const raw = await
|
|
1142
|
+
const raw = await readFile7(f, "utf8");
|
|
831
1143
|
const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
|
|
832
|
-
if (next !== raw) await
|
|
1144
|
+
if (next !== raw) await writeFile7(f, next);
|
|
833
1145
|
} catch {
|
|
834
1146
|
}
|
|
835
1147
|
}
|
|
836
1148
|
}
|
|
837
1149
|
async function patchGitignoreForPm(targetDir, pm) {
|
|
838
|
-
const giPath =
|
|
1150
|
+
const giPath = join8(targetDir, ".gitignore");
|
|
839
1151
|
try {
|
|
840
|
-
let src = await
|
|
1152
|
+
let src = await readFile7(giPath, "utf8");
|
|
841
1153
|
src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
|
|
842
1154
|
if (pm !== "yarn") {
|
|
843
1155
|
src = src.replace(/^\.yarn\/\*\s*\n/m, "").replace(/^!\.yarn\/patches\s*\n/m, "").replace(/^!\.yarn\/plugins\s*\n/m, "").replace(/^!\.yarn\/releases\s*\n/m, "").replace(/^!\.yarn\/sdks\s*\n/m, "").replace(/^!\.yarn\/versions\s*\n/m, "").replace(/^\.pnp\.\*\s*\n/m, "");
|
|
@@ -852,7 +1164,7 @@ async function patchGitignoreForPm(targetDir, pm) {
|
|
|
852
1164
|
src += "\n# npm\nnpm-debug.log*\n";
|
|
853
1165
|
}
|
|
854
1166
|
}
|
|
855
|
-
await
|
|
1167
|
+
await writeFile7(giPath, src);
|
|
856
1168
|
} catch {
|
|
857
1169
|
}
|
|
858
1170
|
}
|
|
@@ -867,7 +1179,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
867
1179
|
if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
|
|
868
1180
|
const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
|
|
869
1181
|
const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
870
|
-
await
|
|
1182
|
+
await writeFile7(join8(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
|
|
871
1183
|
const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
|
|
872
1184
|
const readme = `# ${opts.projectName}
|
|
873
1185
|
|
|
@@ -917,7 +1229,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
|
|
|
917
1229
|
|
|
918
1230
|
Apache-2.0
|
|
919
1231
|
`;
|
|
920
|
-
await
|
|
1232
|
+
await writeFile7(join8(targetDir, "README.md"), readme);
|
|
921
1233
|
const agents = `# ${opts.projectName} \u2014 Agent Instructions
|
|
922
1234
|
|
|
923
1235
|
## Stack snapshot
|
|
@@ -998,8 +1310,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
|
|
|
998
1310
|
- Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
|
|
999
1311
|
- Run: \`${nx} test <project>\`
|
|
1000
1312
|
`;
|
|
1001
|
-
await
|
|
1002
|
-
await mkdir(
|
|
1313
|
+
await writeFile7(join8(targetDir, "AGENTS.md"), agents);
|
|
1314
|
+
await mkdir(join8(targetDir, ".claude"), { recursive: true });
|
|
1003
1315
|
const mcpServers = {
|
|
1004
1316
|
nx: {
|
|
1005
1317
|
command: "npx",
|
|
@@ -1040,8 +1352,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
|
|
|
1040
1352
|
]
|
|
1041
1353
|
}
|
|
1042
1354
|
};
|
|
1043
|
-
await
|
|
1044
|
-
|
|
1355
|
+
await writeFile7(
|
|
1356
|
+
join8(targetDir, ".claude", "settings.json"),
|
|
1045
1357
|
JSON.stringify(settings, null, 2) + "\n"
|
|
1046
1358
|
);
|
|
1047
1359
|
}
|
|
@@ -1065,16 +1377,16 @@ async function copyTree(src, dest) {
|
|
|
1065
1377
|
const entries = await readdir2(src, { withFileTypes: true });
|
|
1066
1378
|
for (const entry of entries) {
|
|
1067
1379
|
if (IGNORE_TOP.has(entry.name)) continue;
|
|
1068
|
-
const s =
|
|
1069
|
-
const d =
|
|
1380
|
+
const s = join9(src, entry.name);
|
|
1381
|
+
const d = join9(dest, entry.name);
|
|
1070
1382
|
if (entry.isDirectory()) await copyTree(s, d);
|
|
1071
1383
|
else if (entry.isFile()) await copyFile(s, d);
|
|
1072
1384
|
}
|
|
1073
1385
|
}
|
|
1074
1386
|
async function selectClientTemplate(targetDir, opts) {
|
|
1075
|
-
const templatesRoot =
|
|
1076
|
-
const chosen =
|
|
1077
|
-
const destClient =
|
|
1387
|
+
const templatesRoot = join9(targetDir, "apps/templates");
|
|
1388
|
+
const chosen = join9(templatesRoot, `client-${opts.ui}`);
|
|
1389
|
+
const destClient = join9(targetDir, "apps/client");
|
|
1078
1390
|
let chosenUi = opts.ui;
|
|
1079
1391
|
try {
|
|
1080
1392
|
const s = await stat(chosen);
|
|
@@ -1082,9 +1394,9 @@ async function selectClientTemplate(targetDir, opts) {
|
|
|
1082
1394
|
await copyTree(chosen, destClient);
|
|
1083
1395
|
} catch {
|
|
1084
1396
|
chosenUi = "shadcn";
|
|
1085
|
-
await copyTree(
|
|
1397
|
+
await copyTree(join9(templatesRoot, "client-shadcn"), destClient);
|
|
1086
1398
|
}
|
|
1087
|
-
await
|
|
1399
|
+
await rm4(templatesRoot, { recursive: true, force: true });
|
|
1088
1400
|
await rewriteClientPaths(destClient, chosenUi);
|
|
1089
1401
|
}
|
|
1090
1402
|
async function rewriteClientPaths(clientDir, ui) {
|
|
@@ -1097,11 +1409,11 @@ async function rewriteClientPaths(clientDir, ui) {
|
|
|
1097
1409
|
"eslint.config.mjs"
|
|
1098
1410
|
];
|
|
1099
1411
|
for (const rel of candidates) {
|
|
1100
|
-
const path =
|
|
1412
|
+
const path = join9(clientDir, rel);
|
|
1101
1413
|
try {
|
|
1102
|
-
const raw = await
|
|
1414
|
+
const raw = await readFile8(path, "utf8");
|
|
1103
1415
|
const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
|
|
1104
|
-
if (next !== raw) await
|
|
1416
|
+
if (next !== raw) await writeFile8(path, next);
|
|
1105
1417
|
} catch {
|
|
1106
1418
|
}
|
|
1107
1419
|
}
|
|
@@ -1117,12 +1429,12 @@ function gitInit(cwd, projectName) {
|
|
|
1117
1429
|
}
|
|
1118
1430
|
function resolveYarnBin(cwd) {
|
|
1119
1431
|
try {
|
|
1120
|
-
const yarnrc = readFileSync(
|
|
1432
|
+
const yarnrc = readFileSync(join9(cwd, ".yarnrc.yml"), "utf8");
|
|
1121
1433
|
const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
|
|
1122
|
-
if (match?.[1]) return
|
|
1434
|
+
if (match?.[1]) return join9(cwd, match[1].trim());
|
|
1123
1435
|
} catch {
|
|
1124
1436
|
}
|
|
1125
|
-
return
|
|
1437
|
+
return join9(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
|
|
1126
1438
|
}
|
|
1127
1439
|
function runInstall(cwd, pm) {
|
|
1128
1440
|
if (pm === "yarn") {
|
|
@@ -1136,7 +1448,7 @@ function runInstall(cwd, pm) {
|
|
|
1136
1448
|
async function scaffold(opts, templatesDir2) {
|
|
1137
1449
|
await copyTree(templatesDir2, opts.targetDir);
|
|
1138
1450
|
await rewriteRootPackageJson(opts.targetDir, opts);
|
|
1139
|
-
await writeAuthEnv(opts.targetDir, opts);
|
|
1451
|
+
if (opts.authProvider !== "none") await writeAuthEnv(opts.targetDir, opts);
|
|
1140
1452
|
await writeUploadEnv(opts.targetDir, opts);
|
|
1141
1453
|
await writeNotesEnv(opts.targetDir, opts);
|
|
1142
1454
|
await writePaymentEnv(opts.targetDir, opts);
|
|
@@ -1145,19 +1457,33 @@ async function scaffold(opts, templatesDir2) {
|
|
|
1145
1457
|
await selectClientTemplate(opts.targetDir, opts);
|
|
1146
1458
|
await writeClientEnv(opts.targetDir);
|
|
1147
1459
|
if (opts.upload === "none") await removeUploadStack(opts.targetDir);
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1460
|
+
await cleanupUnusedFeatures(opts.targetDir, opts);
|
|
1461
|
+
await writeFeaturesWiring(opts.targetDir, opts);
|
|
1462
|
+
await writeNavConfig(opts.targetDir, opts);
|
|
1463
|
+
if (opts.authProvider !== "none") {
|
|
1464
|
+
await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
|
|
1465
|
+
await writeAuthProvider(opts.targetDir, opts.authProvider);
|
|
1466
|
+
} else {
|
|
1467
|
+
await removeAuthStack(opts.targetDir);
|
|
1468
|
+
}
|
|
1469
|
+
if (opts.upload !== "none") {
|
|
1470
|
+
await cleanupUnusedStorage(opts.targetDir, opts.upload);
|
|
1471
|
+
await writeStorageProvider(opts.targetDir, opts.upload);
|
|
1472
|
+
}
|
|
1154
1473
|
const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
1155
1474
|
if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
|
|
1475
|
+
await cleanupUnusedDb(opts.targetDir, opts.dbProvider);
|
|
1476
|
+
if (opts.dbProvider !== "none" && opts.example !== "none") {
|
|
1477
|
+
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1478
|
+
}
|
|
1479
|
+
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1480
|
+
await writeBlueprintJson(opts.targetDir, opts);
|
|
1481
|
+
await writeServiceBlueprints(opts.targetDir, opts);
|
|
1156
1482
|
if (opts.packageManager === "yarn") {
|
|
1157
|
-
await
|
|
1483
|
+
await writeFile8(join9(opts.targetDir, "yarn.lock"), "");
|
|
1158
1484
|
} else {
|
|
1159
|
-
await
|
|
1160
|
-
await
|
|
1485
|
+
await rm4(join9(opts.targetDir, ".yarn"), { recursive: true, force: true });
|
|
1486
|
+
await rm4(join9(opts.targetDir, ".yarnrc.yml"), { force: true });
|
|
1161
1487
|
}
|
|
1162
1488
|
if (opts.packageManager === "pnpm") {
|
|
1163
1489
|
await writePnpmWorkspace(opts.targetDir);
|