@idevconn/create-icore 0.4.1 → 0.5.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/cli.js +304 -24
- package/dist/index.cjs +301 -23
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +300 -23
- package/package.json +1 -1
- package/templates/apps/api/.env.example +14 -0
- package/templates/apps/api/src/app/app.module.ts +5 -1
- package/templates/apps/api/src/main.ts +12 -6
- package/templates/apps/microservices/auth/package.json +1 -1
- package/templates/apps/microservices/auth/project.json +2 -1
- package/templates/apps/microservices/auth/src/app/app.module.ts +50 -39
- package/templates/apps/microservices/auth/src/main.ts +6 -23
- package/templates/apps/microservices/jobs/project.json +2 -1
- package/templates/apps/microservices/jobs/src/app/redis-connection.ts +35 -0
- package/templates/apps/microservices/jobs/src/app/workers/cleanup.worker.ts +2 -1
- package/templates/apps/microservices/jobs/src/app/workers/email.worker.ts +2 -1
- package/templates/apps/microservices/jobs/src/app/workers/image-process.worker.ts +2 -1
- package/templates/apps/microservices/notes/project.json +2 -1
- package/templates/apps/microservices/notes/src/app/app.module.ts +52 -38
- package/templates/apps/microservices/notes/src/main.ts +6 -23
- package/templates/apps/microservices/payment/project.json +2 -1
- package/templates/apps/microservices/payment/src/app/app.module.ts +37 -12
- package/templates/apps/microservices/payment/src/main.ts +6 -23
- package/templates/apps/microservices/upload/package.json +1 -1
- package/templates/apps/microservices/upload/project.json +2 -1
- package/templates/apps/microservices/upload/src/app/app.module.ts +50 -42
- package/templates/apps/microservices/upload/src/main.ts +6 -23
- package/templates/apps/templates/client-antd/.env.example +7 -0
- package/templates/apps/templates/client-antd/vite.config.mts +4 -4
- package/templates/apps/templates/client-mui/.env.example +7 -0
- package/templates/apps/templates/client-mui/vite.config.mts +4 -4
- package/templates/apps/templates/client-shadcn/.env.example +6 -1
- package/templates/apps/templates/client-shadcn/vite.config.mts +4 -4
- package/templates/libs/auth-client/src/index.ts +1 -0
- package/templates/libs/auth-client/src/lib/auth-client.module.ts +1 -1
- package/templates/libs/auth-client/src/lib/auth-client.service.ts +1 -1
- package/templates/libs/auth-client/src/lib/auth-client.tokens.ts +4 -0
- package/templates/libs/firebase-admin/README.md +11 -0
- package/templates/libs/firebase-admin/eslint.config.mjs +24 -0
- package/templates/libs/firebase-admin/package.json +12 -0
- package/templates/libs/firebase-admin/project.json +19 -0
- package/templates/libs/firebase-admin/src/index.ts +1 -0
- package/templates/libs/firebase-admin/src/lib/__tests__/firebase-admin.unit.test.ts +105 -0
- package/templates/libs/firebase-admin/src/lib/firebase-admin.ts +70 -0
- package/templates/libs/firebase-admin/tsconfig.json +23 -0
- package/templates/libs/firebase-admin/tsconfig.lib.json +23 -0
- package/templates/libs/firebase-admin/tsconfig.spec.json +22 -0
- package/templates/libs/firebase-admin/vitest.config.mts +21 -0
- package/templates/libs/jobs-client/src/index.ts +1 -0
- package/templates/libs/jobs-client/src/lib/jobs-client.module.ts +1 -1
- package/templates/libs/jobs-client/src/lib/jobs-client.service.ts +15 -3
- package/templates/libs/jobs-client/src/lib/jobs-client.tokens.ts +4 -0
- package/templates/libs/notes-client/src/index.ts +1 -0
- package/templates/libs/notes-client/src/lib/notes-client.module.ts +1 -1
- package/templates/libs/notes-client/src/lib/notes-client.service.ts +1 -1
- package/templates/libs/notes-client/src/lib/notes-client.tokens.ts +4 -0
- package/templates/libs/payment-client/src/index.ts +1 -0
- package/templates/libs/payment-client/src/lib/payment-client.module.ts +1 -1
- package/templates/libs/payment-client/src/lib/payment-client.service.ts +1 -1
- package/templates/libs/payment-client/src/lib/payment-client.tokens.ts +4 -0
- package/templates/libs/shared/src/__tests__/bootstrap.unit.test.ts +92 -0
- package/templates/libs/shared/src/__tests__/transport.unit.test.ts +14 -2
- package/templates/libs/shared/src/bootstrap.ts +79 -0
- package/templates/libs/shared/src/env.ts +88 -0
- package/templates/libs/shared/src/index.ts +2 -0
- package/templates/libs/shared/src/transport.ts +62 -3
- package/templates/libs/upload-client/src/index.ts +1 -0
- package/templates/libs/upload-client/src/lib/upload-client.module.ts +1 -1
- package/templates/libs/upload-client/src/lib/upload-client.service.ts +1 -1
- package/templates/libs/upload-client/src/lib/upload-client.tokens.ts +4 -0
- package/templates/libs/vite-plugins/src/index.d.mts +6 -0
- package/templates/libs/vite-plugins/src/index.mjs +50 -0
- package/templates/package.json +1 -0
- package/templates/tools/create-icore/_template-shell/package.json +1 -0
- package/templates/tsconfig.base.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
collectOptions: () => collectOptions,
|
|
34
|
+
pmRun: () => pmRun,
|
|
34
35
|
scaffold: () => scaffold
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -39,6 +40,11 @@ module.exports = __toCommonJS(src_exports);
|
|
|
39
40
|
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
40
41
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
41
42
|
|
|
43
|
+
// src/lib/options.ts
|
|
44
|
+
function pmRun(pm, script) {
|
|
45
|
+
return pm === "npm" ? `npm run ${script}` : `${pm} ${script}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
// src/lib/scaffold.ts
|
|
43
49
|
var import_promises = require("fs/promises");
|
|
44
50
|
var import_node_fs = require("fs");
|
|
@@ -76,9 +82,29 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
76
82
|
pkg["version"] = "0.0.1";
|
|
77
83
|
pkg["private"] = true;
|
|
78
84
|
delete pkg.description;
|
|
85
|
+
if (opts.transport === "nats") {
|
|
86
|
+
const deps = pkg["dependencies"] ??= {};
|
|
87
|
+
deps["nats"] = "^2.29.3";
|
|
88
|
+
}
|
|
79
89
|
if (opts.packageManager !== "yarn") {
|
|
80
90
|
delete pkg.packageManager;
|
|
81
91
|
}
|
|
92
|
+
if (opts.packageManager === "pnpm") {
|
|
93
|
+
pkg["pnpm"] = {
|
|
94
|
+
onlyBuiltDependencies: [
|
|
95
|
+
"@firebase/util",
|
|
96
|
+
"@nestjs/core",
|
|
97
|
+
"@parcel/watcher",
|
|
98
|
+
"@scarf/scarf",
|
|
99
|
+
"@swc/core",
|
|
100
|
+
"less",
|
|
101
|
+
"msgpackr-extract",
|
|
102
|
+
"nx",
|
|
103
|
+
"protobufjs",
|
|
104
|
+
"unrs-resolver"
|
|
105
|
+
]
|
|
106
|
+
};
|
|
107
|
+
}
|
|
82
108
|
await (0, import_promises.writeFile)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
83
109
|
}
|
|
84
110
|
async function writeAuthEnv(targetDir, opts) {
|
|
@@ -116,9 +142,9 @@ async function writeNotesEnv(targetDir, opts) {
|
|
|
116
142
|
async function writeGatewayEnv(targetDir, opts) {
|
|
117
143
|
const envExample = (0, import_node_path.join)(targetDir, "apps/api/.env.example");
|
|
118
144
|
const env = await (0, import_promises.readFile)(envExample, "utf8");
|
|
119
|
-
let next = env.replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
|
|
145
|
+
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}`);
|
|
120
146
|
if (opts.transport !== "tcp") {
|
|
121
|
-
next = next.replace(/^# (AUTH_(?:REDIS|NATS)_URL=)/m, "$1").replace(/^# (UPLOAD_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
147
|
+
next = next.replace(/^# (AUTH_(?:REDIS|NATS)_URL=)/m, "$1").replace(/^# (UPLOAD_(?:REDIS|NATS)_URL=)/m, "$1").replace(/^# (NOTES_(?:REDIS|NATS)_URL=)/m, "$1").replace(/^# (PAYMENT_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
122
148
|
}
|
|
123
149
|
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/api/.env"), next);
|
|
124
150
|
}
|
|
@@ -131,6 +157,25 @@ async function writeRootEnv(targetDir, opts) {
|
|
|
131
157
|
];
|
|
132
158
|
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, ".env"), lines.join("\n"));
|
|
133
159
|
}
|
|
160
|
+
async function stripGatewayTransport(targetDir, prefix) {
|
|
161
|
+
const gatewayEnv = (0, import_node_path.join)(targetDir, "apps/api/.env");
|
|
162
|
+
try {
|
|
163
|
+
const env = await (0, import_promises.readFile)(gatewayEnv, "utf8");
|
|
164
|
+
const next = env.split("\n").filter(
|
|
165
|
+
(line) => !line.startsWith(`${prefix}_`) && !line.startsWith(`# ${prefix}_`) && !line.includes(`${prefix} MS transport`)
|
|
166
|
+
).join("\n");
|
|
167
|
+
await (0, import_promises.writeFile)(gatewayEnv, next);
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function writeClientEnv(targetDir) {
|
|
172
|
+
const envExample = (0, import_node_path.join)(targetDir, "apps/client/.env.example");
|
|
173
|
+
try {
|
|
174
|
+
const env = await (0, import_promises.readFile)(envExample, "utf8");
|
|
175
|
+
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/client/.env"), env);
|
|
176
|
+
} catch {
|
|
177
|
+
}
|
|
178
|
+
}
|
|
134
179
|
async function writePaymentEnv(targetDir, opts) {
|
|
135
180
|
if (opts.payment === "none") return;
|
|
136
181
|
const envExample = (0, import_node_path.join)(targetDir, "apps/microservices/payment/.env.example");
|
|
@@ -207,6 +252,7 @@ async function removePaymentStack(targetDir) {
|
|
|
207
252
|
"@icore/payment-client",
|
|
208
253
|
"@idevconn/payment"
|
|
209
254
|
]);
|
|
255
|
+
await stripGatewayTransport(targetDir, "PAYMENT");
|
|
210
256
|
}
|
|
211
257
|
async function removeNotesStack(targetDir) {
|
|
212
258
|
for (const p2 of [
|
|
@@ -228,6 +274,7 @@ async function removeNotesStack(targetDir) {
|
|
|
228
274
|
} catch {
|
|
229
275
|
}
|
|
230
276
|
await stripDeps((0, import_node_path.join)(targetDir, "apps/api/package.json"), ["@icore/notes-client"]);
|
|
277
|
+
await stripGatewayTransport(targetDir, "NOTES");
|
|
231
278
|
const tsconfigPath = (0, import_node_path.join)(targetDir, "tsconfig.base.json");
|
|
232
279
|
try {
|
|
233
280
|
const src = await (0, import_promises.readFile)(tsconfigPath, "utf8");
|
|
@@ -284,15 +331,17 @@ async function stripTsconfigPath(targetDir, alias) {
|
|
|
284
331
|
}
|
|
285
332
|
async function removeUnusedAuthStrategies(targetDir, authProvider) {
|
|
286
333
|
const modulePath = (0, import_node_path.join)(targetDir, "apps/microservices/auth/src/app/app.module.ts");
|
|
334
|
+
const AUTH_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseAuth\(cfg\);\n\s*return makeFirebaseAuth\(cfg\);/m;
|
|
287
335
|
if (authProvider === "supabase") {
|
|
288
336
|
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
|
|
289
337
|
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/auth/package.json"), [
|
|
290
|
-
"@icore/auth-firebase"
|
|
338
|
+
"@icore/auth-firebase",
|
|
339
|
+
"@icore/firebase-admin"
|
|
291
340
|
]);
|
|
292
341
|
await stripTsconfigPath(targetDir, "@icore/auth-firebase");
|
|
293
342
|
try {
|
|
294
343
|
const src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
295
|
-
const next = src.replace(/^import
|
|
344
|
+
const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/m, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/m, "").replace(/^ {2}firebase: \[[^\]]*\],\n/m, "").replace(/\nfunction makeFirebaseAuth[\s\S]*?\n}\n/m, "").replace(AUTH_BRANCH, "return makeSupabaseAuth(cfg);");
|
|
296
345
|
await (0, import_promises.writeFile)(modulePath, next);
|
|
297
346
|
} catch {
|
|
298
347
|
}
|
|
@@ -305,10 +354,7 @@ async function removeUnusedAuthStrategies(targetDir, authProvider) {
|
|
|
305
354
|
await stripTsconfigPath(targetDir, "@icore/auth-supabase");
|
|
306
355
|
try {
|
|
307
356
|
const src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
308
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/m, "").replace(
|
|
309
|
-
/\n {10}case 'supabase': \{[\s\S]*?return new SupabaseAuthStrategy\(\{ client \}\);\n {10}\}\n/m,
|
|
310
|
-
""
|
|
311
|
-
);
|
|
357
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/m, "").replace(/\nfunction makeSupabaseAuth[\s\S]*?\n}\n/m, "").replace(AUTH_BRANCH, "return makeFirebaseAuth(cfg);");
|
|
312
358
|
await (0, import_promises.writeFile)(modulePath, next);
|
|
313
359
|
} catch {
|
|
314
360
|
}
|
|
@@ -320,7 +366,8 @@ async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
|
|
|
320
366
|
if (uploadProvider !== "firebase") {
|
|
321
367
|
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
|
|
322
368
|
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/upload/package.json"), [
|
|
323
|
-
"@icore/storage-firebase"
|
|
369
|
+
"@icore/storage-firebase",
|
|
370
|
+
"@icore/firebase-admin"
|
|
324
371
|
]);
|
|
325
372
|
await stripTsconfigPath(targetDir, "@icore/storage-firebase");
|
|
326
373
|
}
|
|
@@ -344,26 +391,26 @@ async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
|
|
|
344
391
|
try {
|
|
345
392
|
let src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
346
393
|
if (uploadProvider !== "firebase") {
|
|
347
|
-
src = src.replace(/^import
|
|
394
|
+
src = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/m, "").replace(
|
|
348
395
|
/^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/m,
|
|
349
396
|
""
|
|
350
|
-
).replace(/^
|
|
397
|
+
).replace(/^ {2}firebase: \[[^\]]*\],\n/m, "").replace(/\nfunction makeFirebaseStorage[\s\S]*?\n}\n/m, "");
|
|
351
398
|
}
|
|
352
399
|
if (uploadProvider !== "cloudinary") {
|
|
353
400
|
src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/m, "").replace(
|
|
354
401
|
/^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/m,
|
|
355
402
|
""
|
|
356
|
-
).replace(
|
|
403
|
+
).replace(/\nfunction makeCloudinaryStorage[\s\S]*?\n}\n/m, "");
|
|
357
404
|
}
|
|
358
405
|
if (uploadProvider !== "supabase") {
|
|
359
406
|
src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(
|
|
360
407
|
/^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/m,
|
|
361
408
|
""
|
|
362
|
-
).replace(
|
|
363
|
-
/\n {10}case 'supabase': \{[\s\S]*?bucket: requireEnv\(cfg, 'SUPABASE_STORAGE_BUCKET'\),\n {12}\}\);\n {10}\}\n/m,
|
|
364
|
-
""
|
|
365
|
-
);
|
|
409
|
+
).replace(/\nfunction makeSupabaseStorage[\s\S]*?\n}\n/m, "");
|
|
366
410
|
}
|
|
411
|
+
const STORAGE_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseStorage\(cfg\);\n\s*if \(provider === 'firebase'\) return makeFirebaseStorage\(cfg\);\n\s*return makeCloudinaryStorage\(cfg\);/m;
|
|
412
|
+
const chosenReturn = `return make${uploadProvider.charAt(0).toUpperCase() + uploadProvider.slice(1)}Storage(cfg);`;
|
|
413
|
+
src = src.replace(STORAGE_BRANCH, chosenReturn);
|
|
367
414
|
await (0, import_promises.writeFile)(modulePath, src);
|
|
368
415
|
} catch {
|
|
369
416
|
}
|
|
@@ -373,14 +420,15 @@ async function removeUnusedDbStrategies(targetDir, dbProvider) {
|
|
|
373
420
|
if (dbProvider === "supabase") {
|
|
374
421
|
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
|
|
375
422
|
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/notes/package.json"), [
|
|
376
|
-
"@icore/db-firestore"
|
|
423
|
+
"@icore/db-firestore",
|
|
424
|
+
"@icore/firebase-admin"
|
|
377
425
|
]);
|
|
378
426
|
await stripTsconfigPath(targetDir, "@icore/db-firestore");
|
|
379
427
|
try {
|
|
380
428
|
const src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
381
|
-
const next = src.replace(/^import
|
|
382
|
-
|
|
383
|
-
""
|
|
429
|
+
const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/m, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/m, "").replace(/^ {2}firestore: \[[^\]]*\],\n/m, "").replace(/^ {2}firebase: \[[^\]]*\],\n/m, "").replace(/\nfunction makeFirestoreDB[\s\S]*?\n}\n/m, "").replace(
|
|
430
|
+
/if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*return makeFirestoreDB\(cfg\);/m,
|
|
431
|
+
"return makeSupabaseDB(cfg);"
|
|
384
432
|
);
|
|
385
433
|
await (0, import_promises.writeFile)(modulePath, next);
|
|
386
434
|
} catch {
|
|
@@ -394,15 +442,19 @@ async function removeUnusedDbStrategies(targetDir, dbProvider) {
|
|
|
394
442
|
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
395
443
|
try {
|
|
396
444
|
const src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
397
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(
|
|
398
|
-
|
|
399
|
-
""
|
|
445
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/m, "").replace(
|
|
446
|
+
/if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*return makeFirestoreDB\(cfg\);/m,
|
|
447
|
+
"return makeFirestoreDB(cfg);"
|
|
400
448
|
);
|
|
401
449
|
await (0, import_promises.writeFile)(modulePath, next);
|
|
402
450
|
} catch {
|
|
403
451
|
}
|
|
404
452
|
}
|
|
405
453
|
}
|
|
454
|
+
async function removeFirebaseAdminLib(targetDir) {
|
|
455
|
+
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
|
|
456
|
+
await stripTsconfigPath(targetDir, "@icore/firebase-admin");
|
|
457
|
+
}
|
|
406
458
|
async function removeUploadStack(targetDir) {
|
|
407
459
|
const paths = [
|
|
408
460
|
"apps/microservices/upload",
|
|
@@ -507,6 +559,7 @@ async function scaffold(opts, templatesDir) {
|
|
|
507
559
|
await writeGatewayEnv(opts.targetDir, opts);
|
|
508
560
|
await writeRootEnv(opts.targetDir, opts);
|
|
509
561
|
await selectClientTemplate(opts.targetDir, opts);
|
|
562
|
+
await writeClientEnv(opts.targetDir);
|
|
510
563
|
if (opts.upload === "none") await removeUploadStack(opts.targetDir);
|
|
511
564
|
if (opts.payment === "none") await removePaymentStack(opts.targetDir);
|
|
512
565
|
if (opts.jobs === "none") await removeJobsStack(opts.targetDir);
|
|
@@ -514,12 +567,230 @@ async function scaffold(opts, templatesDir) {
|
|
|
514
567
|
await removeUnusedAuthStrategies(opts.targetDir, opts.authProvider);
|
|
515
568
|
await removeUnusedStorageStrategies(opts.targetDir, opts.upload);
|
|
516
569
|
await removeUnusedDbStrategies(opts.targetDir, opts.dbProvider);
|
|
570
|
+
const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
571
|
+
if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
|
|
517
572
|
if (opts.packageManager === "yarn") {
|
|
518
573
|
await (0, import_promises.writeFile)((0, import_node_path.join)(opts.targetDir, "yarn.lock"), "");
|
|
574
|
+
} else {
|
|
575
|
+
await (0, import_promises.rm)((0, import_node_path.join)(opts.targetDir, ".yarn"), { recursive: true, force: true });
|
|
576
|
+
await (0, import_promises.rm)((0, import_node_path.join)(opts.targetDir, ".yarnrc.yml"), { force: true });
|
|
519
577
|
}
|
|
578
|
+
await patchGitignoreForPm(opts.targetDir, opts.packageManager);
|
|
579
|
+
await writeAiFiles(opts.targetDir, opts);
|
|
520
580
|
if (opts.install) runInstall(opts.targetDir, opts.packageManager);
|
|
521
581
|
if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
|
|
522
582
|
}
|
|
583
|
+
async function patchGitignoreForPm(targetDir, pm) {
|
|
584
|
+
const giPath = (0, import_node_path.join)(targetDir, ".gitignore");
|
|
585
|
+
try {
|
|
586
|
+
let src = await (0, import_promises.readFile)(giPath, "utf8");
|
|
587
|
+
src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
|
|
588
|
+
if (pm !== "yarn") {
|
|
589
|
+
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, "");
|
|
590
|
+
}
|
|
591
|
+
if (pm === "pnpm") {
|
|
592
|
+
if (!src.includes(".pnpm-debug.log")) {
|
|
593
|
+
src += "\n# pnpm\n.pnpm-debug.log*\n";
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (pm === "npm") {
|
|
597
|
+
if (!src.includes("npm-debug.log")) {
|
|
598
|
+
src += "\n# npm\nnpm-debug.log*\n";
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
await (0, import_promises.writeFile)(giPath, src);
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
async function writeAiFiles(targetDir, opts) {
|
|
606
|
+
const pm = opts.packageManager;
|
|
607
|
+
const nx = pm === "npm" ? "npx nx" : `${pm} nx`;
|
|
608
|
+
const devCmd = pmRun(pm, "dev");
|
|
609
|
+
const activeMSes = ["auth (port 4001)"];
|
|
610
|
+
if (opts.upload !== "none") activeMSes.push(`upload (port 4002)`);
|
|
611
|
+
if (opts.payment !== "none") activeMSes.push(`payment (port 4003)`);
|
|
612
|
+
if (opts.example !== "none") activeMSes.push(`notes (port 4004)`);
|
|
613
|
+
if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
|
|
614
|
+
const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
|
|
615
|
+
const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
616
|
+
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
|
|
617
|
+
const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
|
|
618
|
+
const readme = `# ${opts.projectName}
|
|
619
|
+
|
|
620
|
+
> Scaffolded with [iCore](https://github.com/iDEVconn/create-icore) \u2014 Nx + NestJS + React full-stack template.
|
|
621
|
+
|
|
622
|
+
## Stack
|
|
623
|
+
|
|
624
|
+
| Layer | Technology |
|
|
625
|
+
|-------|-----------|
|
|
626
|
+
| Monorepo | Nx + ${pm} |
|
|
627
|
+
| Gateway | NestJS 11 + Swagger |
|
|
628
|
+
| Auth | ${opts.authProvider} |
|
|
629
|
+
| Database | ${opts.dbProvider} |
|
|
630
|
+
| Upload | ${opts.upload === "none" ? "\u2014" : opts.upload} |
|
|
631
|
+
| UI | ${uiLabel} + TanStack Router + Query |
|
|
632
|
+
| i18n | i18next (en / ru / he) |
|
|
633
|
+
|
|
634
|
+
## Quick start
|
|
635
|
+
|
|
636
|
+
\`\`\`bash
|
|
637
|
+
# 1. Fill in provider credentials
|
|
638
|
+
# apps/microservices/auth/.env
|
|
639
|
+
# apps/microservices/upload/.env (if upload is enabled)
|
|
640
|
+
# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
|
|
641
|
+
|
|
642
|
+
# 2. Start everything
|
|
643
|
+
${devCmd}
|
|
644
|
+
# \u2192 http://localhost:4200 client
|
|
645
|
+
# \u2192 http://localhost:3001/api/docs Swagger
|
|
646
|
+
\`\`\`
|
|
647
|
+
|
|
648
|
+
## Commands
|
|
649
|
+
|
|
650
|
+
\`\`\`bash
|
|
651
|
+
${nx} run <project>:serve # start a single service
|
|
652
|
+
${nx} test <project> # unit tests
|
|
653
|
+
${nx} lint <project> # lint
|
|
654
|
+
${nx} build <project> # production build
|
|
655
|
+
${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "npm run remove-notes"} # strip the notes sample feature
|
|
656
|
+
\`\`\`
|
|
657
|
+
|
|
658
|
+
## Scaffolded by
|
|
659
|
+
|
|
660
|
+
[iCore](https://github.com/iDEVconn/create-icore) \u2014 [@idevconn/create-icore](https://www.npmjs.com/package/@idevconn/create-icore)
|
|
661
|
+
|
|
662
|
+
## License
|
|
663
|
+
|
|
664
|
+
Apache-2.0
|
|
665
|
+
`;
|
|
666
|
+
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "README.md"), readme);
|
|
667
|
+
const agents = `# ${opts.projectName} \u2014 Agent Instructions
|
|
668
|
+
|
|
669
|
+
## Stack snapshot
|
|
670
|
+
|
|
671
|
+
| Dimension | Choice |
|
|
672
|
+
|------------|--------|
|
|
673
|
+
| Auth | ${opts.authProvider} |
|
|
674
|
+
| Database | ${opts.dbProvider} |
|
|
675
|
+
| Upload | ${opts.upload} |
|
|
676
|
+
| Payment | ${opts.payment} |
|
|
677
|
+
| Jobs | ${opts.jobs} |
|
|
678
|
+
| UI | ${opts.ui} |
|
|
679
|
+
| Transport | ${opts.transport} |
|
|
680
|
+
| PM | ${pm} |
|
|
681
|
+
|
|
682
|
+
## \u{1F680} Mandatory Workflow
|
|
683
|
+
|
|
684
|
+
- **Branch strategy**: \`dev\` is default. Cut \`feature/<name>\` or \`bug/<name>\` from dev. PRs only target dev. Never push directly to main.
|
|
685
|
+
- **No code without approval**: Propose changes first, wait for go-ahead.
|
|
686
|
+
- **\u0417\u0410\u041A\u041E\u041D \u2014 no crash on missing .env**: MS factories must catch config errors, print a boxed banner with ALL missing vars, and return a Fake strategy in dev. In prod (\`NODE_ENV=production\`) throw the same banner. The \`formatEnvBanner\` + \`missingEnv\` helpers from \`@icore/shared\` handle this.
|
|
687
|
+
- **Post-coding routine**: \`npx prettier --write <files>\` \u2192 \`${nx} lint <project>\` \u2192 \`${nx} build <project>\` \u2014 all green before committing.
|
|
688
|
+
- **Nx generators only**: never hand-write \`project.json\` / tsconfig stacks. Use \`${nx} g @nx/<plugin>:<schematic>\`.
|
|
689
|
+
|
|
690
|
+
## Architecture
|
|
691
|
+
|
|
692
|
+
\`\`\`
|
|
693
|
+
apps/
|
|
694
|
+
\u251C\u2500\u2500 api/ NestJS gateway \u2014 all client traffic enters here (:3001)
|
|
695
|
+
\u251C\u2500\u2500 microservices/
|
|
696
|
+
${activeMSes.map((s) => `\u2502 \u251C\u2500\u2500 ${s.split(" ")[0]}/`).join("\n")}
|
|
697
|
+
\u2514\u2500\u2500 client/ Vite + React 19 + ${opts.ui} (:4200)
|
|
698
|
+
libs/
|
|
699
|
+
\u251C\u2500\u2500 shared/ contracts, CASL, transport helpers, env banner utils
|
|
700
|
+
\u251C\u2500\u2500 auth-strategies/${opts.authProvider}/
|
|
701
|
+
${opts.upload !== "none" ? `\u251C\u2500\u2500 storage-strategies/${opts.upload}/
|
|
702
|
+
` : ""}\u251C\u2500\u2500 db-strategies/${opts.dbProvider === "firebase" ? "firestore" : opts.dbProvider}/
|
|
703
|
+
\u251C\u2500\u2500 auth-client/ gateway \u2192 auth MS (TCP/Redis/NATS)
|
|
704
|
+
${opts.upload !== "none" ? `\u251C\u2500\u2500 upload-client/ gateway \u2192 upload MS
|
|
705
|
+
` : ""}\u2514\u2500\u2500 template-shared/ browser-safe React foundation (stores, i18n, CASL)
|
|
706
|
+
\`\`\`
|
|
707
|
+
|
|
708
|
+
## Key patterns
|
|
709
|
+
|
|
710
|
+
**Strategy swap** \u2014 provider is chosen at runtime via env. Never import a concrete strategy in app code; always inject via the factory token (\`AuthStrategy\`, \`StorageStrategy\`, \`DBStrategy\`).
|
|
711
|
+
|
|
712
|
+
**Transport** \u2014 \`buildTransport(prefix)\` reads \`${opts.transport.toUpperCase()}*\` vars. Same helper on gateway client-modules and each MS \`main.ts\`. Supports tcp / redis / nats \u2014 change by flipping \`*_TRANSPORT\` in \`.env\`.
|
|
713
|
+
|
|
714
|
+
**Env layering**:
|
|
715
|
+
1. Root \`.env\` \u2014 \`DB_PROVIDER\`
|
|
716
|
+
2. \`apps/api/.env\` \u2014 gateway transport endpoints
|
|
717
|
+
3. \`apps/microservices/<name>/.env\` \u2014 each MS provider + transport
|
|
718
|
+
4. \`apps/client/.env\` \u2014 \`VITE_API_URL\`
|
|
719
|
+
|
|
720
|
+
## Commands
|
|
721
|
+
|
|
722
|
+
\`\`\`bash
|
|
723
|
+
${devCmd} # start all services
|
|
724
|
+
${nx} run api:serve # gateway only
|
|
725
|
+
${nx} run auth:serve # auth MS only
|
|
726
|
+
${nx} test <project> # run tests
|
|
727
|
+
${nx} lint <project> # lint
|
|
728
|
+
${nx} build <project> # build
|
|
729
|
+
${nx} g @nx/nest:resource # generate NestJS resource
|
|
730
|
+
\`\`\`
|
|
731
|
+
|
|
732
|
+
## .env files to configure
|
|
733
|
+
|
|
734
|
+
| File | Key vars |
|
|
735
|
+
|------|----------|
|
|
736
|
+
| \`apps/microservices/auth/.env\` | \`AUTH_PROVIDER=${opts.authProvider}\`, ${opts.authProvider === "supabase" ? "`SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`" : "`FB_ADMIN_*`, `FIREBASE_WEB_API_KEY`"} |
|
|
737
|
+
${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
|
|
738
|
+
` : ""}| \`apps/microservices/notes/.env\` | \`DB_PROVIDER=${opts.dbProvider}\`, DB creds |
|
|
739
|
+
| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
|
|
740
|
+
|
|
741
|
+
## Testing
|
|
742
|
+
|
|
743
|
+
- Unit tests: Vitest, files named \`*.unit.test.ts(x)\` in \`__tests__/\` next to source.
|
|
744
|
+
- Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
|
|
745
|
+
- Run: \`${nx} test <project>\`
|
|
746
|
+
`;
|
|
747
|
+
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "AGENTS.md"), agents);
|
|
748
|
+
await (0, import_promises.mkdir)((0, import_node_path.join)(targetDir, ".claude"), { recursive: true });
|
|
749
|
+
const mcpServers = {
|
|
750
|
+
nx: {
|
|
751
|
+
command: "npx",
|
|
752
|
+
args: ["-y", "@nx/mcp@latest", "--directory", "."],
|
|
753
|
+
type: "stdio"
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
if (usesSupabase) {
|
|
757
|
+
mcpServers["supabase"] = {
|
|
758
|
+
command: "npx",
|
|
759
|
+
args: [
|
|
760
|
+
"-y",
|
|
761
|
+
"@supabase/mcp-server-supabase@latest",
|
|
762
|
+
"--access-token",
|
|
763
|
+
"<SUPABASE_PERSONAL_ACCESS_TOKEN>"
|
|
764
|
+
],
|
|
765
|
+
type: "stdio"
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
if (usesFirebase) {
|
|
769
|
+
mcpServers["firebase"] = {
|
|
770
|
+
command: "npx",
|
|
771
|
+
args: ["-y", "firebase-tools@latest", "experimental:mcp"],
|
|
772
|
+
type: "stdio"
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
const nxCmds = [`Bash(${nx} *)`, `Bash(${devCmd})`];
|
|
776
|
+
if (pm !== "npm") nxCmds.push(`Bash(npx nx *)`);
|
|
777
|
+
const settings = {
|
|
778
|
+
mcpServers,
|
|
779
|
+
permissions: {
|
|
780
|
+
allow: [
|
|
781
|
+
...nxCmds,
|
|
782
|
+
"Bash(npx prettier *)",
|
|
783
|
+
"Bash(git status)",
|
|
784
|
+
"Bash(git diff *)",
|
|
785
|
+
"Bash(git log *)"
|
|
786
|
+
]
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
await (0, import_promises.writeFile)(
|
|
790
|
+
(0, import_node_path.join)(targetDir, ".claude", "settings.json"),
|
|
791
|
+
JSON.stringify(settings, null, 2) + "\n"
|
|
792
|
+
);
|
|
793
|
+
}
|
|
523
794
|
|
|
524
795
|
// src/lib/prompts.ts
|
|
525
796
|
var p = __toESM(require("@clack/prompts"), 1);
|
|
@@ -715,6 +986,12 @@ Re-run with @latest to refresh:
|
|
|
715
986
|
});
|
|
716
987
|
if (p.isCancel(transport)) throw new Error("cancelled");
|
|
717
988
|
const packageManager = flags.packageManager ?? detectPackageManager();
|
|
989
|
+
if (packageManager === "yarn") {
|
|
990
|
+
p.note(
|
|
991
|
+
"yarn 4.15+ enforces a 24h publish-age gate (npmMinimalAgeGate=1d), so a\n`yarn create @idevconn/icore@latest` run within 24h of a release resolves an\nolder version. If the banner above shows an unexpectedly old version, either:\n \u2022 wait \u2014 the version auto-unlocks 24h after publish, or\n \u2022 bypass once: yarn config set npmMinimalAgeGate 0 (then re-run), or\n \u2022 use npm/pnpm: npm init @idevconn/icore@latest <name> -- [flags]",
|
|
992
|
+
"\u26A0 yarn 24h age-gate"
|
|
993
|
+
);
|
|
994
|
+
}
|
|
718
995
|
const initGit = flags.initGit ?? !await p.confirm({ message: "Initialise git repo?", initialValue: true }) === false;
|
|
719
996
|
const install = flags.install ?? !await p.confirm({
|
|
720
997
|
message: `Run ${packageManager} install?`,
|
|
@@ -739,5 +1016,6 @@ Re-run with @latest to refresh:
|
|
|
739
1016
|
// Annotate the CommonJS export names for ESM import in node:
|
|
740
1017
|
0 && (module.exports = {
|
|
741
1018
|
collectOptions,
|
|
1019
|
+
pmRun,
|
|
742
1020
|
scaffold
|
|
743
1021
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -7,6 +7,12 @@ type ExampleMode = 'notes' | 'none';
|
|
|
7
7
|
type UiLibrary = 'shadcn' | 'antd' | 'mui';
|
|
8
8
|
type MsTransport = 'tcp' | 'redis' | 'nats';
|
|
9
9
|
type PackageManager = 'yarn' | 'npm' | 'pnpm';
|
|
10
|
+
/**
|
|
11
|
+
* Returns the correct invocation for a package.json script.
|
|
12
|
+
* yarn/pnpm: `yarn <script>` / `pnpm <script>`
|
|
13
|
+
* npm: `npm run <script>` (npm requires the `run` keyword for custom scripts)
|
|
14
|
+
*/
|
|
15
|
+
declare function pmRun(pm: PackageManager, script: string): string;
|
|
10
16
|
interface CreateIcoreOptions {
|
|
11
17
|
projectName: string;
|
|
12
18
|
targetDir: string;
|
|
@@ -31,4 +37,4 @@ interface PromptInput {
|
|
|
31
37
|
}
|
|
32
38
|
declare function collectOptions({ argv, cwd }: PromptInput): Promise<CreateIcoreOptions>;
|
|
33
39
|
|
|
34
|
-
export { type AuthProvider, type CreateIcoreOptions, type DbProvider, type ExampleMode, type JobsProvider, type MsTransport, type PackageManager, type PaymentProvider, type UiLibrary, type UploadProvider, collectOptions, scaffold };
|
|
40
|
+
export { type AuthProvider, type CreateIcoreOptions, type DbProvider, type ExampleMode, type JobsProvider, type MsTransport, type PackageManager, type PaymentProvider, type UiLibrary, type UploadProvider, collectOptions, pmRun, scaffold };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ type ExampleMode = 'notes' | 'none';
|
|
|
7
7
|
type UiLibrary = 'shadcn' | 'antd' | 'mui';
|
|
8
8
|
type MsTransport = 'tcp' | 'redis' | 'nats';
|
|
9
9
|
type PackageManager = 'yarn' | 'npm' | 'pnpm';
|
|
10
|
+
/**
|
|
11
|
+
* Returns the correct invocation for a package.json script.
|
|
12
|
+
* yarn/pnpm: `yarn <script>` / `pnpm <script>`
|
|
13
|
+
* npm: `npm run <script>` (npm requires the `run` keyword for custom scripts)
|
|
14
|
+
*/
|
|
15
|
+
declare function pmRun(pm: PackageManager, script: string): string;
|
|
10
16
|
interface CreateIcoreOptions {
|
|
11
17
|
projectName: string;
|
|
12
18
|
targetDir: string;
|
|
@@ -31,4 +37,4 @@ interface PromptInput {
|
|
|
31
37
|
}
|
|
32
38
|
declare function collectOptions({ argv, cwd }: PromptInput): Promise<CreateIcoreOptions>;
|
|
33
39
|
|
|
34
|
-
export { type AuthProvider, type CreateIcoreOptions, type DbProvider, type ExampleMode, type JobsProvider, type MsTransport, type PackageManager, type PaymentProvider, type UiLibrary, type UploadProvider, collectOptions, scaffold };
|
|
40
|
+
export { type AuthProvider, type CreateIcoreOptions, type DbProvider, type ExampleMode, type JobsProvider, type MsTransport, type PackageManager, type PaymentProvider, type UiLibrary, type UploadProvider, collectOptions, pmRun, scaffold };
|