@idevconn/create-icore 0.9.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +752 -190
- package/dist/index.cjs +775 -213
- package/dist/index.js +768 -206
- package/package.json +4 -4
- package/templates/.yarn/releases/{yarn-4.16.0.cjs → yarn-4.17.0.cjs} +326 -326
- package/templates/.yarnrc.yml +1 -1
- package/templates/apps/api/package.json +6 -6
- package/templates/apps/microservices/auth/package.json +4 -4
- package/templates/apps/microservices/jobs/package.json +5 -5
- package/templates/apps/microservices/notes/package.json +5 -5
- package/templates/apps/microservices/payment/package.json +4 -4
- package/templates/apps/microservices/upload/package.json +6 -6
- package/templates/apps/templates/client-antd/package.json +2 -2
- package/templates/apps/templates/client-mui/package.json +4 -4
- package/templates/apps/templates/client-shadcn/package.json +7 -7
- package/templates/apps/templates/client-shadcn/src/components/ui/button.tsx +1 -2
- package/templates/libs/auth-client/package.json +3 -3
- package/templates/libs/auth-strategies/firebase/package.json +4 -4
- package/templates/libs/auth-strategies/mongodb/package.json +4 -4
- package/templates/libs/auth-strategies/supabase/package.json +5 -5
- package/templates/libs/db-strategies/firestore/package.json +4 -4
- package/templates/libs/db-strategies/mongodb/package.json +3 -3
- package/templates/libs/db-strategies/supabase/package.json +5 -5
- package/templates/libs/firebase-admin/package.json +1 -1
- package/templates/libs/jobs-client/package.json +4 -4
- package/templates/libs/notes-client/package.json +3 -3
- package/templates/libs/payment-client/package.json +3 -3
- package/templates/libs/shared/package.json +2 -2
- package/templates/libs/storage-strategies/cloudinary/package.json +4 -4
- package/templates/libs/storage-strategies/firebase/package.json +4 -4
- package/templates/libs/storage-strategies/mongodb/package.json +3 -3
- package/templates/libs/storage-strategies/supabase/package.json +5 -5
- package/templates/libs/template-shared/package.json +8 -8
- package/templates/libs/upload-client/package.json +3 -3
- package/templates/libs/vite-plugins/package.json +3 -3
- package/templates/package.json +32 -32
- package/templates/tools/create-icore/_template-shell/package.json +32 -32
package/dist/index.js
CHANGED
|
@@ -4,9 +4,9 @@ function pmRun(pm, script) {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
// src/lib/scaffold.ts
|
|
7
|
-
import { copyFile, mkdir as
|
|
7
|
+
import { copyFile, mkdir as mkdir3, readdir as readdir2, readFile as readFile7, rmdir as rmdir2, stat, writeFile as writeFile9, rm as rm5 } from "fs/promises";
|
|
8
8
|
import { readFileSync } from "fs";
|
|
9
|
-
import { join as
|
|
9
|
+
import { join as join9 } from "path";
|
|
10
10
|
import { spawnSync } from "child_process";
|
|
11
11
|
|
|
12
12
|
// src/lib/scaffold-env.ts
|
|
@@ -172,6 +172,9 @@ async function writeGatewayEnv(targetDir, opts) {
|
|
|
172
172
|
for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
|
|
173
173
|
next = uncommentTransportEnv(next, prefix, opts.transport);
|
|
174
174
|
}
|
|
175
|
+
if (opts.authProvider === "none") {
|
|
176
|
+
next = next.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
177
|
+
}
|
|
175
178
|
await writeFile(join(targetDir, "apps/api/.env"), next);
|
|
176
179
|
}
|
|
177
180
|
async function writeRootEnv(targetDir, opts) {
|
|
@@ -257,6 +260,7 @@ async function removeStrategiesLib(targetDir) {
|
|
|
257
260
|
await rm(join2(targetDir, "libs/shared/src/strategies"), { recursive: true, force: true });
|
|
258
261
|
await rm(join2(targetDir, "libs/shared/src/testing.ts"), { force: true });
|
|
259
262
|
await rm(join2(targetDir, "libs/shared/src/transport.ts"), { force: true });
|
|
263
|
+
await rm(join2(targetDir, "libs/shared/src/__tests__/transport.unit.test.ts"), { force: true });
|
|
260
264
|
const indexPath = join2(targetDir, "libs/shared/src/index.ts");
|
|
261
265
|
try {
|
|
262
266
|
const src = await readFile2(indexPath, "utf8");
|
|
@@ -275,122 +279,6 @@ async function removeStrategiesLib(targetDir) {
|
|
|
275
279
|
} catch {
|
|
276
280
|
}
|
|
277
281
|
}
|
|
278
|
-
async function removeAuthStack(targetDir) {
|
|
279
|
-
const rmPaths = [
|
|
280
|
-
"apps/microservices/auth",
|
|
281
|
-
"libs/auth-strategies",
|
|
282
|
-
"libs/auth-client",
|
|
283
|
-
"Dockerfile.ms-auth",
|
|
284
|
-
"apps/api/src/app/auth",
|
|
285
|
-
"apps/api/src/app/profile",
|
|
286
|
-
"apps/api/src/app/abilities",
|
|
287
|
-
"libs/shared/src/abilities",
|
|
288
|
-
"apps/client/src/components/auth",
|
|
289
|
-
"apps/client/src/routes/login.tsx",
|
|
290
|
-
"apps/client/src/routes/auth.callback.tsx",
|
|
291
|
-
"apps/client/src/routes/auth.oauth.callback.tsx",
|
|
292
|
-
"apps/client/src/routes/_dashboard/profile.tsx"
|
|
293
|
-
];
|
|
294
|
-
for (const p2 of rmPaths) {
|
|
295
|
-
await rm(join2(targetDir, p2), { recursive: true, force: true });
|
|
296
|
-
}
|
|
297
|
-
const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
|
|
298
|
-
try {
|
|
299
|
-
const src = await readFile2(appModulePath, "utf8");
|
|
300
|
-
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, "");
|
|
301
|
-
await writeFile2(appModulePath, next);
|
|
302
|
-
} catch {
|
|
303
|
-
}
|
|
304
|
-
const dashboardPath = join2(targetDir, "apps/client/src/routes/_dashboard.tsx");
|
|
305
|
-
try {
|
|
306
|
-
const src = await readFile2(dashboardPath, "utf8");
|
|
307
|
-
const next = src.replace(/^import \{ useAuthStore \} from '@icore\/template-shared';\n/m, "").replace(/, redirect/g, "").replace(/\n {2}beforeLoad: \(\) => \{[\s\S]*?\n {2}\},/m, "");
|
|
308
|
-
await writeFile2(dashboardPath, next);
|
|
309
|
-
} catch {
|
|
310
|
-
}
|
|
311
|
-
for (const alias of [
|
|
312
|
-
"@icore/auth-client",
|
|
313
|
-
"@icore/auth-supabase",
|
|
314
|
-
"@icore/auth-firebase",
|
|
315
|
-
"@icore/auth-mongodb"
|
|
316
|
-
]) {
|
|
317
|
-
await stripTsconfigPath(targetDir, alias);
|
|
318
|
-
}
|
|
319
|
-
await stripDeps(join2(targetDir, "apps/api/package.json"), [
|
|
320
|
-
"@icore/auth-client",
|
|
321
|
-
"cookie-parser"
|
|
322
|
-
]);
|
|
323
|
-
const gatewayMainPath = join2(targetDir, "apps/api/src/main.ts");
|
|
324
|
-
try {
|
|
325
|
-
const src = await readFile2(gatewayMainPath, "utf8");
|
|
326
|
-
const next = src.replace(/^import cookieParser from 'cookie-parser';\n/m, "").replace(/^\s*app\.use\(cookieParser\(\)\);\n/m, "");
|
|
327
|
-
await writeFile2(gatewayMainPath, next);
|
|
328
|
-
} catch {
|
|
329
|
-
}
|
|
330
|
-
const gatewayEnv = join2(targetDir, "apps/api/.env");
|
|
331
|
-
try {
|
|
332
|
-
const env = await readFile2(gatewayEnv, "utf8");
|
|
333
|
-
const next = env.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
334
|
-
await writeFile2(gatewayEnv, next);
|
|
335
|
-
} catch {
|
|
336
|
-
}
|
|
337
|
-
const composePath = join2(targetDir, "docker-compose.yml");
|
|
338
|
-
try {
|
|
339
|
-
const compose = await readFile2(composePath, "utf8");
|
|
340
|
-
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, "");
|
|
341
|
-
await writeFile2(composePath, next);
|
|
342
|
-
} catch {
|
|
343
|
-
}
|
|
344
|
-
for (const rel of ["libs/shared/src/index.ts", "libs/shared/src/client.ts"]) {
|
|
345
|
-
const sharedIndexPath = join2(targetDir, rel);
|
|
346
|
-
try {
|
|
347
|
-
const src = await readFile2(sharedIndexPath, "utf8");
|
|
348
|
-
await writeFile2(sharedIndexPath, src.replace(/^export \* from '\.\/abilities';\n?/m, ""));
|
|
349
|
-
} catch {
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const routesIndexPath = join2(targetDir, "apps/client/src/routes/index.tsx");
|
|
353
|
-
try {
|
|
354
|
-
const src = await readFile2(routesIndexPath, "utf8");
|
|
355
|
-
await writeFile2(
|
|
356
|
-
routesIndexPath,
|
|
357
|
-
src.replace(/ctaHref="\/login"/, 'ctaHref="/dashboard"').replace(/ctaLabel="Log in →"/, 'ctaLabel="Dashboard \u2192"')
|
|
358
|
-
);
|
|
359
|
-
} catch {
|
|
360
|
-
}
|
|
361
|
-
const mainTsxPath = join2(targetDir, "apps/client/src/main.tsx");
|
|
362
|
-
try {
|
|
363
|
-
const src = await readFile2(mainTsxPath, "utf8");
|
|
364
|
-
const next = src.replace(/\n {2}onUnauthorized: \(\) => router\.navigate\(\{ to: '\/login' \}\),/, "").replace(/^\s*AbilityProvider,\n/m, "").replace(/^\s*<AbilityProvider>\n/m, "").replace(/^\s*<\/AbilityProvider>\n/m, "");
|
|
365
|
-
await writeFile2(mainTsxPath, next);
|
|
366
|
-
} catch {
|
|
367
|
-
}
|
|
368
|
-
await rm(join2(targetDir, "libs/template-shared/src/lib/abilities"), {
|
|
369
|
-
recursive: true,
|
|
370
|
-
force: true
|
|
371
|
-
});
|
|
372
|
-
const templateSharedIndexPath = join2(targetDir, "libs/template-shared/src/index.ts");
|
|
373
|
-
try {
|
|
374
|
-
const src = await readFile2(templateSharedIndexPath, "utf8");
|
|
375
|
-
await writeFile2(
|
|
376
|
-
templateSharedIndexPath,
|
|
377
|
-
src.replace(/^export \* from '\.\/lib\/abilities\/ability-provider\.js';\n/m, "")
|
|
378
|
-
);
|
|
379
|
-
} catch {
|
|
380
|
-
}
|
|
381
|
-
const headerPath = join2(targetDir, "apps/client/src/components/layout/LayoutHeader.tsx");
|
|
382
|
-
try {
|
|
383
|
-
const src = await readFile2(headerPath, "utf8");
|
|
384
|
-
await writeFile2(
|
|
385
|
-
headerPath,
|
|
386
|
-
src.replace(/^import \{ useTranslation \} from 'react-i18next';\n/m, "").replace(/^import \{ useNavigate \} from '@tanstack\/react-router';\n/m, "").replace(/^import \{ LogOut \} from 'lucide-react';\n/m, "").replace(/^import \{ Button \} from '\.\.\/ui\/button';\n/m, "").replace(
|
|
387
|
-
/import \{ useAuthStore, setStoredLocale, type IcoreLocale \} from '@icore\/template-shared';/,
|
|
388
|
-
"import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';"
|
|
389
|
-
).replace(/^ {2}const \{ t \} = useTranslation\(\);\n/m, "").replace(/^ {2}const navigate = useNavigate\(\);\n/m, "").replace(/^ {2}const user = useAuthStore\(\(s\) => s\.user\);\n/m, "").replace(/^ {2}const logout = useAuthStore\(\(s\) => s\.logout\);\n/m, "").replace(/\n {2}function handleLogout\(\) \{[\s\S]*?\n {2}\}\n(?=\n {2}return)/m, "\n").replace(/\n {8}<div className="hidden sm:flex[\s\S]*?\n {8}<\/div>\n/m, "\n").replace(/\n {8}<Button[\s\S]*?sm:hidden[\s\S]*?\n {8}<\/Button>\n/m, "\n")
|
|
390
|
-
);
|
|
391
|
-
} catch {
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
282
|
async function removeUploadStack(targetDir) {
|
|
395
283
|
const paths = [
|
|
396
284
|
"apps/microservices/upload",
|
|
@@ -432,9 +320,674 @@ async function removeUploadStack(targetDir) {
|
|
|
432
320
|
}
|
|
433
321
|
}
|
|
434
322
|
|
|
323
|
+
// src/lib/scaffold-auth-none.ts
|
|
324
|
+
import { mkdir, rm as rm2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
325
|
+
import { dirname, join as join3 } from "path";
|
|
326
|
+
async function stripDeps2(pkgPath, names) {
|
|
327
|
+
try {
|
|
328
|
+
const raw = await readFile3(pkgPath, "utf8");
|
|
329
|
+
const pkg = JSON.parse(raw);
|
|
330
|
+
for (const n of names) {
|
|
331
|
+
if (pkg.dependencies) delete pkg.dependencies[n];
|
|
332
|
+
if (pkg.devDependencies) delete pkg.devDependencies[n];
|
|
333
|
+
}
|
|
334
|
+
await writeFile3(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
var AUTH_ONLY_PATHS = [
|
|
339
|
+
"apps/microservices/auth",
|
|
340
|
+
"libs/auth-strategies",
|
|
341
|
+
"libs/auth-client",
|
|
342
|
+
"Dockerfile.ms-auth",
|
|
343
|
+
"apps/api/src/app/auth",
|
|
344
|
+
"apps/api/src/app/profile",
|
|
345
|
+
"apps/api/src/app/abilities",
|
|
346
|
+
"libs/shared/src/abilities",
|
|
347
|
+
"apps/client/src/components/auth",
|
|
348
|
+
"apps/client/src/routes/login.tsx",
|
|
349
|
+
"apps/client/src/routes/auth.callback.tsx",
|
|
350
|
+
"apps/client/src/routes/auth.oauth.callback.tsx",
|
|
351
|
+
"apps/client/src/routes/_dashboard/profile.tsx",
|
|
352
|
+
"libs/template-shared/src/lib/abilities"
|
|
353
|
+
];
|
|
354
|
+
async function removeAuthOnlyPaths(targetDir) {
|
|
355
|
+
for (const p2 of AUTH_ONLY_PATHS) {
|
|
356
|
+
await rm2(join3(targetDir, p2), { recursive: true, force: true });
|
|
357
|
+
}
|
|
358
|
+
await stripDeps2(join3(targetDir, "apps/api/package.json"), [
|
|
359
|
+
"@icore/auth-client",
|
|
360
|
+
"cookie-parser"
|
|
361
|
+
]);
|
|
362
|
+
}
|
|
363
|
+
async function stripTsconfigPath2(targetDir, alias) {
|
|
364
|
+
const tsconfigPath = join3(targetDir, "tsconfig.base.json");
|
|
365
|
+
try {
|
|
366
|
+
const src = await readFile3(tsconfigPath, "utf8");
|
|
367
|
+
const escaped = alias.replace(/[@/]/g, (c) => c === "@" ? "@" : "\\/");
|
|
368
|
+
const pretty = src.replace(new RegExp(`^\\s*"${escaped}": \\[[^\\]]*\\],?\\n`, "m"), "");
|
|
369
|
+
if (pretty !== src) {
|
|
370
|
+
await writeFile3(tsconfigPath, pretty);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const parsed = JSON.parse(src);
|
|
374
|
+
if (parsed.compilerOptions?.paths) delete parsed.compilerOptions.paths[alias];
|
|
375
|
+
await writeFile3(tsconfigPath, JSON.stringify(parsed));
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
async function removeAuthTsconfigPaths(targetDir) {
|
|
380
|
+
for (const alias of [
|
|
381
|
+
"@icore/auth-client",
|
|
382
|
+
"@icore/auth-supabase",
|
|
383
|
+
"@icore/auth-firebase",
|
|
384
|
+
"@icore/auth-mongodb"
|
|
385
|
+
]) {
|
|
386
|
+
await stripTsconfigPath2(targetDir, alias);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
async function removeDockerComposeAuthService(targetDir) {
|
|
390
|
+
const composePath = join3(targetDir, "docker-compose.yml");
|
|
391
|
+
try {
|
|
392
|
+
const compose = await readFile3(composePath, "utf8");
|
|
393
|
+
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, "");
|
|
394
|
+
await writeFile3(composePath, next);
|
|
395
|
+
} catch {
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
var GATEWAY_MAIN_TS = `import { Logger } from '@nestjs/common';
|
|
399
|
+
import { NestFactory } from '@nestjs/core';
|
|
400
|
+
import { NestExpressApplication } from '@nestjs/platform-express';
|
|
401
|
+
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
|
402
|
+
import { formatGatewayBanner } from '@icore/shared';
|
|
403
|
+
import { AppModule } from './app/app.module';
|
|
404
|
+
import { GATEWAY_SERVICES } from './app/gateway-services';
|
|
405
|
+
import pkg from '@icore/package.json';
|
|
406
|
+
|
|
407
|
+
const DEFAULT_PORT = 3001;
|
|
408
|
+
|
|
409
|
+
async function bootstrap() {
|
|
410
|
+
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
|
411
|
+
app.setGlobalPrefix('api');
|
|
412
|
+
|
|
413
|
+
const swaggerConfig = new DocumentBuilder()
|
|
414
|
+
.setTitle('iCore API')
|
|
415
|
+
.setDescription('iCore Gateway HTTP surface')
|
|
416
|
+
.setVersion(pkg.version)
|
|
417
|
+
.addBearerAuth()
|
|
418
|
+
.build();
|
|
419
|
+
const document = SwaggerModule.createDocument(app, swaggerConfig);
|
|
420
|
+
SwaggerModule.setup('api/docs', app, document);
|
|
421
|
+
|
|
422
|
+
const port = Number(process.env.API_PORT ?? DEFAULT_PORT);
|
|
423
|
+
await app.listen(port);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
bootstrap()
|
|
427
|
+
.then(() => {
|
|
428
|
+
const origin = process.env.API_ORIGIN ?? 'http://localhost';
|
|
429
|
+
const port = Number(process.env.API_PORT ?? DEFAULT_PORT);
|
|
430
|
+
new Logger('API-Bootstrap').log(
|
|
431
|
+
formatGatewayBanner({ port, origin, services: GATEWAY_SERVICES }),
|
|
432
|
+
);
|
|
433
|
+
})
|
|
434
|
+
.catch((err) => {
|
|
435
|
+
new Logger('API-Bootstrap').error(
|
|
436
|
+
'Gateway bootstrap failed',
|
|
437
|
+
err instanceof Error ? err.stack : err,
|
|
438
|
+
);
|
|
439
|
+
process.exit(1);
|
|
440
|
+
});
|
|
441
|
+
`;
|
|
442
|
+
var GATEWAY_APP_MODULE_TS = `import { join } from 'node:path';
|
|
443
|
+
import { Module } from '@nestjs/common';
|
|
444
|
+
import { ConfigModule } from '@nestjs/config';
|
|
445
|
+
import { ThrottlerModule, seconds } from '@nestjs/throttler';
|
|
446
|
+
import { StorageModule } from './storage/storage.module';
|
|
447
|
+
import { FeaturesModule } from './features.module';
|
|
448
|
+
|
|
449
|
+
@Module({
|
|
450
|
+
imports: [
|
|
451
|
+
ConfigModule.forRoot({
|
|
452
|
+
isGlobal: true,
|
|
453
|
+
envFilePath: [join(process.cwd(), 'apps/api/.env'), join(process.cwd(), '.env')],
|
|
454
|
+
}),
|
|
455
|
+
ThrottlerModule.forRoot([{ name: 'auth-burst', ttl: seconds(60), limit: 10 }]),
|
|
456
|
+
StorageModule,
|
|
457
|
+
FeaturesModule,
|
|
458
|
+
],
|
|
459
|
+
})
|
|
460
|
+
export class AppModule {}
|
|
461
|
+
`;
|
|
462
|
+
var SHARED_CLIENT_TS = `// Browser-safe subset of @icore/shared.
|
|
463
|
+
// Import from '@icore/shared/client' in client-side code to avoid pulling
|
|
464
|
+
// in NestJS / Node.js-only modules (transport, strategies, contracts).
|
|
465
|
+
export * from './types';
|
|
466
|
+
`;
|
|
467
|
+
var SHARED_INDEX_TS = `export * from './env';
|
|
468
|
+
export * from './bootstrap';
|
|
469
|
+
export * from './jobs';
|
|
470
|
+
export * from './strategies';
|
|
471
|
+
export * from './transport';
|
|
472
|
+
export * from './types';
|
|
473
|
+
`;
|
|
474
|
+
var TEMPLATE_SHARED_INDEX_TS = `export * from './lib/api/create-api.js';
|
|
475
|
+
export * from './lib/stores/auth.store.js';
|
|
476
|
+
export * from './lib/stores/loading.store.js';
|
|
477
|
+
export * from './lib/i18n/create-i18n.js';
|
|
478
|
+
export * from './lib/i18n/keys.js';
|
|
479
|
+
export * from './lib/notify/use-notify.js';
|
|
480
|
+
export * from './lib/draft/index.js';
|
|
481
|
+
export * from './lib/landing/LandingPage.js';
|
|
482
|
+
export * from './lib/stores/theme.store.js';
|
|
483
|
+
`;
|
|
484
|
+
var SHADCN_MAIN_TSX = `import './globals.css';
|
|
485
|
+
import { StrictMode } from 'react';
|
|
486
|
+
import { createRoot } from 'react-dom/client';
|
|
487
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
488
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
489
|
+
import {
|
|
490
|
+
createIcoreApi,
|
|
491
|
+
createIcoreI18n,
|
|
492
|
+
ICORE_LOCALES,
|
|
493
|
+
useThemeStore,
|
|
494
|
+
} from '@icore/template-shared';
|
|
495
|
+
import { I18nextProvider } from 'react-i18next';
|
|
496
|
+
import { Toaster } from 'sonner';
|
|
497
|
+
import { routeTree } from './routeTree.gen';
|
|
498
|
+
import { wireShadcnNotifier } from './lib/notify';
|
|
499
|
+
|
|
500
|
+
const queryClient = new QueryClient({
|
|
501
|
+
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
505
|
+
|
|
506
|
+
declare module '@tanstack/react-router' {
|
|
507
|
+
interface Register {
|
|
508
|
+
router: typeof router;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
|
|
513
|
+
|
|
514
|
+
// Single shared API instance \u2014 used by every query in src/queries/
|
|
515
|
+
export const api = createIcoreApi({
|
|
516
|
+
baseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
wireShadcnNotifier();
|
|
520
|
+
|
|
521
|
+
// Apply the theme class before React mounts so the first paint is correct
|
|
522
|
+
const applyTheme = (mode: 'light' | 'dark') => {
|
|
523
|
+
document.documentElement.classList.toggle('dark', mode === 'dark');
|
|
524
|
+
};
|
|
525
|
+
applyTheme(useThemeStore.getState().mode);
|
|
526
|
+
useThemeStore.subscribe((s) => applyTheme(s.mode));
|
|
527
|
+
|
|
528
|
+
createRoot(document.getElementById('root')!).render(
|
|
529
|
+
<StrictMode>
|
|
530
|
+
<I18nextProvider i18n={i18n}>
|
|
531
|
+
<QueryClientProvider client={queryClient}>
|
|
532
|
+
<RouterProvider router={router} />
|
|
533
|
+
<Toaster richColors />
|
|
534
|
+
</QueryClientProvider>
|
|
535
|
+
</I18nextProvider>
|
|
536
|
+
</StrictMode>,
|
|
537
|
+
);
|
|
538
|
+
`;
|
|
539
|
+
var SHADCN_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
|
|
540
|
+
import { MainLayout } from '../layouts/MainLayout';
|
|
541
|
+
|
|
542
|
+
export const Route = createFileRoute('/_dashboard')({
|
|
543
|
+
component: () => (
|
|
544
|
+
<MainLayout>
|
|
545
|
+
<Outlet />
|
|
546
|
+
</MainLayout>
|
|
547
|
+
),
|
|
548
|
+
});
|
|
549
|
+
`;
|
|
550
|
+
var SHADCN_INDEX_TSX = `import { createFileRoute } from '@tanstack/react-router';
|
|
551
|
+
import { LandingPage } from '@icore/template-shared';
|
|
552
|
+
|
|
553
|
+
// All version strings are injected at build time by vite.config.mts
|
|
554
|
+
// (reads root package.json via fs.readFileSync so they stay accurate
|
|
555
|
+
// even when workspace packages are bumped independently).
|
|
556
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
557
|
+
|
|
558
|
+
export const Route = createFileRoute('/')({
|
|
559
|
+
component: () => (
|
|
560
|
+
<LandingPage
|
|
561
|
+
coreVersion={APP_VERSION}
|
|
562
|
+
uiLibrary="shadcn"
|
|
563
|
+
deps={[
|
|
564
|
+
{ name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
|
|
565
|
+
{ name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
|
|
566
|
+
{ name: 'tailwindcss', version: (import.meta.env.VITE_DEP_TAILWINDCSS as string) ?? '?' },
|
|
567
|
+
{
|
|
568
|
+
name: '@tanstack/react-router',
|
|
569
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
name: '@tanstack/react-query',
|
|
573
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
|
|
574
|
+
},
|
|
575
|
+
{ name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
|
|
576
|
+
{ name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
|
|
577
|
+
]}
|
|
578
|
+
ctaHref="/dashboard"
|
|
579
|
+
ctaLabel="Dashboard \u2192"
|
|
580
|
+
/>
|
|
581
|
+
),
|
|
582
|
+
});
|
|
583
|
+
`;
|
|
584
|
+
var SHADCN_LAYOUT_HEADER_TSX = `import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
|
|
585
|
+
import { ThemeToggle } from '../ThemeToggle';
|
|
586
|
+
|
|
587
|
+
const LOCALES: { code: IcoreLocale; label: string }[] = [
|
|
588
|
+
{ code: 'en', label: 'EN' },
|
|
589
|
+
{ code: 'ru', label: 'RU' },
|
|
590
|
+
{ code: 'he', label: 'HE' },
|
|
591
|
+
];
|
|
592
|
+
|
|
593
|
+
export function LayoutHeader() {
|
|
594
|
+
function handleLocale(code: IcoreLocale) {
|
|
595
|
+
setStoredLocale(code);
|
|
596
|
+
window.location.reload();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return (
|
|
600
|
+
<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">
|
|
601
|
+
<div className="flex items-center gap-2">
|
|
602
|
+
<div className="flex h-6 w-6 items-center justify-center rounded bg-[--color-primary]">
|
|
603
|
+
<span className="text-xs font-bold text-[--color-primary-foreground]">i</span>
|
|
604
|
+
</div>
|
|
605
|
+
<span className="text-sm font-semibold tracking-tight">iCore</span>
|
|
606
|
+
</div>
|
|
607
|
+
|
|
608
|
+
<div className="flex items-center gap-1">
|
|
609
|
+
<div className="flex items-center rounded-md border border-[--color-border] overflow-hidden mr-2">
|
|
610
|
+
{LOCALES.map(({ code, label }) => (
|
|
611
|
+
<button
|
|
612
|
+
key={code}
|
|
613
|
+
type="button"
|
|
614
|
+
onClick={() => handleLocale(code)}
|
|
615
|
+
className="px-2.5 py-1 text-xs text-[--color-muted-foreground] hover:bg-[--color-muted] hover:text-[--color-foreground] transition-colors cursor-pointer"
|
|
616
|
+
>
|
|
617
|
+
{label}
|
|
618
|
+
</button>
|
|
619
|
+
))}
|
|
620
|
+
</div>
|
|
621
|
+
|
|
622
|
+
<ThemeToggle />
|
|
623
|
+
</div>
|
|
624
|
+
</header>
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
`;
|
|
628
|
+
var ANTD_MAIN_TSX = `import './globals.less';
|
|
629
|
+
import { StrictMode } from 'react';
|
|
630
|
+
import { createRoot } from 'react-dom/client';
|
|
631
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
632
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
633
|
+
import { ConfigProvider, App as AntApp, theme } from 'antd';
|
|
634
|
+
import { I18nextProvider } from 'react-i18next';
|
|
635
|
+
import {
|
|
636
|
+
createIcoreApi,
|
|
637
|
+
createIcoreI18n,
|
|
638
|
+
ICORE_LOCALES,
|
|
639
|
+
useThemeStore,
|
|
640
|
+
} from '@icore/template-shared';
|
|
641
|
+
import { routeTree } from './routeTree.gen';
|
|
642
|
+
|
|
643
|
+
const queryClient = new QueryClient({
|
|
644
|
+
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
648
|
+
|
|
649
|
+
declare module '@tanstack/react-router' {
|
|
650
|
+
interface Register {
|
|
651
|
+
router: typeof router;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
|
|
656
|
+
|
|
657
|
+
// Single shared API instance \u2014 used by every query in src/queries/
|
|
658
|
+
export const api = createIcoreApi({
|
|
659
|
+
baseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
function Root() {
|
|
663
|
+
const mode = useThemeStore((s) => s.mode);
|
|
664
|
+
const algorithm = mode === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm;
|
|
665
|
+
return (
|
|
666
|
+
<ConfigProvider theme={{ algorithm, token: { colorPrimary: '#22c55e', colorLink: '#22c55e' } }}>
|
|
667
|
+
<AntApp>
|
|
668
|
+
<QueryClientProvider client={queryClient}>
|
|
669
|
+
<RouterProvider router={router} />
|
|
670
|
+
</QueryClientProvider>
|
|
671
|
+
</AntApp>
|
|
672
|
+
</ConfigProvider>
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
createRoot(document.getElementById('root')!).render(
|
|
677
|
+
<StrictMode>
|
|
678
|
+
<I18nextProvider i18n={i18n}>
|
|
679
|
+
<Root />
|
|
680
|
+
</I18nextProvider>
|
|
681
|
+
</StrictMode>,
|
|
682
|
+
);
|
|
683
|
+
`;
|
|
684
|
+
var ANTD_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
|
|
685
|
+
import { MainLayout } from '../layouts/MainLayout';
|
|
686
|
+
|
|
687
|
+
export const Route = createFileRoute('/_dashboard')({
|
|
688
|
+
component: () => (
|
|
689
|
+
<MainLayout>
|
|
690
|
+
<Outlet />
|
|
691
|
+
</MainLayout>
|
|
692
|
+
),
|
|
693
|
+
});
|
|
694
|
+
`;
|
|
695
|
+
var ANTD_INDEX_TSX = `import { createFileRoute, Link } from '@tanstack/react-router';
|
|
696
|
+
import { LandingPage } from '@icore/template-shared';
|
|
697
|
+
|
|
698
|
+
// All version strings are injected at build time by vite.config.mts
|
|
699
|
+
// (reads root package.json via fs.readFileSync so they stay accurate
|
|
700
|
+
// even when workspace packages are bumped independently).
|
|
701
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
702
|
+
|
|
703
|
+
export const Route = createFileRoute('/')({
|
|
704
|
+
component: () => (
|
|
705
|
+
<LandingPage
|
|
706
|
+
coreVersion={APP_VERSION}
|
|
707
|
+
uiLibrary="antd"
|
|
708
|
+
deps={[
|
|
709
|
+
{ name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
|
|
710
|
+
{ name: 'antd', version: (import.meta.env.VITE_DEP_ANTD as string) ?? '?' },
|
|
711
|
+
{ name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
|
|
712
|
+
{
|
|
713
|
+
name: '@tanstack/react-router',
|
|
714
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
name: '@tanstack/react-query',
|
|
718
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
|
|
719
|
+
},
|
|
720
|
+
{ name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
|
|
721
|
+
{ name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
|
|
722
|
+
]}
|
|
723
|
+
ctaHref="/dashboard"
|
|
724
|
+
ctaLabel={<Link to="/dashboard">Dashboard \u2192</Link>}
|
|
725
|
+
/>
|
|
726
|
+
),
|
|
727
|
+
});
|
|
728
|
+
`;
|
|
729
|
+
var ANTD_LAYOUT_HEADER_TSX = `import { Button, Layout, Space } from 'antd';
|
|
730
|
+
import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
|
|
731
|
+
import { ThemeToggle } from '../ThemeToggle';
|
|
732
|
+
|
|
733
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
734
|
+
|
|
735
|
+
const LOCALES: { code: IcoreLocale; label: string }[] = [
|
|
736
|
+
{ code: 'en', label: 'EN' },
|
|
737
|
+
{ code: 'ru', label: 'RU' },
|
|
738
|
+
{ code: 'he', label: 'HE' },
|
|
739
|
+
];
|
|
740
|
+
|
|
741
|
+
export function LayoutHeader() {
|
|
742
|
+
function handleLocale(code: IcoreLocale) {
|
|
743
|
+
setStoredLocale(code);
|
|
744
|
+
window.location.reload();
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return (
|
|
748
|
+
<Layout.Header
|
|
749
|
+
style={{
|
|
750
|
+
display: 'flex',
|
|
751
|
+
alignItems: 'center',
|
|
752
|
+
justifyContent: 'space-between',
|
|
753
|
+
padding: '0 24px',
|
|
754
|
+
}}
|
|
755
|
+
>
|
|
756
|
+
<Space>
|
|
757
|
+
<span style={{ color: '#fff', fontWeight: 600, fontSize: 16 }}>iCore</span>
|
|
758
|
+
<span
|
|
759
|
+
style={{
|
|
760
|
+
color: 'rgba(255,255,255,0.45)',
|
|
761
|
+
fontSize: 11,
|
|
762
|
+
background: 'rgba(255,255,255,0.1)',
|
|
763
|
+
padding: '1px 6px',
|
|
764
|
+
borderRadius: 4,
|
|
765
|
+
}}
|
|
766
|
+
>
|
|
767
|
+
v{APP_VERSION}
|
|
768
|
+
</span>
|
|
769
|
+
</Space>
|
|
770
|
+
|
|
771
|
+
<Space size="middle">
|
|
772
|
+
<Space size={4}>
|
|
773
|
+
{LOCALES.map(({ code, label }) => (
|
|
774
|
+
<Button
|
|
775
|
+
key={code}
|
|
776
|
+
size="small"
|
|
777
|
+
type="text"
|
|
778
|
+
style={{ color: 'rgba(255,255,255,0.65)' }}
|
|
779
|
+
onClick={() => handleLocale(code)}
|
|
780
|
+
>
|
|
781
|
+
{label}
|
|
782
|
+
</Button>
|
|
783
|
+
))}
|
|
784
|
+
</Space>
|
|
785
|
+
|
|
786
|
+
<ThemeToggle />
|
|
787
|
+
</Space>
|
|
788
|
+
</Layout.Header>
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
`;
|
|
792
|
+
var MUI_MAIN_TSX = `import './globals.css';
|
|
793
|
+
import { StrictMode, useMemo } from 'react';
|
|
794
|
+
import { createRoot } from 'react-dom/client';
|
|
795
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
796
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
797
|
+
import { CssBaseline, ThemeProvider, createTheme } from '@mui/material';
|
|
798
|
+
import { I18nextProvider } from 'react-i18next';
|
|
799
|
+
import {
|
|
800
|
+
createIcoreApi,
|
|
801
|
+
createIcoreI18n,
|
|
802
|
+
ICORE_LOCALES,
|
|
803
|
+
useThemeStore,
|
|
804
|
+
} from '@icore/template-shared';
|
|
805
|
+
import { routeTree } from './routeTree.gen';
|
|
806
|
+
import { wireMuiNotifier } from './lib/notify';
|
|
807
|
+
|
|
808
|
+
const queryClient = new QueryClient({
|
|
809
|
+
defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } },
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
813
|
+
|
|
814
|
+
declare module '@tanstack/react-router' {
|
|
815
|
+
interface Register {
|
|
816
|
+
router: typeof router;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const i18n = createIcoreI18n({ resources: ICORE_LOCALES });
|
|
821
|
+
|
|
822
|
+
export const api = createIcoreApi({
|
|
823
|
+
baseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
wireMuiNotifier();
|
|
827
|
+
|
|
828
|
+
function Root() {
|
|
829
|
+
const mode = useThemeStore((s) => s.mode);
|
|
830
|
+
const theme = useMemo(
|
|
831
|
+
() => createTheme({ palette: { mode, primary: { main: '#22c55e' } } }),
|
|
832
|
+
[mode],
|
|
833
|
+
);
|
|
834
|
+
return (
|
|
835
|
+
<ThemeProvider theme={theme}>
|
|
836
|
+
<CssBaseline />
|
|
837
|
+
<QueryClientProvider client={queryClient}>
|
|
838
|
+
<RouterProvider router={router} />
|
|
839
|
+
</QueryClientProvider>
|
|
840
|
+
</ThemeProvider>
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
createRoot(document.getElementById('root')!).render(
|
|
845
|
+
<StrictMode>
|
|
846
|
+
<I18nextProvider i18n={i18n}>
|
|
847
|
+
<Root />
|
|
848
|
+
</I18nextProvider>
|
|
849
|
+
</StrictMode>,
|
|
850
|
+
);
|
|
851
|
+
`;
|
|
852
|
+
var MUI_DASHBOARD_TSX = `import { createFileRoute, Outlet } from '@tanstack/react-router';
|
|
853
|
+
import { MainLayout } from '../layouts/MainLayout';
|
|
854
|
+
|
|
855
|
+
export const Route = createFileRoute('/_dashboard')({
|
|
856
|
+
component: () => (
|
|
857
|
+
<MainLayout>
|
|
858
|
+
<Outlet />
|
|
859
|
+
</MainLayout>
|
|
860
|
+
),
|
|
861
|
+
});
|
|
862
|
+
`;
|
|
863
|
+
var MUI_INDEX_TSX = `import { createFileRoute, Link } from '@tanstack/react-router';
|
|
864
|
+
import { LandingPage } from '@icore/template-shared';
|
|
865
|
+
|
|
866
|
+
// All version strings are injected at build time by vite.config.mts
|
|
867
|
+
// (reads root package.json via fs.readFileSync so they stay accurate
|
|
868
|
+
// even when workspace packages are bumped independently).
|
|
869
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
870
|
+
|
|
871
|
+
export const Route = createFileRoute('/')({
|
|
872
|
+
component: () => (
|
|
873
|
+
<LandingPage
|
|
874
|
+
coreVersion={APP_VERSION}
|
|
875
|
+
uiLibrary="mui"
|
|
876
|
+
deps={[
|
|
877
|
+
{ name: 'react', version: (import.meta.env.VITE_DEP_REACT as string) ?? '?' },
|
|
878
|
+
{ name: '@mui/material', version: (import.meta.env.VITE_DEP_MUI as string) ?? '?' },
|
|
879
|
+
{ name: 'vite', version: (import.meta.env.VITE_DEP_VITE as string) ?? '?' },
|
|
880
|
+
{
|
|
881
|
+
name: '@tanstack/react-router',
|
|
882
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_ROUTER as string) ?? '?',
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
name: '@tanstack/react-query',
|
|
886
|
+
version: (import.meta.env.VITE_DEP_TANSTACK_QUERY as string) ?? '?',
|
|
887
|
+
},
|
|
888
|
+
{ name: 'zustand', version: (import.meta.env.VITE_DEP_ZUSTAND as string) ?? '?' },
|
|
889
|
+
{ name: '@casl/ability', version: (import.meta.env.VITE_DEP_CASL as string) ?? '?' },
|
|
890
|
+
]}
|
|
891
|
+
ctaHref="/dashboard"
|
|
892
|
+
ctaLabel={<Link to="/dashboard">Dashboard \u2192</Link>}
|
|
893
|
+
/>
|
|
894
|
+
),
|
|
895
|
+
});
|
|
896
|
+
`;
|
|
897
|
+
var MUI_LAYOUT_HEADER_TSX = `import { AppBar, Box, Button, Toolbar, Typography } from '@mui/material';
|
|
898
|
+
import { useTranslation } from 'react-i18next';
|
|
899
|
+
import { setStoredLocale, type IcoreLocale } from '@icore/template-shared';
|
|
900
|
+
import { ThemeToggle } from '../ThemeToggle';
|
|
901
|
+
|
|
902
|
+
const APP_VERSION = (import.meta.env.VITE_APP_VERSION as string | undefined) ?? '0.0.0-dev';
|
|
903
|
+
|
|
904
|
+
const LOCALES: { code: IcoreLocale; label: string }[] = [
|
|
905
|
+
{ code: 'en', label: 'EN' },
|
|
906
|
+
{ code: 'ru', label: 'RU' },
|
|
907
|
+
{ code: 'he', label: 'HE' },
|
|
908
|
+
];
|
|
909
|
+
|
|
910
|
+
export function LayoutHeader() {
|
|
911
|
+
const { i18n } = useTranslation();
|
|
912
|
+
const currentLocale = i18n.language as IcoreLocale;
|
|
913
|
+
|
|
914
|
+
function handleLocale(code: IcoreLocale) {
|
|
915
|
+
setStoredLocale(code);
|
|
916
|
+
window.location.reload();
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
return (
|
|
920
|
+
<AppBar position="sticky" color="default" elevation={1}>
|
|
921
|
+
<Toolbar sx={{ justifyContent: 'space-between' }}>
|
|
922
|
+
<Typography variant="h6" component="div" fontWeight={600}>
|
|
923
|
+
iCore{' '}
|
|
924
|
+
<span style={{ opacity: 0.6, fontSize: '0.75em', fontWeight: 400 }}>v{APP_VERSION}</span>
|
|
925
|
+
</Typography>
|
|
926
|
+
|
|
927
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
928
|
+
<Box sx={{ display: 'flex', gap: 0.5 }}>
|
|
929
|
+
{LOCALES.map(({ code, label }) => (
|
|
930
|
+
<Button
|
|
931
|
+
key={code}
|
|
932
|
+
size="small"
|
|
933
|
+
variant={currentLocale === code ? 'contained' : 'text'}
|
|
934
|
+
onClick={() => handleLocale(code)}
|
|
935
|
+
>
|
|
936
|
+
{label}
|
|
937
|
+
</Button>
|
|
938
|
+
))}
|
|
939
|
+
</Box>
|
|
940
|
+
|
|
941
|
+
<ThemeToggle />
|
|
942
|
+
</Box>
|
|
943
|
+
</Toolbar>
|
|
944
|
+
</AppBar>
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
`;
|
|
948
|
+
var COMMON_VARIANTS = {
|
|
949
|
+
"apps/api/src/main.ts": GATEWAY_MAIN_TS,
|
|
950
|
+
"apps/api/src/app/app.module.ts": GATEWAY_APP_MODULE_TS,
|
|
951
|
+
"libs/shared/src/client.ts": SHARED_CLIENT_TS,
|
|
952
|
+
"libs/shared/src/index.ts": SHARED_INDEX_TS,
|
|
953
|
+
"libs/template-shared/src/index.ts": TEMPLATE_SHARED_INDEX_TS
|
|
954
|
+
};
|
|
955
|
+
var UI_VARIANTS = {
|
|
956
|
+
shadcn: {
|
|
957
|
+
"apps/client/src/main.tsx": SHADCN_MAIN_TSX,
|
|
958
|
+
"apps/client/src/routes/_dashboard.tsx": SHADCN_DASHBOARD_TSX,
|
|
959
|
+
"apps/client/src/routes/index.tsx": SHADCN_INDEX_TSX,
|
|
960
|
+
"apps/client/src/components/layout/LayoutHeader.tsx": SHADCN_LAYOUT_HEADER_TSX
|
|
961
|
+
},
|
|
962
|
+
antd: {
|
|
963
|
+
"apps/client/src/main.tsx": ANTD_MAIN_TSX,
|
|
964
|
+
"apps/client/src/routes/_dashboard.tsx": ANTD_DASHBOARD_TSX,
|
|
965
|
+
"apps/client/src/routes/index.tsx": ANTD_INDEX_TSX,
|
|
966
|
+
"apps/client/src/components/layout/LayoutHeader.tsx": ANTD_LAYOUT_HEADER_TSX
|
|
967
|
+
},
|
|
968
|
+
mui: {
|
|
969
|
+
"apps/client/src/main.tsx": MUI_MAIN_TSX,
|
|
970
|
+
"apps/client/src/routes/_dashboard.tsx": MUI_DASHBOARD_TSX,
|
|
971
|
+
"apps/client/src/routes/index.tsx": MUI_INDEX_TSX,
|
|
972
|
+
"apps/client/src/components/layout/LayoutHeader.tsx": MUI_LAYOUT_HEADER_TSX
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
async function applyAuthNoneVariants(targetDir, ui) {
|
|
976
|
+
const uiFiles = UI_VARIANTS[ui] ?? UI_VARIANTS["shadcn"];
|
|
977
|
+
const all = { ...COMMON_VARIANTS, ...uiFiles };
|
|
978
|
+
for (const [rel, content] of Object.entries(all)) {
|
|
979
|
+
const dest = join3(targetDir, rel);
|
|
980
|
+
try {
|
|
981
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
982
|
+
await writeFile3(dest, content);
|
|
983
|
+
} catch {
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
435
988
|
// src/manifest/wire-features.ts
|
|
436
|
-
import { readFile as
|
|
437
|
-
import { join as
|
|
989
|
+
import { readFile as readFile5, writeFile as writeFile5, rm as rm4, rmdir, unlink } from "fs/promises";
|
|
990
|
+
import { join as join5 } from "path";
|
|
438
991
|
|
|
439
992
|
// src/manifest/index.ts
|
|
440
993
|
var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
|
|
@@ -594,8 +1147,8 @@ var MANIFEST = {
|
|
|
594
1147
|
};
|
|
595
1148
|
|
|
596
1149
|
// src/manifest/wire-provider.ts
|
|
597
|
-
import { readFile as
|
|
598
|
-
import { join as
|
|
1150
|
+
import { readFile as readFile4, writeFile as writeFile4, rm as rm3 } from "fs/promises";
|
|
1151
|
+
import { join as join4 } from "path";
|
|
599
1152
|
async function writeProvider(targetDir, axis, provider) {
|
|
600
1153
|
const nestModule = axis.section[provider]?.nestModule;
|
|
601
1154
|
if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
|
|
@@ -606,27 +1159,27 @@ const ENV_PATH = '${axis.envPath}';
|
|
|
606
1159
|
|
|
607
1160
|
export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
|
|
608
1161
|
`;
|
|
609
|
-
await
|
|
1162
|
+
await writeFile4(join4(targetDir, axis.providerFile), content);
|
|
610
1163
|
}
|
|
611
1164
|
async function stripJsonKeys(path, drop) {
|
|
612
1165
|
try {
|
|
613
|
-
const pkg = JSON.parse(await
|
|
1166
|
+
const pkg = JSON.parse(await readFile4(path, "utf8"));
|
|
614
1167
|
for (const field of ["dependencies", "devDependencies"]) {
|
|
615
1168
|
const deps = pkg[field];
|
|
616
1169
|
if (!deps) continue;
|
|
617
1170
|
for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
|
|
618
1171
|
}
|
|
619
|
-
await
|
|
1172
|
+
await writeFile4(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
620
1173
|
} catch {
|
|
621
1174
|
}
|
|
622
1175
|
}
|
|
623
1176
|
async function stripTsconfigKeys(targetDir, aliases) {
|
|
624
|
-
const path =
|
|
1177
|
+
const path = join4(targetDir, "tsconfig.base.json");
|
|
625
1178
|
try {
|
|
626
|
-
const parsed = JSON.parse(await
|
|
1179
|
+
const parsed = JSON.parse(await readFile4(path, "utf8"));
|
|
627
1180
|
const paths = parsed.compilerOptions?.paths;
|
|
628
1181
|
if (paths) for (const a of aliases) delete paths[a];
|
|
629
|
-
await
|
|
1182
|
+
await writeFile4(path, JSON.stringify(parsed, null, 2) + "\n");
|
|
630
1183
|
} catch {
|
|
631
1184
|
}
|
|
632
1185
|
}
|
|
@@ -635,10 +1188,10 @@ async function cleanupUnusedAxis(targetDir, axis, chosen) {
|
|
|
635
1188
|
if (provider === chosen) continue;
|
|
636
1189
|
const unit = axis.section[provider];
|
|
637
1190
|
for (const dir of unit.libDirs)
|
|
638
|
-
await
|
|
639
|
-
for (const t of unit.appTests ?? []) await
|
|
1191
|
+
await rm3(join4(targetDir, dir), { recursive: true, force: true });
|
|
1192
|
+
for (const t of unit.appTests ?? []) await rm3(join4(targetDir, t), { force: true });
|
|
640
1193
|
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
641
|
-
await stripJsonKeys(
|
|
1194
|
+
await stripJsonKeys(join4(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
|
|
642
1195
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
643
1196
|
}
|
|
644
1197
|
}
|
|
@@ -667,7 +1220,7 @@ async function writeFeaturesWiring(targetDir, opts) {
|
|
|
667
1220
|
})
|
|
668
1221
|
export class FeaturesModule {}
|
|
669
1222
|
`;
|
|
670
|
-
await
|
|
1223
|
+
await writeFile5(join5(targetDir, FEATURES_MODULE), featuresModule);
|
|
671
1224
|
const services = [];
|
|
672
1225
|
if (opts.authProvider !== "none") services.push({ name: "auth", prefix: "AUTH" });
|
|
673
1226
|
if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
|
|
@@ -681,14 +1234,14 @@ export const GATEWAY_SERVICES = [
|
|
|
681
1234
|
${entries}
|
|
682
1235
|
];
|
|
683
1236
|
`;
|
|
684
|
-
await
|
|
1237
|
+
await writeFile5(join5(targetDir, GATEWAY_SERVICES), gatewayServices);
|
|
685
1238
|
}
|
|
686
1239
|
async function stripJobsDockerCompose(targetDir) {
|
|
687
|
-
const composePath =
|
|
1240
|
+
const composePath = join5(targetDir, "docker-compose.yml");
|
|
688
1241
|
try {
|
|
689
|
-
const compose = await
|
|
1242
|
+
const compose = await readFile5(composePath, "utf8");
|
|
690
1243
|
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, "");
|
|
691
|
-
await
|
|
1244
|
+
await writeFile5(composePath, next);
|
|
692
1245
|
} catch {
|
|
693
1246
|
}
|
|
694
1247
|
}
|
|
@@ -698,34 +1251,38 @@ async function cleanupUnusedFeatures(targetDir, opts) {
|
|
|
698
1251
|
if (chosen.has(key)) continue;
|
|
699
1252
|
const unit = FEATURES[key];
|
|
700
1253
|
for (const dir of unit.libDirs)
|
|
701
|
-
await
|
|
1254
|
+
await rm4(join5(targetDir, dir), { recursive: true, force: true });
|
|
702
1255
|
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
703
|
-
await stripJsonKeys(
|
|
1256
|
+
await stripJsonKeys(join5(targetDir, API_PKG), (k) => dropKeys.has(k));
|
|
704
1257
|
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
705
1258
|
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
706
1259
|
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
707
1260
|
if (key === "jobs") {
|
|
708
1261
|
try {
|
|
709
|
-
await unlink(
|
|
1262
|
+
await unlink(join5(targetDir, "libs/shared/src/jobs.ts"));
|
|
710
1263
|
} catch {
|
|
711
1264
|
}
|
|
712
|
-
const sharedIdx = join4(targetDir, "libs/shared/src/index.ts");
|
|
713
1265
|
try {
|
|
714
|
-
|
|
715
|
-
|
|
1266
|
+
await unlink(join5(targetDir, "libs/shared/src/__tests__/jobs.unit.test.ts"));
|
|
1267
|
+
} catch {
|
|
1268
|
+
}
|
|
1269
|
+
const sharedIdx = join5(targetDir, "libs/shared/src/index.ts");
|
|
1270
|
+
try {
|
|
1271
|
+
const src = await readFile5(sharedIdx, "utf8");
|
|
1272
|
+
await writeFile5(sharedIdx, src.replace(/^export \* from '\.\/jobs';\n/m, ""));
|
|
716
1273
|
} catch {
|
|
717
1274
|
}
|
|
718
1275
|
}
|
|
719
1276
|
}
|
|
720
1277
|
try {
|
|
721
|
-
await rmdir(
|
|
1278
|
+
await rmdir(join5(targetDir, "apps/client/src/queries"));
|
|
722
1279
|
} catch {
|
|
723
1280
|
}
|
|
724
1281
|
}
|
|
725
1282
|
|
|
726
1283
|
// src/manifest/wire-client.ts
|
|
727
|
-
import { writeFile as
|
|
728
|
-
import { join as
|
|
1284
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
1285
|
+
import { join as join6 } from "path";
|
|
729
1286
|
var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
|
|
730
1287
|
var BASE_NAV = [
|
|
731
1288
|
{ to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
|
|
@@ -763,12 +1320,12 @@ export const NAV_CONFIG: NavItem[] = [
|
|
|
763
1320
|
` + entries.map(renderEntry).join("\n") + `
|
|
764
1321
|
];
|
|
765
1322
|
`;
|
|
766
|
-
await
|
|
1323
|
+
await writeFile6(join6(targetDir, NAV_CONFIG_FILE), content);
|
|
767
1324
|
}
|
|
768
1325
|
|
|
769
1326
|
// src/manifest/blueprint.ts
|
|
770
|
-
import { writeFile as
|
|
771
|
-
import { join as
|
|
1327
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
1328
|
+
import { join as join7 } from "path";
|
|
772
1329
|
async function writeBlueprintJson(targetDir, opts) {
|
|
773
1330
|
const blueprint = {
|
|
774
1331
|
schemaVersion: 1,
|
|
@@ -783,10 +1340,10 @@ async function writeBlueprintJson(targetDir, opts) {
|
|
|
783
1340
|
transport: opts.transport,
|
|
784
1341
|
packageManager: opts.packageManager
|
|
785
1342
|
};
|
|
786
|
-
await
|
|
1343
|
+
await writeFile7(join7(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
|
|
787
1344
|
}
|
|
788
1345
|
async function writeJson(targetDir, rel, data) {
|
|
789
|
-
await
|
|
1346
|
+
await writeFile7(join7(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
|
|
790
1347
|
}
|
|
791
1348
|
async function writeServiceBlueprints(targetDir, opts) {
|
|
792
1349
|
const t = opts.transport;
|
|
@@ -880,11 +1437,11 @@ var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, prov
|
|
|
880
1437
|
var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
|
|
881
1438
|
|
|
882
1439
|
// src/lib/scaffold-pkg.ts
|
|
883
|
-
import { readFile as
|
|
884
|
-
import { join as
|
|
1440
|
+
import { readFile as readFile6, writeFile as writeFile8, mkdir as mkdir2, readdir } from "fs/promises";
|
|
1441
|
+
import { join as join8 } from "path";
|
|
885
1442
|
async function writePnpmWorkspace(targetDir) {
|
|
886
|
-
const pkgPath =
|
|
887
|
-
const pkg = JSON.parse(await
|
|
1443
|
+
const pkgPath = join8(targetDir, "package.json");
|
|
1444
|
+
const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
|
|
888
1445
|
const workspaces = pkg.workspaces ?? [];
|
|
889
1446
|
const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
|
|
890
1447
|
const allowBuilds = [
|
|
@@ -907,7 +1464,7 @@ ${packagesBlock}
|
|
|
907
1464
|
allowBuilds:
|
|
908
1465
|
${allowBuilds}
|
|
909
1466
|
`;
|
|
910
|
-
await
|
|
1467
|
+
await writeFile8(join8(targetDir, "pnpm-workspace.yaml"), content);
|
|
911
1468
|
}
|
|
912
1469
|
async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
913
1470
|
async function walk2(dir) {
|
|
@@ -920,9 +1477,9 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
920
1477
|
}
|
|
921
1478
|
for (const e of entries) {
|
|
922
1479
|
if (e.isDirectory() && e.name !== "node_modules") {
|
|
923
|
-
found.push(...await walk2(
|
|
1480
|
+
found.push(...await walk2(join8(dir, e.name)));
|
|
924
1481
|
} else if (e.isFile() && e.name === "package.json") {
|
|
925
|
-
found.push(
|
|
1482
|
+
found.push(join8(dir, e.name));
|
|
926
1483
|
}
|
|
927
1484
|
}
|
|
928
1485
|
return found;
|
|
@@ -930,17 +1487,17 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
930
1487
|
const pkgFiles = await walk2(targetDir);
|
|
931
1488
|
for (const f of pkgFiles) {
|
|
932
1489
|
try {
|
|
933
|
-
const raw = await
|
|
1490
|
+
const raw = await readFile6(f, "utf8");
|
|
934
1491
|
const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
|
|
935
|
-
if (next !== raw) await
|
|
1492
|
+
if (next !== raw) await writeFile8(f, next);
|
|
936
1493
|
} catch {
|
|
937
1494
|
}
|
|
938
1495
|
}
|
|
939
1496
|
}
|
|
940
1497
|
async function patchGitignoreForPm(targetDir, pm) {
|
|
941
|
-
const giPath =
|
|
1498
|
+
const giPath = join8(targetDir, ".gitignore");
|
|
942
1499
|
try {
|
|
943
|
-
let src = await
|
|
1500
|
+
let src = await readFile6(giPath, "utf8");
|
|
944
1501
|
src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
|
|
945
1502
|
if (pm !== "yarn") {
|
|
946
1503
|
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, "");
|
|
@@ -955,7 +1512,7 @@ async function patchGitignoreForPm(targetDir, pm) {
|
|
|
955
1512
|
src += "\n# npm\nnpm-debug.log*\n";
|
|
956
1513
|
}
|
|
957
1514
|
}
|
|
958
|
-
await
|
|
1515
|
+
await writeFile8(giPath, src);
|
|
959
1516
|
} catch {
|
|
960
1517
|
}
|
|
961
1518
|
}
|
|
@@ -971,7 +1528,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
971
1528
|
if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
|
|
972
1529
|
const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
|
|
973
1530
|
const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
974
|
-
await
|
|
1531
|
+
await writeFile8(join8(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
|
|
975
1532
|
const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
|
|
976
1533
|
const readme = `# ${opts.projectName}
|
|
977
1534
|
|
|
@@ -1019,7 +1576,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
|
|
|
1019
1576
|
|
|
1020
1577
|
Apache-2.0
|
|
1021
1578
|
`;
|
|
1022
|
-
await
|
|
1579
|
+
await writeFile8(join8(targetDir, "README.md"), readme);
|
|
1023
1580
|
const agents = `# ${opts.projectName} \u2014 Agent Instructions
|
|
1024
1581
|
|
|
1025
1582
|
## Stack snapshot
|
|
@@ -1100,8 +1657,8 @@ ${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PR
|
|
|
1100
1657
|
- Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
|
|
1101
1658
|
- Run: \`${nx} test <project>\`
|
|
1102
1659
|
`;
|
|
1103
|
-
await
|
|
1104
|
-
await
|
|
1660
|
+
await writeFile8(join8(targetDir, "AGENTS.md"), agents);
|
|
1661
|
+
await mkdir2(join8(targetDir, ".claude"), { recursive: true });
|
|
1105
1662
|
const mcpServers = {
|
|
1106
1663
|
nx: {
|
|
1107
1664
|
command: "npx",
|
|
@@ -1142,8 +1699,8 @@ ${opts.authProvider !== "none" ? `| \`apps/microservices/auth/.env\` | \`AUTH_PR
|
|
|
1142
1699
|
]
|
|
1143
1700
|
}
|
|
1144
1701
|
};
|
|
1145
|
-
await
|
|
1146
|
-
|
|
1702
|
+
await writeFile8(
|
|
1703
|
+
join8(targetDir, ".claude", "settings.json"),
|
|
1147
1704
|
JSON.stringify(settings, null, 2) + "\n"
|
|
1148
1705
|
);
|
|
1149
1706
|
}
|
|
@@ -1163,20 +1720,20 @@ var IGNORE_TOP = /* @__PURE__ */ new Set([
|
|
|
1163
1720
|
".vscode"
|
|
1164
1721
|
]);
|
|
1165
1722
|
async function copyTree(src, dest) {
|
|
1166
|
-
await
|
|
1723
|
+
await mkdir3(dest, { recursive: true });
|
|
1167
1724
|
const entries = await readdir2(src, { withFileTypes: true });
|
|
1168
1725
|
for (const entry of entries) {
|
|
1169
1726
|
if (IGNORE_TOP.has(entry.name)) continue;
|
|
1170
|
-
const s =
|
|
1171
|
-
const d =
|
|
1727
|
+
const s = join9(src, entry.name);
|
|
1728
|
+
const d = join9(dest, entry.name);
|
|
1172
1729
|
if (entry.isDirectory()) await copyTree(s, d);
|
|
1173
1730
|
else if (entry.isFile()) await copyFile(s, d);
|
|
1174
1731
|
}
|
|
1175
1732
|
}
|
|
1176
1733
|
async function selectClientTemplate(targetDir, opts) {
|
|
1177
|
-
const templatesRoot =
|
|
1178
|
-
const chosen =
|
|
1179
|
-
const destClient =
|
|
1734
|
+
const templatesRoot = join9(targetDir, "apps/templates");
|
|
1735
|
+
const chosen = join9(templatesRoot, `client-${opts.ui}`);
|
|
1736
|
+
const destClient = join9(targetDir, "apps/client");
|
|
1180
1737
|
let chosenUi = opts.ui;
|
|
1181
1738
|
try {
|
|
1182
1739
|
const s = await stat(chosen);
|
|
@@ -1184,9 +1741,9 @@ async function selectClientTemplate(targetDir, opts) {
|
|
|
1184
1741
|
await copyTree(chosen, destClient);
|
|
1185
1742
|
} catch {
|
|
1186
1743
|
chosenUi = "shadcn";
|
|
1187
|
-
await copyTree(
|
|
1744
|
+
await copyTree(join9(templatesRoot, "client-shadcn"), destClient);
|
|
1188
1745
|
}
|
|
1189
|
-
await
|
|
1746
|
+
await rm5(templatesRoot, { recursive: true, force: true });
|
|
1190
1747
|
await rewriteClientPaths(destClient, chosenUi);
|
|
1191
1748
|
}
|
|
1192
1749
|
async function rewriteClientPaths(clientDir, ui) {
|
|
@@ -1199,11 +1756,11 @@ async function rewriteClientPaths(clientDir, ui) {
|
|
|
1199
1756
|
"eslint.config.mjs"
|
|
1200
1757
|
];
|
|
1201
1758
|
for (const rel of candidates) {
|
|
1202
|
-
const path =
|
|
1759
|
+
const path = join9(clientDir, rel);
|
|
1203
1760
|
try {
|
|
1204
|
-
const raw = await
|
|
1761
|
+
const raw = await readFile7(path, "utf8");
|
|
1205
1762
|
const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
|
|
1206
|
-
if (next !== raw) await
|
|
1763
|
+
if (next !== raw) await writeFile9(path, next);
|
|
1207
1764
|
} catch {
|
|
1208
1765
|
}
|
|
1209
1766
|
}
|
|
@@ -1219,12 +1776,12 @@ function gitInit(cwd, projectName) {
|
|
|
1219
1776
|
}
|
|
1220
1777
|
function resolveYarnBin(cwd) {
|
|
1221
1778
|
try {
|
|
1222
|
-
const yarnrc = readFileSync(
|
|
1779
|
+
const yarnrc = readFileSync(join9(cwd, ".yarnrc.yml"), "utf8");
|
|
1223
1780
|
const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
|
|
1224
|
-
if (match?.[1]) return
|
|
1781
|
+
if (match?.[1]) return join9(cwd, match[1].trim());
|
|
1225
1782
|
} catch {
|
|
1226
1783
|
}
|
|
1227
|
-
return
|
|
1784
|
+
return join9(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
|
|
1228
1785
|
}
|
|
1229
1786
|
function runInstall(cwd, pm) {
|
|
1230
1787
|
if (pm === "yarn") {
|
|
@@ -1247,6 +1804,10 @@ async function scaffold(rawOpts, templatesDir) {
|
|
|
1247
1804
|
await writeRootEnv(opts.targetDir, opts);
|
|
1248
1805
|
await selectClientTemplate(opts.targetDir, opts);
|
|
1249
1806
|
await writeClientEnv(opts.targetDir);
|
|
1807
|
+
if (opts.authProvider === "none") {
|
|
1808
|
+
await removeAuthOnlyPaths(opts.targetDir);
|
|
1809
|
+
await applyAuthNoneVariants(opts.targetDir, opts.ui);
|
|
1810
|
+
}
|
|
1250
1811
|
if (opts.upload === "none") await removeUploadStack(opts.targetDir);
|
|
1251
1812
|
await cleanupUnusedFeatures(opts.targetDir, opts);
|
|
1252
1813
|
await writeFeaturesWiring(opts.targetDir, opts);
|
|
@@ -1255,7 +1816,8 @@ async function scaffold(rawOpts, templatesDir) {
|
|
|
1255
1816
|
await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
|
|
1256
1817
|
await writeAuthProvider(opts.targetDir, opts.authProvider);
|
|
1257
1818
|
} else {
|
|
1258
|
-
await
|
|
1819
|
+
await removeAuthTsconfigPaths(opts.targetDir);
|
|
1820
|
+
await removeDockerComposeAuthService(opts.targetDir);
|
|
1259
1821
|
}
|
|
1260
1822
|
if (opts.upload !== "none") {
|
|
1261
1823
|
await cleanupUnusedStorage(opts.targetDir, opts.upload);
|
|
@@ -1268,20 +1830,20 @@ async function scaffold(rawOpts, templatesDir) {
|
|
|
1268
1830
|
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1269
1831
|
}
|
|
1270
1832
|
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1271
|
-
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none") {
|
|
1833
|
+
if (opts.authProvider === "none" && opts.upload === "none" && opts.dbProvider === "none" && opts.payment === "none") {
|
|
1272
1834
|
await removeStrategiesLib(opts.targetDir);
|
|
1273
1835
|
}
|
|
1274
1836
|
try {
|
|
1275
|
-
await rmdir2(
|
|
1837
|
+
await rmdir2(join9(opts.targetDir, "apps/microservices"));
|
|
1276
1838
|
} catch {
|
|
1277
1839
|
}
|
|
1278
1840
|
await writeBlueprintJson(opts.targetDir, opts);
|
|
1279
1841
|
await writeServiceBlueprints(opts.targetDir, opts);
|
|
1280
1842
|
if (opts.packageManager === "yarn") {
|
|
1281
|
-
await
|
|
1843
|
+
await writeFile9(join9(opts.targetDir, "yarn.lock"), "");
|
|
1282
1844
|
} else {
|
|
1283
|
-
await
|
|
1284
|
-
await
|
|
1845
|
+
await rm5(join9(opts.targetDir, ".yarn"), { recursive: true, force: true });
|
|
1846
|
+
await rm5(join9(opts.targetDir, ".yarnrc.yml"), { force: true });
|
|
1285
1847
|
}
|
|
1286
1848
|
if (opts.packageManager === "pnpm") {
|
|
1287
1849
|
await writePnpmWorkspace(opts.targetDir);
|
|
@@ -1296,12 +1858,12 @@ async function scaffold(rawOpts, templatesDir) {
|
|
|
1296
1858
|
// src/lib/prompts.ts
|
|
1297
1859
|
import * as p from "@clack/prompts";
|
|
1298
1860
|
import { resolve } from "path";
|
|
1299
|
-
import { readFile as
|
|
1300
|
-
import { dirname, join as
|
|
1861
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
1862
|
+
import { dirname as dirname2, join as join10 } from "path";
|
|
1301
1863
|
import { fileURLToPath } from "url";
|
|
1302
1864
|
|
|
1303
1865
|
// src/lib/config.ts
|
|
1304
|
-
import { readFile as
|
|
1866
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
1305
1867
|
var ConfigFileError = class extends Error {
|
|
1306
1868
|
constructor(message) {
|
|
1307
1869
|
super(message);
|
|
@@ -1372,7 +1934,7 @@ function validateConfig(raw) {
|
|
|
1372
1934
|
async function loadConfig(filePath) {
|
|
1373
1935
|
let raw;
|
|
1374
1936
|
try {
|
|
1375
|
-
raw = await
|
|
1937
|
+
raw = await readFile8(filePath, "utf8");
|
|
1376
1938
|
} catch {
|
|
1377
1939
|
throw new ConfigFileError(`config file not found: ${filePath}`);
|
|
1378
1940
|
}
|
|
@@ -1397,8 +1959,8 @@ function detectPackageManager() {
|
|
|
1397
1959
|
}
|
|
1398
1960
|
async function readSelfVersion() {
|
|
1399
1961
|
try {
|
|
1400
|
-
const here =
|
|
1401
|
-
const pkgRaw = await
|
|
1962
|
+
const here = dirname2(fileURLToPath(import.meta.url));
|
|
1963
|
+
const pkgRaw = await readFile9(join10(here, "..", "package.json"), "utf8");
|
|
1402
1964
|
const pkg = JSON.parse(pkgRaw);
|
|
1403
1965
|
return pkg.version ?? null;
|
|
1404
1966
|
} catch {
|
|
@@ -1622,23 +2184,23 @@ Re-run with @latest to refresh:
|
|
|
1622
2184
|
}
|
|
1623
2185
|
|
|
1624
2186
|
// src/manifest/audit.ts
|
|
1625
|
-
import { readFile as
|
|
1626
|
-
import { join as
|
|
2187
|
+
import { readFile as readFile10, readdir as readdir3 } from "fs/promises";
|
|
2188
|
+
import { join as join11 } from "path";
|
|
1627
2189
|
var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".nx"]);
|
|
1628
2190
|
async function walk(dir, out = []) {
|
|
1629
2191
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
1630
2192
|
for (const e of entries) {
|
|
1631
2193
|
if (e.isDirectory()) {
|
|
1632
|
-
if (!IGNORE_DIRS.has(e.name)) await walk(
|
|
2194
|
+
if (!IGNORE_DIRS.has(e.name)) await walk(join11(dir, e.name), out);
|
|
1633
2195
|
} else if (/\.(ts|tsx|mjs)$/.test(e.name)) {
|
|
1634
|
-
out.push(
|
|
2196
|
+
out.push(join11(dir, e.name));
|
|
1635
2197
|
}
|
|
1636
2198
|
}
|
|
1637
2199
|
return out;
|
|
1638
2200
|
}
|
|
1639
2201
|
async function tsconfigAliases(dir) {
|
|
1640
2202
|
try {
|
|
1641
|
-
const raw = await
|
|
2203
|
+
const raw = await readFile10(join11(dir, "tsconfig.base.json"), "utf8");
|
|
1642
2204
|
const aliases = /* @__PURE__ */ new Set();
|
|
1643
2205
|
for (const m of raw.matchAll(/"(@icore\/[a-z0-9.-]+)"\s*:/g)) {
|
|
1644
2206
|
if (m[1]) aliases.add(m[1]);
|
|
@@ -1656,7 +2218,7 @@ var PROVIDER_SDKS = {
|
|
|
1656
2218
|
};
|
|
1657
2219
|
async function readBlueprint(dir) {
|
|
1658
2220
|
try {
|
|
1659
|
-
return JSON.parse(await
|
|
2221
|
+
return JSON.parse(await readFile10(join11(dir, "blueprint.json"), "utf8"));
|
|
1660
2222
|
} catch {
|
|
1661
2223
|
return null;
|
|
1662
2224
|
}
|
|
@@ -1673,7 +2235,7 @@ function forbiddenFromBlueprint(bp) {
|
|
|
1673
2235
|
}
|
|
1674
2236
|
async function allPackageJsons(dir) {
|
|
1675
2237
|
const out = [];
|
|
1676
|
-
const root =
|
|
2238
|
+
const root = join11(dir, "package.json");
|
|
1677
2239
|
out.push(root);
|
|
1678
2240
|
async function walk2(d) {
|
|
1679
2241
|
let entries;
|
|
@@ -1684,18 +2246,18 @@ async function allPackageJsons(dir) {
|
|
|
1684
2246
|
}
|
|
1685
2247
|
for (const e of entries) {
|
|
1686
2248
|
if (e.isDirectory()) {
|
|
1687
|
-
if (!IGNORE_DIRS.has(e.name)) await walk2(
|
|
2249
|
+
if (!IGNORE_DIRS.has(e.name)) await walk2(join11(d, e.name));
|
|
1688
2250
|
} else if (e.name === "package.json") {
|
|
1689
|
-
out.push(
|
|
2251
|
+
out.push(join11(d, e.name));
|
|
1690
2252
|
}
|
|
1691
2253
|
}
|
|
1692
2254
|
}
|
|
1693
|
-
await walk2(
|
|
2255
|
+
await walk2(join11(dir, "apps"));
|
|
1694
2256
|
return out;
|
|
1695
2257
|
}
|
|
1696
2258
|
async function depKeys(pkgPath) {
|
|
1697
2259
|
try {
|
|
1698
|
-
const pkg = JSON.parse(await
|
|
2260
|
+
const pkg = JSON.parse(await readFile10(pkgPath, "utf8"));
|
|
1699
2261
|
return /* @__PURE__ */ new Set([
|
|
1700
2262
|
...Object.keys(pkg.dependencies ?? {}),
|
|
1701
2263
|
...Object.keys(pkg.devDependencies ?? {})
|
|
@@ -1709,7 +2271,7 @@ async function auditProject(dir, opts = {}) {
|
|
|
1709
2271
|
const violations = [];
|
|
1710
2272
|
const aliases = await tsconfigAliases(dir);
|
|
1711
2273
|
for (const file of await walk(dir)) {
|
|
1712
|
-
const src = await
|
|
2274
|
+
const src = await readFile10(file, "utf8");
|
|
1713
2275
|
for (const m of src.matchAll(ICORE_IMPORT)) {
|
|
1714
2276
|
const alias = m[1];
|
|
1715
2277
|
if (alias && !aliases.has(alias)) {
|