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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/serve.js +68 -36
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bimba-cli",
3
- "version": "0.7.21",
3
+ "version": "0.7.23",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/HeapVoid/bimba.git"
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 → { mtime, result }
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(filepath)
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 mtime = stat.mtime.getTime()
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.mtime === mtime) return _normalizeResult(cached.result, { changeType: '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, { mtime, result: baked })
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 scheduleCompile(filename) {
758
+ function watchedFile(filename) {
744
759
  filename = filename && String(filename)
745
- if (!filename || !filename.endsWith('.imba')) return
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(filename) || 0) + 1
748
- _watchVersion.set(filename, version)
780
+ const version = (_watchVersion.get(file.rel) || 0) + 1
781
+ _watchVersion.set(file.rel, version)
749
782
 
750
- const pending = _debounce.get(filename)
783
+ const pending = _debounce.get(file.rel)
751
784
  if (pending) clearTimeout(pending)
752
785
 
753
- _debounce.set(filename, setTimeout(() => {
754
- _debounce.delete(filename)
755
- compileChangedFile(filename, version)
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(filename, version) {
760
- return _watchVersion.get(filename) === version
792
+ function isCurrentChange(file, version) {
793
+ return _watchVersion.get(file.rel) === version
761
794
  }
762
795
 
763
- async function compileChangedFile(filename, version) {
764
- const filepath = path.join(srcDir, filename)
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(filename, version)) return
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(filename, version)) return
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
- clearError(rel)
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(filename, version)) return
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
- clearError(file)
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
- clearError(file)
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
- clearError(file)
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
- clearError(file)
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
- clearError(file)
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
- clearError(file)
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
- clearError(file)
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
- clearError(file)
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
- clearError('static:' + normalizeFile(inHtmlDirPath))
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
- clearError('static:' + normalizeFile(inRootPath))
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
- clearError(file)
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
- clearError(file)
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
- clearError(file)
1056
+ markSuccess(file)
1025
1057
  return new Response(transformHtml(html, entrypoint), {
1026
1058
  headers: { 'Content-Type': 'text/html' },
1027
1059
  })