@idevconn/create-icore 0.9.3 → 0.10.0
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 +752 -190
- package/dist/index.cjs +775 -213
- package/dist/index.js +768 -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 +4 -4
- 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 +4 -4
- 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 +1 -1
- 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 +4 -4
- 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/index.cjs
CHANGED
|
@@ -47,9 +47,9 @@ function pmRun(pm, script) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
// src/lib/scaffold.ts
|
|
50
|
-
var
|
|
50
|
+
var import_promises9 = require("fs/promises");
|
|
51
51
|
var import_node_fs = require("fs");
|
|
52
|
-
var
|
|
52
|
+
var import_node_path9 = require("path");
|
|
53
53
|
var import_node_child_process = require("child_process");
|
|
54
54
|
|
|
55
55
|
// src/lib/scaffold-env.ts
|
|
@@ -215,6 +215,9 @@ async function writeGatewayEnv(targetDir, opts) {
|
|
|
215
215
|
for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
|
|
216
216
|
next = uncommentTransportEnv(next, prefix, opts.transport);
|
|
217
217
|
}
|
|
218
|
+
if (opts.authProvider === "none") {
|
|
219
|
+
next = next.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
220
|
+
}
|
|
218
221
|
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/api/.env"), next);
|
|
219
222
|
}
|
|
220
223
|
async function writeRootEnv(targetDir, opts) {
|
|
@@ -300,6 +303,7 @@ async function removeStrategiesLib(targetDir) {
|
|
|
300
303
|
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/shared/src/strategies"), { recursive: true, force: true });
|
|
301
304
|
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/shared/src/testing.ts"), { force: true });
|
|
302
305
|
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/shared/src/transport.ts"), { force: true });
|
|
306
|
+
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/shared/src/__tests__/transport.unit.test.ts"), { force: true });
|
|
303
307
|
const indexPath = (0, import_node_path2.join)(targetDir, "libs/shared/src/index.ts");
|
|
304
308
|
try {
|
|
305
309
|
const src = await (0, import_promises2.readFile)(indexPath, "utf8");
|
|
@@ -318,122 +322,6 @@ async function removeStrategiesLib(targetDir) {
|
|
|
318
322
|
} catch {
|
|
319
323
|
}
|
|
320
324
|
}
|
|
321
|
-
async function removeAuthStack(targetDir) {
|
|
322
|
-
const rmPaths = [
|
|
323
|
-
"apps/microservices/auth",
|
|
324
|
-
"libs/auth-strategies",
|
|
325
|
-
"libs/auth-client",
|
|
326
|
-
"Dockerfile.ms-auth",
|
|
327
|
-
"apps/api/src/app/auth",
|
|
328
|
-
"apps/api/src/app/profile",
|
|
329
|
-
"apps/api/src/app/abilities",
|
|
330
|
-
"libs/shared/src/abilities",
|
|
331
|
-
"apps/client/src/components/auth",
|
|
332
|
-
"apps/client/src/routes/login.tsx",
|
|
333
|
-
"apps/client/src/routes/auth.callback.tsx",
|
|
334
|
-
"apps/client/src/routes/auth.oauth.callback.tsx",
|
|
335
|
-
"apps/client/src/routes/_dashboard/profile.tsx"
|
|
336
|
-
];
|
|
337
|
-
for (const p2 of rmPaths) {
|
|
338
|
-
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
|
|
339
|
-
}
|
|
340
|
-
const appModulePath = (0, import_node_path2.join)(targetDir, "apps/api/src/app/app.module.ts");
|
|
341
|
-
try {
|
|
342
|
-
const src = await (0, import_promises2.readFile)(appModulePath, "utf8");
|
|
343
|
-
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, "");
|
|
344
|
-
await (0, import_promises2.writeFile)(appModulePath, next);
|
|
345
|
-
} catch {
|
|
346
|
-
}
|
|
347
|
-
const dashboardPath = (0, import_node_path2.join)(targetDir, "apps/client/src/routes/_dashboard.tsx");
|
|
348
|
-
try {
|
|
349
|
-
const src = await (0, import_promises2.readFile)(dashboardPath, "utf8");
|
|
350
|
-
const next = src.replace(/^import \{ useAuthStore \} from '@icore\/template-shared';\n/m, "").replace(/, redirect/g, "").replace(/\n {2}beforeLoad: \(\) => \{[\s\S]*?\n {2}\},/m, "");
|
|
351
|
-
await (0, import_promises2.writeFile)(dashboardPath, next);
|
|
352
|
-
} catch {
|
|
353
|
-
}
|
|
354
|
-
for (const alias of [
|
|
355
|
-
"@icore/auth-client",
|
|
356
|
-
"@icore/auth-supabase",
|
|
357
|
-
"@icore/auth-firebase",
|
|
358
|
-
"@icore/auth-mongodb"
|
|
359
|
-
]) {
|
|
360
|
-
await stripTsconfigPath(targetDir, alias);
|
|
361
|
-
}
|
|
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
|
-
}
|
|
373
|
-
const gatewayEnv = (0, import_node_path2.join)(targetDir, "apps/api/.env");
|
|
374
|
-
try {
|
|
375
|
-
const env = await (0, import_promises2.readFile)(gatewayEnv, "utf8");
|
|
376
|
-
const next = env.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
377
|
-
await (0, import_promises2.writeFile)(gatewayEnv, next);
|
|
378
|
-
} catch {
|
|
379
|
-
}
|
|
380
|
-
const composePath = (0, import_node_path2.join)(targetDir, "docker-compose.yml");
|
|
381
|
-
try {
|
|
382
|
-
const compose = await (0, import_promises2.readFile)(composePath, "utf8");
|
|
383
|
-
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, "");
|
|
384
|
-
await (0, import_promises2.writeFile)(composePath, next);
|
|
385
|
-
} catch {
|
|
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
|
-
}
|
|
436
|
-
}
|
|
437
325
|
async function removeUploadStack(targetDir) {
|
|
438
326
|
const paths = [
|
|
439
327
|
"apps/microservices/upload",
|
|
@@ -475,9 +363,674 @@ async function removeUploadStack(targetDir) {
|
|
|
475
363
|
}
|
|
476
364
|
}
|
|
477
365
|
|
|
366
|
+
// src/lib/scaffold-auth-none.ts
|
|
367
|
+
var import_promises3 = require("fs/promises");
|
|
368
|
+
var import_node_path3 = require("path");
|
|
369
|
+
async function stripDeps2(pkgPath, names) {
|
|
370
|
+
try {
|
|
371
|
+
const raw = await (0, import_promises3.readFile)(pkgPath, "utf8");
|
|
372
|
+
const pkg = JSON.parse(raw);
|
|
373
|
+
for (const n of names) {
|
|
374
|
+
if (pkg.dependencies) delete pkg.dependencies[n];
|
|
375
|
+
if (pkg.devDependencies) delete pkg.devDependencies[n];
|
|
376
|
+
}
|
|
377
|
+
await (0, import_promises3.writeFile)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
378
|
+
} catch {
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
var AUTH_ONLY_PATHS = [
|
|
382
|
+
"apps/microservices/auth",
|
|
383
|
+
"libs/auth-strategies",
|
|
384
|
+
"libs/auth-client",
|
|
385
|
+
"Dockerfile.ms-auth",
|
|
386
|
+
"apps/api/src/app/auth",
|
|
387
|
+
"apps/api/src/app/profile",
|
|
388
|
+
"apps/api/src/app/abilities",
|
|
389
|
+
"libs/shared/src/abilities",
|
|
390
|
+
"apps/client/src/components/auth",
|
|
391
|
+
"apps/client/src/routes/login.tsx",
|
|
392
|
+
"apps/client/src/routes/auth.callback.tsx",
|
|
393
|
+
"apps/client/src/routes/auth.oauth.callback.tsx",
|
|
394
|
+
"apps/client/src/routes/_dashboard/profile.tsx",
|
|
395
|
+
"libs/template-shared/src/lib/abilities"
|
|
396
|
+
];
|
|
397
|
+
async function removeAuthOnlyPaths(targetDir) {
|
|
398
|
+
for (const p2 of AUTH_ONLY_PATHS) {
|
|
399
|
+
await (0, import_promises3.rm)((0, import_node_path3.join)(targetDir, p2), { recursive: true, force: true });
|
|
400
|
+
}
|
|
401
|
+
await stripDeps2((0, import_node_path3.join)(targetDir, "apps/api/package.json"), [
|
|
402
|
+
"@icore/auth-client",
|
|
403
|
+
"cookie-parser"
|
|
404
|
+
]);
|
|
405
|
+
}
|
|
406
|
+
async function stripTsconfigPath2(targetDir, alias) {
|
|
407
|
+
const tsconfigPath = (0, import_node_path3.join)(targetDir, "tsconfig.base.json");
|
|
408
|
+
try {
|
|
409
|
+
const src = await (0, import_promises3.readFile)(tsconfigPath, "utf8");
|
|
410
|
+
const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
|
|
411
|
+
const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
|
|
412
|
+
if (pretty !== src) {
|
|
413
|
+
await (0, import_promises3.writeFile)(tsconfigPath, pretty);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const parsed = JSON.parse(src);
|
|
417
|
+
if (parsed.compilerOptions?.paths) delete parsed.compilerOptions.paths[alias];
|
|
418
|
+
await (0, import_promises3.writeFile)(tsconfigPath, JSON.stringify(parsed));
|
|
419
|
+
} catch {
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async function removeAuthTsconfigPaths(targetDir) {
|
|
423
|
+
for (const alias of [
|
|
424
|
+
"@icore/auth-client",
|
|
425
|
+
"@icore/auth-supabase",
|
|
426
|
+
"@icore/auth-firebase",
|
|
427
|
+
"@icore/auth-mongodb"
|
|
428
|
+
]) {
|
|
429
|
+
await stripTsconfigPath2(targetDir, alias);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
async function removeDockerComposeAuthService(targetDir) {
|
|
433
|
+
const composePath = (0, import_node_path3.join)(targetDir, "docker-compose.yml");
|
|
434
|
+
try {
|
|
435
|
+
const compose = await (0, import_promises3.readFile)(composePath, "utf8");
|
|
436
|
+
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, "");
|
|
437
|
+
await (0, import_promises3.writeFile)(composePath, next);
|
|
438
|
+
} catch {
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
var GATEWAY_MAIN_TS = `import { Logger } from '@nestjs/common';
|
|
442
|
+
import { NestFactory } from '@nestjs/core';
|
|
443
|
+
import { NestExpressApplication } from '@nestjs/platform-express';
|
|
444
|
+
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
|
445
|
+
import { formatGatewayBanner } from '@icore/shared';
|
|
446
|
+
import { AppModule } from './app/app.module';
|
|
447
|
+
import { GATEWAY_SERVICES } from './app/gateway-services';
|
|
448
|
+
import pkg from '@icore/package.json';
|
|
449
|
+
|
|
450
|
+
const DEFAULT_PORT = 3001;
|
|
451
|
+
|
|
452
|
+
async function bootstrap() {
|
|
453
|
+
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
|
454
|
+
app.setGlobalPrefix('api');
|
|
455
|
+
|
|
456
|
+
const swaggerConfig = new DocumentBuilder()
|
|
457
|
+
.setTitle('iCore API')
|
|
458
|
+
.setDescription('iCore Gateway HTTP surface')
|
|
459
|
+
.setVersion(pkg.version)
|
|
460
|
+
.addBearerAuth()
|
|
461
|
+
.build();
|
|
462
|
+
const document = SwaggerModule.createDocument(app, swaggerConfig);
|
|
463
|
+
SwaggerModule.setup('api/docs', app, document);
|
|
464
|
+
|
|
465
|
+
const port = Number(process.env.API_PORT ?? DEFAULT_PORT);
|
|
466
|
+
await app.listen(port);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
bootstrap()
|
|
470
|
+
.then(() => {
|
|
471
|
+
const origin = process.env.API_ORIGIN ?? 'http://localhost';
|
|
472
|
+
const port = Number(process.env.API_PORT ?? DEFAULT_PORT);
|
|
473
|
+
new Logger('API-Bootstrap').log(
|
|
474
|
+
formatGatewayBanner({ port, origin, services: GATEWAY_SERVICES }),
|
|
475
|
+
);
|
|
476
|
+
})
|
|
477
|
+
.catch((err) => {
|
|
478
|
+
new Logger('API-Bootstrap').error(
|
|
479
|
+
'Gateway bootstrap failed',
|
|
480
|
+
err instanceof Error ? err.stack : err,
|
|
481
|
+
);
|
|
482
|
+
process.exit(1);
|
|
483
|
+
});
|
|
484
|
+
`;
|
|
485
|
+
var GATEWAY_APP_MODULE_TS = `import { join } from 'node:path';
|
|
486
|
+
import { Module } from '@nestjs/common';
|
|
487
|
+
import { ConfigModule } from '@nestjs/config';
|
|
488
|
+
import { ThrottlerModule, seconds } from '@nestjs/throttler';
|
|
489
|
+
import { StorageModule } from './storage/storage.module';
|
|
490
|
+
import { FeaturesModule } from './features.module';
|
|
491
|
+
|
|
492
|
+
@Module({
|
|
493
|
+
imports: [
|
|
494
|
+
ConfigModule.forRoot({
|
|
495
|
+
isGlobal: true,
|
|
496
|
+
envFilePath: [join(process.cwd(), 'apps/api/.env'), join(process.cwd(), '.env')],
|
|
497
|
+
}),
|
|
498
|
+
ThrottlerModule.forRoot([{ name: 'auth-burst', ttl: seconds(60), limit: 10 }]),
|
|
499
|
+
StorageModule,
|
|
500
|
+
FeaturesModule,
|
|
501
|
+
],
|
|
502
|
+
})
|
|
503
|
+
export class AppModule {}
|
|
504
|
+
`;
|
|
505
|
+
var SHARED_CLIENT_TS = `// Browser-safe subset of @icore/shared.
|
|
506
|
+
// Import from '@icore/shared/client' in client-side code to avoid pulling
|
|
507
|
+
// in NestJS / Node.js-only modules (transport, strategies, contracts).
|
|
508
|
+
export * from './types';
|
|
509
|
+
`;
|
|
510
|
+
var SHARED_INDEX_TS = `export * from './env';
|
|
511
|
+
export * from './bootstrap';
|
|
512
|
+
export * from './jobs';
|
|
513
|
+
export * from './strategies';
|
|
514
|
+
export * from './transport';
|
|
515
|
+
export * from './types';
|
|
516
|
+
`;
|
|
517
|
+
var TEMPLATE_SHARED_INDEX_TS = `export * from './lib/api/create-api.js';
|
|
518
|
+
export * from './lib/stores/auth.store.js';
|
|
519
|
+
export * from './lib/stores/loading.store.js';
|
|
520
|
+
export * from './lib/i18n/create-i18n.js';
|
|
521
|
+
export * from './lib/i18n/keys.js';
|
|
522
|
+
export * from './lib/notify/use-notify.js';
|
|
523
|
+
export * from './lib/draft/index.js';
|
|
524
|
+
export * from './lib/landing/LandingPage.js';
|
|
525
|
+
export * from './lib/stores/theme.store.js';
|
|
526
|
+
`;
|
|
527
|
+
var SHADCN_MAIN_TSX = `import './globals.css';
|
|
528
|
+
import { StrictMode } from 'react';
|
|
529
|
+
import { createRoot } from 'react-dom/client';
|
|
530
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
531
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
532
|
+
import {
|
|
533
|
+
createIcoreApi,
|
|
534
|
+
createIcoreI18n,
|
|
535
|
+
ICORE_LOCALES,
|
|
536
|
+
useThemeStore,
|
|
537
|
+
} from '@icore/template-shared';
|
|
538
|
+
import { I18nextProvider } from 'react-i18next';
|
|
539
|
+
import { Toaster } from 'sonner';
|
|
540
|
+
import { routeTree } from './routeTree.gen';
|
|
541
|
+
import { wireShadcnNotifier } from './lib/notify';
|
|
542
|
+
|
|
543
|
+
const queryClient = new QueryClient({
|
|
544
|
+
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
548
|
+
|
|
549
|
+
declare module '@tanstack/react-router' {
|
|
550
|
+
interface Register {
|
|
551
|
+
router: typeof router;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
|
|
556
|
+
|
|
557
|
+
// Single shared API instance \u2014 used by every query in src/queries/
|
|
558
|
+
export const api = createIcoreApi({
|
|
559
|
+
baseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
wireShadcnNotifier();
|
|
563
|
+
|
|
564
|
+
// Apply the theme class before React mounts so the first paint is correct
|
|
565
|
+
const applyTheme = (mode: 'light' | 'dark') => {
|
|
566
|
+
document.documentElement.classList.toggle('dark', mode === 'dark');
|
|
567
|
+
};
|
|
568
|
+
applyTheme(useThemeStore.getState().mode);
|
|
569
|
+
useThemeStore.subscribe((s) => applyTheme(s.mode));
|
|
570
|
+
|
|
571
|
+
createRoot(document.getElementById('root')!).render(
|
|
572
|
+
<StrictMode>
|
|
573
|
+
<I18nextProvider i18n={i18n}>
|
|
574
|
+
<QueryClientProvider client={queryClient}>
|
|
575
|
+
<RouterProvider router={router} />
|
|
576
|
+
<Toaster richColors />
|
|
577
|
+
</QueryClientProvider>
|
|
578
|
+
</I18nextProvider>
|
|
579
|
+
</StrictMode>,
|
|
580
|
+
);
|
|
581
|
+
`;
|
|
582
|
+
var SHADCN_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
|
|
583
|
+
import { MainLayout } from '../layouts/MainLayout';
|
|
584
|
+
|
|
585
|
+
export const Route = createFileRoute('/_dashboard')({
|
|
586
|
+
component: () => (
|
|
587
|
+
<MainLayout>
|
|
588
|
+
<Outlet />
|
|
589
|
+
</MainLayout>
|
|
590
|
+
),
|
|
591
|
+
});
|
|
592
|
+
`;
|
|
593
|
+
var SHADCN_INDEX_TSX = `import { createFileRoute } from '@tanstack/react-router';
|
|
594
|
+
import { LandingPage } from '@icore/template-shared';
|
|
595
|
+
|
|
596
|
+
// All version strings are injected at build time by vite.config.mts
|
|
597
|
+
// (reads root package.json via fs.readFileSync so they stay accurate
|
|
598
|
+
// even when workspace packages are bumped independently).
|
|
599
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
600
|
+
|
|
601
|
+
export const Route = createFileRoute('/')({
|
|
602
|
+
component: () => (
|
|
603
|
+
<LandingPage
|
|
604
|
+
coreVersion={APP_VERSION}
|
|
605
|
+
uiLibrary="shadcn"
|
|
606
|
+
deps={[
|
|
607
|
+
{ name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
|
|
608
|
+
{ name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
|
|
609
|
+
{ name: 'tailwindcss', version: (import.meta.env.VITE_DEP_TAILWINDCSS as string) ?? '?' },
|
|
610
|
+
{
|
|
611
|
+
name: '@tanstack/react-router',
|
|
612
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
name: '@tanstack/react-query',
|
|
616
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
|
|
617
|
+
},
|
|
618
|
+
{ name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
|
|
619
|
+
{ name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
|
|
620
|
+
]}
|
|
621
|
+
ctaHref="/dashboard"
|
|
622
|
+
ctaLabel="Dashboard \u2192"
|
|
623
|
+
/>
|
|
624
|
+
),
|
|
625
|
+
});
|
|
626
|
+
`;
|
|
627
|
+
var SHADCN_LAYOUT_HEADER_TSX = `import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
|
|
628
|
+
import { ThemeToggle } from '../ThemeToggle';
|
|
629
|
+
|
|
630
|
+
const LOCALES: { code: IcoreLocale; label: string }[] = [
|
|
631
|
+
{ code: 'en', label: 'EN' },
|
|
632
|
+
{ code: 'ru', label: 'RU' },
|
|
633
|
+
{ code: 'he', label: 'HE' },
|
|
634
|
+
];
|
|
635
|
+
|
|
636
|
+
export function LayoutHeader() {
|
|
637
|
+
function handleLocale(code: IcoreLocale) {
|
|
638
|
+
setStoredLocale(code);
|
|
639
|
+
window.location.reload();
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return (
|
|
643
|
+
<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">
|
|
644
|
+
<div className="flex items-center gap-2">
|
|
645
|
+
<div className="flex h-6 w-6 items-center justify-center rounded bg-[--color-primary]">
|
|
646
|
+
<span className="text-xs font-bold text-[--color-primary-foreground]">i</span>
|
|
647
|
+
</div>
|
|
648
|
+
<span className="text-sm font-semibold tracking-tight">iCore</span>
|
|
649
|
+
</div>
|
|
650
|
+
|
|
651
|
+
<div className="flex items-center gap-1">
|
|
652
|
+
<div className="flex items-center rounded-md border border-[--color-border] overflow-hidden mr-2">
|
|
653
|
+
{LOCALES.map(({ code, label }) => (
|
|
654
|
+
<button
|
|
655
|
+
key={code}
|
|
656
|
+
type="button"
|
|
657
|
+
onClick={() => handleLocale(code)}
|
|
658
|
+
className="px-2.5 py-1 text-xs text-[--color-muted-foreground] hover:bg-[--color-muted] hover:text-[--color-foreground] transition-colors cursor-pointer"
|
|
659
|
+
>
|
|
660
|
+
{label}
|
|
661
|
+
</button>
|
|
662
|
+
))}
|
|
663
|
+
</div>
|
|
664
|
+
|
|
665
|
+
<ThemeToggle />
|
|
666
|
+
</div>
|
|
667
|
+
</header>
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
`;
|
|
671
|
+
var ANTD_MAIN_TSX = `import './globals.less';
|
|
672
|
+
import { StrictMode } from 'react';
|
|
673
|
+
import { createRoot } from 'react-dom/client';
|
|
674
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
675
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
676
|
+
import { ConfigProvider, App as AntApp, theme } from 'antd';
|
|
677
|
+
import { I18nextProvider } from 'react-i18next';
|
|
678
|
+
import {
|
|
679
|
+
createIcoreApi,
|
|
680
|
+
createIcoreI18n,
|
|
681
|
+
ICORE_LOCALES,
|
|
682
|
+
useThemeStore,
|
|
683
|
+
} from '@icore/template-shared';
|
|
684
|
+
import { routeTree } from './routeTree.gen';
|
|
685
|
+
|
|
686
|
+
const queryClient = new QueryClient({
|
|
687
|
+
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
691
|
+
|
|
692
|
+
declare module '@tanstack/react-router' {
|
|
693
|
+
interface Register {
|
|
694
|
+
router: typeof router;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
|
|
699
|
+
|
|
700
|
+
// Single shared API instance \u2014 used by every query in src/queries/
|
|
701
|
+
export const api = createIcoreApi({
|
|
702
|
+
baseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
function Root() {
|
|
706
|
+
const mode = useThemeStore((s) => s.mode);
|
|
707
|
+
const algorithm = mode === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm;
|
|
708
|
+
return (
|
|
709
|
+
<ConfigProvider theme={{ algorithm, token: { colorPrimary: '#22c55e', colorLink: '#22c55e' } }}>
|
|
710
|
+
<AntApp>
|
|
711
|
+
<QueryClientProvider client={queryClient}>
|
|
712
|
+
<RouterProvider router={router} />
|
|
713
|
+
</QueryClientProvider>
|
|
714
|
+
</AntApp>
|
|
715
|
+
</ConfigProvider>
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
createRoot(document.getElementById('root')!).render(
|
|
720
|
+
<StrictMode>
|
|
721
|
+
<I18nextProvider i18n={i18n}>
|
|
722
|
+
<Root />
|
|
723
|
+
</I18nextProvider>
|
|
724
|
+
</StrictMode>,
|
|
725
|
+
);
|
|
726
|
+
`;
|
|
727
|
+
var ANTD_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
|
|
728
|
+
import { MainLayout } from '../layouts/MainLayout';
|
|
729
|
+
|
|
730
|
+
export const Route = createFileRoute('/_dashboard')({
|
|
731
|
+
component: () => (
|
|
732
|
+
<MainLayout>
|
|
733
|
+
<Outlet />
|
|
734
|
+
</MainLayout>
|
|
735
|
+
),
|
|
736
|
+
});
|
|
737
|
+
`;
|
|
738
|
+
var ANTD_INDEX_TSX = `import { createFileRoute, Link } from '@tanstack/react-router';
|
|
739
|
+
import { LandingPage } from '@icore/template-shared';
|
|
740
|
+
|
|
741
|
+
// All version strings are injected at build time by vite.config.mts
|
|
742
|
+
// (reads root package.json via fs.readFileSync so they stay accurate
|
|
743
|
+
// even when workspace packages are bumped independently).
|
|
744
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
745
|
+
|
|
746
|
+
export const Route = createFileRoute('/')({
|
|
747
|
+
component: () => (
|
|
748
|
+
<LandingPage
|
|
749
|
+
coreVersion={APP_VERSION}
|
|
750
|
+
uiLibrary="antd"
|
|
751
|
+
deps={[
|
|
752
|
+
{ name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
|
|
753
|
+
{ name: 'antd', version: (import.meta.env.VITE_DEP_ANTD as string) ?? '?' },
|
|
754
|
+
{ name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
|
|
755
|
+
{
|
|
756
|
+
name: '@tanstack/react-router',
|
|
757
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
name: '@tanstack/react-query',
|
|
761
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
|
|
762
|
+
},
|
|
763
|
+
{ name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
|
|
764
|
+
{ name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
|
|
765
|
+
]}
|
|
766
|
+
ctaHref="/dashboard"
|
|
767
|
+
ctaLabel={<Link to="/dashboard">Dashboard \u2192</Link>}
|
|
768
|
+
/>
|
|
769
|
+
),
|
|
770
|
+
});
|
|
771
|
+
`;
|
|
772
|
+
var ANTD_LAYOUT_HEADER_TSX = `import { Button, Layout, Space } from 'antd';
|
|
773
|
+
import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
|
|
774
|
+
import { ThemeToggle } from '../ThemeToggle';
|
|
775
|
+
|
|
776
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
777
|
+
|
|
778
|
+
const LOCALES: { code: IcoreLocale; label: string }[] = [
|
|
779
|
+
{ code: 'en', label: 'EN' },
|
|
780
|
+
{ code: 'ru', label: 'RU' },
|
|
781
|
+
{ code: 'he', label: 'HE' },
|
|
782
|
+
];
|
|
783
|
+
|
|
784
|
+
export function LayoutHeader() {
|
|
785
|
+
function handleLocale(code: IcoreLocale) {
|
|
786
|
+
setStoredLocale(code);
|
|
787
|
+
window.location.reload();
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return (
|
|
791
|
+
<Layout.Header
|
|
792
|
+
style={{
|
|
793
|
+
display: 'flex',
|
|
794
|
+
alignItems: 'center',
|
|
795
|
+
justifyContent: 'space-between',
|
|
796
|
+
padding: '0 24px',
|
|
797
|
+
}}
|
|
798
|
+
>
|
|
799
|
+
<Space>
|
|
800
|
+
<span style={{ color: '#fff', fontWeight: 600, fontSize: 16 }}>iCore</span>
|
|
801
|
+
<span
|
|
802
|
+
style={{
|
|
803
|
+
color: 'rgba(255,255,255,0.45)',
|
|
804
|
+
fontSize: 11,
|
|
805
|
+
background: 'rgba(255,255,255,0.1)',
|
|
806
|
+
padding: '1px 6px',
|
|
807
|
+
borderRadius: 4,
|
|
808
|
+
}}
|
|
809
|
+
>
|
|
810
|
+
v{APP_VERSION}
|
|
811
|
+
</span>
|
|
812
|
+
</Space>
|
|
813
|
+
|
|
814
|
+
<Space size="middle">
|
|
815
|
+
<Space size={4}>
|
|
816
|
+
{LOCALES.map(({ code, label }) => (
|
|
817
|
+
<Button
|
|
818
|
+
key={code}
|
|
819
|
+
size="small"
|
|
820
|
+
type="text"
|
|
821
|
+
style={{ color: 'rgba(255,255,255,0.65)' }}
|
|
822
|
+
onClick={() => handleLocale(code)}
|
|
823
|
+
>
|
|
824
|
+
{label}
|
|
825
|
+
</Button>
|
|
826
|
+
))}
|
|
827
|
+
</Space>
|
|
828
|
+
|
|
829
|
+
<ThemeToggle />
|
|
830
|
+
</Space>
|
|
831
|
+
</Layout.Header>
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
`;
|
|
835
|
+
var MUI_MAIN_TSX = `import './globals.css';
|
|
836
|
+
import { StrictMode, useMemo } from 'react';
|
|
837
|
+
import { createRoot } from 'react-dom/client';
|
|
838
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
839
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
840
|
+
import { CssBaseline, ThemeProvider, createTheme } from '@mui/material';
|
|
841
|
+
import { I18nextProvider } from 'react-i18next';
|
|
842
|
+
import {
|
|
843
|
+
createIcoreApi,
|
|
844
|
+
createIcoreI18n,
|
|
845
|
+
ICORE_LOCALES,
|
|
846
|
+
useThemeStore,
|
|
847
|
+
} from '@icore/template-shared';
|
|
848
|
+
import { routeTree } from './routeTree.gen';
|
|
849
|
+
import { wireMuiNotifier } from './lib/notify';
|
|
850
|
+
|
|
851
|
+
const queryClient = new QueryClient({
|
|
852
|
+
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
856
|
+
|
|
857
|
+
declare module '@tanstack/react-router' {
|
|
858
|
+
interface Register {
|
|
859
|
+
router: typeof router;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
|
|
864
|
+
|
|
865
|
+
export const api = createIcoreApi({
|
|
866
|
+
baseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
wireMuiNotifier();
|
|
870
|
+
|
|
871
|
+
function Root() {
|
|
872
|
+
const mode = useThemeStore((s) => s.mode);
|
|
873
|
+
const theme = useMemo(
|
|
874
|
+
() => createTheme({ palette: { mode, primary: { main: '#22c55e' } } }),
|
|
875
|
+
[mode],
|
|
876
|
+
);
|
|
877
|
+
return (
|
|
878
|
+
<ThemeProvider theme={theme}>
|
|
879
|
+
<CssBaseline />
|
|
880
|
+
<QueryClientProvider client={queryClient}>
|
|
881
|
+
<RouterProvider router={router} />
|
|
882
|
+
</QueryClientProvider>
|
|
883
|
+
</ThemeProvider>
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
createRoot(document.getElementById('root')!).render(
|
|
888
|
+
<StrictMode>
|
|
889
|
+
<I18nextProvider i18n={i18n}>
|
|
890
|
+
<Root />
|
|
891
|
+
</I18nextProvider>
|
|
892
|
+
</StrictMode>,
|
|
893
|
+
);
|
|
894
|
+
`;
|
|
895
|
+
var MUI_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
|
|
896
|
+
import { MainLayout } from '../layouts/MainLayout';
|
|
897
|
+
|
|
898
|
+
export const Route = createFileRoute('/_dashboard')({
|
|
899
|
+
component: () => (
|
|
900
|
+
<MainLayout>
|
|
901
|
+
<Outlet />
|
|
902
|
+
</MainLayout>
|
|
903
|
+
),
|
|
904
|
+
});
|
|
905
|
+
`;
|
|
906
|
+
var MUI_INDEX_TSX = `import { createFileRoute, Link } from '@tanstack/react-router';
|
|
907
|
+
import { LandingPage } from '@icore/template-shared';
|
|
908
|
+
|
|
909
|
+
// All version strings are injected at build time by vite.config.mts
|
|
910
|
+
// (reads root package.json via fs.readFileSync so they stay accurate
|
|
911
|
+
// even when workspace packages are bumped independently).
|
|
912
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
913
|
+
|
|
914
|
+
export const Route = createFileRoute('/')({
|
|
915
|
+
component: () => (
|
|
916
|
+
<LandingPage
|
|
917
|
+
coreVersion={APP_VERSION}
|
|
918
|
+
uiLibrary="mui"
|
|
919
|
+
deps={[
|
|
920
|
+
{ name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
|
|
921
|
+
{ name: '@mui/material', version: (import.meta.env.VITE_DEP_MUI as string) ?? '?' },
|
|
922
|
+
{ name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
|
|
923
|
+
{
|
|
924
|
+
name: '@tanstack/react-router',
|
|
925
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
name: '@tanstack/react-query',
|
|
929
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
|
|
930
|
+
},
|
|
931
|
+
{ name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
|
|
932
|
+
{ name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
|
|
933
|
+
]}
|
|
934
|
+
ctaHref="/dashboard"
|
|
935
|
+
ctaLabel={<Link to="/dashboard">Dashboard \u2192</Link>}
|
|
936
|
+
/>
|
|
937
|
+
),
|
|
938
|
+
});
|
|
939
|
+
`;
|
|
940
|
+
var MUI_LAYOUT_HEADER_TSX = `import { AppBar, Box, Button, Toolbar, Typography } from '@mui/material';
|
|
941
|
+
import { useTranslation } from 'react-i18next';
|
|
942
|
+
import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
|
|
943
|
+
import { ThemeToggle } from '../ThemeToggle';
|
|
944
|
+
|
|
945
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
946
|
+
|
|
947
|
+
const LOCALES: { code: IcoreLocale; label: string }[] = [
|
|
948
|
+
{ code: 'en', label: 'EN' },
|
|
949
|
+
{ code: 'ru', label: 'RU' },
|
|
950
|
+
{ code: 'he', label: 'HE' },
|
|
951
|
+
];
|
|
952
|
+
|
|
953
|
+
export function LayoutHeader() {
|
|
954
|
+
const { i18n } = useTranslation();
|
|
955
|
+
const currentLocale = i18n.language as IcoreLocale;
|
|
956
|
+
|
|
957
|
+
function handleLocale(code: IcoreLocale) {
|
|
958
|
+
setStoredLocale(code);
|
|
959
|
+
window.location.reload();
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
return (
|
|
963
|
+
<AppBar position="sticky" color="default" elevation={1}>
|
|
964
|
+
<Toolbar sx={{ justifyContent: 'space-between' }}>
|
|
965
|
+
<Typography variant="h6" component="div" fontWeight={600}>
|
|
966
|
+
iCore{' '}
|
|
967
|
+
<span style={{ opacity: 0.6, fontSize: '0.75em', fontWeight: 400 }}>v{APP_VERSION}</span>
|
|
968
|
+
</Typography>
|
|
969
|
+
|
|
970
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
971
|
+
<Box sx={{ display: 'flex', gap: 0.5 }}>
|
|
972
|
+
{LOCALES.map(({ code, label }) => (
|
|
973
|
+
<Button
|
|
974
|
+
key={code}
|
|
975
|
+
size="small"
|
|
976
|
+
variant={currentLocale === code ? 'contained' : 'text'}
|
|
977
|
+
onClick={() => handleLocale(code)}
|
|
978
|
+
>
|
|
979
|
+
{label}
|
|
980
|
+
</Button>
|
|
981
|
+
))}
|
|
982
|
+
</Box>
|
|
983
|
+
|
|
984
|
+
<ThemeToggle />
|
|
985
|
+
</Box>
|
|
986
|
+
</Toolbar>
|
|
987
|
+
</AppBar>
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
`;
|
|
991
|
+
var COMMON_VARIANTS = {
|
|
992
|
+
"apps/api/src/main.ts": GATEWAY_MAIN_TS,
|
|
993
|
+
"apps/api/src/app/app.module.ts": GATEWAY_APP_MODULE_TS,
|
|
994
|
+
"libs/shared/src/client.ts": SHARED_CLIENT_TS,
|
|
995
|
+
"libs/shared/src/index.ts": SHARED_INDEX_TS,
|
|
996
|
+
"libs/template-shared/src/index.ts": TEMPLATE_SHARED_INDEX_TS
|
|
997
|
+
};
|
|
998
|
+
var UI_VARIANTS = {
|
|
999
|
+
shadcn: {
|
|
1000
|
+
"apps/client/src/main.tsx": SHADCN_MAIN_TSX,
|
|
1001
|
+
"apps/client/src/routes/_dashboard.tsx": SHADCN_DASHBOARD_TSX,
|
|
1002
|
+
"apps/client/src/routes/index.tsx": SHADCN_INDEX_TSX,
|
|
1003
|
+
"apps/client/src/components/layout/LayoutHeader.tsx": SHADCN_LAYOUT_HEADER_TSX
|
|
1004
|
+
},
|
|
1005
|
+
antd: {
|
|
1006
|
+
"apps/client/src/main.tsx": ANTD_MAIN_TSX,
|
|
1007
|
+
"apps/client/src/routes/_dashboard.tsx": ANTD_DASHBOARD_TSX,
|
|
1008
|
+
"apps/client/src/routes/index.tsx": ANTD_INDEX_TSX,
|
|
1009
|
+
"apps/client/src/components/layout/LayoutHeader.tsx": ANTD_LAYOUT_HEADER_TSX
|
|
1010
|
+
},
|
|
1011
|
+
mui: {
|
|
1012
|
+
"apps/client/src/main.tsx": MUI_MAIN_TSX,
|
|
1013
|
+
"apps/client/src/routes/_dashboard.tsx": MUI_DASHBOARD_TSX,
|
|
1014
|
+
"apps/client/src/routes/index.tsx": MUI_INDEX_TSX,
|
|
1015
|
+
"apps/client/src/components/layout/LayoutHeader.tsx": MUI_LAYOUT_HEADER_TSX
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
async function applyAuthNoneVariants(targetDir, ui) {
|
|
1019
|
+
const uiFiles = UI_VARIANTS[ui] ?? UI_VARIANTS["shadcn"];
|
|
1020
|
+
const all = { ...COMMON_VARIANTS, ...uiFiles };
|
|
1021
|
+
for (const [rel, content] of Object.entries(all)) {
|
|
1022
|
+
const dest = (0, import_node_path3.join)(targetDir, rel);
|
|
1023
|
+
try {
|
|
1024
|
+
await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(dest), { recursive: true });
|
|
1025
|
+
await (0, import_promises3.writeFile)(dest, content);
|
|
1026
|
+
} catch {
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
478
1031
|
// src/manifest/wire-features.ts
|
|
479
|
-
var
|
|
480
|
-
var
|
|
1032
|
+
var import_promises5 = require("fs/promises");
|
|
1033
|
+
var import_node_path5 = require("path");
|
|
481
1034
|
|
|
482
1035
|
// src/manifest/index.ts
|
|
483
1036
|
var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
|
|
@@ -637,8 +1190,8 @@ var MANIFEST = {
|
|
|
637
1190
|
};
|
|
638
1191
|
|
|
639
1192
|
// src/manifest/wire-provider.ts
|
|
640
|
-
var
|
|
641
|
-
var
|
|
1193
|
+
var import_promises4 = require("fs/promises");
|
|
1194
|
+
var import_node_path4 = require("path");
|
|
642
1195
|
async function writeProvider(targetDir, axis, provider) {
|
|
643
1196
|
const nestModule = axis.section[provider]?.nestModule;
|
|
644
1197
|
if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
|
|
@@ -649,27 +1202,27 @@ const ENV_PATH = '${axis.envPath}';
|
|
|
649
1202
|
|
|
650
1203
|
export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
|
|
651
1204
|
`;
|
|
652
|
-
await (0,
|
|
1205
|
+
await (0, import_promises4.writeFile)((0, import_node_path4.join)(targetDir, axis.providerFile), content);
|
|
653
1206
|
}
|
|
654
1207
|
async function stripJsonKeys(path, drop) {
|
|
655
1208
|
try {
|
|
656
|
-
const pkg = JSON.parse(await (0,
|
|
1209
|
+
const pkg = JSON.parse(await (0, import_promises4.readFile)(path, "utf8"));
|
|
657
1210
|
for (const field of ["dependencies", "devDependencies"]) {
|
|
658
1211
|
const deps = pkg[field];
|
|
659
1212
|
if (!deps) continue;
|
|
660
1213
|
for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
|
|
661
1214
|
}
|
|
662
|
-
await (0,
|
|
1215
|
+
await (0, import_promises4.writeFile)(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
663
1216
|
} catch {
|
|
664
1217
|
}
|
|
665
1218
|
}
|
|
666
1219
|
async function stripTsconfigKeys(targetDir, aliases) {
|
|
667
|
-
const path = (0,
|
|
1220
|
+
const path = (0, import_node_path4.join)(targetDir, "tsconfig.base.json");
|
|
668
1221
|
try {
|
|
669
|
-
const parsed = JSON.parse(await (0,
|
|
1222
|
+
const parsed = JSON.parse(await (0, import_promises4.readFile)(path, "utf8"));
|
|
670
1223
|
const paths = parsed.compilerOptions?.paths;
|
|
671
1224
|
if (paths) for (const a of aliases) delete paths[a];
|
|
672
|
-
await (0,
|
|
1225
|
+
await (0, import_promises4.writeFile)(path, JSON.stringify(parsed, null, 2) + "\n");
|
|
673
1226
|
} catch {
|
|
674
1227
|
}
|
|
675
1228
|
}
|
|
@@ -678,10 +1231,10 @@ async function cleanupUnusedAxis(targetDir, axis, chosen) {
|
|
|
678
1231
|
if (provider === chosen) continue;
|
|
679
1232
|
const unit = axis.section[provider];
|
|
680
1233
|
for (const dir of unit.libDirs)
|
|
681
|
-
await (0,
|
|
682
|
-
for (const t of unit.appTests ?? []) await (0,
|
|
1234
|
+
await (0, import_promises4.rm)((0, import_node_path4.join)(targetDir, dir), { recursive: true, force: true });
|
|
1235
|
+
for (const t of unit.appTests ?? []) await (0, import_promises4.rm)((0, import_node_path4.join)(targetDir, t), { force: true });
|
|
683
1236
|
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
684
|
-
await stripJsonKeys((0,
|
|
1237
|
+
await stripJsonKeys((0, import_node_path4.join)(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
|
|
685
1238
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
686
1239
|
}
|
|
687
1240
|
}
|
|
@@ -710,7 +1263,7 @@ async function writeFeaturesWiring(targetDir, opts) {
|
|
|
710
1263
|
})
|
|
711
1264
|
export class FeaturesModule {}
|
|
712
1265
|
`;
|
|
713
|
-
await (0,
|
|
1266
|
+
await (0, import_promises5.writeFile)((0, import_node_path5.join)(targetDir, FEATURES_MODULE), featuresModule);
|
|
714
1267
|
const services = [];
|
|
715
1268
|
if (opts.authProvider !== "none") services.push({ name: "auth", prefix: "AUTH" });
|
|
716
1269
|
if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
|
|
@@ -724,14 +1277,14 @@ export const GATEWAY_SERVICES = [
|
|
|
724
1277
|
${entries}
|
|
725
1278
|
];
|
|
726
1279
|
`;
|
|
727
|
-
await (0,
|
|
1280
|
+
await (0, import_promises5.writeFile)((0, import_node_path5.join)(targetDir, GATEWAY_SERVICES), gatewayServices);
|
|
728
1281
|
}
|
|
729
1282
|
async function stripJobsDockerCompose(targetDir) {
|
|
730
|
-
const composePath = (0,
|
|
1283
|
+
const composePath = (0, import_node_path5.join)(targetDir, "docker-compose.yml");
|
|
731
1284
|
try {
|
|
732
|
-
const compose = await (0,
|
|
1285
|
+
const compose = await (0, import_promises5.readFile)(composePath, "utf8");
|
|
733
1286
|
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, "");
|
|
734
|
-
await (0,
|
|
1287
|
+
await (0, import_promises5.writeFile)(composePath, next);
|
|
735
1288
|
} catch {
|
|
736
1289
|
}
|
|
737
1290
|
}
|
|
@@ -741,34 +1294,38 @@ async function cleanupUnusedFeatures(targetDir, opts) {
|
|
|
741
1294
|
if (chosen.has(key)) continue;
|
|
742
1295
|
const unit = FEATURES[key];
|
|
743
1296
|
for (const dir of unit.libDirs)
|
|
744
|
-
await (0,
|
|
1297
|
+
await (0, import_promises5.rm)((0, import_node_path5.join)(targetDir, dir), { recursive: true, force: true });
|
|
745
1298
|
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
746
|
-
await stripJsonKeys((0,
|
|
1299
|
+
await stripJsonKeys((0, import_node_path5.join)(targetDir, API_PKG), (k) => dropKeys.has(k));
|
|
747
1300
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
748
1301
|
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
749
1302
|
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
750
1303
|
if (key === "jobs") {
|
|
751
1304
|
try {
|
|
752
|
-
await (0,
|
|
1305
|
+
await (0, import_promises5.unlink)((0, import_node_path5.join)(targetDir, "libs/shared/src/jobs.ts"));
|
|
753
1306
|
} catch {
|
|
754
1307
|
}
|
|
755
|
-
const sharedIdx = (0, import_node_path4.join)(targetDir, "libs/shared/src/index.ts");
|
|
756
1308
|
try {
|
|
757
|
-
|
|
758
|
-
|
|
1309
|
+
await (0, import_promises5.unlink)((0, import_node_path5.join)(targetDir, "libs/shared/src/__tests__/jobs.unit.test.ts"));
|
|
1310
|
+
} catch {
|
|
1311
|
+
}
|
|
1312
|
+
const sharedIdx = (0, import_node_path5.join)(targetDir, "libs/shared/src/index.ts");
|
|
1313
|
+
try {
|
|
1314
|
+
const src = await (0, import_promises5.readFile)(sharedIdx, "utf8");
|
|
1315
|
+
await (0, import_promises5.writeFile)(sharedIdx, src.replace(/^export \* from '\.\/jobs';\n/m, ""));
|
|
759
1316
|
} catch {
|
|
760
1317
|
}
|
|
761
1318
|
}
|
|
762
1319
|
}
|
|
763
1320
|
try {
|
|
764
|
-
await (0,
|
|
1321
|
+
await (0, import_promises5.rmdir)((0, import_node_path5.join)(targetDir, "apps/client/src/queries"));
|
|
765
1322
|
} catch {
|
|
766
1323
|
}
|
|
767
1324
|
}
|
|
768
1325
|
|
|
769
1326
|
// src/manifest/wire-client.ts
|
|
770
|
-
var
|
|
771
|
-
var
|
|
1327
|
+
var import_promises6 = require("fs/promises");
|
|
1328
|
+
var import_node_path6 = require("path");
|
|
772
1329
|
var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
|
|
773
1330
|
var BASE_NAV = [
|
|
774
1331
|
{ to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
|
|
@@ -806,12 +1363,12 @@ export const NAV_CONFIG: NavItem[] = [
|
|
|
806
1363
|
` + entries.map(renderEntry).join("\n") + `
|
|
807
1364
|
];
|
|
808
1365
|
`;
|
|
809
|
-
await (0,
|
|
1366
|
+
await (0, import_promises6.writeFile)((0, import_node_path6.join)(targetDir, NAV_CONFIG_FILE), content);
|
|
810
1367
|
}
|
|
811
1368
|
|
|
812
1369
|
// src/manifest/blueprint.ts
|
|
813
|
-
var
|
|
814
|
-
var
|
|
1370
|
+
var import_promises7 = require("fs/promises");
|
|
1371
|
+
var import_node_path7 = require("path");
|
|
815
1372
|
async function writeBlueprintJson(targetDir, opts) {
|
|
816
1373
|
const blueprint = {
|
|
817
1374
|
schemaVersion: 1,
|
|
@@ -826,10 +1383,10 @@ async function writeBlueprintJson(targetDir, opts) {
|
|
|
826
1383
|
transport: opts.transport,
|
|
827
1384
|
packageManager: opts.packageManager
|
|
828
1385
|
};
|
|
829
|
-
await (0,
|
|
1386
|
+
await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
|
|
830
1387
|
}
|
|
831
1388
|
async function writeJson(targetDir, rel, data) {
|
|
832
|
-
await (0,
|
|
1389
|
+
await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
|
|
833
1390
|
}
|
|
834
1391
|
async function writeServiceBlueprints(targetDir, opts) {
|
|
835
1392
|
const t = opts.transport;
|
|
@@ -923,11 +1480,11 @@ var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, prov
|
|
|
923
1480
|
var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
|
|
924
1481
|
|
|
925
1482
|
// src/lib/scaffold-pkg.ts
|
|
926
|
-
var
|
|
927
|
-
var
|
|
1483
|
+
var import_promises8 = require("fs/promises");
|
|
1484
|
+
var import_node_path8 = require("path");
|
|
928
1485
|
async function writePnpmWorkspace(targetDir) {
|
|
929
|
-
const pkgPath = (0,
|
|
930
|
-
const pkg = JSON.parse(await (0,
|
|
1486
|
+
const pkgPath = (0, import_node_path8.join)(targetDir, "package.json");
|
|
1487
|
+
const pkg = JSON.parse(await (0, import_promises8.readFile)(pkgPath, "utf8"));
|
|
931
1488
|
const workspaces = pkg.workspaces ?? [];
|
|
932
1489
|
const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
|
|
933
1490
|
const allowBuilds = [
|
|
@@ -950,22 +1507,22 @@ ${packagesBlock}
|
|
|
950
1507
|
allowBuilds:
|
|
951
1508
|
${allowBuilds}
|
|
952
1509
|
`;
|
|
953
|
-
await (0,
|
|
1510
|
+
await (0, import_promises8.writeFile)((0, import_node_path8.join)(targetDir, "pnpm-workspace.yaml"), content);
|
|
954
1511
|
}
|
|
955
1512
|
async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
956
1513
|
async function walk2(dir) {
|
|
957
1514
|
const found = [];
|
|
958
1515
|
let entries;
|
|
959
1516
|
try {
|
|
960
|
-
entries = await (0,
|
|
1517
|
+
entries = await (0, import_promises8.readdir)(dir, { withFileTypes: true });
|
|
961
1518
|
} catch {
|
|
962
1519
|
return found;
|
|
963
1520
|
}
|
|
964
1521
|
for (const e of entries) {
|
|
965
1522
|
if (e.isDirectory() && e.name !== "node_modules") {
|
|
966
|
-
found.push(...await walk2((0,
|
|
1523
|
+
found.push(...await walk2((0, import_node_path8.join)(dir, e.name)));
|
|
967
1524
|
} else if (e.isFile() && e.name === "package.json") {
|
|
968
|
-
found.push((0,
|
|
1525
|
+
found.push((0, import_node_path8.join)(dir, e.name));
|
|
969
1526
|
}
|
|
970
1527
|
}
|
|
971
1528
|
return found;
|
|
@@ -973,17 +1530,17 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
973
1530
|
const pkgFiles = await walk2(targetDir);
|
|
974
1531
|
for (const f of pkgFiles) {
|
|
975
1532
|
try {
|
|
976
|
-
const raw = await (0,
|
|
1533
|
+
const raw = await (0, import_promises8.readFile)(f, "utf8");
|
|
977
1534
|
const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
|
|
978
|
-
if (next !== raw) await (0,
|
|
1535
|
+
if (next !== raw) await (0, import_promises8.writeFile)(f, next);
|
|
979
1536
|
} catch {
|
|
980
1537
|
}
|
|
981
1538
|
}
|
|
982
1539
|
}
|
|
983
1540
|
async function patchGitignoreForPm(targetDir, pm) {
|
|
984
|
-
const giPath = (0,
|
|
1541
|
+
const giPath = (0, import_node_path8.join)(targetDir, ".gitignore");
|
|
985
1542
|
try {
|
|
986
|
-
let src = await (0,
|
|
1543
|
+
let src = await (0, import_promises8.readFile)(giPath, "utf8");
|
|
987
1544
|
src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
|
|
988
1545
|
if (pm !== "yarn") {
|
|
989
1546
|
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, "");
|
|
@@ -998,7 +1555,7 @@ async function patchGitignoreForPm(targetDir, pm) {
|
|
|
998
1555
|
src += "\n# npm\nnpm-debug.log*\n";
|
|
999
1556
|
}
|
|
1000
1557
|
}
|
|
1001
|
-
await (0,
|
|
1558
|
+
await (0, import_promises8.writeFile)(giPath, src);
|
|
1002
1559
|
} catch {
|
|
1003
1560
|
}
|
|
1004
1561
|
}
|
|
@@ -1014,7 +1571,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
1014
1571
|
if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
|
|
1015
1572
|
const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
|
|
1016
1573
|
const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
1017
|
-
await (0,
|
|
1574
|
+
await (0, import_promises8.writeFile)((0, import_node_path8.join)(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
|
|
1018
1575
|
const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
|
|
1019
1576
|
const readme = `# ${opts.projectName}
|
|
1020
1577
|
|
|
@@ -1062,7 +1619,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
|
|
|
1062
1619
|
|
|
1063
1620
|
Apache-2.0
|
|
1064
1621
|
`;
|
|
1065
|
-
await (0,
|
|
1622
|
+
await (0, import_promises8.writeFile)((0, import_node_path8.join)(targetDir, "README.md"), readme);
|
|
1066
1623
|
const agents = `# ${opts.projectName} \u2014 Agent Instructions
|
|
1067
1624
|
|
|
1068
1625
|
## Stack snapshot
|
|
@@ -1143,8 +1700,8 @@ ${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PR
|
|
|
1143
1700
|
- Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
|
|
1144
1701
|
- Run: \`${nx} test <project>\`
|
|
1145
1702
|
`;
|
|
1146
|
-
await (0,
|
|
1147
|
-
await (0,
|
|
1703
|
+
await (0, import_promises8.writeFile)((0, import_node_path8.join)(targetDir, "AGENTS.md"), agents);
|
|
1704
|
+
await (0, import_promises8.mkdir)((0, import_node_path8.join)(targetDir, ".claude"), { recursive: true });
|
|
1148
1705
|
const mcpServers = {
|
|
1149
1706
|
nx: {
|
|
1150
1707
|
command: "npx",
|
|
@@ -1185,8 +1742,8 @@ ${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PR
|
|
|
1185
1742
|
]
|
|
1186
1743
|
}
|
|
1187
1744
|
};
|
|
1188
|
-
await (0,
|
|
1189
|
-
(0,
|
|
1745
|
+
await (0, import_promises8.writeFile)(
|
|
1746
|
+
(0, import_node_path8.join)(targetDir, ".claude", "settings.json"),
|
|
1190
1747
|
JSON.stringify(settings, null, 2) + "\n"
|
|
1191
1748
|
);
|
|
1192
1749
|
}
|
|
@@ -1206,30 +1763,30 @@ var IGNORE_TOP = /* @__PURE__ */ new Set([
|
|
|
1206
1763
|
".vscode"
|
|
1207
1764
|
]);
|
|
1208
1765
|
async function copyTree(src, dest) {
|
|
1209
|
-
await (0,
|
|
1210
|
-
const entries = await (0,
|
|
1766
|
+
await (0, import_promises9.mkdir)(dest, { recursive: true });
|
|
1767
|
+
const entries = await (0, import_promises9.readdir)(src, { withFileTypes: true });
|
|
1211
1768
|
for (const entry of entries) {
|
|
1212
1769
|
if (IGNORE_TOP.has(entry.name)) continue;
|
|
1213
|
-
const s = (0,
|
|
1214
|
-
const d = (0,
|
|
1770
|
+
const s = (0, import_node_path9.join)(src, entry.name);
|
|
1771
|
+
const d = (0, import_node_path9.join)(dest, entry.name);
|
|
1215
1772
|
if (entry.isDirectory()) await copyTree(s, d);
|
|
1216
|
-
else if (entry.isFile()) await (0,
|
|
1773
|
+
else if (entry.isFile()) await (0, import_promises9.copyFile)(s, d);
|
|
1217
1774
|
}
|
|
1218
1775
|
}
|
|
1219
1776
|
async function selectClientTemplate(targetDir, opts) {
|
|
1220
|
-
const templatesRoot = (0,
|
|
1221
|
-
const chosen = (0,
|
|
1222
|
-
const destClient = (0,
|
|
1777
|
+
const templatesRoot = (0, import_node_path9.join)(targetDir, "apps/templates");
|
|
1778
|
+
const chosen = (0, import_node_path9.join)(templatesRoot, `client-${opts.ui}`);
|
|
1779
|
+
const destClient = (0, import_node_path9.join)(targetDir, "apps/client");
|
|
1223
1780
|
let chosenUi = opts.ui;
|
|
1224
1781
|
try {
|
|
1225
|
-
const s = await (0,
|
|
1782
|
+
const s = await (0, import_promises9.stat)(chosen);
|
|
1226
1783
|
if (!s.isDirectory()) throw new Error("not a dir");
|
|
1227
1784
|
await copyTree(chosen, destClient);
|
|
1228
1785
|
} catch {
|
|
1229
1786
|
chosenUi = "shadcn";
|
|
1230
|
-
await copyTree((0,
|
|
1787
|
+
await copyTree((0, import_node_path9.join)(templatesRoot, "client-shadcn"), destClient);
|
|
1231
1788
|
}
|
|
1232
|
-
await (0,
|
|
1789
|
+
await (0, import_promises9.rm)(templatesRoot, { recursive: true, force: true });
|
|
1233
1790
|
await rewriteClientPaths(destClient, chosenUi);
|
|
1234
1791
|
}
|
|
1235
1792
|
async function rewriteClientPaths(clientDir, ui) {
|
|
@@ -1242,11 +1799,11 @@ async function rewriteClientPaths(clientDir, ui) {
|
|
|
1242
1799
|
"eslint.config.mjs"
|
|
1243
1800
|
];
|
|
1244
1801
|
for (const rel of candidates) {
|
|
1245
|
-
const path = (0,
|
|
1802
|
+
const path = (0, import_node_path9.join)(clientDir, rel);
|
|
1246
1803
|
try {
|
|
1247
|
-
const raw = await (0,
|
|
1804
|
+
const raw = await (0, import_promises9.readFile)(path, "utf8");
|
|
1248
1805
|
const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
|
|
1249
|
-
if (next !== raw) await (0,
|
|
1806
|
+
if (next !== raw) await (0, import_promises9.writeFile)(path, next);
|
|
1250
1807
|
} catch {
|
|
1251
1808
|
}
|
|
1252
1809
|
}
|
|
@@ -1262,12 +1819,12 @@ function gitInit(cwd, projectName) {
|
|
|
1262
1819
|
}
|
|
1263
1820
|
function resolveYarnBin(cwd) {
|
|
1264
1821
|
try {
|
|
1265
|
-
const yarnrc = (0, import_node_fs.readFileSync)((0,
|
|
1822
|
+
const yarnrc = (0, import_node_fs.readFileSync)((0, import_node_path9.join)(cwd, ".yarnrc.yml"), "utf8");
|
|
1266
1823
|
const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
|
|
1267
|
-
if (match?.[1]) return (0,
|
|
1824
|
+
if (match?.[1]) return (0, import_node_path9.join)(cwd, match[1].trim());
|
|
1268
1825
|
} catch {
|
|
1269
1826
|
}
|
|
1270
|
-
return (0,
|
|
1827
|
+
return (0, import_node_path9.join)(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
|
|
1271
1828
|
}
|
|
1272
1829
|
function runInstall(cwd, pm) {
|
|
1273
1830
|
if (pm === "yarn") {
|
|
@@ -1290,6 +1847,10 @@ async function scaffold(rawOpts, templatesDir) {
|
|
|
1290
1847
|
await writeRootEnv(opts.targetDir, opts);
|
|
1291
1848
|
await selectClientTemplate(opts.targetDir, opts);
|
|
1292
1849
|
await writeClientEnv(opts.targetDir);
|
|
1850
|
+
if (opts.authProvider === "none") {
|
|
1851
|
+
await removeAuthOnlyPaths(opts.targetDir);
|
|
1852
|
+
await applyAuthNoneVariants(opts.targetDir, opts.ui);
|
|
1853
|
+
}
|
|
1293
1854
|
if (opts.upload === "none") await removeUploadStack(opts.targetDir);
|
|
1294
1855
|
await cleanupUnusedFeatures(opts.targetDir, opts);
|
|
1295
1856
|
await writeFeaturesWiring(opts.targetDir, opts);
|
|
@@ -1298,7 +1859,8 @@ async function scaffold(rawOpts, templatesDir) {
|
|
|
1298
1859
|
await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
|
|
1299
1860
|
await writeAuthProvider(opts.targetDir, opts.authProvider);
|
|
1300
1861
|
} else {
|
|
1301
|
-
await
|
|
1862
|
+
await removeAuthTsconfigPaths(opts.targetDir);
|
|
1863
|
+
await removeDockerComposeAuthService(opts.targetDir);
|
|
1302
1864
|
}
|
|
1303
1865
|
if (opts.upload !== "none") {
|
|
1304
1866
|
await cleanupUnusedStorage(opts.targetDir, opts.upload);
|
|
@@ -1311,20 +1873,20 @@ async function scaffold(rawOpts, templatesDir) {
|
|
|
1311
1873
|
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1312
1874
|
}
|
|
1313
1875
|
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1314
|
-
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none") {
|
|
1876
|
+
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none" && opts.payment === "none") {
|
|
1315
1877
|
await removeStrategiesLib(opts.targetDir);
|
|
1316
1878
|
}
|
|
1317
1879
|
try {
|
|
1318
|
-
await (0,
|
|
1880
|
+
await (0, import_promises9.rmdir)((0, import_node_path9.join)(opts.targetDir, "apps/microservices"));
|
|
1319
1881
|
} catch {
|
|
1320
1882
|
}
|
|
1321
1883
|
await writeBlueprintJson(opts.targetDir, opts);
|
|
1322
1884
|
await writeServiceBlueprints(opts.targetDir, opts);
|
|
1323
1885
|
if (opts.packageManager === "yarn") {
|
|
1324
|
-
await (0,
|
|
1886
|
+
await (0, import_promises9.writeFile)((0, import_node_path9.join)(opts.targetDir, "yarn.lock"), "");
|
|
1325
1887
|
} else {
|
|
1326
|
-
await (0,
|
|
1327
|
-
await (0,
|
|
1888
|
+
await (0, import_promises9.rm)((0, import_node_path9.join)(opts.targetDir, ".yarn"), { recursive: true, force: true });
|
|
1889
|
+
await (0, import_promises9.rm)((0, import_node_path9.join)(opts.targetDir, ".yarnrc.yml"), { force: true });
|
|
1328
1890
|
}
|
|
1329
1891
|
if (opts.packageManager === "pnpm") {
|
|
1330
1892
|
await writePnpmWorkspace(opts.targetDir);
|
|
@@ -1338,13 +1900,13 @@ async function scaffold(rawOpts, templatesDir) {
|
|
|
1338
1900
|
|
|
1339
1901
|
// src/lib/prompts.ts
|
|
1340
1902
|
var p = __toESM(require("@clack/prompts"), 1);
|
|
1341
|
-
var import_node_path9 = require("path");
|
|
1342
|
-
var import_promises10 = require("fs/promises");
|
|
1343
1903
|
var import_node_path10 = require("path");
|
|
1904
|
+
var import_promises11 = require("fs/promises");
|
|
1905
|
+
var import_node_path11 = require("path");
|
|
1344
1906
|
var import_node_url = require("url");
|
|
1345
1907
|
|
|
1346
1908
|
// src/lib/config.ts
|
|
1347
|
-
var
|
|
1909
|
+
var import_promises10 = require("fs/promises");
|
|
1348
1910
|
var ConfigFileError = class extends Error {
|
|
1349
1911
|
constructor(message) {
|
|
1350
1912
|
super(message);
|
|
@@ -1415,7 +1977,7 @@ function validateConfig(raw) {
|
|
|
1415
1977
|
async function loadConfig(filePath) {
|
|
1416
1978
|
let raw;
|
|
1417
1979
|
try {
|
|
1418
|
-
raw = await (0,
|
|
1980
|
+
raw = await (0, import_promises10.readFile)(filePath, "utf8");
|
|
1419
1981
|
} catch {
|
|
1420
1982
|
throw new ConfigFileError(`config file not found: ${filePath}`);
|
|
1421
1983
|
}
|
|
@@ -1440,8 +2002,8 @@ function detectPackageManager() {
|
|
|
1440
2002
|
}
|
|
1441
2003
|
async function readSelfVersion() {
|
|
1442
2004
|
try {
|
|
1443
|
-
const here = (0,
|
|
1444
|
-
const pkgRaw = await (0,
|
|
2005
|
+
const here = (0, import_node_path11.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
2006
|
+
const pkgRaw = await (0, import_promises11.readFile)((0, import_node_path11.join)(here, "..", "package.json"), "utf8");
|
|
1445
2007
|
const pkg = JSON.parse(pkgRaw);
|
|
1446
2008
|
return pkg.version ?? null;
|
|
1447
2009
|
} catch {
|
|
@@ -1649,7 +2211,7 @@ Re-run with @latest to refresh:
|
|
|
1649
2211
|
}) === false;
|
|
1650
2212
|
return {
|
|
1651
2213
|
projectName,
|
|
1652
|
-
targetDir: (0,
|
|
2214
|
+
targetDir: (0, import_node_path10.resolve)(cwd, projectName),
|
|
1653
2215
|
authProvider,
|
|
1654
2216
|
dbProvider,
|
|
1655
2217
|
upload,
|
|
@@ -1665,23 +2227,23 @@ Re-run with @latest to refresh:
|
|
|
1665
2227
|
}
|
|
1666
2228
|
|
|
1667
2229
|
// src/manifest/audit.ts
|
|
1668
|
-
var
|
|
1669
|
-
var
|
|
2230
|
+
var import_promises12 = require("fs/promises");
|
|
2231
|
+
var import_node_path12 = require("path");
|
|
1670
2232
|
var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".nx"]);
|
|
1671
2233
|
async function walk(dir, out = []) {
|
|
1672
|
-
const entries = await (0,
|
|
2234
|
+
const entries = await (0, import_promises12.readdir)(dir, { withFileTypes: true });
|
|
1673
2235
|
for (const e of entries) {
|
|
1674
2236
|
if (e.isDirectory()) {
|
|
1675
|
-
if (!IGNORE_DIRS.has(e.name)) await walk((0,
|
|
2237
|
+
if (!IGNORE_DIRS.has(e.name)) await walk((0, import_node_path12.join)(dir, e.name), out);
|
|
1676
2238
|
} else if (/\.(ts|tsx|mjs)$/.test(e.name)) {
|
|
1677
|
-
out.push((0,
|
|
2239
|
+
out.push((0, import_node_path12.join)(dir, e.name));
|
|
1678
2240
|
}
|
|
1679
2241
|
}
|
|
1680
2242
|
return out;
|
|
1681
2243
|
}
|
|
1682
2244
|
async function tsconfigAliases(dir) {
|
|
1683
2245
|
try {
|
|
1684
|
-
const raw = await (0,
|
|
2246
|
+
const raw = await (0, import_promises12.readFile)((0, import_node_path12.join)(dir, "tsconfig.base.json"), "utf8");
|
|
1685
2247
|
const aliases = /* @__PURE__ */ new Set();
|
|
1686
2248
|
for (const m of raw.matchAll(/"(@icore\/[a-z0-9.-]+)"\s*:/g)) {
|
|
1687
2249
|
if (m[1]) aliases.add(m[1]);
|
|
@@ -1699,7 +2261,7 @@ var PROVIDER_SDKS = {
|
|
|
1699
2261
|
};
|
|
1700
2262
|
async function readBlueprint(dir) {
|
|
1701
2263
|
try {
|
|
1702
|
-
return JSON.parse(await (0,
|
|
2264
|
+
return JSON.parse(await (0, import_promises12.readFile)((0, import_node_path12.join)(dir, "blueprint.json"), "utf8"));
|
|
1703
2265
|
} catch {
|
|
1704
2266
|
return null;
|
|
1705
2267
|
}
|
|
@@ -1716,29 +2278,29 @@ function forbiddenFromBlueprint(bp) {
|
|
|
1716
2278
|
}
|
|
1717
2279
|
async function allPackageJsons(dir) {
|
|
1718
2280
|
const out = [];
|
|
1719
|
-
const root = (0,
|
|
2281
|
+
const root = (0, import_node_path12.join)(dir, "package.json");
|
|
1720
2282
|
out.push(root);
|
|
1721
2283
|
async function walk2(d) {
|
|
1722
2284
|
let entries;
|
|
1723
2285
|
try {
|
|
1724
|
-
entries = await (0,
|
|
2286
|
+
entries = await (0, import_promises12.readdir)(d, { withFileTypes: true });
|
|
1725
2287
|
} catch {
|
|
1726
2288
|
return;
|
|
1727
2289
|
}
|
|
1728
2290
|
for (const e of entries) {
|
|
1729
2291
|
if (e.isDirectory()) {
|
|
1730
|
-
if (!IGNORE_DIRS.has(e.name)) await walk2((0,
|
|
2292
|
+
if (!IGNORE_DIRS.has(e.name)) await walk2((0, import_node_path12.join)(d, e.name));
|
|
1731
2293
|
} else if (e.name === "package.json") {
|
|
1732
|
-
out.push((0,
|
|
2294
|
+
out.push((0, import_node_path12.join)(d, e.name));
|
|
1733
2295
|
}
|
|
1734
2296
|
}
|
|
1735
2297
|
}
|
|
1736
|
-
await walk2((0,
|
|
2298
|
+
await walk2((0, import_node_path12.join)(dir, "apps"));
|
|
1737
2299
|
return out;
|
|
1738
2300
|
}
|
|
1739
2301
|
async function depKeys(pkgPath) {
|
|
1740
2302
|
try {
|
|
1741
|
-
const pkg = JSON.parse(await (0,
|
|
2303
|
+
const pkg = JSON.parse(await (0, import_promises12.readFile)(pkgPath, "utf8"));
|
|
1742
2304
|
return /* @__PURE__ */ new Set([
|
|
1743
2305
|
...Object.keys(pkg.dependencies ?? {}),
|
|
1744
2306
|
...Object.keys(pkg.devDependencies ?? {})
|
|
@@ -1752,7 +2314,7 @@ async function auditProject(dir, opts = {}) {
|
|
|
1752
2314
|
const violations = [];
|
|
1753
2315
|
const aliases = await tsconfigAliases(dir);
|
|
1754
2316
|
for (const file of await walk(dir)) {
|
|
1755
|
-
const src = await (0,
|
|
2317
|
+
const src = await (0, import_promises12.readFile)(file, "utf8");
|
|
1756
2318
|
for (const m of src.matchAll(ICORE_IMPORT)) {
|
|
1757
2319
|
const alias = m[1];
|
|
1758
2320
|
if (alias && !aliases.has(alias)) {
|