bimba-cli 0.7.28 → 0.7.29

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 +82 -56
  3. package/serve.js +59 -38
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bimba-cli",
3
- "version": "0.7.28",
3
+ "version": "0.7.29",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/HeapVoid/bimba.git"
package/plugin.js CHANGED
@@ -45,6 +45,16 @@ function compileErrorKey(filepath) {
45
45
  return physicalCompileKey(filepath) || `path:${normalizeCompilePath(filepath)}`;
46
46
  }
47
47
 
48
+ function compileFileStamp(filepath) {
49
+ try {
50
+ const stat = fs.statSync(filepath);
51
+ if (!stat.isFile()) return null;
52
+ return `${stat.mtimeMs ?? stat.mtime?.getTime?.() ?? 0}_${stat.size ?? 0}`;
53
+ } catch(_) {
54
+ return null;
55
+ }
56
+ }
57
+
48
58
  function compileErrorMessage(error) {
49
59
  return error?.message || String(error);
50
60
  }
@@ -102,66 +112,82 @@ export const imbaPlugin = {
102
112
  build.onLoad({ filter: /\.imba$/ }, async ({ path }) => {
103
113
 
104
114
  const f = dir.parse(path)
105
- let contents = '';
106
-
107
- // return the cached version if exists (include target in hash to avoid cross-platform cache hits)
108
- const cached = dir.join(cache, Bun.hash(path + ':' + target) + '_' + fs.statSync(path).mtimeMs + '.js');
109
- if (fs.existsSync(cached)) {
110
- clearCompileError(path);
111
- stats.bundled++;
112
- stats.cached++;
113
- return {
114
- contents: await Bun.file(cached).text(),
115
- loader: "js",
116
- };
117
- }
118
115
 
119
- // clear previous cached version
120
- const glob = new Glob(Bun.hash(path + ':' + target) + '_' + "*.js");
121
- for await (const file of glob.scan(cache)) if (fs.existsSync(dir.join(cache, file))) unlink(dir.join(cache, file));
122
-
123
- // if no cached version read and compile it with the imba compiler
124
- const file = await Bun.file(path).text();
125
- const platform = target === 'node' || target === 'bun' ? 'node' : 'browser';
126
- let out
127
- try {
128
- out = compiler.compile(file, {
129
- sourcePath: path,
130
- platform: platform,
131
- comments: false
132
- })
133
- } catch (error) {
134
- out = { js: '', errors: [error] }
135
- }
136
-
137
- // the file has been successfully compiled
138
- if (!out.errors?.length) {
139
- clearCompileError(path);
140
- console.log(theme.action("compiling: ") + theme.folder(dir.join(f.dir,'/')) + theme.filename(f.base) + " - " + theme.success("compiled"));
141
- stats.bundled++;
142
- stats.compiled++;
143
- contents = out.js;
144
- await Bun.write(cached, contents);
145
- }
146
- // there were errors during compilation
147
- else {
148
- const shouldPrint = shouldPrintCompileError(path, out.errors);
149
- if (shouldPrint) console.log(theme.action("compiling: ") + theme.folder(dir.join(f.dir,'/')) + theme.filename(f.base) + " - " + theme.failure(" fail "));
150
- stats.failed++;
151
- if (shouldPrint) {
152
- stats.reported++;
153
- for (let i = 0; i < out.errors.length; i++) {
154
- if(out.errors[i]) printCompileError(out.errors[i]);
116
+ while (true) {
117
+ const stamp = compileFileStamp(path);
118
+ if (!stamp) {
119
+ clearCompileError(path);
120
+ return { contents: '', loader: "js" };
121
+ }
122
+
123
+ let contents = '';
124
+
125
+ // return the cached version if exists (include target in hash to avoid cross-platform cache hits)
126
+ const cached = dir.join(cache, Bun.hash(path + ':' + target) + '_' + stamp + '.js');
127
+ if (fs.existsSync(cached)) {
128
+ const cachedContents = await Bun.file(cached).text();
129
+ if (compileFileStamp(path) !== stamp) continue;
130
+ clearCompileError(path);
131
+ stats.bundled++;
132
+ stats.cached++;
133
+ return {
134
+ contents: cachedContents,
135
+ loader: "js",
136
+ };
137
+ }
138
+
139
+ // clear previous cached version
140
+ const glob = new Glob(Bun.hash(path + ':' + target) + '_' + "*.js");
141
+ for await (const file of glob.scan(cache)) if (fs.existsSync(dir.join(cache, file))) unlink(dir.join(cache, file));
142
+
143
+ // if no cached version read and compile it with the imba compiler
144
+ const file = await Bun.file(path).text();
145
+ if (compileFileStamp(path) !== stamp) continue;
146
+
147
+ const platform = target === 'node' || target === 'bun' ? 'node' : 'browser';
148
+ let out
149
+ try {
150
+ out = compiler.compile(file, {
151
+ sourcePath: path,
152
+ platform: platform,
153
+ comments: false
154
+ })
155
+ } catch (error) {
156
+ out = { js: '', errors: [error] }
157
+ }
158
+
159
+ // Never report or cache a result from an older source snapshot.
160
+ if (compileFileStamp(path) !== stamp) continue;
161
+
162
+ // the file has been successfully compiled
163
+ if (!out.errors?.length) {
164
+ clearCompileError(path);
165
+ console.log(theme.action("compiling: ") + theme.folder(dir.join(f.dir,'/')) + theme.filename(f.base) + " - " + theme.success("compiled"));
166
+ stats.bundled++;
167
+ stats.compiled++;
168
+ contents = out.js;
169
+ await Bun.write(cached, contents);
170
+ }
171
+ // there were errors during compilation
172
+ else {
173
+ const shouldPrint = shouldPrintCompileError(path, out.errors);
174
+ if (shouldPrint) console.log(theme.action("compiling: ") + theme.folder(dir.join(f.dir,'/')) + theme.filename(f.base) + " - " + theme.failure(" fail "));
175
+ stats.failed++;
176
+ if (shouldPrint) {
177
+ stats.reported++;
178
+ for (let i = 0; i < out.errors.length; i++) {
179
+ if(out.errors[i]) printCompileError(out.errors[i]);
180
+ }
155
181
  }
182
+ stats.errors++;
156
183
  }
157
- stats.errors++;
184
+
185
+ // and return the compiled source code as "js"
186
+ return {
187
+ contents,
188
+ loader: "js",
189
+ };
158
190
  }
159
-
160
- // and return the compiled source code as "js"
161
- return {
162
- contents,
163
- loader: "js",
164
- };
165
191
  });
166
192
  }
167
193
  };
package/serve.js CHANGED
@@ -409,6 +409,17 @@ function isMissingFileError(error) {
409
409
  return error?.code === 'ENOENT' || String(error?.message || error).includes('ENOENT: no such file or directory')
410
410
  }
411
411
 
412
+ function fileStamp(abs) {
413
+ try {
414
+ const stat = statSync(abs)
415
+ if (!stat.isFile()) return null
416
+ return `${stat.mtimeMs ?? stat.mtime?.getTime?.() ?? 0}:${stat.size ?? 0}`
417
+ } catch (error) {
418
+ if (isMissingFileError(error)) return null
419
+ throw error
420
+ }
421
+ }
422
+
412
423
  function missingCompileResult(filepath) {
413
424
  dropFileState(filepath)
414
425
  return { js: '', errors: [], slots: null, changeType: 'missing', missing: true }
@@ -416,49 +427,59 @@ function missingCompileResult(filepath) {
416
427
 
417
428
  async function compileFile(filepath) {
418
429
  const abs = path.resolve(filepath)
419
- if (!existsSync(abs)) return missingCompileResult(abs)
420
430
 
421
- const file = Bun.file(abs)
422
- let stat
423
- try {
424
- stat = await file.stat()
425
- } catch (error) {
426
- if (isMissingFileError(error)) return missingCompileResult(abs)
427
- throw error
428
- }
429
- const stamp = `${stat.mtimeMs ?? stat.mtime?.getTime?.() ?? 0}:${stat.size ?? 0}`
431
+ while (true) {
432
+ const stamp = fileStamp(abs)
433
+ if (!stamp) return missingCompileResult(abs)
430
434
 
431
- const cached = _compileCache.get(abs)
432
- if (cached && cached.stamp === stamp) return _normalizeResult(cached.result, { changeType: 'cached' })
435
+ const cached = _compileCache.get(abs)
436
+ if (cached && cached.stamp === stamp) {
437
+ if (fileStamp(abs) !== stamp) continue
438
+ return _normalizeResult(cached.result, { changeType: 'cached' })
439
+ }
433
440
 
434
- let code
435
- try {
436
- code = await file.text()
437
- } catch (error) {
438
- if (isMissingFileError(error)) return missingCompileResult(abs)
439
- throw error
440
- }
441
- const result = compiler.compile(code, {
442
- sourcePath: filepath,
443
- platform: 'browser',
444
- sourcemap: 'inline',
445
- })
441
+ const file = Bun.file(abs)
442
+ let code
443
+ try {
444
+ code = await file.text()
445
+ } catch (error) {
446
+ if (isMissingFileError(error)) return missingCompileResult(abs)
447
+ throw error
448
+ }
446
449
 
447
- const errors = result.errors || []
448
- if (!errors.length && result.js) {
449
- const { js, slotCount } = stabilizeSymbols(result.js, abs)
450
- result.js = rewriteBareImports(js)
451
- const prev = _prevSlots.get(abs)
452
- result.slots = (prev === undefined || prev === slotCount) ? 'stable' : 'shifted'
453
- _prevSlots.set(abs, slotCount)
454
- }
450
+ // A save can land while an older request is compiling. Never publish,
451
+ // cache, or report a result unless it still matches the current file.
452
+ if (fileStamp(abs) !== stamp) continue
455
453
 
456
- // Bake errors as an own property so caching/spreading preserves them.
457
- const baked = { js: result.js, errors, slots: result.slots }
458
- const changeType = _prevJs.get(abs) === baked.js ? 'none' : 'full'
459
- _prevJs.set(abs, baked.js)
460
- _compileCache.set(abs, { stamp, result: baked })
461
- return _normalizeResult(baked, { changeType })
454
+ let result
455
+ try {
456
+ result = compiler.compile(code, {
457
+ sourcePath: filepath,
458
+ platform: 'browser',
459
+ sourcemap: 'inline',
460
+ })
461
+ } catch (error) {
462
+ result = { js: '', errors: [error], slots: null }
463
+ }
464
+
465
+ if (fileStamp(abs) !== stamp) continue
466
+
467
+ const errors = result.errors || []
468
+ if (!errors.length && result.js) {
469
+ const { js, slotCount } = stabilizeSymbols(result.js, abs)
470
+ result.js = rewriteBareImports(js)
471
+ const prev = _prevSlots.get(abs)
472
+ result.slots = (prev === undefined || prev === slotCount) ? 'stable' : 'shifted'
473
+ _prevSlots.set(abs, slotCount)
474
+ }
475
+
476
+ // Bake errors as an own property so caching/spreading preserves them.
477
+ const baked = { js: result.js, errors, slots: result.slots }
478
+ const changeType = _prevJs.get(abs) === baked.js ? 'none' : 'full'
479
+ _prevJs.set(abs, baked.js)
480
+ _compileCache.set(abs, { stamp, result: baked })
481
+ return _normalizeResult(baked, { changeType })
482
+ }
462
483
  }
463
484
 
464
485
  // ─── HTML helpers ─────────────────────────────────────────────────────────────