@idevconn/create-icore 0.3.0 → 0.3.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 +179 -7
- package/dist/index.cjs +168 -5
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +168 -5
- package/package.json +1 -1
- package/templates/apps/microservices/auth/src/app/app.module.ts +13 -9
- package/templates/apps/microservices/notes/src/app/app.module.ts +12 -6
- package/templates/apps/microservices/upload/src/app/app.module.ts +17 -11
- package/templates/libs/shared/src/strategies/fakes/fake-auth.ts +8 -9
- package/templates/libs/shared/src/strategies/fakes/fake-storage.ts +1 -2
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,13 @@ import { resolve } from "path";
|
|
|
13
13
|
import { readFile } from "fs/promises";
|
|
14
14
|
import { dirname, join } from "path";
|
|
15
15
|
import { fileURLToPath } from "url";
|
|
16
|
+
function detectPackageManager() {
|
|
17
|
+
const ua = process.env["npm_config_user_agent"] ?? "";
|
|
18
|
+
if (ua.startsWith("yarn/")) return "yarn";
|
|
19
|
+
if (ua.startsWith("pnpm/")) return "pnpm";
|
|
20
|
+
if (ua.startsWith("npm/")) return "npm";
|
|
21
|
+
return "yarn";
|
|
22
|
+
}
|
|
16
23
|
async function readSelfVersion() {
|
|
17
24
|
try {
|
|
18
25
|
const here2 = dirname(fileURLToPath(import.meta.url));
|
|
@@ -81,6 +88,9 @@ function parseFlags(argv) {
|
|
|
81
88
|
case "transport":
|
|
82
89
|
out.transport = v;
|
|
83
90
|
break;
|
|
91
|
+
case "package-manager":
|
|
92
|
+
out.packageManager = v;
|
|
93
|
+
break;
|
|
84
94
|
case "no-git":
|
|
85
95
|
out.initGit = false;
|
|
86
96
|
break;
|
|
@@ -190,8 +200,12 @@ Re-run with @latest to refresh:
|
|
|
190
200
|
initialValue: "tcp"
|
|
191
201
|
});
|
|
192
202
|
if (p.isCancel(transport)) throw new Error("cancelled");
|
|
203
|
+
const packageManager = flags.packageManager ?? detectPackageManager();
|
|
193
204
|
const initGit = flags.initGit ?? !await p.confirm({ message: "Initialise git repo?", initialValue: true }) === false;
|
|
194
|
-
const install = flags.install ?? !await p.confirm({
|
|
205
|
+
const install = flags.install ?? !await p.confirm({
|
|
206
|
+
message: `Run ${packageManager} install?`,
|
|
207
|
+
initialValue: true
|
|
208
|
+
}) === false;
|
|
195
209
|
return {
|
|
196
210
|
projectName,
|
|
197
211
|
targetDir: resolve(cwd, projectName),
|
|
@@ -203,6 +217,7 @@ Re-run with @latest to refresh:
|
|
|
203
217
|
example,
|
|
204
218
|
ui,
|
|
205
219
|
transport,
|
|
220
|
+
packageManager,
|
|
206
221
|
initGit,
|
|
207
222
|
install
|
|
208
223
|
};
|
|
@@ -244,6 +259,9 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
244
259
|
pkg["version"] = "0.0.1";
|
|
245
260
|
pkg["private"] = true;
|
|
246
261
|
delete pkg.description;
|
|
262
|
+
if (opts.packageManager !== "yarn") {
|
|
263
|
+
delete pkg.packageManager;
|
|
264
|
+
}
|
|
247
265
|
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
248
266
|
}
|
|
249
267
|
async function writeAuthEnv(targetDir, opts) {
|
|
@@ -416,6 +434,145 @@ async function removeNotesStack(targetDir) {
|
|
|
416
434
|
} catch {
|
|
417
435
|
}
|
|
418
436
|
}
|
|
437
|
+
async function stripTsconfigPath(targetDir, alias) {
|
|
438
|
+
const tsconfigPath = join2(targetDir, "tsconfig.base.json");
|
|
439
|
+
try {
|
|
440
|
+
const src = await readFile2(tsconfigPath, "utf8");
|
|
441
|
+
const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
|
|
442
|
+
const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
|
|
443
|
+
if (pretty !== src) {
|
|
444
|
+
await writeFile(tsconfigPath, pretty);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const parsed = JSON.parse(src);
|
|
448
|
+
if (parsed.compilerOptions?.paths) {
|
|
449
|
+
delete parsed.compilerOptions.paths[alias];
|
|
450
|
+
}
|
|
451
|
+
await writeFile(tsconfigPath, JSON.stringify(parsed));
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async function removeUnusedAuthStrategies(targetDir, authProvider) {
|
|
456
|
+
const modulePath = join2(targetDir, "apps/microservices/auth/src/app/app.module.ts");
|
|
457
|
+
if (authProvider === "supabase") {
|
|
458
|
+
await rm(join2(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
|
|
459
|
+
await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
|
|
460
|
+
"@icore/auth-firebase"
|
|
461
|
+
]);
|
|
462
|
+
await stripTsconfigPath(targetDir, "@icore/auth-firebase");
|
|
463
|
+
try {
|
|
464
|
+
const src = await readFile2(modulePath, "utf8");
|
|
465
|
+
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/, "");
|
|
466
|
+
await writeFile(modulePath, next);
|
|
467
|
+
} catch {
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (authProvider === "firebase") {
|
|
471
|
+
await rm(join2(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
|
|
472
|
+
await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
|
|
473
|
+
"@icore/auth-supabase"
|
|
474
|
+
]);
|
|
475
|
+
await stripTsconfigPath(targetDir, "@icore/auth-supabase");
|
|
476
|
+
try {
|
|
477
|
+
const src = await readFile2(modulePath, "utf8");
|
|
478
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/m, "").replace(
|
|
479
|
+
/\n {10}case 'supabase': \{[\s\S]*?return new SupabaseAuthStrategy\(\{ client \}\);\n {10}\}\n/m,
|
|
480
|
+
""
|
|
481
|
+
);
|
|
482
|
+
await writeFile(modulePath, next);
|
|
483
|
+
} catch {
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
|
|
488
|
+
if (uploadProvider === "none") return;
|
|
489
|
+
const modulePath = join2(targetDir, "apps/microservices/upload/src/app/app.module.ts");
|
|
490
|
+
if (uploadProvider !== "firebase") {
|
|
491
|
+
await rm(join2(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
|
|
492
|
+
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
493
|
+
"@icore/storage-firebase"
|
|
494
|
+
]);
|
|
495
|
+
await stripTsconfigPath(targetDir, "@icore/storage-firebase");
|
|
496
|
+
}
|
|
497
|
+
if (uploadProvider !== "cloudinary") {
|
|
498
|
+
await rm(join2(targetDir, "libs/storage-strategies/cloudinary"), {
|
|
499
|
+
recursive: true,
|
|
500
|
+
force: true
|
|
501
|
+
});
|
|
502
|
+
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
503
|
+
"@icore/storage-cloudinary"
|
|
504
|
+
]);
|
|
505
|
+
await stripTsconfigPath(targetDir, "@icore/storage-cloudinary");
|
|
506
|
+
}
|
|
507
|
+
if (uploadProvider !== "supabase") {
|
|
508
|
+
await rm(join2(targetDir, "libs/storage-strategies/supabase"), { recursive: true, force: true });
|
|
509
|
+
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
510
|
+
"@icore/storage-supabase"
|
|
511
|
+
]);
|
|
512
|
+
await stripTsconfigPath(targetDir, "@icore/storage-supabase");
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
let src = await readFile2(modulePath, "utf8");
|
|
516
|
+
if (uploadProvider !== "firebase") {
|
|
517
|
+
src = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(
|
|
518
|
+
/^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/m,
|
|
519
|
+
""
|
|
520
|
+
).replace(/^function makeFirebaseStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'firebase':\n *return makeFirebaseStorage\(cfg\);\n/, "");
|
|
521
|
+
}
|
|
522
|
+
if (uploadProvider !== "cloudinary") {
|
|
523
|
+
src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/m, "").replace(
|
|
524
|
+
/^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/m,
|
|
525
|
+
""
|
|
526
|
+
).replace(/^function makeCloudinaryStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'cloudinary':\n *return makeCloudinaryStorage\(cfg\);\n/, "");
|
|
527
|
+
}
|
|
528
|
+
if (uploadProvider !== "supabase") {
|
|
529
|
+
src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(
|
|
530
|
+
/^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/m,
|
|
531
|
+
""
|
|
532
|
+
).replace(
|
|
533
|
+
/\n {10}case 'supabase': \{[\s\S]*?bucket: requireEnv\(cfg, 'SUPABASE_STORAGE_BUCKET'\),\n {12}\}\);\n {10}\}\n/m,
|
|
534
|
+
""
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
await writeFile(modulePath, src);
|
|
538
|
+
} catch {
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function removeUnusedDbStrategies(targetDir, dbProvider) {
|
|
542
|
+
const modulePath = join2(targetDir, "apps/microservices/notes/src/app/app.module.ts");
|
|
543
|
+
if (dbProvider === "supabase") {
|
|
544
|
+
await rm(join2(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
|
|
545
|
+
await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
|
|
546
|
+
"@icore/db-firestore"
|
|
547
|
+
]);
|
|
548
|
+
await stripTsconfigPath(targetDir, "@icore/db-firestore");
|
|
549
|
+
try {
|
|
550
|
+
const src = await readFile2(modulePath, "utf8");
|
|
551
|
+
const next = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/m, "").replace(
|
|
552
|
+
/\n {8}if \(provider === 'firestore'[\s\S]*?return new FirestoreDBStrategy\(\{[\s\S]*?\}\);\n {8}\}\n/m,
|
|
553
|
+
""
|
|
554
|
+
);
|
|
555
|
+
await writeFile(modulePath, next);
|
|
556
|
+
} catch {
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (dbProvider === "firebase") {
|
|
560
|
+
await rm(join2(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
|
|
561
|
+
await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
|
|
562
|
+
"@icore/db-supabase"
|
|
563
|
+
]);
|
|
564
|
+
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
565
|
+
try {
|
|
566
|
+
const src = await readFile2(modulePath, "utf8");
|
|
567
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(
|
|
568
|
+
/\n {8}if \(provider === 'supabase'\) \{[\s\S]*?return new SupabaseDBStrategy\(\{ client \}\);\n {8}\}\n/m,
|
|
569
|
+
""
|
|
570
|
+
);
|
|
571
|
+
await writeFile(modulePath, next);
|
|
572
|
+
} catch {
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
419
576
|
async function removeUploadStack(targetDir) {
|
|
420
577
|
const paths = [
|
|
421
578
|
"apps/microservices/upload",
|
|
@@ -492,8 +649,9 @@ function gitInit(cwd, projectName) {
|
|
|
492
649
|
{ cwd, stdio: "inherit" }
|
|
493
650
|
);
|
|
494
651
|
}
|
|
495
|
-
function
|
|
496
|
-
|
|
652
|
+
function runInstall(cwd, pm) {
|
|
653
|
+
const [cmd, ...args] = pm === "npm" ? ["npm", "install"] : pm === "pnpm" ? ["pnpm", "install"] : ["yarn", "install"];
|
|
654
|
+
spawnSync(cmd, args, { cwd, stdio: "inherit" });
|
|
497
655
|
}
|
|
498
656
|
async function scaffold(opts, templatesDir2) {
|
|
499
657
|
await copyTree(templatesDir2, opts.targetDir);
|
|
@@ -508,12 +666,26 @@ async function scaffold(opts, templatesDir2) {
|
|
|
508
666
|
if (opts.payment === "none") await removePaymentStack(opts.targetDir);
|
|
509
667
|
if (opts.jobs === "none") await removeJobsStack(opts.targetDir);
|
|
510
668
|
if (opts.example === "none") await removeNotesStack(opts.targetDir);
|
|
511
|
-
await
|
|
512
|
-
|
|
669
|
+
await removeUnusedAuthStrategies(opts.targetDir, opts.authProvider);
|
|
670
|
+
await removeUnusedStorageStrategies(opts.targetDir, opts.upload);
|
|
671
|
+
await removeUnusedDbStrategies(opts.targetDir, opts.dbProvider);
|
|
672
|
+
if (opts.packageManager === "yarn") {
|
|
673
|
+
await writeFile(join2(opts.targetDir, "yarn.lock"), "");
|
|
674
|
+
}
|
|
675
|
+
if (opts.install) runInstall(opts.targetDir, opts.packageManager);
|
|
513
676
|
if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
|
|
514
677
|
}
|
|
515
678
|
|
|
516
679
|
// src/cli.ts
|
|
680
|
+
var [nodeMajor] = process.versions.node.split(".").map(Number);
|
|
681
|
+
if (nodeMajor < 22) {
|
|
682
|
+
process.stderr.write(
|
|
683
|
+
`Error: iCore requires Node.js >= 22. You are running ${process.versions.node}.
|
|
684
|
+
Upgrade: https://nodejs.org
|
|
685
|
+
`
|
|
686
|
+
);
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
517
689
|
var here = dirname2(fileURLToPath2(import.meta.url));
|
|
518
690
|
var templatesDir = resolve2(here, "..", "templates");
|
|
519
691
|
async function main() {
|
|
@@ -533,8 +705,8 @@ async function main() {
|
|
|
533
705
|
p2.outro(kleur.green("Done."));
|
|
534
706
|
p2.log.info(`Next:`);
|
|
535
707
|
p2.log.info(` cd ${opts.projectName}`);
|
|
536
|
-
if (!opts.install) p2.log.info(`
|
|
537
|
-
p2.log.info(`
|
|
708
|
+
if (!opts.install) p2.log.info(` ${opts.packageManager} install`);
|
|
709
|
+
p2.log.info(` ${opts.packageManager} dev # gateway + auth MS + upload MS + client`);
|
|
538
710
|
p2.log.info(` open http://localhost:4200`);
|
|
539
711
|
p2.log.info(` edit apps/microservices/auth/.env to plug in real ${opts.authProvider} creds`);
|
|
540
712
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -75,6 +75,9 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
75
75
|
pkg["version"] = "0.0.1";
|
|
76
76
|
pkg["private"] = true;
|
|
77
77
|
delete pkg.description;
|
|
78
|
+
if (opts.packageManager !== "yarn") {
|
|
79
|
+
delete pkg.packageManager;
|
|
80
|
+
}
|
|
78
81
|
await (0, import_promises.writeFile)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
79
82
|
}
|
|
80
83
|
async function writeAuthEnv(targetDir, opts) {
|
|
@@ -247,6 +250,145 @@ async function removeNotesStack(targetDir) {
|
|
|
247
250
|
} catch {
|
|
248
251
|
}
|
|
249
252
|
}
|
|
253
|
+
async function stripTsconfigPath(targetDir, alias) {
|
|
254
|
+
const tsconfigPath = (0, import_node_path.join)(targetDir, "tsconfig.base.json");
|
|
255
|
+
try {
|
|
256
|
+
const src = await (0, import_promises.readFile)(tsconfigPath, "utf8");
|
|
257
|
+
const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
|
|
258
|
+
const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
|
|
259
|
+
if (pretty !== src) {
|
|
260
|
+
await (0, import_promises.writeFile)(tsconfigPath, pretty);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const parsed = JSON.parse(src);
|
|
264
|
+
if (parsed.compilerOptions?.paths) {
|
|
265
|
+
delete parsed.compilerOptions.paths[alias];
|
|
266
|
+
}
|
|
267
|
+
await (0, import_promises.writeFile)(tsconfigPath, JSON.stringify(parsed));
|
|
268
|
+
} catch {
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async function removeUnusedAuthStrategies(targetDir, authProvider) {
|
|
272
|
+
const modulePath = (0, import_node_path.join)(targetDir, "apps/microservices/auth/src/app/app.module.ts");
|
|
273
|
+
if (authProvider === "supabase") {
|
|
274
|
+
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
|
|
275
|
+
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/auth/package.json"), [
|
|
276
|
+
"@icore/auth-firebase"
|
|
277
|
+
]);
|
|
278
|
+
await stripTsconfigPath(targetDir, "@icore/auth-firebase");
|
|
279
|
+
try {
|
|
280
|
+
const src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
281
|
+
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/, "");
|
|
282
|
+
await (0, import_promises.writeFile)(modulePath, next);
|
|
283
|
+
} catch {
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (authProvider === "firebase") {
|
|
287
|
+
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
|
|
288
|
+
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/auth/package.json"), [
|
|
289
|
+
"@icore/auth-supabase"
|
|
290
|
+
]);
|
|
291
|
+
await stripTsconfigPath(targetDir, "@icore/auth-supabase");
|
|
292
|
+
try {
|
|
293
|
+
const src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
294
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/m, "").replace(
|
|
295
|
+
/\n {10}case 'supabase': \{[\s\S]*?return new SupabaseAuthStrategy\(\{ client \}\);\n {10}\}\n/m,
|
|
296
|
+
""
|
|
297
|
+
);
|
|
298
|
+
await (0, import_promises.writeFile)(modulePath, next);
|
|
299
|
+
} catch {
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
|
|
304
|
+
if (uploadProvider === "none") return;
|
|
305
|
+
const modulePath = (0, import_node_path.join)(targetDir, "apps/microservices/upload/src/app/app.module.ts");
|
|
306
|
+
if (uploadProvider !== "firebase") {
|
|
307
|
+
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
|
|
308
|
+
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/upload/package.json"), [
|
|
309
|
+
"@icore/storage-firebase"
|
|
310
|
+
]);
|
|
311
|
+
await stripTsconfigPath(targetDir, "@icore/storage-firebase");
|
|
312
|
+
}
|
|
313
|
+
if (uploadProvider !== "cloudinary") {
|
|
314
|
+
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/storage-strategies/cloudinary"), {
|
|
315
|
+
recursive: true,
|
|
316
|
+
force: true
|
|
317
|
+
});
|
|
318
|
+
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/upload/package.json"), [
|
|
319
|
+
"@icore/storage-cloudinary"
|
|
320
|
+
]);
|
|
321
|
+
await stripTsconfigPath(targetDir, "@icore/storage-cloudinary");
|
|
322
|
+
}
|
|
323
|
+
if (uploadProvider !== "supabase") {
|
|
324
|
+
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/storage-strategies/supabase"), { recursive: true, force: true });
|
|
325
|
+
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/upload/package.json"), [
|
|
326
|
+
"@icore/storage-supabase"
|
|
327
|
+
]);
|
|
328
|
+
await stripTsconfigPath(targetDir, "@icore/storage-supabase");
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
let src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
332
|
+
if (uploadProvider !== "firebase") {
|
|
333
|
+
src = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(
|
|
334
|
+
/^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/m,
|
|
335
|
+
""
|
|
336
|
+
).replace(/^function makeFirebaseStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'firebase':\n *return makeFirebaseStorage\(cfg\);\n/, "");
|
|
337
|
+
}
|
|
338
|
+
if (uploadProvider !== "cloudinary") {
|
|
339
|
+
src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/m, "").replace(
|
|
340
|
+
/^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/m,
|
|
341
|
+
""
|
|
342
|
+
).replace(/^function makeCloudinaryStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'cloudinary':\n *return makeCloudinaryStorage\(cfg\);\n/, "");
|
|
343
|
+
}
|
|
344
|
+
if (uploadProvider !== "supabase") {
|
|
345
|
+
src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(
|
|
346
|
+
/^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/m,
|
|
347
|
+
""
|
|
348
|
+
).replace(
|
|
349
|
+
/\n {10}case 'supabase': \{[\s\S]*?bucket: requireEnv\(cfg, 'SUPABASE_STORAGE_BUCKET'\),\n {12}\}\);\n {10}\}\n/m,
|
|
350
|
+
""
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
await (0, import_promises.writeFile)(modulePath, src);
|
|
354
|
+
} catch {
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async function removeUnusedDbStrategies(targetDir, dbProvider) {
|
|
358
|
+
const modulePath = (0, import_node_path.join)(targetDir, "apps/microservices/notes/src/app/app.module.ts");
|
|
359
|
+
if (dbProvider === "supabase") {
|
|
360
|
+
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
|
|
361
|
+
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/notes/package.json"), [
|
|
362
|
+
"@icore/db-firestore"
|
|
363
|
+
]);
|
|
364
|
+
await stripTsconfigPath(targetDir, "@icore/db-firestore");
|
|
365
|
+
try {
|
|
366
|
+
const src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
367
|
+
const next = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/m, "").replace(
|
|
368
|
+
/\n {8}if \(provider === 'firestore'[\s\S]*?return new FirestoreDBStrategy\(\{[\s\S]*?\}\);\n {8}\}\n/m,
|
|
369
|
+
""
|
|
370
|
+
);
|
|
371
|
+
await (0, import_promises.writeFile)(modulePath, next);
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (dbProvider === "firebase") {
|
|
376
|
+
await (0, import_promises.rm)((0, import_node_path.join)(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
|
|
377
|
+
await stripDeps((0, import_node_path.join)(targetDir, "apps/microservices/notes/package.json"), [
|
|
378
|
+
"@icore/db-supabase"
|
|
379
|
+
]);
|
|
380
|
+
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
381
|
+
try {
|
|
382
|
+
const src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
383
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(
|
|
384
|
+
/\n {8}if \(provider === 'supabase'\) \{[\s\S]*?return new SupabaseDBStrategy\(\{ client \}\);\n {8}\}\n/m,
|
|
385
|
+
""
|
|
386
|
+
);
|
|
387
|
+
await (0, import_promises.writeFile)(modulePath, next);
|
|
388
|
+
} catch {
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
250
392
|
async function removeUploadStack(targetDir) {
|
|
251
393
|
const paths = [
|
|
252
394
|
"apps/microservices/upload",
|
|
@@ -323,8 +465,9 @@ function gitInit(cwd, projectName) {
|
|
|
323
465
|
{ cwd, stdio: "inherit" }
|
|
324
466
|
);
|
|
325
467
|
}
|
|
326
|
-
function
|
|
327
|
-
|
|
468
|
+
function runInstall(cwd, pm) {
|
|
469
|
+
const [cmd, ...args] = pm === "npm" ? ["npm", "install"] : pm === "pnpm" ? ["pnpm", "install"] : ["yarn", "install"];
|
|
470
|
+
(0, import_node_child_process.spawnSync)(cmd, args, { cwd, stdio: "inherit" });
|
|
328
471
|
}
|
|
329
472
|
async function scaffold(opts, templatesDir) {
|
|
330
473
|
await copyTree(templatesDir, opts.targetDir);
|
|
@@ -339,8 +482,13 @@ async function scaffold(opts, templatesDir) {
|
|
|
339
482
|
if (opts.payment === "none") await removePaymentStack(opts.targetDir);
|
|
340
483
|
if (opts.jobs === "none") await removeJobsStack(opts.targetDir);
|
|
341
484
|
if (opts.example === "none") await removeNotesStack(opts.targetDir);
|
|
342
|
-
await (
|
|
343
|
-
|
|
485
|
+
await removeUnusedAuthStrategies(opts.targetDir, opts.authProvider);
|
|
486
|
+
await removeUnusedStorageStrategies(opts.targetDir, opts.upload);
|
|
487
|
+
await removeUnusedDbStrategies(opts.targetDir, opts.dbProvider);
|
|
488
|
+
if (opts.packageManager === "yarn") {
|
|
489
|
+
await (0, import_promises.writeFile)((0, import_node_path.join)(opts.targetDir, "yarn.lock"), "");
|
|
490
|
+
}
|
|
491
|
+
if (opts.install) runInstall(opts.targetDir, opts.packageManager);
|
|
344
492
|
if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
|
|
345
493
|
}
|
|
346
494
|
|
|
@@ -350,6 +498,13 @@ var import_node_path2 = require("path");
|
|
|
350
498
|
var import_promises2 = require("fs/promises");
|
|
351
499
|
var import_node_path3 = require("path");
|
|
352
500
|
var import_node_url = require("url");
|
|
501
|
+
function detectPackageManager() {
|
|
502
|
+
const ua = process.env["npm_config_user_agent"] ?? "";
|
|
503
|
+
if (ua.startsWith("yarn/")) return "yarn";
|
|
504
|
+
if (ua.startsWith("pnpm/")) return "pnpm";
|
|
505
|
+
if (ua.startsWith("npm/")) return "npm";
|
|
506
|
+
return "yarn";
|
|
507
|
+
}
|
|
353
508
|
async function readSelfVersion() {
|
|
354
509
|
try {
|
|
355
510
|
const here = (0, import_node_path3.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
@@ -418,6 +573,9 @@ function parseFlags(argv) {
|
|
|
418
573
|
case "transport":
|
|
419
574
|
out.transport = v;
|
|
420
575
|
break;
|
|
576
|
+
case "package-manager":
|
|
577
|
+
out.packageManager = v;
|
|
578
|
+
break;
|
|
421
579
|
case "no-git":
|
|
422
580
|
out.initGit = false;
|
|
423
581
|
break;
|
|
@@ -527,8 +685,12 @@ Re-run with @latest to refresh:
|
|
|
527
685
|
initialValue: "tcp"
|
|
528
686
|
});
|
|
529
687
|
if (p.isCancel(transport)) throw new Error("cancelled");
|
|
688
|
+
const packageManager = flags.packageManager ?? detectPackageManager();
|
|
530
689
|
const initGit = flags.initGit ?? !await p.confirm({ message: "Initialise git repo?", initialValue: true }) === false;
|
|
531
|
-
const install = flags.install ?? !await p.confirm({
|
|
690
|
+
const install = flags.install ?? !await p.confirm({
|
|
691
|
+
message: `Run ${packageManager} install?`,
|
|
692
|
+
initialValue: true
|
|
693
|
+
}) === false;
|
|
532
694
|
return {
|
|
533
695
|
projectName,
|
|
534
696
|
targetDir: (0, import_node_path2.resolve)(cwd, projectName),
|
|
@@ -540,6 +702,7 @@ Re-run with @latest to refresh:
|
|
|
540
702
|
example,
|
|
541
703
|
ui,
|
|
542
704
|
transport,
|
|
705
|
+
packageManager,
|
|
543
706
|
initGit,
|
|
544
707
|
install
|
|
545
708
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -6,6 +6,7 @@ type JobsProvider = 'bullmq' | 'none';
|
|
|
6
6
|
type ExampleMode = 'notes' | 'none';
|
|
7
7
|
type UiLibrary = 'shadcn' | 'antd' | 'mui';
|
|
8
8
|
type MsTransport = 'tcp' | 'redis' | 'nats';
|
|
9
|
+
type PackageManager = 'yarn' | 'npm' | 'pnpm';
|
|
9
10
|
interface CreateIcoreOptions {
|
|
10
11
|
projectName: string;
|
|
11
12
|
targetDir: string;
|
|
@@ -17,6 +18,7 @@ interface CreateIcoreOptions {
|
|
|
17
18
|
example: ExampleMode;
|
|
18
19
|
ui: UiLibrary;
|
|
19
20
|
transport: MsTransport;
|
|
21
|
+
packageManager: PackageManager;
|
|
20
22
|
initGit: boolean;
|
|
21
23
|
install: boolean;
|
|
22
24
|
}
|
|
@@ -29,4 +31,4 @@ interface PromptInput {
|
|
|
29
31
|
}
|
|
30
32
|
declare function collectOptions({ argv, cwd }: PromptInput): Promise<CreateIcoreOptions>;
|
|
31
33
|
|
|
32
|
-
export { type AuthProvider, type CreateIcoreOptions, type DbProvider, type ExampleMode, type JobsProvider, type MsTransport, type PaymentProvider, type UiLibrary, type UploadProvider, collectOptions, scaffold };
|
|
34
|
+
export { type AuthProvider, type CreateIcoreOptions, type DbProvider, type ExampleMode, type JobsProvider, type MsTransport, type PackageManager, type PaymentProvider, type UiLibrary, type UploadProvider, collectOptions, scaffold };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ type JobsProvider = 'bullmq' | 'none';
|
|
|
6
6
|
type ExampleMode = 'notes' | 'none';
|
|
7
7
|
type UiLibrary = 'shadcn' | 'antd' | 'mui';
|
|
8
8
|
type MsTransport = 'tcp' | 'redis' | 'nats';
|
|
9
|
+
type PackageManager = 'yarn' | 'npm' | 'pnpm';
|
|
9
10
|
interface CreateIcoreOptions {
|
|
10
11
|
projectName: string;
|
|
11
12
|
targetDir: string;
|
|
@@ -17,6 +18,7 @@ interface CreateIcoreOptions {
|
|
|
17
18
|
example: ExampleMode;
|
|
18
19
|
ui: UiLibrary;
|
|
19
20
|
transport: MsTransport;
|
|
21
|
+
packageManager: PackageManager;
|
|
20
22
|
initGit: boolean;
|
|
21
23
|
install: boolean;
|
|
22
24
|
}
|
|
@@ -29,4 +31,4 @@ interface PromptInput {
|
|
|
29
31
|
}
|
|
30
32
|
declare function collectOptions({ argv, cwd }: PromptInput): Promise<CreateIcoreOptions>;
|
|
31
33
|
|
|
32
|
-
export { type AuthProvider, type CreateIcoreOptions, type DbProvider, type ExampleMode, type JobsProvider, type MsTransport, type PaymentProvider, type UiLibrary, type UploadProvider, collectOptions, scaffold };
|
|
34
|
+
export { type AuthProvider, type CreateIcoreOptions, type DbProvider, type ExampleMode, type JobsProvider, type MsTransport, type PackageManager, type PaymentProvider, type UiLibrary, type UploadProvider, collectOptions, scaffold };
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,9 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
34
34
|
pkg["version"] = "0.0.1";
|
|
35
35
|
pkg["private"] = true;
|
|
36
36
|
delete pkg.description;
|
|
37
|
+
if (opts.packageManager !== "yarn") {
|
|
38
|
+
delete pkg.packageManager;
|
|
39
|
+
}
|
|
37
40
|
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
38
41
|
}
|
|
39
42
|
async function writeAuthEnv(targetDir, opts) {
|
|
@@ -206,6 +209,145 @@ async function removeNotesStack(targetDir) {
|
|
|
206
209
|
} catch {
|
|
207
210
|
}
|
|
208
211
|
}
|
|
212
|
+
async function stripTsconfigPath(targetDir, alias) {
|
|
213
|
+
const tsconfigPath = join(targetDir, "tsconfig.base.json");
|
|
214
|
+
try {
|
|
215
|
+
const src = await readFile(tsconfigPath, "utf8");
|
|
216
|
+
const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
|
|
217
|
+
const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
|
|
218
|
+
if (pretty !== src) {
|
|
219
|
+
await writeFile(tsconfigPath, pretty);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const parsed = JSON.parse(src);
|
|
223
|
+
if (parsed.compilerOptions?.paths) {
|
|
224
|
+
delete parsed.compilerOptions.paths[alias];
|
|
225
|
+
}
|
|
226
|
+
await writeFile(tsconfigPath, JSON.stringify(parsed));
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function removeUnusedAuthStrategies(targetDir, authProvider) {
|
|
231
|
+
const modulePath = join(targetDir, "apps/microservices/auth/src/app/app.module.ts");
|
|
232
|
+
if (authProvider === "supabase") {
|
|
233
|
+
await rm(join(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
|
|
234
|
+
await stripDeps(join(targetDir, "apps/microservices/auth/package.json"), [
|
|
235
|
+
"@icore/auth-firebase"
|
|
236
|
+
]);
|
|
237
|
+
await stripTsconfigPath(targetDir, "@icore/auth-firebase");
|
|
238
|
+
try {
|
|
239
|
+
const src = await readFile(modulePath, "utf8");
|
|
240
|
+
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/, "");
|
|
241
|
+
await writeFile(modulePath, next);
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (authProvider === "firebase") {
|
|
246
|
+
await rm(join(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
|
|
247
|
+
await stripDeps(join(targetDir, "apps/microservices/auth/package.json"), [
|
|
248
|
+
"@icore/auth-supabase"
|
|
249
|
+
]);
|
|
250
|
+
await stripTsconfigPath(targetDir, "@icore/auth-supabase");
|
|
251
|
+
try {
|
|
252
|
+
const src = await readFile(modulePath, "utf8");
|
|
253
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/m, "").replace(
|
|
254
|
+
/\n {10}case 'supabase': \{[\s\S]*?return new SupabaseAuthStrategy\(\{ client \}\);\n {10}\}\n/m,
|
|
255
|
+
""
|
|
256
|
+
);
|
|
257
|
+
await writeFile(modulePath, next);
|
|
258
|
+
} catch {
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
|
|
263
|
+
if (uploadProvider === "none") return;
|
|
264
|
+
const modulePath = join(targetDir, "apps/microservices/upload/src/app/app.module.ts");
|
|
265
|
+
if (uploadProvider !== "firebase") {
|
|
266
|
+
await rm(join(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
|
|
267
|
+
await stripDeps(join(targetDir, "apps/microservices/upload/package.json"), [
|
|
268
|
+
"@icore/storage-firebase"
|
|
269
|
+
]);
|
|
270
|
+
await stripTsconfigPath(targetDir, "@icore/storage-firebase");
|
|
271
|
+
}
|
|
272
|
+
if (uploadProvider !== "cloudinary") {
|
|
273
|
+
await rm(join(targetDir, "libs/storage-strategies/cloudinary"), {
|
|
274
|
+
recursive: true,
|
|
275
|
+
force: true
|
|
276
|
+
});
|
|
277
|
+
await stripDeps(join(targetDir, "apps/microservices/upload/package.json"), [
|
|
278
|
+
"@icore/storage-cloudinary"
|
|
279
|
+
]);
|
|
280
|
+
await stripTsconfigPath(targetDir, "@icore/storage-cloudinary");
|
|
281
|
+
}
|
|
282
|
+
if (uploadProvider !== "supabase") {
|
|
283
|
+
await rm(join(targetDir, "libs/storage-strategies/supabase"), { recursive: true, force: true });
|
|
284
|
+
await stripDeps(join(targetDir, "apps/microservices/upload/package.json"), [
|
|
285
|
+
"@icore/storage-supabase"
|
|
286
|
+
]);
|
|
287
|
+
await stripTsconfigPath(targetDir, "@icore/storage-supabase");
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
let src = await readFile(modulePath, "utf8");
|
|
291
|
+
if (uploadProvider !== "firebase") {
|
|
292
|
+
src = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(
|
|
293
|
+
/^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/m,
|
|
294
|
+
""
|
|
295
|
+
).replace(/^function makeFirebaseStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'firebase':\n *return makeFirebaseStorage\(cfg\);\n/, "");
|
|
296
|
+
}
|
|
297
|
+
if (uploadProvider !== "cloudinary") {
|
|
298
|
+
src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/m, "").replace(
|
|
299
|
+
/^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/m,
|
|
300
|
+
""
|
|
301
|
+
).replace(/^function makeCloudinaryStorage\b[\s\S]*?\n^}\n/m, "").replace(/(?<=\n) *case 'cloudinary':\n *return makeCloudinaryStorage\(cfg\);\n/, "");
|
|
302
|
+
}
|
|
303
|
+
if (uploadProvider !== "supabase") {
|
|
304
|
+
src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(
|
|
305
|
+
/^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/m,
|
|
306
|
+
""
|
|
307
|
+
).replace(
|
|
308
|
+
/\n {10}case 'supabase': \{[\s\S]*?bucket: requireEnv\(cfg, 'SUPABASE_STORAGE_BUCKET'\),\n {12}\}\);\n {10}\}\n/m,
|
|
309
|
+
""
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
await writeFile(modulePath, src);
|
|
313
|
+
} catch {
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async function removeUnusedDbStrategies(targetDir, dbProvider) {
|
|
317
|
+
const modulePath = join(targetDir, "apps/microservices/notes/src/app/app.module.ts");
|
|
318
|
+
if (dbProvider === "supabase") {
|
|
319
|
+
await rm(join(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
|
|
320
|
+
await stripDeps(join(targetDir, "apps/microservices/notes/package.json"), [
|
|
321
|
+
"@icore/db-firestore"
|
|
322
|
+
]);
|
|
323
|
+
await stripTsconfigPath(targetDir, "@icore/db-firestore");
|
|
324
|
+
try {
|
|
325
|
+
const src = await readFile(modulePath, "utf8");
|
|
326
|
+
const next = src.replace(/^import \* as admin from 'firebase-admin';\n/m, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/m, "").replace(
|
|
327
|
+
/\n {8}if \(provider === 'firestore'[\s\S]*?return new FirestoreDBStrategy\(\{[\s\S]*?\}\);\n {8}\}\n/m,
|
|
328
|
+
""
|
|
329
|
+
);
|
|
330
|
+
await writeFile(modulePath, next);
|
|
331
|
+
} catch {
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (dbProvider === "firebase") {
|
|
335
|
+
await rm(join(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
|
|
336
|
+
await stripDeps(join(targetDir, "apps/microservices/notes/package.json"), [
|
|
337
|
+
"@icore/db-supabase"
|
|
338
|
+
]);
|
|
339
|
+
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
340
|
+
try {
|
|
341
|
+
const src = await readFile(modulePath, "utf8");
|
|
342
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(
|
|
343
|
+
/\n {8}if \(provider === 'supabase'\) \{[\s\S]*?return new SupabaseDBStrategy\(\{ client \}\);\n {8}\}\n/m,
|
|
344
|
+
""
|
|
345
|
+
);
|
|
346
|
+
await writeFile(modulePath, next);
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
209
351
|
async function removeUploadStack(targetDir) {
|
|
210
352
|
const paths = [
|
|
211
353
|
"apps/microservices/upload",
|
|
@@ -282,8 +424,9 @@ function gitInit(cwd, projectName) {
|
|
|
282
424
|
{ cwd, stdio: "inherit" }
|
|
283
425
|
);
|
|
284
426
|
}
|
|
285
|
-
function
|
|
286
|
-
|
|
427
|
+
function runInstall(cwd, pm) {
|
|
428
|
+
const [cmd, ...args] = pm === "npm" ? ["npm", "install"] : pm === "pnpm" ? ["pnpm", "install"] : ["yarn", "install"];
|
|
429
|
+
spawnSync(cmd, args, { cwd, stdio: "inherit" });
|
|
287
430
|
}
|
|
288
431
|
async function scaffold(opts, templatesDir) {
|
|
289
432
|
await copyTree(templatesDir, opts.targetDir);
|
|
@@ -298,8 +441,13 @@ async function scaffold(opts, templatesDir) {
|
|
|
298
441
|
if (opts.payment === "none") await removePaymentStack(opts.targetDir);
|
|
299
442
|
if (opts.jobs === "none") await removeJobsStack(opts.targetDir);
|
|
300
443
|
if (opts.example === "none") await removeNotesStack(opts.targetDir);
|
|
301
|
-
await
|
|
302
|
-
|
|
444
|
+
await removeUnusedAuthStrategies(opts.targetDir, opts.authProvider);
|
|
445
|
+
await removeUnusedStorageStrategies(opts.targetDir, opts.upload);
|
|
446
|
+
await removeUnusedDbStrategies(opts.targetDir, opts.dbProvider);
|
|
447
|
+
if (opts.packageManager === "yarn") {
|
|
448
|
+
await writeFile(join(opts.targetDir, "yarn.lock"), "");
|
|
449
|
+
}
|
|
450
|
+
if (opts.install) runInstall(opts.targetDir, opts.packageManager);
|
|
303
451
|
if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
|
|
304
452
|
}
|
|
305
453
|
|
|
@@ -309,6 +457,13 @@ import { resolve } from "path";
|
|
|
309
457
|
import { readFile as readFile2 } from "fs/promises";
|
|
310
458
|
import { dirname, join as join2 } from "path";
|
|
311
459
|
import { fileURLToPath } from "url";
|
|
460
|
+
function detectPackageManager() {
|
|
461
|
+
const ua = process.env["npm_config_user_agent"] ?? "";
|
|
462
|
+
if (ua.startsWith("yarn/")) return "yarn";
|
|
463
|
+
if (ua.startsWith("pnpm/")) return "pnpm";
|
|
464
|
+
if (ua.startsWith("npm/")) return "npm";
|
|
465
|
+
return "yarn";
|
|
466
|
+
}
|
|
312
467
|
async function readSelfVersion() {
|
|
313
468
|
try {
|
|
314
469
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -377,6 +532,9 @@ function parseFlags(argv) {
|
|
|
377
532
|
case "transport":
|
|
378
533
|
out.transport = v;
|
|
379
534
|
break;
|
|
535
|
+
case "package-manager":
|
|
536
|
+
out.packageManager = v;
|
|
537
|
+
break;
|
|
380
538
|
case "no-git":
|
|
381
539
|
out.initGit = false;
|
|
382
540
|
break;
|
|
@@ -486,8 +644,12 @@ Re-run with @latest to refresh:
|
|
|
486
644
|
initialValue: "tcp"
|
|
487
645
|
});
|
|
488
646
|
if (p.isCancel(transport)) throw new Error("cancelled");
|
|
647
|
+
const packageManager = flags.packageManager ?? detectPackageManager();
|
|
489
648
|
const initGit = flags.initGit ?? !await p.confirm({ message: "Initialise git repo?", initialValue: true }) === false;
|
|
490
|
-
const install = flags.install ?? !await p.confirm({
|
|
649
|
+
const install = flags.install ?? !await p.confirm({
|
|
650
|
+
message: `Run ${packageManager} install?`,
|
|
651
|
+
initialValue: true
|
|
652
|
+
}) === false;
|
|
491
653
|
return {
|
|
492
654
|
projectName,
|
|
493
655
|
targetDir: resolve(cwd, projectName),
|
|
@@ -499,6 +661,7 @@ Re-run with @latest to refresh:
|
|
|
499
661
|
example,
|
|
500
662
|
ui,
|
|
501
663
|
transport,
|
|
664
|
+
packageManager,
|
|
502
665
|
initGit,
|
|
503
666
|
install
|
|
504
667
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idevconn/create-icore",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Bootstrap a new project from the iCore scaffold (Nx + NestJS + React + Vite + shadcn/Tailwind, swappable auth + storage providers).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "iDEVconn",
|
|
@@ -8,20 +8,24 @@ import { FirebaseAuthStrategy, HttpIdentityToolkitClient } from '@icore/auth-fir
|
|
|
8
8
|
import type { AuthStrategy } from '@icore/shared';
|
|
9
9
|
import { AuthController } from './auth.controller';
|
|
10
10
|
|
|
11
|
+
function requireEnv(cfg: ConfigService, key: string): string {
|
|
12
|
+
const val = cfg.getOrThrow<string>(key);
|
|
13
|
+
if (!val) throw new Error(`${key} is not set — check apps/microservices/auth/.env`);
|
|
14
|
+
return val;
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
function makeFirebaseStrategy(cfg: ConfigService): AuthStrategy {
|
|
12
|
-
const projectId = cfg
|
|
18
|
+
const projectId = requireEnv(cfg, 'FB_ADMIN_PROJECT_ID');
|
|
13
19
|
if (admin.apps.length === 0) {
|
|
14
20
|
admin.initializeApp({
|
|
15
21
|
credential: admin.credential.cert({
|
|
16
22
|
projectId,
|
|
17
|
-
clientEmail: cfg
|
|
18
|
-
privateKey: cfg
|
|
23
|
+
clientEmail: requireEnv(cfg, 'FB_ADMIN_CLIENT_EMAIL'),
|
|
24
|
+
privateKey: requireEnv(cfg, 'FB_ADMIN_PRIVATE_KEY').replace(/\\n/g, '\n'),
|
|
19
25
|
}),
|
|
20
26
|
});
|
|
21
27
|
}
|
|
22
|
-
const identityToolkit = new HttpIdentityToolkitClient(
|
|
23
|
-
cfg.getOrThrow<string>('FIREBASE_WEB_API_KEY'),
|
|
24
|
-
);
|
|
28
|
+
const identityToolkit = new HttpIdentityToolkitClient(requireEnv(cfg, 'FIREBASE_WEB_API_KEY'));
|
|
25
29
|
return new FirebaseAuthStrategy({
|
|
26
30
|
identityToolkit,
|
|
27
31
|
adminAuth: admin.auth(),
|
|
@@ -43,12 +47,12 @@ function makeFirebaseStrategy(cfg: ConfigService): AuthStrategy {
|
|
|
43
47
|
{
|
|
44
48
|
provide: 'AuthStrategy',
|
|
45
49
|
useFactory: (cfg: ConfigService): AuthStrategy => {
|
|
46
|
-
const provider = cfg
|
|
50
|
+
const provider = requireEnv(cfg, 'AUTH_PROVIDER');
|
|
47
51
|
switch (provider) {
|
|
48
52
|
case 'supabase': {
|
|
49
53
|
const client = createClient(
|
|
50
|
-
cfg
|
|
51
|
-
cfg
|
|
54
|
+
requireEnv(cfg, 'SUPABASE_URL'),
|
|
55
|
+
requireEnv(cfg, 'SUPABASE_SERVICE_ROLE_KEY'),
|
|
52
56
|
{ auth: { autoRefreshToken: false, persistSession: false } },
|
|
53
57
|
);
|
|
54
58
|
return new SupabaseAuthStrategy({ client });
|
|
@@ -8,6 +8,12 @@ import { FirestoreDBStrategy } from '@icore/db-firestore';
|
|
|
8
8
|
import type { DBStrategy } from '@icore/shared';
|
|
9
9
|
import { NotesController } from './notes.controller';
|
|
10
10
|
|
|
11
|
+
function requireEnv(cfg: ConfigService, key: string): string {
|
|
12
|
+
const val = cfg.getOrThrow<string>(key);
|
|
13
|
+
if (!val) throw new Error(`${key} is not set — check apps/microservices/notes/.env`);
|
|
14
|
+
return val;
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
@Module({
|
|
12
18
|
imports: [
|
|
13
19
|
ConfigModule.forRoot({
|
|
@@ -23,11 +29,11 @@ import { NotesController } from './notes.controller';
|
|
|
23
29
|
{
|
|
24
30
|
provide: 'DBStrategy',
|
|
25
31
|
useFactory: (cfg: ConfigService): DBStrategy => {
|
|
26
|
-
const provider = cfg
|
|
32
|
+
const provider = requireEnv(cfg, 'DB_PROVIDER');
|
|
27
33
|
if (provider === 'supabase') {
|
|
28
34
|
const client = createClient(
|
|
29
|
-
cfg
|
|
30
|
-
cfg
|
|
35
|
+
requireEnv(cfg, 'SUPABASE_URL'),
|
|
36
|
+
requireEnv(cfg, 'SUPABASE_SERVICE_ROLE_KEY'),
|
|
31
37
|
{ auth: { autoRefreshToken: false, persistSession: false } },
|
|
32
38
|
);
|
|
33
39
|
return new SupabaseDBStrategy({ client });
|
|
@@ -36,9 +42,9 @@ import { NotesController } from './notes.controller';
|
|
|
36
42
|
if (admin.apps.length === 0) {
|
|
37
43
|
admin.initializeApp({
|
|
38
44
|
credential: admin.credential.cert({
|
|
39
|
-
projectId: cfg
|
|
40
|
-
clientEmail: cfg
|
|
41
|
-
privateKey: cfg
|
|
45
|
+
projectId: requireEnv(cfg, 'FB_ADMIN_PROJECT_ID'),
|
|
46
|
+
clientEmail: requireEnv(cfg, 'FB_ADMIN_CLIENT_EMAIL'),
|
|
47
|
+
privateKey: requireEnv(cfg, 'FB_ADMIN_PRIVATE_KEY').replace(/\\n/g, '\n'),
|
|
42
48
|
}),
|
|
43
49
|
});
|
|
44
50
|
}
|
|
@@ -10,14 +10,20 @@ import { CloudinaryStorageStrategy, type CloudinaryApiLike } from '@icore/storag
|
|
|
10
10
|
import type { StorageStrategy } from '@icore/shared';
|
|
11
11
|
import { StorageController } from './storage.controller';
|
|
12
12
|
|
|
13
|
+
function requireEnv(cfg: ConfigService, key: string): string {
|
|
14
|
+
const val = cfg.getOrThrow<string>(key);
|
|
15
|
+
if (!val) throw new Error(`${key} is not set — check apps/microservices/upload/.env`);
|
|
16
|
+
return val;
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
function makeFirebaseStorage(cfg: ConfigService): StorageStrategy {
|
|
14
|
-
const bucketName = cfg
|
|
20
|
+
const bucketName = requireEnv(cfg, 'FIREBASE_STORAGE_BUCKET');
|
|
15
21
|
if (admin.apps.length === 0) {
|
|
16
22
|
admin.initializeApp({
|
|
17
23
|
credential: admin.credential.cert({
|
|
18
|
-
projectId: cfg
|
|
19
|
-
clientEmail: cfg
|
|
20
|
-
privateKey: cfg
|
|
24
|
+
projectId: requireEnv(cfg, 'FB_ADMIN_PROJECT_ID'),
|
|
25
|
+
clientEmail: requireEnv(cfg, 'FB_ADMIN_CLIENT_EMAIL'),
|
|
26
|
+
privateKey: requireEnv(cfg, 'FB_ADMIN_PRIVATE_KEY').replace(/\\n/g, '\n'),
|
|
21
27
|
}),
|
|
22
28
|
});
|
|
23
29
|
}
|
|
@@ -30,9 +36,9 @@ function makeFirebaseStorage(cfg: ConfigService): StorageStrategy {
|
|
|
30
36
|
|
|
31
37
|
function makeCloudinaryStorage(cfg: ConfigService): StorageStrategy {
|
|
32
38
|
cloudinary.config({
|
|
33
|
-
cloud_name: cfg
|
|
34
|
-
api_key: cfg
|
|
35
|
-
api_secret: cfg
|
|
39
|
+
cloud_name: requireEnv(cfg, 'CLOUDINARY_CLOUD_NAME'),
|
|
40
|
+
api_key: requireEnv(cfg, 'CLOUDINARY_API_KEY'),
|
|
41
|
+
api_secret: requireEnv(cfg, 'CLOUDINARY_API_SECRET'),
|
|
36
42
|
secure: true,
|
|
37
43
|
});
|
|
38
44
|
|
|
@@ -89,17 +95,17 @@ function makeCloudinaryStorage(cfg: ConfigService): StorageStrategy {
|
|
|
89
95
|
{
|
|
90
96
|
provide: 'StorageStrategy',
|
|
91
97
|
useFactory: (cfg: ConfigService): StorageStrategy => {
|
|
92
|
-
const provider = cfg
|
|
98
|
+
const provider = requireEnv(cfg, 'STORAGE_PROVIDER');
|
|
93
99
|
switch (provider) {
|
|
94
100
|
case 'supabase': {
|
|
95
101
|
const client = createClient(
|
|
96
|
-
cfg
|
|
97
|
-
cfg
|
|
102
|
+
requireEnv(cfg, 'SUPABASE_URL'),
|
|
103
|
+
requireEnv(cfg, 'SUPABASE_SERVICE_ROLE_KEY'),
|
|
98
104
|
{ auth: { autoRefreshToken: false, persistSession: false } },
|
|
99
105
|
);
|
|
100
106
|
return new SupabaseStorageStrategy({
|
|
101
107
|
client,
|
|
102
|
-
bucket: cfg
|
|
108
|
+
bucket: requireEnv(cfg, 'SUPABASE_STORAGE_BUCKET'),
|
|
103
109
|
});
|
|
104
110
|
}
|
|
105
111
|
case 'firebase':
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
1
|
import type {
|
|
3
2
|
AuthSession,
|
|
4
3
|
AuthStrategy,
|
|
@@ -33,7 +32,7 @@ export class FakeAuthStrategy implements AuthStrategy {
|
|
|
33
32
|
|
|
34
33
|
async signUp(email: string, password: string): Promise<AuthSession> {
|
|
35
34
|
if (this.users.has(email)) throw new Error('user_exists');
|
|
36
|
-
const user: StoredUser = { id: randomUUID(), email, password };
|
|
35
|
+
const user: StoredUser = { id: globalThis.crypto.randomUUID(), email, password };
|
|
37
36
|
this.users.set(email, user);
|
|
38
37
|
return this.issueSession(user);
|
|
39
38
|
}
|
|
@@ -72,10 +71,10 @@ export class FakeAuthStrategy implements AuthStrategy {
|
|
|
72
71
|
async sendMagicLink(req: MagicLinkRequest): Promise<void> {
|
|
73
72
|
let user = this.users.get(req.email);
|
|
74
73
|
if (!user) {
|
|
75
|
-
user = { id: randomUUID(), email: req.email, password: '' };
|
|
74
|
+
user = { id: globalThis.crypto.randomUUID(), email: req.email, password: '' };
|
|
76
75
|
this.users.set(req.email, user);
|
|
77
76
|
}
|
|
78
|
-
const token = randomUUID();
|
|
77
|
+
const token = globalThis.crypto.randomUUID();
|
|
79
78
|
this.magicLinkTokens.set(token, user.id);
|
|
80
79
|
this.magicLinkByEmail.set(req.email, token);
|
|
81
80
|
}
|
|
@@ -95,7 +94,7 @@ export class FakeAuthStrategy implements AuthStrategy {
|
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
async startOAuth(provider: OAuthProvider, callbackUrl: string): Promise<OAuthStartResult> {
|
|
98
|
-
const state = randomUUID();
|
|
97
|
+
const state = globalThis.crypto.randomUUID();
|
|
99
98
|
this.lastOAuthState = state;
|
|
100
99
|
const url = new URL(`https://fake-${provider}.example.com/authorize`);
|
|
101
100
|
url.searchParams.set('redirect_uri', callbackUrl);
|
|
@@ -111,7 +110,7 @@ export class FakeAuthStrategy implements AuthStrategy {
|
|
|
111
110
|
this.oauthCodes.delete(code);
|
|
112
111
|
let user = this.users.get(pending.email);
|
|
113
112
|
if (!user) {
|
|
114
|
-
user = { id: randomUUID(), email: pending.email, password: '' };
|
|
113
|
+
user = { id: globalThis.crypto.randomUUID(), email: pending.email, password: '' };
|
|
115
114
|
this.users.set(pending.email, user);
|
|
116
115
|
}
|
|
117
116
|
return this.issueSession(user);
|
|
@@ -123,7 +122,7 @@ export class FakeAuthStrategy implements AuthStrategy {
|
|
|
123
122
|
getLastOAuthChallenge(provider: OAuthProvider, email: string): { code: string; state: string } {
|
|
124
123
|
if (!this.lastOAuthState) throw new Error('no startOAuth called yet');
|
|
125
124
|
const state = this.lastOAuthState;
|
|
126
|
-
const code = randomUUID();
|
|
125
|
+
const code = globalThis.crypto.randomUUID();
|
|
127
126
|
this.oauthStates.set(state, { provider, email });
|
|
128
127
|
this.oauthCodes.set(code, state);
|
|
129
128
|
return { code, state };
|
|
@@ -137,8 +136,8 @@ export class FakeAuthStrategy implements AuthStrategy {
|
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
private issueSession(user: StoredUser): AuthSession {
|
|
140
|
-
const accessToken = randomUUID();
|
|
141
|
-
const refreshToken = randomUUID();
|
|
139
|
+
const accessToken = globalThis.crypto.randomUUID();
|
|
140
|
+
const refreshToken = globalThis.crypto.randomUUID();
|
|
142
141
|
this.tokensToUid.set(accessToken, user.id);
|
|
143
142
|
this.refreshToUid.set(refreshToken, user.id);
|
|
144
143
|
return {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
1
|
import type { FileInput, StorageRef, StorageStrategy } from '../storage';
|
|
3
2
|
|
|
4
3
|
interface StoredFile {
|
|
@@ -13,7 +12,7 @@ export class FakeStorageStrategy implements StorageStrategy {
|
|
|
13
12
|
private readonly files = new Map<string, StoredFile>();
|
|
14
13
|
|
|
15
14
|
async upload(userId: string, file: FileInput): Promise<StorageRef> {
|
|
16
|
-
const path = `${userId}/${randomUUID()}-${file.filename}`;
|
|
15
|
+
const path = `${userId}/${globalThis.crypto.randomUUID()}-${file.filename}`;
|
|
17
16
|
const ref: StorageRef = { bucket: this.bucket, path };
|
|
18
17
|
this.files.set(this.key(ref), {
|
|
19
18
|
ownerId: userId,
|