@idevconn/create-icore 0.5.2 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +109 -45
- package/dist/index.cjs +109 -45
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +109 -45
- package/package.json +1 -1
- package/templates/apps/api/.env.example +20 -0
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/tsconfig.json +6 -1
- package/templates/apps/api/webpack.config.js +20 -0
- package/templates/apps/microservices/auth/.env.example +5 -0
- package/templates/apps/microservices/auth/tsconfig.json +6 -1
- package/templates/apps/microservices/auth/webpack.config.js +30 -0
- package/templates/apps/microservices/jobs/tsconfig.json +6 -1
- package/templates/apps/microservices/jobs/webpack.config.js +30 -0
- package/templates/apps/microservices/notes/.env.example +5 -0
- package/templates/apps/microservices/notes/tsconfig.json +6 -1
- package/templates/apps/microservices/notes/webpack.config.js +30 -0
- package/templates/apps/microservices/notes-e2e/src/support/global.d.ts +6 -0
- package/templates/apps/microservices/payment/.env.example +5 -0
- package/templates/apps/microservices/payment/tsconfig.json +6 -1
- package/templates/apps/microservices/payment/webpack.config.js +30 -0
- package/templates/apps/microservices/upload/.env.example +5 -0
- package/templates/apps/microservices/upload/tsconfig.json +6 -1
- package/templates/apps/microservices/upload/webpack.config.js +30 -0
- package/templates/apps/templates/client-antd/src/components/AccessDeniedPage.tsx +1 -1
- package/templates/apps/templates/client-antd/src/components/layout/LayoutHeader.tsx +1 -1
- package/templates/apps/templates/client-antd/src/components/layout/LayoutSider.tsx +3 -3
- package/templates/apps/templates/client-antd/src/routes/_dashboard/dashboard.tsx +2 -2
- package/templates/apps/templates/client-antd/src/routes/_dashboard/notes.tsx +2 -2
- package/templates/apps/templates/client-antd/src/routes/_dashboard/profile.tsx +2 -2
- package/templates/apps/templates/client-antd/src/routes/auth.callback.tsx +1 -1
- package/templates/apps/templates/client-antd/src/routes/auth.oauth.callback.tsx +1 -1
- package/templates/apps/templates/client-antd/src/routes/login.tsx +1 -1
- package/templates/apps/templates/client-antd/tsconfig.json +6 -1
- package/templates/apps/templates/client-antd-e2e/src/icore.spec.ts +2 -2
- package/templates/apps/templates/client-mui/src/components/AccessDeniedPage.tsx +1 -1
- package/templates/apps/templates/client-mui/src/components/layout/LayoutHeader.tsx +1 -1
- package/templates/apps/templates/client-mui/src/components/layout/LayoutSider.tsx +3 -15
- package/templates/apps/templates/client-mui/src/routes/_dashboard/dashboard.tsx +2 -6
- package/templates/apps/templates/client-mui/src/routes/_dashboard/notes.tsx +2 -2
- package/templates/apps/templates/client-mui/src/routes/_dashboard/profile.tsx +3 -3
- package/templates/apps/templates/client-mui/src/routes/auth.callback.tsx +1 -1
- package/templates/apps/templates/client-mui/src/routes/auth.oauth.callback.tsx +1 -1
- package/templates/apps/templates/client-mui/src/routes/login.tsx +3 -3
- package/templates/apps/templates/client-mui/tsconfig.json +6 -1
- package/templates/apps/templates/client-mui-e2e/src/icore.spec.ts +2 -2
- package/templates/apps/templates/client-shadcn/src/components/AccessDeniedPage.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +3 -3
- package/templates/apps/templates/client-shadcn/src/components/notes/DeleteNoteConfirm.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/notes/NoteDialog.tsx +3 -3
- package/templates/apps/templates/client-shadcn/src/components/notes/NotesTable.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/ui/button.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/ui/card.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/ui/input.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/ui/label.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/routes/_dashboard/dashboard.tsx +3 -9
- package/templates/apps/templates/client-shadcn/src/routes/_dashboard/notes.tsx +6 -6
- package/templates/apps/templates/client-shadcn/src/routes/_dashboard/profile.tsx +7 -7
- package/templates/apps/templates/client-shadcn/src/routes/auth.callback.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/routes/auth.oauth.callback.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/routes/login.tsx +4 -4
- package/templates/apps/templates/client-shadcn/tsconfig.json +6 -1
- package/templates/libs/auth-client/package.json +1 -1
- package/templates/libs/auth-strategies/firebase/package.json +1 -1
- package/templates/libs/auth-strategies/firebase/src/lib/__tests__/firebase-auth.contract.unit.test.ts +6 -3
- package/templates/libs/auth-strategies/supabase/package.json +1 -1
- package/templates/libs/auth-strategies/supabase/src/lib/__tests__/supabase-auth.contract.unit.test.ts +5 -2
- package/templates/libs/db-strategies/firestore/package.json +1 -1
- package/templates/libs/db-strategies/firestore/src/lib/__tests__/firestore-db.contract.unit.test.ts +1 -2
- package/templates/libs/db-strategies/supabase/package.json +1 -1
- package/templates/libs/db-strategies/supabase/src/lib/__tests__/supabase-db.contract.unit.test.ts +1 -2
- package/templates/libs/firebase-admin/package.json +1 -1
- package/templates/libs/jobs-client/package.json +1 -1
- package/templates/libs/notes-client/package.json +1 -1
- package/templates/libs/payment-client/package.json +1 -1
- package/templates/libs/shared/package.json +3 -3
- package/templates/libs/shared/src/__tests__/cross-boundary.unit.test.ts +2 -1
- package/templates/libs/shared/src/__tests__/transport.unit.test.ts +47 -8
- package/templates/libs/shared/src/abilities/subjects.ts +12 -1
- package/templates/libs/shared/src/strategies/__tests__/fake-auth.contract.unit.test.ts +2 -2
- package/templates/libs/shared/src/strategies/__tests__/fake-db.contract.unit.test.ts +2 -2
- package/templates/libs/shared/src/strategies/__tests__/fake-storage.contract.unit.test.ts +2 -2
- package/templates/libs/shared/src/transport.ts +41 -0
- package/templates/libs/storage-strategies/cloudinary/package.json +1 -1
- package/templates/libs/storage-strategies/cloudinary/src/lib/__tests__/cloudinary-storage.contract.unit.test.ts +1 -2
- package/templates/libs/storage-strategies/firebase/package.json +1 -1
- package/templates/libs/storage-strategies/firebase/src/lib/__tests__/firebase-storage.contract.unit.test.ts +1 -2
- package/templates/libs/storage-strategies/supabase/package.json +1 -1
- package/templates/libs/storage-strategies/supabase/src/lib/__tests__/supabase-storage.contract.unit.test.ts +1 -2
- package/templates/libs/template-shared/package.json +1 -1
- package/templates/libs/upload-client/package.json +1 -1
- package/templates/libs/vite-plugins/src/index.d.mts +5 -7
- package/templates/libs/vite-plugins/src/index.mjs +1 -1
- package/templates/libs/vite-plugins/tsconfig.json +2 -1
- package/templates/package.json +2 -1
- package/templates/tools/create-icore/_template-shell/package.json +2 -1
- package/templates/.yarn/releases/yarn-4.5.0.cjs +0 -925
package/dist/cli.js
CHANGED
|
@@ -195,7 +195,10 @@ Re-run with @latest to refresh:
|
|
|
195
195
|
options: [
|
|
196
196
|
{ value: "tcp", label: "TCP (default, no broker required)" },
|
|
197
197
|
{ value: "redis", label: "Redis" },
|
|
198
|
-
{ value: "nats", label: "NATS" }
|
|
198
|
+
{ value: "nats", label: "NATS" },
|
|
199
|
+
{ value: "mqtt", label: "MQTT" },
|
|
200
|
+
{ value: "rmq", label: "RabbitMQ" },
|
|
201
|
+
{ value: "kafka", label: "Kafka" }
|
|
199
202
|
],
|
|
200
203
|
initialValue: "tcp"
|
|
201
204
|
});
|
|
@@ -265,6 +268,24 @@ async function copyTree(src, dest) {
|
|
|
265
268
|
else if (entry.isFile()) await copyFile(s, d);
|
|
266
269
|
}
|
|
267
270
|
}
|
|
271
|
+
var TRANSPORT_ENV_TOKEN = {
|
|
272
|
+
redis: "REDIS",
|
|
273
|
+
nats: "NATS",
|
|
274
|
+
mqtt: "MQTT",
|
|
275
|
+
rmq: "RMQ",
|
|
276
|
+
kafka: "KAFKA"
|
|
277
|
+
};
|
|
278
|
+
var TRANSPORT_DEPS = {
|
|
279
|
+
nats: { nats: "^2.29.3" },
|
|
280
|
+
mqtt: { mqtt: "^5.15.1" },
|
|
281
|
+
rmq: { amqplib: "^2.0.1", "amqp-connection-manager": "^5.0.0" },
|
|
282
|
+
kafka: { kafkajs: "^2.2.4" }
|
|
283
|
+
};
|
|
284
|
+
function uncommentTransportEnv(text2, prefix, transport) {
|
|
285
|
+
const token = TRANSPORT_ENV_TOKEN[transport];
|
|
286
|
+
if (!token) return text2;
|
|
287
|
+
return text2.replace(new RegExp(`^# (${prefix}_${token}_[A-Z0-9_]*=)`, "gm"), "$1");
|
|
288
|
+
}
|
|
268
289
|
async function rewriteRootPackageJson(targetDir, opts) {
|
|
269
290
|
const pkgPath = join2(targetDir, "package.json");
|
|
270
291
|
const raw = await readFile2(pkgPath, "utf8");
|
|
@@ -273,38 +294,31 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
273
294
|
pkg["version"] = "0.0.1";
|
|
274
295
|
pkg["private"] = true;
|
|
275
296
|
delete pkg.description;
|
|
276
|
-
|
|
297
|
+
const transportDeps = TRANSPORT_DEPS[opts.transport];
|
|
298
|
+
if (transportDeps) {
|
|
277
299
|
const deps = pkg["dependencies"] ??= {};
|
|
278
|
-
deps
|
|
300
|
+
Object.assign(deps, transportDeps);
|
|
279
301
|
}
|
|
280
302
|
if (opts.packageManager !== "yarn") {
|
|
281
303
|
delete pkg.packageManager;
|
|
304
|
+
} else {
|
|
305
|
+
try {
|
|
306
|
+
const yarnrc = await readFile2(join2(targetDir, ".yarnrc.yml"), "utf8");
|
|
307
|
+
const match = yarnrc.match(/^yarnPath:\s*.+yarn-(\d+\.\d+\.\d+)\.cjs/m);
|
|
308
|
+
if (match?.[1]) {
|
|
309
|
+
pkg["packageManager"] = `yarn@${match[1]}`;
|
|
310
|
+
}
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
282
313
|
}
|
|
283
|
-
|
|
284
|
-
pkg["pnpm"] = {
|
|
285
|
-
onlyBuiltDependencies: [
|
|
286
|
-
"@firebase/util",
|
|
287
|
-
"@nestjs/core",
|
|
288
|
-
"@parcel/watcher",
|
|
289
|
-
"@scarf/scarf",
|
|
290
|
-
"@swc/core",
|
|
291
|
-
"less",
|
|
292
|
-
"msgpackr-extract",
|
|
293
|
-
"nx",
|
|
294
|
-
"protobufjs",
|
|
295
|
-
"unrs-resolver"
|
|
296
|
-
]
|
|
297
|
-
};
|
|
298
|
-
}
|
|
314
|
+
delete pkg.pnpm;
|
|
299
315
|
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
300
316
|
}
|
|
301
317
|
async function writeAuthEnv(targetDir, opts) {
|
|
302
318
|
const envExample = join2(targetDir, "apps/microservices/auth/.env.example");
|
|
303
319
|
const env = await readFile2(envExample, "utf8");
|
|
304
320
|
let next = env.replace(/^AUTH_PROVIDER=.*$/m, `AUTH_PROVIDER=${opts.authProvider}`).replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`);
|
|
305
|
-
|
|
306
|
-
next = next.replace(/^# (AUTH_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
307
|
-
}
|
|
321
|
+
next = uncommentTransportEnv(next, "AUTH", opts.transport);
|
|
308
322
|
await writeFile(join2(targetDir, "apps/microservices/auth/.env"), next);
|
|
309
323
|
}
|
|
310
324
|
async function writeUploadEnv(targetDir, opts) {
|
|
@@ -312,9 +326,7 @@ async function writeUploadEnv(targetDir, opts) {
|
|
|
312
326
|
const envExample = join2(targetDir, "apps/microservices/upload/.env.example");
|
|
313
327
|
const env = await readFile2(envExample, "utf8");
|
|
314
328
|
let next = env.replace(/^STORAGE_PROVIDER=.*$/m, `STORAGE_PROVIDER=${opts.upload}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
|
|
315
|
-
|
|
316
|
-
next = next.replace(/^# (UPLOAD_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
317
|
-
}
|
|
329
|
+
next = uncommentTransportEnv(next, "UPLOAD", opts.transport);
|
|
318
330
|
await writeFile(join2(targetDir, "apps/microservices/upload/.env"), next);
|
|
319
331
|
}
|
|
320
332
|
async function writeNotesEnv(targetDir, opts) {
|
|
@@ -323,9 +335,7 @@ async function writeNotesEnv(targetDir, opts) {
|
|
|
323
335
|
try {
|
|
324
336
|
const env = await readFile2(envExample, "utf8");
|
|
325
337
|
let next = env.replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`);
|
|
326
|
-
|
|
327
|
-
next = next.replace(/^# (NOTES_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
328
|
-
}
|
|
338
|
+
next = uncommentTransportEnv(next, "NOTES", opts.transport);
|
|
329
339
|
await writeFile(join2(targetDir, "apps/microservices/notes/.env"), next);
|
|
330
340
|
} catch {
|
|
331
341
|
}
|
|
@@ -334,8 +344,8 @@ async function writeGatewayEnv(targetDir, opts) {
|
|
|
334
344
|
const envExample = join2(targetDir, "apps/api/.env.example");
|
|
335
345
|
const env = await readFile2(envExample, "utf8");
|
|
336
346
|
let next = env.replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`).replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
|
|
337
|
-
|
|
338
|
-
next = next
|
|
347
|
+
for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
|
|
348
|
+
next = uncommentTransportEnv(next, prefix, opts.transport);
|
|
339
349
|
}
|
|
340
350
|
await writeFile(join2(targetDir, "apps/api/.env"), next);
|
|
341
351
|
}
|
|
@@ -373,9 +383,7 @@ async function writePaymentEnv(targetDir, opts) {
|
|
|
373
383
|
try {
|
|
374
384
|
const env = await readFile2(envExample, "utf8");
|
|
375
385
|
let next = env.replace(/^PAYMENT_PROVIDER=.*$/m, `PAYMENT_PROVIDER=${opts.payment}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
|
|
376
|
-
|
|
377
|
-
next = next.replace(/^# (PAYMENT_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
378
|
-
}
|
|
386
|
+
next = uncommentTransportEnv(next, "PAYMENT", opts.transport);
|
|
379
387
|
await writeFile(join2(targetDir, "apps/microservices/payment/.env"), next);
|
|
380
388
|
} catch {
|
|
381
389
|
}
|
|
@@ -464,7 +472,10 @@ async function removeNotesStack(targetDir) {
|
|
|
464
472
|
await writeFile(appModulePath, next);
|
|
465
473
|
} catch {
|
|
466
474
|
}
|
|
467
|
-
await stripDeps(join2(targetDir, "apps/api/package.json"), [
|
|
475
|
+
await stripDeps(join2(targetDir, "apps/api/package.json"), [
|
|
476
|
+
"@icore/notes-client",
|
|
477
|
+
"@casl/ability"
|
|
478
|
+
]);
|
|
468
479
|
await stripGatewayTransport(targetDir, "NOTES");
|
|
469
480
|
const tsconfigPath = join2(targetDir, "tsconfig.base.json");
|
|
470
481
|
try {
|
|
@@ -476,21 +487,16 @@ async function removeNotesStack(targetDir) {
|
|
|
476
487
|
const siderPath = join2(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
|
|
477
488
|
try {
|
|
478
489
|
const src = await readFile2(siderPath, "utf8");
|
|
479
|
-
const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
|
|
490
|
+
const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
|
|
480
491
|
"const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
|
|
481
492
|
"const selectedKey = pathname.includes('/profile')"
|
|
482
493
|
).replace(
|
|
483
|
-
|
|
484
|
-
{
|
|
485
|
-
key: 'notes',
|
|
486
|
-
icon: <FileTextOutlined />,
|
|
487
|
-
label: <Link to="/_dashboard/notes">{t('notes.title')}</Link>,
|
|
488
|
-
},`,
|
|
494
|
+
/\n {4}\{\n {6}key: 'notes',\n {6}icon: <FileTextOutlined \/>,\n {6}label: <Link to="\/(?:_dashboard\/)?notes">\{t\('notes\.title'\)\}<\/Link>,\n {4}\},/,
|
|
489
495
|
""
|
|
490
496
|
).replace("import NoteOutlinedIcon from '@mui/icons-material/NoteOutlined';\n", "").replace(
|
|
491
|
-
/\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/ListItemButton>/,
|
|
497
|
+
/\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
|
|
492
498
|
""
|
|
493
|
-
).replace(/\n\s*<Link to="\/_dashboard\/notes">[\s\S]*?<\/Link>/m, "");
|
|
499
|
+
).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
|
|
494
500
|
await writeFile(siderPath, next);
|
|
495
501
|
} catch {
|
|
496
502
|
}
|
|
@@ -633,7 +639,7 @@ async function removeUnusedDbStrategies(targetDir, dbProvider) {
|
|
|
633
639
|
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
634
640
|
try {
|
|
635
641
|
const src = await readFile2(modulePath, "utf8");
|
|
636
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/m, "").replace(
|
|
642
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/m, "").replace(/\nfunction requireEnv[\s\S]*?\n}\n/m, "").replace(
|
|
637
643
|
/if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*return makeFirestoreDB\(cfg\);/m,
|
|
638
644
|
"return makeFirestoreDB(cfg);"
|
|
639
645
|
);
|
|
@@ -766,11 +772,69 @@ async function scaffold(opts, templatesDir2) {
|
|
|
766
772
|
await rm(join2(opts.targetDir, ".yarn"), { recursive: true, force: true });
|
|
767
773
|
await rm(join2(opts.targetDir, ".yarnrc.yml"), { force: true });
|
|
768
774
|
}
|
|
775
|
+
if (opts.packageManager === "pnpm") {
|
|
776
|
+
await writePnpmWorkspace(opts.targetDir);
|
|
777
|
+
await rewritePnpmWorkspaceDeps(opts.targetDir);
|
|
778
|
+
}
|
|
769
779
|
await patchGitignoreForPm(opts.targetDir, opts.packageManager);
|
|
770
780
|
await writeAiFiles(opts.targetDir, opts);
|
|
771
781
|
if (opts.install) runInstall(opts.targetDir, opts.packageManager);
|
|
772
782
|
if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
|
|
773
783
|
}
|
|
784
|
+
async function writePnpmWorkspace(targetDir) {
|
|
785
|
+
const pkgPath = join2(targetDir, "package.json");
|
|
786
|
+
const pkg = JSON.parse(await readFile2(pkgPath, "utf8"));
|
|
787
|
+
const workspaces = pkg.workspaces ?? [];
|
|
788
|
+
const packagesBlock = workspaces.map((p3) => ` - '${p3}'`).join("\n");
|
|
789
|
+
const allowBuilds = [
|
|
790
|
+
"@firebase/util",
|
|
791
|
+
"@nestjs/core",
|
|
792
|
+
"@parcel/watcher",
|
|
793
|
+
"@scarf/scarf",
|
|
794
|
+
"@swc/core",
|
|
795
|
+
"less",
|
|
796
|
+
"msgpackr-extract",
|
|
797
|
+
"nx",
|
|
798
|
+
"protobufjs",
|
|
799
|
+
"unrs-resolver"
|
|
800
|
+
].map((p3) => ` '${p3}': true`).join("\n");
|
|
801
|
+
const content = `packages:
|
|
802
|
+
${packagesBlock}
|
|
803
|
+
|
|
804
|
+
allowBuilds:
|
|
805
|
+
${allowBuilds}
|
|
806
|
+
`;
|
|
807
|
+
await writeFile(join2(targetDir, "pnpm-workspace.yaml"), content);
|
|
808
|
+
}
|
|
809
|
+
async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
810
|
+
const { readdir: rd } = await import("fs/promises");
|
|
811
|
+
async function walk(dir) {
|
|
812
|
+
const found = [];
|
|
813
|
+
let entries;
|
|
814
|
+
try {
|
|
815
|
+
entries = await rd(dir, { withFileTypes: true });
|
|
816
|
+
} catch {
|
|
817
|
+
return found;
|
|
818
|
+
}
|
|
819
|
+
for (const e of entries) {
|
|
820
|
+
if (e.isDirectory() && e.name !== "node_modules") {
|
|
821
|
+
found.push(...await walk(join2(dir, e.name)));
|
|
822
|
+
} else if (e.isFile() && e.name === "package.json") {
|
|
823
|
+
found.push(join2(dir, e.name));
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return found;
|
|
827
|
+
}
|
|
828
|
+
const pkgFiles = await walk(targetDir);
|
|
829
|
+
for (const f of pkgFiles) {
|
|
830
|
+
try {
|
|
831
|
+
const raw = await readFile2(f, "utf8");
|
|
832
|
+
const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
|
|
833
|
+
if (next !== raw) await writeFile(f, next);
|
|
834
|
+
} catch {
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
774
838
|
async function patchGitignoreForPm(targetDir, pm) {
|
|
775
839
|
const giPath = join2(targetDir, ".gitignore");
|
|
776
840
|
try {
|
|
@@ -874,7 +938,7 @@ Apache-2.0
|
|
|
874
938
|
|
|
875
939
|
- **Branch strategy**: \`dev\` is default. Cut \`feature/<name>\` or \`bug/<name>\` from dev. PRs only target dev. Never push directly to main.
|
|
876
940
|
- **No code without approval**: Propose changes first, wait for go-ahead.
|
|
877
|
-
-
|
|
941
|
+
- **RULE \u2014 no crash on missing .env**: MS factories must catch config errors, print a boxed banner with ALL missing vars, and return a Fake strategy in dev. In prod (\`NODE_ENV=production\`) throw the same banner. The \`formatEnvBanner\` + \`missingEnv\` helpers from \`@icore/shared\` handle this.
|
|
878
942
|
- **Post-coding routine**: \`npx prettier --write <files>\` \u2192 \`${nx} lint <project>\` \u2192 \`${nx} build <project>\` \u2014 all green before committing.
|
|
879
943
|
- **Nx generators only**: never hand-write \`project.json\` / tsconfig stacks. Use \`${nx} g @nx/<plugin>:<schematic>\`.
|
|
880
944
|
|
package/dist/index.cjs
CHANGED
|
@@ -74,6 +74,24 @@ async function copyTree(src, dest) {
|
|
|
74
74
|
else if (entry.isFile()) await (0, import_promises.copyFile)(s, d);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
var TRANSPORT_ENV_TOKEN = {
|
|
78
|
+
redis: "REDIS",
|
|
79
|
+
nats: "NATS",
|
|
80
|
+
mqtt: "MQTT",
|
|
81
|
+
rmq: "RMQ",
|
|
82
|
+
kafka: "KAFKA"
|
|
83
|
+
};
|
|
84
|
+
var TRANSPORT_DEPS = {
|
|
85
|
+
nats: { nats: "^2.29.3" },
|
|
86
|
+
mqtt: { mqtt: "^5.15.1" },
|
|
87
|
+
rmq: { amqplib: "^2.0.1", "amqp-connection-manager": "^5.0.0" },
|
|
88
|
+
kafka: { kafkajs: "^2.2.4" }
|
|
89
|
+
};
|
|
90
|
+
function uncommentTransportEnv(text2, prefix, transport) {
|
|
91
|
+
const token = TRANSPORT_ENV_TOKEN[transport];
|
|
92
|
+
if (!token) return text2;
|
|
93
|
+
return text2.replace(new RegExp(`^# (${prefix}_${token}_[A-Z0-9_]*=)`, "gm"), "$1");
|
|
94
|
+
}
|
|
77
95
|
async function rewriteRootPackageJson(targetDir, opts) {
|
|
78
96
|
const pkgPath = (0, import_node_path.join)(targetDir, "package.json");
|
|
79
97
|
const raw = await (0, import_promises.readFile)(pkgPath, "utf8");
|
|
@@ -82,38 +100,31 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
82
100
|
pkg["version"] = "0.0.1";
|
|
83
101
|
pkg["private"] = true;
|
|
84
102
|
delete pkg.description;
|
|
85
|
-
|
|
103
|
+
const transportDeps = TRANSPORT_DEPS[opts.transport];
|
|
104
|
+
if (transportDeps) {
|
|
86
105
|
const deps = pkg["dependencies"] ??= {};
|
|
87
|
-
deps
|
|
106
|
+
Object.assign(deps, transportDeps);
|
|
88
107
|
}
|
|
89
108
|
if (opts.packageManager !== "yarn") {
|
|
90
109
|
delete pkg.packageManager;
|
|
110
|
+
} else {
|
|
111
|
+
try {
|
|
112
|
+
const yarnrc = await (0, import_promises.readFile)((0, import_node_path.join)(targetDir, ".yarnrc.yml"), "utf8");
|
|
113
|
+
const match = yarnrc.match(/^yarnPath:\s*.+yarn-(\d+\.\d+\.\d+)\.cjs/m);
|
|
114
|
+
if (match?.[1]) {
|
|
115
|
+
pkg["packageManager"] = `yarn@${match[1]}`;
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
91
119
|
}
|
|
92
|
-
|
|
93
|
-
pkg["pnpm"] = {
|
|
94
|
-
onlyBuiltDependencies: [
|
|
95
|
-
"@firebase/util",
|
|
96
|
-
"@nestjs/core",
|
|
97
|
-
"@parcel/watcher",
|
|
98
|
-
"@scarf/scarf",
|
|
99
|
-
"@swc/core",
|
|
100
|
-
"less",
|
|
101
|
-
"msgpackr-extract",
|
|
102
|
-
"nx",
|
|
103
|
-
"protobufjs",
|
|
104
|
-
"unrs-resolver"
|
|
105
|
-
]
|
|
106
|
-
};
|
|
107
|
-
}
|
|
120
|
+
delete pkg.pnpm;
|
|
108
121
|
await (0, import_promises.writeFile)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
109
122
|
}
|
|
110
123
|
async function writeAuthEnv(targetDir, opts) {
|
|
111
124
|
const envExample = (0, import_node_path.join)(targetDir, "apps/microservices/auth/.env.example");
|
|
112
125
|
const env = await (0, import_promises.readFile)(envExample, "utf8");
|
|
113
126
|
let next = env.replace(/^AUTH_PROVIDER=.*$/m, `AUTH_PROVIDER=${opts.authProvider}`).replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`);
|
|
114
|
-
|
|
115
|
-
next = next.replace(/^# (AUTH_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
116
|
-
}
|
|
127
|
+
next = uncommentTransportEnv(next, "AUTH", opts.transport);
|
|
117
128
|
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/microservices/auth/.env"), next);
|
|
118
129
|
}
|
|
119
130
|
async function writeUploadEnv(targetDir, opts) {
|
|
@@ -121,9 +132,7 @@ async function writeUploadEnv(targetDir, opts) {
|
|
|
121
132
|
const envExample = (0, import_node_path.join)(targetDir, "apps/microservices/upload/.env.example");
|
|
122
133
|
const env = await (0, import_promises.readFile)(envExample, "utf8");
|
|
123
134
|
let next = env.replace(/^STORAGE_PROVIDER=.*$/m, `STORAGE_PROVIDER=${opts.upload}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`);
|
|
124
|
-
|
|
125
|
-
next = next.replace(/^# (UPLOAD_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
126
|
-
}
|
|
135
|
+
next = uncommentTransportEnv(next, "UPLOAD", opts.transport);
|
|
127
136
|
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/microservices/upload/.env"), next);
|
|
128
137
|
}
|
|
129
138
|
async function writeNotesEnv(targetDir, opts) {
|
|
@@ -132,9 +141,7 @@ async function writeNotesEnv(targetDir, opts) {
|
|
|
132
141
|
try {
|
|
133
142
|
const env = await (0, import_promises.readFile)(envExample, "utf8");
|
|
134
143
|
let next = env.replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`);
|
|
135
|
-
|
|
136
|
-
next = next.replace(/^# (NOTES_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
137
|
-
}
|
|
144
|
+
next = uncommentTransportEnv(next, "NOTES", opts.transport);
|
|
138
145
|
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/microservices/notes/.env"), next);
|
|
139
146
|
} catch {
|
|
140
147
|
}
|
|
@@ -143,8 +150,8 @@ async function writeGatewayEnv(targetDir, opts) {
|
|
|
143
150
|
const envExample = (0, import_node_path.join)(targetDir, "apps/api/.env.example");
|
|
144
151
|
const env = await (0, import_promises.readFile)(envExample, "utf8");
|
|
145
152
|
let next = env.replace(/^AUTH_TRANSPORT=.*$/m, `AUTH_TRANSPORT=${opts.transport}`).replace(/^UPLOAD_TRANSPORT=.*$/m, `UPLOAD_TRANSPORT=${opts.transport}`).replace(/^NOTES_TRANSPORT=.*$/m, `NOTES_TRANSPORT=${opts.transport}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
|
|
146
|
-
|
|
147
|
-
next = next
|
|
153
|
+
for (const prefix of ["AUTH", "UPLOAD", "NOTES", "PAYMENT"]) {
|
|
154
|
+
next = uncommentTransportEnv(next, prefix, opts.transport);
|
|
148
155
|
}
|
|
149
156
|
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/api/.env"), next);
|
|
150
157
|
}
|
|
@@ -182,9 +189,7 @@ async function writePaymentEnv(targetDir, opts) {
|
|
|
182
189
|
try {
|
|
183
190
|
const env = await (0, import_promises.readFile)(envExample, "utf8");
|
|
184
191
|
let next = env.replace(/^PAYMENT_PROVIDER=.*$/m, `PAYMENT_PROVIDER=${opts.payment}`).replace(/^PAYMENT_TRANSPORT=.*$/m, `PAYMENT_TRANSPORT=${opts.transport}`);
|
|
185
|
-
|
|
186
|
-
next = next.replace(/^# (PAYMENT_(?:REDIS|NATS)_URL=)/m, "$1");
|
|
187
|
-
}
|
|
192
|
+
next = uncommentTransportEnv(next, "PAYMENT", opts.transport);
|
|
188
193
|
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "apps/microservices/payment/.env"), next);
|
|
189
194
|
} catch {
|
|
190
195
|
}
|
|
@@ -273,7 +278,10 @@ async function removeNotesStack(targetDir) {
|
|
|
273
278
|
await (0, import_promises.writeFile)(appModulePath, next);
|
|
274
279
|
} catch {
|
|
275
280
|
}
|
|
276
|
-
await stripDeps((0, import_node_path.join)(targetDir, "apps/api/package.json"), [
|
|
281
|
+
await stripDeps((0, import_node_path.join)(targetDir, "apps/api/package.json"), [
|
|
282
|
+
"@icore/notes-client",
|
|
283
|
+
"@casl/ability"
|
|
284
|
+
]);
|
|
277
285
|
await stripGatewayTransport(targetDir, "NOTES");
|
|
278
286
|
const tsconfigPath = (0, import_node_path.join)(targetDir, "tsconfig.base.json");
|
|
279
287
|
try {
|
|
@@ -285,21 +293,16 @@ async function removeNotesStack(targetDir) {
|
|
|
285
293
|
const siderPath = (0, import_node_path.join)(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
|
|
286
294
|
try {
|
|
287
295
|
const src = await (0, import_promises.readFile)(siderPath, "utf8");
|
|
288
|
-
const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
|
|
296
|
+
const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
|
|
289
297
|
"const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
|
|
290
298
|
"const selectedKey = pathname.includes('/profile')"
|
|
291
299
|
).replace(
|
|
292
|
-
|
|
293
|
-
{
|
|
294
|
-
key: 'notes',
|
|
295
|
-
icon: <FileTextOutlined />,
|
|
296
|
-
label: <Link to="/_dashboard/notes">{t('notes.title')}</Link>,
|
|
297
|
-
},`,
|
|
300
|
+
/\n {4}\{\n {6}key: 'notes',\n {6}icon: <FileTextOutlined \/>,\n {6}label: <Link to="\/(?:_dashboard\/)?notes">\{t\('notes\.title'\)\}<\/Link>,\n {4}\},/,
|
|
298
301
|
""
|
|
299
302
|
).replace("import NoteOutlinedIcon from '@mui/icons-material/NoteOutlined';\n", "").replace(
|
|
300
|
-
/\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/_dashboard\/notes"[\s\S]*?<\/ListItemButton>/,
|
|
303
|
+
/\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
|
|
301
304
|
""
|
|
302
|
-
).replace(/\n\s*<Link to="\/_dashboard\/notes">[\s\S]*?<\/Link>/m, "");
|
|
305
|
+
).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
|
|
303
306
|
await (0, import_promises.writeFile)(siderPath, next);
|
|
304
307
|
} catch {
|
|
305
308
|
}
|
|
@@ -442,7 +445,7 @@ async function removeUnusedDbStrategies(targetDir, dbProvider) {
|
|
|
442
445
|
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
443
446
|
try {
|
|
444
447
|
const src = await (0, import_promises.readFile)(modulePath, "utf8");
|
|
445
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/m, "").replace(
|
|
448
|
+
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/m, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/m, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/m, "").replace(/\nfunction requireEnv[\s\S]*?\n}\n/m, "").replace(
|
|
446
449
|
/if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*return makeFirestoreDB\(cfg\);/m,
|
|
447
450
|
"return makeFirestoreDB(cfg);"
|
|
448
451
|
);
|
|
@@ -575,11 +578,69 @@ async function scaffold(opts, templatesDir) {
|
|
|
575
578
|
await (0, import_promises.rm)((0, import_node_path.join)(opts.targetDir, ".yarn"), { recursive: true, force: true });
|
|
576
579
|
await (0, import_promises.rm)((0, import_node_path.join)(opts.targetDir, ".yarnrc.yml"), { force: true });
|
|
577
580
|
}
|
|
581
|
+
if (opts.packageManager === "pnpm") {
|
|
582
|
+
await writePnpmWorkspace(opts.targetDir);
|
|
583
|
+
await rewritePnpmWorkspaceDeps(opts.targetDir);
|
|
584
|
+
}
|
|
578
585
|
await patchGitignoreForPm(opts.targetDir, opts.packageManager);
|
|
579
586
|
await writeAiFiles(opts.targetDir, opts);
|
|
580
587
|
if (opts.install) runInstall(opts.targetDir, opts.packageManager);
|
|
581
588
|
if (opts.initGit) gitInit(opts.targetDir, opts.projectName);
|
|
582
589
|
}
|
|
590
|
+
async function writePnpmWorkspace(targetDir) {
|
|
591
|
+
const pkgPath = (0, import_node_path.join)(targetDir, "package.json");
|
|
592
|
+
const pkg = JSON.parse(await (0, import_promises.readFile)(pkgPath, "utf8"));
|
|
593
|
+
const workspaces = pkg.workspaces ?? [];
|
|
594
|
+
const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
|
|
595
|
+
const allowBuilds = [
|
|
596
|
+
"@firebase/util",
|
|
597
|
+
"@nestjs/core",
|
|
598
|
+
"@parcel/watcher",
|
|
599
|
+
"@scarf/scarf",
|
|
600
|
+
"@swc/core",
|
|
601
|
+
"less",
|
|
602
|
+
"msgpackr-extract",
|
|
603
|
+
"nx",
|
|
604
|
+
"protobufjs",
|
|
605
|
+
"unrs-resolver"
|
|
606
|
+
].map((p2) => ` '${p2}': true`).join("\n");
|
|
607
|
+
const content = `packages:
|
|
608
|
+
${packagesBlock}
|
|
609
|
+
|
|
610
|
+
allowBuilds:
|
|
611
|
+
${allowBuilds}
|
|
612
|
+
`;
|
|
613
|
+
await (0, import_promises.writeFile)((0, import_node_path.join)(targetDir, "pnpm-workspace.yaml"), content);
|
|
614
|
+
}
|
|
615
|
+
async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
616
|
+
const { readdir: rd } = await import("fs/promises");
|
|
617
|
+
async function walk(dir) {
|
|
618
|
+
const found = [];
|
|
619
|
+
let entries;
|
|
620
|
+
try {
|
|
621
|
+
entries = await rd(dir, { withFileTypes: true });
|
|
622
|
+
} catch {
|
|
623
|
+
return found;
|
|
624
|
+
}
|
|
625
|
+
for (const e of entries) {
|
|
626
|
+
if (e.isDirectory() && e.name !== "node_modules") {
|
|
627
|
+
found.push(...await walk((0, import_node_path.join)(dir, e.name)));
|
|
628
|
+
} else if (e.isFile() && e.name === "package.json") {
|
|
629
|
+
found.push((0, import_node_path.join)(dir, e.name));
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return found;
|
|
633
|
+
}
|
|
634
|
+
const pkgFiles = await walk(targetDir);
|
|
635
|
+
for (const f of pkgFiles) {
|
|
636
|
+
try {
|
|
637
|
+
const raw = await (0, import_promises.readFile)(f, "utf8");
|
|
638
|
+
const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
|
|
639
|
+
if (next !== raw) await (0, import_promises.writeFile)(f, next);
|
|
640
|
+
} catch {
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
583
644
|
async function patchGitignoreForPm(targetDir, pm) {
|
|
584
645
|
const giPath = (0, import_node_path.join)(targetDir, ".gitignore");
|
|
585
646
|
try {
|
|
@@ -683,7 +744,7 @@ Apache-2.0
|
|
|
683
744
|
|
|
684
745
|
- **Branch strategy**: \`dev\` is default. Cut \`feature/<name>\` or \`bug/<name>\` from dev. PRs only target dev. Never push directly to main.
|
|
685
746
|
- **No code without approval**: Propose changes first, wait for go-ahead.
|
|
686
|
-
-
|
|
747
|
+
- **RULE \u2014 no crash on missing .env**: MS factories must catch config errors, print a boxed banner with ALL missing vars, and return a Fake strategy in dev. In prod (\`NODE_ENV=production\`) throw the same banner. The \`formatEnvBanner\` + \`missingEnv\` helpers from \`@icore/shared\` handle this.
|
|
687
748
|
- **Post-coding routine**: \`npx prettier --write <files>\` \u2192 \`${nx} lint <project>\` \u2192 \`${nx} build <project>\` \u2014 all green before committing.
|
|
688
749
|
- **Nx generators only**: never hand-write \`project.json\` / tsconfig stacks. Use \`${nx} g @nx/<plugin>:<schematic>\`.
|
|
689
750
|
|
|
@@ -980,7 +1041,10 @@ Re-run with @latest to refresh:
|
|
|
980
1041
|
options: [
|
|
981
1042
|
{ value: "tcp", label: "TCP (default, no broker required)" },
|
|
982
1043
|
{ value: "redis", label: "Redis" },
|
|
983
|
-
{ value: "nats", label: "NATS" }
|
|
1044
|
+
{ value: "nats", label: "NATS" },
|
|
1045
|
+
{ value: "mqtt", label: "MQTT" },
|
|
1046
|
+
{ value: "rmq", label: "RabbitMQ" },
|
|
1047
|
+
{ value: "kafka", label: "Kafka" }
|
|
984
1048
|
],
|
|
985
1049
|
initialValue: "tcp"
|
|
986
1050
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -5,7 +5,7 @@ type PaymentProvider = 'paypal' | 'none';
|
|
|
5
5
|
type JobsProvider = 'bullmq' | 'none';
|
|
6
6
|
type ExampleMode = 'notes' | 'none';
|
|
7
7
|
type UiLibrary = 'shadcn' | 'antd' | 'mui';
|
|
8
|
-
type MsTransport = 'tcp' | 'redis' | 'nats';
|
|
8
|
+
type MsTransport = 'tcp' | 'redis' | 'nats' | 'mqtt' | 'rmq' | 'kafka';
|
|
9
9
|
type PackageManager = 'yarn' | 'npm' | 'pnpm';
|
|
10
10
|
/**
|
|
11
11
|
* Returns the correct invocation for a package.json script.
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ type PaymentProvider = 'paypal' | 'none';
|
|
|
5
5
|
type JobsProvider = 'bullmq' | 'none';
|
|
6
6
|
type ExampleMode = 'notes' | 'none';
|
|
7
7
|
type UiLibrary = 'shadcn' | 'antd' | 'mui';
|
|
8
|
-
type MsTransport = 'tcp' | 'redis' | 'nats';
|
|
8
|
+
type MsTransport = 'tcp' | 'redis' | 'nats' | 'mqtt' | 'rmq' | 'kafka';
|
|
9
9
|
type PackageManager = 'yarn' | 'npm' | 'pnpm';
|
|
10
10
|
/**
|
|
11
11
|
* Returns the correct invocation for a package.json script.
|