Today I added a nav bar to the svelte-maplibre demo site, so you don't have to hit the back button all the time to go between examples.

Otherwise, I've mostly been heads down on the book. It's a bit over 7,000 words right now and coming along well. Still lots more to go!



As procrastination for writing my book, I wrote a Neovim plugin to generate word counts per section in a document.

Neovim lets you use an extmark on a position to add virtual text. So the idea was to set an extmark on each header line with the word count, but I didn't quite figure out how the extmark actually moves around with the text. It feels somewhat unintuitive, but there are probably some subtleties that I haven't figured out yet.

Instead of dealing with that, instead I set up my plugin as a "decoration provider." This lets you just set an ephemeral extmark which only lasts for the draw cycle, and then next time a line is redrawn you just create it again (or not). This ended up being a bit more code, but much simpler since I now only have to track the word count and where the headers are, and not worry about if an extmark is still on the right line or not.

Overall Lua feels nice, if rather barebones. I'm definitely missing the functional programming idioms afforded by most modern languages. But the Neovim APIs are very easy to use.

If you're interested in using this, you can get it on Github.



Today I encountered an issue using Vite with the Rush monorepo software. Rush keeps its lockfile in an unusual place, and so Vite could not automatically check the lockfile to know when to clear its cache. But it turns out to be pretty easy to code your own logic here.

// vite.config.js
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';

const dirname = path.dirname(fileURLToPath(import.meta.url));

function lockfileNeedsRebuild() {
  const lockFilePath = path.resolve(
  const lockFileDatePath = path.resolve(dirname, '.last-lockfile-date');

  let lockFileSavedDate = '';
  try {
    lockFileSavedDate = fs.readFileSync(lockFileDatePath).toString();
  } catch (e) {
    // It's fine if there is no existing file.

  const lockFileDate = fs.statSync(lockFilePath).mtime.valueOf().toString();

  if (lockFileSavedDate.trim() !== lockFileDate) {
    fs.writeFileSync(lockFileDatePath, lockFileDate);
    return true;

  return false;

/** @type {import('vite').UserConfig} */
const config = {
  optimizeDeps: {
    force: lockfileNeedsRebuild(),
  // ... and other config
export default config;