@idevconn/create-icore 0.9.2 → 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 +832 -130
- package/dist/index.cjs +848 -149
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +841 -142
- 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
|
|
@@ -150,6 +150,30 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
delete pkg.pnpm;
|
|
153
|
+
const noMs = opts.authProvider === "none" && opts.upload === "none" && opts.payment === "none" && opts.jobs === "none" && opts.example === "none";
|
|
154
|
+
const ws = pkg.workspaces;
|
|
155
|
+
if (ws) {
|
|
156
|
+
pkg.workspaces = ws.filter((entry) => {
|
|
157
|
+
if (entry === "apps/templates/*") return false;
|
|
158
|
+
if (entry === "apps/microservices/*" && noMs) return false;
|
|
159
|
+
if (entry === "libs/auth-strategies/*" && opts.authProvider === "none") return false;
|
|
160
|
+
if (entry === "libs/storage-strategies/*" && opts.upload === "none") return false;
|
|
161
|
+
if (entry === "libs/db-strategies/*" && opts.dbProvider === "none") return false;
|
|
162
|
+
return true;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const rootDeps = pkg.dependencies ?? {};
|
|
166
|
+
const rootDevDeps = pkg.devDependencies ?? {};
|
|
167
|
+
if (opts.authProvider === "none") {
|
|
168
|
+
delete rootDeps["cookie-parser"];
|
|
169
|
+
delete rootDevDeps["@types/cookie-parser"];
|
|
170
|
+
}
|
|
171
|
+
if (opts.upload === "none") {
|
|
172
|
+
delete rootDevDeps["@types/multer"];
|
|
173
|
+
}
|
|
174
|
+
if (noMs) {
|
|
175
|
+
delete rootDeps["@nestjs/microservices"];
|
|
176
|
+
}
|
|
153
177
|
await (0, import_promises.writeFile)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
154
178
|
}
|
|
155
179
|
async function writeAuthEnv(targetDir, opts) {
|
|
@@ -191,6 +215,9 @@ async function writeGatewayEnv(targetDir, opts) {
|
|
|
191
215
|
for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
|
|
192
216
|
next = uncommentTransportEnv(next, prefix, opts.transport);
|
|
193
217
|
}
|
|
218
|
+
if (opts.authProvider === "none") {
|
|
219
|
+
next = next.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
220
|
+
}
|
|
194
221
|
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/api/.env"), next);
|
|
195
222
|
}
|
|
196
223
|
async function writeRootEnv(targetDir, opts) {
|
|
@@ -272,59 +299,26 @@ async function removeFirebaseAdminLib(targetDir) {
|
|
|
272
299
|
"@icore/firebase-admin"
|
|
273
300
|
]);
|
|
274
301
|
}
|
|
275
|
-
async function
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
"apps/api/src/app/auth",
|
|
282
|
-
"apps/api/src/app/profile",
|
|
283
|
-
"apps/api/src/app/abilities",
|
|
284
|
-
"apps/client/src/components/auth",
|
|
285
|
-
"apps/client/src/routes/login.tsx",
|
|
286
|
-
"apps/client/src/routes/auth.callback.tsx",
|
|
287
|
-
"apps/client/src/routes/auth.oauth.callback.tsx",
|
|
288
|
-
"apps/client/src/routes/_dashboard/profile.tsx"
|
|
289
|
-
];
|
|
290
|
-
for (const p2 of rmPaths) {
|
|
291
|
-
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
|
|
292
|
-
}
|
|
293
|
-
const appModulePath = (0, import_node_path2.join)(targetDir, "apps/api/src/app/app.module.ts");
|
|
302
|
+
async function removeStrategiesLib(targetDir) {
|
|
303
|
+
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/shared/src/strategies"), { recursive: true, force: true });
|
|
304
|
+
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, "libs/shared/src/testing.ts"), { force: true });
|
|
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 });
|
|
307
|
+
const indexPath = (0, import_node_path2.join)(targetDir, "libs/shared/src/index.ts");
|
|
294
308
|
try {
|
|
295
|
-
const src = await (0, import_promises2.readFile)(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const dashboardPath = (0, import_node_path2.join)(targetDir, "apps/client/src/routes/_dashboard.tsx");
|
|
301
|
-
try {
|
|
302
|
-
const src = await (0, import_promises2.readFile)(dashboardPath, "utf8");
|
|
303
|
-
const next = src.replace(/^import \{ useAuthStore \} from '@icore\/template-shared';\n/m, "").replace(/, redirect/g, "").replace(/\n {2}beforeLoad: \(\) => \{[\s\S]*?\n {2}\},/m, "");
|
|
304
|
-
await (0, import_promises2.writeFile)(dashboardPath, next);
|
|
305
|
-
} catch {
|
|
306
|
-
}
|
|
307
|
-
for (const alias of [
|
|
308
|
-
"@icore/auth-client",
|
|
309
|
-
"@icore/auth-supabase",
|
|
310
|
-
"@icore/auth-firebase",
|
|
311
|
-
"@icore/auth-mongodb"
|
|
312
|
-
]) {
|
|
313
|
-
await stripTsconfigPath(targetDir, alias);
|
|
314
|
-
}
|
|
315
|
-
await stripDeps((0, import_node_path2.join)(targetDir, "apps/api/package.json"), ["@icore/auth-client"]);
|
|
316
|
-
const gatewayEnv = (0, import_node_path2.join)(targetDir, "apps/api/.env");
|
|
317
|
-
try {
|
|
318
|
-
const env = await (0, import_promises2.readFile)(gatewayEnv, "utf8");
|
|
319
|
-
const next = env.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
320
|
-
await (0, import_promises2.writeFile)(gatewayEnv, next);
|
|
309
|
+
const src = await (0, import_promises2.readFile)(indexPath, "utf8");
|
|
310
|
+
await (0, import_promises2.writeFile)(
|
|
311
|
+
indexPath,
|
|
312
|
+
src.replace(/^export \* from '\.\/strategies';\n/m, "").replace(/^export \* from '\.\/transport';\n?/m, "")
|
|
313
|
+
);
|
|
321
314
|
} catch {
|
|
322
315
|
}
|
|
323
|
-
const
|
|
316
|
+
const pkgPath = (0, import_node_path2.join)(targetDir, "libs/shared/package.json");
|
|
324
317
|
try {
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
318
|
+
const pkg = JSON.parse(await (0, import_promises2.readFile)(pkgPath, "utf8"));
|
|
319
|
+
if (pkg.exports) delete pkg.exports["./testing"];
|
|
320
|
+
if (pkg.dependencies) delete pkg.dependencies["@nestjs/microservices"];
|
|
321
|
+
await (0, import_promises2.writeFile)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
328
322
|
} catch {
|
|
329
323
|
}
|
|
330
324
|
}
|
|
@@ -334,7 +328,8 @@ async function removeUploadStack(targetDir) {
|
|
|
334
328
|
"apps/microservices/upload-e2e",
|
|
335
329
|
"libs/storage-strategies",
|
|
336
330
|
"libs/upload-client",
|
|
337
|
-
"apps/api/src/app/storage"
|
|
331
|
+
"apps/api/src/app/storage",
|
|
332
|
+
"Dockerfile.ms-upload"
|
|
338
333
|
];
|
|
339
334
|
for (const p2 of paths) {
|
|
340
335
|
await (0, import_promises2.rm)((0, import_node_path2.join)(targetDir, p2), { recursive: true, force: true });
|
|
@@ -359,11 +354,683 @@ async function removeUploadStack(targetDir) {
|
|
|
359
354
|
"@icore/upload-client",
|
|
360
355
|
"@types/multer"
|
|
361
356
|
]);
|
|
357
|
+
const uploadComposePath = (0, import_node_path2.join)(targetDir, "docker-compose.yml");
|
|
358
|
+
try {
|
|
359
|
+
const compose = await (0, import_promises2.readFile)(uploadComposePath, "utf8");
|
|
360
|
+
const next = compose.replace(/\n {2}upload:[\s\S]+?(?=\n {2}\w|\nnetworks:)/m, "\n").replace(/\n {6}upload:\n {8}condition: service_started/g, "").replace(/\n {6}UPLOAD_TRANSPORT:[^\n]*/g, "").replace(/\n {6}UPLOAD_REDIS_URL:[^\n]*/g, "");
|
|
361
|
+
await (0, import_promises2.writeFile)(uploadComposePath, next);
|
|
362
|
+
} catch {
|
|
363
|
+
}
|
|
364
|
+
}
|
|
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
|
+
}
|
|
362
1029
|
}
|
|
363
1030
|
|
|
364
1031
|
// src/manifest/wire-features.ts
|
|
365
|
-
var
|
|
366
|
-
var
|
|
1032
|
+
var import_promises5 = require("fs/promises");
|
|
1033
|
+
var import_node_path5 = require("path");
|
|
367
1034
|
|
|
368
1035
|
// src/manifest/index.ts
|
|
369
1036
|
var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
|
|
@@ -523,8 +1190,8 @@ var MANIFEST = {
|
|
|
523
1190
|
};
|
|
524
1191
|
|
|
525
1192
|
// src/manifest/wire-provider.ts
|
|
526
|
-
var
|
|
527
|
-
var
|
|
1193
|
+
var import_promises4 = require("fs/promises");
|
|
1194
|
+
var import_node_path4 = require("path");
|
|
528
1195
|
async function writeProvider(targetDir, axis, provider) {
|
|
529
1196
|
const nestModule = axis.section[provider]?.nestModule;
|
|
530
1197
|
if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
|
|
@@ -535,27 +1202,27 @@ const ENV_PATH = '${axis.envPath}';
|
|
|
535
1202
|
|
|
536
1203
|
export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
|
|
537
1204
|
`;
|
|
538
|
-
await (0,
|
|
1205
|
+
await (0, import_promises4.writeFile)((0, import_node_path4.join)(targetDir, axis.providerFile), content);
|
|
539
1206
|
}
|
|
540
1207
|
async function stripJsonKeys(path, drop) {
|
|
541
1208
|
try {
|
|
542
|
-
const pkg = JSON.parse(await (0,
|
|
1209
|
+
const pkg = JSON.parse(await (0, import_promises4.readFile)(path, "utf8"));
|
|
543
1210
|
for (const field of ["dependencies", "devDependencies"]) {
|
|
544
1211
|
const deps = pkg[field];
|
|
545
1212
|
if (!deps) continue;
|
|
546
1213
|
for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
|
|
547
1214
|
}
|
|
548
|
-
await (0,
|
|
1215
|
+
await (0, import_promises4.writeFile)(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
549
1216
|
} catch {
|
|
550
1217
|
}
|
|
551
1218
|
}
|
|
552
1219
|
async function stripTsconfigKeys(targetDir, aliases) {
|
|
553
|
-
const path = (0,
|
|
1220
|
+
const path = (0, import_node_path4.join)(targetDir, "tsconfig.base.json");
|
|
554
1221
|
try {
|
|
555
|
-
const parsed = JSON.parse(await (0,
|
|
1222
|
+
const parsed = JSON.parse(await (0, import_promises4.readFile)(path, "utf8"));
|
|
556
1223
|
const paths = parsed.compilerOptions?.paths;
|
|
557
1224
|
if (paths) for (const a of aliases) delete paths[a];
|
|
558
|
-
await (0,
|
|
1225
|
+
await (0, import_promises4.writeFile)(path, JSON.stringify(parsed, null, 2) + "\n");
|
|
559
1226
|
} catch {
|
|
560
1227
|
}
|
|
561
1228
|
}
|
|
@@ -564,10 +1231,10 @@ async function cleanupUnusedAxis(targetDir, axis, chosen) {
|
|
|
564
1231
|
if (provider === chosen) continue;
|
|
565
1232
|
const unit = axis.section[provider];
|
|
566
1233
|
for (const dir of unit.libDirs)
|
|
567
|
-
await (0,
|
|
568
|
-
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 });
|
|
569
1236
|
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
570
|
-
await stripJsonKeys((0,
|
|
1237
|
+
await stripJsonKeys((0, import_node_path4.join)(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
|
|
571
1238
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
572
1239
|
}
|
|
573
1240
|
}
|
|
@@ -596,7 +1263,7 @@ async function writeFeaturesWiring(targetDir, opts) {
|
|
|
596
1263
|
})
|
|
597
1264
|
export class FeaturesModule {}
|
|
598
1265
|
`;
|
|
599
|
-
await (0,
|
|
1266
|
+
await (0, import_promises5.writeFile)((0, import_node_path5.join)(targetDir, FEATURES_MODULE), featuresModule);
|
|
600
1267
|
const services = [];
|
|
601
1268
|
if (opts.authProvider !== "none") services.push({ name: "auth", prefix: "AUTH" });
|
|
602
1269
|
if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
|
|
@@ -610,14 +1277,14 @@ export const GATEWAY_SERVICES = [
|
|
|
610
1277
|
${entries}
|
|
611
1278
|
];
|
|
612
1279
|
`;
|
|
613
|
-
await (0,
|
|
1280
|
+
await (0, import_promises5.writeFile)((0, import_node_path5.join)(targetDir, GATEWAY_SERVICES), gatewayServices);
|
|
614
1281
|
}
|
|
615
1282
|
async function stripJobsDockerCompose(targetDir) {
|
|
616
|
-
const composePath = (0,
|
|
1283
|
+
const composePath = (0, import_node_path5.join)(targetDir, "docker-compose.yml");
|
|
617
1284
|
try {
|
|
618
|
-
const compose = await (0,
|
|
1285
|
+
const compose = await (0, import_promises5.readFile)(composePath, "utf8");
|
|
619
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, "");
|
|
620
|
-
await (0,
|
|
1287
|
+
await (0, import_promises5.writeFile)(composePath, next);
|
|
621
1288
|
} catch {
|
|
622
1289
|
}
|
|
623
1290
|
}
|
|
@@ -627,18 +1294,38 @@ async function cleanupUnusedFeatures(targetDir, opts) {
|
|
|
627
1294
|
if (chosen.has(key)) continue;
|
|
628
1295
|
const unit = FEATURES[key];
|
|
629
1296
|
for (const dir of unit.libDirs)
|
|
630
|
-
await (0,
|
|
1297
|
+
await (0, import_promises5.rm)((0, import_node_path5.join)(targetDir, dir), { recursive: true, force: true });
|
|
631
1298
|
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
632
|
-
await stripJsonKeys((0,
|
|
1299
|
+
await stripJsonKeys((0, import_node_path5.join)(targetDir, API_PKG), (k) => dropKeys.has(k));
|
|
633
1300
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
634
1301
|
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
635
1302
|
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
1303
|
+
if (key === "jobs") {
|
|
1304
|
+
try {
|
|
1305
|
+
await (0, import_promises5.unlink)((0, import_node_path5.join)(targetDir, "libs/shared/src/jobs.ts"));
|
|
1306
|
+
} catch {
|
|
1307
|
+
}
|
|
1308
|
+
try {
|
|
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, ""));
|
|
1316
|
+
} catch {
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
try {
|
|
1321
|
+
await (0, import_promises5.rmdir)((0, import_node_path5.join)(targetDir, "apps/client/src/queries"));
|
|
1322
|
+
} catch {
|
|
636
1323
|
}
|
|
637
1324
|
}
|
|
638
1325
|
|
|
639
1326
|
// src/manifest/wire-client.ts
|
|
640
|
-
var
|
|
641
|
-
var
|
|
1327
|
+
var import_promises6 = require("fs/promises");
|
|
1328
|
+
var import_node_path6 = require("path");
|
|
642
1329
|
var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
|
|
643
1330
|
var BASE_NAV = [
|
|
644
1331
|
{ to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
|
|
@@ -676,12 +1363,12 @@ export const NAV_CONFIG: NavItem[] = [
|
|
|
676
1363
|
` + entries.map(renderEntry).join("\n") + `
|
|
677
1364
|
];
|
|
678
1365
|
`;
|
|
679
|
-
await (0,
|
|
1366
|
+
await (0, import_promises6.writeFile)((0, import_node_path6.join)(targetDir, NAV_CONFIG_FILE), content);
|
|
680
1367
|
}
|
|
681
1368
|
|
|
682
1369
|
// src/manifest/blueprint.ts
|
|
683
|
-
var
|
|
684
|
-
var
|
|
1370
|
+
var import_promises7 = require("fs/promises");
|
|
1371
|
+
var import_node_path7 = require("path");
|
|
685
1372
|
async function writeBlueprintJson(targetDir, opts) {
|
|
686
1373
|
const blueprint = {
|
|
687
1374
|
schemaVersion: 1,
|
|
@@ -696,10 +1383,10 @@ async function writeBlueprintJson(targetDir, opts) {
|
|
|
696
1383
|
transport: opts.transport,
|
|
697
1384
|
packageManager: opts.packageManager
|
|
698
1385
|
};
|
|
699
|
-
await (0,
|
|
1386
|
+
await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
|
|
700
1387
|
}
|
|
701
1388
|
async function writeJson(targetDir, rel, data) {
|
|
702
|
-
await (0,
|
|
1389
|
+
await (0, import_promises7.writeFile)((0, import_node_path7.join)(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
|
|
703
1390
|
}
|
|
704
1391
|
async function writeServiceBlueprints(targetDir, opts) {
|
|
705
1392
|
const t = opts.transport;
|
|
@@ -793,11 +1480,11 @@ var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, prov
|
|
|
793
1480
|
var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
|
|
794
1481
|
|
|
795
1482
|
// src/lib/scaffold-pkg.ts
|
|
796
|
-
var
|
|
797
|
-
var
|
|
1483
|
+
var import_promises8 = require("fs/promises");
|
|
1484
|
+
var import_node_path8 = require("path");
|
|
798
1485
|
async function writePnpmWorkspace(targetDir) {
|
|
799
|
-
const pkgPath = (0,
|
|
800
|
-
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"));
|
|
801
1488
|
const workspaces = pkg.workspaces ?? [];
|
|
802
1489
|
const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
|
|
803
1490
|
const allowBuilds = [
|
|
@@ -820,22 +1507,22 @@ ${packagesBlock}
|
|
|
820
1507
|
allowBuilds:
|
|
821
1508
|
${allowBuilds}
|
|
822
1509
|
`;
|
|
823
|
-
await (0,
|
|
1510
|
+
await (0, import_promises8.writeFile)((0, import_node_path8.join)(targetDir, "pnpm-workspace.yaml"), content);
|
|
824
1511
|
}
|
|
825
1512
|
async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
826
1513
|
async function walk2(dir) {
|
|
827
1514
|
const found = [];
|
|
828
1515
|
let entries;
|
|
829
1516
|
try {
|
|
830
|
-
entries = await (0,
|
|
1517
|
+
entries = await (0, import_promises8.readdir)(dir, { withFileTypes: true });
|
|
831
1518
|
} catch {
|
|
832
1519
|
return found;
|
|
833
1520
|
}
|
|
834
1521
|
for (const e of entries) {
|
|
835
1522
|
if (e.isDirectory() && e.name !== "node_modules") {
|
|
836
|
-
found.push(...await walk2((0,
|
|
1523
|
+
found.push(...await walk2((0, import_node_path8.join)(dir, e.name)));
|
|
837
1524
|
} else if (e.isFile() && e.name === "package.json") {
|
|
838
|
-
found.push((0,
|
|
1525
|
+
found.push((0, import_node_path8.join)(dir, e.name));
|
|
839
1526
|
}
|
|
840
1527
|
}
|
|
841
1528
|
return found;
|
|
@@ -843,17 +1530,17 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
843
1530
|
const pkgFiles = await walk2(targetDir);
|
|
844
1531
|
for (const f of pkgFiles) {
|
|
845
1532
|
try {
|
|
846
|
-
const raw = await (0,
|
|
1533
|
+
const raw = await (0, import_promises8.readFile)(f, "utf8");
|
|
847
1534
|
const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
|
|
848
|
-
if (next !== raw) await (0,
|
|
1535
|
+
if (next !== raw) await (0, import_promises8.writeFile)(f, next);
|
|
849
1536
|
} catch {
|
|
850
1537
|
}
|
|
851
1538
|
}
|
|
852
1539
|
}
|
|
853
1540
|
async function patchGitignoreForPm(targetDir, pm) {
|
|
854
|
-
const giPath = (0,
|
|
1541
|
+
const giPath = (0, import_node_path8.join)(targetDir, ".gitignore");
|
|
855
1542
|
try {
|
|
856
|
-
let src = await (0,
|
|
1543
|
+
let src = await (0, import_promises8.readFile)(giPath, "utf8");
|
|
857
1544
|
src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
|
|
858
1545
|
if (pm !== "yarn") {
|
|
859
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, "");
|
|
@@ -868,7 +1555,7 @@ async function patchGitignoreForPm(targetDir, pm) {
|
|
|
868
1555
|
src += "\n# npm\nnpm-debug.log*\n";
|
|
869
1556
|
}
|
|
870
1557
|
}
|
|
871
|
-
await (0,
|
|
1558
|
+
await (0, import_promises8.writeFile)(giPath, src);
|
|
872
1559
|
} catch {
|
|
873
1560
|
}
|
|
874
1561
|
}
|
|
@@ -876,14 +1563,15 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
876
1563
|
const pm = opts.packageManager;
|
|
877
1564
|
const nx = pm === "npm" ? "npx nx" : `${pm} nx`;
|
|
878
1565
|
const devCmd = pmRun(pm, "dev");
|
|
879
|
-
const activeMSes = [
|
|
1566
|
+
const activeMSes = [];
|
|
1567
|
+
if (opts.authProvider !== "none") activeMSes.push("auth (port 4001)");
|
|
880
1568
|
if (opts.upload !== "none") activeMSes.push(`upload (port 4002)`);
|
|
881
1569
|
if (opts.payment !== "none") activeMSes.push(`payment (port 4003)`);
|
|
882
1570
|
if (opts.example !== "none") activeMSes.push(`notes (port 4004)`);
|
|
883
1571
|
if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
|
|
884
1572
|
const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
|
|
885
1573
|
const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
886
|
-
await (0,
|
|
1574
|
+
await (0, import_promises8.writeFile)((0, import_node_path8.join)(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
|
|
887
1575
|
const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
|
|
888
1576
|
const readme = `# ${opts.projectName}
|
|
889
1577
|
|
|
@@ -905,9 +1593,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
905
1593
|
|
|
906
1594
|
\`\`\`bash
|
|
907
1595
|
# 1. Fill in provider credentials
|
|
908
|
-
# apps/microservices/auth/.env
|
|
909
|
-
# apps/microservices/upload/.env (if upload is enabled)
|
|
910
|
-
# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
|
|
1596
|
+
${opts.authProvider !== "none" ? "# apps/microservices/auth/.env\n" : ""}${opts.upload !== "none" ? "# apps/microservices/upload/.env\n" : ""}# apps/client/.env (VITE_API_URL \u2014 already defaults to /api)
|
|
911
1597
|
|
|
912
1598
|
# 2. Start everything
|
|
913
1599
|
${devCmd}
|
|
@@ -933,7 +1619,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
|
|
|
933
1619
|
|
|
934
1620
|
Apache-2.0
|
|
935
1621
|
`;
|
|
936
|
-
await (0,
|
|
1622
|
+
await (0, import_promises8.writeFile)((0, import_node_path8.join)(targetDir, "README.md"), readme);
|
|
937
1623
|
const agents = `# ${opts.projectName} \u2014 Agent Instructions
|
|
938
1624
|
|
|
939
1625
|
## Stack snapshot
|
|
@@ -1003,10 +1689,10 @@ ${nx} g @nx/nest:resource # generate NestJS resource
|
|
|
1003
1689
|
|
|
1004
1690
|
| File | Key vars |
|
|
1005
1691
|
|------|----------|
|
|
1006
|
-
|
|
1007
|
-
${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
|
|
1008
|
-
` : ""}
|
|
1009
|
-
| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
|
|
1692
|
+
${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PROVIDER=${opts.authProvider}\`, ${opts.authProvider === "supabase" ? "`SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`" : "`FB_ADMIN_*`, `FIREBASE_WEB_API_KEY`"} |
|
|
1693
|
+
` : ""}${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PROVIDER=${opts.upload}\`, provider creds |
|
|
1694
|
+
` : ""}${opts.example !== "none" ? `| \`apps/microservices/notes/.env\` | \`DB_PROVIDER=${opts.dbProvider}\`, DB creds |
|
|
1695
|
+
` : ""}| \`apps/client/.env\` | \`VITE_API_URL=/api\` (proxied to :3001 in dev) |
|
|
1010
1696
|
|
|
1011
1697
|
## Testing
|
|
1012
1698
|
|
|
@@ -1014,8 +1700,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
|
|
|
1014
1700
|
- Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
|
|
1015
1701
|
- Run: \`${nx} test <project>\`
|
|
1016
1702
|
`;
|
|
1017
|
-
await (0,
|
|
1018
|
-
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 });
|
|
1019
1705
|
const mcpServers = {
|
|
1020
1706
|
nx: {
|
|
1021
1707
|
command: "npx",
|
|
@@ -1056,8 +1742,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
|
|
|
1056
1742
|
]
|
|
1057
1743
|
}
|
|
1058
1744
|
};
|
|
1059
|
-
await (0,
|
|
1060
|
-
(0,
|
|
1745
|
+
await (0, import_promises8.writeFile)(
|
|
1746
|
+
(0, import_node_path8.join)(targetDir, ".claude", "settings.json"),
|
|
1061
1747
|
JSON.stringify(settings, null, 2) + "\n"
|
|
1062
1748
|
);
|
|
1063
1749
|
}
|
|
@@ -1077,30 +1763,30 @@ var IGNORE_TOP = /* @__PURE__ */ new Set([
|
|
|
1077
1763
|
".vscode"
|
|
1078
1764
|
]);
|
|
1079
1765
|
async function copyTree(src, dest) {
|
|
1080
|
-
await (0,
|
|
1081
|
-
const entries = await (0,
|
|
1766
|
+
await (0, import_promises9.mkdir)(dest, { recursive: true });
|
|
1767
|
+
const entries = await (0, import_promises9.readdir)(src, { withFileTypes: true });
|
|
1082
1768
|
for (const entry of entries) {
|
|
1083
1769
|
if (IGNORE_TOP.has(entry.name)) continue;
|
|
1084
|
-
const s = (0,
|
|
1085
|
-
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);
|
|
1086
1772
|
if (entry.isDirectory()) await copyTree(s, d);
|
|
1087
|
-
else if (entry.isFile()) await (0,
|
|
1773
|
+
else if (entry.isFile()) await (0, import_promises9.copyFile)(s, d);
|
|
1088
1774
|
}
|
|
1089
1775
|
}
|
|
1090
1776
|
async function selectClientTemplate(targetDir, opts) {
|
|
1091
|
-
const templatesRoot = (0,
|
|
1092
|
-
const chosen = (0,
|
|
1093
|
-
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");
|
|
1094
1780
|
let chosenUi = opts.ui;
|
|
1095
1781
|
try {
|
|
1096
|
-
const s = await (0,
|
|
1782
|
+
const s = await (0, import_promises9.stat)(chosen);
|
|
1097
1783
|
if (!s.isDirectory()) throw new Error("not a dir");
|
|
1098
1784
|
await copyTree(chosen, destClient);
|
|
1099
1785
|
} catch {
|
|
1100
1786
|
chosenUi = "shadcn";
|
|
1101
|
-
await copyTree((0,
|
|
1787
|
+
await copyTree((0, import_node_path9.join)(templatesRoot, "client-shadcn"), destClient);
|
|
1102
1788
|
}
|
|
1103
|
-
await (0,
|
|
1789
|
+
await (0, import_promises9.rm)(templatesRoot, { recursive: true, force: true });
|
|
1104
1790
|
await rewriteClientPaths(destClient, chosenUi);
|
|
1105
1791
|
}
|
|
1106
1792
|
async function rewriteClientPaths(clientDir, ui) {
|
|
@@ -1113,11 +1799,11 @@ async function rewriteClientPaths(clientDir, ui) {
|
|
|
1113
1799
|
"eslint.config.mjs"
|
|
1114
1800
|
];
|
|
1115
1801
|
for (const rel of candidates) {
|
|
1116
|
-
const path = (0,
|
|
1802
|
+
const path = (0, import_node_path9.join)(clientDir, rel);
|
|
1117
1803
|
try {
|
|
1118
|
-
const raw = await (0,
|
|
1804
|
+
const raw = await (0, import_promises9.readFile)(path, "utf8");
|
|
1119
1805
|
const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
|
|
1120
|
-
if (next !== raw) await (0,
|
|
1806
|
+
if (next !== raw) await (0, import_promises9.writeFile)(path, next);
|
|
1121
1807
|
} catch {
|
|
1122
1808
|
}
|
|
1123
1809
|
}
|
|
@@ -1133,12 +1819,12 @@ function gitInit(cwd, projectName) {
|
|
|
1133
1819
|
}
|
|
1134
1820
|
function resolveYarnBin(cwd) {
|
|
1135
1821
|
try {
|
|
1136
|
-
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");
|
|
1137
1823
|
const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
|
|
1138
|
-
if (match?.[1]) return (0,
|
|
1824
|
+
if (match?.[1]) return (0, import_node_path9.join)(cwd, match[1].trim());
|
|
1139
1825
|
} catch {
|
|
1140
1826
|
}
|
|
1141
|
-
return (0,
|
|
1827
|
+
return (0, import_node_path9.join)(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
|
|
1142
1828
|
}
|
|
1143
1829
|
function runInstall(cwd, pm) {
|
|
1144
1830
|
if (pm === "yarn") {
|
|
@@ -1149,7 +1835,8 @@ function runInstall(cwd, pm) {
|
|
|
1149
1835
|
(0, import_node_child_process.spawnSync)("pnpm", ["install"], { cwd, stdio: "inherit" });
|
|
1150
1836
|
}
|
|
1151
1837
|
}
|
|
1152
|
-
async function scaffold(
|
|
1838
|
+
async function scaffold(rawOpts, templatesDir) {
|
|
1839
|
+
const opts = rawOpts.authProvider === "none" && rawOpts.example !== "none" ? { ...rawOpts, example: "none" } : rawOpts;
|
|
1153
1840
|
await copyTree(templatesDir, opts.targetDir);
|
|
1154
1841
|
await rewriteRootPackageJson(opts.targetDir, opts);
|
|
1155
1842
|
if (opts.authProvider !== "none") await writeAuthEnv(opts.targetDir, opts);
|
|
@@ -1160,6 +1847,10 @@ async function scaffold(opts, templatesDir) {
|
|
|
1160
1847
|
await writeRootEnv(opts.targetDir, opts);
|
|
1161
1848
|
await selectClientTemplate(opts.targetDir, opts);
|
|
1162
1849
|
await writeClientEnv(opts.targetDir);
|
|
1850
|
+
if (opts.authProvider === "none") {
|
|
1851
|
+
await removeAuthOnlyPaths(opts.targetDir);
|
|
1852
|
+
await applyAuthNoneVariants(opts.targetDir, opts.ui);
|
|
1853
|
+
}
|
|
1163
1854
|
if (opts.upload === "none") await removeUploadStack(opts.targetDir);
|
|
1164
1855
|
await cleanupUnusedFeatures(opts.targetDir, opts);
|
|
1165
1856
|
await writeFeaturesWiring(opts.targetDir, opts);
|
|
@@ -1168,7 +1859,8 @@ async function scaffold(opts, templatesDir) {
|
|
|
1168
1859
|
await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
|
|
1169
1860
|
await writeAuthProvider(opts.targetDir, opts.authProvider);
|
|
1170
1861
|
} else {
|
|
1171
|
-
await
|
|
1862
|
+
await removeAuthTsconfigPaths(opts.targetDir);
|
|
1863
|
+
await removeDockerComposeAuthService(opts.targetDir);
|
|
1172
1864
|
}
|
|
1173
1865
|
if (opts.upload !== "none") {
|
|
1174
1866
|
await cleanupUnusedStorage(opts.targetDir, opts.upload);
|
|
@@ -1181,13 +1873,20 @@ async function scaffold(opts, templatesDir) {
|
|
|
1181
1873
|
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1182
1874
|
}
|
|
1183
1875
|
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1876
|
+
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none" && opts.payment === "none") {
|
|
1877
|
+
await removeStrategiesLib(opts.targetDir);
|
|
1878
|
+
}
|
|
1879
|
+
try {
|
|
1880
|
+
await (0, import_promises9.rmdir)((0, import_node_path9.join)(opts.targetDir, "apps/microservices"));
|
|
1881
|
+
} catch {
|
|
1882
|
+
}
|
|
1184
1883
|
await writeBlueprintJson(opts.targetDir, opts);
|
|
1185
1884
|
await writeServiceBlueprints(opts.targetDir, opts);
|
|
1186
1885
|
if (opts.packageManager === "yarn") {
|
|
1187
|
-
await (0,
|
|
1886
|
+
await (0, import_promises9.writeFile)((0, import_node_path9.join)(opts.targetDir, "yarn.lock"), "");
|
|
1188
1887
|
} else {
|
|
1189
|
-
await (0,
|
|
1190
|
-
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 });
|
|
1191
1890
|
}
|
|
1192
1891
|
if (opts.packageManager === "pnpm") {
|
|
1193
1892
|
await writePnpmWorkspace(opts.targetDir);
|
|
@@ -1201,13 +1900,13 @@ async function scaffold(opts, templatesDir) {
|
|
|
1201
1900
|
|
|
1202
1901
|
// src/lib/prompts.ts
|
|
1203
1902
|
var p = __toESM(require("@clack/prompts"), 1);
|
|
1204
|
-
var import_node_path9 = require("path");
|
|
1205
|
-
var import_promises10 = require("fs/promises");
|
|
1206
1903
|
var import_node_path10 = require("path");
|
|
1904
|
+
var import_promises11 = require("fs/promises");
|
|
1905
|
+
var import_node_path11 = require("path");
|
|
1207
1906
|
var import_node_url = require("url");
|
|
1208
1907
|
|
|
1209
1908
|
// src/lib/config.ts
|
|
1210
|
-
var
|
|
1909
|
+
var import_promises10 = require("fs/promises");
|
|
1211
1910
|
var ConfigFileError = class extends Error {
|
|
1212
1911
|
constructor(message) {
|
|
1213
1912
|
super(message);
|
|
@@ -1278,7 +1977,7 @@ function validateConfig(raw) {
|
|
|
1278
1977
|
async function loadConfig(filePath) {
|
|
1279
1978
|
let raw;
|
|
1280
1979
|
try {
|
|
1281
|
-
raw = await (0,
|
|
1980
|
+
raw = await (0, import_promises10.readFile)(filePath, "utf8");
|
|
1282
1981
|
} catch {
|
|
1283
1982
|
throw new ConfigFileError(`config file not found: ${filePath}`);
|
|
1284
1983
|
}
|
|
@@ -1303,8 +2002,8 @@ function detectPackageManager() {
|
|
|
1303
2002
|
}
|
|
1304
2003
|
async function readSelfVersion() {
|
|
1305
2004
|
try {
|
|
1306
|
-
const here = (0,
|
|
1307
|
-
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");
|
|
1308
2007
|
const pkg = JSON.parse(pkgRaw);
|
|
1309
2008
|
return pkg.version ?? null;
|
|
1310
2009
|
} catch {
|
|
@@ -1512,7 +2211,7 @@ Re-run with @latest to refresh:
|
|
|
1512
2211
|
}) === false;
|
|
1513
2212
|
return {
|
|
1514
2213
|
projectName,
|
|
1515
|
-
targetDir: (0,
|
|
2214
|
+
targetDir: (0, import_node_path10.resolve)(cwd, projectName),
|
|
1516
2215
|
authProvider,
|
|
1517
2216
|
dbProvider,
|
|
1518
2217
|
upload,
|
|
@@ -1528,23 +2227,23 @@ Re-run with @latest to refresh:
|
|
|
1528
2227
|
}
|
|
1529
2228
|
|
|
1530
2229
|
// src/manifest/audit.ts
|
|
1531
|
-
var
|
|
1532
|
-
var
|
|
2230
|
+
var import_promises12 = require("fs/promises");
|
|
2231
|
+
var import_node_path12 = require("path");
|
|
1533
2232
|
var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".nx"]);
|
|
1534
2233
|
async function walk(dir, out = []) {
|
|
1535
|
-
const entries = await (0,
|
|
2234
|
+
const entries = await (0, import_promises12.readdir)(dir, { withFileTypes: true });
|
|
1536
2235
|
for (const e of entries) {
|
|
1537
2236
|
if (e.isDirectory()) {
|
|
1538
|
-
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);
|
|
1539
2238
|
} else if (/\.(ts|tsx|mjs)$/.test(e.name)) {
|
|
1540
|
-
out.push((0,
|
|
2239
|
+
out.push((0, import_node_path12.join)(dir, e.name));
|
|
1541
2240
|
}
|
|
1542
2241
|
}
|
|
1543
2242
|
return out;
|
|
1544
2243
|
}
|
|
1545
2244
|
async function tsconfigAliases(dir) {
|
|
1546
2245
|
try {
|
|
1547
|
-
const raw = await (0,
|
|
2246
|
+
const raw = await (0, import_promises12.readFile)((0, import_node_path12.join)(dir, "tsconfig.base.json"), "utf8");
|
|
1548
2247
|
const aliases = /* @__PURE__ */ new Set();
|
|
1549
2248
|
for (const m of raw.matchAll(/"(@icore\/[a-z0-9.-]+)"\s*:/g)) {
|
|
1550
2249
|
if (m[1]) aliases.add(m[1]);
|
|
@@ -1562,7 +2261,7 @@ var PROVIDER_SDKS = {
|
|
|
1562
2261
|
};
|
|
1563
2262
|
async function readBlueprint(dir) {
|
|
1564
2263
|
try {
|
|
1565
|
-
return JSON.parse(await (0,
|
|
2264
|
+
return JSON.parse(await (0, import_promises12.readFile)((0, import_node_path12.join)(dir, "blueprint.json"), "utf8"));
|
|
1566
2265
|
} catch {
|
|
1567
2266
|
return null;
|
|
1568
2267
|
}
|
|
@@ -1579,29 +2278,29 @@ function forbiddenFromBlueprint(bp) {
|
|
|
1579
2278
|
}
|
|
1580
2279
|
async function allPackageJsons(dir) {
|
|
1581
2280
|
const out = [];
|
|
1582
|
-
const root = (0,
|
|
2281
|
+
const root = (0, import_node_path12.join)(dir, "package.json");
|
|
1583
2282
|
out.push(root);
|
|
1584
2283
|
async function walk2(d) {
|
|
1585
2284
|
let entries;
|
|
1586
2285
|
try {
|
|
1587
|
-
entries = await (0,
|
|
2286
|
+
entries = await (0, import_promises12.readdir)(d, { withFileTypes: true });
|
|
1588
2287
|
} catch {
|
|
1589
2288
|
return;
|
|
1590
2289
|
}
|
|
1591
2290
|
for (const e of entries) {
|
|
1592
2291
|
if (e.isDirectory()) {
|
|
1593
|
-
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));
|
|
1594
2293
|
} else if (e.name === "package.json") {
|
|
1595
|
-
out.push((0,
|
|
2294
|
+
out.push((0, import_node_path12.join)(d, e.name));
|
|
1596
2295
|
}
|
|
1597
2296
|
}
|
|
1598
2297
|
}
|
|
1599
|
-
await walk2((0,
|
|
2298
|
+
await walk2((0, import_node_path12.join)(dir, "apps"));
|
|
1600
2299
|
return out;
|
|
1601
2300
|
}
|
|
1602
2301
|
async function depKeys(pkgPath) {
|
|
1603
2302
|
try {
|
|
1604
|
-
const pkg = JSON.parse(await (0,
|
|
2303
|
+
const pkg = JSON.parse(await (0, import_promises12.readFile)(pkgPath, "utf8"));
|
|
1605
2304
|
return /* @__PURE__ */ new Set([
|
|
1606
2305
|
...Object.keys(pkg.dependencies ?? {}),
|
|
1607
2306
|
...Object.keys(pkg.devDependencies ?? {})
|
|
@@ -1615,7 +2314,7 @@ async function auditProject(dir, opts = {}) {
|
|
|
1615
2314
|
const violations = [];
|
|
1616
2315
|
const aliases = await tsconfigAliases(dir);
|
|
1617
2316
|
for (const file of await walk(dir)) {
|
|
1618
|
-
const src = await (0,
|
|
2317
|
+
const src = await (0, import_promises12.readFile)(file, "utf8");
|
|
1619
2318
|
for (const m of src.matchAll(ICORE_IMPORT)) {
|
|
1620
2319
|
const alias = m[1];
|
|
1621
2320
|
if (alias && !aliases.has(alias)) {
|