VS Code: Hope, Hype, and Hijacking

Feb 21, 2018
Tags: Tooling Text Editors

I’ve always been a bit promiscuous with my text editors. The whole chronology probably looks a little something like this:

Dreamweaver => Coda => BBEdit => TextMate => Espresso => Sublime Text => Espresso (again) => Sublime Text 2 => Atom => TextMate (again) => BBEdit (again) => Sublime Text 2 (again) => Espresso (yet again) => Sublime text 3 => Atom (again) => Espresso (yet again, again) => Sublime Text 3 (again) => VS Code.

As you can see, I’m not shy about “picking up stakes” and moving from one to another—and back again. I’d like to think that I’m always hopeful for better features, performance, extensibility, etc.—that I’m always looking for ways to procrastonate, and avoid acutally doing any work with the editor du jour is probably closer to the mark. Nevertheless, my promisquity has given me a pretty good sense for what does and doesn’t make for a good editor.

Espresso App

From a purely UI/UX perspective I still think Espresso is one of the most elegant editors I’ve ever used—unfortunately its workflow model is a bit anachronistic. I really appreciate the thought that went into some of the features like Dynamo—their proprietary static site generator—but ultimately, it’s playing in the proverbial walled garden. And, I can’t remember the last time I used FTP to send files to the server.

Like most, I’ve welcomed our new decoupled, extension based, Git enabled, Electron App overlords—and it’s probably for the best. Electron apps will never be as blazingly fast as a well written native app, but VS Code does “OK”—better than Atom, I think. The Git integration is nice, though I do most of that stuff from the command line anyway. What really gets me are the extensions and the configuration. Granted, it’s a pain in the ass to setup—Sublime is too—and though VS Code makes it easier than most, it still takes a real investment to get everything configured as you’d like. But the good news is that everything, and I mean everything is configurable, and the extensions are killer.

Killer Extensions

All the usual suspects are there: git gutter, file icons, linting, Emmet, syntax highlighting for every language you can imagine (these are much easier to fine-tune to your liking in VS Code). The IntelliSense—Microsoft’s version of code completion/hinting—extensions are what really seal the deal though.

VS Code

Sure, you get hinted to the right method in Ruby, or ES6—but better than that, you can autocomplete on filenames, CSS class names, even Sass variable, mixin, and function names—I never remember that stuff. There’s no lack of hype around VS Code right now—but on the whole, I’d say it’s living up to it.

Hijacking the Community

Honestly, I feel kind of bad for the Sublime Text folks. Atom, and now VS Code are in many ways a direct hijack of the package/plugin/extension approach—pioneered in TextMate and brought to scale in Sublime—to say nothing of the command pallete, and some other directly hijacked bits of UI. It’s telling that the extension which ports Sublime keyboard shortcuts over to VS Code already has 1/2 a million downloads. But just like any other suffeciently advanced technology these days—it’s the ecosystem that really matters. When TextMate was TextMate, there was a vibrant community around it—creating bundles, themes, and addons—this made the editor much more than it could have been on its own. When development of newer versions stalled, the community moved on to Sublime—and did it all over again. Today VS Code has the community mindshare, but what came before is never really lost—most of the syntax files I’ve seen for VS Code are still based on the original TextMate Grammar scheme from 2004.


Busting CSS Cache with Jekyll

Feb 16, 2018
Tags: Jekyll Ruby

If you’re setting long expires headers on your static assets—as God demands—then at some point, you will want to bust that browser cache. If you’re also using Jekyll, there’s a plugin for that. Here’s the thing though: not everyone needs a full-fledged, sprockets based, asset pipeline for their project. In the world of Jekyll—I’d actually say that the majority of folks don’t need that particular sledgehammer. It adds quite a lot of complexity to your build and, to be fair, the documentation is a bit inscrutable.

There’s nothing wrong with simple workarounds like this:

<link rel="stylesheet" href="/css/main.css?{{site.time | date: '%s'}}">

Which uses the site.time variable formatted as seconds since the epoch (1970). When outputed, the ?1518881511 query paramater gets appended to the end of the filename—browsers see this as a new asset and download it. This is great, and will bust browser cache of the file everytime the site is built.

A Middle Path With MD5 Hashing

Ideally though, you’d only invalidate the browser cache when your asset has changed somehow. You could manually increment when changes occour using semantic versioning e.g. main.css?v=1.2—but my Mtv addled brain would never remember to do that. Somewhere between a Rails asset pipeline and manually incrementing—lies a middle path. A custom plugin that reads in your _sass files, geneartes an MD5 Digest—and then lets you append that digest to your css filname with a filter, e.g.:

<link rel="stylesheet" href="{{ site.baseurl }}/assets/css/{{ 'site.css' | bust_css_cache }}">

Which will give us something like this in the markup: <link rel="stylesheet" href="/assets/css/site.css?c86a8588967580cc0631f5115d9d6b18">. This way, the hash at the end only changes when the content of your Sass files change. A browser could hold onto your stylesheet for a year—but as soon as you make a change to the _typesetting.scss partial, it will ask for the new version.

It’s not as complicated as it sounds. You can add a custom plugin by including an .rb file in the _plugins directory at the root of your project. There are some caveats though, so first have a look at the documentation for running with custom plugins. Plugins can get pretty complicated, but for our purposes we just need to register a simple filter with Liquid.

module Jekyll
  module CacheBust
    # put the stuff that does stuff here
  end
end

Liquid::Template.register_filter(Jekyll::CacheBust)

Basically, Liquid Filters take an input and return an output. In our case, our input will be the name of our CSS file, and the output will be the name of our CSS file with an MD5 Digest of our Sass files appended to it. I dropped the full plugin into this gist if you want to give it a look. The key bits are a CacheDigester class that will take a file or directory of files and generate a digest, and the bust_css_cache method which you call as a filter in Liquid.

We’re passing our _sass directory to the CacheDigester object because we write in Sass and our CSS file will only be generated at the end when the site is built. I keep my Sass files in assets/_sass so if you plan to use this plugin you may have to change that bit in the bust_css_cache method.

Simplicity, and Context

What I love about Jekyll, is what I love about Liquid, is what I love about Ruby, is what I love about Vue. They all do complex things, but offer simple affordances to the user. Extending the functionality of Liquid and Jekyll is relatively straightforward, as you can see above. This means you can build the right solution for your particular context—without being forced to use a sledgehammer to swat a fly.


The Politics of Entertainment

Jan 9, 2018
Tags: Politics Twouts

The more that politics becomes entertainment—the more entertainers will want to become politicians. This is bad for a whole host of reasons—not the least of which is, you can’t change the fucking channel.


A Vue / Jekyll Proof of Concept

Jul 11, 2017
Tags: Web Development Vue Jekyll

I’ve been continuing to play around with an isomorphic(ish) approach to building sites with Jekyll and Vue. In my last post, I looked at routing, navigation, and the history API. This time I look at sharing data, templating, and bringing it all together. Believing that the best way to “learn a thing”, is to just “build a thing”—I wen’t ahead and built out a little example application that brings all this stuff together in a single working site. The Vue code is super rough (still just learning)—I should be using props to pass down ship data from the home component to the ship component, I should add in transitions so moving through the routes looks smoother, etc.—but, “it works”™.

The Key Bits: works with/without JS, static pages and real URLS for SEO and usability, it’s fast un-optimized, and… Culture ships are cool.

Culture Namer

Why use Jekyll at all?

Before we get in too deep—It’s worth pointing out that you could just use Vue to render the static pages. Or you could run Vue on the server and have it render pages on the fly—something like Nuxt comes in handy for these types of things. If your primary concern is universal rendering—there’s no real reason to even reach for Jekyll. However, Jekyll is great. If you’re building a decoupled CMS, there’s lots of reasons why you might want to use Jekyll for the static bit—not the least of which, is that services like Siteleaf mean that you can easily add in a slick web-based interface for writers, editors, and content managers.

Sharing the data and getting all isomorphic

The real trick here is using one set of data to render both sets of views—one for the server and one for the client. So step one is building some JSON endpoints where Vue can consume the site, ship, ship-type, and book data used by Jekyll. Since we’re bundling our Vue code from the same repo as our Jekyll code we could just include the data directly via Webpack—but this may not always be the case (and, depending on the view, we may not always need all the data), so we’ll use Axios to pull on the endpoints as needed.

We setup _ships as a collection in Jekyll so each ship has its own Markdown file and frontmatter, indicating the ship type, the book it first appeared in, its name, and so on. So, we just step through all that, and generate the JSON file for Vue with Liquid. We’ll do the same thing with our _data files, one for the app (basic site level data like, site title, description, etc.), one for books, and one for ship-types.

---
title: shipData
---
[{% for ship in site.ships %}{
  "name": "{{ ship.title }}",
  "url": "{{ ship.url }}",
  "content": "{{ ship.content | strip_html | strip_newlines | remove:"Note: " | remove:'"'}}",
  "typeAbrev": "{{ ship.type-abrev }}",
  "typeLong": "{{ ship.type-long }}",
  "book": "{{ ship.book }}"
  }{% unless forloop.last %},{% endunless %}{% endfor %}]

Using Vue-Axios

Now that we’ve got our data endpoints, we can bring them into our Vue app. We use the created() hook (which triggers after the instance has been created, and data observation has already been setup) to update the existing siteData and shipData objects.

data () {
    return {
      siteData: [],
      shipData: [],
    }
  },
  created(){
    axios.get('/data/data.json').then(response => this.siteData = response.data);
    axios.get('/data/ships.json').then(response => this.shipData = response.data);
  }

Then we just dip into those data objects from each component as we need them. Below are the ship page views for both Jekyll and Vue. In Jekyll, each document in the collection gets its own URL. In Vue we’ll have to grab the ship name from the URL params and then pull the related data from shipData. In Jekyll we’ll use the where_exp filter to connect the book.yml and ship-type.yml to the current ship. In Vue we’ll have to use the v-for and v-if directives. Those types of differences aside, building templates for both Vue and Jekyll is remarkably simillar.

The Vue View

<div id="content" v-for="ship in shipData" v-if="ship.url.includes($route.params.name)">
      <h3>{{ ship.name }}</h3>
      <p>This ship first appeard in the book <a v-for="book in bookData" v-if="ship.book === book.name" v-bind:href="book.url">{{ ship.book }}</a>—the ship is a <strong>{{ ship.typeLong }} ({{ ship.typeAbrev }})</strong>. <span v-for="type in shipTypeData" v-if="ship.typeAbrev === type.type">{{ type.description }}</span></p>
      <p v-if="ship.content.length > 0"><span class="note">Note:</span> {{ ship.content }}</p>
    </div>
    <div class="button"><a v-on:click.prevent="$router.push(shipData[Math.floor(Math.random() * shipData.length)].url)" class="btn btn--lg btn--green">Pick a Random Ship</a></div>

The Jekyll View

<div id="content">
    <h3>{{ page.name }}</h3>
    <p>This ship first appeared in the book <a href="{{ book[0].booklink }}">{{ page.book }}</a>—the ship is a <strong>{{ page.type-long }} ({{ page.type-abrev }})</strong>. {{ type[0].description }}</p>
    {{ content }}
    <div class="button"><a href="{{ ship.url }}" class="btn btn--lg btn--green">Pick a Random Ship</a></div> 
  </div>

That they look so simillar, is a testament to just how intuitive it is to build templates in Vue—because Jekyll is wicked easy. As I said, my Vue code is still a bit rough—I assume there must be a better way to compare properties between shipData and BookData than stepping through each item, I just don’t know what it is right now—I guess I should be abstracting that out into a <template>?

Regardless, the point here is that there’s one store of data being used to populate two different views—one of those is generated at build time and shipped from the server, the other is generated on the client side (after the server ships down the necessary JSON).

Bringing it all together

This has been a fun little side project to mess with in my free time—and I’ll probably use this little boilerplate template for any Vue/Jekyll projects I build in the future, though none spring to mind. On the whole, I think this type of isomorphic approach has a lot of promise—though it is kind of a pain to have to biuld out all the views twice. It would be easier just to use Nuxt or the like—but if I’m building anything that non-technical users will need to interact with, I would prefer the ease and security of a Jekyll + Vue approach. One thing’s for sure, Vue is really great—and I’m looking forward to spending some more time getting to know it.


Vue Routing with Jekyll

Apr 5, 2017
Tags: Web Development Vue Jekyll

I’ve been continuing to play around with an isomorphic-ish approach to building sites with Jekyll and Vue—this week is routing. After we’ve pulled down the first page from the server, and begun relying on Vue, we still want to be able to move through the site while updating the URL, preserving the window’s history, and keeping things (components, sections, pages, etc.) organized in a way that makes sense—without resetting the whole DOM, obviously.

Luckily, the built in router for V2 let’s us do all this stuff. Of course, we don’t have to use the official router, we could drop in our own, or use one off the shelf—but theirs seems pretty great honestly.

Dynamic Matching & the History API

Once we’ve imported the module import VueRouter from 'vue-router' and told Vue to use it Vue.use(VueRouter), defining the routes is straightforward:

const routes = [
  { path: '/about', component: Page },
  { path: '/ship/:name', component: Ship }
]

We can define the routes manually, or use dynamic matching as you see above. We get back the matched value at $route.params.name so we can use the variable to do things like conditionally loading a block. This is how we’ll navigate around the site, after the first page load—instead of asking the server for a new page, we’ll either swap in a new component or grab the paramater from the route to update an existing one.

If we set mode: 'history' on the router object, we can use the history.pushState() method introduced with HTML5 to preserve our browsing history and reflect the changes we’re making to the DOM, in the URL.

const router = new VueRouter({
  mode: 'history',
  routes 
})

So, looking at a ship entry for Sanctioned Parts List the URL will read /ship/santioned-parts-list. Because we’re doing this in an isomorphic-ish way, we don’t have to worry about direct requests to that URL getting 404’d—a static version of the page already exists on the server thanks to Jekyll.

Programatic Navigation

We can use <router-link to='/about'>About</router-link> to explicitly link to a route—or we can use the router’s instance methods to navigate programatically router.push('/ship/little-gravitas'). If, for instance we wanted to generate a random link to a page, we could abstract that logic out into a separate method and then call it with an v-on:click directive <button @click="getShip">Generate Ship</button>.

Moving on from Here

Next up, is to figure out how to build templates for both Jekyll and Vue that leverage as much of the same code as possible. As a single source of data drives both views, the templates should also share as much as possible.

The Distress Signal is the personal blog of Bryan Schuetz. Bryan has been complaining on the Internet since the 90s. If you'd like to get in touch with Bryan, you can find him on twitter.