Welcome to DevChat #19!

Here's what's we've got for today (yes, "today" is a day late -- it was a holiday weekend!):

  • ⚙ Seriously... SvelteKit!
  • ✂ VSCode Snippets
  • 📁 Our art pipeline: debouncing file watchers

This is an archive of a DevChat newsletter. To get the next one early, and in your inbox, sign up! To read past issues, head to the archives!

⚙ Seriously... SvelteKit!

I started a new project this weekend: putting together a sort of book-like thing about How to Automate Everything. I'd written a few pages of content in Markdown, and then wrote a glossary for the terms I was inventing, and then wanted to find a way to re-use the definitions in that glossary.

And so what started as a writing project suddenly became yet another programming project.

How could I set this project up to allow any amount of programmatic content I wanted, without getting too bogged down in the programming so that I could spend most of my time making the actual content?

I'd spent a prior weekend playing with SvelteKit, but hadn't had any obvious content to put in that experimental project. Now I did, and the website structure was going to be easy (basically a book), so I figured it was a good opportunity to see if SvelteKit really made webdev as fast and easy as people claim.

Spoiler alert: it did!

After running all the setup stuff (which was easier than most of these things are), the first thing I dug into was how I could write my content as Markdown but freely mix in programmatic stuff as needed. That way prose would be easy to write, but if I wanted to plop in some non-prose content I wouldn't have to do anything weird or complicated. For example, I wanted all my prose in Markdown but also wanted to be able to flag a word in that prose as a "term", so that hover-text could automatically find and then show its definition.

It turns out that this concept of putting fancy, functional, frontend-framework code right into a Markdown file is already in use for the major frameworks in one way or another, and SvelteKit was no different.

I found mdsvex, which allows putting Svelte code right into Markdown files, and subsequently a SvelteKit command to just... do it. It worked, and I didn't even have to do anything.

But it got even cooler than that. Since SvelteKit was now treating markdown files as Svelte files, I could plop any old plain markdown file into a folder, and BOOM it would appear as a fully rendered web page on the site!

Whether you've been stuck in React/Vue/Angular land and want a change or you've tried to get into those frameworks but found it to be a struggle, I can't recommend SvelteKit highly enough. Easily the best developer experience I've had with any front-end framework.

✂ VSCode Snippets

Last week I was sitting down to write a custom JavaScript error and became immediately annoyed. It's so boilerplate-y and there's one part of that boilerplate that I always have to look up. For the first time, I thought, "I guess this is what snippets are for, right? I should figure out how those work."

After having done that... I'm now even more annoyed. Because snippets are amazing, VSCode already has a ton of useful built-in ones, and I've been using VSCode for years without ever using them (despite definitely knowing that they existed).

WHY!? Why didn't I look into that before?

In short, I misunderstood them. I thought they were just straight-up boilerplate code. Something that was, in effect, copy-pasted out of a library of code.

I had thought... what's the point? Then I'd have to click around and edit the stuff that just got pasted in, and it would be faster to write it all from scratch!

It turns out that VSCode snippets have a spectacular feature that I somehow couldn't imagine them having: placeholders.

You can set up a snippet to have a bunch of placeholder variable names or other code, and then once you've plopped the snippet in you can hit tab to bounce from one to the next.

If two placeholders have the same name, they get filled out at the same time! The snippet can also dictate where your cursor should end up after you've filled the placeholders! It can even put in clipboard or selected text!

On top of now using the built-ins, I've started to build my own little library (which you can do per project, or account-wide (and synced)). Here's that dang custom error code as a snippet:

/** @file MyCustom.code-snippets */
{
  "Custom Error": {
    "scope": "typescript",
    // The prefix is the what gets used for
    // auto-complete, so when I type "error"
    // in VSCode (in a Typescript file) one
    // of the auto-complete options is this very
    // snippet.
    "prefix": "error-custom",
    "body": [
      "export class ${1:name} extends Error {",
      "\tconstructor(message:string) {",
      "\t\tsuper(message);",
      "\t\tthis.name = '$1';",
      "\t\tError.captureStackTrace(this, this.constructor);",
      "\t}",
      "}",
      "",
      "export function assert(claim:any, message = 'Assertion failed'): asserts claim {",
      "\tif(!claim){throw new $1(message);}",
      "}"
    ]
  }
}

That delightful little Typescript snippet drops in a custom error class, plus an assertion to go along with it that throws that very error.

Don't be like me. Use snippets. Presumably all the good editors have them.

📁 Our art pipeline: debouncing file watchers

Games require a lot of art. The harder it is to go from your graphics program to an art asset that is in the game, the less time the artist gets to spend making the art (they're now wrangling the process of getting it into the game), the longer it takes to discover errors, and the costlier it is to revisit an asset because the artist has already moved on.

We've built our own automated art pipelines since the Crashlands days. Of varying quality, definitely, but it turned out that even a fairly medium robot was far better than making people do that stuff!

The current incarnation of our art pipeline consists of:

  1. A carefully constructed workflow inside the graphics program (Clip Studio Paint) to allow for easy batch-exports;
  2. Spritely, a tool that does miscellaneous batch operations on the images inside a folder you point it to (such as cropping and bleeding). Spritely includes a file watcher so that it can auto-manage images when they are added or changed.
  3. Stitch, a tool that can directly manipulate GameMaker Studio projects to allow for things like batch-converting source images into in-game sprites.

A couple of weeks ago Sam (our artist) had finally gotten frustrated with some of the jank in the pipeline. In particular, the file watcher in Spritely would sometimes just stop working, and Stitch had to be manually run each time Sam wanted to see the art assets in-game.

And so I set off to fix that dang watcher functionality. It turned out to be a lot more frustrating than I thought, due to that whole "works on my machine" problem.

I'd write some code, write some tests, confirm that the tests passed, all to then have Sam find that it simply didn't work at all. Or that it didn't work under certain hard-to-understand scenarios.

Here are my hard-won lessons-learned from that battle:

  • Glob patterns, a popular way to indicate that you want a bunch of files while only writing one path (e.g. my-folder/*.png to indicate all PNG files inside my-folder), have multiple implementations and variants. A given implementation can be quite sensitive to naming conventions that are common on Windows. In particular, back-slashes (\), spaces, and parentheses. For example, if you have both business and personal Dropbox accounts you'll find that Dropbox's folder name looks something like C:\Users\adam\Dropbox (Bscotch). It's glob-pattern nightmare.
  • Operating system file-change events can be quite unreliable for files that are already being actively watched and managed, in particular if they exist on network storage (or something that sorta behaves like that, like Dropbox).

You may notice that Dropbox was the source of all of my ills...

Anyway, the end result was that if I used file-watcher methods that didn't use globs and that used polling (periodically checking the files) instead of operating system events, things became dependable!

Probably the two most popular Node-ecosystem libraries out there for triggering actions upon changes to files are chokidar and nodemon. Both of them allow you to separately specify directories and extensions so that you don't have to use glob patterns. They also both allow you to use polling instead of file system events!

Something they don't do, however, is let you debounce file changes before running the triggered action. In short, debouncing is when you wait for changes to stop occurring before triggering an action.

For our art pipeline, we have two considerations that make debouncing useful: art assets are batch-exported, and they are delivered over a network. Batch-exporting is fast for us, but we want to guarantee that we don't trigger automations when a batch is only partially exported. Similarly, if we're auto-importing art into the game project we don't want to trigger that while some files still haven't been delivered over the network (via Dropbox, in our case).

To deal with all that and to make it easy to use more stable default options, I used chokidar to build my own debounced watcher. Since I needed that for both Stitch and Spritely, I made it its own importable module so that I can add fixes and features in one central place while also allowing us to now have this common automation problem generically solved.

I've made Debounce Watch open-source, so you can give it a whirl! If you end up using it in an automation of your own, I'd love to hear about it.

Until next time

That wraps DevChat #19!

Follow me on Twitter and GitHub for texty nerd stuff, and subscribe to the studio Twitch and YouTube for dev content in video form.

I've been having a great time hearing from readers. If you haven't said hello, please do!

Share with others by forwarding, or link directly to the archived post.

Have a great week!

❤ Adam