@idevconn/create-icore 0.9.1 → 0.9.3
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 +157 -17
- package/dist/index.cjs +148 -11
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +150 -13
- package/package.json +1 -1
- package/templates/libs/shared/package.json +7 -3
package/dist/cli.js
CHANGED
|
@@ -336,7 +336,7 @@ Re-run with @latest to refresh:
|
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
// src/lib/scaffold.ts
|
|
339
|
-
import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile8, stat, writeFile as writeFile8, rm as rm4 } from "fs/promises";
|
|
339
|
+
import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile8, rmdir as rmdir2, stat, writeFile as writeFile8, rm as rm4 } from "fs/promises";
|
|
340
340
|
import { readFileSync } from "fs";
|
|
341
341
|
import { join as join9 } from "path";
|
|
342
342
|
import { spawnSync } from "child_process";
|
|
@@ -439,6 +439,30 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
441
|
delete pkg.pnpm;
|
|
442
|
+
const noMs = opts.authProvider === "none" && opts.upload === "none" && opts.payment === "none" && opts.jobs === "none" && opts.example === "none";
|
|
443
|
+
const ws = pkg.workspaces;
|
|
444
|
+
if (ws) {
|
|
445
|
+
pkg.workspaces = ws.filter((entry) => {
|
|
446
|
+
if (entry === "apps/templates/*") return false;
|
|
447
|
+
if (entry === "apps/microservices/*" && noMs) return false;
|
|
448
|
+
if (entry === "libs/auth-strategies/*" && opts.authProvider === "none") return false;
|
|
449
|
+
if (entry === "libs/storage-strategies/*" && opts.upload === "none") return false;
|
|
450
|
+
if (entry === "libs/db-strategies/*" && opts.dbProvider === "none") return false;
|
|
451
|
+
return true;
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
const rootDeps = pkg.dependencies ?? {};
|
|
455
|
+
const rootDevDeps = pkg.devDependencies ?? {};
|
|
456
|
+
if (opts.authProvider === "none") {
|
|
457
|
+
delete rootDeps["cookie-parser"];
|
|
458
|
+
delete rootDevDeps["@types/cookie-parser"];
|
|
459
|
+
}
|
|
460
|
+
if (opts.upload === "none") {
|
|
461
|
+
delete rootDevDeps["@types/multer"];
|
|
462
|
+
}
|
|
463
|
+
if (noMs) {
|
|
464
|
+
delete rootDeps["@nestjs/microservices"];
|
|
465
|
+
}
|
|
442
466
|
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
443
467
|
}
|
|
444
468
|
async function writeAuthEnv(targetDir, opts) {
|
|
@@ -561,6 +585,28 @@ async function removeFirebaseAdminLib(targetDir) {
|
|
|
561
585
|
"@icore/firebase-admin"
|
|
562
586
|
]);
|
|
563
587
|
}
|
|
588
|
+
async function removeStrategiesLib(targetDir) {
|
|
589
|
+
await rm(join3(targetDir, "libs/shared/src/strategies"), { recursive: true, force: true });
|
|
590
|
+
await rm(join3(targetDir, "libs/shared/src/testing.ts"), { force: true });
|
|
591
|
+
await rm(join3(targetDir, "libs/shared/src/transport.ts"), { force: true });
|
|
592
|
+
const indexPath = join3(targetDir, "libs/shared/src/index.ts");
|
|
593
|
+
try {
|
|
594
|
+
const src = await readFile4(indexPath, "utf8");
|
|
595
|
+
await writeFile2(
|
|
596
|
+
indexPath,
|
|
597
|
+
src.replace(/^export \* from '\.\/strategies';\n/m, "").replace(/^export \* from '\.\/transport';\n?/m, "")
|
|
598
|
+
);
|
|
599
|
+
} catch {
|
|
600
|
+
}
|
|
601
|
+
const pkgPath = join3(targetDir, "libs/shared/package.json");
|
|
602
|
+
try {
|
|
603
|
+
const pkg = JSON.parse(await readFile4(pkgPath, "utf8"));
|
|
604
|
+
if (pkg.exports) delete pkg.exports["./testing"];
|
|
605
|
+
if (pkg.dependencies) delete pkg.dependencies["@nestjs/microservices"];
|
|
606
|
+
await writeFile2(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
607
|
+
} catch {
|
|
608
|
+
}
|
|
609
|
+
}
|
|
564
610
|
async function removeAuthStack(targetDir) {
|
|
565
611
|
const rmPaths = [
|
|
566
612
|
"apps/microservices/auth",
|
|
@@ -570,6 +616,7 @@ async function removeAuthStack(targetDir) {
|
|
|
570
616
|
"apps/api/src/app/auth",
|
|
571
617
|
"apps/api/src/app/profile",
|
|
572
618
|
"apps/api/src/app/abilities",
|
|
619
|
+
"libs/shared/src/abilities",
|
|
573
620
|
"apps/client/src/components/auth",
|
|
574
621
|
"apps/client/src/routes/login.tsx",
|
|
575
622
|
"apps/client/src/routes/auth.callback.tsx",
|
|
@@ -601,7 +648,17 @@ async function removeAuthStack(targetDir) {
|
|
|
601
648
|
]) {
|
|
602
649
|
await stripTsconfigPath(targetDir, alias);
|
|
603
650
|
}
|
|
604
|
-
await stripDeps(join3(targetDir, "apps/api/package.json"), [
|
|
651
|
+
await stripDeps(join3(targetDir, "apps/api/package.json"), [
|
|
652
|
+
"@icore/auth-client",
|
|
653
|
+
"cookie-parser"
|
|
654
|
+
]);
|
|
655
|
+
const gatewayMainPath = join3(targetDir, "apps/api/src/main.ts");
|
|
656
|
+
try {
|
|
657
|
+
const src = await readFile4(gatewayMainPath, "utf8");
|
|
658
|
+
const next = src.replace(/^import cookieParser from 'cookie-parser';\n/m, "").replace(/^\s*app\.use\(cookieParser\(\)\);\n/m, "");
|
|
659
|
+
await writeFile2(gatewayMainPath, next);
|
|
660
|
+
} catch {
|
|
661
|
+
}
|
|
605
662
|
const gatewayEnv = join3(targetDir, "apps/api/.env");
|
|
606
663
|
try {
|
|
607
664
|
const env = await readFile4(gatewayEnv, "utf8");
|
|
@@ -616,6 +673,55 @@ async function removeAuthStack(targetDir) {
|
|
|
616
673
|
await writeFile2(composePath, next);
|
|
617
674
|
} catch {
|
|
618
675
|
}
|
|
676
|
+
for (const rel of ["libs/shared/src/index.ts", "libs/shared/src/client.ts"]) {
|
|
677
|
+
const sharedIndexPath = join3(targetDir, rel);
|
|
678
|
+
try {
|
|
679
|
+
const src = await readFile4(sharedIndexPath, "utf8");
|
|
680
|
+
await writeFile2(sharedIndexPath, src.replace(/^export \* from '\.\/abilities';\n?/m, ""));
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const routesIndexPath = join3(targetDir, "apps/client/src/routes/index.tsx");
|
|
685
|
+
try {
|
|
686
|
+
const src = await readFile4(routesIndexPath, "utf8");
|
|
687
|
+
await writeFile2(
|
|
688
|
+
routesIndexPath,
|
|
689
|
+
src.replace(/ctaHref="\/login"/, 'ctaHref="/dashboard"').replace(/ctaLabel="Log in →"/, 'ctaLabel="Dashboard \u2192"')
|
|
690
|
+
);
|
|
691
|
+
} catch {
|
|
692
|
+
}
|
|
693
|
+
const mainTsxPath = join3(targetDir, "apps/client/src/main.tsx");
|
|
694
|
+
try {
|
|
695
|
+
const src = await readFile4(mainTsxPath, "utf8");
|
|
696
|
+
const next = src.replace(/\n {2}onUnauthorized: \(\) => router\.navigate\(\{ to: '\/login' \}\),/, "").replace(/^\s*AbilityProvider,\n/m, "").replace(/^\s*<AbilityProvider>\n/m, "").replace(/^\s*<\/AbilityProvider>\n/m, "");
|
|
697
|
+
await writeFile2(mainTsxPath, next);
|
|
698
|
+
} catch {
|
|
699
|
+
}
|
|
700
|
+
await rm(join3(targetDir, "libs/template-shared/src/lib/abilities"), {
|
|
701
|
+
recursive: true,
|
|
702
|
+
force: true
|
|
703
|
+
});
|
|
704
|
+
const templateSharedIndexPath = join3(targetDir, "libs/template-shared/src/index.ts");
|
|
705
|
+
try {
|
|
706
|
+
const src = await readFile4(templateSharedIndexPath, "utf8");
|
|
707
|
+
await writeFile2(
|
|
708
|
+
templateSharedIndexPath,
|
|
709
|
+
src.replace(/^export \* from '\.\/lib\/abilities\/ability-provider\.js';\n/m, "")
|
|
710
|
+
);
|
|
711
|
+
} catch {
|
|
712
|
+
}
|
|
713
|
+
const headerPath = join3(targetDir, "apps/client/src/components/layout/LayoutHeader.tsx");
|
|
714
|
+
try {
|
|
715
|
+
const src = await readFile4(headerPath, "utf8");
|
|
716
|
+
await writeFile2(
|
|
717
|
+
headerPath,
|
|
718
|
+
src.replace(/^import \{ useTranslation \} from 'react-i18next';\n/m, "").replace(/^import \{ useNavigate \} from '@tanstack\/react-router';\n/m, "").replace(/^import \{ LogOut \} from 'lucide-react';\n/m, "").replace(/^import \{ Button \} from '\.\.\/ui\/button';\n/m, "").replace(
|
|
719
|
+
/import \{ useAuthStore, setStoredLocale, type IcoreLocale \} from '@icore\/template-shared';/,
|
|
720
|
+
"import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';"
|
|
721
|
+
).replace(/^ {2}const \{ t \} = useTranslation\(\);\n/m, "").replace(/^ {2}const navigate = useNavigate\(\);\n/m, "").replace(/^ {2}const user = useAuthStore\(\(s\) => s\.user\);\n/m, "").replace(/^ {2}const logout = useAuthStore\(\(s\) => s\.logout\);\n/m, "").replace(/\n {2}function handleLogout\(\) \{[\s\S]*?\n {2}\}\n(?=\n {2}return)/m, "\n").replace(/\n {8}<div className="hidden sm:flex[\s\S]*?\n {8}<\/div>\n/m, "\n").replace(/\n {8}<Button[\s\S]*?sm:hidden[\s\S]*?\n {8}<\/Button>\n/m, "\n")
|
|
722
|
+
);
|
|
723
|
+
} catch {
|
|
724
|
+
}
|
|
619
725
|
}
|
|
620
726
|
async function removeUploadStack(targetDir) {
|
|
621
727
|
const paths = [
|
|
@@ -623,7 +729,8 @@ async function removeUploadStack(targetDir) {
|
|
|
623
729
|
"apps/microservices/upload-e2e",
|
|
624
730
|
"libs/storage-strategies",
|
|
625
731
|
"libs/upload-client",
|
|
626
|
-
"apps/api/src/app/storage"
|
|
732
|
+
"apps/api/src/app/storage",
|
|
733
|
+
"Dockerfile.ms-upload"
|
|
627
734
|
];
|
|
628
735
|
for (const p3 of paths) {
|
|
629
736
|
await rm(join3(targetDir, p3), { recursive: true, force: true });
|
|
@@ -648,10 +755,17 @@ async function removeUploadStack(targetDir) {
|
|
|
648
755
|
"@icore/upload-client",
|
|
649
756
|
"@types/multer"
|
|
650
757
|
]);
|
|
758
|
+
const uploadComposePath = join3(targetDir, "docker-compose.yml");
|
|
759
|
+
try {
|
|
760
|
+
const compose = await readFile4(uploadComposePath, "utf8");
|
|
761
|
+
const next = compose.replace(/\n {2}upload:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}upload:\n {8}condition: service_started/g, "").replace(/\n {6}UPLOAD_TRANSPORT:[^\n]*/g, "").replace(/\n {6}UPLOAD_REDIS_URL:[^\n]*/g, "");
|
|
762
|
+
await writeFile2(uploadComposePath, next);
|
|
763
|
+
} catch {
|
|
764
|
+
}
|
|
651
765
|
}
|
|
652
766
|
|
|
653
767
|
// src/manifest/wire-features.ts
|
|
654
|
-
import { readFile as readFile6, writeFile as writeFile4, rm as rm3 } from "fs/promises";
|
|
768
|
+
import { readFile as readFile6, writeFile as writeFile4, rm as rm3, rmdir, unlink } from "fs/promises";
|
|
655
769
|
import { join as join5 } from "path";
|
|
656
770
|
|
|
657
771
|
// src/manifest/index.ts
|
|
@@ -922,6 +1036,22 @@ async function cleanupUnusedFeatures(targetDir, opts) {
|
|
|
922
1036
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
923
1037
|
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
924
1038
|
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
1039
|
+
if (key === "jobs") {
|
|
1040
|
+
try {
|
|
1041
|
+
await unlink(join5(targetDir, "libs/shared/src/jobs.ts"));
|
|
1042
|
+
} catch {
|
|
1043
|
+
}
|
|
1044
|
+
const sharedIdx = join5(targetDir, "libs/shared/src/index.ts");
|
|
1045
|
+
try {
|
|
1046
|
+
const src = await readFile6(sharedIdx, "utf8");
|
|
1047
|
+
await writeFile4(sharedIdx, src.replace(/^export \* from '\.\/jobs';\n/m, ""));
|
|
1048
|
+
} catch {
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
try {
|
|
1053
|
+
await rmdir(join5(targetDir, "apps/client/src/queries"));
|
|
1054
|
+
} catch {
|
|
925
1055
|
}
|
|
926
1056
|
}
|
|
927
1057
|
|
|
@@ -1172,7 +1302,8 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
1172
1302
|
const pm = opts.packageManager;
|
|
1173
1303
|
const nx = pm === "npm" ? "npx nx" : `${pm} nx`;
|
|
1174
1304
|
const devCmd = pmRun(pm, "dev");
|
|
1175
|
-
const activeMSes = [
|
|
1305
|
+
const activeMSes = [];
|
|
1306
|
+
if (opts.authProvider !== "none") activeMSes.push("auth (port 4001)");
|
|
1176
1307
|
if (opts.upload !== "none") activeMSes.push(`upload (port 4002)`);
|
|
1177
1308
|
if (opts.payment !== "none") activeMSes.push(`payment (port 4003)`);
|
|
1178
1309
|
if (opts.example !== "none") activeMSes.push(`notes (port 4004)`);
|
|
@@ -1201,9 +1332,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
1201
1332
|
|
|
1202
1333
|
\`\`\`bash
|
|
1203
1334
|
# 1. Fill in provider credentials
|
|
1204
|
-
# apps/microservices/auth/.env
|
|
1205
|
-
# apps/microservices/upload/.env (if upload is enabled)
|
|
1206
|
-
# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
|
|
1335
|
+
${opts.authProvider !== "none" ? "# apps/microservices/auth/.env\n" : ""}${opts.upload !== "none" ? "# apps/microservices/upload/.env\n" : ""}# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
|
|
1207
1336
|
|
|
1208
1337
|
# 2. Start everything
|
|
1209
1338
|
${devCmd}
|
|
@@ -1299,10 +1428,10 @@ ${nx} g @nx/nest:resource # generate NestJS resource
|
|
|
1299
1428
|
|
|
1300
1429
|
| File | Key vars |
|
|
1301
1430
|
|------|----------|
|
|
1302
|
-
|
|
1303
|
-
${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
|
|
1304
|
-
` : ""}
|
|
1305
|
-
| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
|
|
1431
|
+
${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PROVIDER=${opts.authProvider}\`, ${opts.authProvider === "supabase" ? "`SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`" : "`FB_ADMIN_*`, `FIREBASE_WEB_API_KEY`"} |
|
|
1432
|
+
` : ""}${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
|
|
1433
|
+
` : ""}${opts.example !== "none" ? `| \`apps/microservices/notes/.env\` | \`DB_PROVIDER=${opts.dbProvider}\`, DB creds |
|
|
1434
|
+
` : ""}| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
|
|
1306
1435
|
|
|
1307
1436
|
## Testing
|
|
1308
1437
|
|
|
@@ -1445,7 +1574,8 @@ function runInstall(cwd, pm) {
|
|
|
1445
1574
|
spawnSync("pnpm", ["install"], { cwd, stdio: "inherit" });
|
|
1446
1575
|
}
|
|
1447
1576
|
}
|
|
1448
|
-
async function scaffold(
|
|
1577
|
+
async function scaffold(rawOpts, templatesDir2) {
|
|
1578
|
+
const opts = rawOpts.authProvider === "none" && rawOpts.example !== "none" ? { ...rawOpts, example: "none" } : rawOpts;
|
|
1449
1579
|
await copyTree(templatesDir2, opts.targetDir);
|
|
1450
1580
|
await rewriteRootPackageJson(opts.targetDir, opts);
|
|
1451
1581
|
if (opts.authProvider !== "none") await writeAuthEnv(opts.targetDir, opts);
|
|
@@ -1477,6 +1607,13 @@ async function scaffold(opts, templatesDir2) {
|
|
|
1477
1607
|
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1478
1608
|
}
|
|
1479
1609
|
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1610
|
+
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none") {
|
|
1611
|
+
await removeStrategiesLib(opts.targetDir);
|
|
1612
|
+
}
|
|
1613
|
+
try {
|
|
1614
|
+
await rmdir2(join9(opts.targetDir, "apps/microservices"));
|
|
1615
|
+
} catch {
|
|
1616
|
+
}
|
|
1480
1617
|
await writeBlueprintJson(opts.targetDir, opts);
|
|
1481
1618
|
await writeServiceBlueprints(opts.targetDir, opts);
|
|
1482
1619
|
if (opts.packageManager === "yarn") {
|
|
@@ -1525,11 +1662,14 @@ async function main() {
|
|
|
1525
1662
|
p2.log.info(`Next:`);
|
|
1526
1663
|
p2.log.info(` cd ${opts.projectName}`);
|
|
1527
1664
|
if (!opts.install) p2.log.info(` ${opts.packageManager} install`);
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
);
|
|
1665
|
+
const services = ["gateway", "client"];
|
|
1666
|
+
if (opts.authProvider !== "none") services.splice(1, 0, "auth MS");
|
|
1667
|
+
if (opts.upload !== "none") services.splice(-1, 0, "upload MS");
|
|
1668
|
+
p2.log.info(` ${pmRun(opts.packageManager, "dev")} # ${services.join(" + ")}`);
|
|
1531
1669
|
p2.log.info(` open http://localhost:4200`);
|
|
1532
|
-
|
|
1670
|
+
if (opts.authProvider !== "none") {
|
|
1671
|
+
p2.log.info(` edit apps/microservices/auth/.env to plug in real ${opts.authProvider} creds`);
|
|
1672
|
+
}
|
|
1533
1673
|
}
|
|
1534
1674
|
main().catch((err) => {
|
|
1535
1675
|
p2.log.error(err instanceof Error ? err.message : String(err));
|
package/dist/index.cjs
CHANGED
|
@@ -150,6 +150,30 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
delete pkg.pnpm;
|
|
153
|
+
const noMs = opts.authProvider === "none" && opts.upload === "none" && opts.payment === "none" && opts.jobs === "none" && opts.example === "none";
|
|
154
|
+
const ws = pkg.workspaces;
|
|
155
|
+
if (ws) {
|
|
156
|
+
pkg.workspaces = ws.filter((entry) => {
|
|
157
|
+
if (entry === "apps/templates/*") return false;
|
|
158
|
+
if (entry === "apps/microservices/*" && noMs) return false;
|
|
159
|
+
if (entry === "libs/auth-strategies/*" && opts.authProvider === "none") return false;
|
|
160
|
+
if (entry === "libs/storage-strategies/*" && opts.upload === "none") return false;
|
|
161
|
+
if (entry === "libs/db-strategies/*" && opts.dbProvider === "none") return false;
|
|
162
|
+
return true;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const rootDeps = pkg.dependencies ?? {};
|
|
166
|
+
const rootDevDeps = pkg.devDependencies ?? {};
|
|
167
|
+
if (opts.authProvider === "none") {
|
|
168
|
+
delete rootDeps["cookie-parser"];
|
|
169
|
+
delete rootDevDeps["@types/cookie-parser"];
|
|
170
|
+
}
|
|
171
|
+
if (opts.upload === "none") {
|
|
172
|
+
delete rootDevDeps["@types/multer"];
|
|
173
|
+
}
|
|
174
|
+
if (noMs) {
|
|
175
|
+
delete rootDeps["@nestjs/microservices"];
|
|
176
|
+
}
|
|
153
177
|
await (0, import_promises.writeFile)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
154
178
|
}
|
|
155
179
|
async function writeAuthEnv(targetDir, opts) {
|
|
@@ -272,6 +296,28 @@ async function removeFirebaseAdminLib(targetDir) {
|
|
|
272
296
|
"@icore/firebase-admin"
|
|
273
297
|
]);
|
|
274
298
|
}
|
|
299
|
+
async function removeStrategiesLib(targetDir) {
|
|
300
|
+
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/shared/src/strategies"), { recursive: true, force: true });
|
|
301
|
+
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/shared/src/testing.ts"), { force: true });
|
|
302
|
+
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/shared/src/transport.ts"), { force: true });
|
|
303
|
+
const indexPath = (0, import_node_path2.join)(targetDir, "libs/shared/src/index.ts");
|
|
304
|
+
try {
|
|
305
|
+
const src = await (0, import_promises2.readFile)(indexPath, "utf8");
|
|
306
|
+
await (0, import_promises2.writeFile)(
|
|
307
|
+
indexPath,
|
|
308
|
+
src.replace(/^export \* from '\.\/strategies';\n/m, "").replace(/^export \* from '\.\/transport';\n?/m, "")
|
|
309
|
+
);
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
312
|
+
const pkgPath = (0, import_node_path2.join)(targetDir, "libs/shared/package.json");
|
|
313
|
+
try {
|
|
314
|
+
const pkg = JSON.parse(await (0, import_promises2.readFile)(pkgPath, "utf8"));
|
|
315
|
+
if (pkg.exports) delete pkg.exports["./testing"];
|
|
316
|
+
if (pkg.dependencies) delete pkg.dependencies["@nestjs/microservices"];
|
|
317
|
+
await (0, import_promises2.writeFile)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
}
|
|
275
321
|
async function removeAuthStack(targetDir) {
|
|
276
322
|
const rmPaths = [
|
|
277
323
|
"apps/microservices/auth",
|
|
@@ -281,6 +327,7 @@ async function removeAuthStack(targetDir) {
|
|
|
281
327
|
"apps/api/src/app/auth",
|
|
282
328
|
"apps/api/src/app/profile",
|
|
283
329
|
"apps/api/src/app/abilities",
|
|
330
|
+
"libs/shared/src/abilities",
|
|
284
331
|
"apps/client/src/components/auth",
|
|
285
332
|
"apps/client/src/routes/login.tsx",
|
|
286
333
|
"apps/client/src/routes/auth.callback.tsx",
|
|
@@ -312,7 +359,17 @@ async function removeAuthStack(targetDir) {
|
|
|
312
359
|
]) {
|
|
313
360
|
await stripTsconfigPath(targetDir, alias);
|
|
314
361
|
}
|
|
315
|
-
await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), [
|
|
362
|
+
await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), [
|
|
363
|
+
"@icore/auth-client",
|
|
364
|
+
"cookie-parser"
|
|
365
|
+
]);
|
|
366
|
+
const gatewayMainPath = (0, import_node_path2.join)(targetDir, "apps/api/src/main.ts");
|
|
367
|
+
try {
|
|
368
|
+
const src = await (0, import_promises2.readFile)(gatewayMainPath, "utf8");
|
|
369
|
+
const next = src.replace(/^import cookieParser from 'cookie-parser';\n/m, "").replace(/^\s*app\.use\(cookieParser\(\)\);\n/m, "");
|
|
370
|
+
await (0, import_promises2.writeFile)(gatewayMainPath, next);
|
|
371
|
+
} catch {
|
|
372
|
+
}
|
|
316
373
|
const gatewayEnv = (0, import_node_path2.join)(targetDir, "apps/api/.env");
|
|
317
374
|
try {
|
|
318
375
|
const env = await (0, import_promises2.readFile)(gatewayEnv, "utf8");
|
|
@@ -327,6 +384,55 @@ async function removeAuthStack(targetDir) {
|
|
|
327
384
|
await (0, import_promises2.writeFile)(composePath, next);
|
|
328
385
|
} catch {
|
|
329
386
|
}
|
|
387
|
+
for (const rel of ["libs/shared/src/index.ts", "libs/shared/src/client.ts"]) {
|
|
388
|
+
const sharedIndexPath = (0, import_node_path2.join)(targetDir, rel);
|
|
389
|
+
try {
|
|
390
|
+
const src = await (0, import_promises2.readFile)(sharedIndexPath, "utf8");
|
|
391
|
+
await (0, import_promises2.writeFile)(sharedIndexPath, src.replace(/^export \* from '\.\/abilities';\n?/m, ""));
|
|
392
|
+
} catch {
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const routesIndexPath = (0, import_node_path2.join)(targetDir, "apps/client/src/routes/index.tsx");
|
|
396
|
+
try {
|
|
397
|
+
const src = await (0, import_promises2.readFile)(routesIndexPath, "utf8");
|
|
398
|
+
await (0, import_promises2.writeFile)(
|
|
399
|
+
routesIndexPath,
|
|
400
|
+
src.replace(/ctaHref="\/login"/, 'ctaHref="/dashboard"').replace(/ctaLabel="Log in →"/, 'ctaLabel="Dashboard \u2192"')
|
|
401
|
+
);
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
404
|
+
const mainTsxPath = (0, import_node_path2.join)(targetDir, "apps/client/src/main.tsx");
|
|
405
|
+
try {
|
|
406
|
+
const src = await (0, import_promises2.readFile)(mainTsxPath, "utf8");
|
|
407
|
+
const next = src.replace(/\n {2}onUnauthorized: \(\) => router\.navigate\(\{ to: '\/login' \}\),/, "").replace(/^\s*AbilityProvider,\n/m, "").replace(/^\s*<AbilityProvider>\n/m, "").replace(/^\s*<\/AbilityProvider>\n/m, "");
|
|
408
|
+
await (0, import_promises2.writeFile)(mainTsxPath, next);
|
|
409
|
+
} catch {
|
|
410
|
+
}
|
|
411
|
+
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/template-shared/src/lib/abilities"), {
|
|
412
|
+
recursive: true,
|
|
413
|
+
force: true
|
|
414
|
+
});
|
|
415
|
+
const templateSharedIndexPath = (0, import_node_path2.join)(targetDir, "libs/template-shared/src/index.ts");
|
|
416
|
+
try {
|
|
417
|
+
const src = await (0, import_promises2.readFile)(templateSharedIndexPath, "utf8");
|
|
418
|
+
await (0, import_promises2.writeFile)(
|
|
419
|
+
templateSharedIndexPath,
|
|
420
|
+
src.replace(/^export \* from '\.\/lib\/abilities\/ability-provider\.js';\n/m, "")
|
|
421
|
+
);
|
|
422
|
+
} catch {
|
|
423
|
+
}
|
|
424
|
+
const headerPath = (0, import_node_path2.join)(targetDir, "apps/client/src/components/layout/LayoutHeader.tsx");
|
|
425
|
+
try {
|
|
426
|
+
const src = await (0, import_promises2.readFile)(headerPath, "utf8");
|
|
427
|
+
await (0, import_promises2.writeFile)(
|
|
428
|
+
headerPath,
|
|
429
|
+
src.replace(/^import \{ useTranslation \} from 'react-i18next';\n/m, "").replace(/^import \{ useNavigate \} from '@tanstack\/react-router';\n/m, "").replace(/^import \{ LogOut \} from 'lucide-react';\n/m, "").replace(/^import \{ Button \} from '\.\.\/ui\/button';\n/m, "").replace(
|
|
430
|
+
/import \{ useAuthStore, setStoredLocale, type IcoreLocale \} from '@icore\/template-shared';/,
|
|
431
|
+
"import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';"
|
|
432
|
+
).replace(/^ {2}const \{ t \} = useTranslation\(\);\n/m, "").replace(/^ {2}const navigate = useNavigate\(\);\n/m, "").replace(/^ {2}const user = useAuthStore\(\(s\) => s\.user\);\n/m, "").replace(/^ {2}const logout = useAuthStore\(\(s\) => s\.logout\);\n/m, "").replace(/\n {2}function handleLogout\(\) \{[\s\S]*?\n {2}\}\n(?=\n {2}return)/m, "\n").replace(/\n {8}<div className="hidden sm:flex[\s\S]*?\n {8}<\/div>\n/m, "\n").replace(/\n {8}<Button[\s\S]*?sm:hidden[\s\S]*?\n {8}<\/Button>\n/m, "\n")
|
|
433
|
+
);
|
|
434
|
+
} catch {
|
|
435
|
+
}
|
|
330
436
|
}
|
|
331
437
|
async function removeUploadStack(targetDir) {
|
|
332
438
|
const paths = [
|
|
@@ -334,7 +440,8 @@ async function removeUploadStack(targetDir) {
|
|
|
334
440
|
"apps/microservices/upload-e2e",
|
|
335
441
|
"libs/storage-strategies",
|
|
336
442
|
"libs/upload-client",
|
|
337
|
-
"apps/api/src/app/storage"
|
|
443
|
+
"apps/api/src/app/storage",
|
|
444
|
+
"Dockerfile.ms-upload"
|
|
338
445
|
];
|
|
339
446
|
for (const p2 of paths) {
|
|
340
447
|
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
|
|
@@ -359,6 +466,13 @@ async function removeUploadStack(targetDir) {
|
|
|
359
466
|
"@icore/upload-client",
|
|
360
467
|
"@types/multer"
|
|
361
468
|
]);
|
|
469
|
+
const uploadComposePath = (0, import_node_path2.join)(targetDir, "docker-compose.yml");
|
|
470
|
+
try {
|
|
471
|
+
const compose = await (0, import_promises2.readFile)(uploadComposePath, "utf8");
|
|
472
|
+
const next = compose.replace(/\n {2}upload:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}upload:\n {8}condition: service_started/g, "").replace(/\n {6}UPLOAD_TRANSPORT:[^\n]*/g, "").replace(/\n {6}UPLOAD_REDIS_URL:[^\n]*/g, "");
|
|
473
|
+
await (0, import_promises2.writeFile)(uploadComposePath, next);
|
|
474
|
+
} catch {
|
|
475
|
+
}
|
|
362
476
|
}
|
|
363
477
|
|
|
364
478
|
// src/manifest/wire-features.ts
|
|
@@ -633,6 +747,22 @@ async function cleanupUnusedFeatures(targetDir, opts) {
|
|
|
633
747
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
634
748
|
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
635
749
|
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
750
|
+
if (key === "jobs") {
|
|
751
|
+
try {
|
|
752
|
+
await (0, import_promises4.unlink)((0, import_node_path4.join)(targetDir, "libs/shared/src/jobs.ts"));
|
|
753
|
+
} catch {
|
|
754
|
+
}
|
|
755
|
+
const sharedIdx = (0, import_node_path4.join)(targetDir, "libs/shared/src/index.ts");
|
|
756
|
+
try {
|
|
757
|
+
const src = await (0, import_promises4.readFile)(sharedIdx, "utf8");
|
|
758
|
+
await (0, import_promises4.writeFile)(sharedIdx, src.replace(/^export \* from '\.\/jobs';\n/m, ""));
|
|
759
|
+
} catch {
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
try {
|
|
764
|
+
await (0, import_promises4.rmdir)((0, import_node_path4.join)(targetDir, "apps/client/src/queries"));
|
|
765
|
+
} catch {
|
|
636
766
|
}
|
|
637
767
|
}
|
|
638
768
|
|
|
@@ -876,7 +1006,8 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
876
1006
|
const pm = opts.packageManager;
|
|
877
1007
|
const nx = pm === "npm" ? "npx nx" : `${pm} nx`;
|
|
878
1008
|
const devCmd = pmRun(pm, "dev");
|
|
879
|
-
const activeMSes = [
|
|
1009
|
+
const activeMSes = [];
|
|
1010
|
+
if (opts.authProvider !== "none") activeMSes.push("auth (port 4001)");
|
|
880
1011
|
if (opts.upload !== "none") activeMSes.push(`upload (port 4002)`);
|
|
881
1012
|
if (opts.payment !== "none") activeMSes.push(`payment (port 4003)`);
|
|
882
1013
|
if (opts.example !== "none") activeMSes.push(`notes (port 4004)`);
|
|
@@ -905,9 +1036,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
905
1036
|
|
|
906
1037
|
\`\`\`bash
|
|
907
1038
|
# 1. Fill in provider credentials
|
|
908
|
-
# apps/microservices/auth/.env
|
|
909
|
-
# apps/microservices/upload/.env (if upload is enabled)
|
|
910
|
-
# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
|
|
1039
|
+
${opts.authProvider !== "none" ? "# apps/microservices/auth/.env\n" : ""}${opts.upload !== "none" ? "# apps/microservices/upload/.env\n" : ""}# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
|
|
911
1040
|
|
|
912
1041
|
# 2. Start everything
|
|
913
1042
|
${devCmd}
|
|
@@ -1003,10 +1132,10 @@ ${nx} g @nx/nest:resource # generate NestJS resource
|
|
|
1003
1132
|
|
|
1004
1133
|
| File | Key vars |
|
|
1005
1134
|
|------|----------|
|
|
1006
|
-
|
|
1007
|
-
${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
|
|
1008
|
-
` : ""}
|
|
1009
|
-
| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
|
|
1135
|
+
${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PROVIDER=${opts.authProvider}\`, ${opts.authProvider === "supabase" ? "`SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`" : "`FB_ADMIN_*`, `FIREBASE_WEB_API_KEY`"} |
|
|
1136
|
+
` : ""}${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
|
|
1137
|
+
` : ""}${opts.example !== "none" ? `| \`apps/microservices/notes/.env\` | \`DB_PROVIDER=${opts.dbProvider}\`, DB creds |
|
|
1138
|
+
` : ""}| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
|
|
1010
1139
|
|
|
1011
1140
|
## Testing
|
|
1012
1141
|
|
|
@@ -1149,7 +1278,8 @@ function runInstall(cwd, pm) {
|
|
|
1149
1278
|
(0, import_node_child_process.spawnSync)("pnpm", ["install"], { cwd, stdio: "inherit" });
|
|
1150
1279
|
}
|
|
1151
1280
|
}
|
|
1152
|
-
async function scaffold(
|
|
1281
|
+
async function scaffold(rawOpts, templatesDir) {
|
|
1282
|
+
const opts = rawOpts.authProvider === "none" && rawOpts.example !== "none" ? { ...rawOpts, example: "none" } : rawOpts;
|
|
1153
1283
|
await copyTree(templatesDir, opts.targetDir);
|
|
1154
1284
|
await rewriteRootPackageJson(opts.targetDir, opts);
|
|
1155
1285
|
if (opts.authProvider !== "none") await writeAuthEnv(opts.targetDir, opts);
|
|
@@ -1181,6 +1311,13 @@ async function scaffold(opts, templatesDir) {
|
|
|
1181
1311
|
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1182
1312
|
}
|
|
1183
1313
|
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1314
|
+
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none") {
|
|
1315
|
+
await removeStrategiesLib(opts.targetDir);
|
|
1316
|
+
}
|
|
1317
|
+
try {
|
|
1318
|
+
await (0, import_promises8.rmdir)((0, import_node_path8.join)(opts.targetDir, "apps/microservices"));
|
|
1319
|
+
} catch {
|
|
1320
|
+
}
|
|
1184
1321
|
await writeBlueprintJson(opts.targetDir, opts);
|
|
1185
1322
|
await writeServiceBlueprints(opts.targetDir, opts);
|
|
1186
1323
|
if (opts.packageManager === "yarn") {
|
package/dist/index.d.cts
CHANGED
|
@@ -30,7 +30,7 @@ interface CreateIcoreOptions {
|
|
|
30
30
|
install: boolean;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
declare function scaffold(
|
|
33
|
+
declare function scaffold(rawOpts: CreateIcoreOptions, templatesDir: string): Promise<void>;
|
|
34
34
|
|
|
35
35
|
interface PromptInput {
|
|
36
36
|
argv: string[];
|
package/dist/index.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ interface CreateIcoreOptions {
|
|
|
30
30
|
install: boolean;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
declare function scaffold(
|
|
33
|
+
declare function scaffold(rawOpts: CreateIcoreOptions, templatesDir: string): Promise<void>;
|
|
34
34
|
|
|
35
35
|
interface PromptInput {
|
|
36
36
|
argv: string[];
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ function pmRun(pm, script) {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
// src/lib/scaffold.ts
|
|
7
|
-
import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile6, stat, writeFile as writeFile8, rm as rm4 } from "fs/promises";
|
|
7
|
+
import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile6, rmdir as rmdir2, stat, writeFile as writeFile8, rm as rm4 } from "fs/promises";
|
|
8
8
|
import { readFileSync } from "fs";
|
|
9
9
|
import { join as join8 } from "path";
|
|
10
10
|
import { spawnSync } from "child_process";
|
|
@@ -107,6 +107,30 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
delete pkg.pnpm;
|
|
110
|
+
const noMs = opts.authProvider === "none" && opts.upload === "none" && opts.payment === "none" && opts.jobs === "none" && opts.example === "none";
|
|
111
|
+
const ws = pkg.workspaces;
|
|
112
|
+
if (ws) {
|
|
113
|
+
pkg.workspaces = ws.filter((entry) => {
|
|
114
|
+
if (entry === "apps/templates/*") return false;
|
|
115
|
+
if (entry === "apps/microservices/*" && noMs) return false;
|
|
116
|
+
if (entry === "libs/auth-strategies/*" && opts.authProvider === "none") return false;
|
|
117
|
+
if (entry === "libs/storage-strategies/*" && opts.upload === "none") return false;
|
|
118
|
+
if (entry === "libs/db-strategies/*" && opts.dbProvider === "none") return false;
|
|
119
|
+
return true;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const rootDeps = pkg.dependencies ?? {};
|
|
123
|
+
const rootDevDeps = pkg.devDependencies ?? {};
|
|
124
|
+
if (opts.authProvider === "none") {
|
|
125
|
+
delete rootDeps["cookie-parser"];
|
|
126
|
+
delete rootDevDeps["@types/cookie-parser"];
|
|
127
|
+
}
|
|
128
|
+
if (opts.upload === "none") {
|
|
129
|
+
delete rootDevDeps["@types/multer"];
|
|
130
|
+
}
|
|
131
|
+
if (noMs) {
|
|
132
|
+
delete rootDeps["@nestjs/microservices"];
|
|
133
|
+
}
|
|
110
134
|
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
111
135
|
}
|
|
112
136
|
async function writeAuthEnv(targetDir, opts) {
|
|
@@ -229,6 +253,28 @@ async function removeFirebaseAdminLib(targetDir) {
|
|
|
229
253
|
"@icore/firebase-admin"
|
|
230
254
|
]);
|
|
231
255
|
}
|
|
256
|
+
async function removeStrategiesLib(targetDir) {
|
|
257
|
+
await rm(join2(targetDir, "libs/shared/src/strategies"), { recursive: true, force: true });
|
|
258
|
+
await rm(join2(targetDir, "libs/shared/src/testing.ts"), { force: true });
|
|
259
|
+
await rm(join2(targetDir, "libs/shared/src/transport.ts"), { force: true });
|
|
260
|
+
const indexPath = join2(targetDir, "libs/shared/src/index.ts");
|
|
261
|
+
try {
|
|
262
|
+
const src = await readFile2(indexPath, "utf8");
|
|
263
|
+
await writeFile2(
|
|
264
|
+
indexPath,
|
|
265
|
+
src.replace(/^export \* from '\.\/strategies';\n/m, "").replace(/^export \* from '\.\/transport';\n?/m, "")
|
|
266
|
+
);
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
const pkgPath = join2(targetDir, "libs/shared/package.json");
|
|
270
|
+
try {
|
|
271
|
+
const pkg = JSON.parse(await readFile2(pkgPath, "utf8"));
|
|
272
|
+
if (pkg.exports) delete pkg.exports["./testing"];
|
|
273
|
+
if (pkg.dependencies) delete pkg.dependencies["@nestjs/microservices"];
|
|
274
|
+
await writeFile2(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
275
|
+
} catch {
|
|
276
|
+
}
|
|
277
|
+
}
|
|
232
278
|
async function removeAuthStack(targetDir) {
|
|
233
279
|
const rmPaths = [
|
|
234
280
|
"apps/microservices/auth",
|
|
@@ -238,6 +284,7 @@ async function removeAuthStack(targetDir) {
|
|
|
238
284
|
"apps/api/src/app/auth",
|
|
239
285
|
"apps/api/src/app/profile",
|
|
240
286
|
"apps/api/src/app/abilities",
|
|
287
|
+
"libs/shared/src/abilities",
|
|
241
288
|
"apps/client/src/components/auth",
|
|
242
289
|
"apps/client/src/routes/login.tsx",
|
|
243
290
|
"apps/client/src/routes/auth.callback.tsx",
|
|
@@ -269,7 +316,17 @@ async function removeAuthStack(targetDir) {
|
|
|
269
316
|
]) {
|
|
270
317
|
await stripTsconfigPath(targetDir, alias);
|
|
271
318
|
}
|
|
272
|
-
await stripDeps(join2(targetDir, "apps/api/package.json"), [
|
|
319
|
+
await stripDeps(join2(targetDir, "apps/api/package.json"), [
|
|
320
|
+
"@icore/auth-client",
|
|
321
|
+
"cookie-parser"
|
|
322
|
+
]);
|
|
323
|
+
const gatewayMainPath = join2(targetDir, "apps/api/src/main.ts");
|
|
324
|
+
try {
|
|
325
|
+
const src = await readFile2(gatewayMainPath, "utf8");
|
|
326
|
+
const next = src.replace(/^import cookieParser from 'cookie-parser';\n/m, "").replace(/^\s*app\.use\(cookieParser\(\)\);\n/m, "");
|
|
327
|
+
await writeFile2(gatewayMainPath, next);
|
|
328
|
+
} catch {
|
|
329
|
+
}
|
|
273
330
|
const gatewayEnv = join2(targetDir, "apps/api/.env");
|
|
274
331
|
try {
|
|
275
332
|
const env = await readFile2(gatewayEnv, "utf8");
|
|
@@ -284,6 +341,55 @@ async function removeAuthStack(targetDir) {
|
|
|
284
341
|
await writeFile2(composePath, next);
|
|
285
342
|
} catch {
|
|
286
343
|
}
|
|
344
|
+
for (const rel of ["libs/shared/src/index.ts", "libs/shared/src/client.ts"]) {
|
|
345
|
+
const sharedIndexPath = join2(targetDir, rel);
|
|
346
|
+
try {
|
|
347
|
+
const src = await readFile2(sharedIndexPath, "utf8");
|
|
348
|
+
await writeFile2(sharedIndexPath, src.replace(/^export \* from '\.\/abilities';\n?/m, ""));
|
|
349
|
+
} catch {
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const routesIndexPath = join2(targetDir, "apps/client/src/routes/index.tsx");
|
|
353
|
+
try {
|
|
354
|
+
const src = await readFile2(routesIndexPath, "utf8");
|
|
355
|
+
await writeFile2(
|
|
356
|
+
routesIndexPath,
|
|
357
|
+
src.replace(/ctaHref="\/login"/, 'ctaHref="/dashboard"').replace(/ctaLabel="Log in →"/, 'ctaLabel="Dashboard \u2192"')
|
|
358
|
+
);
|
|
359
|
+
} catch {
|
|
360
|
+
}
|
|
361
|
+
const mainTsxPath = join2(targetDir, "apps/client/src/main.tsx");
|
|
362
|
+
try {
|
|
363
|
+
const src = await readFile2(mainTsxPath, "utf8");
|
|
364
|
+
const next = src.replace(/\n {2}onUnauthorized: \(\) => router\.navigate\(\{ to: '\/login' \}\),/, "").replace(/^\s*AbilityProvider,\n/m, "").replace(/^\s*<AbilityProvider>\n/m, "").replace(/^\s*<\/AbilityProvider>\n/m, "");
|
|
365
|
+
await writeFile2(mainTsxPath, next);
|
|
366
|
+
} catch {
|
|
367
|
+
}
|
|
368
|
+
await rm(join2(targetDir, "libs/template-shared/src/lib/abilities"), {
|
|
369
|
+
recursive: true,
|
|
370
|
+
force: true
|
|
371
|
+
});
|
|
372
|
+
const templateSharedIndexPath = join2(targetDir, "libs/template-shared/src/index.ts");
|
|
373
|
+
try {
|
|
374
|
+
const src = await readFile2(templateSharedIndexPath, "utf8");
|
|
375
|
+
await writeFile2(
|
|
376
|
+
templateSharedIndexPath,
|
|
377
|
+
src.replace(/^export \* from '\.\/lib\/abilities\/ability-provider\.js';\n/m, "")
|
|
378
|
+
);
|
|
379
|
+
} catch {
|
|
380
|
+
}
|
|
381
|
+
const headerPath = join2(targetDir, "apps/client/src/components/layout/LayoutHeader.tsx");
|
|
382
|
+
try {
|
|
383
|
+
const src = await readFile2(headerPath, "utf8");
|
|
384
|
+
await writeFile2(
|
|
385
|
+
headerPath,
|
|
386
|
+
src.replace(/^import \{ useTranslation \} from 'react-i18next';\n/m, "").replace(/^import \{ useNavigate \} from '@tanstack\/react-router';\n/m, "").replace(/^import \{ LogOut \} from 'lucide-react';\n/m, "").replace(/^import \{ Button \} from '\.\.\/ui\/button';\n/m, "").replace(
|
|
387
|
+
/import \{ useAuthStore, setStoredLocale, type IcoreLocale \} from '@icore\/template-shared';/,
|
|
388
|
+
"import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';"
|
|
389
|
+
).replace(/^ {2}const \{ t \} = useTranslation\(\);\n/m, "").replace(/^ {2}const navigate = useNavigate\(\);\n/m, "").replace(/^ {2}const user = useAuthStore\(\(s\) => s\.user\);\n/m, "").replace(/^ {2}const logout = useAuthStore\(\(s\) => s\.logout\);\n/m, "").replace(/\n {2}function handleLogout\(\) \{[\s\S]*?\n {2}\}\n(?=\n {2}return)/m, "\n").replace(/\n {8}<div className="hidden sm:flex[\s\S]*?\n {8}<\/div>\n/m, "\n").replace(/\n {8}<Button[\s\S]*?sm:hidden[\s\S]*?\n {8}<\/Button>\n/m, "\n")
|
|
390
|
+
);
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
287
393
|
}
|
|
288
394
|
async function removeUploadStack(targetDir) {
|
|
289
395
|
const paths = [
|
|
@@ -291,7 +397,8 @@ async function removeUploadStack(targetDir) {
|
|
|
291
397
|
"apps/microservices/upload-e2e",
|
|
292
398
|
"libs/storage-strategies",
|
|
293
399
|
"libs/upload-client",
|
|
294
|
-
"apps/api/src/app/storage"
|
|
400
|
+
"apps/api/src/app/storage",
|
|
401
|
+
"Dockerfile.ms-upload"
|
|
295
402
|
];
|
|
296
403
|
for (const p2 of paths) {
|
|
297
404
|
await rm(join2(targetDir, p2), { recursive: true, force: true });
|
|
@@ -316,10 +423,17 @@ async function removeUploadStack(targetDir) {
|
|
|
316
423
|
"@icore/upload-client",
|
|
317
424
|
"@types/multer"
|
|
318
425
|
]);
|
|
426
|
+
const uploadComposePath = join2(targetDir, "docker-compose.yml");
|
|
427
|
+
try {
|
|
428
|
+
const compose = await readFile2(uploadComposePath, "utf8");
|
|
429
|
+
const next = compose.replace(/\n {2}upload:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}upload:\n {8}condition: service_started/g, "").replace(/\n {6}UPLOAD_TRANSPORT:[^\n]*/g, "").replace(/\n {6}UPLOAD_REDIS_URL:[^\n]*/g, "");
|
|
430
|
+
await writeFile2(uploadComposePath, next);
|
|
431
|
+
} catch {
|
|
432
|
+
}
|
|
319
433
|
}
|
|
320
434
|
|
|
321
435
|
// src/manifest/wire-features.ts
|
|
322
|
-
import { readFile as readFile4, writeFile as writeFile4, rm as rm3 } from "fs/promises";
|
|
436
|
+
import { readFile as readFile4, writeFile as writeFile4, rm as rm3, rmdir, unlink } from "fs/promises";
|
|
323
437
|
import { join as join4 } from "path";
|
|
324
438
|
|
|
325
439
|
// src/manifest/index.ts
|
|
@@ -590,6 +704,22 @@ async function cleanupUnusedFeatures(targetDir, opts) {
|
|
|
590
704
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
591
705
|
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
592
706
|
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
707
|
+
if (key === "jobs") {
|
|
708
|
+
try {
|
|
709
|
+
await unlink(join4(targetDir, "libs/shared/src/jobs.ts"));
|
|
710
|
+
} catch {
|
|
711
|
+
}
|
|
712
|
+
const sharedIdx = join4(targetDir, "libs/shared/src/index.ts");
|
|
713
|
+
try {
|
|
714
|
+
const src = await readFile4(sharedIdx, "utf8");
|
|
715
|
+
await writeFile4(sharedIdx, src.replace(/^export \* from '\.\/jobs';\n/m, ""));
|
|
716
|
+
} catch {
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
try {
|
|
721
|
+
await rmdir(join4(targetDir, "apps/client/src/queries"));
|
|
722
|
+
} catch {
|
|
593
723
|
}
|
|
594
724
|
}
|
|
595
725
|
|
|
@@ -833,7 +963,8 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
833
963
|
const pm = opts.packageManager;
|
|
834
964
|
const nx = pm === "npm" ? "npx nx" : `${pm} nx`;
|
|
835
965
|
const devCmd = pmRun(pm, "dev");
|
|
836
|
-
const activeMSes = [
|
|
966
|
+
const activeMSes = [];
|
|
967
|
+
if (opts.authProvider !== "none") activeMSes.push("auth (port 4001)");
|
|
837
968
|
if (opts.upload !== "none") activeMSes.push(`upload (port 4002)`);
|
|
838
969
|
if (opts.payment !== "none") activeMSes.push(`payment (port 4003)`);
|
|
839
970
|
if (opts.example !== "none") activeMSes.push(`notes (port 4004)`);
|
|
@@ -862,9 +993,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
862
993
|
|
|
863
994
|
\`\`\`bash
|
|
864
995
|
# 1. Fill in provider credentials
|
|
865
|
-
# apps/microservices/auth/.env
|
|
866
|
-
# apps/microservices/upload/.env (if upload is enabled)
|
|
867
|
-
# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
|
|
996
|
+
${opts.authProvider !== "none" ? "# apps/microservices/auth/.env\n" : ""}${opts.upload !== "none" ? "# apps/microservices/upload/.env\n" : ""}# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
|
|
868
997
|
|
|
869
998
|
# 2. Start everything
|
|
870
999
|
${devCmd}
|
|
@@ -960,10 +1089,10 @@ ${nx} g @nx/nest:resource # generate NestJS resource
|
|
|
960
1089
|
|
|
961
1090
|
| File | Key vars |
|
|
962
1091
|
|------|----------|
|
|
963
|
-
|
|
964
|
-
${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
|
|
965
|
-
` : ""}
|
|
966
|
-
| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
|
|
1092
|
+
${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PROVIDER=${opts.authProvider}\`, ${opts.authProvider === "supabase" ? "`SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`" : "`FB_ADMIN_*`, `FIREBASE_WEB_API_KEY`"} |
|
|
1093
|
+
` : ""}${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
|
|
1094
|
+
` : ""}${opts.example !== "none" ? `| \`apps/microservices/notes/.env\` | \`DB_PROVIDER=${opts.dbProvider}\`, DB creds |
|
|
1095
|
+
` : ""}| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
|
|
967
1096
|
|
|
968
1097
|
## Testing
|
|
969
1098
|
|
|
@@ -1106,7 +1235,8 @@ function runInstall(cwd, pm) {
|
|
|
1106
1235
|
spawnSync("pnpm", ["install"], { cwd, stdio: "inherit" });
|
|
1107
1236
|
}
|
|
1108
1237
|
}
|
|
1109
|
-
async function scaffold(
|
|
1238
|
+
async function scaffold(rawOpts, templatesDir) {
|
|
1239
|
+
const opts = rawOpts.authProvider === "none" && rawOpts.example !== "none" ? { ...rawOpts, example: "none" } : rawOpts;
|
|
1110
1240
|
await copyTree(templatesDir, opts.targetDir);
|
|
1111
1241
|
await rewriteRootPackageJson(opts.targetDir, opts);
|
|
1112
1242
|
if (opts.authProvider !== "none") await writeAuthEnv(opts.targetDir, opts);
|
|
@@ -1138,6 +1268,13 @@ async function scaffold(opts, templatesDir) {
|
|
|
1138
1268
|
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1139
1269
|
}
|
|
1140
1270
|
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1271
|
+
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none") {
|
|
1272
|
+
await removeStrategiesLib(opts.targetDir);
|
|
1273
|
+
}
|
|
1274
|
+
try {
|
|
1275
|
+
await rmdir2(join8(opts.targetDir, "apps/microservices"));
|
|
1276
|
+
} catch {
|
|
1277
|
+
}
|
|
1141
1278
|
await writeBlueprintJson(opts.targetDir, opts);
|
|
1142
1279
|
await writeServiceBlueprints(opts.targetDir, opts);
|
|
1143
1280
|
if (opts.packageManager === "yarn") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idevconn/create-icore",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
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",
|
|
@@ -3,16 +3,20 @@
|
|
|
3
3
|
"version": "0.0.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "commonjs",
|
|
6
|
-
"main": "./src/index.
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
7
|
"types": "./src/index.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
10
|
+
"default": "./src/index.ts",
|
|
11
11
|
"types": "./src/index.ts"
|
|
12
12
|
},
|
|
13
13
|
"./client": {
|
|
14
|
-
"
|
|
14
|
+
"default": "./src/client.ts",
|
|
15
15
|
"types": "./src/client.ts"
|
|
16
|
+
},
|
|
17
|
+
"./testing": {
|
|
18
|
+
"default": "./src/testing.ts",
|
|
19
|
+
"types": "./src/testing.ts"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"dependencies": {
|