lumpiajs 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/README.md +64 -109
- package/bin/lumpia.js +13 -7
- package/index.js +1 -0
- package/lib/commands/create.js +21 -14
- package/lib/commands/serve.js +96 -13
- package/lib/core/Env.js +23 -0
- package/lib/core/Router.js +10 -3
- package/package.json +1 -1
- package/templates/.env.example +6 -0
package/README.md
CHANGED
|
@@ -1,196 +1,151 @@
|
|
|
1
1
|
# 🥟 LumpiaJS
|
|
2
2
|
|
|
3
3
|
**Bahasa Pemrograman Web dengan Kearifan Lokal Semarangan.**
|
|
4
|
-
|
|
4
|
+
Framework ini hadir sebagai solusi "Have Fun" bagi developer. Meskipun gayanya santai, LumpiaJS mengadopsi arsitektur **MVC (Model-View-Controller)** yang mirip banget sama framework sebelah (uhuk, Laravel). Jadi kamu bisa pakai struktur yang profesional!
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Kamu bisa pilih cara yang paling enak buat mulai bikin (masak) web:
|
|
6
|
+
---
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
## 🚀 Panduan Instalasi & Penggunaan (Lengkap)
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
### Opsi A: Menggunakan Command `lumpia` (Disarankan)
|
|
13
11
|
|
|
14
|
-
**
|
|
12
|
+
**1. Install Secara Global**
|
|
15
13
|
|
|
16
14
|
```bash
|
|
17
|
-
|
|
15
|
+
npm install -g lumpiajs
|
|
18
16
|
```
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
**Langkah 2: Masuk & Install Bumbu (Dependencies)**
|
|
23
|
-
Penting! Kamu harus install dependencies biar `import` framework-nya jalan.
|
|
18
|
+
**2. Buat Project Baru**
|
|
24
19
|
|
|
25
20
|
```bash
|
|
26
|
-
|
|
27
|
-
npm install
|
|
21
|
+
lumpia create-project warung-ku
|
|
28
22
|
```
|
|
29
23
|
|
|
30
|
-
**
|
|
24
|
+
**3. Masuk & Install Dependencies (Wajib)**
|
|
31
25
|
|
|
32
26
|
```bash
|
|
33
|
-
npm
|
|
27
|
+
cd warung-ku && npm install
|
|
34
28
|
```
|
|
35
29
|
|
|
36
|
-
|
|
37
|
-
Websitemu bakal jalan di `http://localhost:3000`.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
### 2. Install Global (Biar Bisa Dipakai Terus)
|
|
42
|
-
|
|
43
|
-
Kalau kamu pengen perintah `lumpia` bisa dipanggil dari mana saja di terminal:
|
|
30
|
+
**4. Jalankan Server**
|
|
44
31
|
|
|
45
32
|
```bash
|
|
46
|
-
|
|
47
|
-
|
|
33
|
+
lumpia kukus
|
|
34
|
+
```
|
|
48
35
|
|
|
49
|
-
|
|
50
|
-
lumpia create-project toko-lumpia
|
|
36
|
+
_(Alias: `lumpia serve`)_
|
|
51
37
|
|
|
52
|
-
|
|
53
|
-
cd toko-lumpia
|
|
54
|
-
npm install
|
|
38
|
+
---
|
|
55
39
|
|
|
56
|
-
|
|
57
|
-
lumpia dodolan
|
|
58
|
-
```
|
|
40
|
+
## ⚙️ Konfigurasi Environment (.env)
|
|
59
41
|
|
|
60
|
-
|
|
42
|
+
Setiap project LumpiaJS dilengkapi file `.env` di root folder.
|
|
61
43
|
|
|
62
|
-
```
|
|
63
|
-
|
|
44
|
+
```env
|
|
45
|
+
BASE_URL="http://localhost:3000"
|
|
46
|
+
APP_ENV="local"
|
|
47
|
+
APP_DEBUG="true"
|
|
64
48
|
```
|
|
65
49
|
|
|
66
50
|
---
|
|
67
51
|
|
|
68
52
|
## 🏗️ Struktur Project (Standar MVC)
|
|
69
53
|
|
|
70
|
-
LumpiaJS sekarang menggunakan arsitektur **MVC (Model-View-Controller)** yang mengikuti standar internasional, jadi developer Laravel atau Express pasti langsung paham.
|
|
71
|
-
|
|
72
54
|
```
|
|
73
55
|
warung-ku/
|
|
74
56
|
├── app/
|
|
75
57
|
│ ├── controllers/ # Otak Logika (Controller)
|
|
76
58
|
│ └── models/ # Pengolah Data (Model)
|
|
77
59
|
├── routes/
|
|
78
|
-
│ └── web.js # Rute URL
|
|
60
|
+
│ └── web.js # Rute URL (Jalur Akses)
|
|
79
61
|
├── views/ # Tampilan (.lmp)
|
|
62
|
+
├── .env # File Konfigurasi (Environment)
|
|
80
63
|
├── package.json
|
|
81
64
|
└── ...
|
|
82
65
|
```
|
|
83
66
|
|
|
84
67
|
### 1. Routes (`routes/web.js`)
|
|
85
68
|
|
|
86
|
-
|
|
69
|
+
Mengatur jalur URL. Sekarang sudah support parameter dinamis seperti Laravel!
|
|
87
70
|
|
|
88
71
|
```javascript
|
|
89
72
|
import { Jalan } from "lumpiajs";
|
|
90
73
|
|
|
91
|
-
//
|
|
92
|
-
Jalan.
|
|
93
|
-
|
|
74
|
+
// Basic GET
|
|
75
|
+
Jalan.get("/", "HomeController@index");
|
|
76
|
+
|
|
77
|
+
// API (POST/PUT/DELETE)
|
|
78
|
+
Jalan.post("/api/products", "ProductController@store");
|
|
79
|
+
Jalan.put("/api/products/{id}", "ProductController@update");
|
|
80
|
+
Jalan.delete("/api/products/{id}", "ProductController@destroy");
|
|
81
|
+
|
|
82
|
+
// Dynamic Route dengan Parameter
|
|
83
|
+
Jalan.get("/produk/{id}", "ProductController@show");
|
|
84
|
+
Jalan.get("/kategori/{slug}", "CategoryController@index");
|
|
94
85
|
```
|
|
95
86
|
|
|
96
87
|
### 2. Controllers (`app/controllers`)
|
|
97
88
|
|
|
98
|
-
Otak dari aplikasimu.
|
|
89
|
+
Otak dari aplikasimu. Parameter dari route (misal `{id}`) otomatis masuk jadi argumen fungsi.
|
|
99
90
|
|
|
100
91
|
```javascript
|
|
101
92
|
import { Controller } from "lumpiajs";
|
|
102
93
|
|
|
103
|
-
export default class
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
tanggal: new Date().toLocaleDateString(),
|
|
94
|
+
export default class ProductController extends Controller {
|
|
95
|
+
// Menangkap parameter {id} dari route
|
|
96
|
+
show(id) {
|
|
97
|
+
return this.json({
|
|
98
|
+
pesan: "Menampilkan produk dengan ID: " + id,
|
|
99
|
+
id: id,
|
|
110
100
|
});
|
|
111
101
|
}
|
|
102
|
+
|
|
103
|
+
index() {
|
|
104
|
+
return this.tampil("home", { env: this.env.APP_ENV });
|
|
105
|
+
}
|
|
112
106
|
}
|
|
113
107
|
```
|
|
114
108
|
|
|
115
109
|
### 3. Views (`views`)
|
|
116
110
|
|
|
117
|
-
File `.lmp
|
|
118
|
-
Gunakan `{{ nama_variabel }}` untuk menampilkan data dari Controller.
|
|
111
|
+
File berekstensi `.lmp`.
|
|
119
112
|
|
|
120
113
|
```html
|
|
121
114
|
<lump>
|
|
122
|
-
<klambi>
|
|
123
|
-
|
|
124
|
-
<
|
|
125
|
-
<!-- HTML di sini -->
|
|
126
|
-
<h1>{{ pesan }}</h1>
|
|
127
|
-
<p>Tanggal server: {{ tanggal }}</p>
|
|
128
|
-
</kulit>
|
|
129
|
-
|
|
130
|
-
<isi>
|
|
131
|
-
// JavaScript Client-side (Bahasa Semarangan) gawe sapa() { alert("Halo,
|
|
132
|
-
Lur!"); }
|
|
133
|
-
</isi>
|
|
115
|
+
<klambi> h1 { color: red; } </klambi>
|
|
116
|
+
<kulit> <h1>{{ pesan }}</h1> </kulit>
|
|
117
|
+
<isi> gawe sapa() { alert("Halo!"); } </isi>
|
|
134
118
|
</lump>
|
|
135
119
|
```
|
|
136
120
|
|
|
137
121
|
### 4. Models (`app/models`)
|
|
138
122
|
|
|
139
|
-
Tempat mengolah data (Database/API). Mendukung gaya penulisan ala Eloquent.
|
|
140
|
-
|
|
141
123
|
```javascript
|
|
142
124
|
import { Model } from "lumpiajs";
|
|
143
|
-
|
|
144
|
-
// Contoh penggunaan di Controller:
|
|
145
|
-
// Model.use(dataProduk).dimana('harga', '<', 5000).kabeh();
|
|
125
|
+
// Model.use(data).dimana('harga', '<', 5000).kabeh();
|
|
146
126
|
```
|
|
147
127
|
|
|
148
128
|
---
|
|
149
129
|
|
|
150
130
|
## 🧐 Kamus Bahasa (Transpiler)
|
|
151
131
|
|
|
152
|
-
|
|
132
|
+
Gunakan istilah Semarangan ini di dalam tag `<isi>` file `.lmp`:
|
|
153
133
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
134
|
+
| Bahasa Semarangan | JavaScript Asli |
|
|
135
|
+
| :---------------- | :-------------- |
|
|
136
|
+
| `ono` | `let` |
|
|
137
|
+
| `paten` | `const` |
|
|
138
|
+
| `gawe` | `function` |
|
|
139
|
+
| `yen` | `if` |
|
|
140
|
+
| `liyane` | `else` |
|
|
141
|
+
| `mandek` | `return` |
|
|
142
|
+
| `ora` | `!` |
|
|
162
143
|
|
|
163
144
|
---
|
|
164
145
|
|
|
165
|
-
##
|
|
166
|
-
|
|
167
|
-
Baru nemu bug? Atau punya ide jenius biar LumpiaJS makin jos? Sampaikan saja!
|
|
168
|
-
|
|
169
|
-
Caranya gampang:
|
|
170
|
-
|
|
171
|
-
1. Buka link ini: [https://github.com/fastroware/lumpiajs/issues](https://github.com/fastroware/lumpiajs/issues)
|
|
172
|
-
2. Klik tombol warna hijau bertuliskan **"New Issue"**.
|
|
173
|
-
3. Isi Judul dengan jelas.
|
|
174
|
-
4. Jelaskan masalah atau saranmu di kolom deskripsi.
|
|
175
|
-
5. Klik **"Submit new issue"**.
|
|
176
|
-
|
|
177
|
-
Selesai! Masukanmu akan saya baca pas lagi senggang.
|
|
146
|
+
## ⚠️ DISCLAIMER
|
|
178
147
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
## ⚠️ DISCLAIMER (PENTING BANGET, WAJIB DIBACA!) ⚠️
|
|
182
|
-
|
|
183
|
-
**LumpiaJS ini 100% project _Have Fun_ & Eksperimen.**
|
|
184
|
-
|
|
185
|
-
Kami **TIDAK BERTANGGUNG JAWAB** atas segala bentuk kerugian yang mungkin terjadi akibat penggunaan software ini, termasuk tapi tidak terbatas pada:
|
|
186
|
-
|
|
187
|
-
- Kebocoran data.
|
|
188
|
-
- Hilangnya file penting.
|
|
189
|
-
- Komputer meledak (lebay, tapi tetap saja hati-hati).
|
|
190
|
-
- Kerugian materiil maupun immateriil lainnya.
|
|
191
|
-
|
|
192
|
-
Gunakan framework ini dengan resiko ditanggung sendiri (_Use at your own risk_). Kalau ada error di production karena nekat pakai ini, jangan nyalahin kami ya! 🙏
|
|
193
|
-
|
|
194
|
-
---
|
|
148
|
+
**LumpiaJS ini adalah project "Have Fun" & Eksperimen Semata.**
|
|
149
|
+
Gunakan dengan resiko ditanggung sendiri (_Use at your own risk_).
|
|
195
150
|
|
|
196
151
|
_Dibuat dengan ❤️ dan 🥟 dari Semarang._
|
package/bin/lumpia.js
CHANGED
|
@@ -7,21 +7,27 @@ async function main() {
|
|
|
7
7
|
const perintah = args[0];
|
|
8
8
|
const parameter = args[1];
|
|
9
9
|
|
|
10
|
+
// ALIAS LIST
|
|
11
|
+
// create-project : buka-cabang
|
|
12
|
+
// serve : dodolan, kukus
|
|
13
|
+
// build : goreng
|
|
14
|
+
|
|
10
15
|
if (perintah === 'create-project' || perintah === 'buka-cabang') {
|
|
11
16
|
createProject(parameter);
|
|
12
17
|
}
|
|
13
|
-
else if (perintah === 'dodolan' || perintah === 'serve') {
|
|
18
|
+
else if (perintah === 'dodolan' || perintah === 'serve' || perintah === 'kukus') {
|
|
14
19
|
serveProject();
|
|
15
20
|
}
|
|
16
|
-
else if (perintah === 'goreng') {
|
|
17
|
-
console.log("🚧 Fitur 'goreng' saiki wis otomatis digabung karo '
|
|
18
|
-
console.log(" Silakan gunake: lumpia
|
|
21
|
+
else if (perintah === 'goreng' || perintah === 'build') {
|
|
22
|
+
console.log("🚧 Fitur 'goreng' (build) saiki wis otomatis digabung karo 'kukus' (serve) via JIT Compiler MVC.");
|
|
23
|
+
console.log(" Silakan gunake: lumpia kukus (atau lumpia serve)");
|
|
19
24
|
}
|
|
20
25
|
else {
|
|
21
|
-
console.log('Perintah ora dikenal.');
|
|
26
|
+
console.log('Perintah ora dikenal / Command not recognized.');
|
|
22
27
|
console.log('------------------------------------------------');
|
|
23
|
-
console.log('1. lumpia create-project <nama>
|
|
24
|
-
console.log('2. lumpia
|
|
28
|
+
console.log('1. lumpia create-project <nama> (Alias: buka-cabang)');
|
|
29
|
+
console.log('2. lumpia serve (Alias: kukus, dodolan)');
|
|
30
|
+
console.log('3. lumpia build (Alias: goreng)');
|
|
25
31
|
console.log('------------------------------------------------');
|
|
26
32
|
}
|
|
27
33
|
}
|
package/index.js
CHANGED
package/lib/commands/create.js
CHANGED
|
@@ -14,10 +14,12 @@ const controllerTemplate = `import { Controller } from 'lumpiajs';
|
|
|
14
14
|
|
|
15
15
|
export default class HomeController extends Controller {
|
|
16
16
|
index() {
|
|
17
|
-
//
|
|
17
|
+
// Tampilkan halaman home
|
|
18
|
+
// this.env bisa diakses di sini
|
|
18
19
|
return this.tampil('home', {
|
|
19
20
|
message: 'Welcome to LumpiaJS MVC!',
|
|
20
|
-
author: 'Pakdhe Koding'
|
|
21
|
+
author: 'Pakdhe Koding',
|
|
22
|
+
env: this.env.APP_ENV
|
|
21
23
|
});
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -42,14 +44,14 @@ import ProductData from '../../app/models/Product.js';
|
|
|
42
44
|
|
|
43
45
|
export default class ProductController extends Controller {
|
|
44
46
|
index() {
|
|
45
|
-
// Eloquent-style: Model.use(data).dimana(...).jupuk()
|
|
46
47
|
const result = Model.use(ProductData)
|
|
47
48
|
.dimana('price', '>', 5500)
|
|
48
49
|
.jupuk();
|
|
49
50
|
|
|
50
51
|
return this.json({
|
|
51
52
|
status: 'success',
|
|
52
|
-
data: result
|
|
53
|
+
data: result,
|
|
54
|
+
debug_mode: this.env.APP_DEBUG
|
|
53
55
|
});
|
|
54
56
|
}
|
|
55
57
|
}
|
|
@@ -59,11 +61,13 @@ const viewHomeTemplate = `<lump>
|
|
|
59
61
|
<klambi>
|
|
60
62
|
h1 { color: #d35400; text-align: center; }
|
|
61
63
|
.box { border: 1px solid #ddd; padding: 20px; text-align: center; margin-top: 20px; }
|
|
64
|
+
.badge { background: #eee; padding: 5px; border-radius: 4px; font-size: 12px; }
|
|
62
65
|
</klambi>
|
|
63
66
|
|
|
64
67
|
<kulit>
|
|
65
68
|
<h1>{{ message }}</h1>
|
|
66
69
|
<div class="box">
|
|
70
|
+
<span class="badge">Environment: {{ env }}</span>
|
|
67
71
|
<p>Created with love by: <strong>{{ author }}</strong></p>
|
|
68
72
|
<button onclick="checkPrice()">Check Price API</button>
|
|
69
73
|
<br><br>
|
|
@@ -96,8 +100,8 @@ const packageJsonTemplate = (name) => `{
|
|
|
96
100
|
"main": "routes/web.js",
|
|
97
101
|
"type": "module",
|
|
98
102
|
"scripts": {
|
|
99
|
-
"start": "lumpia
|
|
100
|
-
"serve": "lumpia
|
|
103
|
+
"start": "lumpia kukus",
|
|
104
|
+
"serve": "lumpia kukus"
|
|
101
105
|
},
|
|
102
106
|
"dependencies": {
|
|
103
107
|
"lumpiajs": "latest"
|
|
@@ -105,6 +109,14 @@ const packageJsonTemplate = (name) => `{
|
|
|
105
109
|
}
|
|
106
110
|
`;
|
|
107
111
|
|
|
112
|
+
const envTemplate = `
|
|
113
|
+
BASE_URL="http://localhost:3000"
|
|
114
|
+
APP_ENV="local"
|
|
115
|
+
# Pilihan: 'local', 'development', 'production'
|
|
116
|
+
APP_DEBUG="true"
|
|
117
|
+
# Pilihan: 'true', 'false'
|
|
118
|
+
`;
|
|
119
|
+
|
|
108
120
|
export function createProject(parameter) {
|
|
109
121
|
const projectName = parameter;
|
|
110
122
|
if (!projectName) {
|
|
@@ -120,12 +132,6 @@ export function createProject(parameter) {
|
|
|
120
132
|
|
|
121
133
|
fs.mkdirSync(root);
|
|
122
134
|
|
|
123
|
-
// STANDARD MVC STRUCTURE:
|
|
124
|
-
// app/controllers
|
|
125
|
-
// app/models
|
|
126
|
-
// views
|
|
127
|
-
// routes
|
|
128
|
-
|
|
129
135
|
fs.mkdirSync(path.join(root, 'app', 'controllers'), { recursive: true });
|
|
130
136
|
fs.mkdirSync(path.join(root, 'app', 'models'), { recursive: true });
|
|
131
137
|
fs.mkdirSync(path.join(root, 'routes'));
|
|
@@ -133,6 +139,7 @@ export function createProject(parameter) {
|
|
|
133
139
|
|
|
134
140
|
// Write files
|
|
135
141
|
fs.writeFileSync(path.join(root, 'package.json'), packageJsonTemplate(projectName));
|
|
142
|
+
fs.writeFileSync(path.join(root, '.env'), envTemplate); // Create .env
|
|
136
143
|
fs.writeFileSync(path.join(root, 'routes', 'web.js'), routesTemplate);
|
|
137
144
|
fs.writeFileSync(path.join(root, 'app', 'controllers', 'HomeController.js'), controllerTemplate);
|
|
138
145
|
fs.writeFileSync(path.join(root, 'app', 'controllers', 'ProductController.js'), productControllerTemplate);
|
|
@@ -140,10 +147,10 @@ export function createProject(parameter) {
|
|
|
140
147
|
fs.writeFileSync(path.join(root, 'views', 'home.lmp'), viewHomeTemplate);
|
|
141
148
|
fs.writeFileSync(path.join(root, 'views', 'profile.lmp'), viewProfileTemplate);
|
|
142
149
|
|
|
143
|
-
console.log('✅ Project ready! Standard MVC Structure.');
|
|
150
|
+
console.log('✅ Project ready! Standard MVC Structure with .env Support.');
|
|
144
151
|
console.log('----------------------------------------------------');
|
|
145
152
|
console.log(`cd ${projectName}`);
|
|
146
153
|
console.log('npm install');
|
|
147
|
-
console.log('
|
|
154
|
+
console.log('lumpia kukus');
|
|
148
155
|
console.log('----------------------------------------------------');
|
|
149
156
|
}
|
package/lib/commands/serve.js
CHANGED
|
@@ -3,21 +3,78 @@ import path from 'path';
|
|
|
3
3
|
import http from 'http';
|
|
4
4
|
import { routes } from '../core/Router.js';
|
|
5
5
|
import { renderLumpia } from '../core/View.js';
|
|
6
|
+
import { loadEnv } from '../core/Env.js';
|
|
7
|
+
|
|
8
|
+
// Baca versi LumpiaJS global (CLI ini)
|
|
9
|
+
const cliPackageJson = JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url)));
|
|
10
|
+
|
|
11
|
+
// Helper untuk mencocokkan Route dengan Parameter {id} dll
|
|
12
|
+
function matchRoute(definedRoute, method, pathname) {
|
|
13
|
+
if (definedRoute.method !== method) return null;
|
|
14
|
+
|
|
15
|
+
// 1. Exact Match
|
|
16
|
+
if (definedRoute.path === pathname) return { params: {} };
|
|
17
|
+
|
|
18
|
+
// 2. Dynamic Match (Regex)
|
|
19
|
+
// Convert /produk/{id}/{slug} -> ^\/produk\/([^\/]+)\/([^\/]+)$
|
|
20
|
+
const paramNames = [];
|
|
21
|
+
const regexPath = definedRoute.path.replace(/\{([a-zA-Z0-9_]+)\}/g, (match, name) => {
|
|
22
|
+
paramNames.push(name);
|
|
23
|
+
return '([^/]+)';
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Skip if no params found in definition (optimization)
|
|
27
|
+
if (regexPath === definedRoute.path) return null;
|
|
28
|
+
|
|
29
|
+
const regex = new RegExp(`^${regexPath}$`);
|
|
30
|
+
const match = pathname.match(regex);
|
|
31
|
+
|
|
32
|
+
if (match) {
|
|
33
|
+
const params = {};
|
|
34
|
+
paramNames.forEach((name, index) => {
|
|
35
|
+
params[name] = match[index + 1]; // capture groups start at 1
|
|
36
|
+
});
|
|
37
|
+
return { params };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
6
42
|
|
|
7
43
|
export async function serveProject() {
|
|
8
44
|
const root = process.cwd();
|
|
9
|
-
|
|
45
|
+
|
|
46
|
+
// --- 1. Version Check ---
|
|
47
|
+
const userPackageJsonPath = path.join(root, 'package.json');
|
|
48
|
+
if (fs.existsSync(userPackageJsonPath)) {
|
|
49
|
+
const userPkg = JSON.parse(fs.readFileSync(userPackageJsonPath, 'utf-8'));
|
|
50
|
+
const userLumpiaVer = (userPkg.dependencies && userPkg.dependencies.lumpiajs) || 'unknown';
|
|
51
|
+
|
|
52
|
+
console.log(`ℹ️ LumpiaJS CLI Version: ${cliPackageJson.version}`);
|
|
53
|
+
console.log(`ℹ️ Project Dependency Version: ${userLumpiaVer}`);
|
|
10
54
|
|
|
55
|
+
const cleanUserVer = userLumpiaVer.replace(/[^0-9.]/g, '');
|
|
56
|
+
if (cleanUserVer !== cliPackageJson.version && userLumpiaVer !== 'latest') {
|
|
57
|
+
console.log('\n⚠️ PERINGATAN BEDA VERSI!');
|
|
58
|
+
console.log(` Versi CLI kamu (${cliPackageJson.version}) beda dengan versi di project (${userLumpiaVer}).`);
|
|
59
|
+
console.log(' Saran: Update project dependencies agar sinkron.');
|
|
60
|
+
console.log(' Cara update: npm install lumpiajs@latest\n');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const routesFile = path.join(root, 'routes', 'web.js');
|
|
11
65
|
if (!fs.existsSync(routesFile)) return console.log("❌ This is not a LumpiaJS MVC project! (Cannot find routes/web.js)");
|
|
12
66
|
|
|
13
|
-
|
|
14
|
-
|
|
67
|
+
// --- 2. Load ENV ---
|
|
68
|
+
const env = loadEnv(root);
|
|
69
|
+
console.log(`🌍 Environment: ${env.APP_ENV}`);
|
|
70
|
+
console.log(`🐛 Debug Mode: ${env.APP_DEBUG}`);
|
|
71
|
+
|
|
15
72
|
try {
|
|
16
73
|
// Load User Routes
|
|
17
74
|
const userRouteUrl = path.join(root, 'routes', 'web.js');
|
|
18
75
|
await import('file://' + userRouteUrl);
|
|
19
76
|
|
|
20
|
-
console.log(`🛣️ Routes
|
|
77
|
+
console.log(`🛣️ Routes registered: ${routes.length}`);
|
|
21
78
|
|
|
22
79
|
// Start Server
|
|
23
80
|
const server = http.createServer(async (req, res) => {
|
|
@@ -25,29 +82,51 @@ export async function serveProject() {
|
|
|
25
82
|
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
26
83
|
const pathname = url.pathname;
|
|
27
84
|
|
|
28
|
-
|
|
85
|
+
if (env.APP_DEBUG === 'true') {
|
|
86
|
+
console.log(`📥 ${method} ${pathname}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// FIND MATCHING ROUTE
|
|
90
|
+
let match = null;
|
|
91
|
+
let params = {};
|
|
29
92
|
|
|
30
|
-
const
|
|
93
|
+
for (const route of routes) {
|
|
94
|
+
const result = matchRoute(route, method, pathname);
|
|
95
|
+
if (result) {
|
|
96
|
+
match = route;
|
|
97
|
+
params = result.params;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
31
101
|
|
|
32
102
|
if (match) {
|
|
33
103
|
try {
|
|
34
|
-
// Action format: 'ControllerName@method'
|
|
35
104
|
const [controllerName, methodName] = match.action.split('@');
|
|
36
|
-
|
|
37
|
-
// Path to 'app/controllers'
|
|
38
105
|
const controllerPath = path.join(root, 'app', 'controllers', controllerName + '.js');
|
|
39
106
|
|
|
40
107
|
if (!fs.existsSync(controllerPath)) throw new Error(`Controller ${controllerName} not found in app/controllers!`);
|
|
41
108
|
|
|
42
|
-
|
|
109
|
+
// In Development/Local, always invalidate cache
|
|
110
|
+
let importUrl = 'file://' + controllerPath;
|
|
111
|
+
if (env.APP_ENV === 'local' || env.APP_ENV === 'development') {
|
|
112
|
+
importUrl += '?update=' + Date.now();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const module = await import(importUrl);
|
|
43
116
|
const ControllerClass = module.default;
|
|
44
117
|
const instance = new ControllerClass();
|
|
45
118
|
|
|
119
|
+
// Inject ENV & Params to Controller
|
|
120
|
+
instance.env = env;
|
|
121
|
+
instance.params = params; // Available as this.params
|
|
122
|
+
|
|
46
123
|
if (typeof instance[methodName] !== 'function') {
|
|
47
124
|
throw new Error(`Method ${methodName} does not exist in ${controllerName}`);
|
|
48
125
|
}
|
|
49
126
|
|
|
50
|
-
|
|
127
|
+
// Pass params as spread arguments to the method: index(id, slug)
|
|
128
|
+
const args = Object.values(params);
|
|
129
|
+
const result = await instance[methodName](...args);
|
|
51
130
|
|
|
52
131
|
if (result.type === 'html') {
|
|
53
132
|
res.writeHead(200, {'Content-Type': 'text/html'});
|
|
@@ -61,8 +140,12 @@ export async function serveProject() {
|
|
|
61
140
|
}
|
|
62
141
|
} catch (e) {
|
|
63
142
|
console.error(e);
|
|
143
|
+
const errorMsg = env.APP_DEBUG === 'true'
|
|
144
|
+
? `<h1>500: Server Error</h1><pre>${e.message}\n${e.stack}</pre>`
|
|
145
|
+
: `<h1>500: Server Error</h1><p>Something went wrong.</p>`;
|
|
146
|
+
|
|
64
147
|
res.writeHead(500, {'Content-Type': 'text/html'});
|
|
65
|
-
res.end(
|
|
148
|
+
res.end(errorMsg);
|
|
66
149
|
}
|
|
67
150
|
} else {
|
|
68
151
|
res.writeHead(404, {'Content-Type': 'text/html'});
|
|
@@ -72,7 +155,7 @@ export async function serveProject() {
|
|
|
72
155
|
|
|
73
156
|
const port = 3000;
|
|
74
157
|
server.listen(port, () => {
|
|
75
|
-
console.log(`🚀 Server running at http://localhost
|
|
158
|
+
console.log(`🚀 Server running at ${env.BASE_URL || 'http://localhost:3000'}`);
|
|
76
159
|
console.log(`(Press Ctrl+C to stop)`);
|
|
77
160
|
});
|
|
78
161
|
|
package/lib/core/Env.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export function loadEnv(root) {
|
|
5
|
+
const envPath = path.join(root, '.env');
|
|
6
|
+
const env = {};
|
|
7
|
+
|
|
8
|
+
if (fs.existsSync(envPath)) {
|
|
9
|
+
const content = fs.readFileSync(envPath, 'utf-8');
|
|
10
|
+
content.split('\n').forEach(line => {
|
|
11
|
+
if(line.includes('=')) {
|
|
12
|
+
const [key, val] = line.split('=');
|
|
13
|
+
env[key.trim()] = val.trim().replace(/"/g, '');
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
// Default values if .env not found
|
|
18
|
+
env.BASE_URL = "http://localhost:3000";
|
|
19
|
+
env.APP_ENV = "local";
|
|
20
|
+
env.APP_DEBUG = "true";
|
|
21
|
+
}
|
|
22
|
+
return env;
|
|
23
|
+
}
|
package/lib/core/Router.js
CHANGED
|
@@ -3,12 +3,19 @@
|
|
|
3
3
|
export const routes = [];
|
|
4
4
|
|
|
5
5
|
export const Jalan = {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// Semarangan
|
|
7
|
+
gawe: (path, action) => routes.push({ path, action, method: 'GET' }),
|
|
8
|
+
jupuk: (path, action) => routes.push({ path, action, method: 'GET' }),
|
|
8
9
|
kirim: (path, action) => routes.push({ path, action, method: 'POST' }),
|
|
9
10
|
anyar: (path, action) => routes.push({ path, action, method: 'PUT' }),
|
|
10
11
|
bucak: (path, action) => routes.push({ path, action, method: 'DELETE' }),
|
|
12
|
+
|
|
13
|
+
// Internasional (Laravel-like)
|
|
14
|
+
get: (path, action) => routes.push({ path, action, method: 'GET' }),
|
|
15
|
+
post: (path, action) => routes.push({ path, action, method: 'POST' }),
|
|
16
|
+
put: (path, action) => routes.push({ path, action, method: 'PUT' }),
|
|
17
|
+
delete: (path, action) => routes.push({ path, action, method: 'DELETE' }),
|
|
11
18
|
};
|
|
12
19
|
|
|
13
|
-
// Global Router Container
|
|
20
|
+
// Global Router Container
|
|
14
21
|
global.LumpiaRouter = routes;
|
package/package.json
CHANGED