bimba-cli 0.7.29 → 0.7.30
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 +57 -68
package/package.json
CHANGED
package/serve.js
CHANGED
|
@@ -653,7 +653,7 @@ export function serve(entrypoint, flags) {
|
|
|
653
653
|
_statusKind = null
|
|
654
654
|
}
|
|
655
655
|
|
|
656
|
-
function printStatus(file, state, errors) {
|
|
656
|
+
function printStatus(file, state, errors, options = {}) {
|
|
657
657
|
// non-TTY (pipes, Claude Code bash, CI): plain newline-terminated output,
|
|
658
658
|
// no ANSI cursor tricks, no fade-out — so logs stay readable.
|
|
659
659
|
if (!_isTTY) {
|
|
@@ -678,66 +678,45 @@ export function serve(entrypoint, flags) {
|
|
|
678
678
|
const now = new Date().toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit', second: '2-digit' })
|
|
679
679
|
const status = state === 'ok' ? theme.success(' ok ') : theme.failure(' fail ')
|
|
680
680
|
|
|
681
|
+
if (errors?.length || options.sticky) {
|
|
682
|
+
process.stdout.write(` ${theme.folder(now)} ${theme.filename(file)} ${status}\n`)
|
|
683
|
+
if (errors?.length) {
|
|
684
|
+
for (const err of errors) {
|
|
685
|
+
try { printerr(err) } catch(_) { process.stdout.write(' ' + err.message + '\n') }
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
_statusFile = null
|
|
689
|
+
_statusKind = null
|
|
690
|
+
return
|
|
691
|
+
}
|
|
692
|
+
|
|
681
693
|
process.stdout.write('\x1b[s')
|
|
682
694
|
_statusSaved = true
|
|
683
695
|
_statusFile = file
|
|
684
696
|
_statusKind = state
|
|
685
697
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
if (i === total) {
|
|
701
|
-
_statusSaved = false
|
|
702
|
-
_statusFile = null
|
|
703
|
-
_statusKind = null
|
|
704
|
-
}
|
|
705
|
-
}, 5000 + i * 22))
|
|
706
|
-
}
|
|
698
|
+
const myId = ++_fadeId
|
|
699
|
+
const plainLine = ` ${now} ${file} ok `
|
|
700
|
+
const total = plainLine.length
|
|
701
|
+
process.stdout.write(` ${theme.folder(now)} ${theme.filename(file)} ${status}`)
|
|
702
|
+
for (let i = 1; i <= total; i++) {
|
|
703
|
+
_fadeTimers.push(setTimeout(() => {
|
|
704
|
+
if (_fadeId !== myId) return
|
|
705
|
+
process.stdout.write('\x1b[1D \x1b[1D')
|
|
706
|
+
if (i === total) {
|
|
707
|
+
_statusSaved = false
|
|
708
|
+
_statusFile = null
|
|
709
|
+
_statusKind = null
|
|
710
|
+
}
|
|
711
|
+
}, 5000 + i * 22))
|
|
707
712
|
}
|
|
708
713
|
}
|
|
709
714
|
|
|
710
715
|
// ── File watcher ───────────────────────────────────────────────────────────
|
|
711
716
|
|
|
712
717
|
const _activeErrors = new Map()
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if (!_isTTY) return false
|
|
716
|
-
|
|
717
|
-
cancelFade()
|
|
718
|
-
if (_statusSaved) {
|
|
719
|
-
process.stdout.write('\x1b[u\x1b[J')
|
|
720
|
-
_statusSaved = false
|
|
721
|
-
}
|
|
722
|
-
_statusFile = null
|
|
723
|
-
_statusKind = null
|
|
724
|
-
|
|
725
|
-
if (!_activeErrors.size) return false
|
|
726
|
-
|
|
727
|
-
process.stdout.write('\x1b[s')
|
|
728
|
-
_statusSaved = true
|
|
729
|
-
_statusKind = 'errors'
|
|
730
|
-
|
|
731
|
-
for (const item of _activeErrors.values()) {
|
|
732
|
-
const file = item.file
|
|
733
|
-
process.stdout.write(` ${theme.folder(item.time)} ${theme.filename(file)} ${theme.failure(' fail ')}\n`)
|
|
734
|
-
for (const err of item.errors) {
|
|
735
|
-
try { printerr(err) } catch(_) { process.stdout.write(' ' + err.message + '\n') }
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
return true
|
|
740
|
-
}
|
|
718
|
+
const _terminalErrors = new Map()
|
|
719
|
+
const TERMINAL_DUPLICATE_MS = 5000
|
|
741
720
|
|
|
742
721
|
function broadcast(payload) {
|
|
743
722
|
const msg = JSON.stringify(payload)
|
|
@@ -774,6 +753,12 @@ export function serve(entrypoint, flags) {
|
|
|
774
753
|
return Array.from(variants).filter(Boolean)
|
|
775
754
|
}
|
|
776
755
|
|
|
756
|
+
function terminalErrorKey(file) {
|
|
757
|
+
const variants = fileVariants(file)
|
|
758
|
+
const rooted = variants.find(variant => srcRel && variant.startsWith(srcRel + '/'))
|
|
759
|
+
return `path:${rooted || variants[0] || normalizeFile(file)}`
|
|
760
|
+
}
|
|
761
|
+
|
|
777
762
|
function fileCandidates(file) {
|
|
778
763
|
const candidates = []
|
|
779
764
|
for (const variant of fileVariants(file)) {
|
|
@@ -862,29 +847,42 @@ export function serve(entrypoint, flags) {
|
|
|
862
847
|
.join('\n---\n')
|
|
863
848
|
}
|
|
864
849
|
|
|
850
|
+
function terminalErrorSignature(errors) {
|
|
851
|
+
return serializeErrors(errors)
|
|
852
|
+
.map(error => error.message)
|
|
853
|
+
.join('\n---\n')
|
|
854
|
+
}
|
|
855
|
+
|
|
865
856
|
function showTrackedError(item) {
|
|
866
857
|
const file = item.file
|
|
867
|
-
|
|
868
|
-
else printStatus(file, 'fail', item.errors)
|
|
858
|
+
printStatus(file, 'fail', item.errors)
|
|
869
859
|
broadcast({ type: 'error', file, time: item.time, errors: item.payload })
|
|
870
860
|
}
|
|
871
861
|
|
|
872
862
|
function reportError(file, errors) {
|
|
873
863
|
const display = normalizeFile(file)
|
|
874
864
|
const key = errorKey(display)
|
|
865
|
+
const terminalKey = terminalErrorKey(display)
|
|
875
866
|
const list = Array.isArray(errors) ? errors : [errors]
|
|
876
867
|
const signature = errorSignature(list)
|
|
868
|
+
const printSignature = terminalErrorSignature(list)
|
|
877
869
|
const previous = takeError(display)
|
|
870
|
+
const now = Date.now()
|
|
871
|
+
const recent = _terminalErrors.get(terminalKey)
|
|
878
872
|
const duplicate = previous?.signature === signature
|
|
873
|
+
|| previous?.printSignature === printSignature
|
|
874
|
+
|| (recent?.signature === printSignature && now - recent.time < TERMINAL_DUPLICATE_MS)
|
|
879
875
|
|
|
880
876
|
const item = {
|
|
881
877
|
file: display,
|
|
882
878
|
signature,
|
|
879
|
+
printSignature,
|
|
883
880
|
errors: list,
|
|
884
881
|
payload: serializeErrors(list),
|
|
885
882
|
time: new Date().toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit', second: '2-digit' }),
|
|
886
883
|
}
|
|
887
884
|
_activeErrors.set(key, item)
|
|
885
|
+
_terminalErrors.set(terminalKey, { signature: printSignature, time: now })
|
|
888
886
|
|
|
889
887
|
// The terminal is append-only in many real shells. Repeated reports of the
|
|
890
888
|
// same active error update the browser overlay, but must not print again.
|
|
@@ -908,36 +906,27 @@ export function serve(entrypoint, flags) {
|
|
|
908
906
|
|
|
909
907
|
function clearError(file) {
|
|
910
908
|
const key = file ? normalizeFile(file) : null
|
|
911
|
-
const hadPanel = _statusKind === 'errors'
|
|
912
909
|
const wasStatusFile = key && _statusFile && sameFile(_statusFile, key)
|
|
913
910
|
const hadError = key ? !!takeError(key) : _activeErrors.size > 0
|
|
914
911
|
|
|
915
912
|
if (!key) _activeErrors.clear()
|
|
916
913
|
|
|
917
|
-
|
|
918
|
-
if (_isTTY && (hadError || wasStatusFile || hadPanel)) {
|
|
919
|
-
showedNext = renderErrorPanel()
|
|
920
|
-
} else if (!key || hadError || wasStatusFile) {
|
|
921
|
-
clearStatus(key)
|
|
922
|
-
}
|
|
914
|
+
if (!key || hadError || wasStatusFile) clearStatus(key)
|
|
923
915
|
|
|
924
916
|
broadcast({ type: 'clear-error', file: key })
|
|
925
917
|
|
|
926
|
-
|
|
927
|
-
const nextItem = Array.from(_activeErrors.values()).at(-1)
|
|
928
|
-
showTrackedError(nextItem)
|
|
929
|
-
showedNext = true
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
return { cleared: hadError || wasStatusFile, file: key, showedNext }
|
|
918
|
+
return { cleared: hadError || wasStatusFile, file: key, showedNext: false }
|
|
933
919
|
}
|
|
934
920
|
|
|
935
921
|
function markSuccess(file) {
|
|
936
922
|
const key = normalizeFile(file)
|
|
937
923
|
const result = clearError(key)
|
|
938
924
|
const active = _activeErrors.size
|
|
939
|
-
const shouldPrint = result?.cleared && !result.showedNext
|
|
940
|
-
if (shouldPrint)
|
|
925
|
+
const shouldPrint = result?.cleared && !result.showedNext
|
|
926
|
+
if (shouldPrint) {
|
|
927
|
+
_terminalErrors.delete(terminalErrorKey(key))
|
|
928
|
+
printStatus(key, 'ok', null, { sticky: true })
|
|
929
|
+
}
|
|
941
930
|
return { cleared: !!result?.cleared, printed: !!shouldPrint, showedNext: !!result?.showedNext, active }
|
|
942
931
|
}
|
|
943
932
|
|