lumpiajs 1.0.11 → 1.0.12
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/lib/commands/build.js +42 -58
- package/lib/commands/create.js +6 -1
- package/lib/commands/serve.js +36 -35
- package/package.json +1 -1
package/lib/commands/build.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
// ... (KAMUS
|
|
3
|
-
// Saya akan fokus update browserCore dan indexHtml
|
|
4
|
-
|
|
2
|
+
// ... (KAMUS & Imports same as before) ...
|
|
5
3
|
const KAMUS = [
|
|
6
4
|
{ from: /paten\s/g, to: 'const ' },
|
|
7
5
|
{ from: /ono\s/g, to: 'let ' },
|
|
@@ -27,11 +25,7 @@ import { loadConfig } from '../core/Config.js';
|
|
|
27
25
|
function transpileSemarangan(content) {
|
|
28
26
|
let code = content;
|
|
29
27
|
code = code.replace(/from\s+['"](.+?)\.lmp['"]/g, "from '$1.js'");
|
|
30
|
-
code = code.replace(/from\s+['"]lumpiajs['"]/g, "from './core/lumpia.js'");
|
|
31
|
-
|
|
32
|
-
// Handle imports agar path-nya relatif browser freindly
|
|
33
|
-
// Jika import './...' -> aman.
|
|
34
|
-
// Jika import '/...' -> bahaya kalau di subfolder.
|
|
28
|
+
code = code.replace(/from\s+['"]lumpiajs['"]/g, "from './core/lumpia.js'");
|
|
35
29
|
|
|
36
30
|
code = code.split('\n').map(line => {
|
|
37
31
|
let l = line;
|
|
@@ -52,19 +46,16 @@ function processDirectory(source, dest, rootDepth = 0) {
|
|
|
52
46
|
} else {
|
|
53
47
|
if (item.endsWith('.lmp') || item.endsWith('.js')) {
|
|
54
48
|
let content = fs.readFileSync(srcPath, 'utf8');
|
|
49
|
+
let backSteps = rootDepth > 0 ? '../'.repeat(rootDepth) : './';
|
|
50
|
+
if(rootDepth === 0) backSteps = './'; // core is in dist/core/.
|
|
51
|
+
|
|
52
|
+
// Fix path calculation to Core
|
|
53
|
+
// dist/routes/web.js (depth 1) -> ../core/lumpia.js
|
|
54
|
+
// dist/app/controllers/Home.js (depth 2) -> ../../core/lumpia.js
|
|
55
55
|
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
// Kita harus rewrite "from 'lumpiajs'" menjadi path relative yang benar ke core.
|
|
59
|
-
// Misal dari /app/controllers (depth 2) -> '../../core/lumpia.js'
|
|
56
|
+
// Let's rely on simple replace logic based on file type location usually known
|
|
57
|
+
// But dynamic is better.
|
|
60
58
|
|
|
61
|
-
// Hitung relative backstep
|
|
62
|
-
let backSteps = '../'.repeat(rootDepth);
|
|
63
|
-
// Karena structure dist:
|
|
64
|
-
// dist/routes/web.js (depth 1) -> butuh '../core/lumpia.js'
|
|
65
|
-
// dist/app/controllers/Home.js (depth 2) -> butuh '../../core/lumpia.js'
|
|
66
|
-
// Jadi logicnya benar.
|
|
67
|
-
|
|
68
59
|
content = content.replace(/from\s+['"]lumpiajs['"]/g, `from '${backSteps}../core/lumpia.js'`);
|
|
69
60
|
|
|
70
61
|
content = transpileSemarangan(content);
|
|
@@ -82,15 +73,10 @@ export class Controller {
|
|
|
82
73
|
constructor() { this.params={}; }
|
|
83
74
|
async tampil(viewName, data={}) {
|
|
84
75
|
try {
|
|
85
|
-
// Fetch view relative to root (we assume <base> tag is set or we detect root)
|
|
86
|
-
// Biar aman, kita cari 'views' relatif terhadap posisi script ini? Gak bisa.
|
|
87
|
-
// Kita asumsi deployment di root atau subfolder dengan <base> tag HTML yang benar.
|
|
88
|
-
|
|
89
76
|
const res = await fetch('views/'+viewName+'.lmp');
|
|
90
77
|
if(!res.ok) throw new Error('View 404: ' + viewName);
|
|
91
78
|
let html = await res.text();
|
|
92
79
|
|
|
93
|
-
// ... (Parsing logic same as before) ...
|
|
94
80
|
const matchKulit = html.match(/<kulit>([\\s\\S]*?)<\\/kulit>/);
|
|
95
81
|
const matchIsi = html.match(/<isi>([\\s\\S]*?)<\\/isi>/);
|
|
96
82
|
const matchKlambi = html.match(/<klambi>([\\s\\S]*?)<\\/klambi>/);
|
|
@@ -130,40 +116,44 @@ const indexHtml = `<!DOCTYPE html>
|
|
|
130
116
|
<meta charset="UTF-8">
|
|
131
117
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
132
118
|
<title>LumpiaJS</title>
|
|
133
|
-
<!-- BASE HREF AUTOMATIC DETECTION MAGIC -->
|
|
134
|
-
<script>
|
|
135
|
-
// Set base href to current folder so imports work in subfolders
|
|
136
|
-
// But for SPA routing, this might fight with History API.
|
|
137
|
-
// Let's rely on relative imports './' everywhere.
|
|
138
|
-
// document.write("<base href='" + document.location.pathname.replace(/index\\.html$/,'') + "' />");
|
|
139
|
-
</script>
|
|
140
119
|
<link rel="stylesheet" href="public/css/style.css">
|
|
141
120
|
</head>
|
|
142
121
|
<body>
|
|
143
122
|
<div id="app">Loading...</div>
|
|
144
123
|
|
|
145
124
|
<script type="module">
|
|
146
|
-
// Import paths WITHOUT leading slash to be relative to index.html
|
|
147
125
|
import { Jalan } from './routes/web.js';
|
|
148
126
|
|
|
149
127
|
async function navigate() {
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
128
|
+
// FIX: Gunakan new RegExp string biar aman dari escaping hell
|
|
129
|
+
const cleanPath = (p) => p.replace(new RegExp('/index\\\\.html$'), '').replace(new RegExp('/$'), '');
|
|
130
|
+
|
|
131
|
+
const basePath = cleanPath(window.location.pathname);
|
|
132
|
+
// Tapi tunggu, basePath itu harusnya folder ROOT project.
|
|
133
|
+
// Misal: /2025/lumpiajs/
|
|
134
|
+
// URL saat ini: /2025/lumpiajs/index.html -> Base: /2025/lumpiajs
|
|
135
|
+
// Kalau kita navigate ke sub-route (via history pushState): /2025/lumpiajs/produk
|
|
136
|
+
// Maka window.location.pathname adalah /2025/lumpiajs/produk.
|
|
137
|
+
// Kita HARUS tau base path awalnya apa. Kita bisa tebak dari script tag src location?
|
|
138
|
+
// Atau kita asumsikan saat pertama load (yang biasanya index.html), itu lah base path.
|
|
154
139
|
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (basePath && currentPath.startsWith(basePath)) {
|
|
159
|
-
currentPath = currentPath.substring(basePath.length);
|
|
140
|
+
// Trik: Simpan base path di window variable saat pertama load (index.html)
|
|
141
|
+
if (!window.LUMPIA_BASE) {
|
|
142
|
+
window.LUMPIA_BASE = window.location.pathname.replace(new RegExp('/index\\\\.html$'), '').replace(new RegExp('/$'), '');
|
|
160
143
|
}
|
|
161
|
-
|
|
144
|
+
|
|
145
|
+
let currentPath = window.location.pathname;
|
|
146
|
+
// Remove Base Path from Current Path to get "Route Path"
|
|
147
|
+
if (currentPath.startsWith(window.LUMPIA_BASE)) {
|
|
148
|
+
currentPath = currentPath.substring(window.LUMPIA_BASE.length);
|
|
149
|
+
}
|
|
150
|
+
if (!currentPath || currentPath === '/index.html') currentPath = '/';
|
|
162
151
|
|
|
163
152
|
let m = null, args = {};
|
|
164
153
|
for(let r of Jalan.routes) {
|
|
165
|
-
//
|
|
166
|
-
let
|
|
154
|
+
// Regex: Replace {slug} -> ([^/]+)
|
|
155
|
+
let regexStr = '^' + r.p.replace(/{([a-zA-Z0-9_]+)}/g, '([^/]+)') + '$';
|
|
156
|
+
let reg = new RegExp(regexStr);
|
|
167
157
|
let res = currentPath.match(reg);
|
|
168
158
|
if(res){ m=r; args=res.slice(1); break; }
|
|
169
159
|
}
|
|
@@ -171,7 +161,6 @@ const indexHtml = `<!DOCTYPE html>
|
|
|
171
161
|
if(m) {
|
|
172
162
|
const [cName, fName] = m.a.split('@');
|
|
173
163
|
try {
|
|
174
|
-
// Import Relative to index.html
|
|
175
164
|
const mod = await import('./app/controllers/'+cName+'.js?'+Date.now());
|
|
176
165
|
const C = mod.default; const i = new C(); i.params=args;
|
|
177
166
|
await i[fName](...args);
|
|
@@ -180,7 +169,7 @@ const indexHtml = `<!DOCTYPE html>
|
|
|
180
169
|
document.getElementById('app').innerHTML='<h1>Error Loading Controller</h1><pre>'+e.message+'</pre>';
|
|
181
170
|
}
|
|
182
171
|
} else {
|
|
183
|
-
document.getElementById('app').innerHTML='<h1>404 Not Found</h1><p>
|
|
172
|
+
document.getElementById('app').innerHTML='<h1>404 Not Found</h1><p>Route: '+currentPath+'</p>';
|
|
184
173
|
}
|
|
185
174
|
}
|
|
186
175
|
|
|
@@ -188,11 +177,13 @@ const indexHtml = `<!DOCTYPE html>
|
|
|
188
177
|
document.body.addEventListener('click', e => {
|
|
189
178
|
if(e.target.tagName==='A' && e.target.href.startsWith(window.location.origin)) {
|
|
190
179
|
e.preventDefault();
|
|
191
|
-
// Push relative path?
|
|
192
180
|
history.pushState(null,'',e.target.href);
|
|
193
181
|
navigate();
|
|
194
182
|
}
|
|
195
183
|
});
|
|
184
|
+
|
|
185
|
+
// Init Base Path immediately
|
|
186
|
+
window.LUMPIA_BASE = window.location.pathname.replace(new RegExp('/index\\\\.html$'), '').replace(new RegExp('/$'), '');
|
|
196
187
|
navigate();
|
|
197
188
|
</script>
|
|
198
189
|
</body>
|
|
@@ -203,7 +194,7 @@ export function buildProject() {
|
|
|
203
194
|
const dist = path.join(root, 'dist');
|
|
204
195
|
const config = loadConfig(root);
|
|
205
196
|
|
|
206
|
-
console.log('🍳 Goreng Project (Mode
|
|
197
|
+
console.log('🍳 Goreng Project (Mode Aman)...');
|
|
207
198
|
if (fs.existsSync(dist)) fs.rmSync(dist, { recursive: true, force: true });
|
|
208
199
|
fs.mkdirSync(dist);
|
|
209
200
|
|
|
@@ -212,10 +203,8 @@ export function buildProject() {
|
|
|
212
203
|
spawnSync(cmd, ['tailwindcss', '-i', './aset/css/style.css', '-o', './public/css/style.css', '--minify'], { cwd: root, stdio: 'ignore', shell: true });
|
|
213
204
|
}
|
|
214
205
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
processDirectory(path.join(root, 'app'), path.join(dist, 'app'), 2); // app/controllers/X -> depth 2 from dist
|
|
218
|
-
processDirectory(path.join(root, 'routes'), path.join(dist, 'routes'), 1); // routes/X -> depth 1 from dist
|
|
206
|
+
processDirectory(path.join(root, 'app'), path.join(dist, 'app'), 2);
|
|
207
|
+
processDirectory(path.join(root, 'routes'), path.join(dist, 'routes'), 1);
|
|
219
208
|
|
|
220
209
|
fs.cpSync(path.join(root, 'views'), path.join(dist, 'views'), { recursive: true });
|
|
221
210
|
if (fs.existsSync(path.join(root, 'public'))) fs.cpSync(path.join(root, 'public'), path.join(dist, 'public'), { recursive: true });
|
|
@@ -224,12 +213,7 @@ export function buildProject() {
|
|
|
224
213
|
fs.writeFileSync(path.join(dist, 'core', 'lumpia.js'), browserCore);
|
|
225
214
|
fs.writeFileSync(path.join(dist, 'index.html'), indexHtml);
|
|
226
215
|
|
|
227
|
-
// .htaccess for "Subfolder Friendly" SPA?
|
|
228
|
-
// RewriteRule ^index\.html$ - [L]
|
|
229
|
-
// RewriteCond %{REQUEST_FILENAME} !-f
|
|
230
|
-
// RewriteRule . index.html [L]
|
|
231
|
-
// Note: Removed leading slash in redirect destination to be relative
|
|
232
216
|
fs.writeFileSync(path.join(dist, '.htaccess'), `<IfModule mod_rewrite.c>\nRewriteEngine On\nRewriteRule ^index\\.html$ - [L]\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule . index.html [L]\n</IfModule>`);
|
|
233
217
|
|
|
234
|
-
console.log('✅ Mateng!
|
|
218
|
+
console.log('✅ Mateng! Siap upload ke Laragon/Hosting.');
|
|
235
219
|
}
|
package/lib/commands/create.js
CHANGED
|
@@ -119,5 +119,10 @@ export async function createProject(parameter) {
|
|
|
119
119
|
fs.writeFileSync(path.join(root, 'aset', 'css', 'style.css'), '/* CSS */');
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
console.log(`✅ Project "${projectName}"
|
|
122
|
+
console.log(`✅ Project "${projectName}" Siap!`);
|
|
123
|
+
console.log('-------------------------------------------');
|
|
124
|
+
console.log(`👉 cd ${projectName}`);
|
|
125
|
+
console.log(`👉 npm install <-- OJO LALI IKI! (Wajib)`);
|
|
126
|
+
console.log(`👉 lumpia kukus`);
|
|
127
|
+
console.log('-------------------------------------------');
|
|
123
128
|
}
|
package/lib/commands/serve.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import http from 'http';
|
|
4
|
-
import { spawn } from 'child_process';
|
|
5
|
-
import { loadEnv } from '../core/Env.js';
|
|
6
|
-
import { loadConfig } from '../core/Config.js';
|
|
7
|
-
|
|
1
|
+
// ... (Same KAMUS & Imports) ...
|
|
8
2
|
const KAMUS = [
|
|
9
3
|
{ from: /paten\s/g, to: 'const ' },
|
|
10
4
|
{ from: /ono\s/g, to: 'let ' },
|
|
@@ -22,6 +16,13 @@ const KAMUS = [
|
|
|
22
16
|
{ from: /->/g, to: '.' }
|
|
23
17
|
];
|
|
24
18
|
|
|
19
|
+
import fs from 'fs';
|
|
20
|
+
import path from 'path';
|
|
21
|
+
import http from 'http';
|
|
22
|
+
import { spawn } from 'child_process';
|
|
23
|
+
import { loadEnv } from '../core/Env.js';
|
|
24
|
+
import { loadConfig } from '../core/Config.js';
|
|
25
|
+
|
|
25
26
|
function matchRoute(definedRoute, method, pathname) {
|
|
26
27
|
if (definedRoute.method !== method) return null;
|
|
27
28
|
if (definedRoute.path === pathname) return { params: {} };
|
|
@@ -57,18 +58,8 @@ async function loadLumpiaModule(filePath) {
|
|
|
57
58
|
const flatName = relativePath.replace(/[\/\\]/g, '_').replace('.lmp', '.js');
|
|
58
59
|
const destPath = path.join(cacheDir, flatName);
|
|
59
60
|
|
|
60
|
-
// INI DEV SERVER: Path ke Core Library bisa tetap absolute (package) atau relative
|
|
61
|
-
// Karena ini dijalankan di Node.js, 'lumpiajs' masih resolve ke node_modules dengan benar.
|
|
62
|
-
// TAPI content user (seperti `from 'lumpiajs'`) harus dijaga tetap valid buat Node.
|
|
63
|
-
// Jika user nulis `from 'lumpiajs'`, biarkan.
|
|
64
|
-
// Hanya transpile Semarangan -> JS.
|
|
65
|
-
|
|
66
61
|
let transcoded = originalContent;
|
|
67
|
-
|
|
68
|
-
// DEV MODE: Keep .lmp imports as .js imports
|
|
69
62
|
transcoded = transcoded.replace(/from\s+['"](.+?)\.lmp['"]/g, "from '$1.js'");
|
|
70
|
-
// DEV MODE: Do NOT rewrite 'lumpiajs' import because Node handles it via package.json resolution
|
|
71
|
-
|
|
72
63
|
transcoded = transcoded.split('\n').map(line => {
|
|
73
64
|
let l = line;
|
|
74
65
|
if (l.trim().startsWith('//')) return l;
|
|
@@ -77,14 +68,33 @@ async function loadLumpiaModule(filePath) {
|
|
|
77
68
|
}).join('\n');
|
|
78
69
|
|
|
79
70
|
fs.writeFileSync(destPath, transcoded);
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
|
|
72
|
+
// Safety check for user forgetting npm install
|
|
73
|
+
try {
|
|
74
|
+
const module = await import('file://' + destPath + '?t=' + Date.now());
|
|
75
|
+
return module;
|
|
76
|
+
} catch (e) {
|
|
77
|
+
if (e.code === 'ERR_MODULE_NOT_FOUND' && e.message.includes('lumpiajs')) {
|
|
78
|
+
console.error('\n❌ ERROR: Gagal load "lumpiajs".');
|
|
79
|
+
console.error('💡 Sampeyan wis njalanke "npm install" durung?');
|
|
80
|
+
console.error(' Ketik: npm install\n');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
throw e;
|
|
84
|
+
}
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
export async function serveProject() {
|
|
85
88
|
const root = process.cwd();
|
|
86
89
|
const routesFile = path.join(root, 'routes', 'web.lmp');
|
|
87
|
-
if (!fs.existsSync(routesFile)) return console.log("❌ Missing routes/web.lmp");
|
|
90
|
+
if (!fs.existsSync(routesFile)) return console.log("❌ Not a LumpiaJS Project (Missing routes/web.lmp)");
|
|
91
|
+
|
|
92
|
+
// Pre-check node_modules
|
|
93
|
+
if (!fs.existsSync(path.join(root, 'node_modules'))) {
|
|
94
|
+
console.log("⚠️ Waduh, folder 'node_modules' ra ono!");
|
|
95
|
+
console.log(" Tolong jalanne 'npm install' sek ya, Lur.");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
88
98
|
|
|
89
99
|
const env = loadEnv(root);
|
|
90
100
|
const config = loadConfig(root);
|
|
@@ -94,18 +104,8 @@ export async function serveProject() {
|
|
|
94
104
|
await loadLumpiaModule(routesFile);
|
|
95
105
|
const activeRoutes = global.LumpiaRouter || [];
|
|
96
106
|
console.log(`🛣️ Routes registered: ${activeRoutes.length}`);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// Di Dev Server (Node), kita merender HTML string langsung (SSR).
|
|
100
|
-
// Jadi tidak ada masalah "loading..." dari sisi client-side fetch.
|
|
101
|
-
// Jika dev server loading terus, itu karena res.end() tidak terpanggil.
|
|
102
|
-
|
|
103
|
-
// Periksa controller user: apakah memanggil `balek` (return)?
|
|
104
|
-
// Jika user lupa return, function return undefined.
|
|
105
|
-
// Logic di bawah menangani undefined result -> `res.end(String(result))` -> `undefined`.
|
|
106
|
-
// Browser akan menampilkan "undefined". Bukan loading terus.
|
|
107
|
-
// Loading terus biasanya hang.
|
|
108
|
-
|
|
107
|
+
console.log(`✨ Mode: Semarangan (paten, ono, fungsi, aku)`);
|
|
108
|
+
|
|
109
109
|
const server = http.createServer(async (req, res) => {
|
|
110
110
|
const method = req.method;
|
|
111
111
|
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
@@ -149,7 +149,6 @@ export async function serveProject() {
|
|
|
149
149
|
res.writeHead(200, {'Content-Type': result.type==='json'?'application/json':'text/html'});
|
|
150
150
|
res.end(result.content);
|
|
151
151
|
} else {
|
|
152
|
-
// FIX: Jika result null/undefined (lupa return balek), kirim response kosong atau error
|
|
153
152
|
if(result === undefined) res.end('');
|
|
154
153
|
else res.end(String(result));
|
|
155
154
|
}
|
|
@@ -161,6 +160,8 @@ export async function serveProject() {
|
|
|
161
160
|
}
|
|
162
161
|
});
|
|
163
162
|
const port = 3000;
|
|
164
|
-
server.listen(port, () => console.log(`🚀 Server
|
|
165
|
-
} catch (e) {
|
|
163
|
+
server.listen(port, () => console.log(`🚀 Server: http://localhost:3000`));
|
|
164
|
+
} catch (e) {
|
|
165
|
+
if(e.code !== 'ERR_MODULE_NOT_FOUND') console.error(e);
|
|
166
|
+
}
|
|
166
167
|
}
|