@idevconn/create-icore 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +509 -353
- package/dist/index.cjs +644 -366
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +635 -358
- 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 +4 -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");
|
|
@@ -186,42 +211,26 @@ async function stripTsconfigPath(targetDir, alias) {
|
|
|
186
211
|
} catch {
|
|
187
212
|
}
|
|
188
213
|
}
|
|
189
|
-
async function
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
"
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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"
|
|
214
|
+
async function removeFirebaseAdminLib(targetDir) {
|
|
215
|
+
await rm(join2(targetDir, "libs/firebase-admin"), { recursive: true, force: true });
|
|
216
|
+
await stripTsconfigPath(targetDir, "@icore/firebase-admin");
|
|
217
|
+
await stripDeps(join2(targetDir, "apps/microservices/auth/package.json"), [
|
|
218
|
+
"@icore/firebase-admin"
|
|
219
|
+
]);
|
|
220
|
+
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
221
|
+
"@icore/firebase-admin"
|
|
222
|
+
]);
|
|
223
|
+
await stripDeps(join2(targetDir, "apps/microservices/notes/package.json"), [
|
|
224
|
+
"@icore/firebase-admin"
|
|
210
225
|
]);
|
|
211
|
-
const composePath = join2(targetDir, "docker-compose.yml");
|
|
212
|
-
try {
|
|
213
|
-
const compose = await readFile2(composePath, "utf8");
|
|
214
|
-
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, "");
|
|
215
|
-
await writeFile2(composePath, next);
|
|
216
|
-
} catch {
|
|
217
|
-
}
|
|
218
226
|
}
|
|
219
|
-
async function
|
|
227
|
+
async function removeUploadStack(targetDir) {
|
|
220
228
|
const paths = [
|
|
221
|
-
"apps/microservices/
|
|
222
|
-
"apps/microservices/
|
|
223
|
-
"libs/
|
|
224
|
-
"
|
|
229
|
+
"apps/microservices/upload",
|
|
230
|
+
"apps/microservices/upload-e2e",
|
|
231
|
+
"libs/storage-strategies",
|
|
232
|
+
"libs/upload-client",
|
|
233
|
+
"apps/api/src/app/storage"
|
|
225
234
|
];
|
|
226
235
|
for (const p2 of paths) {
|
|
227
236
|
await rm(join2(targetDir, p2), { recursive: true, force: true });
|
|
@@ -229,322 +238,459 @@ async function removePaymentStack(targetDir) {
|
|
|
229
238
|
const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
|
|
230
239
|
try {
|
|
231
240
|
const appModule = await readFile2(appModulePath, "utf8");
|
|
232
|
-
const next = appModule.replace(/^import \{
|
|
241
|
+
const next = appModule.replace(/^import \{ StorageModule \} from '\.\/storage\/storage\.module';\n/m, "").replace(/,\s*StorageModule/g, "");
|
|
233
242
|
await writeFile2(appModulePath, next);
|
|
234
243
|
} catch {
|
|
235
244
|
}
|
|
236
|
-
|
|
237
|
-
"@icore/payment-client",
|
|
238
|
-
"@idevconn/payment"
|
|
239
|
-
]);
|
|
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
|
-
}
|
|
249
|
-
async function removeNotesStack(targetDir) {
|
|
250
|
-
for (const p2 of [
|
|
251
|
-
"apps/microservices/notes",
|
|
252
|
-
"apps/microservices/notes-e2e",
|
|
253
|
-
"libs/notes-client",
|
|
254
|
-
"apps/api/src/app/notes",
|
|
255
|
-
"apps/client/src/components/notes"
|
|
256
|
-
]) {
|
|
257
|
-
await rm(join2(targetDir, p2), { recursive: true, force: true });
|
|
258
|
-
}
|
|
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
|
-
const appModulePath = join2(targetDir, "apps/api/src/app/app.module.ts");
|
|
245
|
+
const gatewayEnv = join2(targetDir, "apps/api/.env");
|
|
262
246
|
try {
|
|
263
|
-
const
|
|
264
|
-
const next =
|
|
265
|
-
|
|
247
|
+
const env = await readFile2(gatewayEnv, "utf8");
|
|
248
|
+
const next = env.split("\n").filter(
|
|
249
|
+
(line) => !line.startsWith("UPLOAD_") && !line.startsWith("# UPLOAD_") && !line.startsWith("MAX_FILE_SIZE_KB")
|
|
250
|
+
).join("\n");
|
|
251
|
+
await writeFile2(gatewayEnv, next);
|
|
266
252
|
} catch {
|
|
267
253
|
}
|
|
268
254
|
await stripDeps(join2(targetDir, "apps/api/package.json"), [
|
|
269
|
-
"@icore/
|
|
270
|
-
"@
|
|
255
|
+
"@icore/upload-client",
|
|
256
|
+
"@types/multer"
|
|
271
257
|
]);
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/manifest/wire-features.ts
|
|
261
|
+
import { readFile as readFile4, writeFile as writeFile4, rm as rm3 } from "fs/promises";
|
|
262
|
+
import { join as join4 } from "path";
|
|
263
|
+
|
|
264
|
+
// src/manifest/index.ts
|
|
265
|
+
var EMPTY = { libDirs: [], deps: {}, tsPaths: {} };
|
|
266
|
+
var MANIFEST = {
|
|
267
|
+
auth: {
|
|
268
|
+
supabase: {
|
|
269
|
+
libDirs: ["libs/auth-strategies/supabase"],
|
|
270
|
+
deps: { "@supabase/supabase-js": "^2.106.2" },
|
|
271
|
+
tsPaths: { "@icore/auth-supabase": ["libs/auth-strategies/supabase/src/index.ts"] },
|
|
272
|
+
nestModule: {
|
|
273
|
+
importFrom: "@icore/auth-supabase",
|
|
274
|
+
symbol: "SupabaseAuthModule",
|
|
275
|
+
into: "auth"
|
|
276
|
+
},
|
|
277
|
+
appTests: [
|
|
278
|
+
"apps/microservices/auth/src/app/__tests__/auth.controller.supabase.integration.unit.test.ts"
|
|
279
|
+
]
|
|
280
|
+
},
|
|
281
|
+
firebase: {
|
|
282
|
+
libDirs: ["libs/auth-strategies/firebase"],
|
|
283
|
+
deps: {},
|
|
284
|
+
tsPaths: { "@icore/auth-firebase": ["libs/auth-strategies/firebase/src/index.ts"] },
|
|
285
|
+
nestModule: {
|
|
286
|
+
importFrom: "@icore/auth-firebase",
|
|
287
|
+
symbol: "FirebaseAuthModule",
|
|
288
|
+
into: "auth"
|
|
289
|
+
},
|
|
290
|
+
appTests: [
|
|
291
|
+
"apps/microservices/auth/src/app/__tests__/auth.controller.firebase.integration.unit.test.ts"
|
|
292
|
+
]
|
|
293
|
+
},
|
|
294
|
+
mongodb: {
|
|
295
|
+
libDirs: ["libs/auth-strategies/mongodb"],
|
|
296
|
+
deps: { mongoose: "^9.6.3" },
|
|
297
|
+
tsPaths: { "@icore/auth-mongodb": ["libs/auth-strategies/mongodb/src/index.ts"] },
|
|
298
|
+
nestModule: { importFrom: "@icore/auth-mongodb", symbol: "MongoDbAuthModule", into: "auth" }
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
storage: {
|
|
302
|
+
supabase: {
|
|
303
|
+
libDirs: ["libs/storage-strategies/supabase"],
|
|
304
|
+
deps: { "@supabase/supabase-js": "^2.106.2" },
|
|
305
|
+
tsPaths: { "@icore/storage-supabase": ["libs/storage-strategies/supabase/src/index.ts"] },
|
|
306
|
+
nestModule: {
|
|
307
|
+
importFrom: "@icore/storage-supabase",
|
|
308
|
+
symbol: "SupabaseStorageModule",
|
|
309
|
+
into: "upload"
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
firebase: {
|
|
313
|
+
libDirs: ["libs/storage-strategies/firebase"],
|
|
314
|
+
deps: {},
|
|
315
|
+
tsPaths: { "@icore/storage-firebase": ["libs/storage-strategies/firebase/src/index.ts"] },
|
|
316
|
+
nestModule: {
|
|
317
|
+
importFrom: "@icore/storage-firebase",
|
|
318
|
+
symbol: "FirebaseStorageModule",
|
|
319
|
+
into: "upload"
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
cloudinary: {
|
|
323
|
+
libDirs: ["libs/storage-strategies/cloudinary"],
|
|
324
|
+
deps: { cloudinary: "^2.10.0" },
|
|
325
|
+
tsPaths: { "@icore/storage-cloudinary": ["libs/storage-strategies/cloudinary/src/index.ts"] },
|
|
326
|
+
nestModule: {
|
|
327
|
+
importFrom: "@icore/storage-cloudinary",
|
|
328
|
+
symbol: "CloudinaryStorageModule",
|
|
329
|
+
into: "upload"
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
mongodb: {
|
|
333
|
+
libDirs: ["libs/storage-strategies/mongodb"],
|
|
334
|
+
deps: { mongoose: "^9.6.3" },
|
|
335
|
+
tsPaths: { "@icore/storage-mongodb": ["libs/storage-strategies/mongodb/src/index.ts"] },
|
|
336
|
+
nestModule: {
|
|
337
|
+
importFrom: "@icore/storage-mongodb",
|
|
338
|
+
symbol: "MongoDbStorageModule",
|
|
339
|
+
into: "upload"
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
db: {
|
|
344
|
+
supabase: {
|
|
345
|
+
libDirs: ["libs/db-strategies/supabase"],
|
|
346
|
+
deps: { "@supabase/supabase-js": "^2.106.2" },
|
|
347
|
+
tsPaths: { "@icore/db-supabase": ["libs/db-strategies/supabase/src/index.ts"] },
|
|
348
|
+
nestModule: { importFrom: "@icore/db-supabase", symbol: "SupabaseDbModule", into: "notes" }
|
|
349
|
+
},
|
|
350
|
+
firebase: {
|
|
351
|
+
libDirs: ["libs/db-strategies/firestore"],
|
|
352
|
+
deps: {},
|
|
353
|
+
tsPaths: { "@icore/db-firestore": ["libs/db-strategies/firestore/src/index.ts"] },
|
|
354
|
+
nestModule: { importFrom: "@icore/db-firestore", symbol: "FirestoreDbModule", into: "notes" }
|
|
355
|
+
},
|
|
356
|
+
mongodb: {
|
|
357
|
+
libDirs: ["libs/db-strategies/mongodb"],
|
|
358
|
+
deps: { mongoose: "^9.6.3" },
|
|
359
|
+
tsPaths: { "@icore/db-mongodb": ["libs/db-strategies/mongodb/src/index.ts"] },
|
|
360
|
+
nestModule: { importFrom: "@icore/db-mongodb", symbol: "MongoDbDbModule", into: "notes" }
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
feature: {
|
|
364
|
+
notes: {
|
|
365
|
+
libDirs: [
|
|
366
|
+
"apps/microservices/notes",
|
|
367
|
+
"apps/microservices/notes-e2e",
|
|
368
|
+
"libs/notes-client",
|
|
369
|
+
"libs/db-strategies",
|
|
370
|
+
"apps/api/src/app/notes",
|
|
371
|
+
"apps/client/src/components/notes",
|
|
372
|
+
"apps/client/src/routes/_dashboard/notes.tsx",
|
|
373
|
+
"apps/client/src/queries/notes.ts"
|
|
374
|
+
],
|
|
375
|
+
deps: { "@icore/notes-client": "*", "@casl/ability": "^7.0.0" },
|
|
376
|
+
tsPaths: { "@icore/notes-client": ["libs/notes-client/src/index.ts"] },
|
|
377
|
+
gatewayModule: { importFrom: "./notes/notes.module", symbol: "NotesModule" },
|
|
378
|
+
gatewayService: { name: "notes", prefix: "NOTES" },
|
|
379
|
+
clientNav: { route: "/notes", labelKey: "nav.notes", iconName: "notes" }
|
|
380
|
+
},
|
|
381
|
+
payment: {
|
|
382
|
+
libDirs: [
|
|
383
|
+
"apps/microservices/payment",
|
|
384
|
+
"apps/microservices/payment-e2e",
|
|
385
|
+
"libs/payment-client",
|
|
386
|
+
"apps/api/src/app/payment"
|
|
387
|
+
],
|
|
388
|
+
deps: { "@icore/payment-client": "*", "@idevconn/payment": "^1.2.0" },
|
|
389
|
+
tsPaths: { "@icore/payment-client": ["libs/payment-client/src/index.ts"] },
|
|
390
|
+
gatewayModule: { importFrom: "./payment/payment.module", symbol: "PaymentModule" },
|
|
391
|
+
gatewayService: { name: "payment", prefix: "PAYMENT" }
|
|
392
|
+
},
|
|
393
|
+
jobs: {
|
|
394
|
+
libDirs: [
|
|
395
|
+
"apps/microservices/jobs",
|
|
396
|
+
"libs/jobs-client",
|
|
397
|
+
"apps/api/src/app/admin",
|
|
398
|
+
"Dockerfile.ms-jobs"
|
|
399
|
+
],
|
|
400
|
+
deps: {
|
|
401
|
+
"@icore/jobs-client": "*",
|
|
402
|
+
"@bull-board/api": "^7.1.5",
|
|
403
|
+
"@bull-board/express": "^7.1.5"
|
|
404
|
+
},
|
|
405
|
+
tsPaths: { "@icore/jobs-client": ["libs/jobs-client/src/index.ts"] },
|
|
406
|
+
gatewayModule: { importFrom: "./admin/admin.module", symbol: "AdminModule" },
|
|
407
|
+
dockerService: "jobs"
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
ui: { shadcn: EMPTY, antd: EMPTY, mui: EMPTY },
|
|
411
|
+
transport: { tcp: EMPTY, redis: EMPTY, nats: EMPTY, mqtt: EMPTY, rmq: EMPTY, kafka: EMPTY },
|
|
412
|
+
shared: {
|
|
413
|
+
firebaseAdmin: {
|
|
414
|
+
libDirs: ["libs/firebase-admin"],
|
|
415
|
+
deps: { "firebase-admin": "^13.10.0" },
|
|
416
|
+
tsPaths: { "@icore/firebase-admin": ["libs/firebase-admin/src/index.ts"] }
|
|
417
|
+
}
|
|
286
418
|
}
|
|
287
|
-
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// src/manifest/wire-provider.ts
|
|
422
|
+
import { readFile as readFile3, writeFile as writeFile3, rm as rm2 } from "fs/promises";
|
|
423
|
+
import { join as join3 } from "path";
|
|
424
|
+
async function writeProvider(targetDir, axis, provider) {
|
|
425
|
+
const nestModule = axis.section[provider]?.nestModule;
|
|
426
|
+
if (!nestModule) throw new Error(`provider "${provider}" has no nestModule in the manifest`);
|
|
427
|
+
const { importFrom, symbol } = nestModule;
|
|
428
|
+
const content = `import { ${symbol} } from '${importFrom}';
|
|
429
|
+
|
|
430
|
+
const ENV_PATH = '${axis.envPath}';
|
|
431
|
+
|
|
432
|
+
export const ${axis.exportConst} = ${symbol}.forRoot(ENV_PATH);
|
|
433
|
+
`;
|
|
434
|
+
await writeFile3(join3(targetDir, axis.providerFile), content);
|
|
435
|
+
}
|
|
436
|
+
async function stripJsonKeys(path, drop) {
|
|
288
437
|
try {
|
|
289
|
-
const
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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);
|
|
438
|
+
const pkg = JSON.parse(await readFile3(path, "utf8"));
|
|
439
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
440
|
+
const deps = pkg[field];
|
|
441
|
+
if (!deps) continue;
|
|
442
|
+
for (const k of Object.keys(deps)) if (drop(k)) delete deps[k];
|
|
443
|
+
}
|
|
444
|
+
await writeFile3(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
301
445
|
} catch {
|
|
302
446
|
}
|
|
303
|
-
|
|
447
|
+
}
|
|
448
|
+
async function stripTsconfigKeys(targetDir, aliases) {
|
|
449
|
+
const path = join3(targetDir, "tsconfig.base.json");
|
|
304
450
|
try {
|
|
305
|
-
const
|
|
306
|
-
const
|
|
307
|
-
|
|
451
|
+
const parsed = JSON.parse(await readFile3(path, "utf8"));
|
|
452
|
+
const paths = parsed.compilerOptions?.paths;
|
|
453
|
+
if (paths) for (const a of aliases) delete paths[a];
|
|
454
|
+
await writeFile3(path, JSON.stringify(parsed, null, 2) + "\n");
|
|
308
455
|
} catch {
|
|
309
456
|
}
|
|
310
457
|
}
|
|
311
|
-
async function
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
await
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
}
|
|
458
|
+
async function cleanupUnusedAxis(targetDir, axis, chosen) {
|
|
459
|
+
for (const provider of Object.keys(axis.section)) {
|
|
460
|
+
if (provider === chosen) continue;
|
|
461
|
+
const unit = axis.section[provider];
|
|
462
|
+
for (const dir of unit.libDirs)
|
|
463
|
+
await rm2(join3(targetDir, dir), { recursive: true, force: true });
|
|
464
|
+
for (const t of unit.appTests ?? []) await rm2(join3(targetDir, t), { force: true });
|
|
465
|
+
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
466
|
+
await stripJsonKeys(join3(targetDir, axis.msPackageJson), (k) => dropKeys.has(k));
|
|
467
|
+
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
369
468
|
}
|
|
370
469
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
await stripDeps(join2(targetDir, "apps/microservices/upload/package.json"), [
|
|
403
|
-
"@icore/storage-mongodb"
|
|
404
|
-
]);
|
|
405
|
-
await stripTsconfigPath(targetDir, "@icore/storage-mongodb");
|
|
470
|
+
|
|
471
|
+
// src/manifest/wire-features.ts
|
|
472
|
+
var FEATURES_MODULE = "apps/api/src/app/features.module.ts";
|
|
473
|
+
var GATEWAY_SERVICES = "apps/api/src/app/gateway-services.ts";
|
|
474
|
+
var API_PKG = "apps/api/package.json";
|
|
475
|
+
var FEATURES = MANIFEST.feature;
|
|
476
|
+
function selectedFeatures(opts) {
|
|
477
|
+
const out = [];
|
|
478
|
+
if (opts.example === "notes") out.push("notes");
|
|
479
|
+
if (opts.payment !== "none") out.push("payment");
|
|
480
|
+
if (opts.jobs !== "none") out.push("jobs");
|
|
481
|
+
return out;
|
|
482
|
+
}
|
|
483
|
+
async function writeFeaturesWiring(targetDir, opts) {
|
|
484
|
+
const chosen = selectedFeatures(opts);
|
|
485
|
+
const mods = chosen.map((k) => FEATURES[k].gatewayModule).filter((m) => !!m);
|
|
486
|
+
const imports = mods.map((m) => `import { ${m.symbol} } from '${m.importFrom}';`).join("\n");
|
|
487
|
+
const symbols = mods.map((m) => m.symbol).join(", ");
|
|
488
|
+
const featuresModule = `import { Module } from '@nestjs/common';
|
|
489
|
+
` + (imports ? imports + "\n" : "") + `
|
|
490
|
+
@Module({
|
|
491
|
+
imports: [${symbols}],
|
|
492
|
+
})
|
|
493
|
+
export class FeaturesModule {}
|
|
494
|
+
`;
|
|
495
|
+
await writeFile4(join4(targetDir, FEATURES_MODULE), featuresModule);
|
|
496
|
+
const services = [{ name: "auth", prefix: "AUTH" }];
|
|
497
|
+
if (opts.upload !== "none") services.push({ name: "upload", prefix: "UPLOAD" });
|
|
498
|
+
for (const k of chosen) {
|
|
499
|
+
const svc = FEATURES[k].gatewayService;
|
|
500
|
+
if (svc) services.push(svc);
|
|
406
501
|
}
|
|
502
|
+
const entries = services.map((s) => ` { name: '${s.name}', prefix: '${s.prefix}' },`).join("\n");
|
|
503
|
+
const gatewayServices = `/** Microservices the gateway proxies. Generated by create-icore. */
|
|
504
|
+
export const GATEWAY_SERVICES = [
|
|
505
|
+
${entries}
|
|
506
|
+
];
|
|
507
|
+
`;
|
|
508
|
+
await writeFile4(join4(targetDir, GATEWAY_SERVICES), gatewayServices);
|
|
509
|
+
}
|
|
510
|
+
async function stripJobsDockerCompose(targetDir) {
|
|
511
|
+
const composePath = join4(targetDir, "docker-compose.yml");
|
|
407
512
|
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);
|
|
513
|
+
const compose = await readFile4(composePath, "utf8");
|
|
514
|
+
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, "");
|
|
515
|
+
await writeFile4(composePath, next);
|
|
442
516
|
} catch {
|
|
443
517
|
}
|
|
444
518
|
}
|
|
445
|
-
async function
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
-
}
|
|
519
|
+
async function cleanupUnusedFeatures(targetDir, opts) {
|
|
520
|
+
const chosen = new Set(selectedFeatures(opts));
|
|
521
|
+
for (const key of ["notes", "payment", "jobs"]) {
|
|
522
|
+
if (chosen.has(key)) continue;
|
|
523
|
+
const unit = FEATURES[key];
|
|
524
|
+
for (const dir of unit.libDirs)
|
|
525
|
+
await rm3(join4(targetDir, dir), { recursive: true, force: true });
|
|
526
|
+
const dropKeys = /* @__PURE__ */ new Set([...Object.keys(unit.tsPaths), ...Object.keys(unit.deps)]);
|
|
527
|
+
await stripJsonKeys(join4(targetDir, API_PKG), (k) => dropKeys.has(k));
|
|
528
|
+
await stripTsconfigKeys(targetDir, Object.keys(unit.tsPaths));
|
|
529
|
+
if (unit.gatewayService) await stripGatewayTransport(targetDir, unit.gatewayService.prefix);
|
|
530
|
+
if (unit.dockerService === "jobs") await stripJobsDockerCompose(targetDir);
|
|
486
531
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// src/manifest/wire-client.ts
|
|
535
|
+
import { writeFile as writeFile5 } from "fs/promises";
|
|
536
|
+
import { join as join5 } from "path";
|
|
537
|
+
var NAV_CONFIG_FILE = "apps/client/src/nav.config.ts";
|
|
538
|
+
var BASE_NAV = [
|
|
539
|
+
{ to: "/dashboard", labelKey: "nav.dashboard", iconName: "dashboard", exact: true }
|
|
540
|
+
];
|
|
541
|
+
var PROFILE_NAV = { to: "/profile", labelKey: "nav.profile", iconName: "profile" };
|
|
542
|
+
function renderEntry(n) {
|
|
543
|
+
const parts = [`to: '${n.to}'`, `labelKey: '${n.labelKey}'`, `iconName: '${n.iconName}'`];
|
|
544
|
+
if (n.exact) parts.push("exact: true");
|
|
545
|
+
return ` { ${parts.join(", ")} },`;
|
|
546
|
+
}
|
|
547
|
+
async function writeNavConfig(targetDir, opts) {
|
|
548
|
+
const entries = [...BASE_NAV];
|
|
549
|
+
const notesNav = MANIFEST.feature.notes.clientNav;
|
|
550
|
+
if (opts.example === "notes" && notesNav) {
|
|
551
|
+
entries.push({
|
|
552
|
+
to: notesNav.route,
|
|
553
|
+
labelKey: notesNav.labelKey,
|
|
554
|
+
iconName: notesNav.iconName,
|
|
555
|
+
exact: notesNav.exact
|
|
556
|
+
});
|
|
503
557
|
}
|
|
558
|
+
entries.push(PROFILE_NAV);
|
|
559
|
+
const content = `/**
|
|
560
|
+
* Sidebar navigation. UI-agnostic: each LayoutSider maps \`iconName\` to its own
|
|
561
|
+
* icon library. Generated by create-icore.
|
|
562
|
+
*/
|
|
563
|
+
export interface NavItem {
|
|
564
|
+
to: string;
|
|
565
|
+
labelKey: string;
|
|
566
|
+
iconName: 'dashboard' | 'notes' | 'profile';
|
|
567
|
+
exact?: boolean;
|
|
504
568
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
569
|
+
|
|
570
|
+
export const NAV_CONFIG: NavItem[] = [
|
|
571
|
+
` + entries.map(renderEntry).join("\n") + `
|
|
572
|
+
];
|
|
573
|
+
`;
|
|
574
|
+
await writeFile5(join5(targetDir, NAV_CONFIG_FILE), content);
|
|
508
575
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
576
|
+
|
|
577
|
+
// src/manifest/blueprint.ts
|
|
578
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
579
|
+
import { join as join6 } from "path";
|
|
580
|
+
async function writeBlueprintJson(targetDir, opts) {
|
|
581
|
+
const blueprint = {
|
|
582
|
+
schemaVersion: 1,
|
|
583
|
+
projectName: opts.projectName,
|
|
584
|
+
authProvider: opts.authProvider,
|
|
585
|
+
dbProvider: opts.dbProvider,
|
|
586
|
+
upload: opts.upload,
|
|
587
|
+
payment: opts.payment,
|
|
588
|
+
jobs: opts.jobs,
|
|
589
|
+
example: opts.example,
|
|
590
|
+
ui: opts.ui,
|
|
591
|
+
transport: opts.transport,
|
|
592
|
+
packageManager: opts.packageManager
|
|
593
|
+
};
|
|
594
|
+
await writeFile6(join6(targetDir, "blueprint.json"), JSON.stringify(blueprint, null, 2) + "\n");
|
|
595
|
+
}
|
|
596
|
+
async function writeJson(targetDir, rel, data) {
|
|
597
|
+
await writeFile6(join6(targetDir, rel, "blueprint.json"), JSON.stringify(data, null, 2) + "\n");
|
|
598
|
+
}
|
|
599
|
+
async function writeServiceBlueprints(targetDir, opts) {
|
|
600
|
+
const t = opts.transport;
|
|
601
|
+
await writeJson(targetDir, "apps/microservices/auth", {
|
|
602
|
+
schemaVersion: 1,
|
|
603
|
+
service: "auth",
|
|
604
|
+
authProvider: opts.authProvider,
|
|
605
|
+
transport: t
|
|
606
|
+
});
|
|
607
|
+
if (opts.upload !== "none") {
|
|
608
|
+
await writeJson(targetDir, "apps/microservices/upload", {
|
|
609
|
+
schemaVersion: 1,
|
|
610
|
+
service: "upload",
|
|
611
|
+
storageProvider: opts.upload,
|
|
612
|
+
transport: t
|
|
613
|
+
});
|
|
519
614
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
615
|
+
if (opts.example !== "none") {
|
|
616
|
+
await writeJson(targetDir, "apps/microservices/notes", {
|
|
617
|
+
schemaVersion: 1,
|
|
618
|
+
service: "notes",
|
|
619
|
+
dbProvider: opts.dbProvider,
|
|
620
|
+
transport: t
|
|
621
|
+
});
|
|
526
622
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
} catch {
|
|
623
|
+
if (opts.payment !== "none") {
|
|
624
|
+
await writeJson(targetDir, "apps/microservices/payment", {
|
|
625
|
+
schemaVersion: 1,
|
|
626
|
+
service: "payment",
|
|
627
|
+
paymentProvider: opts.payment,
|
|
628
|
+
transport: t
|
|
629
|
+
});
|
|
535
630
|
}
|
|
536
|
-
|
|
537
|
-
"
|
|
538
|
-
|
|
539
|
-
|
|
631
|
+
if (opts.jobs !== "none") {
|
|
632
|
+
await writeJson(targetDir, "apps/microservices/jobs", {
|
|
633
|
+
schemaVersion: 1,
|
|
634
|
+
service: "jobs",
|
|
635
|
+
jobsProvider: opts.jobs
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
const features = [];
|
|
639
|
+
if (opts.example !== "none") features.push("notes");
|
|
640
|
+
if (opts.payment !== "none") features.push("payment");
|
|
641
|
+
if (opts.jobs !== "none") features.push("jobs");
|
|
642
|
+
await writeJson(targetDir, "apps/api", {
|
|
643
|
+
schemaVersion: 1,
|
|
644
|
+
service: "api",
|
|
645
|
+
features,
|
|
646
|
+
transport: t
|
|
647
|
+
});
|
|
648
|
+
await writeJson(targetDir, "apps/client", {
|
|
649
|
+
schemaVersion: 1,
|
|
650
|
+
service: "client",
|
|
651
|
+
ui: opts.ui
|
|
652
|
+
});
|
|
540
653
|
}
|
|
541
654
|
|
|
655
|
+
// src/manifest/wire-auth.ts
|
|
656
|
+
var AUTH = {
|
|
657
|
+
section: MANIFEST.auth,
|
|
658
|
+
providerFile: "apps/microservices/auth/src/app/auth.provider.ts",
|
|
659
|
+
exportConst: "AuthProviderModule",
|
|
660
|
+
msPackageJson: "apps/microservices/auth/package.json",
|
|
661
|
+
envPath: "apps/microservices/auth/.env"
|
|
662
|
+
};
|
|
663
|
+
var writeAuthProvider = (targetDir, provider) => writeProvider(targetDir, AUTH, provider);
|
|
664
|
+
var cleanupUnusedAuth = (targetDir, chosen) => cleanupUnusedAxis(targetDir, AUTH, chosen);
|
|
665
|
+
|
|
666
|
+
// src/manifest/wire-storage.ts
|
|
667
|
+
var STORAGE = {
|
|
668
|
+
section: MANIFEST.storage,
|
|
669
|
+
providerFile: "apps/microservices/upload/src/app/storage.provider.ts",
|
|
670
|
+
exportConst: "StorageProviderModule",
|
|
671
|
+
msPackageJson: "apps/microservices/upload/package.json",
|
|
672
|
+
envPath: "apps/microservices/upload/.env"
|
|
673
|
+
};
|
|
674
|
+
var writeStorageProvider = (targetDir, provider) => writeProvider(targetDir, STORAGE, provider);
|
|
675
|
+
var cleanupUnusedStorage = (targetDir, chosen) => cleanupUnusedAxis(targetDir, STORAGE, chosen);
|
|
676
|
+
|
|
677
|
+
// src/manifest/wire-db.ts
|
|
678
|
+
var DB = {
|
|
679
|
+
section: MANIFEST.db,
|
|
680
|
+
providerFile: "apps/microservices/notes/src/app/db.provider.ts",
|
|
681
|
+
exportConst: "DbProviderModule",
|
|
682
|
+
msPackageJson: "apps/microservices/notes/package.json",
|
|
683
|
+
envPath: "apps/microservices/notes/.env"
|
|
684
|
+
};
|
|
685
|
+
var writeDbProvider = (targetDir, provider) => writeProvider(targetDir, DB, provider);
|
|
686
|
+
var cleanupUnusedDb = (targetDir, chosen) => cleanupUnusedAxis(targetDir, DB, chosen);
|
|
687
|
+
|
|
542
688
|
// src/lib/scaffold-pkg.ts
|
|
543
|
-
import { readFile as
|
|
544
|
-
import { join as
|
|
689
|
+
import { readFile as readFile5, writeFile as writeFile7, mkdir, readdir } from "fs/promises";
|
|
690
|
+
import { join as join7 } from "path";
|
|
545
691
|
async function writePnpmWorkspace(targetDir) {
|
|
546
|
-
const pkgPath =
|
|
547
|
-
const pkg = JSON.parse(await
|
|
692
|
+
const pkgPath = join7(targetDir, "package.json");
|
|
693
|
+
const pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
|
|
548
694
|
const workspaces = pkg.workspaces ?? [];
|
|
549
695
|
const packagesBlock = workspaces.map((p2) => ` - '${p2}'`).join("\n");
|
|
550
696
|
const allowBuilds = [
|
|
@@ -565,10 +711,10 @@ ${packagesBlock}
|
|
|
565
711
|
allowBuilds:
|
|
566
712
|
${allowBuilds}
|
|
567
713
|
`;
|
|
568
|
-
await
|
|
714
|
+
await writeFile7(join7(targetDir, "pnpm-workspace.yaml"), content);
|
|
569
715
|
}
|
|
570
716
|
async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
571
|
-
async function
|
|
717
|
+
async function walk2(dir) {
|
|
572
718
|
const found = [];
|
|
573
719
|
let entries;
|
|
574
720
|
try {
|
|
@@ -578,27 +724,27 @@ async function rewritePnpmWorkspaceDeps(targetDir) {
|
|
|
578
724
|
}
|
|
579
725
|
for (const e of entries) {
|
|
580
726
|
if (e.isDirectory() && e.name !== "node_modules") {
|
|
581
|
-
found.push(...await
|
|
727
|
+
found.push(...await walk2(join7(dir, e.name)));
|
|
582
728
|
} else if (e.isFile() && e.name === "package.json") {
|
|
583
|
-
found.push(
|
|
729
|
+
found.push(join7(dir, e.name));
|
|
584
730
|
}
|
|
585
731
|
}
|
|
586
732
|
return found;
|
|
587
733
|
}
|
|
588
|
-
const pkgFiles = await
|
|
734
|
+
const pkgFiles = await walk2(targetDir);
|
|
589
735
|
for (const f of pkgFiles) {
|
|
590
736
|
try {
|
|
591
|
-
const raw = await
|
|
737
|
+
const raw = await readFile5(f, "utf8");
|
|
592
738
|
const next = raw.replace(/"(@icore\/[^"]+)":\s*"\*"/g, '"$1": "workspace:*"');
|
|
593
|
-
if (next !== raw) await
|
|
739
|
+
if (next !== raw) await writeFile7(f, next);
|
|
594
740
|
} catch {
|
|
595
741
|
}
|
|
596
742
|
}
|
|
597
743
|
}
|
|
598
744
|
async function patchGitignoreForPm(targetDir, pm) {
|
|
599
|
-
const giPath =
|
|
745
|
+
const giPath = join7(targetDir, ".gitignore");
|
|
600
746
|
try {
|
|
601
|
-
let src = await
|
|
747
|
+
let src = await readFile5(giPath, "utf8");
|
|
602
748
|
src = src.replace(/^# Build artifacts.*\ntools\/create-icore\/templates\/\s*\n/m, "");
|
|
603
749
|
if (pm !== "yarn") {
|
|
604
750
|
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 +759,7 @@ async function patchGitignoreForPm(targetDir, pm) {
|
|
|
613
759
|
src += "\n# npm\nnpm-debug.log*\n";
|
|
614
760
|
}
|
|
615
761
|
}
|
|
616
|
-
await
|
|
762
|
+
await writeFile7(giPath, src);
|
|
617
763
|
} catch {
|
|
618
764
|
}
|
|
619
765
|
}
|
|
@@ -628,7 +774,7 @@ async function writeAiFiles(targetDir, opts) {
|
|
|
628
774
|
if (opts.jobs !== "none") activeMSes.push(`jobs (standalone)`);
|
|
629
775
|
const usesSupabase = opts.authProvider === "supabase" || opts.dbProvider === "supabase" || opts.upload === "supabase";
|
|
630
776
|
const usesFirebase = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
631
|
-
await
|
|
777
|
+
await writeFile7(join7(targetDir, "CLAUDE.md"), "@AGENTS.md\n");
|
|
632
778
|
const uiLabel = { shadcn: "shadcn/ui + Tailwind", antd: "Ant Design 6", mui: "MUI 6" }[opts.ui];
|
|
633
779
|
const readme = `# ${opts.projectName}
|
|
634
780
|
|
|
@@ -678,7 +824,7 @@ ${pm === "yarn" ? "yarn remove-notes" : pm === "pnpm" ? "pnpm remove-notes" : "n
|
|
|
678
824
|
|
|
679
825
|
Apache-2.0
|
|
680
826
|
`;
|
|
681
|
-
await
|
|
827
|
+
await writeFile7(join7(targetDir, "README.md"), readme);
|
|
682
828
|
const agents = `# ${opts.projectName} \u2014 Agent Instructions
|
|
683
829
|
|
|
684
830
|
## Stack snapshot
|
|
@@ -759,8 +905,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
|
|
|
759
905
|
- Test behaviour, not implementation. Fake strategies from \`@icore/shared\` (FakeAuthStrategy etc.) serve as test doubles.
|
|
760
906
|
- Run: \`${nx} test <project>\`
|
|
761
907
|
`;
|
|
762
|
-
await
|
|
763
|
-
await mkdir(
|
|
908
|
+
await writeFile7(join7(targetDir, "AGENTS.md"), agents);
|
|
909
|
+
await mkdir(join7(targetDir, ".claude"), { recursive: true });
|
|
764
910
|
const mcpServers = {
|
|
765
911
|
nx: {
|
|
766
912
|
command: "npx",
|
|
@@ -801,8 +947,8 @@ ${opts.upload !== "none" ? `| \`apps/microservices/upload/.env\` | \`STORAGE_PRO
|
|
|
801
947
|
]
|
|
802
948
|
}
|
|
803
949
|
};
|
|
804
|
-
await
|
|
805
|
-
|
|
950
|
+
await writeFile7(
|
|
951
|
+
join7(targetDir, ".claude", "settings.json"),
|
|
806
952
|
JSON.stringify(settings, null, 2) + "\n"
|
|
807
953
|
);
|
|
808
954
|
}
|
|
@@ -826,16 +972,16 @@ async function copyTree(src, dest) {
|
|
|
826
972
|
const entries = await readdir2(src, { withFileTypes: true });
|
|
827
973
|
for (const entry of entries) {
|
|
828
974
|
if (IGNORE_TOP.has(entry.name)) continue;
|
|
829
|
-
const s =
|
|
830
|
-
const d =
|
|
975
|
+
const s = join8(src, entry.name);
|
|
976
|
+
const d = join8(dest, entry.name);
|
|
831
977
|
if (entry.isDirectory()) await copyTree(s, d);
|
|
832
978
|
else if (entry.isFile()) await copyFile(s, d);
|
|
833
979
|
}
|
|
834
980
|
}
|
|
835
981
|
async function selectClientTemplate(targetDir, opts) {
|
|
836
|
-
const templatesRoot =
|
|
837
|
-
const chosen =
|
|
838
|
-
const destClient =
|
|
982
|
+
const templatesRoot = join8(targetDir, "apps/templates");
|
|
983
|
+
const chosen = join8(templatesRoot, `client-${opts.ui}`);
|
|
984
|
+
const destClient = join8(targetDir, "apps/client");
|
|
839
985
|
let chosenUi = opts.ui;
|
|
840
986
|
try {
|
|
841
987
|
const s = await stat(chosen);
|
|
@@ -843,9 +989,9 @@ async function selectClientTemplate(targetDir, opts) {
|
|
|
843
989
|
await copyTree(chosen, destClient);
|
|
844
990
|
} catch {
|
|
845
991
|
chosenUi = "shadcn";
|
|
846
|
-
await copyTree(
|
|
992
|
+
await copyTree(join8(templatesRoot, "client-shadcn"), destClient);
|
|
847
993
|
}
|
|
848
|
-
await
|
|
994
|
+
await rm4(templatesRoot, { recursive: true, force: true });
|
|
849
995
|
await rewriteClientPaths(destClient, chosenUi);
|
|
850
996
|
}
|
|
851
997
|
async function rewriteClientPaths(clientDir, ui) {
|
|
@@ -858,11 +1004,11 @@ async function rewriteClientPaths(clientDir, ui) {
|
|
|
858
1004
|
"eslint.config.mjs"
|
|
859
1005
|
];
|
|
860
1006
|
for (const rel of candidates) {
|
|
861
|
-
const path =
|
|
1007
|
+
const path = join8(clientDir, rel);
|
|
862
1008
|
try {
|
|
863
|
-
const raw = await
|
|
1009
|
+
const raw = await readFile6(path, "utf8");
|
|
864
1010
|
const next = raw.replaceAll("../../../", "../../").replaceAll(`apps/templates/client-${ui}`, "apps/client").replaceAll(`client-${ui}`, "client");
|
|
865
|
-
if (next !== raw) await
|
|
1011
|
+
if (next !== raw) await writeFile8(path, next);
|
|
866
1012
|
} catch {
|
|
867
1013
|
}
|
|
868
1014
|
}
|
|
@@ -878,12 +1024,12 @@ function gitInit(cwd, projectName) {
|
|
|
878
1024
|
}
|
|
879
1025
|
function resolveYarnBin(cwd) {
|
|
880
1026
|
try {
|
|
881
|
-
const yarnrc = readFileSync(
|
|
1027
|
+
const yarnrc = readFileSync(join8(cwd, ".yarnrc.yml"), "utf8");
|
|
882
1028
|
const match = yarnrc.match(/^yarnPath:\s*(.+)$/m);
|
|
883
|
-
if (match?.[1]) return
|
|
1029
|
+
if (match?.[1]) return join8(cwd, match[1].trim());
|
|
884
1030
|
} catch {
|
|
885
1031
|
}
|
|
886
|
-
return
|
|
1032
|
+
return join8(cwd, ".yarn", "releases", "yarn-4.5.0.cjs");
|
|
887
1033
|
}
|
|
888
1034
|
function runInstall(cwd, pm) {
|
|
889
1035
|
if (pm === "yarn") {
|
|
@@ -906,19 +1052,29 @@ async function scaffold(opts, templatesDir) {
|
|
|
906
1052
|
await selectClientTemplate(opts.targetDir, opts);
|
|
907
1053
|
await writeClientEnv(opts.targetDir);
|
|
908
1054
|
if (opts.upload === "none") await removeUploadStack(opts.targetDir);
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
await
|
|
913
|
-
await
|
|
914
|
-
|
|
1055
|
+
await cleanupUnusedFeatures(opts.targetDir, opts);
|
|
1056
|
+
await writeFeaturesWiring(opts.targetDir, opts);
|
|
1057
|
+
await writeNavConfig(opts.targetDir, opts);
|
|
1058
|
+
await cleanupUnusedAuth(opts.targetDir, opts.authProvider);
|
|
1059
|
+
await writeAuthProvider(opts.targetDir, opts.authProvider);
|
|
1060
|
+
if (opts.upload !== "none") {
|
|
1061
|
+
await cleanupUnusedStorage(opts.targetDir, opts.upload);
|
|
1062
|
+
await writeStorageProvider(opts.targetDir, opts.upload);
|
|
1063
|
+
}
|
|
1064
|
+
if (opts.example !== "none") {
|
|
1065
|
+
await cleanupUnusedDb(opts.targetDir, opts.dbProvider);
|
|
1066
|
+
await writeDbProvider(opts.targetDir, opts.dbProvider);
|
|
1067
|
+
}
|
|
915
1068
|
const firebaseUsed = opts.authProvider === "firebase" || opts.dbProvider === "firebase" || opts.upload === "firebase";
|
|
916
1069
|
if (!firebaseUsed) await removeFirebaseAdminLib(opts.targetDir);
|
|
1070
|
+
await pruneRootProviderDeps(opts.targetDir, opts);
|
|
1071
|
+
await writeBlueprintJson(opts.targetDir, opts);
|
|
1072
|
+
await writeServiceBlueprints(opts.targetDir, opts);
|
|
917
1073
|
if (opts.packageManager === "yarn") {
|
|
918
|
-
await
|
|
1074
|
+
await writeFile8(join8(opts.targetDir, "yarn.lock"), "");
|
|
919
1075
|
} else {
|
|
920
|
-
await
|
|
921
|
-
await
|
|
1076
|
+
await rm4(join8(opts.targetDir, ".yarn"), { recursive: true, force: true });
|
|
1077
|
+
await rm4(join8(opts.targetDir, ".yarnrc.yml"), { force: true });
|
|
922
1078
|
}
|
|
923
1079
|
if (opts.packageManager === "pnpm") {
|
|
924
1080
|
await writePnpmWorkspace(opts.targetDir);
|
|
@@ -933,8 +1089,8 @@ async function scaffold(opts, templatesDir) {
|
|
|
933
1089
|
// src/lib/prompts.ts
|
|
934
1090
|
import * as p from "@clack/prompts";
|
|
935
1091
|
import { resolve } from "path";
|
|
936
|
-
import { readFile as
|
|
937
|
-
import { dirname, join as
|
|
1092
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
1093
|
+
import { dirname, join as join9 } from "path";
|
|
938
1094
|
import { fileURLToPath } from "url";
|
|
939
1095
|
function detectPackageManager() {
|
|
940
1096
|
const ua = process.env["npm_config_user_agent"] ?? "";
|
|
@@ -946,7 +1102,7 @@ function detectPackageManager() {
|
|
|
946
1102
|
async function readSelfVersion() {
|
|
947
1103
|
try {
|
|
948
1104
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
949
|
-
const pkgRaw = await
|
|
1105
|
+
const pkgRaw = await readFile7(join9(here, "..", "package.json"), "utf8");
|
|
950
1106
|
const pkg = JSON.parse(pkgRaw);
|
|
951
1107
|
return pkg.version ?? null;
|
|
952
1108
|
} catch {
|
|
@@ -1157,7 +1313,128 @@ Re-run with @latest to refresh:
|
|
|
1157
1313
|
install
|
|
1158
1314
|
};
|
|
1159
1315
|
}
|
|
1316
|
+
|
|
1317
|
+
// src/manifest/audit.ts
|
|
1318
|
+
import { readFile as readFile8, readdir as readdir3 } from "fs/promises";
|
|
1319
|
+
import { join as join10 } from "path";
|
|
1320
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".nx"]);
|
|
1321
|
+
async function walk(dir, out = []) {
|
|
1322
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
1323
|
+
for (const e of entries) {
|
|
1324
|
+
if (e.isDirectory()) {
|
|
1325
|
+
if (!IGNORE_DIRS.has(e.name)) await walk(join10(dir, e.name), out);
|
|
1326
|
+
} else if (/\.(ts|tsx|mjs)$/.test(e.name)) {
|
|
1327
|
+
out.push(join10(dir, e.name));
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
return out;
|
|
1331
|
+
}
|
|
1332
|
+
async function tsconfigAliases(dir) {
|
|
1333
|
+
try {
|
|
1334
|
+
const raw = await readFile8(join10(dir, "tsconfig.base.json"), "utf8");
|
|
1335
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
1336
|
+
for (const m of raw.matchAll(/"(@icore\/[a-z0-9.-]+)"\s*:/g)) {
|
|
1337
|
+
if (m[1]) aliases.add(m[1]);
|
|
1338
|
+
}
|
|
1339
|
+
return aliases;
|
|
1340
|
+
} catch {
|
|
1341
|
+
return /* @__PURE__ */ new Set();
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
var PROVIDER_SDKS = {
|
|
1345
|
+
supabase: ["@supabase/supabase-js"],
|
|
1346
|
+
cloudinary: ["cloudinary"],
|
|
1347
|
+
mongodb: ["mongoose"],
|
|
1348
|
+
firebase: ["firebase-admin", "@icore/firebase-admin"]
|
|
1349
|
+
};
|
|
1350
|
+
async function readBlueprint(dir) {
|
|
1351
|
+
try {
|
|
1352
|
+
return JSON.parse(await readFile8(join10(dir, "blueprint.json"), "utf8"));
|
|
1353
|
+
} catch {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
function forbiddenFromBlueprint(bp) {
|
|
1358
|
+
const chosen = new Set(
|
|
1359
|
+
[bp.authProvider, bp.dbProvider, bp.upload].filter((p2) => Boolean(p2))
|
|
1360
|
+
);
|
|
1361
|
+
const forbidden = [];
|
|
1362
|
+
for (const [provider, sdks] of Object.entries(PROVIDER_SDKS)) {
|
|
1363
|
+
if (!chosen.has(provider)) forbidden.push(...sdks);
|
|
1364
|
+
}
|
|
1365
|
+
return forbidden;
|
|
1366
|
+
}
|
|
1367
|
+
async function allPackageJsons(dir) {
|
|
1368
|
+
const out = [];
|
|
1369
|
+
const root = join10(dir, "package.json");
|
|
1370
|
+
out.push(root);
|
|
1371
|
+
async function walk2(d) {
|
|
1372
|
+
let entries;
|
|
1373
|
+
try {
|
|
1374
|
+
entries = await readdir3(d, { withFileTypes: true });
|
|
1375
|
+
} catch {
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
for (const e of entries) {
|
|
1379
|
+
if (e.isDirectory()) {
|
|
1380
|
+
if (!IGNORE_DIRS.has(e.name)) await walk2(join10(d, e.name));
|
|
1381
|
+
} else if (e.name === "package.json") {
|
|
1382
|
+
out.push(join10(d, e.name));
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
await walk2(join10(dir, "apps"));
|
|
1387
|
+
return out;
|
|
1388
|
+
}
|
|
1389
|
+
async function depKeys(pkgPath) {
|
|
1390
|
+
try {
|
|
1391
|
+
const pkg = JSON.parse(await readFile8(pkgPath, "utf8"));
|
|
1392
|
+
return /* @__PURE__ */ new Set([
|
|
1393
|
+
...Object.keys(pkg.dependencies ?? {}),
|
|
1394
|
+
...Object.keys(pkg.devDependencies ?? {})
|
|
1395
|
+
]);
|
|
1396
|
+
} catch {
|
|
1397
|
+
return /* @__PURE__ */ new Set();
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
var ICORE_IMPORT = /(?:from|import\()\s*['"](@icore\/[a-z0-9.-]+)/g;
|
|
1401
|
+
async function auditProject(dir, opts = {}) {
|
|
1402
|
+
const violations = [];
|
|
1403
|
+
const aliases = await tsconfigAliases(dir);
|
|
1404
|
+
for (const file of await walk(dir)) {
|
|
1405
|
+
const src = await readFile8(file, "utf8");
|
|
1406
|
+
for (const m of src.matchAll(ICORE_IMPORT)) {
|
|
1407
|
+
const alias = m[1];
|
|
1408
|
+
if (alias && !aliases.has(alias)) {
|
|
1409
|
+
violations.push({
|
|
1410
|
+
kind: "import-of-absent-lib",
|
|
1411
|
+
detail: `${file} imports ${alias} (no tsconfig path \u2192 lib absent)`
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
const bp = await readBlueprint(dir);
|
|
1417
|
+
const forbidden = /* @__PURE__ */ new Set([
|
|
1418
|
+
...opts.forbiddenDeps ?? [],
|
|
1419
|
+
...bp ? forbiddenFromBlueprint(bp) : []
|
|
1420
|
+
]);
|
|
1421
|
+
if (forbidden.size > 0) {
|
|
1422
|
+
for (const pkgPath of await allPackageJsons(dir)) {
|
|
1423
|
+
const deps = await depKeys(pkgPath);
|
|
1424
|
+
for (const f of forbidden) {
|
|
1425
|
+
if (deps.has(f)) {
|
|
1426
|
+
violations.push({
|
|
1427
|
+
kind: "forbidden-dep",
|
|
1428
|
+
detail: `${pkgPath} keeps forbidden dep ${f}`
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
return violations;
|
|
1435
|
+
}
|
|
1160
1436
|
export {
|
|
1437
|
+
auditProject,
|
|
1161
1438
|
collectOptions,
|
|
1162
1439
|
pmRun,
|
|
1163
1440
|
scaffold
|