bimba-cli 0.7.31 → 0.7.32
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 +111 -20
package/package.json
CHANGED
package/serve.js
CHANGED
|
@@ -632,6 +632,9 @@ export function serve(entrypoint, flags) {
|
|
|
632
632
|
let _statusRows = 0
|
|
633
633
|
let _statusTimer = null
|
|
634
634
|
let _statusFile = null
|
|
635
|
+
let _statusInline = false
|
|
636
|
+
let _statusWidth = 0
|
|
637
|
+
let _eraseTimers = []
|
|
635
638
|
const _isTTY = process.stdout.isTTY
|
|
636
639
|
|
|
637
640
|
function stripAnsi(text) {
|
|
@@ -652,11 +655,17 @@ export function serve(entrypoint, flags) {
|
|
|
652
655
|
clearTimeout(_statusTimer)
|
|
653
656
|
_statusTimer = null
|
|
654
657
|
}
|
|
655
|
-
|
|
658
|
+
_eraseTimers.forEach(timer => clearTimeout(timer))
|
|
659
|
+
_eraseTimers = []
|
|
660
|
+
if (_isTTY && _statusInline) {
|
|
661
|
+
process.stdout.write('\r\x1b[J')
|
|
662
|
+
} else if (_isTTY && _statusRows) {
|
|
656
663
|
process.stdout.write(`\x1b[${_statusRows}A\r\x1b[J`)
|
|
657
664
|
}
|
|
658
665
|
_statusRows = 0
|
|
659
666
|
_statusFile = null
|
|
667
|
+
_statusInline = false
|
|
668
|
+
_statusWidth = 0
|
|
660
669
|
return true
|
|
661
670
|
}
|
|
662
671
|
|
|
@@ -703,12 +712,46 @@ export function serve(entrypoint, flags) {
|
|
|
703
712
|
clearStatus()
|
|
704
713
|
_statusFile = file
|
|
705
714
|
const lines = statusLines(file, state, errors)
|
|
715
|
+
if (options.fadeAfter && lines.length === 1) {
|
|
716
|
+
const plain = stripAnsi(lines[0])
|
|
717
|
+
process.stdout.write(lines[0])
|
|
718
|
+
_statusRows = 1
|
|
719
|
+
_statusInline = true
|
|
720
|
+
_statusWidth = plain.length
|
|
721
|
+
_statusTimer = setTimeout(() => eraseStatus(file), options.fadeAfter)
|
|
722
|
+
return
|
|
723
|
+
}
|
|
724
|
+
|
|
706
725
|
process.stdout.write(lines.join('\n') + '\n')
|
|
707
726
|
_statusRows = renderedRows(lines)
|
|
708
727
|
|
|
709
728
|
if (options.clearAfter) _statusTimer = setTimeout(() => clearStatus(file), options.clearAfter)
|
|
710
729
|
}
|
|
711
730
|
|
|
731
|
+
function eraseStatus(file) {
|
|
732
|
+
if (file && _statusFile && _statusFile !== file) return
|
|
733
|
+
_statusTimer = null
|
|
734
|
+
if (!_isTTY || !_statusInline) {
|
|
735
|
+
clearStatus(file)
|
|
736
|
+
return
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
const total = _statusWidth
|
|
740
|
+
for (let i = 1; i <= total; i++) {
|
|
741
|
+
_eraseTimers.push(setTimeout(() => {
|
|
742
|
+
if (!_statusInline || _statusFile !== file) return
|
|
743
|
+
process.stdout.write('\x1b[1D \x1b[1D')
|
|
744
|
+
if (i === total) {
|
|
745
|
+
_statusRows = 0
|
|
746
|
+
_statusFile = null
|
|
747
|
+
_statusInline = false
|
|
748
|
+
_statusWidth = 0
|
|
749
|
+
_eraseTimers = []
|
|
750
|
+
}
|
|
751
|
+
}, i * 22))
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
712
755
|
// ── File watcher ───────────────────────────────────────────────────────────
|
|
713
756
|
|
|
714
757
|
const _activeErrors = new Map()
|
|
@@ -869,6 +912,49 @@ export function serve(entrypoint, flags) {
|
|
|
869
912
|
return true
|
|
870
913
|
}
|
|
871
914
|
|
|
915
|
+
function existingFileForError(file) {
|
|
916
|
+
for (const candidate of fileCandidates(file)) {
|
|
917
|
+
try {
|
|
918
|
+
const stat = statSync(candidate)
|
|
919
|
+
if (stat.isFile()) return candidate
|
|
920
|
+
} catch(_) {
|
|
921
|
+
// ignore aliases that no longer exist
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return null
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
async function reconcileActiveErrors() {
|
|
928
|
+
if (!_activeErrors.size) return false
|
|
929
|
+
|
|
930
|
+
let changed = false
|
|
931
|
+
const items = Array.from(_activeErrors.values())
|
|
932
|
+
for (const item of items) {
|
|
933
|
+
const filepath = existingFileForError(item.file)
|
|
934
|
+
if (!filepath) {
|
|
935
|
+
if (takeError(item.file)) {
|
|
936
|
+
_terminalErrors.delete(terminalErrorKey(item.file))
|
|
937
|
+
broadcast({ type: 'clear-error', file: item.file })
|
|
938
|
+
changed = true
|
|
939
|
+
}
|
|
940
|
+
continue
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
if (!filepath.endsWith('.imba')) continue
|
|
944
|
+
|
|
945
|
+
const out = await compileFile(filepath)
|
|
946
|
+
if (out.errors?.length) continue
|
|
947
|
+
|
|
948
|
+
if (takeError(item.file)) {
|
|
949
|
+
_terminalErrors.delete(terminalErrorKey(item.file))
|
|
950
|
+
broadcast({ type: 'clear-error', file: item.file })
|
|
951
|
+
changed = true
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
return changed
|
|
956
|
+
}
|
|
957
|
+
|
|
872
958
|
function showTrackedError(item) {
|
|
873
959
|
const file = item.file
|
|
874
960
|
if (_isTTY) renderActiveErrors()
|
|
@@ -943,15 +1029,20 @@ export function serve(entrypoint, flags) {
|
|
|
943
1029
|
return { cleared: hadError || wasStatusFile, file: key, showedNext }
|
|
944
1030
|
}
|
|
945
1031
|
|
|
946
|
-
function markSuccess(file) {
|
|
1032
|
+
async function markSuccess(file) {
|
|
947
1033
|
const key = normalizeFile(file)
|
|
948
1034
|
const result = clearError(key)
|
|
1035
|
+
const reconciled = await reconcileActiveErrors()
|
|
949
1036
|
const active = _activeErrors.size
|
|
950
|
-
|
|
1037
|
+
let showedNext = false
|
|
1038
|
+
if (active && _isTTY) showedNext = renderActiveErrors()
|
|
1039
|
+
else if (_isTTY && (reconciled || result.showedNext)) clearStatus()
|
|
1040
|
+
else showedNext = result.showedNext
|
|
1041
|
+
const shouldPrint = (result?.cleared || reconciled) && !showedNext && !active
|
|
951
1042
|
if (shouldPrint) {
|
|
952
|
-
printStatus(key, 'ok', null, {
|
|
1043
|
+
printStatus(key, 'ok', null, { fadeAfter: 3500 })
|
|
953
1044
|
}
|
|
954
|
-
return { cleared: !!result?.cleared, printed: !!shouldPrint, showedNext: !!
|
|
1045
|
+
return { cleared: !!result?.cleared || reconciled, printed: !!shouldPrint, showedNext: !!showedNext, active }
|
|
955
1046
|
}
|
|
956
1047
|
|
|
957
1048
|
const _debounce = new Map()
|
|
@@ -1019,12 +1110,12 @@ export function serve(entrypoint, flags) {
|
|
|
1019
1110
|
return
|
|
1020
1111
|
}
|
|
1021
1112
|
|
|
1022
|
-
const success = markSuccess(rel)
|
|
1113
|
+
const success = await markSuccess(rel)
|
|
1023
1114
|
|
|
1024
1115
|
// No change at all — skip
|
|
1025
1116
|
if (out.changeType === 'none' || out.changeType === 'cached') return
|
|
1026
1117
|
|
|
1027
|
-
if (!success.printed && !success.showedNext && !success.active) printStatus(rel, 'ok', null, {
|
|
1118
|
+
if (!success.printed && !success.showedNext && !success.active) printStatus(rel, 'ok', null, { fadeAfter: 3500 })
|
|
1028
1119
|
broadcast({ type: 'update', file: rel, slots: out.slots || 'shifted' })
|
|
1029
1120
|
} catch(e) {
|
|
1030
1121
|
if (!isCurrentChange(file, version)) return
|
|
@@ -1062,7 +1153,7 @@ export function serve(entrypoint, flags) {
|
|
|
1062
1153
|
const file = 'vendor:' + (specifier || pathname)
|
|
1063
1154
|
const bundled = specifier ? await bundleVendor(specifier) : null
|
|
1064
1155
|
if (bundled?.code) {
|
|
1065
|
-
markSuccess(file)
|
|
1156
|
+
await markSuccess(file)
|
|
1066
1157
|
return new Response(bundled.code, { headers: { 'Content-Type': 'application/javascript' } })
|
|
1067
1158
|
}
|
|
1068
1159
|
|
|
@@ -1075,7 +1166,7 @@ export function serve(entrypoint, flags) {
|
|
|
1075
1166
|
const file = 'html:' + normalizeFile(htmlFile)
|
|
1076
1167
|
try {
|
|
1077
1168
|
let html = await Bun.file(htmlFile).text()
|
|
1078
|
-
markSuccess(file)
|
|
1169
|
+
await markSuccess(file)
|
|
1079
1170
|
return new Response(transformHtml(html, entrypoint), {
|
|
1080
1171
|
headers: { 'Content-Type': 'text/html' },
|
|
1081
1172
|
})
|
|
@@ -1101,7 +1192,7 @@ export function serve(entrypoint, flags) {
|
|
|
1101
1192
|
if (out.errors?.length) {
|
|
1102
1193
|
return errorResponse(file, out.errors)
|
|
1103
1194
|
}
|
|
1104
|
-
markSuccess(file)
|
|
1195
|
+
await markSuccess(file)
|
|
1105
1196
|
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
1106
1197
|
} catch(e) {
|
|
1107
1198
|
if (isMissingFileError(e)) {
|
|
@@ -1123,7 +1214,7 @@ export function serve(entrypoint, flags) {
|
|
|
1123
1214
|
try {
|
|
1124
1215
|
if (cssFile && await cssFile.exists()) {
|
|
1125
1216
|
if (req.headers.get('sec-fetch-dest') === 'style') {
|
|
1126
|
-
markSuccess(file)
|
|
1217
|
+
await markSuccess(file)
|
|
1127
1218
|
return new Response(cssFile, { headers: { 'Content-Type': 'text/css' } })
|
|
1128
1219
|
}
|
|
1129
1220
|
|
|
@@ -1135,7 +1226,7 @@ export function serve(entrypoint, flags) {
|
|
|
1135
1226
|
`if (!el) { el = document.createElement('style'); el.setAttribute('data-bimba-css', id); document.head.appendChild(el); }`,
|
|
1136
1227
|
`el.textContent = ${JSON.stringify(css)};`,
|
|
1137
1228
|
].join('\n')
|
|
1138
|
-
markSuccess(file)
|
|
1229
|
+
await markSuccess(file)
|
|
1139
1230
|
return new Response(js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
1140
1231
|
}
|
|
1141
1232
|
} catch (error) {
|
|
@@ -1153,7 +1244,7 @@ export function serve(entrypoint, flags) {
|
|
|
1153
1244
|
const file = 'js:' + normalizeFile(jsFile)
|
|
1154
1245
|
try {
|
|
1155
1246
|
const response = await serveJavaScriptFile(jsFile)
|
|
1156
|
-
markSuccess(file)
|
|
1247
|
+
await markSuccess(file)
|
|
1157
1248
|
return response
|
|
1158
1249
|
} catch (error) {
|
|
1159
1250
|
if (isMissingFileError(error)) {
|
|
@@ -1180,7 +1271,7 @@ export function serve(entrypoint, flags) {
|
|
|
1180
1271
|
if (out.errors?.length) {
|
|
1181
1272
|
return errorResponse(file, out.errors)
|
|
1182
1273
|
}
|
|
1183
|
-
markSuccess(file)
|
|
1274
|
+
await markSuccess(file)
|
|
1184
1275
|
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
1185
1276
|
}
|
|
1186
1277
|
|
|
@@ -1188,7 +1279,7 @@ export function serve(entrypoint, flags) {
|
|
|
1188
1279
|
const file = 'vendor:' + normalizeFile(pathname)
|
|
1189
1280
|
const bundled = await bundleVendor(path.resolve(resolved))
|
|
1190
1281
|
if (bundled?.code) {
|
|
1191
|
-
markSuccess(file)
|
|
1282
|
+
await markSuccess(file)
|
|
1192
1283
|
return new Response(bundled.code, { headers: { 'Content-Type': 'application/javascript' } })
|
|
1193
1284
|
}
|
|
1194
1285
|
return errorResponse(file, bundled?.errors || [`Could not bundle ${pathname}`])
|
|
@@ -1200,13 +1291,13 @@ export function serve(entrypoint, flags) {
|
|
|
1200
1291
|
const inHtmlDirPath = path.join(htmlDir, pathname)
|
|
1201
1292
|
const inHtmlDir = Bun.file(inHtmlDirPath)
|
|
1202
1293
|
if (await inHtmlDir.exists()) {
|
|
1203
|
-
markSuccess('static:' + normalizeFile(inHtmlDirPath))
|
|
1294
|
+
await markSuccess('static:' + normalizeFile(inHtmlDirPath))
|
|
1204
1295
|
return new Response(inHtmlDir)
|
|
1205
1296
|
}
|
|
1206
1297
|
const inRootPath = '.' + pathname
|
|
1207
1298
|
const inRoot = Bun.file(inRootPath)
|
|
1208
1299
|
if (await inRoot.exists()) {
|
|
1209
|
-
markSuccess('static:' + normalizeFile(inRootPath))
|
|
1300
|
+
await markSuccess('static:' + normalizeFile(inRootPath))
|
|
1210
1301
|
return new Response(inRoot)
|
|
1211
1302
|
}
|
|
1212
1303
|
} catch (error) {
|
|
@@ -1228,7 +1319,7 @@ export function serve(entrypoint, flags) {
|
|
|
1228
1319
|
if (out.errors?.length) {
|
|
1229
1320
|
return errorResponse(file, out.errors)
|
|
1230
1321
|
}
|
|
1231
|
-
markSuccess(file)
|
|
1322
|
+
await markSuccess(file)
|
|
1232
1323
|
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
1233
1324
|
}
|
|
1234
1325
|
for (const ext of ['.js', '.mjs']) {
|
|
@@ -1237,7 +1328,7 @@ export function serve(entrypoint, flags) {
|
|
|
1237
1328
|
const file = 'js:' + normalizeFile(withExt)
|
|
1238
1329
|
try {
|
|
1239
1330
|
const response = await serveJavaScriptFile(withExt)
|
|
1240
|
-
markSuccess(file)
|
|
1331
|
+
await markSuccess(file)
|
|
1241
1332
|
return response
|
|
1242
1333
|
} catch (error) {
|
|
1243
1334
|
if (isMissingFileError(error)) {
|
|
@@ -1255,7 +1346,7 @@ export function serve(entrypoint, flags) {
|
|
|
1255
1346
|
const file = 'html:' + normalizeFile(htmlPath)
|
|
1256
1347
|
try {
|
|
1257
1348
|
let html = await Bun.file(htmlPath).text()
|
|
1258
|
-
markSuccess(file)
|
|
1349
|
+
await markSuccess(file)
|
|
1259
1350
|
return new Response(transformHtml(html, entrypoint), {
|
|
1260
1351
|
headers: { 'Content-Type': 'text/html' },
|
|
1261
1352
|
})
|