@idevconn/create-icore 0.9.3 → 0.10.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 +755 -190
- package/dist/index.cjs +778 -213
- package/dist/index.js +771 -206
- package/package.json +4 -4
- package/templates/.yarn/releases/{yarn-4.16.0.cjs → yarn-4.17.0.cjs} +326 -326
- package/templates/.yarnrc.yml +1 -1
- package/templates/apps/api/package.json +6 -6
- package/templates/apps/microservices/auth/package.json +4 -4
- package/templates/apps/microservices/jobs/package.json +5 -5
- package/templates/apps/microservices/notes/package.json +5 -5
- package/templates/apps/microservices/payment/package.json +4 -4
- package/templates/apps/microservices/upload/package.json +6 -6
- package/templates/apps/templates/client-antd/package.json +2 -2
- package/templates/apps/templates/client-mui/package.json +4 -4
- package/templates/apps/templates/client-shadcn/package.json +7 -7
- package/templates/apps/templates/client-shadcn/src/components/ui/button.tsx +1 -2
- package/templates/libs/auth-client/package.json +3 -3
- package/templates/libs/auth-strategies/firebase/package.json +5 -4
- package/templates/libs/auth-strategies/firebase/src/lib/firebase-auth.module.ts +2 -1
- package/templates/libs/auth-strategies/mongodb/package.json +4 -4
- package/templates/libs/auth-strategies/supabase/package.json +5 -5
- package/templates/libs/db-strategies/firestore/package.json +5 -4
- package/templates/libs/db-strategies/firestore/src/lib/firestore-db.module.ts +2 -1
- package/templates/libs/db-strategies/mongodb/package.json +3 -3
- package/templates/libs/db-strategies/supabase/package.json +5 -5
- package/templates/libs/firebase-admin/package.json +2 -2
- package/templates/libs/firebase-admin/src/lib/__tests__/firebase-admin.unit.test.ts +11 -11
- package/templates/libs/firebase-admin/src/lib/firebase-admin.ts +6 -6
- package/templates/libs/jobs-client/package.json +4 -4
- package/templates/libs/notes-client/package.json +3 -3
- package/templates/libs/payment-client/package.json +3 -3
- package/templates/libs/shared/package.json +2 -2
- package/templates/libs/storage-strategies/cloudinary/package.json +4 -4
- package/templates/libs/storage-strategies/firebase/package.json +5 -4
- package/templates/libs/storage-strategies/firebase/src/lib/firebase-storage.module.ts +4 -1
- package/templates/libs/storage-strategies/mongodb/package.json +3 -3
- package/templates/libs/storage-strategies/supabase/package.json +5 -5
- package/templates/libs/template-shared/package.json +8 -8
- package/templates/libs/upload-client/package.json +3 -3
- package/templates/libs/vite-plugins/package.json +3 -3
- package/templates/package.json +32 -32
- package/templates/tools/create-icore/_template-shell/package.json +32 -32
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { existsSync } from "fs";
|
|
5
5
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6
|
-
import { dirname as
|
|
6
|
+
import { dirname as dirname3, resolve as resolve2 } from "path";
|
|
7
7
|
import kleur from "kleur";
|
|
8
8
|
import * as p2 from "@clack/prompts";
|
|
9
9
|
|
|
@@ -336,9 +336,9 @@ Re-run with @latest to refresh:
|
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
// src/lib/scaffold.ts
|
|
339
|
-
import { copyFile, mkdir as
|
|
339
|
+
import { copyFile, mkdir as mkdir3, readdir as readdir2, readFile as readFile9, rmdir as rmdir2, stat, writeFile as writeFile9, rm as rm5 } from "fs/promises";
|
|
340
340
|
import { readFileSync } from "fs";
|
|
341
|
-
import { join as
|
|
341
|
+
import { join as join10 } from "path";
|
|
342
342
|
import { spawnSync } from "child_process";
|
|
343
343
|
|
|
344
344
|
// src/lib/scaffold-env.ts
|
|
@@ -504,6 +504,9 @@ async function writeGatewayEnv(targetDir, opts) {
|
|
|
504
504
|
for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
|
|
505
505
|
next = uncommentTransportEnv(next, prefix, opts.transport);
|
|
506
506
|
}
|
|
507
|
+
if (opts.authProvider === "none") {
|
|
508
|
+
next = next.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
509
|
+
}
|
|
507
510
|
await writeFile(join2(targetDir, "apps/api/.env"), next);
|
|
508
511
|
}
|
|
509
512
|
async function writeRootEnv(targetDir, opts) {
|
|
@@ -589,6 +592,7 @@ async function removeStrategiesLib(targetDir) {
|
|
|
589
592
|
await rm(join3(targetDir, "libs/shared/src/strategies"), { recursive: true, force: true });
|
|
590
593
|
await rm(join3(targetDir, "libs/shared/src/testing.ts"), { force: true });
|
|
591
594
|
await rm(join3(targetDir, "libs/shared/src/transport.ts"), { force: true });
|
|
595
|
+
await rm(join3(targetDir, "libs/shared/src/__tests__/transport.unit.test.ts"), { force: true });
|
|
592
596
|
const indexPath = join3(targetDir, "libs/shared/src/index.ts");
|
|
593
597
|
try {
|
|
594
598
|
const src = await readFile4(indexPath, "utf8");
|
|
@@ -607,122 +611,6 @@ async function removeStrategiesLib(targetDir) {
|
|
|
607
611
|
} catch {
|
|
608
612
|
}
|
|
609
613
|
}
|
|
610
|
-
async function removeAuthStack(targetDir) {
|
|
611
|
-
const rmPaths = [
|
|
612
|
-
"apps/microservices/auth",
|
|
613
|
-
"libs/auth-strategies",
|
|
614
|
-
"libs/auth-client",
|
|
615
|
-
"Dockerfile.ms-auth",
|
|
616
|
-
"apps/api/src/app/auth",
|
|
617
|
-
"apps/api/src/app/profile",
|
|
618
|
-
"apps/api/src/app/abilities",
|
|
619
|
-
"libs/shared/src/abilities",
|
|
620
|
-
"apps/client/src/components/auth",
|
|
621
|
-
"apps/client/src/routes/login.tsx",
|
|
622
|
-
"apps/client/src/routes/auth.callback.tsx",
|
|
623
|
-
"apps/client/src/routes/auth.oauth.callback.tsx",
|
|
624
|
-
"apps/client/src/routes/_dashboard/profile.tsx"
|
|
625
|
-
];
|
|
626
|
-
for (const p3 of rmPaths) {
|
|
627
|
-
await rm(join3(targetDir, p3), { recursive: true, force: true });
|
|
628
|
-
}
|
|
629
|
-
const appModulePath = join3(targetDir, "apps/api/src/app/app.module.ts");
|
|
630
|
-
try {
|
|
631
|
-
const src = await readFile4(appModulePath, "utf8");
|
|
632
|
-
const next = src.replace(/^import \{ AuthModule \} from '\.\/auth\/auth\.module';\n/m, "").replace(/^import \{ ProfileModule \} from '\.\/profile\/profile\.module';\n/m, "").replace(/^import \{ AbilitiesModule \} from '\.\/abilities\/abilities\.module';\n/m, "").replace(/\bAuthModule,\s*/g, "").replace(/,\s*AuthModule\b/g, "").replace(/\bProfileModule,\s*/g, "").replace(/,\s*ProfileModule\b/g, "").replace(/\bAbilitiesModule,\s*/g, "").replace(/,\s*AbilitiesModule\b/g, "");
|
|
633
|
-
await writeFile2(appModulePath, next);
|
|
634
|
-
} catch {
|
|
635
|
-
}
|
|
636
|
-
const dashboardPath = join3(targetDir, "apps/client/src/routes/_dashboard.tsx");
|
|
637
|
-
try {
|
|
638
|
-
const src = await readFile4(dashboardPath, "utf8");
|
|
639
|
-
const next = src.replace(/^import \{ useAuthStore \} from '@icore\/template-shared';\n/m, "").replace(/, redirect/g, "").replace(/\n {2}beforeLoad: \(\) => \{[\s\S]*?\n {2}\},/m, "");
|
|
640
|
-
await writeFile2(dashboardPath, next);
|
|
641
|
-
} catch {
|
|
642
|
-
}
|
|
643
|
-
for (const alias of [
|
|
644
|
-
"@icore/auth-client",
|
|
645
|
-
"@icore/auth-supabase",
|
|
646
|
-
"@icore/auth-firebase",
|
|
647
|
-
"@icore/auth-mongodb"
|
|
648
|
-
]) {
|
|
649
|
-
await stripTsconfigPath(targetDir, alias);
|
|
650
|
-
}
|
|
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
|
-
}
|
|
662
|
-
const gatewayEnv = join3(targetDir, "apps/api/.env");
|
|
663
|
-
try {
|
|
664
|
-
const env = await readFile4(gatewayEnv, "utf8");
|
|
665
|
-
const next = env.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
666
|
-
await writeFile2(gatewayEnv, next);
|
|
667
|
-
} catch {
|
|
668
|
-
}
|
|
669
|
-
const composePath = join3(targetDir, "docker-compose.yml");
|
|
670
|
-
try {
|
|
671
|
-
const compose = await readFile4(composePath, "utf8");
|
|
672
|
-
const next = compose.replace(/\n {2}auth:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}auth:\n {8}condition: service_started/g, "").replace(/\n {6}AUTH_TRANSPORT:[^\n]*/g, "").replace(/\n {6}AUTH_REDIS_URL:[^\n]*/g, "");
|
|
673
|
-
await writeFile2(composePath, next);
|
|
674
|
-
} catch {
|
|
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
|
-
}
|
|
725
|
-
}
|
|
726
614
|
async function removeUploadStack(targetDir) {
|
|
727
615
|
const paths = [
|
|
728
616
|
"apps/microservices/upload",
|
|
@@ -764,9 +652,674 @@ async function removeUploadStack(targetDir) {
|
|
|
764
652
|
}
|
|
765
653
|
}
|
|
766
654
|
|
|
655
|
+
// src/lib/scaffold-auth-none.ts
|
|
656
|
+
import { mkdir, rm as rm2, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
657
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
658
|
+
async function stripDeps2(pkgPath, names) {
|
|
659
|
+
try {
|
|
660
|
+
const raw = await readFile5(pkgPath, "utf8");
|
|
661
|
+
const pkg = JSON.parse(raw);
|
|
662
|
+
for (const n of names) {
|
|
663
|
+
if (pkg.dependencies) delete pkg.dependencies[n];
|
|
664
|
+
if (pkg.devDependencies) delete pkg.devDependencies[n];
|
|
665
|
+
}
|
|
666
|
+
await writeFile3(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
667
|
+
} catch {
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
var AUTH_ONLY_PATHS = [
|
|
671
|
+
"apps/microservices/auth",
|
|
672
|
+
"libs/auth-strategies",
|
|
673
|
+
"libs/auth-client",
|
|
674
|
+
"Dockerfile.ms-auth",
|
|
675
|
+
"apps/api/src/app/auth",
|
|
676
|
+
"apps/api/src/app/profile",
|
|
677
|
+
"apps/api/src/app/abilities",
|
|
678
|
+
"libs/shared/src/abilities",
|
|
679
|
+
"apps/client/src/components/auth",
|
|
680
|
+
"apps/client/src/routes/login.tsx",
|
|
681
|
+
"apps/client/src/routes/auth.callback.tsx",
|
|
682
|
+
"apps/client/src/routes/auth.oauth.callback.tsx",
|
|
683
|
+
"apps/client/src/routes/_dashboard/profile.tsx",
|
|
684
|
+
"libs/template-shared/src/lib/abilities"
|
|
685
|
+
];
|
|
686
|
+
async function removeAuthOnlyPaths(targetDir) {
|
|
687
|
+
for (const p3 of AUTH_ONLY_PATHS) {
|
|
688
|
+
await rm2(join4(targetDir, p3), { recursive: true, force: true });
|
|
689
|
+
}
|
|
690
|
+
await stripDeps2(join4(targetDir, "apps/api/package.json"), [
|
|
691
|
+
"@icore/auth-client",
|
|
692
|
+
"cookie-parser"
|
|
693
|
+
]);
|
|
694
|
+
}
|
|
695
|
+
async function stripTsconfigPath2(targetDir, alias) {
|
|
696
|
+
const tsconfigPath = join4(targetDir, "tsconfig.base.json");
|
|
697
|
+
try {
|
|
698
|
+
const src = await readFile5(tsconfigPath, "utf8");
|
|
699
|
+
const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
|
|
700
|
+
const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
|
|
701
|
+
if (pretty !== src) {
|
|
702
|
+
await writeFile3(tsconfigPath, pretty);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const parsed = JSON.parse(src);
|
|
706
|
+
if (parsed.compilerOptions?.paths) delete parsed.compilerOptions.paths[alias];
|
|
707
|
+
await writeFile3(tsconfigPath, JSON.stringify(parsed));
|
|
708
|
+
} catch {
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
async function removeAuthTsconfigPaths(targetDir) {
|
|
712
|
+
for (const alias of [
|
|
713
|
+
"@icore/auth-client",
|
|
714
|
+
"@icore/auth-supabase",
|
|
715
|
+
"@icore/auth-firebase",
|
|
716
|
+
"@icore/auth-mongodb"
|
|
717
|
+
]) {
|
|
718
|
+
await stripTsconfigPath2(targetDir, alias);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
async function removeDockerComposeAuthService(targetDir) {
|
|
722
|
+
const composePath = join4(targetDir, "docker-compose.yml");
|
|
723
|
+
try {
|
|
724
|
+
const compose = await readFile5(composePath, "utf8");
|
|
725
|
+
const next = compose.replace(/\n {2}auth:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}auth:\n {8}condition: service_started/g, "").replace(/\n {6}AUTH_TRANSPORT:[^\n]*/g, "").replace(/\n {6}AUTH_REDIS_URL:[^\n]*/g, "");
|
|
726
|
+
await writeFile3(composePath, next);
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
var GATEWAY_MAIN_TS = `import { Logger } from '@nestjs/common';
|
|
731
|
+
import { NestFactory } from '@nestjs/core';
|
|
732
|
+
import { NestExpressApplication } from '@nestjs/platform-express';
|
|
733
|
+
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
|
734
|
+
import { formatGatewayBanner } from '@icore/shared';
|
|
735
|
+
import { AppModule } from './app/app.module';
|
|
736
|
+
import { GATEWAY_SERVICES } from './app/gateway-services';
|
|
737
|
+
import pkg from '@icore/package.json';
|
|
738
|
+
|
|
739
|
+
const DEFAULT_PORT = 3001;
|
|
740
|
+
|
|
741
|
+
async function bootstrap() {
|
|
742
|
+
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
|
743
|
+
app.setGlobalPrefix('api');
|
|
744
|
+
|
|
745
|
+
const swaggerConfig = new DocumentBuilder()
|
|
746
|
+
.setTitle('iCore API')
|
|
747
|
+
.setDescription('iCore Gateway HTTP surface')
|
|
748
|
+
.setVersion(pkg.version)
|
|
749
|
+
.addBearerAuth()
|
|
750
|
+
.build();
|
|
751
|
+
const document = SwaggerModule.createDocument(app, swaggerConfig);
|
|
752
|
+
SwaggerModule.setup('api/docs', app, document);
|
|
753
|
+
|
|
754
|
+
const port = Number(process.env.API_PORT ?? DEFAULT_PORT);
|
|
755
|
+
await app.listen(port);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
bootstrap()
|
|
759
|
+
.then(() => {
|
|
760
|
+
const origin = process.env.API_ORIGIN ?? 'http://localhost';
|
|
761
|
+
const port = Number(process.env.API_PORT ?? DEFAULT_PORT);
|
|
762
|
+
new Logger('API-Bootstrap').log(
|
|
763
|
+
formatGatewayBanner({ port, origin, services: GATEWAY_SERVICES }),
|
|
764
|
+
);
|
|
765
|
+
})
|
|
766
|
+
.catch((err) => {
|
|
767
|
+
new Logger('API-Bootstrap').error(
|
|
768
|
+
'Gateway bootstrap failed',
|
|
769
|
+
err instanceof Error ? err.stack : err,
|
|
770
|
+
);
|
|
771
|
+
process.exit(1);
|
|
772
|
+
});
|
|
773
|
+
`;
|
|
774
|
+
var GATEWAY_APP_MODULE_TS = `import { join } from 'node:path';
|
|
775
|
+
import { Module } from '@nestjs/common';
|
|
776
|
+
import { ConfigModule } from '@nestjs/config';
|
|
777
|
+
import { ThrottlerModule, seconds } from '@nestjs/throttler';
|
|
778
|
+
import { StorageModule } from './storage/storage.module';
|
|
779
|
+
import { FeaturesModule } from './features.module';
|
|
780
|
+
|
|
781
|
+
@Module({
|
|
782
|
+
imports: [
|
|
783
|
+
ConfigModule.forRoot({
|
|
784
|
+
isGlobal: true,
|
|
785
|
+
envFilePath: [join(process.cwd(), 'apps/api/.env'), join(process.cwd(), '.env')],
|
|
786
|
+
}),
|
|
787
|
+
ThrottlerModule.forRoot([{ name: 'auth-burst', ttl: seconds(60), limit: 10 }]),
|
|
788
|
+
StorageModule,
|
|
789
|
+
FeaturesModule,
|
|
790
|
+
],
|
|
791
|
+
})
|
|
792
|
+
export class AppModule {}
|
|
793
|
+
`;
|
|
794
|
+
var SHARED_CLIENT_TS = `// Browser-safe subset of @icore/shared.
|
|
795
|
+
// Import from '@icore/shared/client' in client-side code to avoid pulling
|
|
796
|
+
// in NestJS / Node.js-only modules (transport, strategies, contracts).
|
|
797
|
+
export * from './types';
|
|
798
|
+
`;
|
|
799
|
+
var SHARED_INDEX_TS = `export * from './env';
|
|
800
|
+
export * from './bootstrap';
|
|
801
|
+
export * from './jobs';
|
|
802
|
+
export * from './strategies';
|
|
803
|
+
export * from './transport';
|
|
804
|
+
export * from './types';
|
|
805
|
+
`;
|
|
806
|
+
var TEMPLATE_SHARED_INDEX_TS = `export * from './lib/api/create-api.js';
|
|
807
|
+
export * from './lib/stores/auth.store.js';
|
|
808
|
+
export * from './lib/stores/loading.store.js';
|
|
809
|
+
export * from './lib/i18n/create-i18n.js';
|
|
810
|
+
export * from './lib/i18n/keys.js';
|
|
811
|
+
export * from './lib/notify/use-notify.js';
|
|
812
|
+
export * from './lib/draft/index.js';
|
|
813
|
+
export * from './lib/landing/LandingPage.js';
|
|
814
|
+
export * from './lib/stores/theme.store.js';
|
|
815
|
+
`;
|
|
816
|
+
var SHADCN_MAIN_TSX = `import './globals.css';
|
|
817
|
+
import { StrictMode } from 'react';
|
|
818
|
+
import { createRoot } from 'react-dom/client';
|
|
819
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
820
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
821
|
+
import {
|
|
822
|
+
createIcoreApi,
|
|
823
|
+
createIcoreI18n,
|
|
824
|
+
ICORE_LOCALES,
|
|
825
|
+
useThemeStore,
|
|
826
|
+
} from '@icore/template-shared';
|
|
827
|
+
import { I18nextProvider } from 'react-i18next';
|
|
828
|
+
import { Toaster } from 'sonner';
|
|
829
|
+
import { routeTree } from './routeTree.gen';
|
|
830
|
+
import { wireShadcnNotifier } from './lib/notify';
|
|
831
|
+
|
|
832
|
+
const queryClient = new QueryClient({
|
|
833
|
+
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
837
|
+
|
|
838
|
+
declare module '@tanstack/react-router' {
|
|
839
|
+
interface Register {
|
|
840
|
+
router: typeof router;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
|
|
845
|
+
|
|
846
|
+
// Single shared API instance \u2014 used by every query in src/queries/
|
|
847
|
+
export const api = createIcoreApi({
|
|
848
|
+
baseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
wireShadcnNotifier();
|
|
852
|
+
|
|
853
|
+
// Apply the theme class before React mounts so the first paint is correct
|
|
854
|
+
const applyTheme = (mode: 'light' | 'dark') => {
|
|
855
|
+
document.documentElement.classList.toggle('dark', mode === 'dark');
|
|
856
|
+
};
|
|
857
|
+
applyTheme(useThemeStore.getState().mode);
|
|
858
|
+
useThemeStore.subscribe((s) => applyTheme(s.mode));
|
|
859
|
+
|
|
860
|
+
createRoot(document.getElementById('root')!).render(
|
|
861
|
+
<StrictMode>
|
|
862
|
+
<I18nextProvider i18n={i18n}>
|
|
863
|
+
<QueryClientProvider client={queryClient}>
|
|
864
|
+
<RouterProvider router={router} />
|
|
865
|
+
<Toaster richColors />
|
|
866
|
+
</QueryClientProvider>
|
|
867
|
+
</I18nextProvider>
|
|
868
|
+
</StrictMode>,
|
|
869
|
+
);
|
|
870
|
+
`;
|
|
871
|
+
var SHADCN_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
|
|
872
|
+
import { MainLayout } from '../layouts/MainLayout';
|
|
873
|
+
|
|
874
|
+
export const Route = createFileRoute('/_dashboard')({
|
|
875
|
+
component: () => (
|
|
876
|
+
<MainLayout>
|
|
877
|
+
<Outlet />
|
|
878
|
+
</MainLayout>
|
|
879
|
+
),
|
|
880
|
+
});
|
|
881
|
+
`;
|
|
882
|
+
var SHADCN_INDEX_TSX = `import { createFileRoute } from '@tanstack/react-router';
|
|
883
|
+
import { LandingPage } from '@icore/template-shared';
|
|
884
|
+
|
|
885
|
+
// All version strings are injected at build time by vite.config.mts
|
|
886
|
+
// (reads root package.json via fs.readFileSync so they stay accurate
|
|
887
|
+
// even when workspace packages are bumped independently).
|
|
888
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
889
|
+
|
|
890
|
+
export const Route = createFileRoute('/')({
|
|
891
|
+
component: () => (
|
|
892
|
+
<LandingPage
|
|
893
|
+
coreVersion={APP_VERSION}
|
|
894
|
+
uiLibrary="shadcn"
|
|
895
|
+
deps={[
|
|
896
|
+
{ name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
|
|
897
|
+
{ name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
|
|
898
|
+
{ name: 'tailwindcss', version: (import.meta.env.VITE_DEP_TAILWINDCSS as string) ?? '?' },
|
|
899
|
+
{
|
|
900
|
+
name: '@tanstack/react-router',
|
|
901
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
name: '@tanstack/react-query',
|
|
905
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
|
|
906
|
+
},
|
|
907
|
+
{ name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
|
|
908
|
+
{ name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
|
|
909
|
+
]}
|
|
910
|
+
ctaHref="/dashboard"
|
|
911
|
+
ctaLabel="Dashboard \u2192"
|
|
912
|
+
/>
|
|
913
|
+
),
|
|
914
|
+
});
|
|
915
|
+
`;
|
|
916
|
+
var SHADCN_LAYOUT_HEADER_TSX = `import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
|
|
917
|
+
import { ThemeToggle } from '../ThemeToggle';
|
|
918
|
+
|
|
919
|
+
const LOCALES: { code: IcoreLocale; label: string }[] = [
|
|
920
|
+
{ code: 'en', label: 'EN' },
|
|
921
|
+
{ code: 'ru', label: 'RU' },
|
|
922
|
+
{ code: 'he', label: 'HE' },
|
|
923
|
+
];
|
|
924
|
+
|
|
925
|
+
export function LayoutHeader() {
|
|
926
|
+
function handleLocale(code: IcoreLocale) {
|
|
927
|
+
setStoredLocale(code);
|
|
928
|
+
window.location.reload();
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
return (
|
|
932
|
+
<header className="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-[--color-border] bg-[--color-background]/80 px-4 backdrop-blur-sm">
|
|
933
|
+
<div className="flex items-center gap-2">
|
|
934
|
+
<div className="flex h-6 w-6 items-center justify-center rounded bg-[--color-primary]">
|
|
935
|
+
<span className="text-xs font-bold text-[--color-primary-foreground]">i</span>
|
|
936
|
+
</div>
|
|
937
|
+
<span className="text-sm font-semibold tracking-tight">iCore</span>
|
|
938
|
+
</div>
|
|
939
|
+
|
|
940
|
+
<div className="flex items-center gap-1">
|
|
941
|
+
<div className="flex items-center rounded-md border border-[--color-border] overflow-hidden mr-2">
|
|
942
|
+
{LOCALES.map(({ code, label }) => (
|
|
943
|
+
<button
|
|
944
|
+
key={code}
|
|
945
|
+
type="button"
|
|
946
|
+
onClick={() => handleLocale(code)}
|
|
947
|
+
className="px-2.5 py-1 text-xs text-[--color-muted-foreground] hover:bg-[--color-muted] hover:text-[--color-foreground] transition-colors cursor-pointer"
|
|
948
|
+
>
|
|
949
|
+
{label}
|
|
950
|
+
</button>
|
|
951
|
+
))}
|
|
952
|
+
</div>
|
|
953
|
+
|
|
954
|
+
<ThemeToggle />
|
|
955
|
+
</div>
|
|
956
|
+
</header>
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
`;
|
|
960
|
+
var ANTD_MAIN_TSX = `import './globals.less';
|
|
961
|
+
import { StrictMode } from 'react';
|
|
962
|
+
import { createRoot } from 'react-dom/client';
|
|
963
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
964
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
965
|
+
import { ConfigProvider, App as AntApp, theme } from 'antd';
|
|
966
|
+
import { I18nextProvider } from 'react-i18next';
|
|
967
|
+
import {
|
|
968
|
+
createIcoreApi,
|
|
969
|
+
createIcoreI18n,
|
|
970
|
+
ICORE_LOCALES,
|
|
971
|
+
useThemeStore,
|
|
972
|
+
} from '@icore/template-shared';
|
|
973
|
+
import { routeTree } from './routeTree.gen';
|
|
974
|
+
|
|
975
|
+
const queryClient = new QueryClient({
|
|
976
|
+
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
980
|
+
|
|
981
|
+
declare module '@tanstack/react-router' {
|
|
982
|
+
interface Register {
|
|
983
|
+
router: typeof router;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
|
|
988
|
+
|
|
989
|
+
// Single shared API instance \u2014 used by every query in src/queries/
|
|
990
|
+
export const api = createIcoreApi({
|
|
991
|
+
baseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
function Root() {
|
|
995
|
+
const mode = useThemeStore((s) => s.mode);
|
|
996
|
+
const algorithm = mode === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm;
|
|
997
|
+
return (
|
|
998
|
+
<ConfigProvider theme={{ algorithm, token: { colorPrimary: '#22c55e', colorLink: '#22c55e' } }}>
|
|
999
|
+
<AntApp>
|
|
1000
|
+
<QueryClientProvider client={queryClient}>
|
|
1001
|
+
<RouterProvider router={router} />
|
|
1002
|
+
</QueryClientProvider>
|
|
1003
|
+
</AntApp>
|
|
1004
|
+
</ConfigProvider>
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
createRoot(document.getElementById('root')!).render(
|
|
1009
|
+
<StrictMode>
|
|
1010
|
+
<I18nextProvider i18n={i18n}>
|
|
1011
|
+
<Root />
|
|
1012
|
+
</I18nextProvider>
|
|
1013
|
+
</StrictMode>,
|
|
1014
|
+
);
|
|
1015
|
+
`;
|
|
1016
|
+
var ANTD_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
|
|
1017
|
+
import { MainLayout } from '../layouts/MainLayout';
|
|
1018
|
+
|
|
1019
|
+
export const Route = createFileRoute('/_dashboard')({
|
|
1020
|
+
component: () => (
|
|
1021
|
+
<MainLayout>
|
|
1022
|
+
<Outlet />
|
|
1023
|
+
</MainLayout>
|
|
1024
|
+
),
|
|
1025
|
+
});
|
|
1026
|
+
`;
|
|
1027
|
+
var ANTD_INDEX_TSX = `import { createFileRoute, Link } from '@tanstack/react-router';
|
|
1028
|
+
import { LandingPage } from '@icore/template-shared';
|
|
1029
|
+
|
|
1030
|
+
// All version strings are injected at build time by vite.config.mts
|
|
1031
|
+
// (reads root package.json via fs.readFileSync so they stay accurate
|
|
1032
|
+
// even when workspace packages are bumped independently).
|
|
1033
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
1034
|
+
|
|
1035
|
+
export const Route = createFileRoute('/')({
|
|
1036
|
+
component: () => (
|
|
1037
|
+
<LandingPage
|
|
1038
|
+
coreVersion={APP_VERSION}
|
|
1039
|
+
uiLibrary="antd"
|
|
1040
|
+
deps={[
|
|
1041
|
+
{ name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
|
|
1042
|
+
{ name: 'antd', version: (import.meta.env.VITE_DEP_ANTD as string) ?? '?' },
|
|
1043
|
+
{ name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
|
|
1044
|
+
{
|
|
1045
|
+
name: '@tanstack/react-router',
|
|
1046
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
name: '@tanstack/react-query',
|
|
1050
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
|
|
1051
|
+
},
|
|
1052
|
+
{ name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
|
|
1053
|
+
{ name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
|
|
1054
|
+
]}
|
|
1055
|
+
ctaHref="/dashboard"
|
|
1056
|
+
ctaLabel={<Link to="/dashboard">Dashboard \u2192</Link>}
|
|
1057
|
+
/>
|
|
1058
|
+
),
|
|
1059
|
+
});
|
|
1060
|
+
`;
|
|
1061
|
+
var ANTD_LAYOUT_HEADER_TSX = `import { Button, Layout, Space } from 'antd';
|
|
1062
|
+
import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
|
|
1063
|
+
import { ThemeToggle } from '../ThemeToggle';
|
|
1064
|
+
|
|
1065
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
1066
|
+
|
|
1067
|
+
const LOCALES: { code: IcoreLocale; label: string }[] = [
|
|
1068
|
+
{ code: 'en', label: 'EN' },
|
|
1069
|
+
{ code: 'ru', label: 'RU' },
|
|
1070
|
+
{ code: 'he', label: 'HE' },
|
|
1071
|
+
];
|
|
1072
|
+
|
|
1073
|
+
export function LayoutHeader() {
|
|
1074
|
+
function handleLocale(code: IcoreLocale) {
|
|
1075
|
+
setStoredLocale(code);
|
|
1076
|
+
window.location.reload();
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
return (
|
|
1080
|
+
<Layout.Header
|
|
1081
|
+
style={{
|
|
1082
|
+
display: 'flex',
|
|
1083
|
+
alignItems: 'center',
|
|
1084
|
+
justifyContent: 'space-between',
|
|
1085
|
+
padding: '0 24px',
|
|
1086
|
+
}}
|
|
1087
|
+
>
|
|
1088
|
+
<Space>
|
|
1089
|
+
<span style={{ color: '#fff', fontWeight: 600, fontSize: 16 }}>iCore</span>
|
|
1090
|
+
<span
|
|
1091
|
+
style={{
|
|
1092
|
+
color: 'rgba(255,255,255,0.45)',
|
|
1093
|
+
fontSize: 11,
|
|
1094
|
+
background: 'rgba(255,255,255,0.1)',
|
|
1095
|
+
padding: '1px 6px',
|
|
1096
|
+
borderRadius: 4,
|
|
1097
|
+
}}
|
|
1098
|
+
>
|
|
1099
|
+
v{APP_VERSION}
|
|
1100
|
+
</span>
|
|
1101
|
+
</Space>
|
|
1102
|
+
|
|
1103
|
+
<Space size="middle">
|
|
1104
|
+
<Space size={4}>
|
|
1105
|
+
{LOCALES.map(({ code, label }) => (
|
|
1106
|
+
<Button
|
|
1107
|
+
key={code}
|
|
1108
|
+
size="small"
|
|
1109
|
+
type="text"
|
|
1110
|
+
style={{ color: 'rgba(255,255,255,0.65)' }}
|
|
1111
|
+
onClick={() => handleLocale(code)}
|
|
1112
|
+
>
|
|
1113
|
+
{label}
|
|
1114
|
+
</Button>
|
|
1115
|
+
))}
|
|
1116
|
+
</Space>
|
|
1117
|
+
|
|
1118
|
+
<ThemeToggle />
|
|
1119
|
+
</Space>
|
|
1120
|
+
</Layout.Header>
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
`;
|
|
1124
|
+
var MUI_MAIN_TSX = `import './globals.css';
|
|
1125
|
+
import { StrictMode, useMemo } from 'react';
|
|
1126
|
+
import { createRoot } from 'react-dom/client';
|
|
1127
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
1128
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
1129
|
+
import { CssBaseline, ThemeProvider, createTheme } from '@mui/material';
|
|
1130
|
+
import { I18nextProvider } from 'react-i18next';
|
|
1131
|
+
import {
|
|
1132
|
+
createIcoreApi,
|
|
1133
|
+
createIcoreI18n,
|
|
1134
|
+
ICORE_LOCALES,
|
|
1135
|
+
useThemeStore,
|
|
1136
|
+
} from '@icore/template-shared';
|
|
1137
|
+
import { routeTree } from './routeTree.gen';
|
|
1138
|
+
import { wireMuiNotifier } from './lib/notify';
|
|
1139
|
+
|
|
1140
|
+
const queryClient = new QueryClient({
|
|
1141
|
+
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
1145
|
+
|
|
1146
|
+
declare module '@tanstack/react-router' {
|
|
1147
|
+
interface Register {
|
|
1148
|
+
router: typeof router;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
|
|
1153
|
+
|
|
1154
|
+
export const api = createIcoreApi({
|
|
1155
|
+
baseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
wireMuiNotifier();
|
|
1159
|
+
|
|
1160
|
+
function Root() {
|
|
1161
|
+
const mode = useThemeStore((s) => s.mode);
|
|
1162
|
+
const theme = useMemo(
|
|
1163
|
+
() => createTheme({ palette: { mode, primary: { main: '#22c55e' } } }),
|
|
1164
|
+
[mode],
|
|
1165
|
+
);
|
|
1166
|
+
return (
|
|
1167
|
+
<ThemeProvider theme={theme}>
|
|
1168
|
+
<CssBaseline />
|
|
1169
|
+
<QueryClientProvider client={queryClient}>
|
|
1170
|
+
<RouterProvider router={router} />
|
|
1171
|
+
</QueryClientProvider>
|
|
1172
|
+
</ThemeProvider>
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
createRoot(document.getElementById('root')!).render(
|
|
1177
|
+
<StrictMode>
|
|
1178
|
+
<I18nextProvider i18n={i18n}>
|
|
1179
|
+
<Root />
|
|
1180
|
+
</I18nextProvider>
|
|
1181
|
+
</StrictMode>,
|
|
1182
|
+
);
|
|
1183
|
+
`;
|
|
1184
|
+
var MUI_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
|
|
1185
|
+
import { MainLayout } from '../layouts/MainLayout';
|
|
1186
|
+
|
|
1187
|
+
export const Route = createFileRoute('/_dashboard')({
|
|
1188
|
+
component: () => (
|
|
1189
|
+
<MainLayout>
|
|
1190
|
+
<Outlet />
|
|
1191
|
+
</MainLayout>
|
|
1192
|
+
),
|
|
1193
|
+
});
|
|
1194
|
+
`;
|
|
1195
|
+
var MUI_INDEX_TSX = `import { createFileRoute, Link } from '@tanstack/react-router';
|
|
1196
|
+
import { LandingPage } from '@icore/template-shared';
|
|
1197
|
+
|
|
1198
|
+
// All version strings are injected at build time by vite.config.mts
|
|
1199
|
+
// (reads root package.json via fs.readFileSync so they stay accurate
|
|
1200
|
+
// even when workspace packages are bumped independently).
|
|
1201
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
1202
|
+
|
|
1203
|
+
export const Route = createFileRoute('/')({
|
|
1204
|
+
component: () => (
|
|
1205
|
+
<LandingPage
|
|
1206
|
+
coreVersion={APP_VERSION}
|
|
1207
|
+
uiLibrary="mui"
|
|
1208
|
+
deps={[
|
|
1209
|
+
{ name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
|
|
1210
|
+
{ name: '@mui/material', version: (import.meta.env.VITE_DEP_MUI as string) ?? '?' },
|
|
1211
|
+
{ name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
|
|
1212
|
+
{
|
|
1213
|
+
name: '@tanstack/react-router',
|
|
1214
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
|
|
1215
|
+
},
|
|
1216
|
+
{
|
|
1217
|
+
name: '@tanstack/react-query',
|
|
1218
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
|
|
1219
|
+
},
|
|
1220
|
+
{ name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
|
|
1221
|
+
{ name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
|
|
1222
|
+
]}
|
|
1223
|
+
ctaHref="/dashboard"
|
|
1224
|
+
ctaLabel={<Link to="/dashboard">Dashboard \u2192</Link>}
|
|
1225
|
+
/>
|
|
1226
|
+
),
|
|
1227
|
+
});
|
|
1228
|
+
`;
|
|
1229
|
+
var MUI_LAYOUT_HEADER_TSX = `import { AppBar, Box, Button, Toolbar, Typography } from '@mui/material';
|
|
1230
|
+
import { useTranslation } from 'react-i18next';
|
|
1231
|
+
import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
|
|
1232
|
+
import { ThemeToggle } from '../ThemeToggle';
|
|
1233
|
+
|
|
1234
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
1235
|
+
|
|
1236
|
+
const LOCALES: { code: IcoreLocale; label: string }[] = [
|
|
1237
|
+
{ code: 'en', label: 'EN' },
|
|
1238
|
+
{ code: 'ru', label: 'RU' },
|
|
1239
|
+
{ code: 'he', label: 'HE' },
|
|
1240
|
+
];
|
|
1241
|
+
|
|
1242
|
+
export function LayoutHeader() {
|
|
1243
|
+
const { i18n } = useTranslation();
|
|
1244
|
+
const currentLocale = i18n.language as IcoreLocale;
|
|
1245
|
+
|
|
1246
|
+
function handleLocale(code: IcoreLocale) {
|
|
1247
|
+
setStoredLocale(code);
|
|
1248
|
+
window.location.reload();
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
return (
|
|
1252
|
+
<AppBar position="sticky" color="default" elevation={1}>
|
|
1253
|
+
<Toolbar sx={{ justifyContent: 'space-between' }}>
|
|
1254
|
+
<Typography variant="h6" component="div" fontWeight={600}>
|
|
1255
|
+
iCore{' '}
|
|
1256
|
+
<span style={{ opacity: 0.6, fontSize: '0.75em', fontWeight: 400 }}>v{APP_VERSION}</span>
|
|
1257
|
+
</Typography>
|
|
1258
|
+
|
|
1259
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
1260
|
+
<Box sx={{ display: 'flex', gap: 0.5 }}>
|
|
1261
|
+
{LOCALES.map(({ code, label }) => (
|
|
1262
|
+
<Button
|
|
1263
|
+
key={code}
|
|
1264
|
+
size="small"
|
|
1265
|
+
variant={currentLocale === code ? 'contained' : 'text'}
|
|
1266
|
+
onClick={() => handleLocale(code)}
|
|
1267
|
+
>
|
|
1268
|
+
{label}
|
|
1269
|
+
</Button>
|
|
1270
|
+
))}
|
|
1271
|
+
</Box>
|
|
1272
|
+
|
|
1273
|
+
<ThemeToggle />
|
|
1274
|
+
</Box>
|
|
1275
|
+
</Toolbar>
|
|
1276
|
+
</AppBar>
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
`;
|
|
1280
|
+
var COMMON_VARIANTS = {
|
|
1281
|
+
"apps/api/src/main.ts": GATEWAY_MAIN_TS,
|
|
1282
|
+
"apps/api/src/app/app.module.ts": GATEWAY_APP_MODULE_TS,
|
|
1283
|
+
"libs/shared/src/client.ts": SHARED_CLIENT_TS,
|
|
1284
|
+
"libs/shared/src/index.ts": SHARED_INDEX_TS,
|
|
1285
|
+
"libs/template-shared/src/index.ts": TEMPLATE_SHARED_INDEX_TS
|
|
1286
|
+
};
|
|
1287
|
+
var UI_VARIANTS = {
|
|
1288
|
+
shadcn: {
|
|
1289
|
+
"apps/client/src/main.tsx": SHADCN_MAIN_TSX,
|
|
1290
|
+
"apps/client/src/routes/_dashboard.tsx": SHADCN_DASHBOARD_TSX,
|
|
1291
|
+
"apps/client/src/routes/index.tsx": SHADCN_INDEX_TSX,
|
|
1292
|
+
"apps/client/src/components/layout/LayoutHeader.tsx": SHADCN_LAYOUT_HEADER_TSX
|
|
1293
|
+
},
|
|
1294
|
+
antd: {
|
|
1295
|
+
"apps/client/src/main.tsx": ANTD_MAIN_TSX,
|
|
1296
|
+
"apps/client/src/routes/_dashboard.tsx": ANTD_DASHBOARD_TSX,
|
|
1297
|
+
"apps/client/src/routes/index.tsx": ANTD_INDEX_TSX,
|
|
1298
|
+
"apps/client/src/components/layout/LayoutHeader.tsx": ANTD_LAYOUT_HEADER_TSX
|
|
1299
|
+
},
|
|
1300
|
+
mui: {
|
|
1301
|
+
"apps/client/src/main.tsx": MUI_MAIN_TSX,
|
|
1302
|
+
"apps/client/src/routes/_dashboard.tsx": MUI_DASHBOARD_TSX,
|
|
1303
|
+
"apps/client/src/routes/index.tsx": MUI_INDEX_TSX,
|
|
1304
|
+
"apps/client/src/components/layout/LayoutHeader.tsx": MUI_LAYOUT_HEADER_TSX
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
async function applyAuthNoneVariants(targetDir, ui) {
|
|
1308
|
+
const uiFiles = UI_VARIANTS[ui] ?? UI_VARIANTS["shadcn"];
|
|
1309
|
+
const all = { ...COMMON_VARIANTS, ...uiFiles };
|
|
1310
|
+
for (const [rel, content] of Object.entries(all)) {
|
|
1311
|
+
const dest = join4(targetDir, rel);
|
|
1312
|
+
try {
|
|
1313
|
+
await mkdir(dirname2(dest), { recursive: true });
|
|
1314
|
+
await writeFile3(dest, content);
|
|
1315
|
+
} catch {
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
767
1320
|
// src/manifest/wire-features.ts
|
|
768
|
-
import { readFile as
|
|
769
|
-
import { join as
|
|
1321
|
+
import { readFile as readFile7, writeFile as writeFile5, rm as rm4, rmdir, unlink } from "fs/promises";
|
|
1322
|
+
import { join as join6 } from "path";
|
|
770
1323
|
|
|
771
1324
|
// src/manifest/index.ts
|
|
772
1325
|
var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
|
|
@@ -926,8 +1479,8 @@ var MANIFEST = {
|
|
|
926
1479
|
};
|
|
927
1480
|
|
|
928
1481
|
// src/manifest/wire-provider.ts
|
|
929
|
-
import { readFile as
|
|
930
|
-
import { join as
|
|
1482
|
+
import { readFile as readFile6, writeFile as writeFile4, rm as rm3 } from "fs/promises";
|
|
1483
|
+
import { join as join5 } from "path";
|
|
931
1484
|
async function writeProvider(targetDir, axis, provider) {
|
|
932
1485
|
const nestModule = axis.section[provider]?.nestModule;
|
|
933
1486
|
if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
|
|
@@ -938,27 +1491,27 @@ const ENV_PATH = '${axis.envPath}';
|
|
|
938
1491
|
|
|
939
1492
|
export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
|
|
940
1493
|
`;
|
|
941
|
-
await
|
|
1494
|
+
await writeFile4(join5(targetDir, axis.providerFile), content);
|
|
942
1495
|
}
|
|
943
1496
|
async function stripJsonKeys(path, drop) {
|
|
944
1497
|
try {
|
|
945
|
-
const pkg = JSON.parse(await
|
|
1498
|
+
const pkg = JSON.parse(await readFile6(path, "utf8"));
|
|
946
1499
|
for (const field of ["dependencies", "devDependencies"]) {
|
|
947
1500
|
const deps = pkg[field];
|
|
948
1501
|
if (!deps) continue;
|
|
949
1502
|
for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
|
|
950
1503
|
}
|
|
951
|
-
await
|
|
1504
|
+
await writeFile4(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
952
1505
|
} catch {
|
|
953
1506
|
}
|
|
954
1507
|
}
|
|
955
1508
|
async function stripTsconfigKeys(targetDir, aliases) {
|
|
956
|
-
const path =
|
|
1509
|
+
const path = join5(targetDir, "tsconfig.base.json");
|
|
957
1510
|
try {
|
|
958
|
-
const parsed = JSON.parse(await
|
|
1511
|
+
const parsed = JSON.parse(await readFile6(path, "utf8"));
|
|
959
1512
|
const paths = parsed.compilerOptions?.paths;
|
|
960
1513
|
if (paths) for (const a of aliases) delete paths[a];
|
|
961
|
-
await
|
|
1514
|
+
await writeFile4(path, JSON.stringify(parsed, null, 2) + "\n");
|
|
962
1515
|
} catch {
|
|
963
1516
|
}
|
|
964
1517
|
}
|
|
@@ -967,10 +1520,10 @@ async function cleanupUnusedAxis(targetDir, axis, chosen) {
|
|
|
967
1520
|
if (provider === chosen) continue;
|
|
968
1521
|
const unit = axis.section[provider];
|
|
969
1522
|
for (const dir of unit.libDirs)
|
|
970
|
-
await
|
|
971
|
-
for (const t of unit.appTests ?? []) await
|
|
1523
|
+
await rm3(join5(targetDir, dir), { recursive: true, force: true });
|
|
1524
|
+
for (const t of unit.appTests ?? []) await rm3(join5(targetDir, t), { force: true });
|
|
972
1525
|
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
973
|
-
await stripJsonKeys(
|
|
1526
|
+
await stripJsonKeys(join5(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
|
|
974
1527
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
975
1528
|
}
|
|
976
1529
|
}
|
|
@@ -999,7 +1552,7 @@ async function writeFeaturesWiring(targetDir, opts) {
|
|
|
999
1552
|
})
|
|
1000
1553
|
export class FeaturesModule {}
|
|
1001
1554
|
`;
|
|
1002
|
-
await
|
|
1555
|
+
await writeFile5(join6(targetDir, FEATURES_MODULE), featuresModule);
|
|
1003
1556
|
const services = [];
|
|
1004
1557
|
if (opts.authProvider !== "none") services.push({ name: "auth", prefix: "AUTH" });
|
|
1005
1558
|
if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
|
|
@@ -1013,14 +1566,14 @@ export const GATEWAY_SERVICES = [
|
|
|
1013
1566
|
${entries}
|
|
1014
1567
|
];
|
|
1015
1568
|
`;
|
|
1016
|
-
await
|
|
1569
|
+
await writeFile5(join6(targetDir, GATEWAY_SERVICES), gatewayServices);
|
|
1017
1570
|
}
|
|
1018
1571
|
async function stripJobsDockerCompose(targetDir) {
|
|
1019
|
-
const composePath =
|
|
1572
|
+
const composePath = join6(targetDir, "docker-compose.yml");
|
|
1020
1573
|
try {
|
|
1021
|
-
const compose = await
|
|
1574
|
+
const compose = await readFile7(composePath, "utf8");
|
|
1022
1575
|
const next = compose.replace(/\n {2}jobs:[\s\S]+?(?=\n {2}\w+:|\nnetworks:)/m, "\n").replace(/\n {6}jobs:\n {8}condition: service_started/g, "").replace(/\n {6}JOBS_REDIS_URL:[^\n]*/g, "");
|
|
1023
|
-
await
|
|
1576
|
+
await writeFile5(composePath, next);
|
|
1024
1577
|
} catch {
|
|
1025
1578
|
}
|
|
1026
1579
|
}
|
|
@@ -1030,34 +1583,38 @@ async function cleanupUnusedFeatures(targetDir, opts) {
|
|
|
1030
1583
|
if (chosen.has(key)) continue;
|
|
1031
1584
|
const unit = FEATURES[key];
|
|
1032
1585
|
for (const dir of unit.libDirs)
|
|
1033
|
-
await
|
|
1586
|
+
await rm4(join6(targetDir, dir), { recursive: true, force: true });
|
|
1034
1587
|
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
1035
|
-
await stripJsonKeys(
|
|
1588
|
+
await stripJsonKeys(join6(targetDir, API_PKG), (k) => dropKeys.has(k));
|
|
1036
1589
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
1037
1590
|
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
1038
1591
|
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
1039
1592
|
if (key === "jobs") {
|
|
1040
1593
|
try {
|
|
1041
|
-
await unlink(
|
|
1594
|
+
await unlink(join6(targetDir, "libs/shared/src/jobs.ts"));
|
|
1595
|
+
} catch {
|
|
1596
|
+
}
|
|
1597
|
+
try {
|
|
1598
|
+
await unlink(join6(targetDir, "libs/shared/src/__tests__/jobs.unit.test.ts"));
|
|
1042
1599
|
} catch {
|
|
1043
1600
|
}
|
|
1044
|
-
const sharedIdx =
|
|
1601
|
+
const sharedIdx = join6(targetDir, "libs/shared/src/index.ts");
|
|
1045
1602
|
try {
|
|
1046
|
-
const src = await
|
|
1047
|
-
await
|
|
1603
|
+
const src = await readFile7(sharedIdx, "utf8");
|
|
1604
|
+
await writeFile5(sharedIdx, src.replace(/^export \* from '\.\/jobs';\n/m, ""));
|
|
1048
1605
|
} catch {
|
|
1049
1606
|
}
|
|
1050
1607
|
}
|
|
1051
1608
|
}
|
|
1052
1609
|
try {
|
|
1053
|
-
await rmdir(
|
|
1610
|
+
await rmdir(join6(targetDir, "apps/client/src/queries"));
|
|
1054
1611
|
} catch {
|
|
1055
1612
|
}
|
|
1056
1613
|
}
|
|
1057
1614
|
|
|
1058
1615
|
// src/manifest/wire-client.ts
|
|
1059
|
-
import { writeFile as
|
|
1060
|
-
import { join as
|
|
1616
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
1617
|
+
import { join as join7 } from "path";
|
|
1061
1618
|
var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
|
|
1062
1619
|
var BASE_NAV = [
|
|
1063
1620
|
{ to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
|
|
@@ -1095,12 +1652,12 @@ export const NAV_CONFIG: NavItem[] = [
|
|
|
1095
1652
|
` + entries.map(renderEntry).join("\n") + `
|
|
1096
1653
|
];
|
|
1097
1654
|
`;
|
|
1098
|
-
await
|
|
1655
|
+
await writeFile6(join7(targetDir, NAV_CONFIG_FILE), content);
|
|
1099
1656
|
}
|
|
1100
1657
|
|
|
1101
1658
|
// src/manifest/blueprint.ts
|
|
1102
|
-
import { writeFile as
|
|
1103
|
-
import { join as
|
|
1659
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
1660
|
+
import { join as join8 } from "path";
|
|
1104
1661
|
async function writeBlueprintJson(targetDir, opts) {
|
|
1105
1662
|
const blueprint = {
|
|
1106
1663
|
schemaVersion: 1,
|
|
@@ -1115,10 +1672,10 @@ async function writeBlueprintJson(targetDir, opts) {
|
|
|
1115
1672
|
transport: opts.transport,
|
|
1116
1673
|
packageManager: opts.packageManager
|
|
1117
1674
|
};
|
|
1118
|
-
await
|
|
1675
|
+
await writeFile7(join8(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
|
|
1119
1676
|
}
|
|
1120
1677
|
async function writeJson(targetDir, rel, data) {
|
|
1121
|
-
await
|
|
1678
|
+
await writeFile7(join8(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
|
|
1122
1679
|
}
|
|
1123
1680
|
async function writeServiceBlueprints(targetDir, opts) {
|
|
1124
1681
|
const t = opts.transport;
|
|
@@ -1212,8 +1769,8 @@ var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, prov
|
|
|
1212
1769
|
var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
|
|
1213
1770
|
|
|
1214
1771
|
// src/lib/scaffold-pkg.ts
|
|
1215
|
-
import { readFile as
|
|
1216
|
-
import { join as
|
|
1772
|
+
import { readFile as readFile8, writeFile as writeFile8, mkdir as mkdir2, readdir } from "fs/promises";
|
|
1773
|
+
import { join as join9 } from "path";
|
|
1217
1774
|
|
|
1218
1775
|
// src/lib/options.ts
|
|
1219
1776
|
function pmRun(pm, script) {
|
|
@@ -1222,8 +1779,8 @@ function pmRun(pm, script) {
|
|
|
1222
1779
|
|
|
1223
1780
|
// src/lib/scaffold-pkg.ts
|
|
1224
1781
|
async function writePnpmWorkspace(targetDir) {
|
|
1225
|
-
const pkgPath =
|
|
1226
|
-
const pkg = JSON.parse(await
|
|
1782
|
+
const pkgPath = join9(targetDir, "package.json");
|
|
1783
|
+
const pkg = JSON.parse(await readFile8(pkgPath, "utf8"));
|
|
1227
1784
|
const workspaces = pkg.workspaces ?? [];
|
|
1228
1785
|
const packagesBlock = workspaces.map((p3) => ` - '${p3}'`).join("\n");
|
|
1229
1786
|
const allowBuilds = [
|
|
@@ -1246,7 +1803,7 @@ ${packagesBlock}
|
|
|
1246
1803
|
allowBuilds:
|
|
1247
1804
|
${allowBuilds}
|
|
1248
1805
|
`;
|
|
1249
|
-
await
|
|
1806
|
+
await writeFile8(join9(targetDir, "pnpm-workspace.yaml"), content);
|
|
1250
1807
|
}
|
|
1251
1808
|
async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
1252
1809
|
async function walk(dir) {
|
|
@@ -1259,9 +1816,9 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
1259
1816
|
}
|
|
1260
1817
|
for (const e of entries) {
|
|
1261
1818
|
if (e.isDirectory() && e.name !== "node_modules") {
|
|
1262
|
-
found.push(...await walk(
|
|
1819
|
+
found.push(...await walk(join9(dir, e.name)));
|
|
1263
1820
|
} else if (e.isFile() && e.name === "package.json") {
|
|
1264
|
-
found.push(
|
|
1821
|
+
found.push(join9(dir, e.name));
|
|
1265
1822
|
}
|
|
1266
1823
|
}
|
|
1267
1824
|
return found;
|
|
@@ -1269,17 +1826,17 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
1269
1826
|
const pkgFiles = await walk(targetDir);
|
|
1270
1827
|
for (const f of pkgFiles) {
|
|
1271
1828
|
try {
|
|
1272
|
-
const raw = await
|
|
1829
|
+
const raw = await readFile8(f, "utf8");
|
|
1273
1830
|
const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
|
|
1274
|
-
if (next !== raw) await
|
|
1831
|
+
if (next !== raw) await writeFile8(f, next);
|
|
1275
1832
|
} catch {
|
|
1276
1833
|
}
|
|
1277
1834
|
}
|
|
1278
1835
|
}
|
|
1279
1836
|
async function patchGitignoreForPm(targetDir, pm) {
|
|
1280
|
-
const giPath =
|
|
1837
|
+
const giPath = join9(targetDir, ".gitignore");
|
|
1281
1838
|
try {
|
|
1282
|
-
let src = await
|
|
1839
|
+
let src = await readFile8(giPath, "utf8");
|
|
1283
1840
|
src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
|
|
1284
1841
|
if (pm !== "yarn") {
|
|
1285
1842
|
src = src.replace(/^\.yarn\/\*\s*\n/m, "").replace(/^!\.yarn\/patches\s*\n/m, "").replace(/^!\.yarn\/plugins\s*\n/m, "").replace(/^!\.yarn\/releases\s*\n/m, "").replace(/^!\.yarn\/sdks\s*\n/m, "").replace(/^!\.yarn\/versions\s*\n/m, "").replace(/^\.pnp\.\*\s*\n/m, "");
|
|
@@ -1294,7 +1851,7 @@ async function patchGitignoreForPm(targetDir, pm) {
|
|
|
1294
1851
|
src += "\n# npm\nnpm-debug.log*\n";
|
|
1295
1852
|
}
|
|
1296
1853
|
}
|
|
1297
|
-
await
|
|
1854
|
+
await writeFile8(giPath, src);
|
|
1298
1855
|
} catch {
|
|
1299
1856
|
}
|
|
1300
1857
|
}
|
|
@@ -1310,7 +1867,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
1310
1867
|
if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
|
|
1311
1868
|
const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
|
|
1312
1869
|
const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
1313
|
-
await
|
|
1870
|
+
await writeFile8(join9(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
|
|
1314
1871
|
const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
|
|
1315
1872
|
const readme = `# ${opts.projectName}
|
|
1316
1873
|
|
|
@@ -1358,7 +1915,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
|
|
|
1358
1915
|
|
|
1359
1916
|
Apache-2.0
|
|
1360
1917
|
`;
|
|
1361
|
-
await
|
|
1918
|
+
await writeFile8(join9(targetDir, "README.md"), readme);
|
|
1362
1919
|
const agents = `# ${opts.projectName} \u2014 Agent Instructions
|
|
1363
1920
|
|
|
1364
1921
|
## Stack snapshot
|
|
@@ -1439,8 +1996,8 @@ ${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PR
|
|
|
1439
1996
|
- Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
|
|
1440
1997
|
- Run: \`${nx} test <project>\`
|
|
1441
1998
|
`;
|
|
1442
|
-
await
|
|
1443
|
-
await
|
|
1999
|
+
await writeFile8(join9(targetDir, "AGENTS.md"), agents);
|
|
2000
|
+
await mkdir2(join9(targetDir, ".claude"), { recursive: true });
|
|
1444
2001
|
const mcpServers = {
|
|
1445
2002
|
nx: {
|
|
1446
2003
|
command: "npx",
|
|
@@ -1481,8 +2038,8 @@ ${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PR
|
|
|
1481
2038
|
]
|
|
1482
2039
|
}
|
|
1483
2040
|
};
|
|
1484
|
-
await
|
|
1485
|
-
|
|
2041
|
+
await writeFile8(
|
|
2042
|
+
join9(targetDir, ".claude", "settings.json"),
|
|
1486
2043
|
JSON.stringify(settings, null, 2) + "\n"
|
|
1487
2044
|
);
|
|
1488
2045
|
}
|
|
@@ -1502,20 +2059,20 @@ var IGNORE_TOP = /* @__PURE__ */ new Set([
|
|
|
1502
2059
|
".vscode"
|
|
1503
2060
|
]);
|
|
1504
2061
|
async function copyTree(src, dest) {
|
|
1505
|
-
await
|
|
2062
|
+
await mkdir3(dest, { recursive: true });
|
|
1506
2063
|
const entries = await readdir2(src, { withFileTypes: true });
|
|
1507
2064
|
for (const entry of entries) {
|
|
1508
2065
|
if (IGNORE_TOP.has(entry.name)) continue;
|
|
1509
|
-
const s =
|
|
1510
|
-
const d =
|
|
2066
|
+
const s = join10(src, entry.name);
|
|
2067
|
+
const d = join10(dest, entry.name);
|
|
1511
2068
|
if (entry.isDirectory()) await copyTree(s, d);
|
|
1512
2069
|
else if (entry.isFile()) await copyFile(s, d);
|
|
1513
2070
|
}
|
|
1514
2071
|
}
|
|
1515
2072
|
async function selectClientTemplate(targetDir, opts) {
|
|
1516
|
-
const templatesRoot =
|
|
1517
|
-
const chosen =
|
|
1518
|
-
const destClient =
|
|
2073
|
+
const templatesRoot = join10(targetDir, "apps/templates");
|
|
2074
|
+
const chosen = join10(templatesRoot, `client-${opts.ui}`);
|
|
2075
|
+
const destClient = join10(targetDir, "apps/client");
|
|
1519
2076
|
let chosenUi = opts.ui;
|
|
1520
2077
|
try {
|
|
1521
2078
|
const s = await stat(chosen);
|
|
@@ -1523,9 +2080,9 @@ async function selectClientTemplate(targetDir, opts) {
|
|
|
1523
2080
|
await copyTree(chosen, destClient);
|
|
1524
2081
|
} catch {
|
|
1525
2082
|
chosenUi = "shadcn";
|
|
1526
|
-
await copyTree(
|
|
2083
|
+
await copyTree(join10(templatesRoot, "client-shadcn"), destClient);
|
|
1527
2084
|
}
|
|
1528
|
-
await
|
|
2085
|
+
await rm5(templatesRoot, { recursive: true, force: true });
|
|
1529
2086
|
await rewriteClientPaths(destClient, chosenUi);
|
|
1530
2087
|
}
|
|
1531
2088
|
async function rewriteClientPaths(clientDir, ui) {
|
|
@@ -1538,11 +2095,11 @@ async function rewriteClientPaths(clientDir, ui) {
|
|
|
1538
2095
|
"eslint.config.mjs"
|
|
1539
2096
|
];
|
|
1540
2097
|
for (const rel of candidates) {
|
|
1541
|
-
const path =
|
|
2098
|
+
const path = join10(clientDir, rel);
|
|
1542
2099
|
try {
|
|
1543
|
-
const raw = await
|
|
2100
|
+
const raw = await readFile9(path, "utf8");
|
|
1544
2101
|
const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
|
|
1545
|
-
if (next !== raw) await
|
|
2102
|
+
if (next !== raw) await writeFile9(path, next);
|
|
1546
2103
|
} catch {
|
|
1547
2104
|
}
|
|
1548
2105
|
}
|
|
@@ -1558,12 +2115,12 @@ function gitInit(cwd, projectName) {
|
|
|
1558
2115
|
}
|
|
1559
2116
|
function resolveYarnBin(cwd) {
|
|
1560
2117
|
try {
|
|
1561
|
-
const yarnrc = readFileSync(
|
|
2118
|
+
const yarnrc = readFileSync(join10(cwd, ".yarnrc.yml"), "utf8");
|
|
1562
2119
|
const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
|
|
1563
|
-
if (match?.[1]) return
|
|
2120
|
+
if (match?.[1]) return join10(cwd, match[1].trim());
|
|
1564
2121
|
} catch {
|
|
1565
2122
|
}
|
|
1566
|
-
return
|
|
2123
|
+
return join10(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
|
|
1567
2124
|
}
|
|
1568
2125
|
function runInstall(cwd, pm) {
|
|
1569
2126
|
if (pm === "yarn") {
|
|
@@ -1586,6 +2143,10 @@ async function scaffold(rawOpts, templatesDir2) {
|
|
|
1586
2143
|
await writeRootEnv(opts.targetDir, opts);
|
|
1587
2144
|
await selectClientTemplate(opts.targetDir, opts);
|
|
1588
2145
|
await writeClientEnv(opts.targetDir);
|
|
2146
|
+
if (opts.authProvider === "none") {
|
|
2147
|
+
await removeAuthOnlyPaths(opts.targetDir);
|
|
2148
|
+
await applyAuthNoneVariants(opts.targetDir, opts.ui);
|
|
2149
|
+
}
|
|
1589
2150
|
if (opts.upload === "none") await removeUploadStack(opts.targetDir);
|
|
1590
2151
|
await cleanupUnusedFeatures(opts.targetDir, opts);
|
|
1591
2152
|
await writeFeaturesWiring(opts.targetDir, opts);
|
|
@@ -1594,7 +2155,8 @@ async function scaffold(rawOpts, templatesDir2) {
|
|
|
1594
2155
|
await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
|
|
1595
2156
|
await writeAuthProvider(opts.targetDir, opts.authProvider);
|
|
1596
2157
|
} else {
|
|
1597
|
-
await
|
|
2158
|
+
await removeAuthTsconfigPaths(opts.targetDir);
|
|
2159
|
+
await removeDockerComposeAuthService(opts.targetDir);
|
|
1598
2160
|
}
|
|
1599
2161
|
if (opts.upload !== "none") {
|
|
1600
2162
|
await cleanupUnusedStorage(opts.targetDir, opts.upload);
|
|
@@ -1607,25 +2169,28 @@ async function scaffold(rawOpts, templatesDir2) {
|
|
|
1607
2169
|
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1608
2170
|
}
|
|
1609
2171
|
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1610
|
-
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none") {
|
|
2172
|
+
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none" && opts.payment === "none") {
|
|
1611
2173
|
await removeStrategiesLib(opts.targetDir);
|
|
1612
2174
|
}
|
|
1613
2175
|
try {
|
|
1614
|
-
await rmdir2(
|
|
2176
|
+
await rmdir2(join10(opts.targetDir, "apps/microservices"));
|
|
1615
2177
|
} catch {
|
|
1616
2178
|
}
|
|
1617
2179
|
await writeBlueprintJson(opts.targetDir, opts);
|
|
1618
2180
|
await writeServiceBlueprints(opts.targetDir, opts);
|
|
1619
2181
|
if (opts.packageManager === "yarn") {
|
|
1620
|
-
await
|
|
2182
|
+
await writeFile9(join10(opts.targetDir, "yarn.lock"), "");
|
|
1621
2183
|
} else {
|
|
1622
|
-
await
|
|
1623
|
-
await
|
|
2184
|
+
await rm5(join10(opts.targetDir, ".yarn"), { recursive: true, force: true });
|
|
2185
|
+
await rm5(join10(opts.targetDir, ".yarnrc.yml"), { force: true });
|
|
1624
2186
|
}
|
|
1625
2187
|
if (opts.packageManager === "pnpm") {
|
|
1626
2188
|
await writePnpmWorkspace(opts.targetDir);
|
|
1627
2189
|
await rewritePnpmWorkspaceDeps(opts.targetDir);
|
|
1628
2190
|
}
|
|
2191
|
+
if (opts.packageManager === "npm") {
|
|
2192
|
+
await writeFile9(join10(opts.targetDir, ".npmrc"), "legacy-peer-deps=true\n");
|
|
2193
|
+
}
|
|
1629
2194
|
await patchGitignoreForPm(opts.targetDir, opts.packageManager);
|
|
1630
2195
|
await writeAiFiles(opts.targetDir, opts);
|
|
1631
2196
|
if (opts.install) runInstall(opts.targetDir, opts.packageManager);
|
|
@@ -1642,7 +2207,7 @@ Upgrade: https://nodejs.org
|
|
|
1642
2207
|
);
|
|
1643
2208
|
process.exit(1);
|
|
1644
2209
|
}
|
|
1645
|
-
var here =
|
|
2210
|
+
var here = dirname3(fileURLToPath2(import.meta.url));
|
|
1646
2211
|
var templatesDir = resolve2(here, "..", "templates");
|
|
1647
2212
|
async function main() {
|
|
1648
2213
|
if (!existsSync(templatesDir)) {
|