bimba-cli 0.7.24 → 0.7.26
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { serve as bunServe } from 'bun'
|
|
2
2
|
import * as compiler from 'imba/compiler'
|
|
3
|
-
import { mkdirSync, watch, existsSync, statSync, writeFileSync } from 'fs'
|
|
3
|
+
import { mkdirSync, watch, existsSync, statSync, writeFileSync, realpathSync } from 'fs'
|
|
4
4
|
import path from 'path'
|
|
5
5
|
import { theme } from './utils.js'
|
|
6
6
|
import { printerr } from './plugin.js'
|
|
@@ -219,12 +219,19 @@ const hmrClient = `
|
|
|
219
219
|
const _compileErrors = new Map();
|
|
220
220
|
|
|
221
221
|
function normalizeFile(file) {
|
|
222
|
-
let value = String(file || '').split(String.fromCharCode(92)).join('/');
|
|
222
|
+
let value = String(file || '').split(/[?#]/)[0].split(String.fromCharCode(92)).join('/');
|
|
223
223
|
while (value.startsWith('./')) value = value.slice(2);
|
|
224
224
|
while (value.startsWith('/')) value = value.slice(1);
|
|
225
225
|
return value;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
function sameFile(left, right) {
|
|
229
|
+
const a = normalizeFile(left);
|
|
230
|
+
const b = normalizeFile(right);
|
|
231
|
+
if (!a || !b) return false;
|
|
232
|
+
return a === b || a.endsWith('/' + b) || b.endsWith('/' + a);
|
|
233
|
+
}
|
|
234
|
+
|
|
228
235
|
function escapeHtml(value) {
|
|
229
236
|
return String(value ?? '').replace(/[&<>"']/g, ch => ({
|
|
230
237
|
'&': '&',
|
|
@@ -281,6 +288,9 @@ const hmrClient = `
|
|
|
281
288
|
|
|
282
289
|
function showError(file, errors, time) {
|
|
283
290
|
const displayFile = normalizeFile(file);
|
|
291
|
+
for (const key of Array.from(_compileErrors.keys())) {
|
|
292
|
+
if (sameFile(key, displayFile)) _compileErrors.delete(key);
|
|
293
|
+
}
|
|
284
294
|
_compileErrors.set(displayFile, {
|
|
285
295
|
errors: Array.isArray(errors) ? errors : [errors],
|
|
286
296
|
time: time || new Date().toLocaleTimeString(),
|
|
@@ -289,8 +299,14 @@ const hmrClient = `
|
|
|
289
299
|
}
|
|
290
300
|
|
|
291
301
|
function clearError(file) {
|
|
292
|
-
if (file)
|
|
293
|
-
|
|
302
|
+
if (file) {
|
|
303
|
+
const displayFile = normalizeFile(file);
|
|
304
|
+
for (const key of Array.from(_compileErrors.keys())) {
|
|
305
|
+
if (sameFile(key, displayFile)) _compileErrors.delete(key);
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
_compileErrors.clear();
|
|
309
|
+
}
|
|
294
310
|
renderErrors();
|
|
295
311
|
}
|
|
296
312
|
|
|
@@ -691,7 +707,8 @@ export function serve(entrypoint, flags) {
|
|
|
691
707
|
_statusSaved = true
|
|
692
708
|
_statusKind = 'errors'
|
|
693
709
|
|
|
694
|
-
for (const
|
|
710
|
+
for (const item of _activeErrors.values()) {
|
|
711
|
+
const file = item.file
|
|
695
712
|
process.stdout.write(` ${theme.folder(item.time)} ${theme.filename(file)} ${theme.failure(' fail ')}\n`)
|
|
696
713
|
for (const err of item.errors) {
|
|
697
714
|
try { printerr(err) } catch(_) { process.stdout.write(' ' + err.message + '\n') }
|
|
@@ -708,6 +725,7 @@ export function serve(entrypoint, flags) {
|
|
|
708
725
|
|
|
709
726
|
function normalizeFile(file) {
|
|
710
727
|
let value = String(file || '')
|
|
728
|
+
value = value.split(/[?#]/)[0]
|
|
711
729
|
if (path.isAbsolute(value)) {
|
|
712
730
|
const rel = path.relative(process.cwd(), value)
|
|
713
731
|
if (!rel.startsWith('..')) value = rel
|
|
@@ -721,6 +739,78 @@ export function serve(entrypoint, flags) {
|
|
|
721
739
|
const srcRoot = path.resolve(srcDir)
|
|
722
740
|
const srcRel = normalizeFile(srcRoot)
|
|
723
741
|
|
|
742
|
+
function unprefixFile(file) {
|
|
743
|
+
return normalizeFile(file).replace(/^(?:html|css|js|static):/, '')
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function fileVariants(file) {
|
|
747
|
+
const key = unprefixFile(file)
|
|
748
|
+
const variants = new Set([key])
|
|
749
|
+
|
|
750
|
+
if (srcRel && key.startsWith(srcRel + '/')) variants.add(key.slice(srcRel.length + 1))
|
|
751
|
+
else if (srcRel && key) variants.add(srcRel + '/' + key)
|
|
752
|
+
|
|
753
|
+
return Array.from(variants).filter(Boolean)
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function fileCandidates(file) {
|
|
757
|
+
const candidates = []
|
|
758
|
+
for (const variant of fileVariants(file)) {
|
|
759
|
+
candidates.push(path.resolve(variant))
|
|
760
|
+
if (!variant.startsWith(srcRel + '/')) candidates.push(path.resolve(srcRoot, variant))
|
|
761
|
+
}
|
|
762
|
+
return candidates
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function physicalFileKey(file) {
|
|
766
|
+
for (const candidate of fileCandidates(file)) {
|
|
767
|
+
try {
|
|
768
|
+
const stat = statSync(candidate)
|
|
769
|
+
if (!stat.isFile()) continue
|
|
770
|
+
const real = realpathSync(candidate).replaceAll('\\', '/')
|
|
771
|
+
return `fs:${stat.dev}:${stat.ino}:${real}`
|
|
772
|
+
} catch(_) {
|
|
773
|
+
// ignore non-existing aliases
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return null
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function errorKey(file) {
|
|
780
|
+
return physicalFileKey(file) || `path:${normalizeFile(file)}`
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function sameFile(left, right) {
|
|
784
|
+
const leftPhysical = physicalFileKey(left)
|
|
785
|
+
const rightPhysical = physicalFileKey(right)
|
|
786
|
+
if (leftPhysical && rightPhysical && leftPhysical === rightPhysical) return true
|
|
787
|
+
|
|
788
|
+
const lefts = fileVariants(left)
|
|
789
|
+
const rights = fileVariants(right)
|
|
790
|
+
|
|
791
|
+
for (const a of lefts) {
|
|
792
|
+
for (const b of rights) {
|
|
793
|
+
if (a === b || a.endsWith('/' + b) || b.endsWith('/' + a)) return true
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
return false
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function takeError(file) {
|
|
801
|
+
let previous = null
|
|
802
|
+
const target = errorKey(file)
|
|
803
|
+
const keys = Array.from(_activeErrors.keys())
|
|
804
|
+
for (const key of keys) {
|
|
805
|
+
const item = _activeErrors.get(key)
|
|
806
|
+
const storedFile = item?.file || key.replace(/^path:/, '')
|
|
807
|
+
if (key !== target && !sameFile(storedFile, file)) continue
|
|
808
|
+
previous ||= _activeErrors.get(key)
|
|
809
|
+
_activeErrors.delete(key)
|
|
810
|
+
}
|
|
811
|
+
return previous
|
|
812
|
+
}
|
|
813
|
+
|
|
724
814
|
function errorMessage(error) {
|
|
725
815
|
return error?.message || String(error)
|
|
726
816
|
}
|
|
@@ -751,19 +841,22 @@ export function serve(entrypoint, flags) {
|
|
|
751
841
|
.join('\n---\n')
|
|
752
842
|
}
|
|
753
843
|
|
|
754
|
-
function showTrackedError(
|
|
844
|
+
function showTrackedError(item) {
|
|
845
|
+
const file = item.file
|
|
755
846
|
if (_isTTY) renderErrorPanel()
|
|
756
847
|
else printStatus(file, 'fail', item.errors)
|
|
757
848
|
broadcast({ type: 'error', file, time: item.time, errors: item.payload })
|
|
758
849
|
}
|
|
759
850
|
|
|
760
851
|
function reportError(file, errors) {
|
|
761
|
-
const
|
|
852
|
+
const display = normalizeFile(file)
|
|
853
|
+
const key = errorKey(display)
|
|
762
854
|
const list = Array.isArray(errors) ? errors : [errors]
|
|
763
855
|
const signature = errorSignature(list)
|
|
764
|
-
const previous =
|
|
856
|
+
const previous = takeError(display)
|
|
765
857
|
|
|
766
858
|
const item = {
|
|
859
|
+
file: display,
|
|
767
860
|
signature,
|
|
768
861
|
errors: list,
|
|
769
862
|
payload: serializeErrors(list),
|
|
@@ -771,11 +864,11 @@ export function serve(entrypoint, flags) {
|
|
|
771
864
|
}
|
|
772
865
|
_activeErrors.set(key, item)
|
|
773
866
|
if (previous?.signature === signature && !_isTTY) {
|
|
774
|
-
broadcast({ type: 'error', file:
|
|
867
|
+
broadcast({ type: 'error', file: display, time: item.time, errors: item.payload })
|
|
775
868
|
return
|
|
776
869
|
}
|
|
777
870
|
|
|
778
|
-
showTrackedError(
|
|
871
|
+
showTrackedError(item)
|
|
779
872
|
}
|
|
780
873
|
|
|
781
874
|
function errorText(errors) {
|
|
@@ -790,26 +883,24 @@ export function serve(entrypoint, flags) {
|
|
|
790
883
|
|
|
791
884
|
function clearError(file) {
|
|
792
885
|
const key = file ? normalizeFile(file) : null
|
|
793
|
-
const
|
|
794
|
-
const
|
|
886
|
+
const hadPanel = _statusKind === 'errors'
|
|
887
|
+
const wasStatusFile = key && _statusFile && sameFile(_statusFile, key)
|
|
888
|
+
const hadError = key ? !!takeError(key) : _activeErrors.size > 0
|
|
795
889
|
|
|
796
|
-
if (key) _activeErrors.
|
|
797
|
-
else _activeErrors.clear()
|
|
798
|
-
|
|
799
|
-
if (key && !hadError && !wasStatusFile) return
|
|
890
|
+
if (!key) _activeErrors.clear()
|
|
800
891
|
|
|
801
892
|
let showedNext = false
|
|
802
|
-
if (_isTTY && (hadError ||
|
|
893
|
+
if (_isTTY && (hadError || wasStatusFile || hadPanel)) {
|
|
803
894
|
showedNext = renderErrorPanel()
|
|
804
|
-
} else {
|
|
895
|
+
} else if (!key || hadError || wasStatusFile) {
|
|
805
896
|
clearStatus(key)
|
|
806
897
|
}
|
|
807
898
|
|
|
808
899
|
broadcast({ type: 'clear-error', file: key })
|
|
809
900
|
|
|
810
901
|
if (!_isTTY && wasStatusFile && _activeErrors.size) {
|
|
811
|
-
const
|
|
812
|
-
showTrackedError(
|
|
902
|
+
const nextItem = Array.from(_activeErrors.values()).at(-1)
|
|
903
|
+
showTrackedError(nextItem)
|
|
813
904
|
showedNext = true
|
|
814
905
|
}
|
|
815
906
|
|