bimba-cli 0.7.16 → 0.7.18
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/package.json +1 -1
- package/serve.js +67 -8
package/package.json
CHANGED
package/serve.js
CHANGED
|
@@ -208,7 +208,7 @@ const hmrClient = `
|
|
|
208
208
|
if (msg.type === 'update') _applyUpdate(msg.file, msg.slots);
|
|
209
209
|
else if (msg.type === 'reload') location.reload();
|
|
210
210
|
else if (msg.type === 'error') showError(msg.file, msg.errors);
|
|
211
|
-
else if (msg.type === 'clear-error') clearError();
|
|
211
|
+
else if (msg.type === 'clear-error') clearError(msg.file);
|
|
212
212
|
};
|
|
213
213
|
|
|
214
214
|
ws.onclose = () => setTimeout(connect, 1000);
|
|
@@ -225,6 +225,7 @@ const hmrClient = `
|
|
|
225
225
|
overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });
|
|
226
226
|
document.body.appendChild(overlay);
|
|
227
227
|
}
|
|
228
|
+
overlay.dataset.file = file;
|
|
228
229
|
overlay.innerHTML = \`
|
|
229
230
|
<div style="background:#1a1a1a;border:1px solid #ff4444;border-radius:8px;max-width:860px;width:100%;max-height:90vh;overflow:auto;box-shadow:0 0 40px rgba(255,68,68,.3)">
|
|
230
231
|
<div style="background:#ff4444;color:#fff;padding:10px 16px;font-size:13px;font-weight:600;display:flex;justify-content:space-between;align-items:center">
|
|
@@ -241,9 +242,9 @@ const hmrClient = `
|
|
|
241
242
|
\`;
|
|
242
243
|
}
|
|
243
244
|
|
|
244
|
-
function clearError() {
|
|
245
|
+
function clearError(file) {
|
|
245
246
|
const overlay = document.getElementById('__bimba_error__');
|
|
246
|
-
if (overlay) overlay.remove();
|
|
247
|
+
if (overlay && (!file || overlay.dataset.file === file)) overlay.remove();
|
|
247
248
|
}
|
|
248
249
|
|
|
249
250
|
connect();
|
|
@@ -257,6 +258,13 @@ const _prevJs = new Map() // filepath → compiled js — for change detection
|
|
|
257
258
|
const _prevSlots = new Map() // filepath → previous symbol slot count
|
|
258
259
|
const _importScanner = new Bun.Transpiler({ loader: 'js' })
|
|
259
260
|
|
|
261
|
+
function dropFileState(filepath) {
|
|
262
|
+
const abs = path.resolve(filepath)
|
|
263
|
+
_compileCache.delete(abs)
|
|
264
|
+
_prevJs.delete(abs)
|
|
265
|
+
_prevSlots.delete(abs)
|
|
266
|
+
}
|
|
267
|
+
|
|
260
268
|
// Imba compiles tag render-cache slots as anonymous local Symbols at module top
|
|
261
269
|
// level: `var $4 = Symbol(), $11 = Symbol(), ...; let c$0 = Symbol();`. Each
|
|
262
270
|
// re-import of the file creates fresh Symbol objects, so old slot data on live
|
|
@@ -517,6 +525,7 @@ export function serve(entrypoint, flags) {
|
|
|
517
525
|
|
|
518
526
|
let _fadeTimers = []
|
|
519
527
|
let _fadeId = 0
|
|
528
|
+
let _statusFile = null
|
|
520
529
|
let _statusSaved = false
|
|
521
530
|
const _isTTY = process.stdout.isTTY
|
|
522
531
|
|
|
@@ -525,6 +534,16 @@ export function serve(entrypoint, flags) {
|
|
|
525
534
|
_fadeTimers = []
|
|
526
535
|
}
|
|
527
536
|
|
|
537
|
+
function clearStatus(file) {
|
|
538
|
+
if (file && _statusFile && _statusFile !== file) return
|
|
539
|
+
cancelFade()
|
|
540
|
+
if (_statusSaved) {
|
|
541
|
+
process.stdout.write('\x1b[u\x1b[J')
|
|
542
|
+
_statusSaved = false
|
|
543
|
+
}
|
|
544
|
+
_statusFile = null
|
|
545
|
+
}
|
|
546
|
+
|
|
528
547
|
function printStatus(file, state, errors) {
|
|
529
548
|
// non-TTY (pipes, Claude Code bash, CI): plain newline-terminated output,
|
|
530
549
|
// no ANSI cursor tricks, no fade-out — so logs stay readable.
|
|
@@ -552,6 +571,7 @@ export function serve(entrypoint, flags) {
|
|
|
552
571
|
|
|
553
572
|
process.stdout.write('\x1b[s')
|
|
554
573
|
_statusSaved = true
|
|
574
|
+
_statusFile = file
|
|
555
575
|
|
|
556
576
|
if (errors?.length) {
|
|
557
577
|
process.stdout.write(` ${theme.folder(now)} ${theme.filename(file)} ${status}\n`)
|
|
@@ -567,7 +587,10 @@ export function serve(entrypoint, flags) {
|
|
|
567
587
|
_fadeTimers.push(setTimeout(() => {
|
|
568
588
|
if (_fadeId !== myId) return
|
|
569
589
|
process.stdout.write('\x1b[1D \x1b[1D')
|
|
570
|
-
if (i === total)
|
|
590
|
+
if (i === total) {
|
|
591
|
+
_statusSaved = false
|
|
592
|
+
_statusFile = null
|
|
593
|
+
}
|
|
571
594
|
}, 5000 + i * 22))
|
|
572
595
|
}
|
|
573
596
|
}
|
|
@@ -580,19 +603,49 @@ export function serve(entrypoint, flags) {
|
|
|
580
603
|
for (const socket of sockets) socket.send(msg)
|
|
581
604
|
}
|
|
582
605
|
|
|
606
|
+
function clearError(file) {
|
|
607
|
+
clearStatus(file)
|
|
608
|
+
broadcast({ type: 'clear-error', file })
|
|
609
|
+
}
|
|
610
|
+
|
|
583
611
|
const _debounce = new Map()
|
|
612
|
+
const _watchVersion = new Map()
|
|
584
613
|
|
|
585
|
-
|
|
614
|
+
function scheduleCompile(filename) {
|
|
615
|
+
filename = filename && String(filename)
|
|
586
616
|
if (!filename || !filename.endsWith('.imba')) return
|
|
587
|
-
if (_debounce.has(filename)) return
|
|
588
|
-
_debounce.set(filename, setTimeout(() => _debounce.delete(filename), 150))
|
|
589
617
|
|
|
618
|
+
const version = (_watchVersion.get(filename) || 0) + 1
|
|
619
|
+
_watchVersion.set(filename, version)
|
|
620
|
+
|
|
621
|
+
const pending = _debounce.get(filename)
|
|
622
|
+
if (pending) clearTimeout(pending)
|
|
623
|
+
|
|
624
|
+
_debounce.set(filename, setTimeout(() => {
|
|
625
|
+
_debounce.delete(filename)
|
|
626
|
+
compileChangedFile(filename, version)
|
|
627
|
+
}, 150))
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function isCurrentChange(filename, version) {
|
|
631
|
+
return _watchVersion.get(filename) === version
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
async function compileChangedFile(filename, version) {
|
|
590
635
|
const filepath = path.join(srcDir, filename)
|
|
591
636
|
const rel = path.join(path.relative('.', srcDir), filename).replaceAll('\\', '/')
|
|
592
637
|
|
|
593
638
|
try {
|
|
639
|
+
if (!existsSync(filepath)) {
|
|
640
|
+
if (!isCurrentChange(filename, version)) return
|
|
641
|
+
dropFileState(filepath)
|
|
642
|
+
clearError(rel)
|
|
643
|
+
return
|
|
644
|
+
}
|
|
645
|
+
|
|
594
646
|
const out = await compileFile(filepath)
|
|
595
647
|
|
|
648
|
+
if (!isCurrentChange(filename, version)) return
|
|
596
649
|
|
|
597
650
|
if (out.errors?.length) {
|
|
598
651
|
printStatus(rel, 'fail', out.errors)
|
|
@@ -604,16 +657,22 @@ export function serve(entrypoint, flags) {
|
|
|
604
657
|
return
|
|
605
658
|
}
|
|
606
659
|
|
|
660
|
+
clearError(rel)
|
|
661
|
+
|
|
607
662
|
// No change at all — skip
|
|
608
663
|
if (out.changeType === 'none' || out.changeType === 'cached') return
|
|
609
664
|
|
|
610
665
|
printStatus(rel, 'ok')
|
|
611
|
-
broadcast({ type: 'clear-error' })
|
|
612
666
|
broadcast({ type: 'update', file: rel, slots: out.slots || 'shifted' })
|
|
613
667
|
} catch(e) {
|
|
668
|
+
if (!isCurrentChange(filename, version)) return
|
|
614
669
|
printStatus(rel, 'fail', [{ message: e.message }])
|
|
615
670
|
broadcast({ type: 'error', file: rel, errors: [{ message: e.message, snippet: e.stack || e.message }] })
|
|
616
671
|
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
watch(srcDir, { recursive: true }, (_event, filename) => {
|
|
675
|
+
scheduleCompile(filename)
|
|
617
676
|
})
|
|
618
677
|
|
|
619
678
|
// ── HTTP + WebSocket server ────────────────────────────────────────────────
|