bimba-cli 0.7.21 → 0.7.23
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 +68 -36
package/package.json
CHANGED
package/serve.js
CHANGED
|
@@ -265,7 +265,7 @@ const hmrClient = `
|
|
|
265
265
|
|
|
266
266
|
// ─── Server-side compile cache ────────────────────────────────────────────────
|
|
267
267
|
|
|
268
|
-
const _compileCache = new Map() // filepath → {
|
|
268
|
+
const _compileCache = new Map() // filepath → { stamp, result }
|
|
269
269
|
const _prevJs = new Map() // filepath → compiled js — for change detection
|
|
270
270
|
const _prevSlots = new Map() // filepath → previous symbol slot count
|
|
271
271
|
const _importScanner = new Bun.Transpiler({ loader: 'js' })
|
|
@@ -367,7 +367,7 @@ async function compileFile(filepath) {
|
|
|
367
367
|
const abs = path.resolve(filepath)
|
|
368
368
|
if (!existsSync(abs)) return missingCompileResult(abs)
|
|
369
369
|
|
|
370
|
-
const file = Bun.file(
|
|
370
|
+
const file = Bun.file(abs)
|
|
371
371
|
let stat
|
|
372
372
|
try {
|
|
373
373
|
stat = await file.stat()
|
|
@@ -375,10 +375,10 @@ async function compileFile(filepath) {
|
|
|
375
375
|
if (isMissingFileError(error)) return missingCompileResult(abs)
|
|
376
376
|
throw error
|
|
377
377
|
}
|
|
378
|
-
const
|
|
378
|
+
const stamp = `${stat.mtimeMs ?? stat.mtime?.getTime?.() ?? 0}:${stat.size ?? 0}`
|
|
379
379
|
|
|
380
380
|
const cached = _compileCache.get(abs)
|
|
381
|
-
if (cached && cached.
|
|
381
|
+
if (cached && cached.stamp === stamp) return _normalizeResult(cached.result, { changeType: 'cached' })
|
|
382
382
|
|
|
383
383
|
let code
|
|
384
384
|
try {
|
|
@@ -406,7 +406,7 @@ async function compileFile(filepath) {
|
|
|
406
406
|
const baked = { js: result.js, errors, slots: result.slots }
|
|
407
407
|
const changeType = _prevJs.get(abs) === baked.js ? 'none' : 'full'
|
|
408
408
|
_prevJs.set(abs, baked.js)
|
|
409
|
-
_compileCache.set(abs, {
|
|
409
|
+
_compileCache.set(abs, { stamp, result: baked })
|
|
410
410
|
return _normalizeResult(baked, { changeType })
|
|
411
411
|
}
|
|
412
412
|
|
|
@@ -652,6 +652,9 @@ export function serve(entrypoint, flags) {
|
|
|
652
652
|
return value
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
+
const srcRoot = path.resolve(srcDir)
|
|
656
|
+
const srcRel = normalizeFile(srcRoot)
|
|
657
|
+
|
|
655
658
|
function errorMessage(error) {
|
|
656
659
|
return error?.message || String(error)
|
|
657
660
|
}
|
|
@@ -731,42 +734,71 @@ export function serve(entrypoint, flags) {
|
|
|
731
734
|
clearStatus(key)
|
|
732
735
|
broadcast({ type: 'clear-error', file: key })
|
|
733
736
|
|
|
737
|
+
let showedNext = false
|
|
734
738
|
if (wasStatusFile && _activeErrors.size) {
|
|
735
739
|
const [nextFile, nextItem] = Array.from(_activeErrors.entries()).at(-1)
|
|
736
740
|
showTrackedError(nextFile, nextItem)
|
|
741
|
+
showedNext = true
|
|
737
742
|
}
|
|
743
|
+
|
|
744
|
+
return { cleared: hadError || wasStatusFile, file: key, showedNext }
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function markSuccess(file) {
|
|
748
|
+
const key = normalizeFile(file)
|
|
749
|
+
const result = clearError(key)
|
|
750
|
+
const shouldPrint = result?.cleared && !result.showedNext
|
|
751
|
+
if (shouldPrint) printStatus(key, 'ok')
|
|
752
|
+
return { cleared: !!result?.cleared, printed: !!shouldPrint, showedNext: !!result?.showedNext }
|
|
738
753
|
}
|
|
739
754
|
|
|
740
755
|
const _debounce = new Map()
|
|
741
756
|
const _watchVersion = new Map()
|
|
742
757
|
|
|
743
|
-
function
|
|
758
|
+
function watchedFile(filename) {
|
|
744
759
|
filename = filename && String(filename)
|
|
745
|
-
if (!filename
|
|
760
|
+
if (!filename) return null
|
|
761
|
+
|
|
762
|
+
let filepath
|
|
763
|
+
if (path.isAbsolute(filename)) {
|
|
764
|
+
filepath = path.resolve(filename)
|
|
765
|
+
} else {
|
|
766
|
+
const rel = normalizeFile(filename)
|
|
767
|
+
filepath = (rel === srcRel || rel.startsWith(srcRel + '/'))
|
|
768
|
+
? path.resolve(filename)
|
|
769
|
+
: path.resolve(srcRoot, filename)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const rel = normalizeFile(filepath)
|
|
773
|
+
return { filepath, rel }
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function scheduleCompile(filename) {
|
|
777
|
+
const file = watchedFile(filename)
|
|
778
|
+
if (!file || !file.rel.endsWith('.imba')) return
|
|
746
779
|
|
|
747
|
-
const version = (_watchVersion.get(
|
|
748
|
-
_watchVersion.set(
|
|
780
|
+
const version = (_watchVersion.get(file.rel) || 0) + 1
|
|
781
|
+
_watchVersion.set(file.rel, version)
|
|
749
782
|
|
|
750
|
-
const pending = _debounce.get(
|
|
783
|
+
const pending = _debounce.get(file.rel)
|
|
751
784
|
if (pending) clearTimeout(pending)
|
|
752
785
|
|
|
753
|
-
_debounce.set(
|
|
754
|
-
_debounce.delete(
|
|
755
|
-
compileChangedFile(
|
|
786
|
+
_debounce.set(file.rel, setTimeout(() => {
|
|
787
|
+
_debounce.delete(file.rel)
|
|
788
|
+
compileChangedFile(file, version)
|
|
756
789
|
}, 150))
|
|
757
790
|
}
|
|
758
791
|
|
|
759
|
-
function isCurrentChange(
|
|
760
|
-
return _watchVersion.get(
|
|
792
|
+
function isCurrentChange(file, version) {
|
|
793
|
+
return _watchVersion.get(file.rel) === version
|
|
761
794
|
}
|
|
762
795
|
|
|
763
|
-
async function compileChangedFile(
|
|
764
|
-
const filepath =
|
|
765
|
-
const rel = normalizeFile(path.join(path.relative('.', srcDir), filename))
|
|
796
|
+
async function compileChangedFile(file, version) {
|
|
797
|
+
const { filepath, rel } = file
|
|
766
798
|
|
|
767
799
|
try {
|
|
768
800
|
if (!existsSync(filepath)) {
|
|
769
|
-
if (!isCurrentChange(
|
|
801
|
+
if (!isCurrentChange(file, version)) return
|
|
770
802
|
dropFileState(filepath)
|
|
771
803
|
clearError(rel)
|
|
772
804
|
return
|
|
@@ -774,7 +806,7 @@ export function serve(entrypoint, flags) {
|
|
|
774
806
|
|
|
775
807
|
const out = await compileFile(filepath)
|
|
776
808
|
|
|
777
|
-
if (!isCurrentChange(
|
|
809
|
+
if (!isCurrentChange(file, version)) return
|
|
778
810
|
if (out.missing) {
|
|
779
811
|
clearError(rel)
|
|
780
812
|
return
|
|
@@ -785,15 +817,15 @@ export function serve(entrypoint, flags) {
|
|
|
785
817
|
return
|
|
786
818
|
}
|
|
787
819
|
|
|
788
|
-
|
|
820
|
+
const success = markSuccess(rel)
|
|
789
821
|
|
|
790
822
|
// No change at all — skip
|
|
791
823
|
if (out.changeType === 'none' || out.changeType === 'cached') return
|
|
792
824
|
|
|
793
|
-
printStatus(rel, 'ok')
|
|
825
|
+
if (!success.printed && !success.showedNext) printStatus(rel, 'ok')
|
|
794
826
|
broadcast({ type: 'update', file: rel, slots: out.slots || 'shifted' })
|
|
795
827
|
} catch(e) {
|
|
796
|
-
if (!isCurrentChange(
|
|
828
|
+
if (!isCurrentChange(file, version)) return
|
|
797
829
|
if (isMissingFileError(e)) {
|
|
798
830
|
dropFileState(filepath)
|
|
799
831
|
clearError(rel)
|
|
@@ -828,7 +860,7 @@ export function serve(entrypoint, flags) {
|
|
|
828
860
|
const file = 'vendor:' + (specifier || pathname)
|
|
829
861
|
const bundled = specifier ? await bundleVendor(specifier) : null
|
|
830
862
|
if (bundled?.code) {
|
|
831
|
-
|
|
863
|
+
markSuccess(file)
|
|
832
864
|
return new Response(bundled.code, { headers: { 'Content-Type': 'application/javascript' } })
|
|
833
865
|
}
|
|
834
866
|
|
|
@@ -841,7 +873,7 @@ export function serve(entrypoint, flags) {
|
|
|
841
873
|
const file = 'html:' + normalizeFile(htmlFile)
|
|
842
874
|
try {
|
|
843
875
|
let html = await Bun.file(htmlFile).text()
|
|
844
|
-
|
|
876
|
+
markSuccess(file)
|
|
845
877
|
return new Response(transformHtml(html, entrypoint), {
|
|
846
878
|
headers: { 'Content-Type': 'text/html' },
|
|
847
879
|
})
|
|
@@ -867,7 +899,7 @@ export function serve(entrypoint, flags) {
|
|
|
867
899
|
if (out.errors?.length) {
|
|
868
900
|
return errorResponse(file, out.errors)
|
|
869
901
|
}
|
|
870
|
-
|
|
902
|
+
markSuccess(file)
|
|
871
903
|
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
872
904
|
} catch(e) {
|
|
873
905
|
if (isMissingFileError(e)) {
|
|
@@ -889,7 +921,7 @@ export function serve(entrypoint, flags) {
|
|
|
889
921
|
try {
|
|
890
922
|
if (cssFile && await cssFile.exists()) {
|
|
891
923
|
if (req.headers.get('sec-fetch-dest') === 'style') {
|
|
892
|
-
|
|
924
|
+
markSuccess(file)
|
|
893
925
|
return new Response(cssFile, { headers: { 'Content-Type': 'text/css' } })
|
|
894
926
|
}
|
|
895
927
|
|
|
@@ -901,7 +933,7 @@ export function serve(entrypoint, flags) {
|
|
|
901
933
|
`if (!el) { el = document.createElement('style'); el.setAttribute('data-bimba-css', id); document.head.appendChild(el); }`,
|
|
902
934
|
`el.textContent = ${JSON.stringify(css)};`,
|
|
903
935
|
].join('\n')
|
|
904
|
-
|
|
936
|
+
markSuccess(file)
|
|
905
937
|
return new Response(js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
906
938
|
}
|
|
907
939
|
} catch (error) {
|
|
@@ -919,7 +951,7 @@ export function serve(entrypoint, flags) {
|
|
|
919
951
|
const file = 'js:' + normalizeFile(jsFile)
|
|
920
952
|
try {
|
|
921
953
|
const response = await serveJavaScriptFile(jsFile)
|
|
922
|
-
|
|
954
|
+
markSuccess(file)
|
|
923
955
|
return response
|
|
924
956
|
} catch (error) {
|
|
925
957
|
if (isMissingFileError(error)) {
|
|
@@ -946,7 +978,7 @@ export function serve(entrypoint, flags) {
|
|
|
946
978
|
if (out.errors?.length) {
|
|
947
979
|
return errorResponse(file, out.errors)
|
|
948
980
|
}
|
|
949
|
-
|
|
981
|
+
markSuccess(file)
|
|
950
982
|
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
951
983
|
}
|
|
952
984
|
|
|
@@ -954,7 +986,7 @@ export function serve(entrypoint, flags) {
|
|
|
954
986
|
const file = 'vendor:' + normalizeFile(pathname)
|
|
955
987
|
const bundled = await bundleVendor(path.resolve(resolved))
|
|
956
988
|
if (bundled?.code) {
|
|
957
|
-
|
|
989
|
+
markSuccess(file)
|
|
958
990
|
return new Response(bundled.code, { headers: { 'Content-Type': 'application/javascript' } })
|
|
959
991
|
}
|
|
960
992
|
return errorResponse(file, bundled?.errors || [`Could not bundle ${pathname}`])
|
|
@@ -966,13 +998,13 @@ export function serve(entrypoint, flags) {
|
|
|
966
998
|
const inHtmlDirPath = path.join(htmlDir, pathname)
|
|
967
999
|
const inHtmlDir = Bun.file(inHtmlDirPath)
|
|
968
1000
|
if (await inHtmlDir.exists()) {
|
|
969
|
-
|
|
1001
|
+
markSuccess('static:' + normalizeFile(inHtmlDirPath))
|
|
970
1002
|
return new Response(inHtmlDir)
|
|
971
1003
|
}
|
|
972
1004
|
const inRootPath = '.' + pathname
|
|
973
1005
|
const inRoot = Bun.file(inRootPath)
|
|
974
1006
|
if (await inRoot.exists()) {
|
|
975
|
-
|
|
1007
|
+
markSuccess('static:' + normalizeFile(inRootPath))
|
|
976
1008
|
return new Response(inRoot)
|
|
977
1009
|
}
|
|
978
1010
|
} catch (error) {
|
|
@@ -994,7 +1026,7 @@ export function serve(entrypoint, flags) {
|
|
|
994
1026
|
if (out.errors?.length) {
|
|
995
1027
|
return errorResponse(file, out.errors)
|
|
996
1028
|
}
|
|
997
|
-
|
|
1029
|
+
markSuccess(file)
|
|
998
1030
|
return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
|
|
999
1031
|
}
|
|
1000
1032
|
for (const ext of ['.js', '.mjs']) {
|
|
@@ -1003,7 +1035,7 @@ export function serve(entrypoint, flags) {
|
|
|
1003
1035
|
const file = 'js:' + normalizeFile(withExt)
|
|
1004
1036
|
try {
|
|
1005
1037
|
const response = await serveJavaScriptFile(withExt)
|
|
1006
|
-
|
|
1038
|
+
markSuccess(file)
|
|
1007
1039
|
return response
|
|
1008
1040
|
} catch (error) {
|
|
1009
1041
|
if (isMissingFileError(error)) {
|
|
@@ -1021,7 +1053,7 @@ export function serve(entrypoint, flags) {
|
|
|
1021
1053
|
const file = 'html:' + normalizeFile(htmlPath)
|
|
1022
1054
|
try {
|
|
1023
1055
|
let html = await Bun.file(htmlPath).text()
|
|
1024
|
-
|
|
1056
|
+
markSuccess(file)
|
|
1025
1057
|
return new Response(transformHtml(html, entrypoint), {
|
|
1026
1058
|
headers: { 'Content-Type': 'text/html' },
|
|
1027
1059
|
})
|