lapeh 1.0.5 → 1.0.7

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/.env.example CHANGED
@@ -1,6 +1,6 @@
1
1
  PORT=4000
2
2
  DATABASE_PROVIDER="postgresql"
3
- DATABASE_URL="postgresql://roby:12341234@localhost:5432/db_contact?schema=public"
3
+ DATABASE_URL="postgresql://roby:12341234@localhost:5432/db_example_test?schema=public"
4
4
  JWT_SECRET="replace_this_with_a_secure_random_string"
5
5
 
6
6
  # redis example:
package/bin/index.js CHANGED
@@ -3,12 +3,15 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { execSync } = require('child_process');
6
+ const readline = require('readline');
6
7
 
7
- const projectName = process.argv[2];
8
+ const args = process.argv.slice(2);
9
+ const projectName = args.find(arg => !arg.startsWith('-'));
10
+ const isFull = args.includes('--full');
8
11
 
9
12
  if (!projectName) {
10
13
  console.error('āŒ Please specify the project name:');
11
- console.error(' npx lapeh-cli <project-name>');
14
+ console.error(' npx lapeh-cli <project-name> [--full]');
12
15
  process.exit(1);
13
16
  }
14
17
 
@@ -21,89 +24,210 @@ if (fs.existsSync(projectDir)) {
21
24
  process.exit(1);
22
25
  }
23
26
 
24
- console.log(`šŸš€ Creating a new API Lapeh project in ${projectDir}...`);
25
- fs.mkdirSync(projectDir);
26
-
27
- // List of files/folders to exclude
28
- const ignoreList = [
29
- 'node_modules',
30
- 'dist',
31
- '.git',
32
- '.env',
33
- 'bin', // Don't copy the CLI script itself
34
- 'package-lock.json',
35
- '.DS_Store',
36
- projectName // Don't copy the destination folder itself if creating inside the template
37
- ];
38
-
39
- function copyDir(src, dest) {
40
- const entries = fs.readdirSync(src, { withFileTypes: true });
41
-
42
- for (const entry of entries) {
43
- const srcPath = path.join(src, entry.name);
44
- const destPath = path.join(dest, entry.name);
45
-
46
- if (ignoreList.includes(entry.name)) {
47
- continue;
48
- }
49
-
50
- if (entry.isDirectory()) {
51
- fs.mkdirSync(destPath);
52
- copyDir(srcPath, destPath);
27
+ // Setup readline interface
28
+ const rl = readline.createInterface({
29
+ input: process.stdin,
30
+ output: process.stdout,
31
+ });
32
+
33
+ const ask = (query, defaultVal) => {
34
+ return new Promise((resolve) => {
35
+ rl.question(`${query} ${defaultVal ? `[${defaultVal}]` : ""}: `, (answer) => {
36
+ resolve(answer.trim() || defaultVal);
37
+ });
38
+ });
39
+ };
40
+
41
+ const selectOption = async (query, options) => {
42
+ console.log(query);
43
+ options.forEach((opt, idx) => {
44
+ console.log(` [${opt.key}] ${opt.label}`);
45
+ });
46
+
47
+ while (true) {
48
+ const answer = await ask(">", options[0].key);
49
+ const selected = options.find(o => o.key.toLowerCase() === answer.toLowerCase());
50
+ if (selected) return selected;
51
+
52
+ const byLabel = options.find(o => o.label.toLowerCase().includes(answer.toLowerCase()));
53
+ if (byLabel) return byLabel;
54
+
55
+ console.log("Pilihan tidak valid. Silakan coba lagi.");
56
+ }
57
+ };
58
+
59
+ (async () => {
60
+ console.log(`šŸš€ Creating a new API Lapeh project in ${projectDir}...`);
61
+ fs.mkdirSync(projectDir);
62
+
63
+ // --- DATABASE SELECTION ---
64
+ console.log("\n--- Database Configuration ---");
65
+ const dbType = await selectOption("Database apa yang akan digunakan?", [
66
+ { key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" },
67
+ { key: "mysql", label: "MySQL", provider: "mysql", defaultPort: "3306" },
68
+ { key: "mariadb", label: "MariaDB", provider: "mysql", defaultPort: "3306" },
69
+ { key: "sqlite", label: "SQLite", provider: "sqlite", defaultPort: "" },
70
+ ]);
71
+
72
+ let dbUrl = "";
73
+ let dbProvider = dbType.provider;
74
+
75
+ if (dbType.key === "sqlite") {
76
+ dbUrl = "file:./dev.db";
77
+ } else {
78
+ const host = await ask("Database Host", "localhost");
79
+ const port = await ask("Database Port", dbType.defaultPort);
80
+ const user = await ask("Database User", "root");
81
+ const password = await ask("Database Password", "");
82
+ const dbName = await ask("Database Name", projectName.replace(/-/g, '_')); // Default db name based on project name
83
+
84
+ if (dbType.key === "pgsql") {
85
+ dbUrl = `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=public`;
53
86
  } else {
54
- fs.copyFileSync(srcPath, destPath);
87
+ dbUrl = `mysql://${user}:${password}@${host}:${port}/${dbName}`;
55
88
  }
56
89
  }
57
- }
58
90
 
59
- console.log('šŸ“‚ Copying template files...');
60
- copyDir(templateDir, projectDir);
91
+ rl.close();
92
+
93
+ // List of files/folders to exclude
94
+ const ignoreList = [
95
+ 'node_modules',
96
+ 'dist',
97
+ '.git',
98
+ '.env',
99
+ 'bin', // Don't copy the CLI script itself
100
+ 'package-lock.json',
101
+ '.DS_Store',
102
+ projectName // Don't copy the destination folder itself if creating inside the template
103
+ ];
104
+
105
+ function copyDir(src, dest) {
106
+ const entries = fs.readdirSync(src, { withFileTypes: true });
107
+
108
+ for (const entry of entries) {
109
+ const srcPath = path.join(src, entry.name);
110
+ const destPath = path.join(dest, entry.name);
111
+
112
+ if (ignoreList.includes(entry.name)) {
113
+ continue;
114
+ }
115
+
116
+ if (entry.isDirectory()) {
117
+ fs.mkdirSync(destPath);
118
+ copyDir(srcPath, destPath);
119
+ } else {
120
+ fs.copyFileSync(srcPath, destPath);
121
+ }
122
+ }
123
+ }
61
124
 
62
- // Update package.json
63
- console.log('šŸ“ Updating package.json...');
64
- const packageJsonPath = path.join(projectDir, 'package.json');
65
- const packageJson = require(packageJsonPath);
125
+ console.log('\nšŸ“‚ Copying template files...');
126
+ copyDir(templateDir, projectDir);
127
+
128
+ // Update package.json
129
+ console.log('šŸ“ Updating package.json...');
130
+ const packageJsonPath = path.join(projectDir, 'package.json');
131
+ const packageJson = require(packageJsonPath);
132
+
133
+ packageJson.name = projectName;
134
+ // Add lapeh framework version to dependencies to track it like react-router
135
+ packageJson.dependencies = packageJson.dependencies || {};
136
+ packageJson.dependencies["lapeh"] = packageJson.version;
137
+
138
+ packageJson.version = '1.0.0';
139
+ packageJson.description = 'Generated by lapeh';
140
+ delete packageJson.bin; // Remove the bin entry from the generated project
141
+ delete packageJson.repository; // Remove repository info if specific to the template
142
+
143
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
144
+
145
+ // Create .env from .env.example with correct DB config
146
+ console.log('āš™ļø Configuring environment...');
147
+ const envExamplePath = path.join(projectDir, '.env.example');
148
+ const envPath = path.join(projectDir, '.env');
149
+ const prismaBaseFile = path.join(projectDir, "prisma", "base.prisma");
150
+
151
+ if (fs.existsSync(envExamplePath)) {
152
+ let envContent = fs.readFileSync(envExamplePath, 'utf8');
153
+
154
+ // Replace DATABASE_URL and DATABASE_PROVIDER
155
+ if (envContent.includes("DATABASE_URL=")) {
156
+ envContent = envContent.replace(/DATABASE_URL=".+"/g, `DATABASE_URL="${dbUrl}"`);
157
+ envContent = envContent.replace(/DATABASE_URL=.+/g, `DATABASE_URL="${dbUrl}"`);
158
+ } else {
159
+ envContent += `\nDATABASE_URL="${dbUrl}"`;
160
+ }
66
161
 
67
- packageJson.name = projectName;
68
- // Add lapeh framework version to dependencies to track it like react-router
69
- packageJson.dependencies = packageJson.dependencies || {};
70
- packageJson.dependencies["lapeh"] = packageJson.version;
162
+ if (envContent.includes("DATABASE_PROVIDER=")) {
163
+ envContent = envContent.replace(/DATABASE_PROVIDER=".+"/g, `DATABASE_PROVIDER="${dbProvider}"`);
164
+ envContent = envContent.replace(/DATABASE_PROVIDER=.+/g, `DATABASE_PROVIDER="${dbProvider}"`);
165
+ } else {
166
+ envContent += `\nDATABASE_PROVIDER="${dbProvider}"`;
167
+ }
71
168
 
72
- packageJson.version = '1.0.0';
73
- packageJson.description = 'Generated by lapeh';
74
- delete packageJson.bin; // Remove the bin entry from the generated project
75
- delete packageJson.repository; // Remove repository info if specific to the template
169
+ fs.writeFileSync(envPath, envContent);
170
+ }
76
171
 
77
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
172
+ // Update prisma/base.prisma
173
+ console.log("šŸ“„ Updating prisma/base.prisma...");
174
+ if (fs.existsSync(prismaBaseFile)) {
175
+ let baseContent = fs.readFileSync(prismaBaseFile, "utf8");
176
+ // Replace provider in datasource block
177
+ baseContent = baseContent.replace(
178
+ /(datasource\s+db\s+\{[\s\S]*?provider\s*=\s*")([^"]+)(")/,
179
+ `$1${dbProvider}$3`
180
+ );
181
+ fs.writeFileSync(prismaBaseFile, baseContent);
182
+ }
78
183
 
79
- // Create .env from .env.example
80
- console.log('āš™ļø Configuring environment...');
81
- const envExamplePath = path.join(projectDir, '.env.example');
82
- const envPath = path.join(projectDir, '.env');
184
+ // Install dependencies
185
+ console.log('šŸ“¦ Installing dependencies (this might take a while)...');
186
+ try {
187
+ execSync('npm install', { cwd: projectDir, stdio: 'inherit' });
188
+ } catch (error) {
189
+ console.error('āŒ Error installing dependencies.');
190
+ process.exit(1);
191
+ }
83
192
 
84
- if (fs.existsSync(envExamplePath)) {
85
- fs.copyFileSync(envExamplePath, envPath);
86
- }
193
+ // Generate JWT Secret
194
+ console.log('šŸ”‘ Generating JWT Secret...');
195
+ try {
196
+ execSync('npm run generate:jwt', { cwd: projectDir, stdio: 'inherit' });
197
+ } catch (error) {
198
+ console.warn('āš ļø Could not generate JWT secret automatically.');
199
+ }
87
200
 
88
- // Install dependencies
89
- console.log('šŸ“¦ Installing dependencies (this might take a while)...');
90
- try {
91
- execSync('npm install', { cwd: projectDir, stdio: 'inherit' });
92
- } catch (error) {
93
- console.error('āŒ Error installing dependencies.');
94
- process.exit(1);
95
- }
201
+ // Generate Prisma Client & Migrate
202
+ console.log('šŸ—„ļø Setting up database...');
203
+ try {
204
+ console.log(' Compiling schema...');
205
+ execSync('node scripts/compile-schema.js', { cwd: projectDir, stdio: 'inherit' });
206
+
207
+ console.log(' Generating Prisma Client...');
208
+ execSync('npx prisma generate', { cwd: projectDir, stdio: 'inherit' });
209
+
210
+ // Try to migrate (this will create the DB if it doesn't exist)
211
+ console.log(' Running migration (creates DB if missing)...');
212
+ execSync('npx prisma migrate dev --name init_setup', { cwd: projectDir, stdio: 'inherit' });
213
+
214
+ // Seed
215
+ if (isFull) {
216
+ console.log(' Seeding database...');
217
+ execSync('npm run db:seed', { cwd: projectDir, stdio: 'inherit' });
218
+ } else {
219
+ console.log(' ā„¹ļø Skipping database seeding (use --full to seed default data)...');
220
+ }
96
221
 
97
- // Generate JWT Secret
98
- console.log('šŸ”‘ Generating JWT Secret...');
99
- try {
100
- execSync('npm run generate:jwt', { cwd: projectDir, stdio: 'inherit' });
101
- } catch (error) {
102
- console.warn('āš ļø Could not generate JWT secret automatically. Please run "npm run generate:jwt" manually.');
103
- }
222
+ } catch (error) {
223
+ console.warn('āš ļø Database setup encountered an issue.');
224
+ console.warn(' You may need to check your .env credentials and run:');
225
+ console.warn(' cd ' + projectName);
226
+ console.warn(' npm run prisma:migrate');
227
+ }
104
228
 
105
- console.log('\nāœ… Project created successfully!');
106
- console.log(`\nNext steps:\n`);
107
- console.log(` cd ${projectName}`);
108
- console.log(` npm run dev`);
109
- console.log('\nHappy coding! šŸš€\n');
229
+ console.log(`\nāœ… Project ${projectName} created successfully!`);
230
+ console.log(`\nNext steps:`);
231
+ console.log(` cd ${projectName}`);
232
+ console.log(` npm run dev`);
233
+ })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lapeh",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Framework API Express yang siap pakai (Standardized)",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1,5 +1,5 @@
1
1
  generator client {
2
- provider = "postgresql"
2
+ provider = "prisma-client-js"
3
3
  output = "../generated/prisma"
4
4
  }
5
5
 
package/readme.md CHANGED
@@ -20,12 +20,20 @@ Buat project baru cukup dengan satu perintah:
20
20
  npx lapeh nama-project-anda
21
21
  ```
22
22
 
23
+ Atau gunakan flag `--full` untuk setup lengkap (termasuk seeding data default user & roles):
24
+
25
+ ```bash
26
+ npx lapeh nama-project-anda --full
27
+ ```
28
+
23
29
  ### Apa yang terjadi otomatis?
24
30
 
25
31
  1. Struktur project dibuat.
26
32
  2. Dependencies diinstall.
27
- 3. Environment variable (`.env`) disiapkan.
28
- 4. **JWT Secret** di-generate otomatis.
33
+ 3. Database dipilih & dikonfigurasi secara interaktif.
34
+ 4. **Database** dibuat dan dimigrasi otomatis.
35
+ 5. **JWT Secret** di-generate otomatis.
36
+ 6. **Seeding Data** (jika menggunakan `--full`).
29
37
 
30
38
  Masuk ke folder project dan jalankan:
31
39
 
@@ -108,8 +108,11 @@ const selectOption = async (query, options) => {
108
108
  console.log("šŸ“„ Updating prisma/base.prisma...");
109
109
  if (fs.existsSync(prismaBaseFile)) {
110
110
  let baseContent = fs.readFileSync(prismaBaseFile, "utf8");
111
- // Replace provider
112
- baseContent = baseContent.replace(/provider\s*=\s*".*"/, `provider = "${dbProvider}"`);
111
+ // Replace provider in datasource block
112
+ baseContent = baseContent.replace(
113
+ /(datasource\s+db\s+\{[\s\S]*?provider\s*=\s*")([^"]+)(")/,
114
+ `$1${dbProvider}$3`
115
+ );
113
116
  fs.writeFileSync(prismaBaseFile, baseContent);
114
117
  } else {
115
118
  console.warn("āš ļø prisma/base.prisma not found. Skipping.");