bimba-cli 0.7.25 → 0.7.27

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 (3) hide show
  1. package/package.json +1 -1
  2. package/plugin.js +37 -7
  3. package/serve.js +53 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bimba-cli",
3
- "version": "0.7.25",
3
+ "version": "0.7.27",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/HeapVoid/bimba.git"
package/plugin.js CHANGED
@@ -22,7 +22,7 @@ export let stats = {
22
22
  const _activeCompileErrors = new Map();
23
23
 
24
24
  function normalizeCompilePath(filepath) {
25
- let value = String(filepath || '');
25
+ let value = String(filepath || '').split(/[?#]/)[0];
26
26
  if (dir.isAbsolute(value)) {
27
27
  const rel = dir.relative(process.cwd(), value);
28
28
  if (!rel.startsWith('..')) value = rel;
@@ -30,6 +30,21 @@ function normalizeCompilePath(filepath) {
30
30
  return value.replaceAll('\\', '/');
31
31
  }
32
32
 
33
+ function physicalCompileKey(filepath) {
34
+ try {
35
+ const stat = fs.statSync(filepath);
36
+ if (!stat.isFile()) return null;
37
+ const real = fs.realpathSync(filepath).replaceAll('\\', '/');
38
+ return `fs:${stat.dev}:${stat.ino}:${real}`;
39
+ } catch(_) {
40
+ return null;
41
+ }
42
+ }
43
+
44
+ function compileErrorKey(filepath) {
45
+ return physicalCompileKey(filepath) || `path:${normalizeCompilePath(filepath)}`;
46
+ }
47
+
33
48
  function compileErrorMessage(error) {
34
49
  return error?.message || String(error);
35
50
  }
@@ -53,15 +68,25 @@ function compileErrorSignature(errors) {
53
68
  }
54
69
 
55
70
  function shouldPrintCompileError(filepath, errors) {
56
- const key = normalizeCompilePath(filepath);
71
+ const key = compileErrorKey(filepath);
57
72
  const signature = compileErrorSignature(errors);
58
73
  const previous = _activeCompileErrors.get(key);
59
- _activeCompileErrors.set(key, { signature, time: Date.now() });
74
+ _activeCompileErrors.set(key, { file: normalizeCompilePath(filepath), signature, time: Date.now() });
60
75
  return previous?.signature !== signature;
61
76
  }
62
77
 
63
78
  function clearCompileError(filepath) {
64
- _activeCompileErrors.delete(normalizeCompilePath(filepath));
79
+ _activeCompileErrors.delete(compileErrorKey(filepath));
80
+ }
81
+
82
+ function printCompileError(error) {
83
+ try {
84
+ printerr(error);
85
+ } catch(_) {
86
+ console.log('');
87
+ console.log(' ' + theme.error(' ' + compileErrorMessage(error) + ' '));
88
+ console.log('');
89
+ }
65
90
  }
66
91
 
67
92
  // Target platform for the Imba compiler: 'browser' or 'node'
@@ -98,11 +123,16 @@ export const imbaPlugin = {
98
123
  // if no cached version read and compile it with the imba compiler
99
124
  const file = await Bun.file(path).text();
100
125
  const platform = target === 'node' || target === 'bun' ? 'node' : 'browser';
101
- const out = compiler.compile(file, {
126
+ let out
127
+ try {
128
+ out = compiler.compile(file, {
102
129
  sourcePath: path,
103
130
  platform: platform,
104
131
  comments: false
105
- })
132
+ })
133
+ } catch (error) {
134
+ out = { js: '', errors: [error] }
135
+ }
106
136
 
107
137
  // the file has been successfully compiled
108
138
  if (!out.errors?.length) {
@@ -121,7 +151,7 @@ export const imbaPlugin = {
121
151
  if (shouldPrint) {
122
152
  stats.reported++;
123
153
  for (let i = 0; i < out.errors.length; i++) {
124
- if(out.errors[i]) printerr(out.errors[i]);
154
+ if(out.errors[i]) printCompileError(out.errors[i]);
125
155
  }
126
156
  }
127
157
  stats.errors++;
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'
@@ -707,7 +707,8 @@ export function serve(entrypoint, flags) {
707
707
  _statusSaved = true
708
708
  _statusKind = 'errors'
709
709
 
710
- for (const [file, item] of _activeErrors.entries()) {
710
+ for (const item of _activeErrors.values()) {
711
+ const file = item.file
711
712
  process.stdout.write(` ${theme.folder(item.time)} ${theme.filename(file)} ${theme.failure(' fail ')}\n`)
712
713
  for (const err of item.errors) {
713
714
  try { printerr(err) } catch(_) { process.stdout.write(' ' + err.message + '\n') }
@@ -738,8 +739,12 @@ export function serve(entrypoint, flags) {
738
739
  const srcRoot = path.resolve(srcDir)
739
740
  const srcRel = normalizeFile(srcRoot)
740
741
 
742
+ function unprefixFile(file) {
743
+ return normalizeFile(file).replace(/^(?:html|css|js|static):/, '')
744
+ }
745
+
741
746
  function fileVariants(file) {
742
- const key = normalizeFile(file)
747
+ const key = unprefixFile(file)
743
748
  const variants = new Set([key])
744
749
 
745
750
  if (srcRel && key.startsWith(srcRel + '/')) variants.add(key.slice(srcRel.length + 1))
@@ -748,7 +753,38 @@ export function serve(entrypoint, flags) {
748
753
  return Array.from(variants).filter(Boolean)
749
754
  }
750
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
+
751
783
  function sameFile(left, right) {
784
+ const leftPhysical = physicalFileKey(left)
785
+ const rightPhysical = physicalFileKey(right)
786
+ if (leftPhysical && rightPhysical && leftPhysical === rightPhysical) return true
787
+
752
788
  const lefts = fileVariants(left)
753
789
  const rights = fileVariants(right)
754
790
 
@@ -763,9 +799,12 @@ export function serve(entrypoint, flags) {
763
799
 
764
800
  function takeError(file) {
765
801
  let previous = null
802
+ const target = errorKey(file)
766
803
  const keys = Array.from(_activeErrors.keys())
767
804
  for (const key of keys) {
768
- if (!sameFile(key, file)) continue
805
+ const item = _activeErrors.get(key)
806
+ const storedFile = item?.file || key.replace(/^path:/, '')
807
+ if (key !== target && !sameFile(storedFile, file)) continue
769
808
  previous ||= _activeErrors.get(key)
770
809
  _activeErrors.delete(key)
771
810
  }
@@ -802,19 +841,22 @@ export function serve(entrypoint, flags) {
802
841
  .join('\n---\n')
803
842
  }
804
843
 
805
- function showTrackedError(file, item) {
844
+ function showTrackedError(item) {
845
+ const file = item.file
806
846
  if (_isTTY) renderErrorPanel()
807
847
  else printStatus(file, 'fail', item.errors)
808
848
  broadcast({ type: 'error', file, time: item.time, errors: item.payload })
809
849
  }
810
850
 
811
851
  function reportError(file, errors) {
812
- const key = normalizeFile(file)
852
+ const display = normalizeFile(file)
853
+ const key = errorKey(display)
813
854
  const list = Array.isArray(errors) ? errors : [errors]
814
855
  const signature = errorSignature(list)
815
- const previous = takeError(key)
856
+ const previous = takeError(display)
816
857
 
817
858
  const item = {
859
+ file: display,
818
860
  signature,
819
861
  errors: list,
820
862
  payload: serializeErrors(list),
@@ -822,11 +864,11 @@ export function serve(entrypoint, flags) {
822
864
  }
823
865
  _activeErrors.set(key, item)
824
866
  if (previous?.signature === signature && !_isTTY) {
825
- broadcast({ type: 'error', file: key, time: item.time, errors: item.payload })
867
+ broadcast({ type: 'error', file: display, time: item.time, errors: item.payload })
826
868
  return
827
869
  }
828
870
 
829
- showTrackedError(key, item)
871
+ showTrackedError(item)
830
872
  }
831
873
 
832
874
  function errorText(errors) {
@@ -857,8 +899,8 @@ export function serve(entrypoint, flags) {
857
899
  broadcast({ type: 'clear-error', file: key })
858
900
 
859
901
  if (!_isTTY && wasStatusFile && _activeErrors.size) {
860
- const [nextFile, nextItem] = Array.from(_activeErrors.entries()).at(-1)
861
- showTrackedError(nextFile, nextItem)
902
+ const nextItem = Array.from(_activeErrors.values()).at(-1)
903
+ showTrackedError(nextItem)
862
904
  showedNext = true
863
905
  }
864
906