lumpiajs 1.0.13 → 1.0.14

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.
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "be-tester",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "start": "lumpia kukus",
7
+ "build": "lumpia goreng"
8
+ },
9
+ "dependencies": {
10
+ "lumpiajs": "latest"
11
+ },
12
+ "devDependencies": {
13
+ "tailwindcss": "^3.4.0"
14
+ }
15
+ }
@@ -0,0 +1 @@
1
+ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}
@@ -0,0 +1,4 @@
1
+ import { Jalan } from 'lumpiajs';
2
+
3
+ Jalan->get('/', 'HomeController@index');
4
+ Jalan->get('/toko', 'ProductController@tampilBarang');
@@ -0,0 +1 @@
1
+ module.exports={content:["./views/**/*.lmp"],theme:{extend:{}},plugins:[]}
@@ -0,0 +1,12 @@
1
+ <lump>
2
+ <klambi>
3
+ h1 { color: #d35400; text-align: center; }
4
+ </klambi>
5
+ <kulit>
6
+ <div style="text-align: center; font-family: sans-serif; margin-top: 50px;">
7
+ <h1>{{ message }}</h1>
8
+ <p>{{ info }}</p>
9
+ <a href="/toko">Cek Toko Sebelah</a>
10
+ </div>
11
+ </kulit>
12
+ </lump>
@@ -0,0 +1,9 @@
1
+ <lump>
2
+ <kulit>
3
+ <div style="padding: 20px; font-family: sans-serif;">
4
+ <h1>Daftar Barang</h1>
5
+ <ul>{{ daftar }}</ul>
6
+ <a href="/">Balik Omah</a>
7
+ </div>
8
+ </kulit>
9
+ </lump>
@@ -1,5 +1,5 @@
1
1
 
2
- // ... (KAMUS same as before) ...
2
+ // ... (KAMUS same) ...
3
3
  const KAMUS = [
4
4
  { from: /paten\s/g, to: 'const ' },
5
5
  { from: /ono\s/g, to: 'let ' },
@@ -24,13 +24,7 @@ import { loadConfig } from '../core/Config.js';
24
24
 
25
25
  function transpileSemarangan(content) {
26
26
  let code = content;
27
- // 1. Keep .lmp import relative for now, or assume .js
28
27
  code = code.replace(/from\s+['"](.+?)\.lmp['"]/g, "from '$1.js'");
29
-
30
- // 2. DO NOT TOUCH 'lumpiajs' IMPORT.
31
- // We will use Import Map in index.html to resolve 'lumpiajs' to './core/lumpia.js'
32
- // This solves the relative path hell once and for all.
33
-
34
28
  code = code.split('\n').map(line => {
35
29
  let l = line;
36
30
  if (l.trim().startsWith('//')) return l;
@@ -60,26 +54,16 @@ function processDirectory(source, dest, rootDepth = 0) {
60
54
  });
61
55
  }
62
56
 
63
- const browserCore = `
57
+ // BROWSER CORE (Exported so serve.js can use it too if needed)
58
+ export const browserCore = `
64
59
  export class Controller {
65
60
  constructor() { this.params={}; }
66
61
  async tampil(viewName, data={}) {
67
62
  try {
68
- // Fetch view must be relative to root of dist.
69
- // Since we use Import Map, we are modernized.
70
- // Better to assume deployment at known base or use explicit relative if possible.
71
- // But fetch('views/...') is relative to current page URL.
72
- // If current page is /2025/lumpiajs/produk, fetch('views/home.lmp') -> /2025/lumpiajs/views/home.lmp (Works!)
73
- // Wait, if /produk is virtual path, browser thinks we are in folder /lumpiajs/.
74
- // If /produk/detail/1 -> browser thinks folder is /lumpiajs/produk/detail/.
75
- // Fetch 'views/...' will fail (404).
76
-
77
- // SOLUSI: Selalu fetch dari ROOT relative (pakai Window Base yang kita set di index.html)
78
63
  const basePath = window.LUMPIA_BASE || '';
79
- // Ensure slash
80
64
  const url = basePath + '/views/' + viewName + '.lmp';
81
-
82
- const res = await fetch(url.replace('//', '/')); // simple cleanup
65
+ // Simple double-slash cleanup
66
+ const res = await fetch(url.replace(/([^:])\\/\\//g, '$1/'));
83
67
  if(!res.ok) throw new Error('View 404: ' + viewName);
84
68
  let html = await res.text();
85
69
 
@@ -122,7 +106,6 @@ const indexHtml = `<!DOCTYPE html>
122
106
  <meta charset="UTF-8">
123
107
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
124
108
  <title>LumpiaJS</title>
125
- <!-- IMPORT MAP: KUNCI SUKSES LOAD MODULE DI SUBFOLDER -->
126
109
  <script type="importmap">
127
110
  {
128
111
  "imports": {
@@ -132,25 +115,20 @@ const indexHtml = `<!DOCTYPE html>
132
115
  </script>
133
116
  <link rel="stylesheet" href="public/css/style.css">
134
117
  <script>
135
- // DETEKSI BASE PATH DENGAN LEBIH AMAN (Via current script location fallback)
136
- // Kita ambil lokasi index.html ini sebagai 'Source of Truth' root aplikasi.
137
118
  window.LUMPIA_BASE = window.location.pathname
138
119
  .replace(new RegExp('/index\\\\.html$'), '')
139
120
  .replace(new RegExp('/$'), '');
140
- // console.log('Base:', window.LUMPIA_BASE);
141
121
  </script>
142
122
  </head>
143
123
  <body>
144
124
  <div id="app">Loading...</div>
145
-
146
125
  <script type="module">
147
- // Import Map makes this work:
148
- import { Jalan } from './routes/web.js';
126
+ // FIX: Import Jalan from CORE, not module. Module has side effects only.
127
+ import { Jalan } from 'lumpiajs';
128
+ import './routes/web.js';
149
129
 
150
130
  async function navigate() {
151
131
  let currentPath = window.location.pathname;
152
-
153
- // Normalize: Strip Base Path
154
132
  if (window.LUMPIA_BASE && currentPath.startsWith(window.LUMPIA_BASE)) {
155
133
  currentPath = currentPath.substring(window.LUMPIA_BASE.length);
156
134
  }
@@ -167,7 +145,6 @@ const indexHtml = `<!DOCTYPE html>
167
145
  if(m) {
168
146
  const [cName, fName] = m.a.split('@');
169
147
  try {
170
- // Import Relative to index.html (selalu root dist)
171
148
  const mod = await import('./app/controllers/'+cName+'.js?'+Date.now());
172
149
  const C = mod.default; const i = new C(); i.params=args;
173
150
  await i[fName](...args);
@@ -181,21 +158,15 @@ const indexHtml = `<!DOCTYPE html>
181
158
  }
182
159
 
183
160
  window.addEventListener('popstate', navigate);
184
-
185
- // SPA LINK INTERCEPTOR (SAT SET MODE)
186
161
  document.body.addEventListener('click', e => {
187
- // Cari elemen <a> (bisa jadi klik di span dalam a)
188
162
  const link = e.target.closest('a');
189
163
  if (link && link.href.startsWith(window.location.origin)) {
190
- // Ignore if target blank or control click
191
164
  if (link.target === '_blank' || e.ctrlKey) return;
192
-
193
165
  e.preventDefault();
194
166
  history.pushState(null, '', link.href);
195
167
  navigate();
196
168
  }
197
169
  });
198
-
199
170
  navigate();
200
171
  </script>
201
172
  </body>
@@ -206,7 +177,7 @@ export function buildProject() {
206
177
  const dist = path.join(root, 'dist');
207
178
  const config = loadConfig(root);
208
179
 
209
- console.log('🍳 Goreng Project (Mode Import Map + Sat Set SPA)...');
180
+ console.log('🍳 Goreng Project (Fix Import Jalan)...');
210
181
  if (fs.existsSync(dist)) fs.rmSync(dist, { recursive: true, force: true });
211
182
  fs.mkdirSync(dist);
212
183
 
@@ -227,5 +198,5 @@ export function buildProject() {
227
198
 
228
199
  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>`);
229
200
 
230
- console.log('✅ Mateng! Siap dihidangkan di folder manapun.');
201
+ console.log('✅ Mateng!');
231
202
  }
@@ -1,4 +1,12 @@
1
- // ... (Same KAMUS & Imports) ...
1
+
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import http from 'http';
5
+ import { spawn } from 'child_process';
6
+ import { loadEnv } from '../core/Env.js';
7
+ import { loadConfig } from '../core/Config.js';
8
+ import { browserCore } from './build.js'; // Use the same Browser Core logic
9
+
2
10
  const KAMUS = [
3
11
  { from: /paten\s/g, to: 'const ' },
4
12
  { from: /ono\s/g, to: 'let ' },
@@ -16,30 +24,16 @@ const KAMUS = [
16
24
  { from: /->/g, to: '.' }
17
25
  ];
18
26
 
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
-
26
- function matchRoute(definedRoute, method, pathname) {
27
- if (definedRoute.method !== method) return null;
28
- if (definedRoute.path === pathname) return { params: {} };
29
- const paramNames = [];
30
- const regexPath = definedRoute.path.replace(/\{([a-zA-Z0-9_]+)\}/g, (match, name) => {
31
- paramNames.push(name);
32
- return '([^/]+)';
33
- });
34
- if (regexPath === definedRoute.path) return null;
35
- const regex = new RegExp(`^${regexPath}$`);
36
- const match = pathname.match(regex);
37
- if (match) {
38
- const params = {};
39
- paramNames.forEach((name, index) => { params[name] = match[index + 1]; });
40
- return { params };
41
- }
42
- return null;
27
+ function transpileOnTheFly(content) {
28
+ let code = content;
29
+ code = code.replace(/from\s+['"](.+?)\.lmp['"]/g, "from '$1.js'");
30
+ code = code.split('\n').map(line => {
31
+ let l = line;
32
+ if (l.trim().startsWith('//')) return l;
33
+ KAMUS.forEach(rule => { l = l.replace(rule.from, rule.to); });
34
+ return l;
35
+ }).join('\n');
36
+ return code;
43
37
  }
44
38
 
45
39
  const backgroundProcess = [];
@@ -49,119 +43,138 @@ function startTailwindWatcher(root) {
49
43
  backgroundProcess.push(tailwind);
50
44
  }
51
45
 
52
- async function loadLumpiaModule(filePath) {
53
- const originalContent = fs.readFileSync(filePath, 'utf-8');
54
- const cacheDir = path.join(process.cwd(), '.lumpia', 'cache');
55
- if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
56
-
57
- const relativePath = path.relative(process.cwd(), filePath);
58
- const flatName = relativePath.replace(/[\/\\]/g, '_').replace('.lmp', '.js');
59
- const destPath = path.join(cacheDir, flatName);
46
+ // Dev Template (Similar to Build index.html)
47
+ const devIndexHtml = `<!DOCTYPE html>
48
+ <html lang="en">
49
+ <head>
50
+ <meta charset="UTF-8">
51
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
52
+ <title>LumpiaJS (Dev)</title>
53
+ <script type="importmap">
54
+ {
55
+ "imports": {
56
+ "lumpiajs": "/core/lumpia.js"
57
+ }
58
+ }
59
+ </script>
60
+ <link rel="stylesheet" href="/public/css/style.css">
61
+ <script>window.LUMPIA_BASE = '';</script>
62
+ </head>
63
+ <body>
64
+ <div id="app">Loading...</div>
65
+ <script type="module">
66
+ import { Jalan } from 'lumpiajs';
67
+ import '/routes/web.js';
68
+
69
+ async function navigate() {
70
+ let currentPath = window.location.pathname;
71
+ if (!currentPath || currentPath === '/index.html') currentPath = '/';
60
72
 
61
- let transcoded = originalContent;
62
- transcoded = transcoded.replace(/from\s+['"](.+?)\.lmp['"]/g, "from '$1.js'");
63
- transcoded = transcoded.split('\n').map(line => {
64
- let l = line;
65
- if (l.trim().startsWith('//')) return l;
66
- KAMUS.forEach(rule => l = l.replace(rule.from, rule.to));
67
- return l;
68
- }).join('\n');
73
+ let m = null, args = {};
74
+ for(let r of Jalan.routes) {
75
+ let regexStr = '^' + r.p.replace(/{([a-zA-Z0-9_]+)}/g, '([^/]+)') + '$';
76
+ let reg = new RegExp(regexStr);
77
+ let res = currentPath.match(reg);
78
+ if(res){ m=r; args=res.slice(1); break; }
79
+ }
69
80
 
70
- fs.writeFileSync(destPath, transcoded);
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);
81
+ if(m) {
82
+ const [cName, fName] = m.a.split('@');
83
+ try {
84
+ // In Dev, we serve /app/... URLs directly map to filesystem
85
+ const mod = await import('/app/controllers/'+cName+'.js?'+Date.now());
86
+ const C = mod.default; const i = new C(); i.params=args;
87
+ await i[fName](...args);
88
+ } catch(e) {
89
+ console.error(e);
90
+ document.getElementById('app').innerHTML='<h1>Dev Error</h1><pre>'+e.message+'</pre>';
91
+ }
92
+ } else {
93
+ document.getElementById('app').innerHTML='<h1>404 Not Found</h1><p>Route: '+currentPath+'</p>';
94
+ }
82
95
  }
83
- throw e;
84
- }
85
- }
96
+
97
+ window.addEventListener('popstate', navigate);
98
+ document.body.addEventListener('click', e => {
99
+ const link = e.target.closest('a');
100
+ if (link && link.href.startsWith(window.location.origin)) {
101
+ if (link.target === '_blank' || e.ctrlKey) return;
102
+ e.preventDefault();
103
+ history.pushState(null, '', link.href);
104
+ navigate();
105
+ }
106
+ });
107
+ navigate();
108
+ </script>
109
+ </body>
110
+ </html>`;
86
111
 
87
112
  export async function serveProject() {
88
113
  const root = process.cwd();
89
114
  const routesFile = path.join(root, '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
- }
115
+ if (!fs.existsSync(routesFile)) return console.log("❌ Not a LumpiaJS Project");
116
+ if (!fs.existsSync(path.join(root, 'node_modules'))) return console.log("⚠️ Please run 'npm install'");
98
117
 
99
- const env = loadEnv(root);
100
118
  const config = loadConfig(root);
101
119
  if (config.klambi === 'tailwindcss') startTailwindWatcher(root);
102
120
 
103
- try {
104
- await loadLumpiaModule(routesFile);
105
- const activeRoutes = global.LumpiaRouter || [];
106
- console.log(`🛣️ Routes registered: ${activeRoutes.length}`);
107
- console.log(`✨ Mode: Semarangan (paten, ono, fungsi, aku)`);
108
-
109
- const server = http.createServer(async (req, res) => {
110
- const method = req.method;
111
- const url = new URL(req.url, `http://${req.headers.host}`);
112
- const pathname = url.pathname;
113
-
114
- const publicMap = { '/css/': path.join(root, 'public', 'css'), '/vendor/': path.join(root, 'public', 'vendor') };
115
- for (const [prefix, localPath] of Object.entries(publicMap)) {
116
- if (pathname.startsWith(prefix)) {
117
- const filePath = path.join(localPath, pathname.slice(prefix.length));
118
- if (fs.existsSync(filePath) && fs.lstatSync(filePath).isFile()) {
119
- const ext = path.extname(filePath);
120
- const mime = ext === '.css' ? 'text/css' : (ext === '.js' ? 'text/javascript' : 'application/octet-stream');
121
- res.writeHead(200, {'Content-Type': mime});
122
- res.end(fs.readFileSync(filePath));
123
- return;
124
- }
125
- }
126
- }
121
+ console.log('🍳 Kukus Project (SPA Mode)...');
122
+
123
+ http.createServer((req, res) => {
124
+ const url = new URL(req.url, `http://${req.headers.host}`);
125
+ let pathname = url.pathname;
126
+
127
+ // 1. Core Library
128
+ if (pathname === '/core/lumpia.js') {
129
+ res.writeHead(200, {'Content-Type': 'text/javascript'});
130
+ return res.end(browserCore);
131
+ }
127
132
 
128
- let match = null, params = {};
129
- const currentRoutes = global.LumpiaRouter || [];
130
- for (const route of currentRoutes) {
131
- const result = matchRoute(route, method, pathname);
132
- if (result) { match = route; params = result.params; break; }
133
+ // 2. Static Assets (Public)
134
+ if (pathname.startsWith('/public/')) {
135
+ const f = path.join(root, pathname);
136
+ if(fs.existsSync(f)) {
137
+ res.writeHead(200, {'Content-Type': pathname.endsWith('.css')?'text/css':'application/javascript'});
138
+ return res.end(fs.readFileSync(f));
133
139
  }
140
+ }
141
+
142
+ // 3. Views (Static .lmp serve)
143
+ // In browserCore, we fetch 'views/home.lmp'
144
+ // If pathname starts with /views/, serve it.
145
+ if (pathname.startsWith('/views/') && pathname.endsWith('.lmp')) {
146
+ const f = path.join(root, pathname);
147
+ if(fs.existsSync(f)) return res.end(fs.readFileSync(f));
148
+ else { res.writeHead(404); return res.end('View 404'); }
149
+ }
150
+
151
+ // 4. JS/LMP Modules (Transpile on fly)
152
+ if (pathname.endsWith('.js')) {
153
+ // Map .js request to .lmp file in User Project
154
+ // /routes/web.js -> /routes/web.lmp
155
+ // /app/controllers/Home.js -> /app/controllers/Home.lmp
134
156
 
135
- if (match) {
136
- try {
137
- const [cName, mName] = match.action.split('@');
138
- const cPath = path.join(root, 'app', 'controllers', cName + '.lmp');
139
- if (!fs.existsSync(cPath)) throw new Error('Controller Not Found');
140
-
141
- const module = await loadLumpiaModule(cPath);
142
- const Ctrl = module.default;
143
- const instance = new Ctrl();
144
- instance.env = env; instance.params = params; instance.config = config;
145
-
146
- const result = await instance[mName](...Object.values(params));
147
-
148
- if (result && result.type) {
149
- res.writeHead(200, {'Content-Type': result.type==='json'?'application/json':'text/html'});
150
- res.end(result.content);
151
- } else {
152
- if(result === undefined) res.end('');
153
- else res.end(String(result));
154
- }
155
- } catch (e) {
156
- res.writeHead(500); res.end(`<pre>${e.stack}</pre>`);
157
- }
158
- } else {
159
- res.writeHead(404); res.end('404 Not Found');
157
+ // Check if .lmp exists
158
+ const lmpPath = path.join(root, pathname.replace('.js', '.lmp'));
159
+ if (fs.existsSync(lmpPath)) {
160
+ const content = fs.readFileSync(lmpPath, 'utf8');
161
+ const js = transpileOnTheFly(content);
162
+ res.writeHead(200, {'Content-Type': 'text/javascript'});
163
+ return res.end(js);
160
164
  }
161
- });
162
- const port = 3000;
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
- }
165
+ // Check if .js exists natively? (Maybe helpers)
166
+ const jsPath = path.join(root, pathname);
167
+ if (fs.existsSync(jsPath)) {
168
+ res.writeHead(200, {'Content-Type': 'text/javascript'});
169
+ return res.end(fs.readFileSync(jsPath));
170
+ }
171
+ }
172
+
173
+ // 5. Default Fallback -> SPA Shell (index.html)
174
+ // For any other route (/, /produk, etc), serve HTML
175
+ // Browser will then run JS, which does routing.
176
+ res.writeHead(200, {'Content-Type': 'text/html'});
177
+ res.end(devIndexHtml);
178
+
179
+ }).listen(3000, () => console.log('🚀 Server: http://localhost:3000'));
167
180
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lumpiajs",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Bahasa Pemrograman Semarangan",
5
5
  "type": "module",
6
6
  "main": "index.js",