lumpiajs 1.0.10 ā 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 +114 -68
- package/lib/commands/create.js +6 -1
- package/lib/commands/serve.js +30 -11
- package/package.json +1 -1
package/lib/commands/build.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
// KAMUS
|
|
3
|
-
// Urutan penting! Keyword panjang dulu baru pendek.
|
|
2
|
+
// ... (KAMUS & Imports same as before) ...
|
|
4
3
|
const KAMUS = [
|
|
5
4
|
{ from: /paten\s/g, to: 'const ' },
|
|
6
5
|
{ from: /ono\s/g, to: 'let ' },
|
|
7
|
-
{ from: /fungsi\s/g, to: 'function ' },
|
|
6
|
+
{ from: /fungsi\s/g, to: 'function ' },
|
|
8
7
|
{ from: /nteni\s/g, to: 'await ' },
|
|
9
8
|
{ from: /mengko\s/g, to: 'async ' },
|
|
10
9
|
{ from: /balek\s/g, to: 'return ' },
|
|
@@ -13,56 +12,55 @@ const KAMUS = [
|
|
|
13
12
|
{ from: /jajal\s*\{/g, to: 'try {' },
|
|
14
13
|
{ from: /gagal\s*\(/g, to: 'catch(' },
|
|
15
14
|
{ from: /kandani\(/g, to: 'console.log(' },
|
|
16
|
-
{ from: /aku->/g, to: 'this.' },
|
|
17
|
-
{ from: /aku\./g, to: 'this.' },
|
|
15
|
+
{ from: /aku->/g, to: 'this.' },
|
|
16
|
+
{ from: /aku\./g, to: 'this.' },
|
|
18
17
|
{ from: /->/g, to: '.' }
|
|
19
18
|
];
|
|
20
19
|
|
|
20
|
+
import fs from 'fs';
|
|
21
|
+
import path from 'path';
|
|
22
|
+
import { spawnSync } from 'child_process';
|
|
23
|
+
import { loadConfig } from '../core/Config.js';
|
|
24
|
+
|
|
21
25
|
function transpileSemarangan(content) {
|
|
22
26
|
let code = content;
|
|
23
|
-
|
|
24
|
-
// Import .lmp -> .js
|
|
25
27
|
code = code.replace(/from\s+['"](.+?)\.lmp['"]/g, "from '$1.js'");
|
|
26
|
-
code = code.replace(/from\s+['"]lumpiajs['"]/g, "from '
|
|
28
|
+
code = code.replace(/from\s+['"]lumpiajs['"]/g, "from './core/lumpia.js'");
|
|
27
29
|
|
|
28
|
-
// Safe Replace Logic
|
|
29
|
-
// We must handle this carefully to not break valid JS code if mixed.
|
|
30
|
-
// 'aku' is common word, but as keyword usually followed by -> or .
|
|
31
|
-
|
|
32
30
|
code = code.split('\n').map(line => {
|
|
33
31
|
let l = line;
|
|
34
32
|
if (l.trim().startsWith('//')) return l;
|
|
35
|
-
|
|
36
|
-
KAMUS.forEach(rule => {
|
|
37
|
-
l = l.replace(rule.from, rule.to);
|
|
38
|
-
});
|
|
33
|
+
KAMUS.forEach(rule => { l = l.replace(rule.from, rule.to); });
|
|
39
34
|
return l;
|
|
40
35
|
}).join('\n');
|
|
41
|
-
|
|
42
36
|
return code;
|
|
43
37
|
}
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
// ... I will copy the previous logic but update browserCore as well.
|
|
47
|
-
|
|
48
|
-
import fs from 'fs';
|
|
49
|
-
import path from 'path';
|
|
50
|
-
import { spawnSync } from 'child_process';
|
|
51
|
-
import { loadConfig } from '../core/Config.js';
|
|
52
|
-
|
|
53
|
-
function processDirectory(source, dest) {
|
|
39
|
+
function processDirectory(source, dest, rootDepth = 0) {
|
|
54
40
|
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
55
41
|
fs.readdirSync(source).forEach(item => {
|
|
56
42
|
const srcPath = path.join(source, item);
|
|
57
43
|
const destPath = path.join(dest, item);
|
|
58
44
|
if (fs.statSync(srcPath).isDirectory()) {
|
|
59
|
-
processDirectory(srcPath, destPath);
|
|
45
|
+
processDirectory(srcPath, destPath, rootDepth + 1);
|
|
60
46
|
} else {
|
|
61
47
|
if (item.endsWith('.lmp') || item.endsWith('.js')) {
|
|
62
|
-
|
|
63
|
-
|
|
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
|
+
|
|
56
|
+
// Let's rely on simple replace logic based on file type location usually known
|
|
57
|
+
// But dynamic is better.
|
|
58
|
+
|
|
59
|
+
content = content.replace(/from\s+['"]lumpiajs['"]/g, `from '${backSteps}../core/lumpia.js'`);
|
|
60
|
+
|
|
61
|
+
content = transpileSemarangan(content);
|
|
64
62
|
const finalDest = destPath.replace('.lmp', '.js');
|
|
65
|
-
fs.writeFileSync(finalDest,
|
|
63
|
+
fs.writeFileSync(finalDest, content);
|
|
66
64
|
} else {
|
|
67
65
|
fs.copyFileSync(srcPath, destPath);
|
|
68
66
|
}
|
|
@@ -75,8 +73,8 @@ export class Controller {
|
|
|
75
73
|
constructor() { this.params={}; }
|
|
76
74
|
async tampil(viewName, data={}) {
|
|
77
75
|
try {
|
|
78
|
-
const res = await fetch('
|
|
79
|
-
if(!res.ok) throw new Error('View 404');
|
|
76
|
+
const res = await fetch('views/'+viewName+'.lmp');
|
|
77
|
+
if(!res.ok) throw new Error('View 404: ' + viewName);
|
|
80
78
|
let html = await res.text();
|
|
81
79
|
|
|
82
80
|
const matchKulit = html.match(/<kulit>([\\s\\S]*?)<\\/kulit>/);
|
|
@@ -97,7 +95,6 @@ export class Controller {
|
|
|
97
95
|
}
|
|
98
96
|
|
|
99
97
|
if(script) {
|
|
100
|
-
// Client-side transpile
|
|
101
98
|
const kamus = [
|
|
102
99
|
{f:/paten\\s/g,t:'const '}, {f:/ono\\s/g,t:'let '}, {f:/fungsi\\s/g,t:'function '},
|
|
103
100
|
{f:/nteni\\s/g,t:'await '}, {f:/mengko\\s/g,t:'async '}, {f:/balek\\s/g,t:'return '},
|
|
@@ -105,7 +102,7 @@ export class Controller {
|
|
|
105
102
|
{f:/aku->/g,t:'this.'}, {f:/aku\\./g,t:'this.'}
|
|
106
103
|
];
|
|
107
104
|
kamus.forEach(r => script = script.replace(r.f, r.t));
|
|
108
|
-
new Function(script)();
|
|
105
|
+
try { new Function(script)(); } catch(e){ console.error(e); }
|
|
109
106
|
}
|
|
110
107
|
} catch(e) { document.getElementById('app').innerHTML = e.message; }
|
|
111
108
|
}
|
|
@@ -115,42 +112,89 @@ export const Jalan = { routes:[], get:(p,a)=>Jalan.routes.push({p,a}) };
|
|
|
115
112
|
|
|
116
113
|
const indexHtml = `<!DOCTYPE html>
|
|
117
114
|
<html lang="en">
|
|
118
|
-
<head
|
|
119
|
-
<
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
window.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
115
|
+
<head>
|
|
116
|
+
<meta charset="UTF-8">
|
|
117
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
118
|
+
<title>LumpiaJS</title>
|
|
119
|
+
<link rel="stylesheet" href="public/css/style.css">
|
|
120
|
+
</head>
|
|
121
|
+
<body>
|
|
122
|
+
<div id="app">Loading...</div>
|
|
123
|
+
|
|
124
|
+
<script type="module">
|
|
125
|
+
import { Jalan } from './routes/web.js';
|
|
126
|
+
|
|
127
|
+
async function navigate() {
|
|
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.
|
|
139
|
+
|
|
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('/$'), '');
|
|
143
|
+
}
|
|
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 = '/';
|
|
151
|
+
|
|
152
|
+
let m = null, args = {};
|
|
153
|
+
for(let r of Jalan.routes) {
|
|
154
|
+
// Regex: Replace {slug} -> ([^/]+)
|
|
155
|
+
let regexStr = '^' + r.p.replace(/{([a-zA-Z0-9_]+)}/g, '([^/]+)') + '$';
|
|
156
|
+
let reg = new RegExp(regexStr);
|
|
157
|
+
let res = currentPath.match(reg);
|
|
158
|
+
if(res){ m=r; args=res.slice(1); break; }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if(m) {
|
|
162
|
+
const [cName, fName] = m.a.split('@');
|
|
163
|
+
try {
|
|
164
|
+
const mod = await import('./app/controllers/'+cName+'.js?'+Date.now());
|
|
165
|
+
const C = mod.default; const i = new C(); i.params=args;
|
|
166
|
+
await i[fName](...args);
|
|
167
|
+
} catch(e) {
|
|
168
|
+
console.error(e);
|
|
169
|
+
document.getElementById('app').innerHTML='<h1>Error Loading Controller</h1><pre>'+e.message+'</pre>';
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
document.getElementById('app').innerHTML='<h1>404 Not Found</h1><p>Route: '+currentPath+'</p>';
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
window.addEventListener('popstate', navigate);
|
|
177
|
+
document.body.addEventListener('click', e => {
|
|
178
|
+
if(e.target.tagName==='A' && e.target.href.startsWith(window.location.origin)) {
|
|
179
|
+
e.preventDefault();
|
|
180
|
+
history.pushState(null,'',e.target.href);
|
|
181
|
+
navigate();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Init Base Path immediately
|
|
186
|
+
window.LUMPIA_BASE = window.location.pathname.replace(new RegExp('/index\\\\.html$'), '').replace(new RegExp('/$'), '');
|
|
187
|
+
navigate();
|
|
188
|
+
</script>
|
|
189
|
+
</body>
|
|
190
|
+
</html>`;
|
|
147
191
|
|
|
148
192
|
export function buildProject() {
|
|
149
193
|
const root = process.cwd();
|
|
150
194
|
const dist = path.join(root, 'dist');
|
|
151
195
|
const config = loadConfig(root);
|
|
152
196
|
|
|
153
|
-
console.log('š³ Goreng Project (Mode
|
|
197
|
+
console.log('š³ Goreng Project (Mode Aman)...');
|
|
154
198
|
if (fs.existsSync(dist)) fs.rmSync(dist, { recursive: true, force: true });
|
|
155
199
|
fs.mkdirSync(dist);
|
|
156
200
|
|
|
@@ -159,15 +203,17 @@ export function buildProject() {
|
|
|
159
203
|
spawnSync(cmd, ['tailwindcss', '-i', './aset/css/style.css', '-o', './public/css/style.css', '--minify'], { cwd: root, stdio: 'ignore', shell: true });
|
|
160
204
|
}
|
|
161
205
|
|
|
162
|
-
processDirectory(path.join(root, 'app'), path.join(dist, 'app'));
|
|
163
|
-
processDirectory(path.join(root, 'routes'), path.join(dist, 'routes'));
|
|
206
|
+
processDirectory(path.join(root, 'app'), path.join(dist, 'app'), 2);
|
|
207
|
+
processDirectory(path.join(root, 'routes'), path.join(dist, 'routes'), 1);
|
|
208
|
+
|
|
164
209
|
fs.cpSync(path.join(root, 'views'), path.join(dist, 'views'), { recursive: true });
|
|
165
210
|
if (fs.existsSync(path.join(root, 'public'))) fs.cpSync(path.join(root, 'public'), path.join(dist, 'public'), { recursive: true });
|
|
166
211
|
|
|
167
212
|
fs.mkdirSync(path.join(dist, 'core'), { recursive: true });
|
|
168
213
|
fs.writeFileSync(path.join(dist, 'core', 'lumpia.js'), browserCore);
|
|
169
214
|
fs.writeFileSync(path.join(dist, 'index.html'), indexHtml);
|
|
170
|
-
|
|
215
|
+
|
|
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>`);
|
|
171
217
|
|
|
172
|
-
console.log('ā
Mateng!
|
|
218
|
+
console.log('ā
Mateng! Siap upload ke Laragon/Hosting.');
|
|
173
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,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
// KAMUS SEMARANGAN (Regex Replacement Rules)
|
|
1
|
+
// ... (Same KAMUS & Imports) ...
|
|
3
2
|
const KAMUS = [
|
|
4
3
|
{ from: /paten\s/g, to: 'const ' },
|
|
5
4
|
{ from: /ono\s/g, to: 'let ' },
|
|
@@ -24,8 +23,6 @@ import { spawn } from 'child_process';
|
|
|
24
23
|
import { loadEnv } from '../core/Env.js';
|
|
25
24
|
import { loadConfig } from '../core/Config.js';
|
|
26
25
|
|
|
27
|
-
// ... Same helper functions (matchRoute, backgroundProcess, startTailwindWatcher) ...
|
|
28
|
-
|
|
29
26
|
function matchRoute(definedRoute, method, pathname) {
|
|
30
27
|
if (definedRoute.method !== method) return null;
|
|
31
28
|
if (definedRoute.path === pathname) return { params: {} };
|
|
@@ -63,7 +60,6 @@ async function loadLumpiaModule(filePath) {
|
|
|
63
60
|
|
|
64
61
|
let transcoded = originalContent;
|
|
65
62
|
transcoded = transcoded.replace(/from\s+['"](.+?)\.lmp['"]/g, "from '$1.js'");
|
|
66
|
-
|
|
67
63
|
transcoded = transcoded.split('\n').map(line => {
|
|
68
64
|
let l = line;
|
|
69
65
|
if (l.trim().startsWith('//')) return l;
|
|
@@ -72,14 +68,33 @@ async function loadLumpiaModule(filePath) {
|
|
|
72
68
|
}).join('\n');
|
|
73
69
|
|
|
74
70
|
fs.writeFileSync(destPath, transcoded);
|
|
75
|
-
|
|
76
|
-
|
|
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
|
+
}
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
export async function serveProject() {
|
|
80
88
|
const root = process.cwd();
|
|
81
89
|
const routesFile = path.join(root, 'routes', 'web.lmp');
|
|
82
|
-
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
|
+
}
|
|
83
98
|
|
|
84
99
|
const env = loadEnv(root);
|
|
85
100
|
const config = loadConfig(root);
|
|
@@ -134,7 +149,8 @@ export async function serveProject() {
|
|
|
134
149
|
res.writeHead(200, {'Content-Type': result.type==='json'?'application/json':'text/html'});
|
|
135
150
|
res.end(result.content);
|
|
136
151
|
} else {
|
|
137
|
-
res.end(
|
|
152
|
+
if(result === undefined) res.end('');
|
|
153
|
+
else res.end(String(result));
|
|
138
154
|
}
|
|
139
155
|
} catch (e) {
|
|
140
156
|
res.writeHead(500); res.end(`<pre>${e.stack}</pre>`);
|
|
@@ -143,6 +159,9 @@ export async function serveProject() {
|
|
|
143
159
|
res.writeHead(404); res.end('404 Not Found');
|
|
144
160
|
}
|
|
145
161
|
});
|
|
146
|
-
|
|
147
|
-
|
|
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
|
+
}
|
|
148
167
|
}
|