lumpiajs 1.0.1 → 1.0.4
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 +130 -0
- package/bin/lumpia.js +29 -0
- package/index.js +3 -104
- package/lib/commands/create.js +148 -0
- package/lib/commands/serve.js +82 -0
- package/lib/core/Controller.js +20 -0
- package/lib/core/Model.js +41 -0
- package/lib/core/Router.js +14 -0
- package/lib/core/View.js +58 -0
- package/package.json +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# 🥟 LumpiaJS
|
|
2
|
+
|
|
3
|
+
**Framework Web MVC dengan Kearifan Lokal Semarangan.**
|
|
4
|
+
_Framework ini dibuat untuk seru-seruan (have fun), tapi diam-diam powerful seperti PHP Framework modern!_
|
|
5
|
+
|
|
6
|
+
## 🚀 Cara Mulai (Quick Start)
|
|
7
|
+
|
|
8
|
+
### 1. Buat Project Baru
|
|
9
|
+
|
|
10
|
+
Langsung gas pakai perintah ini:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx lumpiajs create-project warung-ku
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
_(Atau pakai istilah lokal: `npx lumpiajs buka-cabang warung-ku`)_
|
|
17
|
+
|
|
18
|
+
### 2. Masuk & Install Bumbu
|
|
19
|
+
|
|
20
|
+
Masuk ke foldernya dan install dependencies (wajib, biar kodingannya gurih):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd warung-ku
|
|
24
|
+
npm install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 3. Dodolan (Jalankan Server)
|
|
28
|
+
|
|
29
|
+
Nyalakan server development:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm start
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Atau pakai perintah manual: `npx lumpia dodolan`.
|
|
36
|
+
Websitemu bakal jalan di `http://localhost:3000`.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🏗️ Struktur Project (MVL - Model View Logika)
|
|
41
|
+
|
|
42
|
+
LumpiaJS nggunake istilah sing luwih "Njawani" tapi tetep MVC:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
warung-ku/
|
|
46
|
+
├── app/
|
|
47
|
+
│ ├── logika/ # Logika Program (Controller)
|
|
48
|
+
│ └── belakang/ # Data & Dapur (Model)
|
|
49
|
+
├── jalur/
|
|
50
|
+
│ └── web.js # Rute URL (Routes)
|
|
51
|
+
├── depan/ # Tampilan (View)
|
|
52
|
+
├── package.json
|
|
53
|
+
└── ...
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 1. Jalur (Routes)
|
|
57
|
+
|
|
58
|
+
Atur URL di `jalur/web.js`:
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
import { Jalan } from "lumpiajs";
|
|
62
|
+
|
|
63
|
+
// Nulis rute: Jalan.gawe(url, 'NamaLogika@method')
|
|
64
|
+
Jalan.gawe("/", "BerandaLogika@index");
|
|
65
|
+
Jalan.gawe("/api/produk", "ProdukLogika@index");
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Logika (Controller)
|
|
69
|
+
|
|
70
|
+
Bikin logika di `app/logika/BerandaLogika.js`.
|
|
71
|
+
Class harus extend `Logika`.
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
import { Logika } from "lumpiajs";
|
|
75
|
+
|
|
76
|
+
export default class BerandaLogika extends Logika {
|
|
77
|
+
index() {
|
|
78
|
+
// Tampilke file ning folder 'depan/beranda.lmp'
|
|
79
|
+
return this.tampil("beranda", {
|
|
80
|
+
pesan: "Sugeng Rawuh, Lur!",
|
|
81
|
+
tanggal: new Date().toLocaleDateString(),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. Depan (View)
|
|
88
|
+
|
|
89
|
+
Bikin tampilan di `depan/beranda.lmp`.
|
|
90
|
+
Gunakan `{{ variabel }}` buat nampilin data.
|
|
91
|
+
|
|
92
|
+
```html
|
|
93
|
+
<lump>
|
|
94
|
+
<klambi> h1 { color: red; } </klambi>
|
|
95
|
+
|
|
96
|
+
<kulit>
|
|
97
|
+
<h1>{{ pesan }}</h1>
|
|
98
|
+
<p>Saiki tanggal: {{ tanggal }}</p>
|
|
99
|
+
</kulit>
|
|
100
|
+
</lump>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 4. Belakang (Model)
|
|
104
|
+
|
|
105
|
+
Simpen data utawa logika database ning `app/belakang/Produk.js`.
|
|
106
|
+
Bisa nggunake `Model` ala Eloquent.
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
import { Model } from "lumpiajs";
|
|
110
|
+
// ... logika model ...
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🤝 Cara Lapor Masalah
|
|
116
|
+
|
|
117
|
+
Nembe nemu bug? Atau punya ide jenius?
|
|
118
|
+
|
|
119
|
+
1. Buka link ini: [https://github.com/fastroware/lumpiajs/issues](https://github.com/fastroware/lumpiajs/issues)
|
|
120
|
+
2. Klik **"New Issue"**.
|
|
121
|
+
3. Ceritakan keluh kesahmu.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## ⚠️ DISCLAIMER
|
|
126
|
+
|
|
127
|
+
**LumpiaJS ini 100% project _Have Fun_.**
|
|
128
|
+
Gunakan dengan bijak. Kalau ada error di production, jangan nyalahin kami ya! 🙏
|
|
129
|
+
|
|
130
|
+
_Dibuat dengan ❤️ dan 🥟 dari Semarang._
|
package/bin/lumpia.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createProject } from '../lib/commands/create.js';
|
|
3
|
+
import { serveProject } from '../lib/commands/serve.js';
|
|
4
|
+
|
|
5
|
+
async function main() {
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const perintah = args[0];
|
|
8
|
+
const parameter = args[1];
|
|
9
|
+
|
|
10
|
+
if (perintah === 'create-project' || perintah === 'buka-cabang') {
|
|
11
|
+
createProject(parameter);
|
|
12
|
+
}
|
|
13
|
+
else if (perintah === 'dodolan' || perintah === 'serve') {
|
|
14
|
+
serveProject();
|
|
15
|
+
}
|
|
16
|
+
else if (perintah === 'goreng') {
|
|
17
|
+
console.log("🚧 Fitur 'goreng' saiki wis otomatis digabung karo 'dodolan' via JIT Compiler MVC.");
|
|
18
|
+
console.log(" Silakan gunake: lumpia dodolan");
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
console.log('Perintah ora dikenal.');
|
|
22
|
+
console.log('------------------------------------------------');
|
|
23
|
+
console.log('1. lumpia create-project <nama> (Bikin project)');
|
|
24
|
+
console.log('2. lumpia dodolan (Jalanin server)');
|
|
25
|
+
console.log('------------------------------------------------');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
main();
|
package/index.js
CHANGED
|
@@ -1,105 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
1
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
{ asal: /<lump>/g, jadi: '' },
|
|
9
|
-
{ asal: /<\/lump>/g, jadi: '' },
|
|
10
|
-
|
|
11
|
-
// Variabel
|
|
12
|
-
{ asal: /ono\s/g, jadi: 'let ' },
|
|
13
|
-
{ asal: /paten\s/g, jadi: 'const ' },
|
|
14
|
-
|
|
15
|
-
// Logic
|
|
16
|
-
{ asal: /gawe\s/g, jadi: 'function ' },
|
|
17
|
-
{ asal: /yen\s/g, jadi: 'if ' },
|
|
18
|
-
{ asal: /liyane/g, jadi: 'else' },
|
|
19
|
-
{ asal: /mandek;/g, jadi: 'return;' },
|
|
20
|
-
|
|
21
|
-
// Operator
|
|
22
|
-
{ asal: /ora\s/g, jadi: '!' },
|
|
23
|
-
{ asal: /panjang\(/g, jadi: 'len(' },
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
// Script Bantuan (Runtime)
|
|
27
|
-
const runtimeScript = `
|
|
28
|
-
<script>
|
|
29
|
-
function ambil(sel) { return document.querySelector(sel).value; }
|
|
30
|
-
function tampil(txt) {
|
|
31
|
-
let el = document.getElementById('output-lumpia');
|
|
32
|
-
if(el) el.innerText = txt;
|
|
33
|
-
else alert(txt);
|
|
34
|
-
}
|
|
35
|
-
function len(x) { return x.length; }
|
|
36
|
-
</script>
|
|
37
|
-
`;
|
|
38
|
-
|
|
39
|
-
async function main() {
|
|
40
|
-
const args = process.argv.slice(2);
|
|
41
|
-
const perintah = args[0];
|
|
42
|
-
|
|
43
|
-
// Perintah: lumpia goreng
|
|
44
|
-
if (perintah === 'goreng') {
|
|
45
|
-
console.log('🍳 Sik, lagi nggoreng kodingan...');
|
|
46
|
-
|
|
47
|
-
const srcDir = './src';
|
|
48
|
-
const distDir = './dist';
|
|
49
|
-
|
|
50
|
-
// Cek folder src
|
|
51
|
-
if (!fs.existsSync(srcDir)) {
|
|
52
|
-
console.log('❌ Waduh, folder "src" ora ono! Gaweo sek.');
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Bikin folder dist kalo belum ada
|
|
57
|
-
if (!fs.existsSync(distDir)) fs.mkdirSync(distDir);
|
|
58
|
-
|
|
59
|
-
// Baca file .lmp
|
|
60
|
-
const files = fs.readdirSync(srcDir).filter(file => file.endsWith('.lmp'));
|
|
61
|
-
|
|
62
|
-
if (files.length === 0) {
|
|
63
|
-
console.log('⚠️ Gak ono file .lmp nang folder src.');
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
files.forEach(file => {
|
|
68
|
-
let content = fs.readFileSync(path.join(srcDir, file), 'utf-8');
|
|
69
|
-
|
|
70
|
-
// Pisah Kulit & Isi
|
|
71
|
-
const matchKulit = content.match(/<kulit>([\s\S]*?)<\/kulit>/);
|
|
72
|
-
const matchIsi = content.match(/<isi>([\s\S]*?)<\/isi>/);
|
|
73
|
-
|
|
74
|
-
let htmlnya = matchKulit ? matchKulit[1] : '';
|
|
75
|
-
let logicnya = matchIsi ? matchIsi[1] : '';
|
|
76
|
-
|
|
77
|
-
// Translate Logic
|
|
78
|
-
dictionary.forEach(kata => {
|
|
79
|
-
logicnya = logicnya.replace(kata.asal, kata.jadi);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// Gabung jadi HTML
|
|
83
|
-
const hasil = `<!DOCTYPE html>
|
|
84
|
-
<html>
|
|
85
|
-
<head><title>Lumpia App</title></head>
|
|
86
|
-
<body style="font-family:sans-serif; padding:20px;">
|
|
87
|
-
${htmlnya}
|
|
88
|
-
<div id="output-lumpia" style="margin-top:20px; font-weight:bold;"></div>
|
|
89
|
-
${runtimeScript}
|
|
90
|
-
<script>${logicnya}</script>
|
|
91
|
-
</body>
|
|
92
|
-
</html>`;
|
|
93
|
-
|
|
94
|
-
// Simpan
|
|
95
|
-
const namaFileBaru = file.replace('.lmp', '.html');
|
|
96
|
-
fs.writeFileSync(path.join(distDir, namaFileBaru), hasil);
|
|
97
|
-
console.log(`✅ Mateng: dist/${namaFileBaru}`);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
} else {
|
|
101
|
-
console.log('Gunakno perintah: lumpia goreng');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
main();
|
|
2
|
+
export { Jalan, routes } from './lib/core/Router.js';
|
|
3
|
+
export { LumpiaModel as Model } from './lib/core/Model.js';
|
|
4
|
+
export { Logika } from './lib/core/Controller.js'; // Export Logika instead of Kontroler
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// --- TEMPLATES ---
|
|
5
|
+
const routesTemplate = `import { Jalan } from 'lumpiajs';
|
|
6
|
+
|
|
7
|
+
// Definisi Jalur (Routes)
|
|
8
|
+
Jalan.gawe('/', 'BerandaLogika@index');
|
|
9
|
+
Jalan.gawe('/profil', 'BerandaLogika@profil');
|
|
10
|
+
Jalan.gawe('/api/produk', 'ProdukLogika@index');
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const controllerTemplate = `import { Logika, Model } from 'lumpiajs';
|
|
14
|
+
|
|
15
|
+
export default class BerandaLogika extends Logika {
|
|
16
|
+
index() {
|
|
17
|
+
// Kirim data ke tampilan (view) ning folder 'depan'
|
|
18
|
+
return this.tampil('beranda', {
|
|
19
|
+
pesan: 'Sugeng Rawuh di LumpiaJS!',
|
|
20
|
+
penulis: 'Pakdhe Koding'
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
profil() {
|
|
25
|
+
return this.tampil('profil', { nama: 'User Setia' });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
const modelTemplate = `// Contoh dummy data
|
|
31
|
+
const dataProduk = [
|
|
32
|
+
{ id: 1, jeneng: 'Lumpia Basah', rego: 5000 },
|
|
33
|
+
{ id: 2, jeneng: 'Lumpia Goreng', rego: 6000 },
|
|
34
|
+
{ id: 3, jeneng: 'Tahu Gimbal', rego: 12000 }
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
export default dataProduk;
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
const produkControllerTemplate = `import { Logika, Model } from 'lumpiajs';
|
|
41
|
+
import DataProduk from '../../belakang/Produk.js';
|
|
42
|
+
|
|
43
|
+
export default class ProdukLogika extends Logika {
|
|
44
|
+
index() {
|
|
45
|
+
// Eloquent-style: Model.use(data).dimana(...).jupuk()
|
|
46
|
+
const asil = Model.use(DataProduk)
|
|
47
|
+
.dimana('rego', '>', 5500)
|
|
48
|
+
.jupuk();
|
|
49
|
+
|
|
50
|
+
return this.json({
|
|
51
|
+
status: 'sukses',
|
|
52
|
+
data: asil
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const viewBerandaTemplate = `<lump>
|
|
59
|
+
<klambi>
|
|
60
|
+
h1 { color: #d35400; text-align: center; }
|
|
61
|
+
.box { border: 1px solid #ddd; padding: 20px; text-align: center; margin-top: 20px; }
|
|
62
|
+
</klambi>
|
|
63
|
+
|
|
64
|
+
<kulit>
|
|
65
|
+
<h1>{{ pesan }}</h1>
|
|
66
|
+
<div class="box">
|
|
67
|
+
<p>Digawe karo tresno dening: <strong>{{ penulis }}</strong></p>
|
|
68
|
+
<button onclick="cekrego()">Cek Harga API</button>
|
|
69
|
+
<br><br>
|
|
70
|
+
<a href="/profil">Menyang Profil</a>
|
|
71
|
+
</div>
|
|
72
|
+
</kulit>
|
|
73
|
+
|
|
74
|
+
<isi>
|
|
75
|
+
gawe cekrego() {
|
|
76
|
+
fetch('/api/produk')
|
|
77
|
+
.then(res => res.json())
|
|
78
|
+
.then(data => {
|
|
79
|
+
alert('Data seko API: ' + JSON.stringify(data));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
</isi>
|
|
83
|
+
</lump>`;
|
|
84
|
+
|
|
85
|
+
const viewProfilTemplate = `<lump>
|
|
86
|
+
<kulit>
|
|
87
|
+
<h1>Profil Pengguna</h1>
|
|
88
|
+
<p>Halo, <strong>{{ nama }}</strong>!</p>
|
|
89
|
+
<a href="/">Bali Mulih</a>
|
|
90
|
+
</kulit>
|
|
91
|
+
</lump>`;
|
|
92
|
+
|
|
93
|
+
const packageJsonTemplate = (name) => `{
|
|
94
|
+
"name": "${name}",
|
|
95
|
+
"version": "1.0.0",
|
|
96
|
+
"main": "jalur/web.js",
|
|
97
|
+
"type": "module",
|
|
98
|
+
"scripts": {
|
|
99
|
+
"start": "lumpia dodolan",
|
|
100
|
+
"serve": "lumpia dodolan",
|
|
101
|
+
"goreng": "lumpia goreng"
|
|
102
|
+
},
|
|
103
|
+
"dependencies": {
|
|
104
|
+
"lumpiajs": "latest"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
export function createProject(parameter) {
|
|
110
|
+
const namaProject = parameter;
|
|
111
|
+
if (!namaProject) {
|
|
112
|
+
console.log('❌ Eh, jeneng project-e opo?');
|
|
113
|
+
console.log('Contoh: lumpia create-project warung-baru');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const root = path.join(process.cwd(), namaProject);
|
|
118
|
+
if (fs.existsSync(root)) return console.log(`❌ Folder ${namaProject} wis ono. Ganti jeneng liyo.`);
|
|
119
|
+
|
|
120
|
+
console.log(`🔨 Mbangun pondasi warung MVC (Model-View-Logika) ing ${namaProject}...`);
|
|
121
|
+
|
|
122
|
+
fs.mkdirSync(root);
|
|
123
|
+
// STRUCTURE UPDATE:
|
|
124
|
+
// app/logika (Controller)
|
|
125
|
+
// app/belakang (Model)
|
|
126
|
+
// depan (View)
|
|
127
|
+
|
|
128
|
+
fs.mkdirSync(path.join(root, 'app', 'logika'), { recursive: true });
|
|
129
|
+
fs.mkdirSync(path.join(root, 'app', 'belakang'), { recursive: true });
|
|
130
|
+
fs.mkdirSync(path.join(root, 'jalur'));
|
|
131
|
+
fs.mkdirSync(path.join(root, 'depan'));
|
|
132
|
+
|
|
133
|
+
// Write files
|
|
134
|
+
fs.writeFileSync(path.join(root, 'package.json'), packageJsonTemplate(namaProject));
|
|
135
|
+
fs.writeFileSync(path.join(root, 'jalur', 'web.js'), routesTemplate);
|
|
136
|
+
fs.writeFileSync(path.join(root, 'app', 'logika', 'BerandaLogika.js'), controllerTemplate);
|
|
137
|
+
fs.writeFileSync(path.join(root, 'app', 'logika', 'ProdukLogika.js'), produkControllerTemplate);
|
|
138
|
+
fs.writeFileSync(path.join(root, 'app', 'belakang', 'Produk.js'), modelTemplate);
|
|
139
|
+
fs.writeFileSync(path.join(root, 'depan', 'beranda.lmp'), viewBerandaTemplate);
|
|
140
|
+
fs.writeFileSync(path.join(root, 'depan', 'profil.lmp'), viewProfilTemplate);
|
|
141
|
+
|
|
142
|
+
console.log('✅ Warung siap! Struktur M-V-L wis ketata rapi.');
|
|
143
|
+
console.log('----------------------------------------------------');
|
|
144
|
+
console.log(`cd ${namaProject}`);
|
|
145
|
+
console.log('npm install <-- (Wajib ben lumpiajs ke-install)');
|
|
146
|
+
console.log('npm run serve <-- (Kanggo dodolan/start server)');
|
|
147
|
+
console.log('----------------------------------------------------');
|
|
148
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import http from 'http';
|
|
4
|
+
import { routes } from '../core/Router.js';
|
|
5
|
+
import { renderLumpia } from '../core/View.js';
|
|
6
|
+
|
|
7
|
+
export async function serveProject() {
|
|
8
|
+
const root = process.cwd();
|
|
9
|
+
const routesFile = path.join(root, 'jalur', 'web.js');
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(routesFile)) return console.log("❌ Iki dudu warung LumpiaJS MVC! (Ora nemu jalur/web.js)");
|
|
12
|
+
|
|
13
|
+
console.log('🔄 Loading core framework...');
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
// Load User Routes
|
|
17
|
+
const userRouteUrl = path.join(root, 'jalur', 'web.js');
|
|
18
|
+
await import('file://' + userRouteUrl);
|
|
19
|
+
|
|
20
|
+
console.log(`🛣️ Total jalur ditemu: ${routes.length}`);
|
|
21
|
+
|
|
22
|
+
// Start Server
|
|
23
|
+
const server = http.createServer(async (req, res) => {
|
|
24
|
+
const method = req.method;
|
|
25
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
26
|
+
const pathname = url.pathname;
|
|
27
|
+
|
|
28
|
+
console.log(`📥 ${method} ${pathname}`);
|
|
29
|
+
|
|
30
|
+
const match = routes.find(r => r.path === pathname && r.method === method);
|
|
31
|
+
|
|
32
|
+
if (match) {
|
|
33
|
+
try {
|
|
34
|
+
// Action format: 'NamaLogika@method'
|
|
35
|
+
const [controllerName, methodName] = match.action.split('@');
|
|
36
|
+
|
|
37
|
+
// Path ke folder 'app/logika'
|
|
38
|
+
const controllerPath = path.join(root, 'app', 'logika', controllerName + '.js');
|
|
39
|
+
|
|
40
|
+
if (!fs.existsSync(controllerPath)) throw new Error(`Logika (Controller) ${controllerName} ora ketemu ning app/logika!`);
|
|
41
|
+
|
|
42
|
+
const module = await import('file://' + controllerPath + '?update=' + Date.now());
|
|
43
|
+
const ControllerClass = module.default;
|
|
44
|
+
const instance = new ControllerClass();
|
|
45
|
+
|
|
46
|
+
if (typeof instance[methodName] !== 'function') {
|
|
47
|
+
throw new Error(`Method ${methodName} ora ono nang ${controllerName}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const result = await instance[methodName]();
|
|
51
|
+
|
|
52
|
+
if (result.type === 'html') {
|
|
53
|
+
res.writeHead(200, {'Content-Type': 'text/html'});
|
|
54
|
+
res.end(result.content);
|
|
55
|
+
} else if (result.type === 'json') {
|
|
56
|
+
res.writeHead(200, {'Content-Type': 'application/json'});
|
|
57
|
+
res.end(result.content);
|
|
58
|
+
} else {
|
|
59
|
+
res.writeHead(200, {'Content-Type': 'text/plain'});
|
|
60
|
+
res.end(String(result));
|
|
61
|
+
}
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.error(e);
|
|
64
|
+
res.writeHead(500, {'Content-Type': 'text/html'});
|
|
65
|
+
res.end(`<h1>500: Kompor Meleduk</h1><pre>${e.message}\n${e.stack}</pre>`);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
res.writeHead(404, {'Content-Type': 'text/html'});
|
|
69
|
+
res.end('<h1>404: Dalane buntu, Mas.</h1>');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const port = 3000;
|
|
74
|
+
server.listen(port, () => {
|
|
75
|
+
console.log(`🚀 Warung buka ning http://localhost:${port}`);
|
|
76
|
+
console.log(`(Tekan Ctrl+C kanggo tutup warung)`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error('Fatal Error saat loading routes:', err);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { renderLumpia } from './View.js';
|
|
4
|
+
|
|
5
|
+
// 3. BASE LOGIKA (Controller)
|
|
6
|
+
export class Logika {
|
|
7
|
+
tampil(viewName, data = {}) {
|
|
8
|
+
// Cek lokasi view relative dari CWD user (folder 'depan')
|
|
9
|
+
const viewPath = path.join(process.cwd(), 'depan', `${viewName}.lmp`);
|
|
10
|
+
if (fs.existsSync(viewPath)) {
|
|
11
|
+
return { type: 'html', content: renderLumpia(viewPath, data) };
|
|
12
|
+
} else {
|
|
13
|
+
return { type: 'html', content: `<h1>404: Tampilan '${viewName}' ora ketemu ning folder 'depan', Lur.</h1>` };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
json(data) {
|
|
18
|
+
return { type: 'json', content: JSON.stringify(data) };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// 1. MODEL (Eloquent-like)
|
|
2
|
+
export class LumpiaModel {
|
|
3
|
+
constructor(data = []) {
|
|
4
|
+
this.data = data;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
static use(jsonData) {
|
|
8
|
+
return new LumpiaModel(jsonData);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Ambil kabeh data
|
|
12
|
+
kabeh() {
|
|
13
|
+
return this.data;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Filter cari (where)
|
|
17
|
+
dimana(key, operator, value) {
|
|
18
|
+
if (value === undefined) { value = operator; operator = '=='; }
|
|
19
|
+
|
|
20
|
+
const filtered = this.data.filter(item => {
|
|
21
|
+
if (operator === '==') return item[key] == value;
|
|
22
|
+
if (operator === '>') return item[key] > value;
|
|
23
|
+
if (operator === '<') return item[key] < value;
|
|
24
|
+
if (operator === '>=') return item[key] >= value;
|
|
25
|
+
if (operator === '<=') return item[key] <= value;
|
|
26
|
+
if (operator === '!=') return item[key] != value;
|
|
27
|
+
return false;
|
|
28
|
+
});
|
|
29
|
+
return new LumpiaModel(filtered);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Ambil satu (first)
|
|
33
|
+
siji() {
|
|
34
|
+
return this.data[0] || null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Return raw array
|
|
38
|
+
jupuk() {
|
|
39
|
+
return this.data;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
// 2. ROUTER : Simpan Jalur
|
|
3
|
+
export const routes = [];
|
|
4
|
+
|
|
5
|
+
export const Jalan = {
|
|
6
|
+
gawe: (path, action) => routes.push({ path, action, method: 'GET' }), // GET
|
|
7
|
+
jupuk: (path, action) => routes.push({ path, action, method: 'GET' }), // Alias GET
|
|
8
|
+
kirim: (path, action) => routes.push({ path, action, method: 'POST' }),
|
|
9
|
+
anyar: (path, action) => routes.push({ path, action, method: 'PUT' }),
|
|
10
|
+
bucak: (path, action) => routes.push({ path, action, method: 'DELETE' }),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Global Router Container (for easy access in runtime)
|
|
14
|
+
global.LumpiaRouter = routes;
|
package/lib/core/View.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
// --- KAMUS BAHASA SEMARANG (Blade-like Template Engine) ---
|
|
4
|
+
// Mengubah sintaks .lmp ke HTML siap render dengan interpolasi data
|
|
5
|
+
export function renderLumpia(viewPath, data = {}) {
|
|
6
|
+
let content = fs.readFileSync(viewPath, 'utf-8');
|
|
7
|
+
|
|
8
|
+
// 1. Ekstrak bagian <klambi>, <kulit>, <isi>
|
|
9
|
+
const matchKulit = content.match(/<kulit>([\s\S]*?)<\/kulit>/);
|
|
10
|
+
const matchIsi = content.match(/<isi>([\s\S]*?)<\/isi>/); // Client-side JS
|
|
11
|
+
const matchKlambi = content.match(/<klambi>([\s\S]*?)<\/klambi>/);
|
|
12
|
+
|
|
13
|
+
let htmlBody = matchKulit ? matchKulit[1] : '';
|
|
14
|
+
let clientScript = matchIsi ? matchIsi[1] : '';
|
|
15
|
+
let cssStyle = matchKlambi ? matchKlambi[1] : '';
|
|
16
|
+
|
|
17
|
+
// 2. Transpile Client-side JS (Semarangan -> JS Standard)
|
|
18
|
+
const dictionary = [
|
|
19
|
+
{ asal: /ono\s/g, jadi: 'let ' },
|
|
20
|
+
{ asal: /paten\s/g, jadi: 'const ' },
|
|
21
|
+
{ asal: /gawe\s/g, jadi: 'function ' },
|
|
22
|
+
{ asal: /yen\s/g, jadi: 'if ' },
|
|
23
|
+
{ asal: /liyane/g, jadi: 'else' },
|
|
24
|
+
{ asal: /mandek;/g, jadi: 'return;' },
|
|
25
|
+
{ asal: /ora\s/g, jadi: '!' },
|
|
26
|
+
{ asal: /panjang\(/g, jadi: 'len(' },
|
|
27
|
+
];
|
|
28
|
+
dictionary.forEach(kata => clientScript = clientScript.replace(kata.asal, kata.jadi));
|
|
29
|
+
|
|
30
|
+
// 3. Templating Engine (Mirip Blade {{ variable }})
|
|
31
|
+
// Mengganti {{ variable }} dengan value dari `data`
|
|
32
|
+
for (const [key, value] of Object.entries(data)) {
|
|
33
|
+
const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g');
|
|
34
|
+
// Simple XSS protection could be added here
|
|
35
|
+
htmlBody = htmlBody.replace(regex, value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 4. Rakit Akhir
|
|
39
|
+
return `<!DOCTYPE html>
|
|
40
|
+
<html>
|
|
41
|
+
<head>
|
|
42
|
+
<title>Lumpia App</title>
|
|
43
|
+
<style>body{font-family:sans-serif;padding:20px;} ${cssStyle}</style>
|
|
44
|
+
</head>
|
|
45
|
+
<body>
|
|
46
|
+
${htmlBody}
|
|
47
|
+
<div id="output-lumpia"></div>
|
|
48
|
+
<script>
|
|
49
|
+
// Runtime Helper
|
|
50
|
+
function tampil(txt) {
|
|
51
|
+
let el = document.getElementById('output-lumpia');
|
|
52
|
+
if(el) el.innerText = txt;
|
|
53
|
+
}
|
|
54
|
+
${clientScript}
|
|
55
|
+
</script>
|
|
56
|
+
</body>
|
|
57
|
+
</html>`;
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lumpiajs",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Bahasa Pemrograman Semarangan",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
6
7
|
"bin": {
|
|
7
|
-
"lumpia": "./
|
|
8
|
+
"lumpia": "./bin/lumpia.js"
|
|
8
9
|
},
|
|
9
10
|
"dependencies": {
|
|
10
11
|
"fs-extra": "^11.1.1"
|