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.
@@ -1,7 +1,5 @@
1
1
 
2
- // ... (KAMUS dan Helper functions sama seperti sebelumnya) ...
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'"); // FIX: Relative import to core
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
- // ADJUST IMPORT PATHS FOR BROWSER (CRITICAL!)
57
- // Controller/Route ada di kedalaman tertentu. Core ada di /dist/core.
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
- // Normalize path: Remove base folder logic needed?
151
- // Simple approach: Match pathname against routes.
152
- // If app is in /my-app/, and route is '/', browser pathname is '/my-app/'.
153
- // WE NEED TO STRIP THE BASE PATH.
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
- // Hacky detection of Base Path (where index.html sits)
156
- const basePath = window.location.pathname.replace(/\/index\.html$/, '').replace(/\/$/, '');
157
- let currentPath = window.location.pathname.replace(/\/index\.html$/, '');
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
- if (!currentPath) currentPath = '/'; // Root
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
- // Route defined as '/home'
166
- let reg = new RegExp('^'+r.p.replace(/{([a-zA-Z0-9_]+)}/g, '([^/]+)')+'$');
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>Path: '+currentPath+'</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 Bahasa Semarangan + Relative Paths)...');
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
- // Process using Relative Path Logic
216
- // We pass 'rootDepth' to calculate imports to 'core'
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! (Support Subfolder Hosting)');
218
+ console.log('✅ Mateng! Siap upload ke Laragon/Hosting.');
235
219
  }
@@ -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}" Ready!`);
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
  }
@@ -1,10 +1,4 @@
1
- import fs from 'fs';
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
- const module = await import('file://' + destPath + '?t=' + Date.now());
81
- return module;
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
- // Cek URL, jika '/' tapi masih loading, itu karena logic view belum sempurna?
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 running at http://localhost:${port}`));
165
- } catch (e) { console.error(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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lumpiajs",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Bahasa Pemrograman Semarangan",
5
5
  "type": "module",
6
6
  "main": "index.js",