bimba-cli 0.7.23 → 0.7.24
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 +104 -31
package/package.json
CHANGED
package/serve.js
CHANGED
|
@@ -98,7 +98,7 @@ const hmrClient = `
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
async function _doUpdate(file, slots) {
|
|
101
|
-
clearError();
|
|
101
|
+
clearError(file);
|
|
102
102
|
|
|
103
103
|
const bodyBefore = new Set(document.body.children);
|
|
104
104
|
const tagsBefore = new Set();
|
|
@@ -216,6 +216,8 @@ const hmrClient = `
|
|
|
216
216
|
|
|
217
217
|
// ── Error overlay ──────────────────────────────────────────────────────────
|
|
218
218
|
|
|
219
|
+
const _compileErrors = new Map();
|
|
220
|
+
|
|
219
221
|
function normalizeFile(file) {
|
|
220
222
|
let value = String(file || '').split(String.fromCharCode(92)).join('/');
|
|
221
223
|
while (value.startsWith('./')) value = value.slice(2);
|
|
@@ -223,10 +225,24 @@ const hmrClient = `
|
|
|
223
225
|
return value;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
|
-
function
|
|
227
|
-
|
|
228
|
-
|
|
228
|
+
function escapeHtml(value) {
|
|
229
|
+
return String(value ?? '').replace(/[&<>"']/g, ch => ({
|
|
230
|
+
'&': '&',
|
|
231
|
+
'<': '<',
|
|
232
|
+
'>': '>',
|
|
233
|
+
'"': '"',
|
|
234
|
+
"'": ''',
|
|
235
|
+
})[ch]);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function renderErrors() {
|
|
229
239
|
let overlay = document.getElementById('__bimba_error__');
|
|
240
|
+
|
|
241
|
+
if (!_compileErrors.size) {
|
|
242
|
+
if (overlay) overlay.remove();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
230
246
|
if (!overlay) {
|
|
231
247
|
overlay = document.createElement('div');
|
|
232
248
|
overlay.id = '__bimba_error__';
|
|
@@ -234,29 +250,48 @@ const hmrClient = `
|
|
|
234
250
|
overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });
|
|
235
251
|
document.body.appendChild(overlay);
|
|
236
252
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
<span
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
253
|
+
|
|
254
|
+
const files = Array.from(_compileErrors.entries());
|
|
255
|
+
overlay.innerHTML =
|
|
256
|
+
'<div style="background:#1a1a1a;border:1px solid #ff4444;border-radius:8px;max-width:900px;width:100%;max-height:90vh;overflow:auto;box-shadow:0 0 40px rgba(255,68,68,.3)">' +
|
|
257
|
+
'<div style="background:#ff4444;color:#fff;padding:10px 16px;font-size:13px;font-weight:600;display:flex;justify-content:space-between;align-items:center">' +
|
|
258
|
+
'<span>Compile errors — ' + files.length + '</span>' +
|
|
259
|
+
'<span onclick="document.getElementById(\\'__bimba_error__\\').remove()" style="cursor:pointer;opacity:.7;font-size:16px">✕</span>' +
|
|
260
|
+
'</div>' +
|
|
261
|
+
files.map(([displayFile, item]) => {
|
|
262
|
+
const errors = item.errors || [];
|
|
263
|
+
return '<div style="border-bottom:1px solid #333">' +
|
|
264
|
+
'<div style="padding:10px 16px;background:#241616;color:#ffd1d1;font-size:13px;font-weight:600;display:flex;justify-content:space-between;gap:16px">' +
|
|
265
|
+
'<span>' + escapeHtml(displayFile) + '</span>' +
|
|
266
|
+
'<span style="opacity:.75;font-weight:400">' + escapeHtml(item.time || '') + '</span>' +
|
|
267
|
+
'</div>' +
|
|
268
|
+
errors.map(err =>
|
|
269
|
+
'<div style="padding:16px;border-top:1px solid #333">' +
|
|
270
|
+
'<div style="color:#ff8080;font-size:13px;margin-bottom:10px">' +
|
|
271
|
+
escapeHtml(err.message) +
|
|
272
|
+
(err.line ? ' <span style="color:#888">line ' + escapeHtml(err.line) + '</span>' : '') +
|
|
273
|
+
'</div>' +
|
|
274
|
+
(err.snippet ? '<pre style="margin:0;padding:10px;background:#111;border-radius:4px;font-size:12px;line-height:1.6;color:#ccc;overflow-x:auto;white-space:pre">' + escapeHtml(err.snippet) + '</pre>' : '') +
|
|
275
|
+
'</div>'
|
|
276
|
+
).join('') +
|
|
277
|
+
'</div>';
|
|
278
|
+
}).join('') +
|
|
279
|
+
'</div>';
|
|
252
280
|
}
|
|
253
281
|
|
|
254
|
-
function
|
|
255
|
-
const
|
|
256
|
-
|
|
282
|
+
function showError(file, errors, time) {
|
|
283
|
+
const displayFile = normalizeFile(file);
|
|
284
|
+
_compileErrors.set(displayFile, {
|
|
285
|
+
errors: Array.isArray(errors) ? errors : [errors],
|
|
286
|
+
time: time || new Date().toLocaleTimeString(),
|
|
287
|
+
});
|
|
288
|
+
renderErrors();
|
|
289
|
+
}
|
|
257
290
|
|
|
258
|
-
|
|
259
|
-
if (
|
|
291
|
+
function clearError(file) {
|
|
292
|
+
if (file) _compileErrors.delete(normalizeFile(file));
|
|
293
|
+
else _compileErrors.clear();
|
|
294
|
+
renderErrors();
|
|
260
295
|
}
|
|
261
296
|
|
|
262
297
|
connect();
|
|
@@ -562,6 +597,7 @@ export function serve(entrypoint, flags) {
|
|
|
562
597
|
let _fadeId = 0
|
|
563
598
|
let _statusFile = null
|
|
564
599
|
let _statusSaved = false
|
|
600
|
+
let _statusKind = null
|
|
565
601
|
const _isTTY = process.stdout.isTTY
|
|
566
602
|
|
|
567
603
|
function cancelFade() {
|
|
@@ -577,6 +613,7 @@ export function serve(entrypoint, flags) {
|
|
|
577
613
|
_statusSaved = false
|
|
578
614
|
}
|
|
579
615
|
_statusFile = null
|
|
616
|
+
_statusKind = null
|
|
580
617
|
}
|
|
581
618
|
|
|
582
619
|
function printStatus(file, state, errors) {
|
|
@@ -607,6 +644,7 @@ export function serve(entrypoint, flags) {
|
|
|
607
644
|
process.stdout.write('\x1b[s')
|
|
608
645
|
_statusSaved = true
|
|
609
646
|
_statusFile = file
|
|
647
|
+
_statusKind = state
|
|
610
648
|
|
|
611
649
|
if (errors?.length) {
|
|
612
650
|
process.stdout.write(` ${theme.folder(now)} ${theme.filename(file)} ${status}\n`)
|
|
@@ -625,6 +663,7 @@ export function serve(entrypoint, flags) {
|
|
|
625
663
|
if (i === total) {
|
|
626
664
|
_statusSaved = false
|
|
627
665
|
_statusFile = null
|
|
666
|
+
_statusKind = null
|
|
628
667
|
}
|
|
629
668
|
}, 5000 + i * 22))
|
|
630
669
|
}
|
|
@@ -635,6 +674,33 @@ export function serve(entrypoint, flags) {
|
|
|
635
674
|
|
|
636
675
|
const _activeErrors = new Map()
|
|
637
676
|
|
|
677
|
+
function renderErrorPanel() {
|
|
678
|
+
if (!_isTTY) return false
|
|
679
|
+
|
|
680
|
+
cancelFade()
|
|
681
|
+
if (_statusSaved) {
|
|
682
|
+
process.stdout.write('\x1b[u\x1b[J')
|
|
683
|
+
_statusSaved = false
|
|
684
|
+
}
|
|
685
|
+
_statusFile = null
|
|
686
|
+
_statusKind = null
|
|
687
|
+
|
|
688
|
+
if (!_activeErrors.size) return false
|
|
689
|
+
|
|
690
|
+
process.stdout.write('\x1b[s')
|
|
691
|
+
_statusSaved = true
|
|
692
|
+
_statusKind = 'errors'
|
|
693
|
+
|
|
694
|
+
for (const [file, item] of _activeErrors.entries()) {
|
|
695
|
+
process.stdout.write(` ${theme.folder(item.time)} ${theme.filename(file)} ${theme.failure(' fail ')}\n`)
|
|
696
|
+
for (const err of item.errors) {
|
|
697
|
+
try { printerr(err) } catch(_) { process.stdout.write(' ' + err.message + '\n') }
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
return true
|
|
702
|
+
}
|
|
703
|
+
|
|
638
704
|
function broadcast(payload) {
|
|
639
705
|
const msg = JSON.stringify(payload)
|
|
640
706
|
for (const socket of sockets) socket.send(msg)
|
|
@@ -686,7 +752,8 @@ export function serve(entrypoint, flags) {
|
|
|
686
752
|
}
|
|
687
753
|
|
|
688
754
|
function showTrackedError(file, item) {
|
|
689
|
-
|
|
755
|
+
if (_isTTY) renderErrorPanel()
|
|
756
|
+
else printStatus(file, 'fail', item.errors)
|
|
690
757
|
broadcast({ type: 'error', file, time: item.time, errors: item.payload })
|
|
691
758
|
}
|
|
692
759
|
|
|
@@ -731,11 +798,16 @@ export function serve(entrypoint, flags) {
|
|
|
731
798
|
|
|
732
799
|
if (key && !hadError && !wasStatusFile) return
|
|
733
800
|
|
|
734
|
-
|
|
801
|
+
let showedNext = false
|
|
802
|
+
if (_isTTY && (hadError || _statusKind === 'errors')) {
|
|
803
|
+
showedNext = renderErrorPanel()
|
|
804
|
+
} else {
|
|
805
|
+
clearStatus(key)
|
|
806
|
+
}
|
|
807
|
+
|
|
735
808
|
broadcast({ type: 'clear-error', file: key })
|
|
736
809
|
|
|
737
|
-
|
|
738
|
-
if (wasStatusFile && _activeErrors.size) {
|
|
810
|
+
if (!_isTTY && wasStatusFile && _activeErrors.size) {
|
|
739
811
|
const [nextFile, nextItem] = Array.from(_activeErrors.entries()).at(-1)
|
|
740
812
|
showTrackedError(nextFile, nextItem)
|
|
741
813
|
showedNext = true
|
|
@@ -747,9 +819,10 @@ export function serve(entrypoint, flags) {
|
|
|
747
819
|
function markSuccess(file) {
|
|
748
820
|
const key = normalizeFile(file)
|
|
749
821
|
const result = clearError(key)
|
|
750
|
-
const
|
|
822
|
+
const active = _activeErrors.size
|
|
823
|
+
const shouldPrint = result?.cleared && !result.showedNext && !active
|
|
751
824
|
if (shouldPrint) printStatus(key, 'ok')
|
|
752
|
-
return { cleared: !!result?.cleared, printed: !!shouldPrint, showedNext: !!result?.showedNext }
|
|
825
|
+
return { cleared: !!result?.cleared, printed: !!shouldPrint, showedNext: !!result?.showedNext, active }
|
|
753
826
|
}
|
|
754
827
|
|
|
755
828
|
const _debounce = new Map()
|
|
@@ -822,7 +895,7 @@ export function serve(entrypoint, flags) {
|
|
|
822
895
|
// No change at all — skip
|
|
823
896
|
if (out.changeType === 'none' || out.changeType === 'cached') return
|
|
824
897
|
|
|
825
|
-
if (!success.printed && !success.showedNext) printStatus(rel, 'ok')
|
|
898
|
+
if (!success.printed && !success.showedNext && !success.active) printStatus(rel, 'ok')
|
|
826
899
|
broadcast({ type: 'update', file: rel, slots: out.slots || 'shifted' })
|
|
827
900
|
} catch(e) {
|
|
828
901
|
if (!isCurrentChange(file, version)) return
|