@idevconn/create-icore 0.5.0 → 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.
Files changed (39) hide show
  1. package/dist/cli.js +84 -25
  2. package/dist/index.cjs +81 -24
  3. package/dist/index.d.cts +7 -1
  4. package/dist/index.d.ts +7 -1
  5. package/dist/index.js +80 -24
  6. package/package.json +1 -1
  7. package/templates/apps/api/.env.example +14 -0
  8. package/templates/apps/microservices/auth/package.json +1 -1
  9. package/templates/apps/microservices/auth/src/app/app.module.ts +17 -30
  10. package/templates/apps/microservices/auth/src/main.ts +6 -23
  11. package/templates/apps/microservices/jobs/src/app/redis-connection.ts +35 -0
  12. package/templates/apps/microservices/jobs/src/app/workers/cleanup.worker.ts +2 -1
  13. package/templates/apps/microservices/jobs/src/app/workers/email.worker.ts +2 -1
  14. package/templates/apps/microservices/jobs/src/app/workers/image-process.worker.ts +2 -1
  15. package/templates/apps/microservices/notes/src/app/app.module.ts +22 -27
  16. package/templates/apps/microservices/notes/src/main.ts +6 -23
  17. package/templates/apps/microservices/payment/src/app/app.module.ts +6 -4
  18. package/templates/apps/microservices/payment/src/main.ts +6 -23
  19. package/templates/apps/microservices/upload/package.json +1 -1
  20. package/templates/apps/microservices/upload/src/app/app.module.ts +18 -30
  21. package/templates/apps/microservices/upload/src/main.ts +6 -23
  22. package/templates/libs/firebase-admin/README.md +11 -0
  23. package/templates/libs/firebase-admin/eslint.config.mjs +24 -0
  24. package/templates/libs/firebase-admin/package.json +12 -0
  25. package/templates/libs/firebase-admin/project.json +19 -0
  26. package/templates/libs/firebase-admin/src/index.ts +1 -0
  27. package/templates/libs/firebase-admin/src/lib/__tests__/firebase-admin.unit.test.ts +105 -0
  28. package/templates/libs/firebase-admin/src/lib/firebase-admin.ts +70 -0
  29. package/templates/libs/firebase-admin/tsconfig.json +23 -0
  30. package/templates/libs/firebase-admin/tsconfig.lib.json +23 -0
  31. package/templates/libs/firebase-admin/tsconfig.spec.json +22 -0
  32. package/templates/libs/firebase-admin/vitest.config.mts +21 -0
  33. package/templates/libs/jobs-client/src/lib/jobs-client.service.ts +14 -2
  34. package/templates/libs/shared/src/__tests__/bootstrap.unit.test.ts +92 -0
  35. package/templates/libs/shared/src/__tests__/transport.unit.test.ts +14 -2
  36. package/templates/libs/shared/src/bootstrap.ts +79 -0
  37. package/templates/libs/shared/src/index.ts +1 -0
  38. package/templates/libs/shared/src/transport.ts +25 -3
  39. package/templates/tsconfig.base.json +2 -1
package/dist/cli.js CHANGED
@@ -234,6 +234,13 @@ import { copyFile, mkdir, readdir, readFile as readFile2, stat, writeFile, rm }
234
234
  import { readFileSync } from "fs";
235
235
  import { join as join2 } from "path";
236
236
  import { spawnSync } from "child_process";
237
+
238
+ // src/lib/options.ts
239
+ function pmRun(pm, script) {
240
+ return pm === "npm" ? `npm run ${script}` : `${pm} ${script}`;
241
+ }
242
+
243
+ // src/lib/scaffold.ts
237
244
  var IGNORE_TOP = /* @__PURE__ */ new Set([
238
245
  ".git",
239
246
  "node_modules",
@@ -266,6 +273,10 @@ async function rewriteRootPackageJson(targetDir, opts) {
266
273
  pkg["version"] = "0.0.1";
267
274
  pkg["private"] = true;
268
275
  delete pkg.description;
276
+ if (opts.transport === "nats") {
277
+ const deps = pkg["dependencies"] ??= {};
278
+ deps["nats"] = "^2.29.3";
279
+ }
269
280
  if (opts.packageManager !== "yarn") {
270
281
  delete pkg.packageManager;
271
282
  }
@@ -322,9 +333,9 @@ async function writeNotesEnv(targetDir, opts) {
322
333
  async function writeGatewayEnv(targetDir, opts) {
323
334
  const envExample = join2(targetDir, "apps/api/.env.example");
324
335
  const env = await readFile2(envExample, "utf8");
325
- let next = env.replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
336
+ 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}`);
326
337
  if (opts.transport !== "tcp") {
327
- next = next.replace(/^# (AUTH_(?:REDIS|NATS)_URL=)/m, "$1").replace(/^# (UPLOAD_(?:REDIS|NATS)_URL=)/m, "$1");
338
+ 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");
328
339
  }
329
340
  await writeFile(join2(targetDir, "apps/api/.env"), next);
330
341
  }
@@ -337,6 +348,17 @@ async function writeRootEnv(targetDir, opts) {
337
348
  ];
338
349
  await writeFile(join2(targetDir, ".env"), lines.join("\n"));
339
350
  }
351
+ async function stripGatewayTransport(targetDir, prefix) {
352
+ const gatewayEnv = join2(targetDir, "apps/api/.env");
353
+ try {
354
+ const env = await readFile2(gatewayEnv, "utf8");
355
+ const next = env.split("\n").filter(
356
+ (line) => !line.startsWith(`${prefix}_`) && !line.startsWith(`# ${prefix}_`) && !line.includes(`${prefix} MS transport`)
357
+ ).join("\n");
358
+ await writeFile(gatewayEnv, next);
359
+ } catch {
360
+ }
361
+ }
340
362
  async function writeClientEnv(targetDir) {
341
363
  const envExample = join2(targetDir, "apps/client/.env.example");
342
364
  try {
@@ -421,6 +443,7 @@ async function removePaymentStack(targetDir) {
421
443
  "@icore/payment-client",
422
444
  "@idevconn/payment"
423
445
  ]);
446
+ await stripGatewayTransport(targetDir, "PAYMENT");
424
447
  }
425
448
  async function removeNotesStack(targetDir) {
426
449
  for (const p3 of [
@@ -442,6 +465,7 @@ async function removeNotesStack(targetDir) {
442
465
  } catch {
443
466
  }
444
467
  await stripDeps(join2(targetDir, "apps/api/package.json"), ["@icore/notes-client"]);
468
+ await stripGatewayTransport(targetDir, "NOTES");
445
469
  const tsconfigPath = join2(targetDir, "tsconfig.base.json");
446
470
  try {
447
471
  const src = await readFile2(tsconfigPath, "utf8");
@@ -498,15 +522,17 @@ async function stripTsconfigPath(targetDir, alias) {
498
522
  }
499
523
  async function removeUnusedAuthStrategies(targetDir, authProvider) {
500
524
  const modulePath = join2(targetDir, "apps/microservices/auth/src/app/app.module.ts");
525
+ const AUTH_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseAuth\(cfg\);\n\s*return makeFirebaseAuth\(cfg\);/m;
501
526
  if (authProvider === "supabase") {
502
527
  await rm(join2(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
503
528
  await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
504
- "@icore/auth-firebase"
529
+ "@icore/auth-firebase",
530
+ "@icore/firebase-admin"
505
531
  ]);
506
532
  await stripTsconfigPath(targetDir, "@icore/auth-firebase");
507
533
  try {
508
534
  const src = await readFile2(modulePath, "utf8");
509
- const next = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/m, "").replace(/^function makeFirebaseStrategy\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'firebase':\n *return makeFirebaseStrategy\(cfg\);\n/, "");
535
+ 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);");
510
536
  await writeFile(modulePath, next);
511
537
  } catch {
512
538
  }
@@ -519,10 +545,7 @@ async function removeUnusedAuthStrategies(targetDir, authProvider) {
519
545
  await stripTsconfigPath(targetDir, "@icore/auth-supabase");
520
546
  try {
521
547
  const src = await readFile2(modulePath, "utf8");
522
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/m, "").replace(
523
- /\n {10}case 'supabase': \{[\s\S]*?return new SupabaseAuthStrategy\(\{ client \}\);\n {10}\}\n/m,
524
- ""
525
- );
548
+ 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);");
526
549
  await writeFile(modulePath, next);
527
550
  } catch {
528
551
  }
@@ -534,7 +557,8 @@ async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
534
557
  if (uploadProvider !== "firebase") {
535
558
  await rm(join2(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
536
559
  await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
537
- "@icore/storage-firebase"
560
+ "@icore/storage-firebase",
561
+ "@icore/firebase-admin"
538
562
  ]);
539
563
  await stripTsconfigPath(targetDir, "@icore/storage-firebase");
540
564
  }
@@ -558,26 +582,26 @@ async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
558
582
  try {
559
583
  let src = await readFile2(modulePath, "utf8");
560
584
  if (uploadProvider !== "firebase") {
561
- src = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(
585
+ src = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/m, "").replace(
562
586
  /^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/m,
563
587
  ""
564
- ).replace(/^function makeFirebaseStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'firebase':\n *return makeFirebaseStorage\(cfg\);\n/, "");
588
+ ).replace(/^ {2}firebase: \[[^\]]*\],\n/m, "").replace(/\nfunction makeFirebaseStorage[\s\S]*?\n}\n/m, "");
565
589
  }
566
590
  if (uploadProvider !== "cloudinary") {
567
591
  src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/m, "").replace(
568
592
  /^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/m,
569
593
  ""
570
- ).replace(/^function makeCloudinaryStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'cloudinary':\n *return makeCloudinaryStorage\(cfg\);\n/, "");
594
+ ).replace(/\nfunction makeCloudinaryStorage[\s\S]*?\n}\n/m, "");
571
595
  }
572
596
  if (uploadProvider !== "supabase") {
573
597
  src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(
574
598
  /^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/m,
575
599
  ""
576
- ).replace(
577
- /\n {10}case 'supabase': \{[\s\S]*?bucket: requireEnv\(cfg, 'SUPABASE_STORAGE_BUCKET'\),\n {12}\}\);\n {10}\}\n/m,
578
- ""
579
- );
600
+ ).replace(/\nfunction makeSupabaseStorage[\s\S]*?\n}\n/m, "");
580
601
  }
602
+ const STORAGE_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseStorage\(cfg\);\n\s*if \(provider === 'firebase'\) return makeFirebaseStorage\(cfg\);\n\s*return makeCloudinaryStorage\(cfg\);/m;
603
+ const chosenReturn = `return make${uploadProvider.charAt(0).toUpperCase() + uploadProvider.slice(1)}Storage(cfg);`;
604
+ src = src.replace(STORAGE_BRANCH, chosenReturn);
581
605
  await writeFile(modulePath, src);
582
606
  } catch {
583
607
  }
@@ -587,14 +611,15 @@ async function removeUnusedDbStrategies(targetDir, dbProvider) {
587
611
  if (dbProvider === "supabase") {
588
612
  await rm(join2(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
589
613
  await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
590
- "@icore/db-firestore"
614
+ "@icore/db-firestore",
615
+ "@icore/firebase-admin"
591
616
  ]);
592
617
  await stripTsconfigPath(targetDir, "@icore/db-firestore");
593
618
  try {
594
619
  const src = await readFile2(modulePath, "utf8");
595
- const next = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/m, "").replace(
596
- /\n {8}if \(provider === 'firestore'[\s\S]*?return new FirestoreDBStrategy\(\{[\s\S]*?\}\);\n {8}\}\n/m,
597
- ""
620
+ 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(
621
+ /if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*return makeFirestoreDB\(cfg\);/m,
622
+ "return makeSupabaseDB(cfg);"
598
623
  );
599
624
  await writeFile(modulePath, next);
600
625
  } catch {
@@ -608,15 +633,19 @@ async function removeUnusedDbStrategies(targetDir, dbProvider) {
608
633
  await stripTsconfigPath(targetDir, "@icore/db-supabase");
609
634
  try {
610
635
  const src = await readFile2(modulePath, "utf8");
611
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(
612
- /\n {8}if \(provider === 'supabase'\) \{[\s\S]*?return new SupabaseDBStrategy\(\{ client \}\);\n {8}\}\n/m,
613
- ""
636
+ 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(
637
+ /if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*return makeFirestoreDB\(cfg\);/m,
638
+ "return makeFirestoreDB(cfg);"
614
639
  );
615
640
  await writeFile(modulePath, next);
616
641
  } catch {
617
642
  }
618
643
  }
619
644
  }
645
+ async function removeFirebaseAdminLib(targetDir) {
646
+ await rm(join2(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
647
+ await stripTsconfigPath(targetDir, "@icore/firebase-admin");
648
+ }
620
649
  async function removeUploadStack(targetDir) {
621
650
  const paths = [
622
651
  "apps/microservices/upload",
@@ -729,17 +758,45 @@ async function scaffold(opts, templatesDir2) {
729
758
  await removeUnusedAuthStrategies(opts.targetDir, opts.authProvider);
730
759
  await removeUnusedStorageStrategies(opts.targetDir, opts.upload);
731
760
  await removeUnusedDbStrategies(opts.targetDir, opts.dbProvider);
761
+ const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
762
+ if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
732
763
  if (opts.packageManager === "yarn") {
733
764
  await writeFile(join2(opts.targetDir, "yarn.lock"), "");
765
+ } else {
766
+ await rm(join2(opts.targetDir, ".yarn"), { recursive: true, force: true });
767
+ await rm(join2(opts.targetDir, ".yarnrc.yml"), { force: true });
734
768
  }
769
+ await patchGitignoreForPm(opts.targetDir, opts.packageManager);
735
770
  await writeAiFiles(opts.targetDir, opts);
736
771
  if (opts.install) runInstall(opts.targetDir, opts.packageManager);
737
772
  if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
738
773
  }
774
+ async function patchGitignoreForPm(targetDir, pm) {
775
+ const giPath = join2(targetDir, ".gitignore");
776
+ try {
777
+ let src = await readFile2(giPath, "utf8");
778
+ src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
779
+ if (pm !== "yarn") {
780
+ 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, "");
781
+ }
782
+ if (pm === "pnpm") {
783
+ if (!src.includes(".pnpm-debug.log")) {
784
+ src += "\n# pnpm\n.pnpm-debug.log*\n";
785
+ }
786
+ }
787
+ if (pm === "npm") {
788
+ if (!src.includes("npm-debug.log")) {
789
+ src += "\n# npm\nnpm-debug.log*\n";
790
+ }
791
+ }
792
+ await writeFile(giPath, src);
793
+ } catch {
794
+ }
795
+ }
739
796
  async function writeAiFiles(targetDir, opts) {
740
797
  const pm = opts.packageManager;
741
798
  const nx = pm === "npm" ? "npx nx" : `${pm} nx`;
742
- const devCmd = `${pm} dev`;
799
+ const devCmd = pmRun(pm, "dev");
743
800
  const activeMSes = ["auth (port 4001)"];
744
801
  if (opts.upload !== "none") activeMSes.push(`upload (port 4002)`);
745
802
  if (opts.payment !== "none") activeMSes.push(`payment (port 4003)`);
@@ -956,7 +1013,9 @@ async function main() {
956
1013
  p2.log.info(`Next:`);
957
1014
  p2.log.info(` cd ${opts.projectName}`);
958
1015
  if (!opts.install) p2.log.info(` ${opts.packageManager} install`);
959
- p2.log.info(` ${opts.packageManager} dev # gateway + auth MS + upload MS + client`);
1016
+ p2.log.info(
1017
+ ` ${pmRun(opts.packageManager, "dev")} # gateway + auth MS + upload MS + client`
1018
+ );
960
1019
  p2.log.info(` open http://localhost:4200`);
961
1020
  p2.log.info(` edit apps/microservices/auth/.env to plug in real ${opts.authProvider} creds`);
962
1021
  }
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,6 +82,10 @@ 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
  }
@@ -132,9 +142,9 @@ async function writeNotesEnv(targetDir, opts) {
132
142
  async function writeGatewayEnv(targetDir, opts) {
133
143
  const envExample = (0, import_node_path.join)(targetDir, "apps/api/.env.example");
134
144
  const env = await (0, import_promises.readFile)(envExample, "utf8");
135
- 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}`);
136
146
  if (opts.transport !== "tcp") {
137
- 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");
138
148
  }
139
149
  await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/api/.env"), next);
140
150
  }
@@ -147,6 +157,17 @@ async function writeRootEnv(targetDir, opts) {
147
157
  ];
148
158
  await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, ".env"), lines.join("\n"));
149
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
+ }
150
171
  async function writeClientEnv(targetDir) {
151
172
  const envExample = (0, import_node_path.join)(targetDir, "apps/client/.env.example");
152
173
  try {
@@ -231,6 +252,7 @@ async function removePaymentStack(targetDir) {
231
252
  "@icore/payment-client",
232
253
  "@idevconn/payment"
233
254
  ]);
255
+ await stripGatewayTransport(targetDir, "PAYMENT");
234
256
  }
235
257
  async function removeNotesStack(targetDir) {
236
258
  for (const p2 of [
@@ -252,6 +274,7 @@ async function removeNotesStack(targetDir) {
252
274
  } catch {
253
275
  }
254
276
  await stripDeps((0, import_node_path.join)(targetDir, "apps/api/package.json"), ["@icore/notes-client"]);
277
+ await stripGatewayTransport(targetDir, "NOTES");
255
278
  const tsconfigPath = (0, import_node_path.join)(targetDir, "tsconfig.base.json");
256
279
  try {
257
280
  const src = await (0, import_promises.readFile)(tsconfigPath, "utf8");
@@ -308,15 +331,17 @@ async function stripTsconfigPath(targetDir, alias) {
308
331
  }
309
332
  async function removeUnusedAuthStrategies(targetDir, authProvider) {
310
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;
311
335
  if (authProvider === "supabase") {
312
336
  await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
313
337
  await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/auth/package.json"), [
314
- "@icore/auth-firebase"
338
+ "@icore/auth-firebase",
339
+ "@icore/firebase-admin"
315
340
  ]);
316
341
  await stripTsconfigPath(targetDir, "@icore/auth-firebase");
317
342
  try {
318
343
  const src = await (0, import_promises.readFile)(modulePath, "utf8");
319
- const next = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/m, "").replace(/^function makeFirebaseStrategy\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'firebase':\n *return makeFirebaseStrategy\(cfg\);\n/, "");
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);");
320
345
  await (0, import_promises.writeFile)(modulePath, next);
321
346
  } catch {
322
347
  }
@@ -329,10 +354,7 @@ async function removeUnusedAuthStrategies(targetDir, authProvider) {
329
354
  await stripTsconfigPath(targetDir, "@icore/auth-supabase");
330
355
  try {
331
356
  const src = await (0, import_promises.readFile)(modulePath, "utf8");
332
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/m, "").replace(
333
- /\n {10}case 'supabase': \{[\s\S]*?return new SupabaseAuthStrategy\(\{ client \}\);\n {10}\}\n/m,
334
- ""
335
- );
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);");
336
358
  await (0, import_promises.writeFile)(modulePath, next);
337
359
  } catch {
338
360
  }
@@ -344,7 +366,8 @@ async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
344
366
  if (uploadProvider !== "firebase") {
345
367
  await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
346
368
  await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/upload/package.json"), [
347
- "@icore/storage-firebase"
369
+ "@icore/storage-firebase",
370
+ "@icore/firebase-admin"
348
371
  ]);
349
372
  await stripTsconfigPath(targetDir, "@icore/storage-firebase");
350
373
  }
@@ -368,26 +391,26 @@ async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
368
391
  try {
369
392
  let src = await (0, import_promises.readFile)(modulePath, "utf8");
370
393
  if (uploadProvider !== "firebase") {
371
- src = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(
394
+ src = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/m, "").replace(
372
395
  /^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/m,
373
396
  ""
374
- ).replace(/^function makeFirebaseStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'firebase':\n *return makeFirebaseStorage\(cfg\);\n/, "");
397
+ ).replace(/^ {2}firebase: \[[^\]]*\],\n/m, "").replace(/\nfunction makeFirebaseStorage[\s\S]*?\n}\n/m, "");
375
398
  }
376
399
  if (uploadProvider !== "cloudinary") {
377
400
  src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/m, "").replace(
378
401
  /^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/m,
379
402
  ""
380
- ).replace(/^function makeCloudinaryStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'cloudinary':\n *return makeCloudinaryStorage\(cfg\);\n/, "");
403
+ ).replace(/\nfunction makeCloudinaryStorage[\s\S]*?\n}\n/m, "");
381
404
  }
382
405
  if (uploadProvider !== "supabase") {
383
406
  src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(
384
407
  /^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/m,
385
408
  ""
386
- ).replace(
387
- /\n {10}case 'supabase': \{[\s\S]*?bucket: requireEnv\(cfg, 'SUPABASE_STORAGE_BUCKET'\),\n {12}\}\);\n {10}\}\n/m,
388
- ""
389
- );
409
+ ).replace(/\nfunction makeSupabaseStorage[\s\S]*?\n}\n/m, "");
390
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);
391
414
  await (0, import_promises.writeFile)(modulePath, src);
392
415
  } catch {
393
416
  }
@@ -397,14 +420,15 @@ async function removeUnusedDbStrategies(targetDir, dbProvider) {
397
420
  if (dbProvider === "supabase") {
398
421
  await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
399
422
  await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/notes/package.json"), [
400
- "@icore/db-firestore"
423
+ "@icore/db-firestore",
424
+ "@icore/firebase-admin"
401
425
  ]);
402
426
  await stripTsconfigPath(targetDir, "@icore/db-firestore");
403
427
  try {
404
428
  const src = await (0, import_promises.readFile)(modulePath, "utf8");
405
- const next = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/m, "").replace(
406
- /\n {8}if \(provider === 'firestore'[\s\S]*?return new FirestoreDBStrategy\(\{[\s\S]*?\}\);\n {8}\}\n/m,
407
- ""
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);"
408
432
  );
409
433
  await (0, import_promises.writeFile)(modulePath, next);
410
434
  } catch {
@@ -418,15 +442,19 @@ async function removeUnusedDbStrategies(targetDir, dbProvider) {
418
442
  await stripTsconfigPath(targetDir, "@icore/db-supabase");
419
443
  try {
420
444
  const src = await (0, import_promises.readFile)(modulePath, "utf8");
421
- const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(
422
- /\n {8}if \(provider === 'supabase'\) \{[\s\S]*?return new SupabaseDBStrategy\(\{ client \}\);\n {8}\}\n/m,
423
- ""
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);"
424
448
  );
425
449
  await (0, import_promises.writeFile)(modulePath, next);
426
450
  } catch {
427
451
  }
428
452
  }
429
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
+ }
430
458
  async function removeUploadStack(targetDir) {
431
459
  const paths = [
432
460
  "apps/microservices/upload",
@@ -539,17 +567,45 @@ async function scaffold(opts, templatesDir) {
539
567
  await removeUnusedAuthStrategies(opts.targetDir, opts.authProvider);
540
568
  await removeUnusedStorageStrategies(opts.targetDir, opts.upload);
541
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);
542
572
  if (opts.packageManager === "yarn") {
543
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 });
544
577
  }
578
+ await patchGitignoreForPm(opts.targetDir, opts.packageManager);
545
579
  await writeAiFiles(opts.targetDir, opts);
546
580
  if (opts.install) runInstall(opts.targetDir, opts.packageManager);
547
581
  if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
548
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
+ }
549
605
  async function writeAiFiles(targetDir, opts) {
550
606
  const pm = opts.packageManager;
551
607
  const nx = pm === "npm" ? "npx nx" : `${pm} nx`;
552
- const devCmd = `${pm} dev`;
608
+ const devCmd = pmRun(pm, "dev");
553
609
  const activeMSes = ["auth (port 4001)"];
554
610
  if (opts.upload !== "none") activeMSes.push(`upload (port 4002)`);
555
611
  if (opts.payment !== "none") activeMSes.push(`payment (port 4003)`);
@@ -960,5 +1016,6 @@ Re-run with @latest to refresh:
960
1016
  // Annotate the CommonJS export names for ESM import in node:
961
1017
  0 && (module.exports = {
962
1018
  collectOptions,
1019
+ pmRun,
963
1020
  scaffold
964
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 };