@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/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,25 +1873,28 @@ 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);
|
|
1331
1893
|
await rewritePnpmWorkspaceDeps(opts.targetDir);
|
|
1332
1894
|
}
|
|
1895
|
+
if (opts.packageManager === "npm") {
|
|
1896
|
+
await (0, import_promises9.writeFile)((0, import_node_path9.join)(opts.targetDir, ".npmrc"), "legacy-peer-deps=true\n");
|
|
1897
|
+
}
|
|
1333
1898
|
await patchGitignoreForPm(opts.targetDir, opts.packageManager);
|
|
1334
1899
|
await writeAiFiles(opts.targetDir, opts);
|
|
1335
1900
|
if (opts.install) runInstall(opts.targetDir, opts.packageManager);
|
|
@@ -1338,13 +1903,13 @@ async function scaffold(rawOpts, templatesDir) {
|
|
|
1338
1903
|
|
|
1339
1904
|
// src/lib/prompts.ts
|
|
1340
1905
|
var p = __toESM(require("@clack/prompts"), 1);
|
|
1341
|
-
var import_node_path9 = require("path");
|
|
1342
|
-
var import_promises10 = require("fs/promises");
|
|
1343
1906
|
var import_node_path10 = require("path");
|
|
1907
|
+
var import_promises11 = require("fs/promises");
|
|
1908
|
+
var import_node_path11 = require("path");
|
|
1344
1909
|
var import_node_url = require("url");
|
|
1345
1910
|
|
|
1346
1911
|
// src/lib/config.ts
|
|
1347
|
-
var
|
|
1912
|
+
var import_promises10 = require("fs/promises");
|
|
1348
1913
|
var ConfigFileError = class extends Error {
|
|
1349
1914
|
constructor(message) {
|
|
1350
1915
|
super(message);
|
|
@@ -1415,7 +1980,7 @@ function validateConfig(raw) {
|
|
|
1415
1980
|
async function loadConfig(filePath) {
|
|
1416
1981
|
let raw;
|
|
1417
1982
|
try {
|
|
1418
|
-
raw = await (0,
|
|
1983
|
+
raw = await (0, import_promises10.readFile)(filePath, "utf8");
|
|
1419
1984
|
} catch {
|
|
1420
1985
|
throw new ConfigFileError(`config file not found: ${filePath}`);
|
|
1421
1986
|
}
|
|
@@ -1440,8 +2005,8 @@ function detectPackageManager() {
|
|
|
1440
2005
|
}
|
|
1441
2006
|
async function readSelfVersion() {
|
|
1442
2007
|
try {
|
|
1443
|
-
const here = (0,
|
|
1444
|
-
const pkgRaw = await (0,
|
|
2008
|
+
const here = (0, import_node_path11.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
2009
|
+
const pkgRaw = await (0, import_promises11.readFile)((0, import_node_path11.join)(here, "..", "package.json"), "utf8");
|
|
1445
2010
|
const pkg = JSON.parse(pkgRaw);
|
|
1446
2011
|
return pkg.version ?? null;
|
|
1447
2012
|
} catch {
|
|
@@ -1649,7 +2214,7 @@ Re-run with @latest to refresh:
|
|
|
1649
2214
|
}) === false;
|
|
1650
2215
|
return {
|
|
1651
2216
|
projectName,
|
|
1652
|
-
targetDir: (0,
|
|
2217
|
+
targetDir: (0, import_node_path10.resolve)(cwd, projectName),
|
|
1653
2218
|
authProvider,
|
|
1654
2219
|
dbProvider,
|
|
1655
2220
|
upload,
|
|
@@ -1665,23 +2230,23 @@ Re-run with @latest to refresh:
|
|
|
1665
2230
|
}
|
|
1666
2231
|
|
|
1667
2232
|
// src/manifest/audit.ts
|
|
1668
|
-
var
|
|
1669
|
-
var
|
|
2233
|
+
var import_promises12 = require("fs/promises");
|
|
2234
|
+
var import_node_path12 = require("path");
|
|
1670
2235
|
var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".nx"]);
|
|
1671
2236
|
async function walk(dir, out = []) {
|
|
1672
|
-
const entries = await (0,
|
|
2237
|
+
const entries = await (0, import_promises12.readdir)(dir, { withFileTypes: true });
|
|
1673
2238
|
for (const e of entries) {
|
|
1674
2239
|
if (e.isDirectory()) {
|
|
1675
|
-
if (!IGNORE_DIRS.has(e.name)) await walk((0,
|
|
2240
|
+
if (!IGNORE_DIRS.has(e.name)) await walk((0, import_node_path12.join)(dir, e.name), out);
|
|
1676
2241
|
} else if (/\.(ts|tsx|mjs)$/.test(e.name)) {
|
|
1677
|
-
out.push((0,
|
|
2242
|
+
out.push((0, import_node_path12.join)(dir, e.name));
|
|
1678
2243
|
}
|
|
1679
2244
|
}
|
|
1680
2245
|
return out;
|
|
1681
2246
|
}
|
|
1682
2247
|
async function tsconfigAliases(dir) {
|
|
1683
2248
|
try {
|
|
1684
|
-
const raw = await (0,
|
|
2249
|
+
const raw = await (0, import_promises12.readFile)((0, import_node_path12.join)(dir, "tsconfig.base.json"), "utf8");
|
|
1685
2250
|
const aliases = /* @__PURE__ */ new Set();
|
|
1686
2251
|
for (const m of raw.matchAll(/"(@icore\/[a-z0-9.-]+)"\s*:/g)) {
|
|
1687
2252
|
if (m[1]) aliases.add(m[1]);
|
|
@@ -1699,7 +2264,7 @@ var PROVIDER_SDKS = {
|
|
|
1699
2264
|
};
|
|
1700
2265
|
async function readBlueprint(dir) {
|
|
1701
2266
|
try {
|
|
1702
|
-
return JSON.parse(await (0,
|
|
2267
|
+
return JSON.parse(await (0, import_promises12.readFile)((0, import_node_path12.join)(dir, "blueprint.json"), "utf8"));
|
|
1703
2268
|
} catch {
|
|
1704
2269
|
return null;
|
|
1705
2270
|
}
|
|
@@ -1716,29 +2281,29 @@ function forbiddenFromBlueprint(bp) {
|
|
|
1716
2281
|
}
|
|
1717
2282
|
async function allPackageJsons(dir) {
|
|
1718
2283
|
const out = [];
|
|
1719
|
-
const root = (0,
|
|
2284
|
+
const root = (0, import_node_path12.join)(dir, "package.json");
|
|
1720
2285
|
out.push(root);
|
|
1721
2286
|
async function walk2(d) {
|
|
1722
2287
|
let entries;
|
|
1723
2288
|
try {
|
|
1724
|
-
entries = await (0,
|
|
2289
|
+
entries = await (0, import_promises12.readdir)(d, { withFileTypes: true });
|
|
1725
2290
|
} catch {
|
|
1726
2291
|
return;
|
|
1727
2292
|
}
|
|
1728
2293
|
for (const e of entries) {
|
|
1729
2294
|
if (e.isDirectory()) {
|
|
1730
|
-
if (!IGNORE_DIRS.has(e.name)) await walk2((0,
|
|
2295
|
+
if (!IGNORE_DIRS.has(e.name)) await walk2((0, import_node_path12.join)(d, e.name));
|
|
1731
2296
|
} else if (e.name === "package.json") {
|
|
1732
|
-
out.push((0,
|
|
2297
|
+
out.push((0, import_node_path12.join)(d, e.name));
|
|
1733
2298
|
}
|
|
1734
2299
|
}
|
|
1735
2300
|
}
|
|
1736
|
-
await walk2((0,
|
|
2301
|
+
await walk2((0, import_node_path12.join)(dir, "apps"));
|
|
1737
2302
|
return out;
|
|
1738
2303
|
}
|
|
1739
2304
|
async function depKeys(pkgPath) {
|
|
1740
2305
|
try {
|
|
1741
|
-
const pkg = JSON.parse(await (0,
|
|
2306
|
+
const pkg = JSON.parse(await (0, import_promises12.readFile)(pkgPath, "utf8"));
|
|
1742
2307
|
return /* @__PURE__ */ new Set([
|
|
1743
2308
|
...Object.keys(pkg.dependencies ?? {}),
|
|
1744
2309
|
...Object.keys(pkg.devDependencies ?? {})
|
|
@@ -1752,7 +2317,7 @@ async function auditProject(dir, opts = {}) {
|
|
|
1752
2317
|
const violations = [];
|
|
1753
2318
|
const aliases = await tsconfigAliases(dir);
|
|
1754
2319
|
for (const file of await walk(dir)) {
|
|
1755
|
-
const src = await (0,
|
|
2320
|
+
const src = await (0, import_promises12.readFile)(file, "utf8");
|
|
1756
2321
|
for (const m of src.matchAll(ICORE_IMPORT)) {
|
|
1757
2322
|
const alias = m[1];
|
|
1758
2323
|
if (alias && !aliases.has(alias)) {
|