@idevconn/create-icore 0.7.2 → 0.9.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/README.md +44 -9
- package/dist/cli.js +689 -363
- package/dist/index.cjs +806 -358
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +798 -351
- package/dist/manifest/audit.js +122 -0
- package/package.json +1 -1
- package/templates/apps/api/src/app/app.module.ts +2 -6
- package/templates/apps/api/src/app/features.module.ts +9 -0
- package/templates/apps/api/src/app/gateway-services.ts +7 -0
- package/templates/apps/api/src/main.ts +1 -5
- package/templates/apps/microservices/auth/src/app/app.module.ts +4 -93
- package/templates/apps/microservices/auth/src/app/auth.provider.ts +9 -0
- package/templates/apps/microservices/notes/src/app/app.module.ts +4 -86
- package/templates/apps/microservices/notes/src/app/db.provider.ts +9 -0
- package/templates/apps/microservices/upload/src/app/app.module.ts +4 -140
- package/templates/apps/microservices/upload/src/app/storage.provider.ts +9 -0
- package/templates/apps/templates/client-antd/src/components/layout/LayoutSider.tsx +15 -23
- package/templates/apps/templates/client-antd/src/nav.config.ts +17 -0
- package/templates/apps/templates/client-mui/src/components/layout/LayoutSider.tsx +19 -20
- package/templates/apps/templates/client-mui/src/nav.config.ts +17 -0
- package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +20 -16
- package/templates/apps/templates/client-shadcn/src/nav.config.ts +17 -0
- package/templates/libs/auth-strategies/firebase/eslint.config.mjs +1 -0
- package/templates/libs/auth-strategies/firebase/package.json +4 -0
- package/templates/libs/auth-strategies/firebase/src/index.ts +1 -0
- package/templates/libs/auth-strategies/firebase/src/lib/__tests__/firebase-auth.module.unit.test.ts +49 -0
- package/templates/libs/auth-strategies/firebase/src/lib/firebase-auth.module.ts +41 -0
- package/templates/libs/auth-strategies/firebase/tsconfig.json +2 -0
- package/templates/libs/auth-strategies/mongodb/package.json +8 -1
- package/templates/libs/auth-strategies/mongodb/src/index.ts +1 -0
- package/templates/libs/auth-strategies/mongodb/src/lib/__tests__/mongodb-auth.module.unit.test.ts +16 -0
- package/templates/libs/auth-strategies/mongodb/src/lib/mongodb-auth.module.ts +45 -0
- package/templates/libs/auth-strategies/mongodb/tsconfig.json +2 -0
- package/templates/libs/auth-strategies/supabase/eslint.config.mjs +1 -0
- package/templates/libs/auth-strategies/supabase/package.json +3 -0
- package/templates/libs/auth-strategies/supabase/src/index.ts +1 -0
- package/templates/libs/auth-strategies/supabase/src/lib/__tests__/supabase-auth.module.unit.test.ts +43 -0
- package/templates/libs/auth-strategies/supabase/src/lib/supabase-auth.module.ts +41 -0
- package/templates/libs/auth-strategies/supabase/tsconfig.json +2 -0
- package/templates/libs/db-strategies/firestore/eslint.config.mjs +1 -1
- package/templates/libs/db-strategies/firestore/package.json +4 -0
- package/templates/libs/db-strategies/firestore/src/index.ts +1 -0
- package/templates/libs/db-strategies/firestore/src/lib/__tests__/firestore-db.module.unit.test.ts +37 -0
- package/templates/libs/db-strategies/firestore/src/lib/firestore-db.module.ts +41 -0
- package/templates/libs/db-strategies/firestore/tsconfig.json +2 -0
- package/templates/libs/db-strategies/mongodb/package.json +4 -1
- package/templates/libs/db-strategies/mongodb/src/index.ts +1 -0
- package/templates/libs/db-strategies/mongodb/src/lib/__tests__/mongodb-db.module.unit.test.ts +14 -0
- package/templates/libs/db-strategies/mongodb/src/lib/mongodb-db.module.ts +41 -0
- package/templates/libs/db-strategies/mongodb/tsconfig.json +2 -0
- package/templates/libs/db-strategies/supabase/eslint.config.mjs +6 -1
- package/templates/libs/db-strategies/supabase/package.json +3 -0
- package/templates/libs/db-strategies/supabase/src/index.ts +1 -0
- package/templates/libs/db-strategies/supabase/src/lib/__tests__/supabase-db.module.unit.test.ts +32 -0
- package/templates/libs/db-strategies/supabase/src/lib/supabase-db.module.ts +41 -0
- package/templates/libs/db-strategies/supabase/tsconfig.json +2 -0
- package/templates/libs/shared/src/strategies/__tests__/provide-strategy.unit.test.ts +73 -0
- package/templates/libs/shared/src/strategies/index.ts +1 -0
- package/templates/libs/shared/src/strategies/provide-strategy.ts +44 -0
- package/templates/libs/storage-strategies/cloudinary/eslint.config.mjs +1 -1
- package/templates/libs/storage-strategies/cloudinary/package.json +4 -0
- package/templates/libs/storage-strategies/cloudinary/src/index.ts +1 -0
- package/templates/libs/storage-strategies/cloudinary/src/lib/__tests__/cloudinary-storage.module.unit.test.ts +40 -0
- package/templates/libs/storage-strategies/cloudinary/src/lib/cloudinary-storage.module.ts +85 -0
- package/templates/libs/storage-strategies/cloudinary/tsconfig.json +2 -0
- package/templates/libs/storage-strategies/firebase/eslint.config.mjs +1 -1
- package/templates/libs/storage-strategies/firebase/package.json +4 -0
- package/templates/libs/storage-strategies/firebase/src/index.ts +1 -0
- package/templates/libs/storage-strategies/firebase/src/lib/__tests__/firebase-storage.module.unit.test.ts +42 -0
- package/templates/libs/storage-strategies/firebase/src/lib/firebase-storage.module.ts +46 -0
- package/templates/libs/storage-strategies/firebase/tsconfig.json +2 -0
- package/templates/libs/storage-strategies/mongodb/package.json +4 -1
- package/templates/libs/storage-strategies/mongodb/src/index.ts +1 -0
- package/templates/libs/storage-strategies/mongodb/src/lib/__tests__/mongodb-storage.module.unit.test.ts +14 -0
- package/templates/libs/storage-strategies/mongodb/src/lib/mongodb-storage.module.ts +41 -0
- package/templates/libs/storage-strategies/mongodb/tsconfig.json +2 -0
- package/templates/libs/storage-strategies/supabase/eslint.config.mjs +1 -0
- package/templates/libs/storage-strategies/supabase/package.json +3 -0
- package/templates/libs/storage-strategies/supabase/src/index.ts +1 -0
- package/templates/libs/storage-strategies/supabase/src/lib/__tests__/supabase-storage.module.unit.test.ts +46 -0
- package/templates/libs/storage-strategies/supabase/src/lib/supabase-storage.module.ts +46 -0
- package/templates/libs/storage-strategies/supabase/tsconfig.json +2 -0
- package/templates/package.json +1 -1
- package/templates/tools/create-icore/_template-shell/package.json +1 -1
- package/templates/tsconfig.base.json +1 -1
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 mkdir2, readdir as readdir2, readFile as
|
|
7
|
+
import { copyFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile6, stat, writeFile as writeFile8, rm as rm4 } from "fs/promises";
|
|
8
8
|
import { readFileSync } from "fs";
|
|
9
|
-
import { join as
|
|
9
|
+
import { join as join8 } from "path";
|
|
10
10
|
import { spawnSync } from "child_process";
|
|
11
11
|
|
|
12
12
|
// src/lib/scaffold-env.ts
|
|
@@ -47,6 +47,31 @@ async function stripGatewayTransport(targetDir, prefix) {
|
|
|
47
47
|
} catch {
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
var ROOT_PROVIDER_SDKS = {
|
|
51
|
+
supabase: ["@supabase/supabase-js"],
|
|
52
|
+
cloudinary: ["cloudinary"],
|
|
53
|
+
mongodb: ["mongoose"],
|
|
54
|
+
firebase: ["firebase-admin"]
|
|
55
|
+
};
|
|
56
|
+
async function pruneRootProviderDeps(targetDir, opts) {
|
|
57
|
+
const chosen = /* @__PURE__ */ new Set([opts.authProvider, opts.dbProvider, opts.upload]);
|
|
58
|
+
const drop = /* @__PURE__ */ new Set();
|
|
59
|
+
for (const [provider, sdks] of Object.entries(ROOT_PROVIDER_SDKS)) {
|
|
60
|
+
if (!chosen.has(provider)) for (const sdk of sdks) drop.add(sdk);
|
|
61
|
+
}
|
|
62
|
+
if (drop.size === 0) return;
|
|
63
|
+
const pkgPath = join(targetDir, "package.json");
|
|
64
|
+
try {
|
|
65
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
|
|
66
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
67
|
+
const deps = pkg[field];
|
|
68
|
+
if (!deps) continue;
|
|
69
|
+
for (const sdk of drop) delete deps[sdk];
|
|
70
|
+
}
|
|
71
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
50
75
|
async function rewriteRootPackageJson(targetDir, opts) {
|
|
51
76
|
const pkgPath = join(targetDir, "package.json");
|
|
52
77
|
const raw = await readFile(pkgPath, "utf8");
|
|
@@ -64,6 +89,11 @@ async function rewriteRootPackageJson(targetDir, opts) {
|
|
|
64
89
|
const deps = pkg["dependencies"] ??= {};
|
|
65
90
|
Object.assign(deps, MONGODB_DEPS);
|
|
66
91
|
}
|
|
92
|
+
if (opts.authProvider === "mongodb") {
|
|
93
|
+
const devDeps = pkg["devDependencies"] ??= {};
|
|
94
|
+
devDeps["@types/bcrypt"] = "^6.0.0";
|
|
95
|
+
devDeps["@types/jsonwebtoken"] = "^9.0.10";
|
|
96
|
+
}
|
|
67
97
|
if (opts.packageManager !== "yarn") {
|
|
68
98
|
delete pkg.packageManager;
|
|
69
99
|
} else {
|
|
@@ -186,326 +216,75 @@ async function stripTsconfigPath(targetDir, alias) {
|
|
|
186
216
|
} catch {
|
|
187
217
|
}
|
|
188
218
|
}
|
|
189
|
-
async function
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
"
|
|
194
|
-
"Dockerfile.ms-jobs"
|
|
195
|
-
];
|
|
196
|
-
for (const p2 of paths) {
|
|
197
|
-
await rm(join2(targetDir, p2), { recursive: true, force: true });
|
|
198
|
-
}
|
|
199
|
-
const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
|
|
200
|
-
try {
|
|
201
|
-
const appModule = await readFile2(appModulePath, "utf8");
|
|
202
|
-
const next = appModule.replace(/^import \{ AdminModule \} from '\.\/admin\/admin\.module';\n/m, "").replace(/,\s*AdminModule/g, "");
|
|
203
|
-
await writeFile2(appModulePath, next);
|
|
204
|
-
} catch {
|
|
205
|
-
}
|
|
206
|
-
await stripDeps(join2(targetDir, "apps/api/package.json"), [
|
|
207
|
-
"@icore/jobs-client",
|
|
208
|
-
"@bull-board/api",
|
|
209
|
-
"@bull-board/express"
|
|
219
|
+
async function removeFirebaseAdminLib(targetDir) {
|
|
220
|
+
await rm(join2(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
|
|
221
|
+
await stripTsconfigPath(targetDir, "@icore/firebase-admin");
|
|
222
|
+
await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
|
|
223
|
+
"@icore/firebase-admin"
|
|
210
224
|
]);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
} catch {
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
async function removePaymentStack(targetDir) {
|
|
220
|
-
const paths = [
|
|
221
|
-
"apps/microservices/payment",
|
|
222
|
-
"apps/microservices/payment-e2e",
|
|
223
|
-
"libs/payment-client",
|
|
224
|
-
"apps/api/src/app/payment"
|
|
225
|
-
];
|
|
226
|
-
for (const p2 of paths) {
|
|
227
|
-
await rm(join2(targetDir, p2), { recursive: true, force: true });
|
|
228
|
-
}
|
|
229
|
-
const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
|
|
230
|
-
try {
|
|
231
|
-
const appModule = await readFile2(appModulePath, "utf8");
|
|
232
|
-
const next = appModule.replace(/^import \{ PaymentModule \} from '\.\/payment\/payment\.module';\n/m, "").replace(/,\s*PaymentModule/g, "");
|
|
233
|
-
await writeFile2(appModulePath, next);
|
|
234
|
-
} catch {
|
|
235
|
-
}
|
|
236
|
-
await stripDeps(join2(targetDir, "apps/api/package.json"), [
|
|
237
|
-
"@icore/payment-client",
|
|
238
|
-
"@idevconn/payment"
|
|
225
|
+
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
226
|
+
"@icore/firebase-admin"
|
|
227
|
+
]);
|
|
228
|
+
await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
|
|
229
|
+
"@icore/firebase-admin"
|
|
239
230
|
]);
|
|
240
|
-
await stripGatewayTransport(targetDir, "PAYMENT");
|
|
241
|
-
const mainTsPath = join2(targetDir, "apps/api/src/main.ts");
|
|
242
|
-
try {
|
|
243
|
-
const src = await readFile2(mainTsPath, "utf8");
|
|
244
|
-
const next = src.replace(/\n\s*\{ name: 'payment', prefix: 'PAYMENT' \},/, "");
|
|
245
|
-
await writeFile2(mainTsPath, next);
|
|
246
|
-
} catch {
|
|
247
|
-
}
|
|
248
231
|
}
|
|
249
|
-
async function
|
|
250
|
-
|
|
251
|
-
"apps/microservices/
|
|
252
|
-
"
|
|
253
|
-
"libs/
|
|
254
|
-
"
|
|
255
|
-
"apps/
|
|
256
|
-
|
|
232
|
+
async function removeAuthStack(targetDir) {
|
|
233
|
+
const rmPaths = [
|
|
234
|
+
"apps/microservices/auth",
|
|
235
|
+
"libs/auth-strategies",
|
|
236
|
+
"libs/auth-client",
|
|
237
|
+
"Dockerfile.ms-auth",
|
|
238
|
+
"apps/api/src/app/auth",
|
|
239
|
+
"apps/api/src/app/profile",
|
|
240
|
+
"apps/api/src/app/abilities",
|
|
241
|
+
"apps/client/src/components/auth",
|
|
242
|
+
"apps/client/src/routes/login.tsx",
|
|
243
|
+
"apps/client/src/routes/auth.callback.tsx",
|
|
244
|
+
"apps/client/src/routes/auth.oauth.callback.tsx",
|
|
245
|
+
"apps/client/src/routes/_dashboard/profile.tsx"
|
|
246
|
+
];
|
|
247
|
+
for (const p2 of rmPaths) {
|
|
257
248
|
await rm(join2(targetDir, p2), { recursive: true, force: true });
|
|
258
249
|
}
|
|
259
|
-
await rm(join2(targetDir, "apps/client/src/routes/_dashboard/notes.tsx"), { force: true });
|
|
260
|
-
await rm(join2(targetDir, "apps/client/src/queries/notes.ts"), { force: true });
|
|
261
250
|
const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
|
|
262
251
|
try {
|
|
263
252
|
const src = await readFile2(appModulePath, "utf8");
|
|
264
|
-
const next = src.replace(/^import \{
|
|
253
|
+
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, "");
|
|
265
254
|
await writeFile2(appModulePath, next);
|
|
266
255
|
} catch {
|
|
267
256
|
}
|
|
268
|
-
|
|
269
|
-
"@icore/notes-client",
|
|
270
|
-
"@casl/ability"
|
|
271
|
-
]);
|
|
272
|
-
await stripGatewayTransport(targetDir, "NOTES");
|
|
273
|
-
const mainTsPath = join2(targetDir, "apps/api/src/main.ts");
|
|
257
|
+
const dashboardPath = join2(targetDir, "apps/client/src/routes/_dashboard.tsx");
|
|
274
258
|
try {
|
|
275
|
-
const src = await readFile2(
|
|
276
|
-
const next = src.replace(
|
|
277
|
-
await writeFile2(
|
|
259
|
+
const src = await readFile2(dashboardPath, "utf8");
|
|
260
|
+
const next = src.replace(/^import \{ useAuthStore \} from '@icore\/template-shared';\n/m, "").replace(/, redirect/g, "").replace(/\n {2}beforeLoad: \(\) => \{[\s\S]*?\n {2}\},/m, "");
|
|
261
|
+
await writeFile2(dashboardPath, next);
|
|
278
262
|
} catch {
|
|
279
263
|
}
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const siderPath = join2(targetDir, "apps/client/src/components/layout/LayoutSider.tsx");
|
|
288
|
-
try {
|
|
289
|
-
const src = await readFile2(siderPath, "utf8");
|
|
290
|
-
const next = src.replace(", StickyNote", "").replace(/\n {8}<Link\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/Link>/, "").replace(", FileTextOutlined", "").replace(
|
|
291
|
-
"const selectedKey = pathname.includes('/notes')\n ? 'notes'\n : pathname.includes('/profile')",
|
|
292
|
-
"const selectedKey = pathname.includes('/profile')"
|
|
293
|
-
).replace(
|
|
294
|
-
/\n {4}\{\n {6}key: 'notes',\n {6}icon: <FileTextOutlined \/>,\n {6}label: <Link to="\/(?:_dashboard\/)?notes">\{t\('notes\.title'\)\}<\/Link>,\n {4}\},/,
|
|
295
|
-
""
|
|
296
|
-
).replace("import NoteOutlinedIcon from '@mui/icons-material/NoteOutlined';\n", "").replace(
|
|
297
|
-
/\n {8}<ListItemButton\n {10}component=\{Link\}\n {10}to="\/(?:_dashboard\/)?notes"[\s\S]*?<\/ListItemButton>/,
|
|
298
|
-
""
|
|
299
|
-
).replace(/\n\s*<Link to="\/(?:_dashboard\/)?notes">[\s\S]*?<\/Link>/m, "");
|
|
300
|
-
await writeFile2(siderPath, next);
|
|
301
|
-
} catch {
|
|
264
|
+
for (const alias of [
|
|
265
|
+
"@icore/auth-client",
|
|
266
|
+
"@icore/auth-supabase",
|
|
267
|
+
"@icore/auth-firebase",
|
|
268
|
+
"@icore/auth-mongodb"
|
|
269
|
+
]) {
|
|
270
|
+
await stripTsconfigPath(targetDir, alias);
|
|
302
271
|
}
|
|
303
|
-
|
|
272
|
+
await stripDeps(join2(targetDir, "apps/api/package.json"), ["@icore/auth-client"]);
|
|
273
|
+
const gatewayEnv = join2(targetDir, "apps/api/.env");
|
|
304
274
|
try {
|
|
305
|
-
const
|
|
306
|
-
const next =
|
|
307
|
-
await writeFile2(
|
|
275
|
+
const env = await readFile2(gatewayEnv, "utf8");
|
|
276
|
+
const next = env.split("\n").filter((line) => !line.startsWith("AUTH_") && !line.startsWith("# AUTH_")).join("\n");
|
|
277
|
+
await writeFile2(gatewayEnv, next);
|
|
308
278
|
} catch {
|
|
309
279
|
}
|
|
310
|
-
|
|
311
|
-
async function removeUnusedAuthStrategies(targetDir, authProvider) {
|
|
312
|
-
const modulePath = join2(targetDir, "apps/microservices/auth/src/app/app.module.ts");
|
|
313
|
-
const AUTH_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseAuth\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDbAuth\(connection, cfg\);\n\s*return makeFirebaseAuth\(cfg\);/m;
|
|
314
|
-
if (authProvider === "supabase") {
|
|
315
|
-
await rm(join2(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
|
|
316
|
-
await rm(join2(targetDir, "libs/auth-strategies/mongodb"), { recursive: true, force: true });
|
|
317
|
-
await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
|
|
318
|
-
"@icore/auth-firebase",
|
|
319
|
-
"@icore/firebase-admin",
|
|
320
|
-
"@icore/auth-mongodb"
|
|
321
|
-
]);
|
|
322
|
-
await stripTsconfigPath(targetDir, "@icore/auth-firebase");
|
|
323
|
-
await stripTsconfigPath(targetDir, "@icore/auth-mongodb");
|
|
324
|
-
try {
|
|
325
|
-
const src = await readFile2(modulePath, "utf8");
|
|
326
|
-
const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/gm, "").replace(/^import \{[^}]*MongoDbAuthStrategy[^}]*\} from '@icore\/auth-mongodb';\n/gm, "").replace(
|
|
327
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
328
|
-
""
|
|
329
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirebaseAuth[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDbAuth[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(AUTH_BRANCH, "return makeSupabaseAuth(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
330
|
-
await writeFile2(modulePath, next);
|
|
331
|
-
} catch {
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
if (authProvider === "firebase") {
|
|
335
|
-
await rm(join2(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
|
|
336
|
-
await rm(join2(targetDir, "libs/auth-strategies/mongodb"), { recursive: true, force: true });
|
|
337
|
-
await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
|
|
338
|
-
"@icore/auth-supabase",
|
|
339
|
-
"@icore/auth-mongodb"
|
|
340
|
-
]);
|
|
341
|
-
await stripTsconfigPath(targetDir, "@icore/auth-supabase");
|
|
342
|
-
await stripTsconfigPath(targetDir, "@icore/auth-mongodb");
|
|
343
|
-
try {
|
|
344
|
-
const src = await readFile2(modulePath, "utf8");
|
|
345
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/gm, "").replace(/^import \{[^}]*MongoDbAuthStrategy[^}]*\} from '@icore\/auth-mongodb';\n/gm, "").replace(
|
|
346
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
347
|
-
""
|
|
348
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseAuth[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDbAuth[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(AUTH_BRANCH, "return makeFirebaseAuth(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
349
|
-
await writeFile2(modulePath, next);
|
|
350
|
-
} catch {
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
if (authProvider === "mongodb") {
|
|
354
|
-
await rm(join2(targetDir, "libs/auth-strategies/supabase"), { recursive: true, force: true });
|
|
355
|
-
await rm(join2(targetDir, "libs/auth-strategies/firebase"), { recursive: true, force: true });
|
|
356
|
-
await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
|
|
357
|
-
"@icore/auth-supabase",
|
|
358
|
-
"@icore/auth-firebase",
|
|
359
|
-
"@icore/firebase-admin"
|
|
360
|
-
]);
|
|
361
|
-
await stripTsconfigPath(targetDir, "@icore/auth-supabase");
|
|
362
|
-
await stripTsconfigPath(targetDir, "@icore/auth-firebase");
|
|
363
|
-
try {
|
|
364
|
-
const src = await readFile2(modulePath, "utf8");
|
|
365
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseAuthStrategy[^}]*\} from '@icore\/auth-supabase';\n/gm, "").replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirebaseAuthStrategy[^}]*\} from '@icore\/auth-firebase';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseAuth[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeFirebaseAuth[\s\S]*?\n}\n/gm, "").replace(AUTH_BRANCH, "return makeMongoDbAuth(connection, cfg);");
|
|
366
|
-
await writeFile2(modulePath, next);
|
|
367
|
-
} catch {
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
async function removeUnusedStorageStrategies(targetDir, uploadProvider) {
|
|
372
|
-
if (uploadProvider === "none") return;
|
|
373
|
-
const modulePath = join2(targetDir, "apps/microservices/upload/src/app/app.module.ts");
|
|
374
|
-
const STORAGE_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseStorage\(cfg\);\n\s*if \(provider === 'firebase'\) return makeFirebaseStorage\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDbStorage\(connection\);\n\s*return makeCloudinaryStorage\(cfg\);/m;
|
|
375
|
-
if (uploadProvider !== "firebase") {
|
|
376
|
-
await rm(join2(targetDir, "libs/storage-strategies/firebase"), { recursive: true, force: true });
|
|
377
|
-
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
378
|
-
"@icore/storage-firebase",
|
|
379
|
-
"@icore/firebase-admin"
|
|
380
|
-
]);
|
|
381
|
-
await stripTsconfigPath(targetDir, "@icore/storage-firebase");
|
|
382
|
-
}
|
|
383
|
-
if (uploadProvider !== "cloudinary") {
|
|
384
|
-
await rm(join2(targetDir, "libs/storage-strategies/cloudinary"), {
|
|
385
|
-
recursive: true,
|
|
386
|
-
force: true
|
|
387
|
-
});
|
|
388
|
-
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
389
|
-
"@icore/storage-cloudinary"
|
|
390
|
-
]);
|
|
391
|
-
await stripTsconfigPath(targetDir, "@icore/storage-cloudinary");
|
|
392
|
-
}
|
|
393
|
-
if (uploadProvider !== "supabase") {
|
|
394
|
-
await rm(join2(targetDir, "libs/storage-strategies/supabase"), { recursive: true, force: true });
|
|
395
|
-
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
396
|
-
"@icore/storage-supabase"
|
|
397
|
-
]);
|
|
398
|
-
await stripTsconfigPath(targetDir, "@icore/storage-supabase");
|
|
399
|
-
}
|
|
400
|
-
if (uploadProvider !== "mongodb") {
|
|
401
|
-
await rm(join2(targetDir, "libs/storage-strategies/mongodb"), { recursive: true, force: true });
|
|
402
|
-
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
403
|
-
"@icore/storage-mongodb"
|
|
404
|
-
]);
|
|
405
|
-
await stripTsconfigPath(targetDir, "@icore/storage-mongodb");
|
|
406
|
-
}
|
|
280
|
+
const composePath = join2(targetDir, "docker-compose.yml");
|
|
407
281
|
try {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
/^import \{[^}]*FirebaseStorageStrategy[^}]*\} from '@icore\/storage-firebase';\n/gm,
|
|
412
|
-
""
|
|
413
|
-
).replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirebaseStorage[\s\S]*?\n}\n/gm, "");
|
|
414
|
-
}
|
|
415
|
-
if (uploadProvider !== "cloudinary") {
|
|
416
|
-
src = src.replace(/^import \{ v2 as cloudinary \} from 'cloudinary';\n/gm, "").replace(
|
|
417
|
-
/^import \{[^}]*CloudinaryStorageStrategy[^}]*\} from '@icore\/storage-cloudinary';\n/gm,
|
|
418
|
-
""
|
|
419
|
-
).replace(/\nfunction makeCloudinaryStorage[\s\S]*?\n}\n/gm, "");
|
|
420
|
-
}
|
|
421
|
-
if (uploadProvider !== "supabase") {
|
|
422
|
-
src = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(
|
|
423
|
-
/^import \{[^}]*SupabaseStorageStrategy[^}]*\} from '@icore\/storage-supabase';\n/gm,
|
|
424
|
-
""
|
|
425
|
-
).replace(/\nfunction makeSupabaseStorage[\s\S]*?\n}\n/gm, "");
|
|
426
|
-
}
|
|
427
|
-
if (uploadProvider !== "mongodb") {
|
|
428
|
-
src = src.replace(
|
|
429
|
-
/^import \{[^}]*MongoDbStorageStrategy[^}]*\} from '@icore\/storage-mongodb';\n/gm,
|
|
430
|
-
""
|
|
431
|
-
).replace(
|
|
432
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
433
|
-
""
|
|
434
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeMongoDbStorage[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "");
|
|
435
|
-
}
|
|
436
|
-
const chosenReturn = uploadProvider === "mongodb" ? `return makeMongoDbStorage(connection);` : `return make${uploadProvider.charAt(0).toUpperCase() + uploadProvider.slice(1)}Storage(cfg);`;
|
|
437
|
-
src = src.replace(STORAGE_BRANCH, chosenReturn);
|
|
438
|
-
if (uploadProvider !== "mongodb") {
|
|
439
|
-
src = src.replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
440
|
-
}
|
|
441
|
-
await writeFile2(modulePath, src);
|
|
282
|
+
const compose = await readFile2(composePath, "utf8");
|
|
283
|
+
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, "");
|
|
284
|
+
await writeFile2(composePath, next);
|
|
442
285
|
} catch {
|
|
443
286
|
}
|
|
444
287
|
}
|
|
445
|
-
async function removeUnusedDbStrategies(targetDir, dbProvider) {
|
|
446
|
-
const modulePath = join2(targetDir, "apps/microservices/notes/src/app/app.module.ts");
|
|
447
|
-
const DB_BRANCH = /if \(provider === 'supabase'\) return makeSupabaseDB\(cfg\);\n\s*if \(provider === 'mongodb'\) return makeMongoDb\(connection\);\n\s*return makeFirestoreDB\(cfg\);/m;
|
|
448
|
-
if (dbProvider === "supabase") {
|
|
449
|
-
await rm(join2(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
|
|
450
|
-
await rm(join2(targetDir, "libs/db-strategies/mongodb"), { recursive: true, force: true });
|
|
451
|
-
await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
|
|
452
|
-
"@icore/db-firestore",
|
|
453
|
-
"@icore/firebase-admin",
|
|
454
|
-
"@icore/db-mongodb"
|
|
455
|
-
]);
|
|
456
|
-
await stripTsconfigPath(targetDir, "@icore/db-firestore");
|
|
457
|
-
await stripTsconfigPath(targetDir, "@icore/db-mongodb");
|
|
458
|
-
try {
|
|
459
|
-
const src = await readFile2(modulePath, "utf8");
|
|
460
|
-
const next = src.replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/gm, "").replace(/^import \{[^}]*MongoDbDBStrategy[^}]*\} from '@icore\/db-mongodb';\n/gm, "").replace(
|
|
461
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
462
|
-
""
|
|
463
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}firestore: \[[^\]]*\],\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeFirestoreDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDb[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(DB_BRANCH, "return makeSupabaseDB(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
464
|
-
await writeFile2(modulePath, next);
|
|
465
|
-
} catch {
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
if (dbProvider === "firebase") {
|
|
469
|
-
await rm(join2(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
|
|
470
|
-
await rm(join2(targetDir, "libs/db-strategies/mongodb"), { recursive: true, force: true });
|
|
471
|
-
await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
|
|
472
|
-
"@icore/db-supabase",
|
|
473
|
-
"@icore/db-mongodb"
|
|
474
|
-
]);
|
|
475
|
-
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
476
|
-
await stripTsconfigPath(targetDir, "@icore/db-mongodb");
|
|
477
|
-
try {
|
|
478
|
-
const src = await readFile2(modulePath, "utf8");
|
|
479
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/gm, "").replace(/^import \{[^}]*MongoDbDBStrategy[^}]*\} from '@icore\/db-mongodb';\n/gm, "").replace(
|
|
480
|
-
/^import \{ MongooseModule, getConnectionToken \} from '@nestjs\/mongoose';\n/gm,
|
|
481
|
-
""
|
|
482
|
-
).replace(/^import \{ Connection \} from 'mongoose';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}mongodb: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeMongoDb[\s\S]*?\n}\n/gm, "").replace(/^ {4}MongooseModule\.forRootAsync[\s\S]*?\n {4}\}\),\n/gm, "").replace(/\nfunction requireEnv[\s\S]*?\n}\n/gm, "").replace(DB_BRANCH, "return makeFirestoreDB(cfg);").replace(/, connection: Connection/, "").replace(/, getConnectionToken\(\)/, "");
|
|
483
|
-
await writeFile2(modulePath, next);
|
|
484
|
-
} catch {
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
if (dbProvider === "mongodb") {
|
|
488
|
-
await rm(join2(targetDir, "libs/db-strategies/supabase"), { recursive: true, force: true });
|
|
489
|
-
await rm(join2(targetDir, "libs/db-strategies/firestore"), { recursive: true, force: true });
|
|
490
|
-
await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
|
|
491
|
-
"@icore/db-supabase",
|
|
492
|
-
"@icore/db-firestore",
|
|
493
|
-
"@icore/firebase-admin"
|
|
494
|
-
]);
|
|
495
|
-
await stripTsconfigPath(targetDir, "@icore/db-supabase");
|
|
496
|
-
await stripTsconfigPath(targetDir, "@icore/db-firestore");
|
|
497
|
-
try {
|
|
498
|
-
const src = await readFile2(modulePath, "utf8");
|
|
499
|
-
const next = src.replace(/^import \{ createClient \} from '@supabase\/supabase-js';\n/gm, "").replace(/^import \{[^}]*SupabaseDBStrategy[^}]*\} from '@icore\/db-supabase';\n/gm, "").replace(/^import \{[^}]*\} from '@icore\/firebase-admin';\n/gm, "").replace(/^import \{[^}]*FirestoreDBStrategy[^}]*\} from '@icore\/db-firestore';\n/gm, "").replace(/^ {2}supabase: \[[^\]]*\],\n/gm, "").replace(/^ {2}firestore: \[[^\]]*\],\n/gm, "").replace(/^ {2}firebase: \[[^\]]*\],\n/gm, "").replace(/\nfunction makeSupabaseDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction makeFirestoreDB[\s\S]*?\n}\n/gm, "").replace(/\nfunction requireEnv[\s\S]*?\n}\n/gm, "").replace(DB_BRANCH, "return makeMongoDb(connection);");
|
|
500
|
-
await writeFile2(modulePath, next);
|
|
501
|
-
} catch {
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
async function removeFirebaseAdminLib(targetDir) {
|
|
506
|
-
await rm(join2(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
|
|
507
|
-
await stripTsconfigPath(targetDir, "@icore/firebase-admin");
|
|
508
|
-
}
|
|
509
288
|
async function removeUploadStack(targetDir) {
|
|
510
289
|
const paths = [
|
|
511
290
|
"apps/microservices/upload",
|
|
@@ -539,12 +318,443 @@ async function removeUploadStack(targetDir) {
|
|
|
539
318
|
]);
|
|
540
319
|
}
|
|
541
320
|
|
|
542
|
-
// src/
|
|
543
|
-
import { readFile as
|
|
321
|
+
// src/manifest/wire-features.ts
|
|
322
|
+
import { readFile as readFile4, writeFile as writeFile4, rm as rm3 } from "fs/promises";
|
|
323
|
+
import { join as join4 } from "path";
|
|
324
|
+
|
|
325
|
+
// src/manifest/index.ts
|
|
326
|
+
var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
|
|
327
|
+
var MANIFEST = {
|
|
328
|
+
auth: {
|
|
329
|
+
supabase: {
|
|
330
|
+
libDirs: ["libs/auth-strategies/supabase"],
|
|
331
|
+
deps: { "@supabase/supabase-js": "^2.106.2" },
|
|
332
|
+
tsPaths: { "@icore/auth-supabase": ["libs/auth-strategies/supabase/src/index.ts"] },
|
|
333
|
+
nestModule: {
|
|
334
|
+
importFrom: "@icore/auth-supabase",
|
|
335
|
+
symbol: "SupabaseAuthModule",
|
|
336
|
+
into: "auth"
|
|
337
|
+
},
|
|
338
|
+
appTests: [
|
|
339
|
+
"apps/microservices/auth/src/app/__tests__/auth.controller.supabase.integration.unit.test.ts"
|
|
340
|
+
]
|
|
341
|
+
},
|
|
342
|
+
firebase: {
|
|
343
|
+
libDirs: ["libs/auth-strategies/firebase"],
|
|
344
|
+
deps: {},
|
|
345
|
+
tsPaths: { "@icore/auth-firebase": ["libs/auth-strategies/firebase/src/index.ts"] },
|
|
346
|
+
nestModule: {
|
|
347
|
+
importFrom: "@icore/auth-firebase",
|
|
348
|
+
symbol: "FirebaseAuthModule",
|
|
349
|
+
into: "auth"
|
|
350
|
+
},
|
|
351
|
+
appTests: [
|
|
352
|
+
"apps/microservices/auth/src/app/__tests__/auth.controller.firebase.integration.unit.test.ts"
|
|
353
|
+
]
|
|
354
|
+
},
|
|
355
|
+
mongodb: {
|
|
356
|
+
libDirs: ["libs/auth-strategies/mongodb"],
|
|
357
|
+
deps: { mongoose: "^9.6.3" },
|
|
358
|
+
tsPaths: { "@icore/auth-mongodb": ["libs/auth-strategies/mongodb/src/index.ts"] },
|
|
359
|
+
nestModule: { importFrom: "@icore/auth-mongodb", symbol: "MongoDbAuthModule", into: "auth" }
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
storage: {
|
|
363
|
+
supabase: {
|
|
364
|
+
libDirs: ["libs/storage-strategies/supabase"],
|
|
365
|
+
deps: { "@supabase/supabase-js": "^2.106.2" },
|
|
366
|
+
tsPaths: { "@icore/storage-supabase": ["libs/storage-strategies/supabase/src/index.ts"] },
|
|
367
|
+
nestModule: {
|
|
368
|
+
importFrom: "@icore/storage-supabase",
|
|
369
|
+
symbol: "SupabaseStorageModule",
|
|
370
|
+
into: "upload"
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
firebase: {
|
|
374
|
+
libDirs: ["libs/storage-strategies/firebase"],
|
|
375
|
+
deps: {},
|
|
376
|
+
tsPaths: { "@icore/storage-firebase": ["libs/storage-strategies/firebase/src/index.ts"] },
|
|
377
|
+
nestModule: {
|
|
378
|
+
importFrom: "@icore/storage-firebase",
|
|
379
|
+
symbol: "FirebaseStorageModule",
|
|
380
|
+
into: "upload"
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
cloudinary: {
|
|
384
|
+
libDirs: ["libs/storage-strategies/cloudinary"],
|
|
385
|
+
deps: { cloudinary: "^2.10.0" },
|
|
386
|
+
tsPaths: { "@icore/storage-cloudinary": ["libs/storage-strategies/cloudinary/src/index.ts"] },
|
|
387
|
+
nestModule: {
|
|
388
|
+
importFrom: "@icore/storage-cloudinary",
|
|
389
|
+
symbol: "CloudinaryStorageModule",
|
|
390
|
+
into: "upload"
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
mongodb: {
|
|
394
|
+
libDirs: ["libs/storage-strategies/mongodb"],
|
|
395
|
+
deps: { mongoose: "^9.6.3" },
|
|
396
|
+
tsPaths: { "@icore/storage-mongodb": ["libs/storage-strategies/mongodb/src/index.ts"] },
|
|
397
|
+
nestModule: {
|
|
398
|
+
importFrom: "@icore/storage-mongodb",
|
|
399
|
+
symbol: "MongoDbStorageModule",
|
|
400
|
+
into: "upload"
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
db: {
|
|
405
|
+
supabase: {
|
|
406
|
+
libDirs: ["libs/db-strategies/supabase"],
|
|
407
|
+
deps: { "@supabase/supabase-js": "^2.106.2" },
|
|
408
|
+
tsPaths: { "@icore/db-supabase": ["libs/db-strategies/supabase/src/index.ts"] },
|
|
409
|
+
nestModule: { importFrom: "@icore/db-supabase", symbol: "SupabaseDbModule", into: "notes" }
|
|
410
|
+
},
|
|
411
|
+
firebase: {
|
|
412
|
+
libDirs: ["libs/db-strategies/firestore"],
|
|
413
|
+
deps: {},
|
|
414
|
+
tsPaths: { "@icore/db-firestore": ["libs/db-strategies/firestore/src/index.ts"] },
|
|
415
|
+
nestModule: { importFrom: "@icore/db-firestore", symbol: "FirestoreDbModule", into: "notes" }
|
|
416
|
+
},
|
|
417
|
+
mongodb: {
|
|
418
|
+
libDirs: ["libs/db-strategies/mongodb"],
|
|
419
|
+
deps: { mongoose: "^9.6.3" },
|
|
420
|
+
tsPaths: { "@icore/db-mongodb": ["libs/db-strategies/mongodb/src/index.ts"] },
|
|
421
|
+
nestModule: { importFrom: "@icore/db-mongodb", symbol: "MongoDbDbModule", into: "notes" }
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
feature: {
|
|
425
|
+
notes: {
|
|
426
|
+
libDirs: [
|
|
427
|
+
"apps/microservices/notes",
|
|
428
|
+
"apps/microservices/notes-e2e",
|
|
429
|
+
"libs/notes-client",
|
|
430
|
+
"libs/db-strategies",
|
|
431
|
+
"apps/api/src/app/notes",
|
|
432
|
+
"apps/client/src/components/notes",
|
|
433
|
+
"apps/client/src/routes/_dashboard/notes.tsx",
|
|
434
|
+
"apps/client/src/queries/notes.ts"
|
|
435
|
+
],
|
|
436
|
+
deps: { "@icore/notes-client": "*", "@casl/ability": "^7.0.0" },
|
|
437
|
+
tsPaths: { "@icore/notes-client": ["libs/notes-client/src/index.ts"] },
|
|
438
|
+
gatewayModule: { importFrom: "./notes/notes.module", symbol: "NotesModule" },
|
|
439
|
+
gatewayService: { name: "notes", prefix: "NOTES" },
|
|
440
|
+
clientNav: { route: "/notes", labelKey: "nav.notes", iconName: "notes" }
|
|
441
|
+
},
|
|
442
|
+
payment: {
|
|
443
|
+
libDirs: [
|
|
444
|
+
"apps/microservices/payment",
|
|
445
|
+
"apps/microservices/payment-e2e",
|
|
446
|
+
"libs/payment-client",
|
|
447
|
+
"apps/api/src/app/payment"
|
|
448
|
+
],
|
|
449
|
+
deps: { "@icore/payment-client": "*", "@idevconn/payment": "^1.2.0" },
|
|
450
|
+
tsPaths: { "@icore/payment-client": ["libs/payment-client/src/index.ts"] },
|
|
451
|
+
gatewayModule: { importFrom: "./payment/payment.module", symbol: "PaymentModule" },
|
|
452
|
+
gatewayService: { name: "payment", prefix: "PAYMENT" }
|
|
453
|
+
},
|
|
454
|
+
jobs: {
|
|
455
|
+
libDirs: [
|
|
456
|
+
"apps/microservices/jobs",
|
|
457
|
+
"libs/jobs-client",
|
|
458
|
+
"apps/api/src/app/admin",
|
|
459
|
+
"Dockerfile.ms-jobs"
|
|
460
|
+
],
|
|
461
|
+
deps: {
|
|
462
|
+
"@icore/jobs-client": "*",
|
|
463
|
+
"@bull-board/api": "^7.1.5",
|
|
464
|
+
"@bull-board/express": "^7.1.5"
|
|
465
|
+
},
|
|
466
|
+
tsPaths: { "@icore/jobs-client": ["libs/jobs-client/src/index.ts"] },
|
|
467
|
+
gatewayModule: { importFrom: "./admin/admin.module", symbol: "AdminModule" },
|
|
468
|
+
dockerService: "jobs"
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
ui: { shadcn: EMPTY, antd: EMPTY, mui: EMPTY },
|
|
472
|
+
transport: { tcp: EMPTY, redis: EMPTY, nats: EMPTY, mqtt: EMPTY, rmq: EMPTY, kafka: EMPTY },
|
|
473
|
+
shared: {
|
|
474
|
+
firebaseAdmin: {
|
|
475
|
+
libDirs: ["libs/firebase-admin"],
|
|
476
|
+
deps: { "firebase-admin": "^13.10.0" },
|
|
477
|
+
tsPaths: { "@icore/firebase-admin": ["libs/firebase-admin/src/index.ts"] }
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
// src/manifest/wire-provider.ts
|
|
483
|
+
import { readFile as readFile3, writeFile as writeFile3, rm as rm2 } from "fs/promises";
|
|
544
484
|
import { join as join3 } from "path";
|
|
485
|
+
async function writeProvider(targetDir, axis, provider) {
|
|
486
|
+
const nestModule = axis.section[provider]?.nestModule;
|
|
487
|
+
if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
|
|
488
|
+
const { importFrom, symbol } = nestModule;
|
|
489
|
+
const content = `import { ${symbol} } from '${importFrom}';
|
|
490
|
+
|
|
491
|
+
const ENV_PATH = '${axis.envPath}';
|
|
492
|
+
|
|
493
|
+
export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
|
|
494
|
+
`;
|
|
495
|
+
await writeFile3(join3(targetDir, axis.providerFile), content);
|
|
496
|
+
}
|
|
497
|
+
async function stripJsonKeys(path, drop) {
|
|
498
|
+
try {
|
|
499
|
+
const pkg = JSON.parse(await readFile3(path, "utf8"));
|
|
500
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
501
|
+
const deps = pkg[field];
|
|
502
|
+
if (!deps) continue;
|
|
503
|
+
for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
|
|
504
|
+
}
|
|
505
|
+
await writeFile3(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async function stripTsconfigKeys(targetDir, aliases) {
|
|
510
|
+
const path = join3(targetDir, "tsconfig.base.json");
|
|
511
|
+
try {
|
|
512
|
+
const parsed = JSON.parse(await readFile3(path, "utf8"));
|
|
513
|
+
const paths = parsed.compilerOptions?.paths;
|
|
514
|
+
if (paths) for (const a of aliases) delete paths[a];
|
|
515
|
+
await writeFile3(path, JSON.stringify(parsed, null, 2) + "\n");
|
|
516
|
+
} catch {
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
async function cleanupUnusedAxis(targetDir, axis, chosen) {
|
|
520
|
+
for (const provider of Object.keys(axis.section)) {
|
|
521
|
+
if (provider === chosen) continue;
|
|
522
|
+
const unit = axis.section[provider];
|
|
523
|
+
for (const dir of unit.libDirs)
|
|
524
|
+
await rm2(join3(targetDir, dir), { recursive: true, force: true });
|
|
525
|
+
for (const t of unit.appTests ?? []) await rm2(join3(targetDir, t), { force: true });
|
|
526
|
+
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
527
|
+
await stripJsonKeys(join3(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
|
|
528
|
+
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/manifest/wire-features.ts
|
|
533
|
+
var FEATURES_MODULE = "apps/api/src/app/features.module.ts";
|
|
534
|
+
var GATEWAY_SERVICES = "apps/api/src/app/gateway-services.ts";
|
|
535
|
+
var API_PKG = "apps/api/package.json";
|
|
536
|
+
var FEATURES = MANIFEST.feature;
|
|
537
|
+
function selectedFeatures(opts) {
|
|
538
|
+
const out = [];
|
|
539
|
+
if (opts.example === "notes") out.push("notes");
|
|
540
|
+
if (opts.payment !== "none") out.push("payment");
|
|
541
|
+
if (opts.jobs !== "none") out.push("jobs");
|
|
542
|
+
return out;
|
|
543
|
+
}
|
|
544
|
+
async function writeFeaturesWiring(targetDir, opts) {
|
|
545
|
+
const chosen = selectedFeatures(opts);
|
|
546
|
+
const mods = chosen.map((k) => FEATURES[k].gatewayModule).filter((m) => !!m);
|
|
547
|
+
const imports = mods.map((m) => `import { ${m.symbol} } from '${m.importFrom}';`).join("\n");
|
|
548
|
+
const symbols = mods.map((m) => m.symbol).join(", ");
|
|
549
|
+
const featuresModule = `import { Module } from '@nestjs/common';
|
|
550
|
+
` + (imports ? imports + "\n" : "") + `
|
|
551
|
+
@Module({
|
|
552
|
+
imports: [${symbols}],
|
|
553
|
+
})
|
|
554
|
+
export class FeaturesModule {}
|
|
555
|
+
`;
|
|
556
|
+
await writeFile4(join4(targetDir, FEATURES_MODULE), featuresModule);
|
|
557
|
+
const services = [];
|
|
558
|
+
if (opts.authProvider !== "none") services.push({ name: "auth", prefix: "AUTH" });
|
|
559
|
+
if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
|
|
560
|
+
for (const k of chosen) {
|
|
561
|
+
const svc = FEATURES[k].gatewayService;
|
|
562
|
+
if (svc) services.push(svc);
|
|
563
|
+
}
|
|
564
|
+
const entries = services.map((s) => ` { name: '${s.name}', prefix: '${s.prefix}' },`).join("\n");
|
|
565
|
+
const gatewayServices = `/** Microservices the gateway proxies. Generated by create-icore. */
|
|
566
|
+
export const GATEWAY_SERVICES = [
|
|
567
|
+
${entries}
|
|
568
|
+
];
|
|
569
|
+
`;
|
|
570
|
+
await writeFile4(join4(targetDir, GATEWAY_SERVICES), gatewayServices);
|
|
571
|
+
}
|
|
572
|
+
async function stripJobsDockerCompose(targetDir) {
|
|
573
|
+
const composePath = join4(targetDir, "docker-compose.yml");
|
|
574
|
+
try {
|
|
575
|
+
const compose = await readFile4(composePath, "utf8");
|
|
576
|
+
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, "");
|
|
577
|
+
await writeFile4(composePath, next);
|
|
578
|
+
} catch {
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
async function cleanupUnusedFeatures(targetDir, opts) {
|
|
582
|
+
const chosen = new Set(selectedFeatures(opts));
|
|
583
|
+
for (const key of ["notes", "payment", "jobs"]) {
|
|
584
|
+
if (chosen.has(key)) continue;
|
|
585
|
+
const unit = FEATURES[key];
|
|
586
|
+
for (const dir of unit.libDirs)
|
|
587
|
+
await rm3(join4(targetDir, dir), { recursive: true, force: true });
|
|
588
|
+
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
589
|
+
await stripJsonKeys(join4(targetDir, API_PKG), (k) => dropKeys.has(k));
|
|
590
|
+
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
591
|
+
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
592
|
+
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// src/manifest/wire-client.ts
|
|
597
|
+
import { writeFile as writeFile5 } from "fs/promises";
|
|
598
|
+
import { join as join5 } from "path";
|
|
599
|
+
var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
|
|
600
|
+
var BASE_NAV = [
|
|
601
|
+
{ to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
|
|
602
|
+
];
|
|
603
|
+
var PROFILE_NAV = { to: "/profile", labelKey: "nav.profile", iconName: "profile" };
|
|
604
|
+
function renderEntry(n) {
|
|
605
|
+
const parts = [`to: '${n.to}'`, `labelKey: '${n.labelKey}'`, `iconName: '${n.iconName}'`];
|
|
606
|
+
if (n.exact) parts.push("exact: true");
|
|
607
|
+
return ` { ${parts.join(", ")} },`;
|
|
608
|
+
}
|
|
609
|
+
async function writeNavConfig(targetDir, opts) {
|
|
610
|
+
const entries = [...BASE_NAV];
|
|
611
|
+
const notesNav = MANIFEST.feature.notes.clientNav;
|
|
612
|
+
if (opts.example === "notes" && notesNav) {
|
|
613
|
+
entries.push({
|
|
614
|
+
to: notesNav.route,
|
|
615
|
+
labelKey: notesNav.labelKey,
|
|
616
|
+
iconName: notesNav.iconName,
|
|
617
|
+
exact: notesNav.exact
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
entries.push(PROFILE_NAV);
|
|
621
|
+
const content = `/**
|
|
622
|
+
* Sidebar navigation. UI-agnostic: each LayoutSider maps \`iconName\` to its own
|
|
623
|
+
* icon library. Generated by create-icore.
|
|
624
|
+
*/
|
|
625
|
+
export interface NavItem {
|
|
626
|
+
to: string;
|
|
627
|
+
labelKey: string;
|
|
628
|
+
iconName: 'dashboard' | 'notes' | 'profile';
|
|
629
|
+
exact?: boolean;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export const NAV_CONFIG: NavItem[] = [
|
|
633
|
+
` + entries.map(renderEntry).join("\n") + `
|
|
634
|
+
];
|
|
635
|
+
`;
|
|
636
|
+
await writeFile5(join5(targetDir, NAV_CONFIG_FILE), content);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// src/manifest/blueprint.ts
|
|
640
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
641
|
+
import { join as join6 } from "path";
|
|
642
|
+
async function writeBlueprintJson(targetDir, opts) {
|
|
643
|
+
const blueprint = {
|
|
644
|
+
schemaVersion: 1,
|
|
645
|
+
projectName: opts.projectName,
|
|
646
|
+
authProvider: opts.authProvider,
|
|
647
|
+
dbProvider: opts.dbProvider,
|
|
648
|
+
upload: opts.upload,
|
|
649
|
+
payment: opts.payment,
|
|
650
|
+
jobs: opts.jobs,
|
|
651
|
+
example: opts.example,
|
|
652
|
+
ui: opts.ui,
|
|
653
|
+
transport: opts.transport,
|
|
654
|
+
packageManager: opts.packageManager
|
|
655
|
+
};
|
|
656
|
+
await writeFile6(join6(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
|
|
657
|
+
}
|
|
658
|
+
async function writeJson(targetDir, rel, data) {
|
|
659
|
+
await writeFile6(join6(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
|
|
660
|
+
}
|
|
661
|
+
async function writeServiceBlueprints(targetDir, opts) {
|
|
662
|
+
const t = opts.transport;
|
|
663
|
+
if (opts.authProvider !== "none") {
|
|
664
|
+
await writeJson(targetDir, "apps/microservices/auth", {
|
|
665
|
+
schemaVersion: 1,
|
|
666
|
+
service: "auth",
|
|
667
|
+
authProvider: opts.authProvider,
|
|
668
|
+
transport: t
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
if (opts.upload !== "none") {
|
|
672
|
+
await writeJson(targetDir, "apps/microservices/upload", {
|
|
673
|
+
schemaVersion: 1,
|
|
674
|
+
service: "upload",
|
|
675
|
+
storageProvider: opts.upload,
|
|
676
|
+
transport: t
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
if (opts.example !== "none") {
|
|
680
|
+
await writeJson(targetDir, "apps/microservices/notes", {
|
|
681
|
+
schemaVersion: 1,
|
|
682
|
+
service: "notes",
|
|
683
|
+
dbProvider: opts.dbProvider,
|
|
684
|
+
transport: t
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
if (opts.payment !== "none") {
|
|
688
|
+
await writeJson(targetDir, "apps/microservices/payment", {
|
|
689
|
+
schemaVersion: 1,
|
|
690
|
+
service: "payment",
|
|
691
|
+
paymentProvider: opts.payment,
|
|
692
|
+
transport: t
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
if (opts.jobs !== "none") {
|
|
696
|
+
await writeJson(targetDir, "apps/microservices/jobs", {
|
|
697
|
+
schemaVersion: 1,
|
|
698
|
+
service: "jobs",
|
|
699
|
+
jobsProvider: opts.jobs
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
const features = [];
|
|
703
|
+
if (opts.example !== "none") features.push("notes");
|
|
704
|
+
if (opts.payment !== "none") features.push("payment");
|
|
705
|
+
if (opts.jobs !== "none") features.push("jobs");
|
|
706
|
+
await writeJson(targetDir, "apps/api", {
|
|
707
|
+
schemaVersion: 1,
|
|
708
|
+
service: "api",
|
|
709
|
+
features,
|
|
710
|
+
transport: t
|
|
711
|
+
});
|
|
712
|
+
await writeJson(targetDir, "apps/client", {
|
|
713
|
+
schemaVersion: 1,
|
|
714
|
+
service: "client",
|
|
715
|
+
ui: opts.ui
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// src/manifest/wire-auth.ts
|
|
720
|
+
var AUTH = {
|
|
721
|
+
section: MANIFEST.auth,
|
|
722
|
+
providerFile: "apps/microservices/auth/src/app/auth.provider.ts",
|
|
723
|
+
exportConst: "AuthProviderModule",
|
|
724
|
+
msPackageJson: "apps/microservices/auth/package.json",
|
|
725
|
+
envPath: "apps/microservices/auth/.env"
|
|
726
|
+
};
|
|
727
|
+
var writeAuthProvider = (targetDir, provider) => writeProvider(targetDir, AUTH, provider);
|
|
728
|
+
var cleanupUnusedAuth = (targetDir, chosen) => cleanupUnusedAxis(targetDir, AUTH, chosen);
|
|
729
|
+
|
|
730
|
+
// src/manifest/wire-storage.ts
|
|
731
|
+
var STORAGE = {
|
|
732
|
+
section: MANIFEST.storage,
|
|
733
|
+
providerFile: "apps/microservices/upload/src/app/storage.provider.ts",
|
|
734
|
+
exportConst: "StorageProviderModule",
|
|
735
|
+
msPackageJson: "apps/microservices/upload/package.json",
|
|
736
|
+
envPath: "apps/microservices/upload/.env"
|
|
737
|
+
};
|
|
738
|
+
var writeStorageProvider = (targetDir, provider) => writeProvider(targetDir, STORAGE, provider);
|
|
739
|
+
var cleanupUnusedStorage = (targetDir, chosen) => cleanupUnusedAxis(targetDir, STORAGE, chosen);
|
|
740
|
+
|
|
741
|
+
// src/manifest/wire-db.ts
|
|
742
|
+
var DB = {
|
|
743
|
+
section: MANIFEST.db,
|
|
744
|
+
providerFile: "apps/microservices/notes/src/app/db.provider.ts",
|
|
745
|
+
exportConst: "DbProviderModule",
|
|
746
|
+
msPackageJson: "apps/microservices/notes/package.json",
|
|
747
|
+
envPath: "apps/microservices/notes/.env"
|
|
748
|
+
};
|
|
749
|
+
var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, provider);
|
|
750
|
+
var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
|
|
751
|
+
|
|
752
|
+
// src/lib/scaffold-pkg.ts
|
|
753
|
+
import { readFile as readFile5, writeFile as writeFile7, mkdir, readdir } from "fs/promises";
|
|
754
|
+
import { join as join7 } from "path";
|
|
545
755
|
async function writePnpmWorkspace(targetDir) {
|
|
546
|
-
const pkgPath =
|
|
547
|
-
const pkg = JSON.parse(await
|
|
756
|
+
const pkgPath = join7(targetDir, "package.json");
|
|
757
|
+
const pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
|
|
548
758
|
const workspaces = pkg.workspaces ?? [];
|
|
549
759
|
const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
|
|
550
760
|
const allowBuilds = [
|
|
@@ -553,7 +763,9 @@ async function writePnpmWorkspace(targetDir) {
|
|
|
553
763
|
"@parcel/watcher",
|
|
554
764
|
"@scarf/scarf",
|
|
555
765
|
"@swc/core",
|
|
766
|
+
"bcrypt",
|
|
556
767
|
"less",
|
|
768
|
+
"mongodb-memory-server",
|
|
557
769
|
"msgpackr-extract",
|
|
558
770
|
"nx",
|
|
559
771
|
"protobufjs",
|
|
@@ -565,10 +777,10 @@ ${packagesBlock}
|
|
|
565
777
|
allowBuilds:
|
|
566
778
|
${allowBuilds}
|
|
567
779
|
`;
|
|
568
|
-
await
|
|
780
|
+
await writeFile7(join7(targetDir, "pnpm-workspace.yaml"), content);
|
|
569
781
|
}
|
|
570
782
|
async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
571
|
-
async function
|
|
783
|
+
async function walk2(dir) {
|
|
572
784
|
const found = [];
|
|
573
785
|
let entries;
|
|
574
786
|
try {
|
|
@@ -578,27 +790,27 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
578
790
|
}
|
|
579
791
|
for (const e of entries) {
|
|
580
792
|
if (e.isDirectory() && e.name !== "node_modules") {
|
|
581
|
-
found.push(...await
|
|
793
|
+
found.push(...await walk2(join7(dir, e.name)));
|
|
582
794
|
} else if (e.isFile() && e.name === "package.json") {
|
|
583
|
-
found.push(
|
|
795
|
+
found.push(join7(dir, e.name));
|
|
584
796
|
}
|
|
585
797
|
}
|
|
586
798
|
return found;
|
|
587
799
|
}
|
|
588
|
-
const pkgFiles = await
|
|
800
|
+
const pkgFiles = await walk2(targetDir);
|
|
589
801
|
for (const f of pkgFiles) {
|
|
590
802
|
try {
|
|
591
|
-
const raw = await
|
|
803
|
+
const raw = await readFile5(f, "utf8");
|
|
592
804
|
const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
|
|
593
|
-
if (next !== raw) await
|
|
805
|
+
if (next !== raw) await writeFile7(f, next);
|
|
594
806
|
} catch {
|
|
595
807
|
}
|
|
596
808
|
}
|
|
597
809
|
}
|
|
598
810
|
async function patchGitignoreForPm(targetDir, pm) {
|
|
599
|
-
const giPath =
|
|
811
|
+
const giPath = join7(targetDir, ".gitignore");
|
|
600
812
|
try {
|
|
601
|
-
let src = await
|
|
813
|
+
let src = await readFile5(giPath, "utf8");
|
|
602
814
|
src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
|
|
603
815
|
if (pm !== "yarn") {
|
|
604
816
|
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, "");
|
|
@@ -613,7 +825,7 @@ async function patchGitignoreForPm(targetDir, pm) {
|
|
|
613
825
|
src += "\n# npm\nnpm-debug.log*\n";
|
|
614
826
|
}
|
|
615
827
|
}
|
|
616
|
-
await
|
|
828
|
+
await writeFile7(giPath, src);
|
|
617
829
|
} catch {
|
|
618
830
|
}
|
|
619
831
|
}
|
|
@@ -628,7 +840,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
628
840
|
if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
|
|
629
841
|
const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
|
|
630
842
|
const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
631
|
-
await
|
|
843
|
+
await writeFile7(join7(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
|
|
632
844
|
const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
|
|
633
845
|
const readme = `# ${opts.projectName}
|
|
634
846
|
|
|
@@ -678,7 +890,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
|
|
|
678
890
|
|
|
679
891
|
Apache-2.0
|
|
680
892
|
`;
|
|
681
|
-
await
|
|
893
|
+
await writeFile7(join7(targetDir, "README.md"), readme);
|
|
682
894
|
const agents = `# ${opts.projectName} \u2014 Agent Instructions
|
|
683
895
|
|
|
684
896
|
## Stack snapshot
|
|
@@ -759,8 +971,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
|
|
|
759
971
|
- Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
|
|
760
972
|
- Run: \`${nx} test <project>\`
|
|
761
973
|
`;
|
|
762
|
-
await
|
|
763
|
-
await mkdir(
|
|
974
|
+
await writeFile7(join7(targetDir, "AGENTS.md"), agents);
|
|
975
|
+
await mkdir(join7(targetDir, ".claude"), { recursive: true });
|
|
764
976
|
const mcpServers = {
|
|
765
977
|
nx: {
|
|
766
978
|
command: "npx",
|
|
@@ -801,8 +1013,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
|
|
|
801
1013
|
]
|
|
802
1014
|
}
|
|
803
1015
|
};
|
|
804
|
-
await
|
|
805
|
-
|
|
1016
|
+
await writeFile7(
|
|
1017
|
+
join7(targetDir, ".claude", "settings.json"),
|
|
806
1018
|
JSON.stringify(settings, null, 2) + "\n"
|
|
807
1019
|
);
|
|
808
1020
|
}
|
|
@@ -826,16 +1038,16 @@ async function copyTree(src, dest) {
|
|
|
826
1038
|
const entries = await readdir2(src, { withFileTypes: true });
|
|
827
1039
|
for (const entry of entries) {
|
|
828
1040
|
if (IGNORE_TOP.has(entry.name)) continue;
|
|
829
|
-
const s =
|
|
830
|
-
const d =
|
|
1041
|
+
const s = join8(src, entry.name);
|
|
1042
|
+
const d = join8(dest, entry.name);
|
|
831
1043
|
if (entry.isDirectory()) await copyTree(s, d);
|
|
832
1044
|
else if (entry.isFile()) await copyFile(s, d);
|
|
833
1045
|
}
|
|
834
1046
|
}
|
|
835
1047
|
async function selectClientTemplate(targetDir, opts) {
|
|
836
|
-
const templatesRoot =
|
|
837
|
-
const chosen =
|
|
838
|
-
const destClient =
|
|
1048
|
+
const templatesRoot = join8(targetDir, "apps/templates");
|
|
1049
|
+
const chosen = join8(templatesRoot, `client-${opts.ui}`);
|
|
1050
|
+
const destClient = join8(targetDir, "apps/client");
|
|
839
1051
|
let chosenUi = opts.ui;
|
|
840
1052
|
try {
|
|
841
1053
|
const s = await stat(chosen);
|
|
@@ -843,9 +1055,9 @@ async function selectClientTemplate(targetDir, opts) {
|
|
|
843
1055
|
await copyTree(chosen, destClient);
|
|
844
1056
|
} catch {
|
|
845
1057
|
chosenUi = "shadcn";
|
|
846
|
-
await copyTree(
|
|
1058
|
+
await copyTree(join8(templatesRoot, "client-shadcn"), destClient);
|
|
847
1059
|
}
|
|
848
|
-
await
|
|
1060
|
+
await rm4(templatesRoot, { recursive: true, force: true });
|
|
849
1061
|
await rewriteClientPaths(destClient, chosenUi);
|
|
850
1062
|
}
|
|
851
1063
|
async function rewriteClientPaths(clientDir, ui) {
|
|
@@ -858,11 +1070,11 @@ async function rewriteClientPaths(clientDir, ui) {
|
|
|
858
1070
|
"eslint.config.mjs"
|
|
859
1071
|
];
|
|
860
1072
|
for (const rel of candidates) {
|
|
861
|
-
const path =
|
|
1073
|
+
const path = join8(clientDir, rel);
|
|
862
1074
|
try {
|
|
863
|
-
const raw = await
|
|
1075
|
+
const raw = await readFile6(path, "utf8");
|
|
864
1076
|
const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
|
|
865
|
-
if (next !== raw) await
|
|
1077
|
+
if (next !== raw) await writeFile8(path, next);
|
|
866
1078
|
} catch {
|
|
867
1079
|
}
|
|
868
1080
|
}
|
|
@@ -878,12 +1090,12 @@ function gitInit(cwd, projectName) {
|
|
|
878
1090
|
}
|
|
879
1091
|
function resolveYarnBin(cwd) {
|
|
880
1092
|
try {
|
|
881
|
-
const yarnrc = readFileSync(
|
|
1093
|
+
const yarnrc = readFileSync(join8(cwd, ".yarnrc.yml"), "utf8");
|
|
882
1094
|
const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
|
|
883
|
-
if (match?.[1]) return
|
|
1095
|
+
if (match?.[1]) return join8(cwd, match[1].trim());
|
|
884
1096
|
} catch {
|
|
885
1097
|
}
|
|
886
|
-
return
|
|
1098
|
+
return join8(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
|
|
887
1099
|
}
|
|
888
1100
|
function runInstall(cwd, pm) {
|
|
889
1101
|
if (pm === "yarn") {
|
|
@@ -897,7 +1109,7 @@ function runInstall(cwd, pm) {
|
|
|
897
1109
|
async function scaffold(opts, templatesDir) {
|
|
898
1110
|
await copyTree(templatesDir, opts.targetDir);
|
|
899
1111
|
await rewriteRootPackageJson(opts.targetDir, opts);
|
|
900
|
-
await writeAuthEnv(opts.targetDir, opts);
|
|
1112
|
+
if (opts.authProvider !== "none") await writeAuthEnv(opts.targetDir, opts);
|
|
901
1113
|
await writeUploadEnv(opts.targetDir, opts);
|
|
902
1114
|
await writeNotesEnv(opts.targetDir, opts);
|
|
903
1115
|
await writePaymentEnv(opts.targetDir, opts);
|
|
@@ -906,19 +1118,33 @@ async function scaffold(opts, templatesDir) {
|
|
|
906
1118
|
await selectClientTemplate(opts.targetDir, opts);
|
|
907
1119
|
await writeClientEnv(opts.targetDir);
|
|
908
1120
|
if (opts.upload === "none") await removeUploadStack(opts.targetDir);
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1121
|
+
await cleanupUnusedFeatures(opts.targetDir, opts);
|
|
1122
|
+
await writeFeaturesWiring(opts.targetDir, opts);
|
|
1123
|
+
await writeNavConfig(opts.targetDir, opts);
|
|
1124
|
+
if (opts.authProvider !== "none") {
|
|
1125
|
+
await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
|
|
1126
|
+
await writeAuthProvider(opts.targetDir, opts.authProvider);
|
|
1127
|
+
} else {
|
|
1128
|
+
await removeAuthStack(opts.targetDir);
|
|
1129
|
+
}
|
|
1130
|
+
if (opts.upload !== "none") {
|
|
1131
|
+
await cleanupUnusedStorage(opts.targetDir, opts.upload);
|
|
1132
|
+
await writeStorageProvider(opts.targetDir, opts.upload);
|
|
1133
|
+
}
|
|
915
1134
|
const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
916
1135
|
if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
|
|
1136
|
+
await cleanupUnusedDb(opts.targetDir, opts.dbProvider);
|
|
1137
|
+
if (opts.dbProvider !== "none" && opts.example !== "none") {
|
|
1138
|
+
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1139
|
+
}
|
|
1140
|
+
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1141
|
+
await writeBlueprintJson(opts.targetDir, opts);
|
|
1142
|
+
await writeServiceBlueprints(opts.targetDir, opts);
|
|
917
1143
|
if (opts.packageManager === "yarn") {
|
|
918
|
-
await
|
|
1144
|
+
await writeFile8(join8(opts.targetDir, "yarn.lock"), "");
|
|
919
1145
|
} else {
|
|
920
|
-
await
|
|
921
|
-
await
|
|
1146
|
+
await rm4(join8(opts.targetDir, ".yarn"), { recursive: true, force: true });
|
|
1147
|
+
await rm4(join8(opts.targetDir, ".yarnrc.yml"), { force: true });
|
|
922
1148
|
}
|
|
923
1149
|
if (opts.packageManager === "pnpm") {
|
|
924
1150
|
await writePnpmWorkspace(opts.targetDir);
|
|
@@ -933,9 +1159,98 @@ async function scaffold(opts, templatesDir) {
|
|
|
933
1159
|
// src/lib/prompts.ts
|
|
934
1160
|
import * as p from "@clack/prompts";
|
|
935
1161
|
import { resolve } from "path";
|
|
936
|
-
import { readFile as
|
|
937
|
-
import { dirname, join as
|
|
1162
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
1163
|
+
import { dirname, join as join9 } from "path";
|
|
938
1164
|
import { fileURLToPath } from "url";
|
|
1165
|
+
|
|
1166
|
+
// src/lib/config.ts
|
|
1167
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
1168
|
+
var ConfigFileError = class extends Error {
|
|
1169
|
+
constructor(message) {
|
|
1170
|
+
super(message);
|
|
1171
|
+
this.name = "ConfigFileError";
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
var AUTH_PROVIDERS = ["supabase", "firebase", "mongodb", "none"];
|
|
1175
|
+
var DB_PROVIDERS = ["supabase", "firebase", "mongodb", "none"];
|
|
1176
|
+
var UPLOAD_PROVIDERS = [
|
|
1177
|
+
"supabase",
|
|
1178
|
+
"firebase",
|
|
1179
|
+
"cloudinary",
|
|
1180
|
+
"mongodb",
|
|
1181
|
+
"none"
|
|
1182
|
+
];
|
|
1183
|
+
var PAYMENT_PROVIDERS = ["paypal", "none"];
|
|
1184
|
+
var JOBS_PROVIDERS = ["bullmq", "none"];
|
|
1185
|
+
var EXAMPLE_MODES = ["notes", "none"];
|
|
1186
|
+
var UI_LIBRARIES = ["shadcn", "antd", "mui"];
|
|
1187
|
+
var MS_TRANSPORTS = ["tcp", "redis", "nats", "mqtt", "rmq", "kafka"];
|
|
1188
|
+
var PACKAGE_MANAGERS = ["yarn", "npm", "pnpm"];
|
|
1189
|
+
function assertEnum(field, value, valid) {
|
|
1190
|
+
if (typeof value !== "string" || !valid.includes(value)) {
|
|
1191
|
+
throw new ConfigFileError(
|
|
1192
|
+
`config field "${field}" got "${String(value)}", expected one of: ${valid.join(", ")}`
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
return value;
|
|
1196
|
+
}
|
|
1197
|
+
function assertBoolean(field, value) {
|
|
1198
|
+
if (typeof value !== "boolean") {
|
|
1199
|
+
throw new ConfigFileError(`config field "${field}" must be a boolean, got ${typeof value}`);
|
|
1200
|
+
}
|
|
1201
|
+
return value;
|
|
1202
|
+
}
|
|
1203
|
+
function validateConfig(raw) {
|
|
1204
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
1205
|
+
throw new ConfigFileError("config file must be a JSON object");
|
|
1206
|
+
}
|
|
1207
|
+
const obj = raw;
|
|
1208
|
+
const result = {};
|
|
1209
|
+
if ("projectName" in obj) {
|
|
1210
|
+
const v = obj["projectName"];
|
|
1211
|
+
if (typeof v !== "string" || !/^[a-z0-9-]+$/i.test(v)) {
|
|
1212
|
+
throw new ConfigFileError(
|
|
1213
|
+
`config field "projectName" must match /^[a-z0-9-]+$/i, got "${String(v)}"`
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
result.projectName = v;
|
|
1217
|
+
}
|
|
1218
|
+
if ("authProvider" in obj)
|
|
1219
|
+
result.authProvider = assertEnum("authProvider", obj["authProvider"], AUTH_PROVIDERS);
|
|
1220
|
+
if ("dbProvider" in obj)
|
|
1221
|
+
result.dbProvider = assertEnum("dbProvider", obj["dbProvider"], DB_PROVIDERS);
|
|
1222
|
+
if ("upload" in obj) result.upload = assertEnum("upload", obj["upload"], UPLOAD_PROVIDERS);
|
|
1223
|
+
if ("payment" in obj) result.payment = assertEnum("payment", obj["payment"], PAYMENT_PROVIDERS);
|
|
1224
|
+
if ("jobs" in obj) result.jobs = assertEnum("jobs", obj["jobs"], JOBS_PROVIDERS);
|
|
1225
|
+
if ("example" in obj) result.example = assertEnum("example", obj["example"], EXAMPLE_MODES);
|
|
1226
|
+
if ("ui" in obj) result.ui = assertEnum("ui", obj["ui"], UI_LIBRARIES);
|
|
1227
|
+
if ("transport" in obj)
|
|
1228
|
+
result.transport = assertEnum("transport", obj["transport"], MS_TRANSPORTS);
|
|
1229
|
+
if ("packageManager" in obj)
|
|
1230
|
+
result.packageManager = assertEnum("packageManager", obj["packageManager"], PACKAGE_MANAGERS);
|
|
1231
|
+
if ("initGit" in obj) result.initGit = assertBoolean("initGit", obj["initGit"]);
|
|
1232
|
+
if ("install" in obj) result.install = assertBoolean("install", obj["install"]);
|
|
1233
|
+
return result;
|
|
1234
|
+
}
|
|
1235
|
+
async function loadConfig(filePath) {
|
|
1236
|
+
let raw;
|
|
1237
|
+
try {
|
|
1238
|
+
raw = await readFile7(filePath, "utf8");
|
|
1239
|
+
} catch {
|
|
1240
|
+
throw new ConfigFileError(`config file not found: ${filePath}`);
|
|
1241
|
+
}
|
|
1242
|
+
let parsed;
|
|
1243
|
+
try {
|
|
1244
|
+
parsed = JSON.parse(raw);
|
|
1245
|
+
} catch (e) {
|
|
1246
|
+
throw new ConfigFileError(
|
|
1247
|
+
`config file is not valid JSON: ${e instanceof Error ? e.message : String(e)}`
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1250
|
+
return validateConfig(parsed);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/lib/prompts.ts
|
|
939
1254
|
function detectPackageManager() {
|
|
940
1255
|
const ua = process.env["npm_config_user_agent"] ?? "";
|
|
941
1256
|
if (ua.startsWith("yarn/")) return "yarn";
|
|
@@ -946,7 +1261,7 @@ function detectPackageManager() {
|
|
|
946
1261
|
async function readSelfVersion() {
|
|
947
1262
|
try {
|
|
948
1263
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
949
|
-
const pkgRaw = await
|
|
1264
|
+
const pkgRaw = await readFile8(join9(here, "..", "package.json"), "utf8");
|
|
950
1265
|
const pkg = JSON.parse(pkgRaw);
|
|
951
1266
|
return pkg.version ?? null;
|
|
952
1267
|
} catch {
|
|
@@ -1020,12 +1335,21 @@ function parseFlags(argv) {
|
|
|
1020
1335
|
case "no-install":
|
|
1021
1336
|
out.install = false;
|
|
1022
1337
|
break;
|
|
1338
|
+
case "config":
|
|
1339
|
+
out._configPath = v;
|
|
1340
|
+
break;
|
|
1023
1341
|
}
|
|
1024
1342
|
}
|
|
1025
1343
|
return out;
|
|
1026
1344
|
}
|
|
1027
1345
|
async function collectOptions({ argv, cwd }) {
|
|
1028
1346
|
const flags = parseFlags(argv);
|
|
1347
|
+
const configPath = flags._configPath;
|
|
1348
|
+
delete flags._configPath;
|
|
1349
|
+
if (configPath) {
|
|
1350
|
+
const configValues = await loadConfig(configPath);
|
|
1351
|
+
Object.assign(flags, { ...configValues, ...flags });
|
|
1352
|
+
}
|
|
1029
1353
|
const [selfVersion, latestVersion] = await Promise.all([readSelfVersion(), fetchLatestVersion()]);
|
|
1030
1354
|
const versionTag = selfVersion ? ` v${selfVersion}` : "";
|
|
1031
1355
|
p.intro(`iCore${versionTag} \u2014 bootstrap a new project`);
|
|
@@ -1048,11 +1372,12 @@ Re-run with @latest to refresh:
|
|
|
1048
1372
|
options: [
|
|
1049
1373
|
{ value: "supabase", label: "Supabase" },
|
|
1050
1374
|
{ value: "firebase", label: "Firebase" },
|
|
1051
|
-
{ value: "mongodb", label: "MongoDB (Custom Auth)" }
|
|
1375
|
+
{ value: "mongodb", label: "MongoDB (Custom Auth)" },
|
|
1376
|
+
{ value: "none", label: "None \u2014 no login, open API (simple SPA)" }
|
|
1052
1377
|
]
|
|
1053
1378
|
});
|
|
1054
1379
|
if (p.isCancel(authProvider)) throw new Error("cancelled");
|
|
1055
|
-
const dbProvider = flags.dbProvider ?? await p.select({
|
|
1380
|
+
const dbProvider = authProvider === "none" ? "none" : flags.dbProvider ?? await p.select({
|
|
1056
1381
|
message: "Database backend",
|
|
1057
1382
|
options: [
|
|
1058
1383
|
{ value: "supabase", label: "Supabase Postgres" },
|
|
@@ -1091,7 +1416,7 @@ Re-run with @latest to refresh:
|
|
|
1091
1416
|
initialValue: "none"
|
|
1092
1417
|
});
|
|
1093
1418
|
if (p.isCancel(jobs)) throw new Error("cancelled");
|
|
1094
|
-
const example = flags.example ?? await p.select({
|
|
1419
|
+
const example = authProvider === "none" ? "none" : flags.example ?? await p.select({
|
|
1095
1420
|
message: "Include notes sample feature? (CRUD demo \u2014 remove before production)",
|
|
1096
1421
|
options: [
|
|
1097
1422
|
{ value: "notes", label: "Yes \u2014 include notes sample" },
|
|
@@ -1116,7 +1441,8 @@ Re-run with @latest to refresh:
|
|
|
1116
1441
|
initialValue: "shadcn"
|
|
1117
1442
|
});
|
|
1118
1443
|
if (p.isCancel(ui)) throw new Error("cancelled");
|
|
1119
|
-
const
|
|
1444
|
+
const noMicroservices = authProvider === "none" && upload === "none" && payment === "none";
|
|
1445
|
+
const transport = flags.transport ?? (noMicroservices ? "tcp" : await p.select({
|
|
1120
1446
|
message: "Microservice transport",
|
|
1121
1447
|
options: [
|
|
1122
1448
|
{ value: "tcp", label: "TCP (default, no broker required)" },
|
|
@@ -1127,7 +1453,7 @@ Re-run with @latest to refresh:
|
|
|
1127
1453
|
{ value: "kafka", label: "Kafka" }
|
|
1128
1454
|
],
|
|
1129
1455
|
initialValue: "tcp"
|
|
1130
|
-
});
|
|
1456
|
+
}));
|
|
1131
1457
|
if (p.isCancel(transport)) throw new Error("cancelled");
|
|
1132
1458
|
const packageManager = flags.packageManager ?? detectPackageManager();
|
|
1133
1459
|
if (packageManager === "yarn") {
|
|
@@ -1157,7 +1483,128 @@ Re-run with @latest to refresh:
|
|
|
1157
1483
|
install
|
|
1158
1484
|
};
|
|
1159
1485
|
}
|
|
1486
|
+
|
|
1487
|
+
// src/manifest/audit.ts
|
|
1488
|
+
import { readFile as readFile9, readdir as readdir3 } from "fs/promises";
|
|
1489
|
+
import { join as join10 } from "path";
|
|
1490
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".nx"]);
|
|
1491
|
+
async function walk(dir, out = []) {
|
|
1492
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
1493
|
+
for (const e of entries) {
|
|
1494
|
+
if (e.isDirectory()) {
|
|
1495
|
+
if (!IGNORE_DIRS.has(e.name)) await walk(join10(dir, e.name), out);
|
|
1496
|
+
} else if (/\.(ts|tsx|mjs)$/.test(e.name)) {
|
|
1497
|
+
out.push(join10(dir, e.name));
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return out;
|
|
1501
|
+
}
|
|
1502
|
+
async function tsconfigAliases(dir) {
|
|
1503
|
+
try {
|
|
1504
|
+
const raw = await readFile9(join10(dir, "tsconfig.base.json"), "utf8");
|
|
1505
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
1506
|
+
for (const m of raw.matchAll(/"(@icore\/[a-z0-9.-]+)"\s*:/g)) {
|
|
1507
|
+
if (m[1]) aliases.add(m[1]);
|
|
1508
|
+
}
|
|
1509
|
+
return aliases;
|
|
1510
|
+
} catch {
|
|
1511
|
+
return /* @__PURE__ */ new Set();
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
var PROVIDER_SDKS = {
|
|
1515
|
+
supabase: ["@supabase/supabase-js"],
|
|
1516
|
+
cloudinary: ["cloudinary"],
|
|
1517
|
+
mongodb: ["mongoose"],
|
|
1518
|
+
firebase: ["firebase-admin", "@icore/firebase-admin"]
|
|
1519
|
+
};
|
|
1520
|
+
async function readBlueprint(dir) {
|
|
1521
|
+
try {
|
|
1522
|
+
return JSON.parse(await readFile9(join10(dir, "blueprint.json"), "utf8"));
|
|
1523
|
+
} catch {
|
|
1524
|
+
return null;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
function forbiddenFromBlueprint(bp) {
|
|
1528
|
+
const chosen = new Set(
|
|
1529
|
+
[bp.authProvider, bp.dbProvider, bp.upload].filter((p2) => Boolean(p2))
|
|
1530
|
+
);
|
|
1531
|
+
const forbidden = [];
|
|
1532
|
+
for (const [provider, sdks] of Object.entries(PROVIDER_SDKS)) {
|
|
1533
|
+
if (!chosen.has(provider)) forbidden.push(...sdks);
|
|
1534
|
+
}
|
|
1535
|
+
return forbidden;
|
|
1536
|
+
}
|
|
1537
|
+
async function allPackageJsons(dir) {
|
|
1538
|
+
const out = [];
|
|
1539
|
+
const root = join10(dir, "package.json");
|
|
1540
|
+
out.push(root);
|
|
1541
|
+
async function walk2(d) {
|
|
1542
|
+
let entries;
|
|
1543
|
+
try {
|
|
1544
|
+
entries = await readdir3(d, { withFileTypes: true });
|
|
1545
|
+
} catch {
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
for (const e of entries) {
|
|
1549
|
+
if (e.isDirectory()) {
|
|
1550
|
+
if (!IGNORE_DIRS.has(e.name)) await walk2(join10(d, e.name));
|
|
1551
|
+
} else if (e.name === "package.json") {
|
|
1552
|
+
out.push(join10(d, e.name));
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
await walk2(join10(dir, "apps"));
|
|
1557
|
+
return out;
|
|
1558
|
+
}
|
|
1559
|
+
async function depKeys(pkgPath) {
|
|
1560
|
+
try {
|
|
1561
|
+
const pkg = JSON.parse(await readFile9(pkgPath, "utf8"));
|
|
1562
|
+
return /* @__PURE__ */ new Set([
|
|
1563
|
+
...Object.keys(pkg.dependencies ?? {}),
|
|
1564
|
+
...Object.keys(pkg.devDependencies ?? {})
|
|
1565
|
+
]);
|
|
1566
|
+
} catch {
|
|
1567
|
+
return /* @__PURE__ */ new Set();
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
var ICORE_IMPORT = /(?:from|import\()\s*['"](@icore\/[a-z0-9.-]+)/g;
|
|
1571
|
+
async function auditProject(dir, opts = {}) {
|
|
1572
|
+
const violations = [];
|
|
1573
|
+
const aliases = await tsconfigAliases(dir);
|
|
1574
|
+
for (const file of await walk(dir)) {
|
|
1575
|
+
const src = await readFile9(file, "utf8");
|
|
1576
|
+
for (const m of src.matchAll(ICORE_IMPORT)) {
|
|
1577
|
+
const alias = m[1];
|
|
1578
|
+
if (alias && !aliases.has(alias)) {
|
|
1579
|
+
violations.push({
|
|
1580
|
+
kind: "import-of-absent-lib",
|
|
1581
|
+
detail: `${file} imports ${alias} (no tsconfig path \u2192 lib absent)`
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
const bp = await readBlueprint(dir);
|
|
1587
|
+
const forbidden = /* @__PURE__ */ new Set([
|
|
1588
|
+
...opts.forbiddenDeps ?? [],
|
|
1589
|
+
...bp ? forbiddenFromBlueprint(bp) : []
|
|
1590
|
+
]);
|
|
1591
|
+
if (forbidden.size > 0) {
|
|
1592
|
+
for (const pkgPath of await allPackageJsons(dir)) {
|
|
1593
|
+
const deps = await depKeys(pkgPath);
|
|
1594
|
+
for (const f of forbidden) {
|
|
1595
|
+
if (deps.has(f)) {
|
|
1596
|
+
violations.push({
|
|
1597
|
+
kind: "forbidden-dep",
|
|
1598
|
+
detail: `${pkgPath} keeps forbidden dep ${f}`
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return violations;
|
|
1605
|
+
}
|
|
1160
1606
|
export {
|
|
1607
|
+
auditProject,
|
|
1161
1608
|
collectOptions,
|
|
1162
1609
|
pmRun,
|
|
1163
1610
|
scaffold
|