lapeh 2.2.6 → 2.2.8
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/bin/index.js +229 -23
- package/package.json +6 -6
package/bin/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
@@ -37,10 +37,14 @@ function runDev() {
|
|
|
37
37
|
const tsNodePath = require.resolve('ts-node/register');
|
|
38
38
|
const tsConfigPathsPath = require.resolve('tsconfig-paths/register');
|
|
39
39
|
|
|
40
|
-
// Resolve bootstrap file
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
// Resolve bootstrap file
|
|
41
|
+
// 1. Try to find it in the current project's node_modules (preferred)
|
|
42
|
+
const localBootstrapPath = path.join(process.cwd(), 'node_modules/lapeh/lib/bootstrap.ts');
|
|
43
|
+
|
|
44
|
+
// 2. Fallback to relative to this script (if running from source or global cache without local install)
|
|
45
|
+
const fallbackBootstrapPath = path.resolve(__dirname, '../lib/bootstrap.ts');
|
|
46
|
+
|
|
47
|
+
const bootstrapPath = fs.existsSync(localBootstrapPath) ? localBootstrapPath : fallbackBootstrapPath;
|
|
44
48
|
|
|
45
49
|
// We execute a script that requires ts-node to run lib/bootstrap.ts
|
|
46
50
|
execSync(`npx nodemon --watch src --watch lib --ext ts,json --exec "node -r ${tsNodePath} -r ${tsConfigPathsPath} ${bootstrapPath}"`, { stdio: 'inherit' });
|
|
@@ -183,7 +187,7 @@ async function upgradeProject() {
|
|
|
183
187
|
|
|
184
188
|
// Update paths
|
|
185
189
|
if (tsconfig.compilerOptions && tsconfig.compilerOptions.paths) {
|
|
186
|
-
tsconfig.compilerOptions.paths["@lapeh/*"] = ["node_modules/lapeh/lib/*"];
|
|
190
|
+
tsconfig.compilerOptions.paths["@lapeh/*"] = ["./node_modules/lapeh/lib/*"];
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
// Add ts-node ignore configuration
|
|
@@ -210,10 +214,35 @@ async function upgradeProject() {
|
|
|
210
214
|
function createProject() {
|
|
211
215
|
const projectName = args.find(arg => !arg.startsWith('-'));
|
|
212
216
|
const isFull = args.includes('--full');
|
|
217
|
+
// Allow -y alias for --defaults
|
|
218
|
+
const useDefaults = args.includes('--defaults') || args.includes('-y');
|
|
219
|
+
|
|
220
|
+
// Helper to parse arguments like --key=value
|
|
221
|
+
const getArg = (key) => {
|
|
222
|
+
const prefix = `--${key}=`;
|
|
223
|
+
const arg = args.find(a => a.startsWith(prefix));
|
|
224
|
+
return arg ? arg.substring(prefix.length) : undefined;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const dbTypeArg = getArg('db-type');
|
|
228
|
+
const dbHostArg = getArg('db-host');
|
|
229
|
+
const dbPortArg = getArg('db-port');
|
|
230
|
+
const dbUserArg = getArg('db-user');
|
|
231
|
+
const dbPassArg = getArg('db-pass');
|
|
232
|
+
const dbNameArg = getArg('db-name');
|
|
213
233
|
|
|
214
234
|
if (!projectName) {
|
|
215
235
|
console.error('❌ Please specify the project name:');
|
|
216
|
-
console.error(' npx lapeh-cli <project-name> [--full]');
|
|
236
|
+
console.error(' npx lapeh-cli <project-name> [--full] [--defaults|-y]');
|
|
237
|
+
console.error(' Options:');
|
|
238
|
+
console.error(' --full : Run full setup including seed and dev server');
|
|
239
|
+
console.error(' --defaults, -y: Use default configuration (can be overridden with args)');
|
|
240
|
+
console.error(' --db-type= : pgsql | mysql');
|
|
241
|
+
console.error(' --db-host= : Database host');
|
|
242
|
+
console.error(' --db-port= : Database port');
|
|
243
|
+
console.error(' --db-user= : Database user');
|
|
244
|
+
console.error(' --db-pass= : Database password');
|
|
245
|
+
console.error(' --db-name= : Database name');
|
|
217
246
|
console.error(' OR');
|
|
218
247
|
console.error(' npx lapeh-cli upgrade (inside existing project)');
|
|
219
248
|
process.exit(1);
|
|
@@ -266,27 +295,65 @@ function createProject() {
|
|
|
266
295
|
|
|
267
296
|
// --- DATABASE SELECTION ---
|
|
268
297
|
console.log("\n--- Database Configuration ---");
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
298
|
+
let dbType, host, port, user, password, dbName;
|
|
299
|
+
|
|
300
|
+
if (useDefaults) {
|
|
301
|
+
console.log("ℹ️ Using default configuration (--defaults)...");
|
|
302
|
+
|
|
303
|
+
// Default to PostgreSQL
|
|
304
|
+
dbType = { key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" };
|
|
305
|
+
host = "localhost";
|
|
306
|
+
port = "5432";
|
|
307
|
+
user = "postgres";
|
|
308
|
+
password = "password";
|
|
309
|
+
dbName = projectName.replace(/-/g, '_');
|
|
310
|
+
|
|
311
|
+
// Override with CLI args
|
|
312
|
+
if (dbTypeArg) {
|
|
313
|
+
if (dbTypeArg.toLowerCase() === 'mysql') {
|
|
314
|
+
dbType = { key: "mysql", label: "MySQL", provider: "mysql", defaultPort: "3306" };
|
|
315
|
+
port = "3306";
|
|
316
|
+
} else if (dbTypeArg.toLowerCase() === 'pgsql') {
|
|
317
|
+
dbType = { key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" };
|
|
318
|
+
port = "5432";
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (dbHostArg) host = dbHostArg;
|
|
323
|
+
if (dbPortArg) port = dbPortArg;
|
|
324
|
+
if (dbUserArg) user = dbUserArg;
|
|
325
|
+
if (dbPassArg) password = dbPassArg;
|
|
326
|
+
if (dbNameArg) dbName = dbNameArg;
|
|
327
|
+
|
|
328
|
+
} else {
|
|
329
|
+
dbType = await selectOption("Database apa yang akan digunakan?", [
|
|
330
|
+
{ key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" },
|
|
331
|
+
{ key: "mysql", label: "MySQL", provider: "mysql", defaultPort: "3306" },
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
host = await ask("Database Host", "localhost");
|
|
335
|
+
port = await ask("Database Port", dbType.defaultPort);
|
|
336
|
+
user = await ask("Database User", "root");
|
|
337
|
+
password = await ask("Database Password", "");
|
|
338
|
+
dbName = await ask("Database Name", projectName.replace(/-/g, '_')); // Default db name based on project name
|
|
339
|
+
}
|
|
273
340
|
|
|
274
341
|
let dbUrl = "";
|
|
275
342
|
let dbProvider = dbType.provider;
|
|
276
343
|
|
|
277
|
-
const host = await ask("Database Host", "localhost");
|
|
278
|
-
const port = await ask("Database Port", dbType.defaultPort);
|
|
279
|
-
const user = await ask("Database User", "root");
|
|
280
|
-
const password = await ask("Database Password", "");
|
|
281
|
-
const dbName = await ask("Database Name", projectName.replace(/-/g, '_')); // Default db name based on project name
|
|
282
|
-
|
|
283
344
|
if (dbType.key === "pgsql") {
|
|
284
345
|
dbUrl = `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=public`;
|
|
285
346
|
} else {
|
|
286
347
|
dbUrl = `mysql://${user}:${password}@${host}:${port}/${dbName}`;
|
|
287
348
|
}
|
|
288
349
|
|
|
289
|
-
|
|
350
|
+
if (!useDefaults) {
|
|
351
|
+
rl.close();
|
|
352
|
+
} else {
|
|
353
|
+
// If we didn't use rl, we might not need to close it if we didn't open it?
|
|
354
|
+
// Actually rl is created at the top. We should close it.
|
|
355
|
+
rl.close();
|
|
356
|
+
}
|
|
290
357
|
|
|
291
358
|
// List of files/folders to exclude
|
|
292
359
|
const ignoreList = [
|
|
@@ -346,6 +413,10 @@ function createProject() {
|
|
|
346
413
|
// Ensure prisma CLI is available in devDependencies for the new project
|
|
347
414
|
packageJson.devDependencies = packageJson.devDependencies || {};
|
|
348
415
|
packageJson.devDependencies["prisma"] = "7.2.0";
|
|
416
|
+
|
|
417
|
+
// Add missing types for dev
|
|
418
|
+
packageJson.devDependencies["@types/express"] = "^5.0.0";
|
|
419
|
+
packageJson.devDependencies["@types/compression"] = "^1.7.5";
|
|
349
420
|
|
|
350
421
|
packageJson.version = '1.0.0';
|
|
351
422
|
packageJson.description = 'Generated by lapeh';
|
|
@@ -355,6 +426,7 @@ function createProject() {
|
|
|
355
426
|
// Update scripts to use lapeh binary
|
|
356
427
|
packageJson.scripts = {
|
|
357
428
|
...packageJson.scripts,
|
|
429
|
+
"postinstall": "node scripts/compile-schema.js && prisma generate",
|
|
358
430
|
"dev": "lapeh dev",
|
|
359
431
|
"start": "lapeh start",
|
|
360
432
|
"build": "lapeh build",
|
|
@@ -373,9 +445,12 @@ function createProject() {
|
|
|
373
445
|
|
|
374
446
|
// Update paths
|
|
375
447
|
if (tsconfig.compilerOptions && tsconfig.compilerOptions.paths) {
|
|
376
|
-
tsconfig.compilerOptions.paths["@lapeh/*"] = ["node_modules/lapeh/lib/*"];
|
|
448
|
+
tsconfig.compilerOptions.paths["@lapeh/*"] = ["./node_modules/lapeh/lib/*"];
|
|
377
449
|
}
|
|
378
450
|
|
|
451
|
+
// Add baseUrl
|
|
452
|
+
tsconfig.compilerOptions.baseUrl = ".";
|
|
453
|
+
|
|
379
454
|
// Add ts-node ignore configuration
|
|
380
455
|
tsconfig["ts-node"] = {
|
|
381
456
|
"ignore": ["node_modules/(?!lapeh)"]
|
|
@@ -384,6 +459,38 @@ function createProject() {
|
|
|
384
459
|
fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
|
|
385
460
|
}
|
|
386
461
|
|
|
462
|
+
// Configure prisma.config.ts to use tsconfig-paths
|
|
463
|
+
const prismaConfigPath = path.join(projectDir, 'prisma.config.ts');
|
|
464
|
+
if (fs.existsSync(prismaConfigPath)) {
|
|
465
|
+
console.log('🔧 Configuring prisma.config.ts...');
|
|
466
|
+
let prismaConfigContent = fs.readFileSync(prismaConfigPath, 'utf8');
|
|
467
|
+
prismaConfigContent = prismaConfigContent.replace(
|
|
468
|
+
/seed:\s*"ts-node\s+prisma\/seed\.ts"/g,
|
|
469
|
+
'seed: "ts-node -r tsconfig-paths/register prisma/seed.ts"'
|
|
470
|
+
);
|
|
471
|
+
fs.writeFileSync(prismaConfigPath, prismaConfigContent);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Configure prisma/seed.ts imports
|
|
475
|
+
const prismaSeedPath = path.join(projectDir, 'prisma', 'seed.ts');
|
|
476
|
+
if (fs.existsSync(prismaSeedPath)) {
|
|
477
|
+
console.log('🔧 Configuring prisma/seed.ts...');
|
|
478
|
+
let seedContent = fs.readFileSync(prismaSeedPath, 'utf8');
|
|
479
|
+
|
|
480
|
+
// Add dotenv config if missing
|
|
481
|
+
if (!seedContent.includes('dotenv.config()')) {
|
|
482
|
+
seedContent = 'import dotenv from "dotenv";\ndotenv.config();\n\n' + seedContent;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Update import path
|
|
486
|
+
seedContent = seedContent.replace(
|
|
487
|
+
/import\s+{\s*prisma\s*}\s+from\s+["']@lapeh\/core\/database["']/,
|
|
488
|
+
'import { prisma } from "@lapeh/core/database"'
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
fs.writeFileSync(prismaSeedPath, seedContent);
|
|
492
|
+
}
|
|
493
|
+
|
|
387
494
|
// Create .env from .env.example with correct DB config
|
|
388
495
|
console.log('⚙️ Configuring environment...');
|
|
389
496
|
const envExamplePath = path.join(projectDir, '.env.example');
|
|
@@ -446,14 +553,113 @@ function createProject() {
|
|
|
446
553
|
console.log(' Compiling schema...');
|
|
447
554
|
execSync('node scripts/compile-schema.js', { cwd: projectDir, stdio: 'inherit' });
|
|
448
555
|
|
|
449
|
-
console.log(' Generating Prisma Client...');
|
|
450
|
-
execSync('npx prisma generate', { cwd: projectDir, stdio: 'inherit' });
|
|
451
|
-
|
|
452
556
|
// Try to migrate (this will create the DB if it doesn't exist)
|
|
453
557
|
console.log(' Running migration (creates DB if missing)...');
|
|
454
558
|
execSync('npx prisma migrate dev --name init_setup', { cwd: projectDir, stdio: 'inherit' });
|
|
455
559
|
|
|
456
|
-
// Seed
|
|
560
|
+
// Seed (Users & Roles are mandatory, Pets are demo data)
|
|
561
|
+
console.log(' Seeding mandatory data (Users, Roles, Permissions)...');
|
|
562
|
+
// Always seed mandatory data by default or check if --full is meant for demo data only?
|
|
563
|
+
// Based on user request: "user seeder dan pets itu selalu ada karena default, jadi seharusnya tidak digenerate saat user buat project baru"
|
|
564
|
+
// Wait, user says: "user seeder and pets are always there because default, so it SHOULD NOT be generated when user creates new project, UNLESS user starts using framework and adding new tables"
|
|
565
|
+
|
|
566
|
+
// Interpretation: The user implies that `prisma generate` and `prisma migrate` (which creates the physical tables and client code)
|
|
567
|
+
// MIGHT be redundant if the core framework already provides a pre-built client for the default models (Users, Pets).
|
|
568
|
+
// However, Prisma doesn't work like that easily because the Client is generated into node_modules/@prisma/client.
|
|
569
|
+
|
|
570
|
+
// If the user means "don't run migration/seed unless needed", we still need to run them to have a working app.
|
|
571
|
+
// Re-reading carefully: "masalahnya user seeder dan pets itu selalu ada karane default ,jadi seharusnya tidak digenerate saat user buuat project baru , kecuali user mulai menggunakan framework ini dan menambahkan table baru untuk developmen"
|
|
572
|
+
|
|
573
|
+
// AH! The user might mean: Since User/Pets are CORE models, the Prisma Client for them should ALREADY exist or be pre-packaged,
|
|
574
|
+
// so we don't need to run `prisma generate` during project creation?
|
|
575
|
+
// OR: The user means the *migration files* shouldn't be generated?
|
|
576
|
+
|
|
577
|
+
// "seharusnya tidak digenerate saat user buuat project baru" -> The user is annoyed by the `prisma generate` and `migration` step taking time?
|
|
578
|
+
// But we need the DB tables.
|
|
579
|
+
|
|
580
|
+
// Let's assume the user wants to SKIP the `prisma generate` and `migrate` step if possible,
|
|
581
|
+
// but that's impossible for a fresh project connecting to a fresh DB.
|
|
582
|
+
|
|
583
|
+
// Alternative interpretation: The user thinks `prisma generate` is only for NEW tables.
|
|
584
|
+
// But `prisma generate` is required to create the client library itself.
|
|
585
|
+
|
|
586
|
+
// Let's look at the "Pets" part. Maybe the user considers Pets as "bloat" that shouldn't be there by default?
|
|
587
|
+
// "user seeder dan pets itu selalu ada karane default"
|
|
588
|
+
|
|
589
|
+
// Wait, maybe the user is saying: "Since these are default, why do we need to RE-generate/RE-migrate them every time? Can't we just have them ready?"
|
|
590
|
+
// Answer: No, because every user has a different DB connection string.
|
|
591
|
+
|
|
592
|
+
// Let's explain this to the user. Prisma Client is NOT a static library like `lodash`. It is generated based on the schema.
|
|
593
|
+
// If we don't generate it, `import { prisma }` will fail because `node_modules/@prisma/client` will be empty.
|
|
594
|
+
|
|
595
|
+
// However, we CAN optimize.
|
|
596
|
+
// Maybe the user is asking: "Why do we generate migration files (`prisma/migrations`) for default tables?"
|
|
597
|
+
// We can ship the project WITH the migration folder for the default tables already there!
|
|
598
|
+
// If we include `prisma/migrations` in the template, `prisma migrate dev` will see they exist and just apply them,
|
|
599
|
+
// instead of creating a NEW migration `init_setup`.
|
|
600
|
+
|
|
601
|
+
// Let's try to verify if `prisma/migrations` is in the ignore list.
|
|
602
|
+
// Line 367: `'prisma/migrations', // Exclude existing migrations`
|
|
603
|
+
// Yes, we are explicitly excluding them!
|
|
604
|
+
|
|
605
|
+
// If we INCLUDE the base migrations in the template, then we don't need to create a new migration on init.
|
|
606
|
+
// We just run `prisma migrate deploy` (faster) or `dev` (which will see it's in sync).
|
|
607
|
+
|
|
608
|
+
// Let's change the strategy:
|
|
609
|
+
// 1. Remove `prisma/migrations` from ignore list.
|
|
610
|
+
// 2. But wait, the user's template might have dirty migrations.
|
|
611
|
+
|
|
612
|
+
// Let's assume the user wants me to explain/fix the perception.
|
|
613
|
+
// But wait, the user says "seharusnya tidak digenerate".
|
|
614
|
+
|
|
615
|
+
// Let's stick to the most robust interpretation:
|
|
616
|
+
// The user wants to avoid the overhead of generation for default stuff.
|
|
617
|
+
// But generation is fast (ms). Migration is slow.
|
|
618
|
+
|
|
619
|
+
// If the user means the `seed` data (Pets/Users):
|
|
620
|
+
// "user seeder dan pets itu selalu ada... jadi seharusnya tidak digenerate"
|
|
621
|
+
// Maybe the user means: Don't SEED them by default?
|
|
622
|
+
// "kecuali user mulai menggunakan framework ini dan menambahkan table baru"
|
|
623
|
+
|
|
624
|
+
// Let's assume the user is talking about the `prisma generate` process being redundant for default models.
|
|
625
|
+
// I need to explain that it IS required.
|
|
626
|
+
|
|
627
|
+
// BUT, I can make the process cleaner.
|
|
628
|
+
// If I look at the previous context: "postinstall" script.
|
|
629
|
+
// The user is replying to my explanation about "postinstall".
|
|
630
|
+
|
|
631
|
+
// User: "The default models (User/Pets) are always there. So they shouldn't need generation when creating a project. Generation should only happen when I add NEW tables."
|
|
632
|
+
|
|
633
|
+
// This is a misunderstanding of how Prisma works.
|
|
634
|
+
// Prisma Client is NOT installed via npm registry with the models pre-baked.
|
|
635
|
+
// It is an empty shell that fills itself when you run `prisma generate`.
|
|
636
|
+
|
|
637
|
+
// My previous explanation might have been unclear.
|
|
638
|
+
// I should clarify: "Generate is mandatory to install the library itself".
|
|
639
|
+
|
|
640
|
+
// HOWEVER, maybe I can PRE-GENERATE it?
|
|
641
|
+
// No, because it depends on the OS/Architecture of the user's machine.
|
|
642
|
+
|
|
643
|
+
// Let's try to satisfy the user's request by skipping the explicit `prisma generate` step in the CLI
|
|
644
|
+
// IF we rely on `prisma migrate dev` to do it automatically (it does generate client under the hood).
|
|
645
|
+
// Line 557: `execSync('npx prisma generate'...)`
|
|
646
|
+
// Line 561: `execSync('npx prisma migrate dev'...)` -> This AUTOMATICALLY runs generate.
|
|
647
|
+
|
|
648
|
+
// So line 557 is redundant! removing it speeds things up and reduces "generation" noise.
|
|
649
|
+
|
|
650
|
+
// Also, about "seeder":
|
|
651
|
+
// "seharusnya tidak digenerate saat user buuat project baru"
|
|
652
|
+
// If the user means the SEED FILE content.
|
|
653
|
+
// We are copying `prisma/seed.ts` from template.
|
|
654
|
+
|
|
655
|
+
// Let's remove the redundant `prisma generate` call in line 557.
|
|
656
|
+
// And I will explain to the user WHY generation is still needed (internally) but I removed the explicit step.
|
|
657
|
+
|
|
658
|
+
// Wait, if I remove `prisma generate`, `node scripts/compile-schema.js` must run before migrate.
|
|
659
|
+
// It does (Line 554).
|
|
660
|
+
|
|
661
|
+
// Let's remove the explicit `prisma generate` logging and command.
|
|
662
|
+
|
|
457
663
|
if (isFull) {
|
|
458
664
|
console.log(' Seeding database...');
|
|
459
665
|
execSync('npm run db:seed', { cwd: projectDir, stdio: 'inherit' });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lapeh",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.8",
|
|
4
4
|
"description": "Framework API Express yang siap pakai (Standardized)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -88,10 +88,7 @@
|
|
|
88
88
|
"winston": "^3.19.0",
|
|
89
89
|
"winston-daily-rotate-file": "^5.0.0",
|
|
90
90
|
"zod": "3.23.8",
|
|
91
|
-
"prisma": "7.2.0"
|
|
92
|
-
},
|
|
93
|
-
"devDependencies": {
|
|
94
|
-
"@eslint/js": "^9.39.2",
|
|
91
|
+
"prisma": "7.2.0",
|
|
95
92
|
"@types/bcryptjs": "2.4.6",
|
|
96
93
|
"@types/compression": "^1.8.1",
|
|
97
94
|
"@types/cors": "2.8.19",
|
|
@@ -100,7 +97,10 @@
|
|
|
100
97
|
"@types/multer": "^2.0.0",
|
|
101
98
|
"@types/node": "25.0.3",
|
|
102
99
|
"@types/pg": "8.16.0",
|
|
103
|
-
"@types/uuid": "10.0.0"
|
|
100
|
+
"@types/uuid": "10.0.0"
|
|
101
|
+
},
|
|
102
|
+
"devDependencies": {
|
|
103
|
+
"@eslint/js": "^9.39.2",
|
|
104
104
|
"eslint": "^9.39.2",
|
|
105
105
|
"globals": "^16.5.0",
|
|
106
106
|
"typescript-eslint": "^8.50.1"
|