« get me outta code hell

real pragma, and some eslint fixes - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2022-06-26 18:02:27 -0300
committer(quasar) nebula <qznebula@protonmail.com>2022-06-26 18:02:27 -0300
commitc75b029160248b6935e5c0f5156cc7a870311e82 (patch)
tree693c5cca195e50b048b0086e768aa06a7c1986ee
parentf65e712fe8b8b1a196da2db286ebc6a5c9bf7433 (diff)
real pragma, and some eslint fixes
-rw-r--r--.prettierrc.json2
-rw-r--r--package.json3
-rw-r--r--src/data/cacheable-object.js33
-rw-r--r--src/data/patches.js45
-rw-r--r--src/data/serialize.js6
-rw-r--r--src/data/things.js691
-rw-r--r--src/data/validators.js42
-rw-r--r--src/data/yaml.js595
-rw-r--r--src/file-size-preloader.js8
-rw-r--r--src/gen-thumbs.js113
-rw-r--r--src/listing-spec.js500
-rw-r--r--src/misc-templates.js335
-rw-r--r--src/page/album-commentary.js87
-rw-r--r--src/page/album.js223
-rw-r--r--src/page/artist-alias.js16
-rw-r--r--src/page/artist.js258
-rw-r--r--src/page/flash.js160
-rw-r--r--src/page/group.js128
-rw-r--r--src/page/homepage.js70
-rw-r--r--src/page/index.js28
-rw-r--r--src/page/listing.js104
-rw-r--r--src/page/news.js60
-rw-r--r--src/page/static.js18
-rw-r--r--src/page/tag.js46
-rw-r--r--src/page/track.js157
-rw-r--r--src/repl.js42
-rw-r--r--src/static/client.js208
-rw-r--r--src/static/lazy-loading.js18
-rwxr-xr-xsrc/upd8.js992
-rw-r--r--src/url-spec.js90
-rw-r--r--src/util/cli.js70
-rw-r--r--src/util/colors.js4
-rw-r--r--src/util/find.js62
-rw-r--r--src/util/html.js60
-rw-r--r--src/util/io.js10
-rw-r--r--src/util/link.js94
-rw-r--r--src/util/magic-constants.js8
-rw-r--r--src/util/node-utils.js10
-rw-r--r--src/util/replacer.js84
-rw-r--r--src/util/serialize.js20
-rw-r--r--src/util/sugar.js110
-rw-r--r--src/util/urls.js36
-rw-r--r--src/util/wiki-data.js84
43 files changed, 2848 insertions, 2882 deletions
diff --git a/.prettierrc.json b/.prettierrc.json
index 7d30a07..bba89fe 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,6 +1,6 @@
 {
   "arrowParens": "always",
-  "bracketSpacing": true,
+  "bracketSpacing": false,
   "printWidth": 80,
   "proseWrap": "never",
   "quoteProps": "as-needed",
diff --git a/package.json b/package.json
index 938ec6a..1ca350e 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,8 @@
         "hsmusic": "./src/upd8.js"
     },
     "scripts": {
-        "test": "tape test/**/*.js"
+        "test": "tape test/**/*.js",
+        "dev": "prettier --loglevel warn --write . && eslint src && node src/upd8.js"
     },
     "dependencies": {
         "fix-whitespace": "^1.0.4",
diff --git a/src/data/cacheable-object.js b/src/data/cacheable-object.js
index 4728193..fe1817f 100644
--- a/src/data/cacheable-object.js
+++ b/src/data/cacheable-object.js
@@ -1,5 +1,7 @@
-// @format
-//
+/**
+ * @format
+ */
+
 // Generally extendable class for caching properties and handling dependencies,
 // with a few key properties:
 //
@@ -76,16 +78,16 @@
 //      function, which provides a mapping of exposed property names to whether
 //      or not their dependencies are yet met.
 
-import { color, ENABLE_COLOR } from "../util/cli.js";
+import {color, ENABLE_COLOR} from '../util/cli.js';
 
-import { inspect as nodeInspect } from "util";
+import {inspect as nodeInspect} from 'util';
 
 function inspect(value) {
-  return nodeInspect(value, { colors: ENABLE_COLOR });
+  return nodeInspect(value, {colors: ENABLE_COLOR});
 }
 
 export default class CacheableObject {
-  static instance = Symbol("CacheableObject `this` instance");
+  static instance = Symbol('CacheableObject `this` instance');
 
   #propertyUpdateValues = Object.create(null);
   #propertyUpdateCacheInvalidators = Object.create(null);
@@ -109,7 +111,7 @@ export default class CacheableObject {
       return new Proxy(this, {
         get: (obj, key) => {
           if (!Object.hasOwn(obj, key)) {
-            if (key !== "constructor") {
+            if (key !== 'constructor') {
               CacheableObject._invalidAccesses.add(
                 `(${obj.constructor.name}).${key}`
               );
@@ -125,7 +127,7 @@ export default class CacheableObject {
     for (const [property, descriptor] of Object.entries(
       this.constructor.propertyDescriptors
     )) {
-      const { flags, update } = descriptor;
+      const {flags, update} = descriptor;
 
       if (!flags.update) {
         continue;
@@ -149,7 +151,7 @@ export default class CacheableObject {
     for (const [property, descriptor] of Object.entries(
       this.constructor.propertyDescriptors
     )) {
-      const { flags } = descriptor;
+      const {flags} = descriptor;
 
       const definition = {
         configurable: false,
@@ -173,9 +175,8 @@ export default class CacheableObject {
   }
 
   #getUpdateObjectDefinitionSetterFunction(property) {
-    const { update } = this.#getPropertyDescriptor(property);
+    const {update} = this.#getPropertyDescriptor(property);
     const validate = update?.validate;
-    const allowNull = update?.allowNull;
 
     return (newValue) => {
       const oldValue = this.#propertyUpdateValues[property];
@@ -209,10 +210,6 @@ export default class CacheableObject {
     };
   }
 
-  #getUpdatePropertyValidateFunction(property) {
-    const descriptor = this.#getPropertyDescriptor(property);
-  }
-
   #getPropertyDescriptor(property) {
     return this.constructor.propertyDescriptors[property];
   }
@@ -225,7 +222,7 @@ export default class CacheableObject {
   }
 
   #getExposeObjectDefinitionGetterFunction(property) {
-    const { flags } = this.#getPropertyDescriptor(property);
+    const {flags} = this.#getPropertyDescriptor(property);
     const compute = this.#getExposeComputeFunction(property);
 
     if (compute) {
@@ -248,7 +245,7 @@ export default class CacheableObject {
   }
 
   #getExposeComputeFunction(property) {
-    const { flags, expose } = this.#getPropertyDescriptor(property);
+    const {flags, expose} = this.#getPropertyDescriptor(property);
 
     const compute = expose?.compute;
     const transform = expose?.transform;
@@ -286,7 +283,7 @@ export default class CacheableObject {
   }
 
   #getExposeCheckCacheValidFunction(property) {
-    const { flags, expose } = this.#getPropertyDescriptor(property);
+    const {flags, expose} = this.#getPropertyDescriptor(property);
 
     let valid = false;
 
diff --git a/src/data/patches.js b/src/data/patches.js
index 937fb09..dc757fa 100644
--- a/src/data/patches.js
+++ b/src/data/patches.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // --> Patch
 
 export class Patch {
@@ -76,7 +76,7 @@ export class Patch {
               inputs[inputName] = [Patch.INPUT_AVAILABLE, output[1]];
               break;
           }
-          throw new Error("Unreachable");
+          throw new Error('Unreachable');
         }
 
         case Patch.INPUT_MANAGED_CONNECTION: {
@@ -169,7 +169,7 @@ export class PatchManager extends Patch {
       return false;
     }
 
-    for (const inputNames of patch.inputNames) {
+    for (const inputName of patch.inputNames) {
       const input = patch.inputs[inputName];
       if (input[0] === Patch.INPUT_MANAGED_CONNECTION) {
         this.dropManagedInput(input[1]);
@@ -202,7 +202,7 @@ export class PatchManager extends Patch {
   }
 
   dropManagedInput(identifier) {
-    return delete this.managedInputs[key];
+    return delete this.managedInputs[identifier];
   }
 
   getManagedInput(identifier) {
@@ -213,7 +213,7 @@ export class PatchManager extends Patch {
     return this.computeManagedInput(patch, outputName, memory);
   }
 
-  computeManagedInput(patch, outputName, memory) {
+  computeManagedInput(patch, outputName) {
     // Override this function in subclasses to alter behavior of the "wire"
     // used for connecting patches.
 
@@ -255,7 +255,7 @@ export class PatchManager extends Patch {
 }
 
 class PatchManagerExternalInputPatch extends Patch {
-  constructor({ manager, ...rest }) {
+  constructor({manager, ...rest}) {
     super({
       manager,
       inputNames: manager.inputNames,
@@ -284,7 +284,7 @@ class PatchManagerExternalInputPatch extends Patch {
 }
 
 class PatchManagerExternalOutputPatch extends Patch {
-  constructor({ manager, ...rest }) {
+  constructor({manager, ...rest}) {
     super({
       manager,
       inputNames: manager.outputNames,
@@ -312,7 +312,6 @@ class PatchManagerExternalOutputPatch extends Patch {
 
 const caches = Symbol();
 const common = Symbol();
-const hsmusic = Symbol();
 
 Patch[caches] = {
   WireCachedPatchManager: class extends PatchManager {
@@ -327,8 +326,8 @@ Patch[caches] = {
     computeManagedInput(patch, outputName, memory) {
       let cache = true;
 
-      const { previousInputs } = memory;
-      const { inputs } = patch;
+      const {previousInputs} = memory;
+      const {inputs} = patch;
       if (memory.previousInputs) {
         for (const inputName of patch.inputNames) {
           // TODO: This doesn't account for connections whose values
@@ -348,7 +347,7 @@ Patch[caches] = {
 
       const outputs = patch.computeOutputs();
       memory.previousOutputs = outputs;
-      memory.previousInputs = { ...inputs };
+      memory.previousInputs = {...inputs};
       return outputs[outputName];
     }
   },
@@ -356,8 +355,8 @@ Patch[caches] = {
 
 Patch[common] = {
   Stringify: class extends Patch {
-    static inputNames = ["value"];
-    static outputNames = ["value"];
+    static inputNames = ['value'];
+    static outputNames = ['value'];
 
     compute(inputs, outputs) {
       if (inputs.value[0] === Patch.INPUT_AVAILABLE) {
@@ -369,8 +368,8 @@ Patch[common] = {
   },
 
   Echo: class extends Patch {
-    static inputNames = ["value"];
-    static outputNames = ["value"];
+    static inputNames = ['value'];
+    static outputNames = ['value'];
 
     compute(inputs, outputs) {
       if (inputs.value[0] === Patch.INPUT_AVAILABLE) {
@@ -383,16 +382,16 @@ Patch[common] = {
 };
 
 const PM = new Patch[caches].WireCachedPatchManager({
-  inputNames: ["externalInput"],
-  outputNames: ["externalOutput"],
+  inputNames: ['externalInput'],
+  outputNames: ['externalOutput'],
 });
 
-const P1 = new Patch[common].Stringify({ manager: PM });
-const P2 = new Patch[common].Echo({ manager: PM });
+const P1 = new Patch[common].Stringify({manager: PM});
+const P2 = new Patch[common].Echo({manager: PM});
 
-PM.addExternalInput(P1, "value", "externalInput");
-PM.addManagedInput(P2, "value", P1, "value");
-PM.setExternalOutput("externalOutput", P2, "value");
+PM.addExternalInput(P1, 'value', 'externalInput');
+PM.addManagedInput(P2, 'value', P1, 'value');
+PM.setExternalOutput('externalOutput', P2, 'value');
 
 PM.inputs.externalInput = [Patch.INPUT_CONSTANT, 123];
 console.log(PM.computeOutputs());
diff --git a/src/data/serialize.js b/src/data/serialize.js
index 13b20e1..a4206fd 100644
--- a/src/data/serialize.js
+++ b/src/data/serialize.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // serialize-util.js: simple interface and utility functions for converting
 // Things into a directly serializeable format
 
@@ -18,7 +18,7 @@ export function toRefs(things) {
 }
 
 export function toContribRefs(contribs) {
-  return contribs?.map(({ who, what }) => ({ who: toRef(who), what }));
+  return contribs?.map(({who, what}) => ({who: toRef(who), what}));
 }
 
 // Interface
diff --git a/src/data/things.js b/src/data/things.js
index c86add3..e2bbfd7 100644
--- a/src/data/things.js
+++ b/src/data/things.js
@@ -1,9 +1,9 @@
-// @format
-//
+/** @format */
+
 // things.js: class definitions for various object types used across the wiki,
 // most of which correspond to an output page, such as Track, Album, Artist
 
-import CacheableObject from "./cacheable-object.js";
+import CacheableObject from './cacheable-object.js';
 
 import {
   isAdditionalFileList,
@@ -16,32 +16,30 @@ import {
   isDimensions,
   isDirectory,
   isDuration,
-  isInstance,
   isFileExtension,
   isLanguageCode,
   isName,
   isNumber,
   isURL,
   isString,
-  isWholeNumber,
   oneOf,
   validateArrayItems,
   validateInstanceOf,
   validateReference,
   validateReferenceList,
-} from "./validators.js";
+} from './validators.js';
 
-import * as S from "./serialize.js";
+import * as S from './serialize.js';
 
 import {
   getKebabCase,
   sortAlbumsTracksChronologically,
-} from "../util/wiki-data.js";
+} from '../util/wiki-data.js';
 
-import find from "../util/find.js";
+import find from '../util/find.js';
 
-import { inspect } from "util";
-import { color } from "../util/cli.js";
+import {inspect} from 'util';
+import {color} from '../util/cli.js';
 
 // Stub classes (and their exports) at the top of the file - these are
 // referenced later when we actually define static class fields. We deliberately
@@ -96,16 +94,16 @@ export class Language extends CacheableObject {}
 // Before initializing property descriptors, set additional independent
 // constants on the classes (which are referenced later).
 
-Thing.referenceType = Symbol("Thing.referenceType");
+Thing.referenceType = Symbol('Thing.referenceType');
 
-Album[Thing.referenceType] = "album";
-Track[Thing.referenceType] = "track";
-Artist[Thing.referenceType] = "artist";
-Group[Thing.referenceType] = "group";
-ArtTag[Thing.referenceType] = "tag";
-NewsEntry[Thing.referenceType] = "news-entry";
-StaticPage[Thing.referenceType] = "static";
-Flash[Thing.referenceType] = "flash";
+Album[Thing.referenceType] = 'album';
+Track[Thing.referenceType] = 'track';
+Artist[Thing.referenceType] = 'artist';
+Group[Thing.referenceType] = 'group';
+ArtTag[Thing.referenceType] = 'tag';
+NewsEntry[Thing.referenceType] = 'news-entry';
+StaticPage[Thing.referenceType] = 'static';
+Flash[Thing.referenceType] = 'flash';
 
 // -> Thing: base class for wiki data types, providing wiki-specific utility
 // functions on top of essential CacheableObject behavior.
@@ -115,21 +113,21 @@ Flash[Thing.referenceType] = "flash";
 // functions, so check each for how its own arguments behave!
 Thing.common = {
   name: (defaultName) => ({
-    flags: { update: true, expose: true },
-    update: { validate: isName, default: defaultName },
+    flags: {update: true, expose: true},
+    update: {validate: isName, default: defaultName},
   }),
 
   color: () => ({
-    flags: { update: true, expose: true },
-    update: { validate: isColor },
+    flags: {update: true, expose: true},
+    update: {validate: isColor},
   }),
 
   directory: () => ({
-    flags: { update: true, expose: true },
-    update: { validate: isDirectory },
+    flags: {update: true, expose: true},
+    update: {validate: isDirectory},
     expose: {
-      dependencies: ["name"],
-      transform(directory, { name }) {
+      dependencies: ['name'],
+      transform(directory, {name}) {
         if (directory === null && name === null) return null;
         else if (directory === null) return getKebabCase(name);
         else return directory;
@@ -138,27 +136,27 @@ Thing.common = {
   }),
 
   urls: () => ({
-    flags: { update: true, expose: true },
-    update: { validate: validateArrayItems(isURL) },
+    flags: {update: true, expose: true},
+    update: {validate: validateArrayItems(isURL)},
   }),
 
   // A file extension! Or the default, if provided when calling this.
   fileExtension: (defaultFileExtension = null) => ({
-    flags: { update: true, expose: true },
-    update: { validate: isFileExtension },
-    expose: { transform: (value) => value ?? defaultFileExtension },
+    flags: {update: true, expose: true},
+    update: {validate: isFileExtension},
+    expose: {transform: (value) => value ?? defaultFileExtension},
   }),
 
   // Straightforward flag descriptor for a variety of property purposes.
   // Provide a default value, true or false!
   flag: (defaultValue = false) => {
-    if (typeof defaultValue !== "boolean") {
+    if (typeof defaultValue !== 'boolean') {
       throw new TypeError(`Always set explicit defaults for flags!`);
     }
 
     return {
-      flags: { update: true, expose: true },
-      update: { validate: isBoolean, default: defaultValue },
+      flags: {update: true, expose: true},
+      update: {validate: isBoolean, default: defaultValue},
     };
   },
 
@@ -166,23 +164,23 @@ Thing.common = {
   // This isn't dynamic though - it won't inherit from a date stored on
   // another object, for example.
   simpleDate: () => ({
-    flags: { update: true, expose: true },
-    update: { validate: isDate },
+    flags: {update: true, expose: true},
+    update: {validate: isDate},
   }),
 
   // General string type. This should probably generally be avoided in favor
   // of more specific validation, but using it makes it easy to find where we
   // might want to improve later, and it's a useful shorthand meanwhile.
   simpleString: () => ({
-    flags: { update: true, expose: true },
-    update: { validate: isString },
+    flags: {update: true, expose: true},
+    update: {validate: isString},
   }),
 
   // External function. These should only be used as dependencies for other
   // properties, so they're left unexposed.
   externalFunction: () => ({
-    flags: { update: true },
-    update: { validate: (t) => typeof t === "function" },
+    flags: {update: true},
+    update: {validate: (t) => typeof t === 'function'},
   }),
 
   // Super simple "contributions by reference" list, used for a variety of
@@ -197,14 +195,14 @@ Thing.common = {
   //
   // ...processed from YAML, spreadsheet, or any other kind of input.
   contribsByRef: () => ({
-    flags: { update: true, expose: true },
-    update: { validate: isContributionList },
+    flags: {update: true, expose: true},
+    update: {validate: isContributionList},
   }),
 
   // Artist commentary! Generally present on tracks and albums.
   commentary: () => ({
-    flags: { update: true, expose: true },
-    update: { validate: isCommentary },
+    flags: {update: true, expose: true},
+    update: {validate: isCommentary},
   }),
 
   // This is a somewhat more involved data structure - it's for additional
@@ -223,8 +221,8 @@ Thing.common = {
   //     ]
   //
   additionalFiles: () => ({
-    flags: { update: true, expose: true },
-    update: { validate: isAdditionalFileList },
+    flags: {update: true, expose: true},
+    update: {validate: isAdditionalFileList},
   }),
 
   // A reference list! Keep in mind this is for general references to wiki
@@ -236,7 +234,7 @@ Thing.common = {
   // string in multiple places by referencing the value saved on the class
   // instead.
   referenceList: (thingClass) => {
-    const { [Thing.referenceType]: referenceType } = thingClass;
+    const {[Thing.referenceType]: referenceType} = thingClass;
     if (!referenceType) {
       throw new Error(
         `The passed constructor ${thingClass.name} doesn't define Thing.referenceType!`
@@ -244,14 +242,14 @@ Thing.common = {
     }
 
     return {
-      flags: { update: true, expose: true },
-      update: { validate: validateReferenceList(referenceType) },
+      flags: {update: true, expose: true},
+      update: {validate: validateReferenceList(referenceType)},
     };
   },
 
   // Corresponding function for a single reference.
   singleReference: (thingClass) => {
-    const { [Thing.referenceType]: referenceType } = thingClass;
+    const {[Thing.referenceType]: referenceType} = thingClass;
     if (!referenceType) {
       throw new Error(
         `The passed constructor ${thingClass.name} doesn't define Thing.referenceType!`
@@ -259,8 +257,8 @@ Thing.common = {
     }
 
     return {
-      flags: { update: true, expose: true },
-      update: { validate: validateReference(referenceType) },
+      flags: {update: true, expose: true},
+      update: {validate: validateReference(referenceType)},
     };
   },
 
@@ -272,7 +270,7 @@ Thing.common = {
     thingDataProperty,
     findFn
   ) => ({
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
       dependencies: [referenceListProperty, thingDataProperty],
@@ -282,7 +280,7 @@ Thing.common = {
       }) =>
         refs && thingData
           ? refs
-              .map((ref) => findFn(ref, thingData, { mode: "quiet" }))
+              .map((ref) => findFn(ref, thingData, {mode: 'quiet'}))
               .filter(Boolean)
           : [],
     },
@@ -294,15 +292,14 @@ Thing.common = {
     thingDataProperty,
     findFn
   ) => ({
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
       dependencies: [singleReferenceProperty, thingDataProperty],
       compute: ({
         [singleReferenceProperty]: ref,
         [thingDataProperty]: thingData,
-      }) =>
-        ref && thingData ? findFn(ref, thingData, { mode: "quiet" }) : null,
+      }) => (ref && thingData ? findFn(ref, thingData, {mode: 'quiet'}) : null),
     },
   }),
 
@@ -322,17 +319,17 @@ Thing.common = {
   // reference list is somehow messed up, or artistData isn't being provided
   // properly.)
   dynamicContribs: (contribsByRefProperty) => ({
-    flags: { expose: true },
+    flags: {expose: true},
     expose: {
-      dependencies: ["artistData", contribsByRefProperty],
-      compute: ({ artistData, [contribsByRefProperty]: contribsByRef }) =>
+      dependencies: ['artistData', contribsByRefProperty],
+      compute: ({artistData, [contribsByRefProperty]: contribsByRef}) =>
         contribsByRef && artistData
           ? contribsByRef
-              .map(({ who: ref, what }) => ({
+              .map(({who: ref, what}) => ({
                 who: find.artist(ref, artistData),
                 what,
               }))
-              .filter(({ who }) => who)
+              .filter(({who}) => who)
           : [],
     },
   }),
@@ -350,9 +347,9 @@ Thing.common = {
     thingDataProperty,
     findFn
   ) => ({
-    flags: { expose: true },
+    flags: {expose: true},
     expose: {
-      dependencies: [contribsByRefProperty, thingDataProperty, "artistData"],
+      dependencies: [contribsByRefProperty, thingDataProperty, 'artistData'],
       compute({
         [Thing.instance]: thing,
         [contribsByRefProperty]: contribsByRef,
@@ -362,16 +359,16 @@ Thing.common = {
         if (!artistData) return [];
         const refs =
           contribsByRef ??
-          findFn(thing, thingData, { mode: "quiet" })?.[
+          findFn(thing, thingData, {mode: 'quiet'})?.[
             parentContribsByRefProperty
           ];
         if (!refs) return [];
         return refs
-          .map(({ who: ref, what }) => ({
+          .map(({who: ref, what}) => ({
             who: find.artist(ref, artistData),
             what,
           }))
-          .filter(({ who }) => who);
+          .filter(({who}) => who);
       },
     },
   }),
@@ -382,12 +379,12 @@ Thing.common = {
   // property. Naturally, the passed ref list property is of the things in the
   // wiki data provided, not the requesting Thing itself.
   reverseReferenceList: (wikiDataProperty, referencerRefListProperty) => ({
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
       dependencies: [wikiDataProperty],
 
-      compute: ({ [wikiDataProperty]: wikiData, [Thing.instance]: thing }) =>
+      compute: ({[wikiDataProperty]: wikiData, [Thing.instance]: thing}) =>
         wikiData
           ? wikiData.filter((t) =>
               t[referencerRefListProperty]?.includes(thing)
@@ -400,12 +397,12 @@ Thing.common = {
   // is still a list - this is for matching all the objects whose single
   // reference (in the given property) matches this Thing.
   reverseSingleReference: (wikiDataProperty, referencerRefListProperty) => ({
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
       dependencies: [wikiDataProperty],
 
-      compute: ({ [wikiDataProperty]: wikiData, [Thing.instance]: thing }) =>
+      compute: ({[wikiDataProperty]: wikiData, [Thing.instance]: thing}) =>
         wikiData?.filter((t) => t[referencerRefListProperty] === thing),
     },
   }),
@@ -413,7 +410,7 @@ Thing.common = {
   // General purpose wiki data constructor, for properties like artistData,
   // trackData, etc.
   wikiData: (thingClass) => ({
-    flags: { update: true },
+    flags: {update: true},
     update: {
       validate: validateArrayItems(validateInstanceOf(thingClass)),
     },
@@ -423,21 +420,21 @@ Thing.common = {
   // commentary content, and finds the matching artist for each reference.
   // This is mostly useful for credits and listings on artist pages.
   commentatorArtists: () => ({
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["artistData", "commentary"],
+      dependencies: ['artistData', 'commentary'],
 
-      compute: ({ artistData, commentary }) =>
+      compute: ({artistData, commentary}) =>
         artistData && commentary
           ? Array.from(
               new Set(
                 Array.from(
                   commentary
-                    .replace(/<\/?b>/g, "")
+                    .replace(/<\/?b>/g, '')
                     .matchAll(/<i>(?<who>.*?):<\/i>/g)
-                ).map(({ groups: { who } }) =>
-                  find.artist(who, artistData, { mode: "quiet" })
+                ).map(({groups: {who}}) =>
+                  find.artist(who, artistData, {mode: 'quiet'})
                 )
               )
             )
@@ -472,7 +469,7 @@ Thing.prototype[inspect.custom] = function () {
 
   return (
     (this.name ? `${cname} ${color.green(`"${this.name}"`)}` : `${cname}`) +
-    (this.directory ? ` (${color.blue(Thing.getReference(this))})` : "")
+    (this.directory ? ` (${color.blue(Thing.getReference(this))})` : '')
   );
 };
 
@@ -481,7 +478,7 @@ Thing.prototype[inspect.custom] = function () {
 Album.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Album"),
+  name: Thing.common.name('Unnamed Album'),
   color: Thing.common.color(),
   directory: Thing.common.directory(),
   urls: Thing.common.urls(),
@@ -491,13 +488,13 @@ Album.propertyDescriptors = {
   dateAddedToWiki: Thing.common.simpleDate(),
 
   coverArtDate: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
 
-    update: { validate: isDate },
+    update: {validate: isDate},
 
     expose: {
-      dependencies: ["date"],
-      transform: (coverArtDate, { date }) => coverArtDate ?? date ?? null,
+      dependencies: ['date'],
+      transform: (coverArtDate, {date}) => coverArtDate ?? date ?? null,
     },
   },
 
@@ -511,24 +508,24 @@ Album.propertyDescriptors = {
   artTagsByRef: Thing.common.referenceList(ArtTag),
 
   trackGroups: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
 
     update: {
       validate: validateArrayItems(validateInstanceOf(TrackGroup)),
     },
   },
 
-  coverArtFileExtension: Thing.common.fileExtension("jpg"),
-  trackCoverArtFileExtension: Thing.common.fileExtension("jpg"),
+  coverArtFileExtension: Thing.common.fileExtension('jpg'),
+  trackCoverArtFileExtension: Thing.common.fileExtension('jpg'),
 
   wallpaperStyle: Thing.common.simpleString(),
-  wallpaperFileExtension: Thing.common.fileExtension("jpg"),
+  wallpaperFileExtension: Thing.common.fileExtension('jpg'),
 
   bannerStyle: Thing.common.simpleString(),
-  bannerFileExtension: Thing.common.fileExtension("jpg"),
+  bannerFileExtension: Thing.common.fileExtension('jpg'),
   bannerDimensions: {
-    flags: { update: true, expose: true },
-    update: { validate: isDimensions },
+    flags: {update: true, expose: true},
+    update: {validate: isDimensions},
   },
 
   hasCoverArt: Thing.common.flag(true),
@@ -549,44 +546,44 @@ Album.propertyDescriptors = {
 
   // Expose only
 
-  artistContribs: Thing.common.dynamicContribs("artistContribsByRef"),
-  coverArtistContribs: Thing.common.dynamicContribs("coverArtistContribsByRef"),
+  artistContribs: Thing.common.dynamicContribs('artistContribsByRef'),
+  coverArtistContribs: Thing.common.dynamicContribs('coverArtistContribsByRef'),
   trackCoverArtistContribs: Thing.common.dynamicContribs(
-    "trackCoverArtistContribsByRef"
+    'trackCoverArtistContribsByRef'
   ),
   wallpaperArtistContribs: Thing.common.dynamicContribs(
-    "wallpaperArtistContribsByRef"
+    'wallpaperArtistContribsByRef'
   ),
   bannerArtistContribs: Thing.common.dynamicContribs(
-    "bannerArtistContribsByRef"
+    'bannerArtistContribsByRef'
   ),
 
   commentatorArtists: Thing.common.commentatorArtists(),
 
   tracks: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["trackGroups", "trackData"],
-      compute: ({ trackGroups, trackData }) =>
+      dependencies: ['trackGroups', 'trackData'],
+      compute: ({trackGroups, trackData}) =>
         trackGroups && trackData
           ? trackGroups
               .flatMap((group) => group.tracksByRef ?? [])
-              .map((ref) => find.track(ref, trackData, { mode: "quiet" }))
+              .map((ref) => find.track(ref, trackData, {mode: 'quiet'}))
               .filter(Boolean)
           : [],
     },
   },
 
   groups: Thing.common.dynamicThingsFromReferenceList(
-    "groupsByRef",
-    "groupData",
+    'groupsByRef',
+    'groupData',
     find.group
   ),
 
   artTags: Thing.common.dynamicThingsFromReferenceList(
-    "artTagsByRef",
-    "artTagData",
+    'artTagsByRef',
+    'artTagData',
     find.artTag
   ),
 };
@@ -632,17 +629,17 @@ Album[S.serializeDescriptors] = {
 TrackGroup.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Track Group"),
+  name: Thing.common.name('Unnamed Track Group'),
 
   color: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
 
-    update: { validate: isColor },
+    update: {validate: isColor},
 
     expose: {
-      dependencies: ["album"],
+      dependencies: ['album'],
 
-      transform(color, { album }) {
+      transform(color, {album}) {
         return color ?? album?.color ?? null;
       },
     },
@@ -657,8 +654,8 @@ TrackGroup.propertyDescriptors = {
   // Update only
 
   album: {
-    flags: { update: true },
-    update: { validate: validateInstanceOf(Album) },
+    flags: {update: true},
+    update: {validate: validateInstanceOf(Album)},
   },
 
   trackData: Thing.common.wikiData(Track),
@@ -666,11 +663,11 @@ TrackGroup.propertyDescriptors = {
   // Expose only
 
   tracks: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["tracksByRef", "trackData"],
-      compute: ({ tracksByRef, trackData }) =>
+      dependencies: ['tracksByRef', 'trackData'],
+      compute: ({tracksByRef, trackData}) =>
         tracksByRef && trackData
           ? tracksByRef.map((ref) => find.track(ref, trackData)).filter(Boolean)
           : [],
@@ -678,11 +675,11 @@ TrackGroup.propertyDescriptors = {
   },
 
   startIndex: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["album"],
-      compute: ({ album, [TrackGroup.instance]: trackGroup }) =>
+      dependencies: ['album'],
+      compute: ({album, [TrackGroup.instance]: trackGroup}) =>
         album.trackGroups
           .slice(0, album.trackGroups.indexOf(trackGroup))
           .reduce((acc, tg) => acc + tg.tracks.length, 0),
@@ -717,12 +714,12 @@ Track.hasCoverArt = (
 Track.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Track"),
+  name: Thing.common.name('Unnamed Track'),
   directory: Thing.common.directory(),
 
   duration: {
-    flags: { update: true, expose: true },
-    update: { validate: isDuration },
+    flags: {update: true, expose: true},
+    update: {validate: isDuration},
   },
 
   urls: Thing.common.urls(),
@@ -738,15 +735,15 @@ Track.propertyDescriptors = {
   artTagsByRef: Thing.common.referenceList(ArtTag),
 
   hasCoverArt: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
 
-    update: { validate: isBoolean },
+    update: {validate: isBoolean},
 
     expose: {
-      dependencies: ["albumData", "coverArtistContribsByRef"],
+      dependencies: ['albumData', 'coverArtistContribsByRef'],
       transform: (
         hasCoverArt,
-        { albumData, coverArtistContribsByRef, [Track.instance]: track }
+        {albumData, coverArtistContribsByRef, [Track.instance]: track}
       ) =>
         Track.hasCoverArt(
           track,
@@ -758,12 +755,12 @@ Track.propertyDescriptors = {
   },
 
   coverArtFileExtension: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
 
-    update: { validate: isFileExtension },
+    update: {validate: isFileExtension},
 
     expose: {
-      dependencies: ["albumData", "coverArtistContribsByRef"],
+      dependencies: ['albumData', 'coverArtistContribsByRef'],
       transform: (
         coverArtFileExtension,
         {
@@ -782,7 +779,7 @@ Track.propertyDescriptors = {
         )
           ? Track.findAlbum(track, albumData)?.trackCoverArtFileExtension
           : Track.findAlbum(track, albumData)?.coverArtFileExtension) ??
-        "jpg",
+        'jpg',
     },
   },
 
@@ -808,11 +805,11 @@ Track.propertyDescriptors = {
   commentatorArtists: Thing.common.commentatorArtists(),
 
   album: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["albumData"],
-      compute: ({ [Track.instance]: track, albumData }) =>
+      dependencies: ['albumData'],
+      compute: ({[Track.instance]: track, albumData}) =>
         albumData?.find((album) => album.tracks.includes(track)) ?? null,
     },
   },
@@ -825,28 +822,28 @@ Track.propertyDescriptors = {
   // dataSourceAlbum is available (depending on the Track creator to optionally
   // provide dataSourceAlbumByRef).
   dataSourceAlbum: Thing.common.dynamicThingFromSingleReference(
-    "dataSourceAlbumByRef",
-    "albumData",
+    'dataSourceAlbumByRef',
+    'albumData',
     find.album
   ),
 
   date: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["albumData", "dateFirstReleased"],
-      compute: ({ albumData, dateFirstReleased, [Track.instance]: track }) =>
+      dependencies: ['albumData', 'dateFirstReleased'],
+      compute: ({albumData, dateFirstReleased, [Track.instance]: track}) =>
         dateFirstReleased ?? Track.findAlbum(track, albumData)?.date ?? null,
     },
   },
 
   color: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["albumData"],
+      dependencies: ['albumData'],
 
-      compute: ({ albumData, [Track.instance]: track }) =>
+      compute: ({albumData, [Track.instance]: track}) =>
         Track.findAlbum(track, albumData)?.trackGroups.find((tg) =>
           tg.tracks.includes(track)
         )?.color ?? null,
@@ -854,15 +851,15 @@ Track.propertyDescriptors = {
   },
 
   coverArtDate: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
 
-    update: { validate: isDate },
+    update: {validate: isDate},
 
     expose: {
-      dependencies: ["albumData", "dateFirstReleased"],
+      dependencies: ['albumData', 'dateFirstReleased'],
       transform: (
         coverArtDate,
-        { albumData, dateFirstReleased, [Track.instance]: track }
+        {albumData, dateFirstReleased, [Track.instance]: track}
       ) =>
         coverArtDate ??
         dateFirstReleased ??
@@ -873,16 +870,16 @@ Track.propertyDescriptors = {
   },
 
   originalReleaseTrack: Thing.common.dynamicThingFromSingleReference(
-    "originalReleaseTrackByRef",
-    "trackData",
+    'originalReleaseTrackByRef',
+    'trackData',
     find.track
   ),
 
   otherReleases: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["originalReleaseTrackByRef", "trackData"],
+      dependencies: ['originalReleaseTrackByRef', 'trackData'],
 
       compute: ({
         originalReleaseTrackByRef: t1origRef,
@@ -898,7 +895,7 @@ Track.propertyDescriptors = {
         return [
           t1orig,
           ...trackData.filter((t2) => {
-            const { originalReleaseTrack: t2orig } = t2;
+            const {originalReleaseTrack: t2orig} = t2;
             return t2 !== t1 && t2orig && (t2orig === t1orig || t2orig === t1);
           }),
         ].filter(Boolean);
@@ -908,27 +905,27 @@ Track.propertyDescriptors = {
 
   // Previously known as: (track).artists
   artistContribs: Thing.common.dynamicInheritContribs(
-    "artistContribsByRef",
-    "artistContribsByRef",
-    "albumData",
+    'artistContribsByRef',
+    'artistContribsByRef',
+    'albumData',
     Track.findAlbum
   ),
 
   // Previously known as: (track).contributors
-  contributorContribs: Thing.common.dynamicContribs("contributorContribsByRef"),
+  contributorContribs: Thing.common.dynamicContribs('contributorContribsByRef'),
 
   // Previously known as: (track).coverArtists
   coverArtistContribs: Thing.common.dynamicInheritContribs(
-    "coverArtistContribsByRef",
-    "trackCoverArtistContribsByRef",
-    "albumData",
+    'coverArtistContribsByRef',
+    'trackCoverArtistContribsByRef',
+    'albumData',
     Track.findAlbum
   ),
 
   // Previously known as: (track).references
   referencedTracks: Thing.common.dynamicThingsFromReferenceList(
-    "referencedTracksByRef",
-    "trackData",
+    'referencedTracksByRef',
+    'trackData',
     find.track
   ),
 
@@ -941,12 +938,12 @@ Track.propertyDescriptors = {
   // the "Tracks - by Times Referenced" listing page (or other data
   // processing).
   referencedByTracks: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["trackData"],
+      dependencies: ['trackData'],
 
-      compute: ({ trackData, [Track.instance]: track }) =>
+      compute: ({trackData, [Track.instance]: track}) =>
         trackData
           ? trackData
               .filter((t) => !t.originalReleaseTrack)
@@ -957,13 +954,13 @@ Track.propertyDescriptors = {
 
   // Previously known as: (track).flashes
   featuredInFlashes: Thing.common.reverseReferenceList(
-    "flashData",
-    "featuredTracks"
+    'flashData',
+    'featuredTracks'
   ),
 
   artTags: Thing.common.dynamicThingsFromReferenceList(
-    "artTagsByRef",
-    "artTagData",
+    'artTagsByRef',
+    'artTagData',
     find.artTag
   ),
 };
@@ -971,12 +968,12 @@ Track.propertyDescriptors = {
 Track.prototype[inspect.custom] = function () {
   const base = Thing.prototype[inspect.custom].apply(this);
 
-  const { album, dataSourceAlbum } = this;
+  const {album, dataSourceAlbum} = this;
   const albumName = album ? album.name : dataSourceAlbum?.name;
   const albumIndex =
     albumName &&
     (album ? album.tracks.indexOf(this) : dataSourceAlbum.tracks.indexOf(this));
-  const trackNum = albumIndex === -1 ? "#?" : `#${albumIndex + 1}`;
+  const trackNum = albumIndex === -1 ? '#?' : `#${albumIndex + 1}`;
 
   return albumName
     ? base + ` (${color.yellow(trackNum)} in ${color.green(albumName)})`
@@ -986,13 +983,13 @@ Track.prototype[inspect.custom] = function () {
 // -> Artist
 
 Artist.filterByContrib = (thingDataProperty, contribsProperty) => ({
-  flags: { expose: true },
+  flags: {expose: true},
 
   expose: {
     dependencies: [thingDataProperty],
 
-    compute: ({ [thingDataProperty]: thingData, [Artist.instance]: artist }) =>
-      thingData?.filter(({ [contribsProperty]: contribs }) =>
+    compute: ({[thingDataProperty]: thingData, [Artist.instance]: artist}) =>
+      thingData?.filter(({[contribsProperty]: contribs}) =>
         contribs?.some((contrib) => contrib.who === artist)
       ),
   },
@@ -1001,16 +998,16 @@ Artist.filterByContrib = (thingDataProperty, contribsProperty) => ({
 Artist.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Artist"),
+  name: Thing.common.name('Unnamed Artist'),
   directory: Thing.common.directory(),
   urls: Thing.common.urls(),
   contextNotes: Thing.common.simpleString(),
 
   hasAvatar: Thing.common.flag(false),
-  avatarFileExtension: Thing.common.fileExtension("jpg"),
+  avatarFileExtension: Thing.common.fileExtension('jpg'),
 
   aliasNames: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
     update: {
       validate: validateArrayItems(isName),
     },
@@ -1029,87 +1026,87 @@ Artist.propertyDescriptors = {
   // Expose only
 
   aliasedArtist: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["artistData", "aliasedArtistRef"],
-      compute: ({ artistData, aliasedArtistRef }) =>
+      dependencies: ['artistData', 'aliasedArtistRef'],
+      compute: ({artistData, aliasedArtistRef}) =>
         aliasedArtistRef && artistData
-          ? find.artist(aliasedArtistRef, artistData, { mode: "quiet" })
+          ? find.artist(aliasedArtistRef, artistData, {mode: 'quiet'})
           : null,
     },
   },
 
-  tracksAsArtist: Artist.filterByContrib("trackData", "artistContribs"),
+  tracksAsArtist: Artist.filterByContrib('trackData', 'artistContribs'),
   tracksAsContributor: Artist.filterByContrib(
-    "trackData",
-    "contributorContribs"
+    'trackData',
+    'contributorContribs'
   ),
   tracksAsCoverArtist: Artist.filterByContrib(
-    "trackData",
-    "coverArtistContribs"
+    'trackData',
+    'coverArtistContribs'
   ),
 
   tracksAsAny: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["trackData"],
+      dependencies: ['trackData'],
 
-      compute: ({ trackData, [Artist.instance]: artist }) =>
+      compute: ({trackData, [Artist.instance]: artist}) =>
         trackData?.filter((track) =>
           [
             ...track.artistContribs,
             ...track.contributorContribs,
             ...track.coverArtistContribs,
-          ].some(({ who }) => who === artist)
+          ].some(({who}) => who === artist)
         ),
     },
   },
 
   tracksAsCommentator: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["trackData"],
+      dependencies: ['trackData'],
 
-      compute: ({ trackData, [Artist.instance]: artist }) =>
-        trackData.filter(({ commentatorArtists }) =>
+      compute: ({trackData, [Artist.instance]: artist}) =>
+        trackData.filter(({commentatorArtists}) =>
           commentatorArtists?.includes(artist)
         ),
     },
   },
 
-  albumsAsAlbumArtist: Artist.filterByContrib("albumData", "artistContribs"),
+  albumsAsAlbumArtist: Artist.filterByContrib('albumData', 'artistContribs'),
   albumsAsCoverArtist: Artist.filterByContrib(
-    "albumData",
-    "coverArtistContribs"
+    'albumData',
+    'coverArtistContribs'
   ),
   albumsAsWallpaperArtist: Artist.filterByContrib(
-    "albumData",
-    "wallpaperArtistContribs"
+    'albumData',
+    'wallpaperArtistContribs'
   ),
   albumsAsBannerArtist: Artist.filterByContrib(
-    "albumData",
-    "bannerArtistContribs"
+    'albumData',
+    'bannerArtistContribs'
   ),
 
   albumsAsCommentator: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["albumData"],
+      dependencies: ['albumData'],
 
-      compute: ({ albumData, [Artist.instance]: artist }) =>
-        albumData.filter(({ commentatorArtists }) =>
+      compute: ({albumData, [Artist.instance]: artist}) =>
+        albumData.filter(({commentatorArtists}) =>
           commentatorArtists?.includes(artist)
         ),
     },
   },
 
   flashesAsContributor: Artist.filterByContrib(
-    "flashData",
-    "contributorContribs"
+    'flashData',
+    'contributorContribs'
   ),
 };
 
@@ -1143,7 +1140,7 @@ Artist[S.serializeDescriptors] = {
 Group.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Group"),
+  name: Thing.common.name('Unnamed Group'),
   directory: Thing.common.directory(),
 
   description: Thing.common.simpleString(),
@@ -1158,42 +1155,42 @@ Group.propertyDescriptors = {
   // Expose only
 
   descriptionShort: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["description"],
-      compute: ({ description }) => description.split('<hr class="split">')[0],
+      dependencies: ['description'],
+      compute: ({description}) => description.split('<hr class="split">')[0],
     },
   },
 
   albums: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["albumData"],
-      compute: ({ albumData, [Group.instance]: group }) =>
+      dependencies: ['albumData'],
+      compute: ({albumData, [Group.instance]: group}) =>
         albumData?.filter((album) => album.groups.includes(group)) ?? [],
     },
   },
 
   color: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["groupCategoryData"],
+      dependencies: ['groupCategoryData'],
 
-      compute: ({ groupCategoryData, [Group.instance]: group }) =>
+      compute: ({groupCategoryData, [Group.instance]: group}) =>
         groupCategoryData.find((category) => category.groups.includes(group))
           ?.color ?? null,
     },
   },
 
   category: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["groupCategoryData"],
-      compute: ({ groupCategoryData, [Group.instance]: group }) =>
+      dependencies: ['groupCategoryData'],
+      compute: ({groupCategoryData, [Group.instance]: group}) =>
         groupCategoryData.find((category) => category.groups.includes(group)) ??
         null,
     },
@@ -1203,7 +1200,7 @@ Group.propertyDescriptors = {
 GroupCategory.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Group Category"),
+  name: Thing.common.name('Unnamed Group Category'),
   color: Thing.common.color(),
 
   groupsByRef: Thing.common.referenceList(Group),
@@ -1215,8 +1212,8 @@ GroupCategory.propertyDescriptors = {
   // Expose only
 
   groups: Thing.common.dynamicThingsFromReferenceList(
-    "groupsByRef",
-    "groupData",
+    'groupsByRef',
+    'groupData',
     find.group
   ),
 };
@@ -1226,7 +1223,7 @@ GroupCategory.propertyDescriptors = {
 ArtTag.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Art Tag"),
+  name: Thing.common.name('Unnamed Art Tag'),
   directory: Thing.common.directory(),
   color: Thing.common.color(),
   isContentWarning: Thing.common.flag(false),
@@ -1240,16 +1237,16 @@ ArtTag.propertyDescriptors = {
 
   // Previously known as: (tag).things
   taggedInThings: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["albumData", "trackData"],
-      compute: ({ albumData, trackData, [ArtTag.instance]: artTag }) =>
+      dependencies: ['albumData', 'trackData'],
+      compute: ({albumData, trackData, [ArtTag.instance]: artTag}) =>
         sortAlbumsTracksChronologically(
           [...albumData, ...trackData].filter((thing) =>
             thing.artTags?.includes(artTag)
           ),
-          { getDate: (o) => o.coverArtDate }
+          {getDate: (o) => o.coverArtDate}
         ),
     },
   },
@@ -1260,7 +1257,7 @@ ArtTag.propertyDescriptors = {
 NewsEntry.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed News Entry"),
+  name: Thing.common.name('Unnamed News Entry'),
   directory: Thing.common.directory(),
   date: Thing.common.simpleDate(),
 
@@ -1269,12 +1266,12 @@ NewsEntry.propertyDescriptors = {
   // Expose only
 
   contentShort: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["content"],
+      dependencies: ['content'],
 
-      compute: ({ content }) => content.split('<hr class="split">')[0],
+      compute: ({content}) => content.split('<hr class="split">')[0],
     },
   },
 };
@@ -1284,15 +1281,15 @@ NewsEntry.propertyDescriptors = {
 StaticPage.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Static Page"),
+  name: Thing.common.name('Unnamed Static Page'),
 
   nameShort: {
-    flags: { update: true, expose: true },
-    update: { validate: isName },
+    flags: {update: true, expose: true},
+    update: {validate: isName},
 
     expose: {
-      dependencies: ["name"],
-      transform: (value, { name }) => value ?? name,
+      dependencies: ['name'],
+      transform: (value, {name}) => value ?? name,
     },
   },
 
@@ -1310,7 +1307,7 @@ HomepageLayout.propertyDescriptors = {
   sidebarContent: Thing.common.simpleString(),
 
   rows: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
 
     update: {
       validate: validateArrayItems(validateInstanceOf(HomepageLayoutRow)),
@@ -1321,13 +1318,13 @@ HomepageLayout.propertyDescriptors = {
 HomepageLayoutRow.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Homepage Row"),
+  name: Thing.common.name('Unnamed Homepage Row'),
 
   type: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
 
     update: {
-      validate(value) {
+      validate() {
         throw new Error(`'type' property validator must be overridden`);
       },
     },
@@ -1350,10 +1347,10 @@ HomepageLayoutAlbumsRow.propertyDescriptors = {
   // Update & expose
 
   type: {
-    flags: { update: true, expose: true },
+    flags: {update: true, expose: true},
     update: {
       validate(value) {
-        if (value !== "albums") {
+        if (value !== 'albums') {
           throw new TypeError(`Expected 'albums'`);
         }
 
@@ -1366,25 +1363,25 @@ HomepageLayoutAlbumsRow.propertyDescriptors = {
   sourceAlbumsByRef: Thing.common.referenceList(Album),
 
   countAlbumsFromGroup: {
-    flags: { update: true, expose: true },
-    update: { validate: isCountingNumber },
+    flags: {update: true, expose: true},
+    update: {validate: isCountingNumber},
   },
 
   actionLinks: {
-    flags: { update: true, expose: true },
-    update: { validate: validateArrayItems(isString) },
+    flags: {update: true, expose: true},
+    update: {validate: validateArrayItems(isString)},
   },
 
   // Expose only
 
   sourceGroup: Thing.common.dynamicThingFromSingleReference(
-    "sourceGroupByRef",
-    "groupData",
+    'sourceGroupByRef',
+    'groupData',
     find.group
   ),
   sourceAlbums: Thing.common.dynamicThingsFromReferenceList(
-    "sourceAlbumsByRef",
-    "albumData",
+    'sourceAlbumsByRef',
+    'albumData',
     find.album
   ),
 };
@@ -1394,18 +1391,18 @@ HomepageLayoutAlbumsRow.propertyDescriptors = {
 Flash.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Flash"),
+  name: Thing.common.name('Unnamed Flash'),
 
   directory: {
-    flags: { update: true, expose: true },
-    update: { validate: isDirectory },
+    flags: {update: true, expose: true},
+    update: {validate: isDirectory},
 
     // Flashes expose directory differently from other Things! Their
     // default directory is dependent on the page number (or ID), not
     // the name.
     expose: {
-      dependencies: ["page"],
-      transform(directory, { page }) {
+      dependencies: ['page'],
+      transform(directory, {page}) {
         if (directory === null && page === null) return null;
         else if (directory === null) return page;
         else return directory;
@@ -1414,8 +1411,8 @@ Flash.propertyDescriptors = {
   },
 
   page: {
-    flags: { update: true, expose: true },
-    update: { validate: oneOf(isString, isNumber) },
+    flags: {update: true, expose: true},
+    update: {validate: oneOf(isString, isNumber)},
 
     expose: {
       transform: (value) => (value === null ? null : value.toString()),
@@ -1424,7 +1421,7 @@ Flash.propertyDescriptors = {
 
   date: Thing.common.simpleDate(),
 
-  coverArtFileExtension: Thing.common.fileExtension("jpg"),
+  coverArtFileExtension: Thing.common.fileExtension('jpg'),
 
   contributorContribsByRef: Thing.common.contribsByRef(),
 
@@ -1440,32 +1437,32 @@ Flash.propertyDescriptors = {
 
   // Expose only
 
-  contributorContribs: Thing.common.dynamicContribs("contributorContribsByRef"),
+  contributorContribs: Thing.common.dynamicContribs('contributorContribsByRef'),
 
   featuredTracks: Thing.common.dynamicThingsFromReferenceList(
-    "featuredTracksByRef",
-    "trackData",
+    'featuredTracksByRef',
+    'trackData',
     find.track
   ),
 
   act: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["flashActData"],
+      dependencies: ['flashActData'],
 
-      compute: ({ flashActData, [Flash.instance]: flash }) =>
+      compute: ({flashActData, [Flash.instance]: flash}) =>
         flashActData.find((act) => act.flashes.includes(flash)) ?? null,
     },
   },
 
   color: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["flashActData"],
+      dependencies: ['flashActData'],
 
-      compute: ({ flashActData, [Flash.instance]: flash }) =>
+      compute: ({flashActData, [Flash.instance]: flash}) =>
         flashActData.find((act) => act.flashes.includes(flash))?.color ?? null,
     },
   },
@@ -1485,7 +1482,7 @@ Flash[S.serializeDescriptors] = {
 FlashAct.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Flash Act"),
+  name: Thing.common.name('Unnamed Flash Act'),
   color: Thing.common.color(),
   anchor: Thing.common.simpleString(),
   jump: Thing.common.simpleString(),
@@ -1500,8 +1497,8 @@ FlashAct.propertyDescriptors = {
   // Expose only
 
   flashes: Thing.common.dynamicThingsFromReferenceList(
-    "flashesByRef",
-    "flashData",
+    'flashesByRef',
+    'flashData',
     find.flash
   ),
 };
@@ -1511,16 +1508,16 @@ FlashAct.propertyDescriptors = {
 WikiInfo.propertyDescriptors = {
   // Update & expose
 
-  name: Thing.common.name("Unnamed Wiki"),
+  name: Thing.common.name('Unnamed Wiki'),
 
   // Displayed in nav bar.
   nameShort: {
-    flags: { update: true, expose: true },
-    update: { validate: isName },
+    flags: {update: true, expose: true},
+    update: {validate: isName},
 
     expose: {
-      dependencies: ["name"],
-      transform: (value, { name }) => value ?? name,
+      dependencies: ['name'],
+      transform: (value, {name}) => value ?? name,
     },
   },
 
@@ -1532,13 +1529,13 @@ WikiInfo.propertyDescriptors = {
   footerContent: Thing.common.simpleString(),
 
   defaultLanguage: {
-    flags: { update: true, expose: true },
-    update: { validate: isLanguageCode },
+    flags: {update: true, expose: true},
+    update: {validate: isLanguageCode},
   },
 
   canonicalBase: {
-    flags: { update: true, expose: true },
-    update: { validate: isURL },
+    flags: {update: true, expose: true},
+    update: {validate: isURL},
   },
 
   divideTrackListsByGroupsByRef: Thing.common.referenceList(Group),
@@ -1557,8 +1554,8 @@ WikiInfo.propertyDescriptors = {
   // Expose only
 
   divideTrackListsByGroups: Thing.common.dynamicThingsFromReferenceList(
-    "divideTrackListsByGroupsByRef",
-    "groupData",
+    'divideTrackListsByGroupsByRef',
+    'groupData',
     find.group
   ),
 };
@@ -1566,10 +1563,10 @@ WikiInfo.propertyDescriptors = {
 // -> Language
 
 const intlHelper = (constructor, opts) => ({
-  flags: { expose: true },
+  flags: {expose: true},
   expose: {
-    dependencies: ["code", "intlCode"],
-    compute: ({ code, intlCode }) => {
+    dependencies: ['code', 'intlCode'],
+    compute: ({code, intlCode}) => {
       const constructCode = intlCode ?? code;
       if (!constructCode) return null;
       return Reflect.construct(constructor, [constructCode, opts]);
@@ -1584,8 +1581,8 @@ Language.propertyDescriptors = {
   // from other languages (similar to how "Directory" operates in many data
   // objects).
   code: {
-    flags: { update: true, expose: true },
-    update: { validate: isLanguageCode },
+    flags: {update: true, expose: true},
+    update: {validate: isLanguageCode},
   },
 
   // Human-readable name. This should be the language's own native name, not
@@ -1596,11 +1593,11 @@ Language.propertyDescriptors = {
   // Usually this will be the same as the language's general code, but it
   // may be overridden to provide Intl constructors an alternative value.
   intlCode: {
-    flags: { update: true, expose: true },
-    update: { validate: isLanguageCode },
+    flags: {update: true, expose: true},
+    update: {validate: isLanguageCode},
     expose: {
-      dependencies: ["code"],
-      transform: (intlCode, { code }) => intlCode ?? code,
+      dependencies: ['code'],
+      transform: (intlCode, {code}) => intlCode ?? code,
     },
   },
 
@@ -1617,13 +1614,13 @@ Language.propertyDescriptors = {
   // Mapping of translation keys to values (strings). Generally, don't
   // access this object directly - use methods instead.
   strings: {
-    flags: { update: true, expose: true },
-    update: { validate: (t) => typeof t === "object" },
+    flags: {update: true, expose: true},
+    update: {validate: (t) => typeof t === 'object'},
     expose: {
-      dependencies: ["inheritedStrings"],
-      transform(strings, { inheritedStrings }) {
+      dependencies: ['inheritedStrings'],
+      transform(strings, {inheritedStrings}) {
         if (strings || inheritedStrings) {
-          return { ...(inheritedStrings ?? {}), ...(strings ?? {}) };
+          return {...(inheritedStrings ?? {}), ...(strings ?? {})};
         } else {
           return null;
         }
@@ -1634,8 +1631,8 @@ Language.propertyDescriptors = {
   // May be provided to specify "default" strings, generally (but not
   // necessarily) inherited from another Language object.
   inheritedStrings: {
-    flags: { update: true, expose: true },
-    update: { validate: (t) => typeof t === "object" },
+    flags: {update: true, expose: true},
+    update: {validate: (t) => typeof t === 'object'},
   },
 
   // Update only
@@ -1644,20 +1641,20 @@ Language.propertyDescriptors = {
 
   // Expose only
 
-  intl_date: intlHelper(Intl.DateTimeFormat, { full: true }),
+  intl_date: intlHelper(Intl.DateTimeFormat, {full: true}),
   intl_number: intlHelper(Intl.NumberFormat),
-  intl_listConjunction: intlHelper(Intl.ListFormat, { type: "conjunction" }),
-  intl_listDisjunction: intlHelper(Intl.ListFormat, { type: "disjunction" }),
-  intl_listUnit: intlHelper(Intl.ListFormat, { type: "unit" }),
-  intl_pluralCardinal: intlHelper(Intl.PluralRules, { type: "cardinal" }),
-  intl_pluralOrdinal: intlHelper(Intl.PluralRules, { type: "ordinal" }),
+  intl_listConjunction: intlHelper(Intl.ListFormat, {type: 'conjunction'}),
+  intl_listDisjunction: intlHelper(Intl.ListFormat, {type: 'disjunction'}),
+  intl_listUnit: intlHelper(Intl.ListFormat, {type: 'unit'}),
+  intl_pluralCardinal: intlHelper(Intl.PluralRules, {type: 'cardinal'}),
+  intl_pluralOrdinal: intlHelper(Intl.PluralRules, {type: 'ordinal'}),
 
   validKeys: {
-    flags: { expose: true },
+    flags: {expose: true},
 
     expose: {
-      dependencies: ["strings", "inheritedStrings"],
-      compute: ({ strings, inheritedStrings }) =>
+      dependencies: ['strings', 'inheritedStrings'],
+      compute: ({strings, inheritedStrings}) =>
         Array.from(
           new Set([
             ...Object.keys(inheritedStrings ?? {}),
@@ -1668,12 +1665,12 @@ Language.propertyDescriptors = {
   },
 
   strings_htmlEscaped: {
-    flags: { expose: true },
+    flags: {expose: true},
     expose: {
-      dependencies: ["strings", "inheritedStrings", "escapeHTML"],
-      compute({ strings, inheritedStrings, escapeHTML }) {
+      dependencies: ['strings', 'inheritedStrings', 'escapeHTML'],
+      compute({strings, inheritedStrings, escapeHTML}) {
         if (!(strings || inheritedStrings) || !escapeHTML) return null;
-        const allStrings = { ...(inheritedStrings ?? {}), ...(strings ?? {}) };
+        const allStrings = {...(inheritedStrings ?? {}), ...(strings ?? {})};
         return Object.fromEntries(
           Object.entries(allStrings).map(([k, v]) => [k, escapeHTML(v)])
         );
@@ -1683,12 +1680,12 @@ Language.propertyDescriptors = {
 };
 
 const countHelper = (stringKey, argName = stringKey) =>
-  function (value, { unit = false } = {}) {
+  function (value, {unit = false} = {}) {
     return this.$(
       unit
         ? `count.${stringKey}.withUnit.` + this.getUnitForm(value)
         : `count.${stringKey}`,
-      { [argName]: this.formatNumber(value) }
+      {[argName]: this.formatNumber(value)}
     );
   };
 
@@ -1704,7 +1701,7 @@ Object.assign(Language.prototype, {
   },
 
   getUnitForm(value) {
-    this.assertIntlAvailable("intl_pluralCardinal");
+    this.assertIntlAvailable('intl_pluralCardinal');
     return this.intl_pluralCardinal.select(value);
   },
 
@@ -1738,7 +1735,7 @@ Object.assign(Language.prototype, {
     // like, who cares, dude?) Also, this is an array, 8ecause it's handy
     // for the iterating we're a8out to do.
     const processedArgs = Object.entries(args).map(([k, v]) => [
-      k.replace(/[A-Z]/g, "_$&").toUpperCase(),
+      k.replace(/[A-Z]/g, '_$&').toUpperCase(),
       v,
     ]);
 
@@ -1758,55 +1755,55 @@ Object.assign(Language.prototype, {
   },
 
   formatDate(date) {
-    this.assertIntlAvailable("intl_date");
+    this.assertIntlAvailable('intl_date');
     return this.intl_date.format(date);
   },
 
   formatDateRange(startDate, endDate) {
-    this.assertIntlAvailable("intl_date");
+    this.assertIntlAvailable('intl_date');
     return this.intl_date.formatRange(startDate, endDate);
   },
 
-  formatDuration(secTotal, { approximate = false, unit = false } = {}) {
+  formatDuration(secTotal, {approximate = false, unit = false} = {}) {
     if (secTotal === 0) {
-      return this.formatString("count.duration.missing");
+      return this.formatString('count.duration.missing');
     }
 
     const hour = Math.floor(secTotal / 3600);
     const min = Math.floor((secTotal - hour * 3600) / 60);
     const sec = Math.floor(secTotal - hour * 3600 - min * 60);
 
-    const pad = (val) => val.toString().padStart(2, "0");
+    const pad = (val) => val.toString().padStart(2, '0');
 
-    const stringSubkey = unit ? ".withUnit" : "";
+    const stringSubkey = unit ? '.withUnit' : '';
 
     const duration =
       hour > 0
-        ? this.formatString("count.duration.hours" + stringSubkey, {
+        ? this.formatString('count.duration.hours' + stringSubkey, {
             hours: hour,
             minutes: pad(min),
             seconds: pad(sec),
           })
-        : this.formatString("count.duration.minutes" + stringSubkey, {
+        : this.formatString('count.duration.minutes' + stringSubkey, {
             minutes: min,
             seconds: pad(sec),
           });
 
     return approximate
-      ? this.formatString("count.duration.approximate", { duration })
+      ? this.formatString('count.duration.approximate', {duration})
       : duration;
   },
 
   formatIndex(value) {
-    this.assertIntlAvailable("intl_pluralOrdinal");
+    this.assertIntlAvailable('intl_pluralOrdinal');
     return this.formatString(
-      "count.index." + this.intl_pluralOrdinal.select(value),
-      { index: value }
+      'count.index.' + this.intl_pluralOrdinal.select(value),
+      {index: value}
     );
   },
 
   formatNumber(value) {
-    this.assertIntlAvailable("intl_number");
+    this.assertIntlAvailable('intl_number');
     return this.intl_number.format(value);
   },
 
@@ -1817,70 +1814,70 @@ Object.assign(Language.prototype, {
 
     const words =
       value > 1000
-        ? this.formatString("count.words.thousand", { words: num })
-        : this.formatString("count.words", { words: num });
+        ? this.formatString('count.words.thousand', {words: num})
+        : this.formatString('count.words', {words: num});
 
     return this.formatString(
-      "count.words.withUnit." + this.getUnitForm(value),
-      { words }
+      'count.words.withUnit.' + this.getUnitForm(value),
+      {words}
     );
   },
 
   // Conjunction list: A, B, and C
   formatConjunctionList(array) {
-    this.assertIntlAvailable("intl_listConjunction");
+    this.assertIntlAvailable('intl_listConjunction');
     return this.intl_listConjunction.format(array);
   },
 
   // Disjunction lists: A, B, or C
   formatDisjunctionList(array) {
-    this.assertIntlAvailable("intl_listDisjunction");
+    this.assertIntlAvailable('intl_listDisjunction');
     return this.intl_listDisjunction.format(array);
   },
 
   // Unit lists: A, B, C
   formatUnitList(array) {
-    this.assertIntlAvailable("intl_listUnit");
+    this.assertIntlAvailable('intl_listUnit');
     return this.intl_listUnit.format(array);
   },
 
   // File sizes: 42.5 kB, 127.2 MB, 4.13 GB, 998.82 TB
   formatFileSize(bytes) {
-    if (!bytes) return "";
+    if (!bytes) return '';
 
     bytes = parseInt(bytes);
-    if (isNaN(bytes)) return "";
+    if (isNaN(bytes)) return '';
 
     const round = (exp) => Math.round(bytes / 10 ** (exp - 1)) / 10;
 
     if (bytes >= 10 ** 12) {
-      return this.formatString("count.fileSize.terabytes", {
+      return this.formatString('count.fileSize.terabytes', {
         terabytes: round(12),
       });
     } else if (bytes >= 10 ** 9) {
-      return this.formatString("count.fileSize.gigabytes", {
+      return this.formatString('count.fileSize.gigabytes', {
         gigabytes: round(9),
       });
     } else if (bytes >= 10 ** 6) {
-      return this.formatString("count.fileSize.megabytes", {
+      return this.formatString('count.fileSize.megabytes', {
         megabytes: round(6),
       });
     } else if (bytes >= 10 ** 3) {
-      return this.formatString("count.fileSize.kilobytes", {
+      return this.formatString('count.fileSize.kilobytes', {
         kilobytes: round(3),
       });
     } else {
-      return this.formatString("count.fileSize.bytes", { bytes });
+      return this.formatString('count.fileSize.bytes', {bytes});
     }
   },
 
   // TODO: These are hard-coded. Is there a better way?
-  countAdditionalFiles: countHelper("additionalFiles", "files"),
-  countAlbums: countHelper("albums"),
-  countCommentaryEntries: countHelper("commentaryEntries", "entries"),
-  countContributions: countHelper("contributions"),
-  countCoverArts: countHelper("coverArts"),
-  countTimesReferenced: countHelper("timesReferenced"),
-  countTimesUsed: countHelper("timesUsed"),
-  countTracks: countHelper("tracks"),
+  countAdditionalFiles: countHelper('additionalFiles', 'files'),
+  countAlbums: countHelper('albums'),
+  countCommentaryEntries: countHelper('commentaryEntries', 'entries'),
+  countContributions: countHelper('contributions'),
+  countCoverArts: countHelper('coverArts'),
+  countTimesReferenced: countHelper('timesReferenced'),
+  countTimesUsed: countHelper('timesUsed'),
+  countTracks: countHelper('tracks'),
 });
diff --git a/src/data/validators.js b/src/data/validators.js
index eab2689..8d92239 100644
--- a/src/data/validators.js
+++ b/src/data/validators.js
@@ -1,13 +1,13 @@
-// @format
+/** @format */
 
-import { withAggregate } from "../util/sugar.js";
+import {withAggregate} from '../util/sugar.js';
 
-import { color, ENABLE_COLOR, decorateTime } from "../util/cli.js";
+import {color, ENABLE_COLOR} from '../util/cli.js';
 
-import { inspect as nodeInspect } from "util";
+import {inspect as nodeInspect} from 'util';
 
 function inspect(value) {
-  return nodeInspect(value, { colors: ENABLE_COLOR });
+  return nodeInspect(value, {colors: ENABLE_COLOR});
 }
 
 // Basic types (primitives)
@@ -24,11 +24,11 @@ function isType(value, type) {
 }
 
 export function isBoolean(value) {
-  return isType(value, "boolean");
+  return isType(value, 'boolean');
 }
 
 export function isNumber(value) {
-  return isType(value, "number");
+  return isType(value, 'number');
 }
 
 export function isPositive(number) {
@@ -86,7 +86,7 @@ export function isWholeNumber(number) {
 }
 
 export function isString(value) {
-  return isType(value, "string");
+  return isType(value, 'string');
 }
 
 export function isStringNonEmpty(value) {
@@ -116,7 +116,7 @@ export function isDate(value) {
 }
 
 export function isObject(value) {
-  isType(value, "object");
+  isType(value, 'object');
 
   // Note: Please remember that null is always a valid value for properties
   // held by a CacheableObject. This assertion is exclusively for use in other
@@ -127,7 +127,7 @@ export function isObject(value) {
 }
 
 export function isArray(value) {
-  if (typeof value !== "object" || value === null || !Array.isArray(value))
+  if (typeof value !== 'object' || value === null || !Array.isArray(value))
     throw new TypeError(`Expected an array, got ${value}`);
 
   return true;
@@ -156,7 +156,7 @@ export function validateArrayItems(itemValidator) {
   return (array) => {
     isArray(array);
 
-    withAggregate({ message: "Errors validating array items" }, ({ wrap }) => {
+    withAggregate({message: 'Errors validating array items'}, ({wrap}) => {
       array.forEach(wrap(fn));
     });
 
@@ -173,7 +173,7 @@ export function validateInstanceOf(constructor) {
 export function isColor(color) {
   isStringNonEmpty(color);
 
-  if (color.startsWith("#")) {
+  if (color.startsWith('#')) {
     if (![1 + 3, 1 + 4, 1 + 6, 1 + 8].includes(color.length))
       throw new TypeError(
         `Expected #rgb, #rgba, #rrggbb, or #rrggbbaa, got length ${color.length}`
@@ -192,7 +192,7 @@ export function isCommentary(commentary) {
   return isString(commentary);
 }
 
-const isArtistRef = validateReference("artist");
+const isArtistRef = validateReference('artist');
 
 export function validateProperties(spec) {
   const specEntries = Object.entries(spec);
@@ -205,8 +205,8 @@ export function validateProperties(spec) {
       throw new TypeError(`Expected an object, got array`);
 
     withAggregate(
-      { message: `Errors validating object properties` },
-      ({ call }) => {
+      {message: `Errors validating object properties`},
+      ({call}) => {
         for (const [specKey, specValidator] of specEntries) {
           call(() => {
             const value = object[specKey];
@@ -229,7 +229,7 @@ export function validateProperties(spec) {
             throw new Error(
               `Unknown keys present (${
                 unknownKeys.length
-              }): [${unknownKeys.join(", ")}]`
+              }): [${unknownKeys.join(', ')}]`
             );
           });
         }
@@ -273,7 +273,7 @@ export function isDimensions(dimensions) {
 export function isDirectory(directory) {
   isStringNonEmpty(directory);
 
-  if (directory.match(/[^a-zA-Z0-9_\-]/))
+  if (directory.match(/[^a-zA-Z0-9_-]/))
     throw new TypeError(
       `Expected only letters, numbers, dash, and underscore, got "${directory}"`
     );
@@ -291,7 +291,7 @@ export function isDuration(duration) {
 export function isFileExtension(string) {
   isStringNonEmpty(string);
 
-  if (string[0] === ".")
+  if (string[0] === '.')
     throw new TypeError(`Expected no dot (.) at the start of file extension`);
 
   if (string.match(/[^a-zA-Z0-9_]/))
@@ -321,7 +321,7 @@ export function isURL(string) {
   return true;
 }
 
-export function validateReference(type = "track") {
+export function validateReference(type = 'track') {
   return (ref) => {
     isStringNonEmpty(ref);
 
@@ -332,7 +332,7 @@ export function validateReference(type = "track") {
     if (!match) throw new TypeError(`Malformed reference`);
 
     const {
-      groups: { typePart, directoryPart },
+      groups: {typePart, directoryPart},
     } = match;
 
     if (typePart && typePart !== type)
@@ -348,7 +348,7 @@ export function validateReference(type = "track") {
   };
 }
 
-export function validateReferenceList(type = "") {
+export function validateReferenceList(type = '') {
   return validateArrayItems(validateReference(type));
 }
 
diff --git a/src/data/yaml.js b/src/data/yaml.js
index 5058bb3..a425576 100644
--- a/src/data/yaml.js
+++ b/src/data/yaml.js
@@ -1,13 +1,13 @@
-// @format
-//
+/** @format */
+
 // yaml.js - specification for HSMusic YAML data file format and utilities for
 // loading and processing YAML files and documents
 
-import * as path from "path";
-import yaml from "js-yaml";
+import * as path from 'path';
+import yaml from 'js-yaml';
 
-import { readFile } from "fs/promises";
-import { inspect as nodeInspect } from "util";
+import {readFile} from 'fs/promises';
+import {inspect as nodeInspect} from 'util';
 
 import {
   Album,
@@ -19,16 +19,15 @@ import {
   GroupCategory,
   HomepageLayout,
   HomepageLayoutAlbumsRow,
-  HomepageLayoutRow,
   NewsEntry,
   StaticPage,
   Thing,
   Track,
   TrackGroup,
   WikiInfo,
-} from "./things.js";
+} from './things.js';
 
-import { color, ENABLE_COLOR, logInfo, logWarn } from "../util/cli.js";
+import {color, ENABLE_COLOR, logInfo, logWarn} from '../util/cli.js';
 
 import {
   decorateErrorWithIndex,
@@ -36,36 +35,36 @@ import {
   openAggregate,
   showAggregate,
   withAggregate,
-} from "../util/sugar.js";
+} from '../util/sugar.js';
 
 import {
   sortAlbumsTracksChronologically,
   sortAlphabetically,
   sortChronologically,
-} from "../util/wiki-data.js";
+} from '../util/wiki-data.js';
 
-import find, { bindFind } from "../util/find.js";
-import { findFiles } from "../util/io.js";
+import find, {bindFind} from '../util/find.js';
+import {findFiles} from '../util/io.js';
 
 // --> General supporting stuff
 
 function inspect(value) {
-  return nodeInspect(value, { colors: ENABLE_COLOR });
+  return nodeInspect(value, {colors: ENABLE_COLOR});
 }
 
 // --> YAML data repository structure constants
 
-export const WIKI_INFO_FILE = "wiki-info.yaml";
-export const BUILD_DIRECTIVE_DATA_FILE = "build-directives.yaml";
-export const HOMEPAGE_LAYOUT_DATA_FILE = "homepage.yaml";
-export const ARTIST_DATA_FILE = "artists.yaml";
-export const FLASH_DATA_FILE = "flashes.yaml";
-export const NEWS_DATA_FILE = "news.yaml";
-export const ART_TAG_DATA_FILE = "tags.yaml";
-export const GROUP_DATA_FILE = "groups.yaml";
-export const STATIC_PAGE_DATA_FILE = "static-pages.yaml";
+export const WIKI_INFO_FILE = 'wiki-info.yaml';
+export const BUILD_DIRECTIVE_DATA_FILE = 'build-directives.yaml';
+export const HOMEPAGE_LAYOUT_DATA_FILE = 'homepage.yaml';
+export const ARTIST_DATA_FILE = 'artists.yaml';
+export const FLASH_DATA_FILE = 'flashes.yaml';
+export const NEWS_DATA_FILE = 'news.yaml';
+export const ART_TAG_DATA_FILE = 'tags.yaml';
+export const GROUP_DATA_FILE = 'groups.yaml';
+export const STATIC_PAGE_DATA_FILE = 'static-pages.yaml';
 
-export const DATA_ALBUM_DIRECTORY = "album";
+export const DATA_ALBUM_DIRECTORY = 'album';
 
 // --> Document processing functions
 
@@ -119,7 +118,7 @@ function makeProcessDocument(
   );
 
   const decorateErrorWithName = (fn) => {
-    const nameField = propertyFieldMapping["name"];
+    const nameField = propertyFieldMapping['name'];
     if (!nameField) return fn;
 
     return (document) => {
@@ -168,8 +167,8 @@ function makeProcessDocument(
     const thing = Reflect.construct(thingClass, []);
 
     withAggregate(
-      { message: `Errors applying ${color.green(thingClass.name)} properties` },
-      ({ call }) => {
+      {message: `Errors applying ${color.green(thingClass.name)} properties`},
+      ({call}) => {
         for (const [property, value] of Object.entries(sourceProperties)) {
           call(() => (thing[property] = value));
         }
@@ -184,7 +183,7 @@ makeProcessDocument.UnknownFieldsError = class UnknownFieldsError extends (
   Error
 ) {
   constructor(fields) {
-    super(`Unknown fields present: ${fields.join(", ")}`);
+    super(`Unknown fields present: ${fields.join(', ')}`);
     this.fields = fields;
   }
 };
@@ -192,72 +191,72 @@ makeProcessDocument.UnknownFieldsError = class UnknownFieldsError extends (
 export const processAlbumDocument = makeProcessDocument(Album, {
   fieldTransformations: {
     Artists: parseContributors,
-    "Cover Artists": parseContributors,
-    "Default Track Cover Artists": parseContributors,
-    "Wallpaper Artists": parseContributors,
-    "Banner Artists": parseContributors,
+    'Cover Artists': parseContributors,
+    'Default Track Cover Artists': parseContributors,
+    'Wallpaper Artists': parseContributors,
+    'Banner Artists': parseContributors,
 
     Date: (value) => new Date(value),
-    "Date Added": (value) => new Date(value),
-    "Cover Art Date": (value) => new Date(value),
-    "Default Track Cover Art Date": (value) => new Date(value),
+    'Date Added': (value) => new Date(value),
+    'Cover Art Date': (value) => new Date(value),
+    'Default Track Cover Art Date': (value) => new Date(value),
 
-    "Banner Dimensions": parseDimensions,
+    'Banner Dimensions': parseDimensions,
 
-    "Additional Files": parseAdditionalFiles,
+    'Additional Files': parseAdditionalFiles,
   },
 
   propertyFieldMapping: {
-    name: "Album",
+    name: 'Album',
 
-    color: "Color",
-    directory: "Directory",
-    urls: "URLs",
+    color: 'Color',
+    directory: 'Directory',
+    urls: 'URLs',
 
-    artistContribsByRef: "Artists",
-    coverArtistContribsByRef: "Cover Artists",
-    trackCoverArtistContribsByRef: "Default Track Cover Artists",
+    artistContribsByRef: 'Artists',
+    coverArtistContribsByRef: 'Cover Artists',
+    trackCoverArtistContribsByRef: 'Default Track Cover Artists',
 
-    coverArtFileExtension: "Cover Art File Extension",
-    trackCoverArtFileExtension: "Track Art File Extension",
+    coverArtFileExtension: 'Cover Art File Extension',
+    trackCoverArtFileExtension: 'Track Art File Extension',
 
-    wallpaperArtistContribsByRef: "Wallpaper Artists",
-    wallpaperStyle: "Wallpaper Style",
-    wallpaperFileExtension: "Wallpaper File Extension",
+    wallpaperArtistContribsByRef: 'Wallpaper Artists',
+    wallpaperStyle: 'Wallpaper Style',
+    wallpaperFileExtension: 'Wallpaper File Extension',
 
-    bannerArtistContribsByRef: "Banner Artists",
-    bannerStyle: "Banner Style",
-    bannerFileExtension: "Banner File Extension",
-    bannerDimensions: "Banner Dimensions",
+    bannerArtistContribsByRef: 'Banner Artists',
+    bannerStyle: 'Banner Style',
+    bannerFileExtension: 'Banner File Extension',
+    bannerDimensions: 'Banner Dimensions',
 
-    date: "Date",
-    trackArtDate: "Default Track Cover Art Date",
-    coverArtDate: "Cover Art Date",
-    dateAddedToWiki: "Date Added",
+    date: 'Date',
+    trackArtDate: 'Default Track Cover Art Date',
+    coverArtDate: 'Cover Art Date',
+    dateAddedToWiki: 'Date Added',
 
-    hasCoverArt: "Has Cover Art",
-    hasTrackArt: "Has Track Art",
-    hasTrackNumbers: "Has Track Numbers",
-    isMajorRelease: "Major Release",
-    isListedOnHomepage: "Listed on Homepage",
+    hasCoverArt: 'Has Cover Art',
+    hasTrackArt: 'Has Track Art',
+    hasTrackNumbers: 'Has Track Numbers',
+    isMajorRelease: 'Major Release',
+    isListedOnHomepage: 'Listed on Homepage',
 
-    groupsByRef: "Groups",
-    artTagsByRef: "Art Tags",
-    commentary: "Commentary",
+    groupsByRef: 'Groups',
+    artTagsByRef: 'Art Tags',
+    commentary: 'Commentary',
 
-    additionalFiles: "Additional Files",
+    additionalFiles: 'Additional Files',
   },
 });
 
 export const processTrackGroupDocument = makeProcessDocument(TrackGroup, {
   fieldTransformations: {
-    "Date Originally Released": (value) => new Date(value),
+    'Date Originally Released': (value) => new Date(value),
   },
 
   propertyFieldMapping: {
-    name: "Group",
-    color: "Color",
-    dateOriginallyReleased: "Date Originally Released",
+    name: 'Group',
+    color: 'Color',
+    dateOriginallyReleased: 'Date Originally Released',
   },
 });
 
@@ -265,60 +264,60 @@ export const processTrackDocument = makeProcessDocument(Track, {
   fieldTransformations: {
     Duration: getDurationInSeconds,
 
-    "Date First Released": (value) => new Date(value),
-    "Cover Art Date": (value) => new Date(value),
+    'Date First Released': (value) => new Date(value),
+    'Cover Art Date': (value) => new Date(value),
 
     Artists: parseContributors,
     Contributors: parseContributors,
-    "Cover Artists": parseContributors,
+    'Cover Artists': parseContributors,
 
-    "Additional Files": parseAdditionalFiles,
+    'Additional Files': parseAdditionalFiles,
   },
 
   propertyFieldMapping: {
-    name: "Track",
+    name: 'Track',
 
-    directory: "Directory",
-    duration: "Duration",
-    urls: "URLs",
+    directory: 'Directory',
+    duration: 'Duration',
+    urls: 'URLs',
 
-    coverArtDate: "Cover Art Date",
-    coverArtFileExtension: "Cover Art File Extension",
-    dateFirstReleased: "Date First Released",
-    hasCoverArt: "Has Cover Art",
-    hasURLs: "Has URLs",
+    coverArtDate: 'Cover Art Date',
+    coverArtFileExtension: 'Cover Art File Extension',
+    dateFirstReleased: 'Date First Released',
+    hasCoverArt: 'Has Cover Art',
+    hasURLs: 'Has URLs',
 
-    referencedTracksByRef: "Referenced Tracks",
-    artistContribsByRef: "Artists",
-    contributorContribsByRef: "Contributors",
-    coverArtistContribsByRef: "Cover Artists",
-    artTagsByRef: "Art Tags",
-    originalReleaseTrackByRef: "Originally Released As",
+    referencedTracksByRef: 'Referenced Tracks',
+    artistContribsByRef: 'Artists',
+    contributorContribsByRef: 'Contributors',
+    coverArtistContribsByRef: 'Cover Artists',
+    artTagsByRef: 'Art Tags',
+    originalReleaseTrackByRef: 'Originally Released As',
 
-    commentary: "Commentary",
-    lyrics: "Lyrics",
+    commentary: 'Commentary',
+    lyrics: 'Lyrics',
 
-    additionalFiles: "Additional Files",
+    additionalFiles: 'Additional Files',
   },
 
-  ignoredFields: ["Sampled Tracks"],
+  ignoredFields: ['Sampled Tracks'],
 });
 
 export const processArtistDocument = makeProcessDocument(Artist, {
   propertyFieldMapping: {
-    name: "Artist",
+    name: 'Artist',
 
-    directory: "Directory",
-    urls: "URLs",
-    hasAvatar: "Has Avatar",
-    avatarFileExtension: "Avatar File Extension",
+    directory: 'Directory',
+    urls: 'URLs',
+    hasAvatar: 'Has Avatar',
+    avatarFileExtension: 'Avatar File Extension',
 
-    aliasNames: "Aliases",
+    aliasNames: 'Aliases',
 
-    contextNotes: "Context Notes",
+    contextNotes: 'Context Notes',
   },
 
-  ignoredFields: ["Dead URLs"],
+  ignoredFields: ['Dead URLs'],
 });
 
 export const processFlashDocument = makeProcessDocument(Flash, {
@@ -329,26 +328,26 @@ export const processFlashDocument = makeProcessDocument(Flash, {
   },
 
   propertyFieldMapping: {
-    name: "Flash",
+    name: 'Flash',
 
-    directory: "Directory",
-    page: "Page",
-    date: "Date",
-    coverArtFileExtension: "Cover Art File Extension",
+    directory: 'Directory',
+    page: 'Page',
+    date: 'Date',
+    coverArtFileExtension: 'Cover Art File Extension',
 
-    featuredTracksByRef: "Featured Tracks",
-    contributorContribsByRef: "Contributors",
-    urls: "URLs",
+    featuredTracksByRef: 'Featured Tracks',
+    contributorContribsByRef: 'Contributors',
+    urls: 'URLs',
   },
 });
 
 export const processFlashActDocument = makeProcessDocument(FlashAct, {
   propertyFieldMapping: {
-    name: "Act",
-    color: "Color",
-    anchor: "Anchor",
-    jump: "Jump",
-    jumpColor: "Jump Color",
+    name: 'Act',
+    color: 'Color',
+    anchor: 'Anchor',
+    jump: 'Jump',
+    jumpColor: 'Jump Color',
   },
 });
 
@@ -358,66 +357,66 @@ export const processNewsEntryDocument = makeProcessDocument(NewsEntry, {
   },
 
   propertyFieldMapping: {
-    name: "Name",
-    directory: "Directory",
-    date: "Date",
-    content: "Content",
+    name: 'Name',
+    directory: 'Directory',
+    date: 'Date',
+    content: 'Content',
   },
 });
 
 export const processArtTagDocument = makeProcessDocument(ArtTag, {
   propertyFieldMapping: {
-    name: "Tag",
-    directory: "Directory",
-    color: "Color",
-    isContentWarning: "Is CW",
+    name: 'Tag',
+    directory: 'Directory',
+    color: 'Color',
+    isContentWarning: 'Is CW',
   },
 });
 
 export const processGroupDocument = makeProcessDocument(Group, {
   propertyFieldMapping: {
-    name: "Group",
-    directory: "Directory",
-    description: "Description",
-    urls: "URLs",
+    name: 'Group',
+    directory: 'Directory',
+    description: 'Description',
+    urls: 'URLs',
   },
 });
 
 export const processGroupCategoryDocument = makeProcessDocument(GroupCategory, {
   propertyFieldMapping: {
-    name: "Category",
-    color: "Color",
+    name: 'Category',
+    color: 'Color',
   },
 });
 
 export const processStaticPageDocument = makeProcessDocument(StaticPage, {
   propertyFieldMapping: {
-    name: "Name",
-    nameShort: "Short Name",
-    directory: "Directory",
+    name: 'Name',
+    nameShort: 'Short Name',
+    directory: 'Directory',
 
-    content: "Content",
-    stylesheet: "Style",
+    content: 'Content',
+    stylesheet: 'Style',
 
-    showInNavigationBar: "Show in Navigation Bar",
+    showInNavigationBar: 'Show in Navigation Bar',
   },
 });
 
 export const processWikiInfoDocument = makeProcessDocument(WikiInfo, {
   propertyFieldMapping: {
-    name: "Name",
-    nameShort: "Short Name",
-    color: "Color",
-    description: "Description",
-    footerContent: "Footer Content",
-    defaultLanguage: "Default Language",
-    canonicalBase: "Canonical Base",
-    divideTrackListsByGroupsByRef: "Divide Track Lists By Groups",
-    enableFlashesAndGames: "Enable Flashes & Games",
-    enableListings: "Enable Listings",
-    enableNews: "Enable News",
-    enableArtTagUI: "Enable Art Tag UI",
-    enableGroupUI: "Enable Group UI",
+    name: 'Name',
+    nameShort: 'Short Name',
+    color: 'Color',
+    description: 'Description',
+    footerContent: 'Footer Content',
+    defaultLanguage: 'Default Language',
+    canonicalBase: 'Canonical Base',
+    divideTrackListsByGroupsByRef: 'Divide Track Lists By Groups',
+    enableFlashesAndGames: 'Enable Flashes & Games',
+    enableListings: 'Enable Listings',
+    enableNews: 'Enable News',
+    enableArtTagUI: 'Enable Art Tag UI',
+    enableGroupUI: 'Enable Group UI',
   },
 });
 
@@ -425,10 +424,10 @@ export const processHomepageLayoutDocument = makeProcessDocument(
   HomepageLayout,
   {
     propertyFieldMapping: {
-      sidebarContent: "Sidebar Content",
+      sidebarContent: 'Sidebar Content',
     },
 
-    ignoredFields: ["Homepage"],
+    ignoredFields: ['Homepage'],
   }
 );
 
@@ -437,9 +436,9 @@ export function makeProcessHomepageLayoutRowDocument(rowClass, spec) {
     ...spec,
 
     propertyFieldMapping: {
-      name: "Row",
-      color: "Color",
-      type: "Type",
+      name: 'Row',
+      color: 'Color',
+      type: 'Type',
       ...spec.propertyFieldMapping,
     },
   });
@@ -448,16 +447,16 @@ export function makeProcessHomepageLayoutRowDocument(rowClass, spec) {
 export const homepageLayoutRowTypeProcessMapping = {
   albums: makeProcessHomepageLayoutRowDocument(HomepageLayoutAlbumsRow, {
     propertyFieldMapping: {
-      sourceGroupByRef: "Group",
-      countAlbumsFromGroup: "Count",
-      sourceAlbumsByRef: "Albums",
-      actionLinks: "Actions",
+      sourceGroupByRef: 'Group',
+      countAlbumsFromGroup: 'Count',
+      sourceAlbumsByRef: 'Albums',
+      actionLinks: 'Actions',
     },
   }),
 };
 
 export function processHomepageLayoutRowDocument(document) {
-  const type = document["Type"];
+  const type = document['Type'];
 
   const match = Object.entries(homepageLayoutRowTypeProcessMapping).find(
     ([key]) => key === type
@@ -473,15 +472,15 @@ export function processHomepageLayoutRowDocument(document) {
 // --> Utilities shared across document parsing functions
 
 export function getDurationInSeconds(string) {
-  if (typeof string === "number") {
+  if (typeof string === 'number') {
     return string;
   }
 
-  if (typeof string !== "string") {
+  if (typeof string !== 'string') {
     throw new TypeError(`Expected a string or number, got ${string}`);
   }
 
-  const parts = string.split(":").map((n) => parseInt(n));
+  const parts = string.split(':').map((n) => parseInt(n));
   if (parts.length === 3) {
     return parts[0] * 3600 + parts[1] * 60 + parts[2];
   } else if (parts.length === 2) {
@@ -499,16 +498,16 @@ export function parseAdditionalFiles(array) {
   }
 
   return array.map((item) => ({
-    title: item["Title"],
-    description: item["Description"] ?? null,
-    files: item["Files"],
+    title: item['Title'],
+    description: item['Description'] ?? null,
+    files: item['Files'],
   }));
 }
 
 export function parseCommentary(text) {
   if (text) {
-    const lines = String(text).split("\n");
-    if (!lines[0].replace(/<\/b>/g, "").includes(":</i>")) {
+    const lines = String(text).split('\n');
+    if (!lines[0].replace(/<\/b>/g, '').includes(':</i>')) {
       return {
         error: `An entry is missing commentary citation: "${lines[0].slice(
           0,
@@ -527,7 +526,7 @@ export function parseContributors(contributors) {
     return null;
   }
 
-  if (contributors.length === 1 && contributors[0].startsWith("<i>")) {
+  if (contributors.length === 1 && contributors[0].startsWith('<i>')) {
     const arr = [];
     arr.textContent = contributors[0];
     return arr;
@@ -542,17 +541,17 @@ export function parseContributors(contributors) {
     }
     const who = match[1];
     const what = match[3] || null;
-    return { who, what };
+    return {who, what};
   });
 
-  const badContributor = contributors.find((val) => typeof val === "string");
+  const badContributor = contributors.find((val) => typeof val === 'string');
   if (badContributor) {
     return {
       error: `An entry has an incorrectly formatted contributor, "${badContributor}".`,
     };
   }
 
-  if (contributors.length === 1 && contributors[0].who === "none") {
+  if (contributors.length === 1 && contributors[0].who === 'none') {
     return null;
   }
 
@@ -584,7 +583,7 @@ export const documentModes = {
   // processDocument function. Obviously, each specified data file should only
   // contain one YAML document (an error will be thrown otherwise). Calls save
   // with an array of processed documents (wiki objects).
-  onePerFile: Symbol("Document mode: onePerFile"),
+  onePerFile: Symbol('Document mode: onePerFile'),
 
   // headerAndEntries: One or more documents per file; the first document is
   // treated as a "header" and represents data which pertains to all following
@@ -599,12 +598,12 @@ export const documentModes = {
   // aggregate. However, if the processHeaderDocument function fails, all
   // following documents in the same file will be ignored as well (i.e. an
   // entire file will be excempt from the save() function's input).
-  headerAndEntries: Symbol("Document mode: headerAndEntries"),
+  headerAndEntries: Symbol('Document mode: headerAndEntries'),
 
   // allInOne: One or more documents, all contained in one file. Expects file
   // string (or function) and processDocument function. Calls save with an
   // array of processed documents (wiki objects).
-  allInOne: Symbol("Document mode: allInOne"),
+  allInOne: Symbol('Document mode: allInOne'),
 
   // oneDocumentTotal: Just a single document, represented in one file.
   // Expects file string (or function) and processDocument function. Calls
@@ -614,7 +613,7 @@ export const documentModes = {
   // function won't be called at all, generally resulting in an altogether
   // missing property from the global wikiData object. This should be caught
   // and handled externally.
-  oneDocumentTotal: Symbol("Document mode: oneDocumentTotal"),
+  oneDocumentTotal: Symbol('Document mode: oneDocumentTotal'),
 };
 
 // dataSteps: Top-level array of "steps" for loading YAML document files.
@@ -662,7 +661,7 @@ export const dataSteps = [
         return;
       }
 
-      return { wikiInfo };
+      return {wikiInfo};
     },
   },
 
@@ -671,7 +670,7 @@ export const dataSteps = [
     files: async (dataPath) =>
       (
         await findFiles(path.join(dataPath, DATA_ALBUM_DIRECTORY), {
-          filter: (f) => path.extname(f) === ".yaml",
+          filter: (f) => path.extname(f) === '.yaml',
           joinParentDirectory: false,
         })
       ).map((file) => path.join(DATA_ALBUM_DIRECTORY, file)),
@@ -679,7 +678,7 @@ export const dataSteps = [
     documentMode: documentModes.headerAndEntries,
     processHeaderDocument: processAlbumDocument,
     processEntryDocument(document) {
-      return "Group" in document
+      return 'Group' in document
         ? processTrackGroupDocument(document)
         : processTrackDocument(document);
     },
@@ -688,7 +687,7 @@ export const dataSteps = [
       const albumData = [];
       const trackData = [];
 
-      for (const { header: album, entries } of results) {
+      for (const {header: album, entries} of results) {
         // We can't mutate an array once it's set as a property
         // value, so prepare the tracks and track groups that will
         // show up in a track list all the way before actually
@@ -699,7 +698,7 @@ export const dataSteps = [
 
         const albumRef = Thing.getReference(album);
 
-        function closeCurrentTrackGroup() {
+        const closeCurrentTrackGroup = () => {
           if (currentTracksByRef) {
             let trackGroup;
 
@@ -715,7 +714,7 @@ export const dataSteps = [
             trackGroup.tracksByRef = currentTracksByRef;
             trackGroups.push(trackGroup);
           }
-        }
+        };
 
         for (const entry of entries) {
           if (entry instanceof TrackGroup) {
@@ -743,7 +742,7 @@ export const dataSteps = [
         albumData.push(album);
       }
 
-      return { albumData, trackData };
+      return {albumData, trackData};
     },
   },
 
@@ -771,7 +770,7 @@ export const dataSteps = [
         );
       });
 
-      return { artistData, artistAliasData };
+      return {artistData, artistAliasData};
     },
   },
 
@@ -782,7 +781,7 @@ export const dataSteps = [
 
     documentMode: documentModes.allInOne,
     processDocument(document) {
-      return "Act" in document
+      return 'Act' in document
         ? processFlashActDocument(document)
         : processFlashDocument(document);
     },
@@ -798,7 +797,7 @@ export const dataSteps = [
       for (const thing of results) {
         if (thing instanceof FlashAct) {
           if (flashAct) {
-            Object.assign(flashAct, { flashesByRef });
+            Object.assign(flashAct, {flashesByRef});
           }
 
           flashAct = thing;
@@ -809,13 +808,13 @@ export const dataSteps = [
       }
 
       if (flashAct) {
-        Object.assign(flashAct, { flashesByRef });
+        Object.assign(flashAct, {flashesByRef});
       }
 
       const flashData = results.filter((x) => x instanceof Flash);
       const flashActData = results.filter((x) => x instanceof FlashAct);
 
-      return { flashData, flashActData };
+      return {flashData, flashActData};
     },
   },
 
@@ -825,7 +824,7 @@ export const dataSteps = [
 
     documentMode: documentModes.allInOne,
     processDocument(document) {
-      return "Category" in document
+      return 'Category' in document
         ? processGroupCategoryDocument(document)
         : processGroupDocument(document);
     },
@@ -841,7 +840,7 @@ export const dataSteps = [
       for (const thing of results) {
         if (thing instanceof GroupCategory) {
           if (groupCategory) {
-            Object.assign(groupCategory, { groupsByRef });
+            Object.assign(groupCategory, {groupsByRef});
           }
 
           groupCategory = thing;
@@ -852,7 +851,7 @@ export const dataSteps = [
       }
 
       if (groupCategory) {
-        Object.assign(groupCategory, { groupsByRef });
+        Object.assign(groupCategory, {groupsByRef});
       }
 
       const groupData = results.filter((x) => x instanceof Group);
@@ -860,7 +859,7 @@ export const dataSteps = [
         (x) => x instanceof GroupCategory
       );
 
-      return { groupData, groupCategoryData };
+      return {groupData, groupCategoryData};
     },
   },
 
@@ -877,9 +876,9 @@ export const dataSteps = [
         return;
       }
 
-      const { header: homepageLayout, entries: rows } = results[0];
-      Object.assign(homepageLayout, { rows });
-      return { homepageLayout };
+      const {header: homepageLayout, entries: rows} = results[0];
+      Object.assign(homepageLayout, {rows});
+      return {homepageLayout};
     },
   },
 
@@ -895,7 +894,7 @@ export const dataSteps = [
       sortChronologically(newsData);
       newsData.reverse();
 
-      return { newsData };
+      return {newsData};
     },
   },
 
@@ -909,7 +908,7 @@ export const dataSteps = [
     save(artTagData) {
       sortAlphabetically(artTagData);
 
-      return { artTagData };
+      return {artTagData};
     },
   },
 
@@ -921,12 +920,12 @@ export const dataSteps = [
     processDocument: processStaticPageDocument,
 
     save(staticPageData) {
-      return { staticPageData };
+      return {staticPageData};
     },
   },
 ];
 
-export async function loadAndProcessDataDocuments({ dataPath }) {
+export async function loadAndProcessDataDocuments({dataPath}) {
   const processDataAggregate = openAggregate({
     message: `Errors processing data files`,
   });
@@ -938,7 +937,7 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
         return fn(x, index, array);
       } catch (error) {
         error.message +=
-          (error.message.includes("\n") ? "\n" : " ") +
+          (error.message.includes('\n') ? '\n' : ' ') +
           `(file: ${color.bright(
             color.blue(path.relative(dataPath, x.file))
           )})`;
@@ -949,9 +948,9 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
 
   for (const dataStep of dataSteps) {
     await processDataAggregate.nestAsync(
-      { message: `Errors during data step: ${dataStep.title}` },
-      async ({ call, callAsync, map, mapAsync, nest }) => {
-        const { documentMode } = dataStep;
+      {message: `Errors during data step: ${dataStep.title}`},
+      async ({call, callAsync, map, mapAsync, nest}) => {
+        const {documentMode} = dataStep;
 
         if (!Object.values(documentModes).includes(documentMode)) {
           throw new Error(`Invalid documentMode: ${documentMode.toString()}`);
@@ -969,12 +968,12 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
 
           const file = path.join(
             dataPath,
-            typeof dataStep.file === "function"
+            typeof dataStep.file === 'function'
               ? await callAsync(dataStep.file, dataPath)
               : dataStep.file
           );
 
-          const readResult = await callAsync(readFile, file, "utf-8");
+          const readResult = await callAsync(readFile, file, 'utf-8');
 
           if (!readResult) {
             return;
@@ -992,14 +991,14 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
           let processResults;
 
           if (documentMode === documentModes.oneDocumentTotal) {
-            nest({ message: `Errors processing document` }, ({ call }) => {
+            nest({message: `Errors processing document`}, ({call}) => {
               processResults = call(dataStep.processDocument, yamlResult);
             });
           } else {
-            const { result, aggregate } = mapAggregate(
+            const {result, aggregate} = mapAggregate(
               yamlResult,
               decorateErrorWithIndex(dataStep.processDocument),
-              { message: `Errors processing documents` }
+              {message: `Errors processing documents`}
             );
             processResults = result;
             call(aggregate.close);
@@ -1023,7 +1022,7 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
         }
 
         const files = (
-          typeof dataStep.files === "function"
+          typeof dataStep.files === 'function'
             ? await callAsync(dataStep.files, dataPath)
             : dataStep.files
         ).map((file) => path.join(dataPath, file));
@@ -1031,35 +1030,35 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
         const readResults = await mapAsync(
           files,
           (file) =>
-            readFile(file, "utf-8").then((contents) => ({ file, contents })),
-          { message: `Errors reading data files` }
+            readFile(file, 'utf-8').then((contents) => ({file, contents})),
+          {message: `Errors reading data files`}
         );
 
         const yamlResults = map(
           readResults,
-          decorateErrorWithFile(({ file, contents }) => ({
+          decorateErrorWithFile(({file, contents}) => ({
             file,
             documents: yaml.loadAll(contents),
           })),
-          { message: `Errors parsing data files as valid YAML` }
+          {message: `Errors parsing data files as valid YAML`}
         );
 
         let processResults;
 
         if (documentMode === documentModes.headerAndEntries) {
           nest(
-            { message: `Errors processing data files as valid documents` },
-            ({ call, map }) => {
+            {message: `Errors processing data files as valid documents`},
+            ({call, map}) => {
               processResults = [];
 
-              yamlResults.forEach(({ file, documents }) => {
+              yamlResults.forEach(({file, documents}) => {
                 const [headerDocument, ...entryDocuments] = documents;
 
                 const header = call(
-                  decorateErrorWithFile(({ document }) =>
+                  decorateErrorWithFile(({document}) =>
                     dataStep.processHeaderDocument(document)
                   ),
-                  { file, document: headerDocument }
+                  {file, document: headerDocument}
                 );
 
                 // Don't continue processing files whose header
@@ -1070,13 +1069,13 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
                 }
 
                 const entries = map(
-                  entryDocuments.map((document) => ({ file, document })),
+                  entryDocuments.map((document) => ({file, document})),
                   decorateErrorWithFile(
-                    decorateErrorWithIndex(({ document }) =>
+                    decorateErrorWithIndex(({document}) =>
                       dataStep.processEntryDocument(document)
                     )
                   ),
-                  { message: `Errors processing entry documents` }
+                  {message: `Errors processing entry documents`}
                 );
 
                 // Entries may be incomplete (i.e. any errored
@@ -1084,7 +1083,7 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
                 // represented here) - this is intentional! By
                 // principle, partial output is preferred over
                 // erroring an entire file.
-                processResults.push({ header, entries });
+                processResults.push({header, entries});
               });
             }
           );
@@ -1092,11 +1091,11 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
 
         if (documentMode === documentModes.onePerFile) {
           nest(
-            { message: `Errors processing data files as valid documents` },
-            ({ call, map }) => {
+            {message: `Errors processing data files as valid documents`},
+            ({call}) => {
               processResults = [];
 
-              yamlResults.forEach(({ file, documents }) => {
+              yamlResults.forEach(({file, documents}) => {
                 if (documents.length > 1) {
                   call(
                     decorateErrorWithFile(() => {
@@ -1109,10 +1108,10 @@ export async function loadAndProcessDataDocuments({ dataPath }) {
                 }
 
                 const result = call(
-                  decorateErrorWithFile(({ document }) =>
+                  decorateErrorWithFile(({document}) =>
                     dataStep.processDocument(document)
                   ),
-                  { file, document: documents[0] }
+                  {file, document: documents[0]}
                 );
 
                 if (!result) {
@@ -1155,40 +1154,40 @@ export function linkWikiDataArrays(wikiData) {
 
   const WD = wikiData;
 
-  assignWikiData([WD.wikiInfo], "groupData");
+  assignWikiData([WD.wikiInfo], 'groupData');
 
   assignWikiData(
     WD.albumData,
-    "artistData",
-    "artTagData",
-    "groupData",
-    "trackData"
+    'artistData',
+    'artTagData',
+    'groupData',
+    'trackData'
   );
   WD.albumData.forEach((album) =>
-    assignWikiData(album.trackGroups, "trackData")
+    assignWikiData(album.trackGroups, 'trackData')
   );
 
   assignWikiData(
     WD.trackData,
-    "albumData",
-    "artistData",
-    "artTagData",
-    "flashData",
-    "trackData"
+    'albumData',
+    'artistData',
+    'artTagData',
+    'flashData',
+    'trackData'
   );
   assignWikiData(
     WD.artistData,
-    "albumData",
-    "artistData",
-    "flashData",
-    "trackData"
+    'albumData',
+    'artistData',
+    'flashData',
+    'trackData'
   );
-  assignWikiData(WD.groupData, "albumData", "groupCategoryData");
-  assignWikiData(WD.groupCategoryData, "groupData");
-  assignWikiData(WD.flashData, "artistData", "flashActData", "trackData");
-  assignWikiData(WD.flashActData, "flashData");
-  assignWikiData(WD.artTagData, "albumData", "trackData");
-  assignWikiData(WD.homepageLayout.rows, "albumData", "groupData");
+  assignWikiData(WD.groupData, 'albumData', 'groupCategoryData');
+  assignWikiData(WD.groupCategoryData, 'groupData');
+  assignWikiData(WD.flashData, 'artistData', 'flashActData', 'trackData');
+  assignWikiData(WD.flashActData, 'flashData');
+  assignWikiData(WD.artTagData, 'albumData', 'trackData');
+  assignWikiData(WD.homepageLayout.rows, 'albumData', 'groupData');
 }
 
 export function sortWikiDataArrays(wikiData) {
@@ -1213,28 +1212,28 @@ export function sortWikiDataArrays(wikiData) {
 // build, for example).
 export function filterDuplicateDirectories(wikiData) {
   const deduplicateSpec = [
-    "albumData",
-    "artTagData",
-    "flashData",
-    "groupData",
-    "newsData",
-    "trackData",
+    'albumData',
+    'artTagData',
+    'flashData',
+    'groupData',
+    'newsData',
+    'trackData',
   ];
 
-  const aggregate = openAggregate({ message: `Duplicate directories found` });
+  const aggregate = openAggregate({message: `Duplicate directories found`});
   for (const thingDataProp of deduplicateSpec) {
     const thingData = wikiData[thingDataProp];
     aggregate.nest(
       {
         message: `Duplicate directories found in ${color.green(
-          "wikiData." + thingDataProp
+          'wikiData.' + thingDataProp
         )}`,
       },
-      ({ call }) => {
+      ({call}) => {
         const directoryPlaces = Object.create(null);
         const duplicateDirectories = [];
         for (const thing of thingData) {
-          const { directory } = thing;
+          const {directory} = thing;
           if (directory in directoryPlaces) {
             directoryPlaces[directory].push(thing);
             duplicateDirectories.push(directory);
@@ -1253,7 +1252,7 @@ export function filterDuplicateDirectories(wikiData) {
           call(() => {
             throw new Error(
               `Duplicate directory ${color.green(directory)}:\n` +
-                places.map((thing) => ` - ` + inspect(thing)).join("\n")
+                places.map((thing) => ` - ` + inspect(thing)).join('\n')
             );
           });
         }
@@ -1292,64 +1291,64 @@ export function filterDuplicateDirectories(wikiData) {
 export function filterReferenceErrors(wikiData) {
   const referenceSpec = [
     [
-      "wikiInfo",
+      'wikiInfo',
       {
-        divideTrackListsByGroupsByRef: "group",
+        divideTrackListsByGroupsByRef: 'group',
       },
     ],
 
     [
-      "albumData",
+      'albumData',
       {
-        artistContribsByRef: "_contrib",
-        coverArtistContribsByRef: "_contrib",
-        trackCoverArtistContribsByRef: "_contrib",
-        wallpaperArtistContribsByRef: "_contrib",
-        bannerArtistContribsByRef: "_contrib",
-        groupsByRef: "group",
-        artTagsByRef: "artTag",
+        artistContribsByRef: '_contrib',
+        coverArtistContribsByRef: '_contrib',
+        trackCoverArtistContribsByRef: '_contrib',
+        wallpaperArtistContribsByRef: '_contrib',
+        bannerArtistContribsByRef: '_contrib',
+        groupsByRef: 'group',
+        artTagsByRef: 'artTag',
       },
     ],
 
     [
-      "trackData",
+      'trackData',
       {
-        artistContribsByRef: "_contrib",
-        contributorContribsByRef: "_contrib",
-        coverArtistContribsByRef: "_contrib",
-        referencedTracksByRef: "track",
-        artTagsByRef: "artTag",
-        originalReleaseTrackByRef: "track",
+        artistContribsByRef: '_contrib',
+        contributorContribsByRef: '_contrib',
+        coverArtistContribsByRef: '_contrib',
+        referencedTracksByRef: 'track',
+        artTagsByRef: 'artTag',
+        originalReleaseTrackByRef: 'track',
       },
     ],
 
     [
-      "groupCategoryData",
+      'groupCategoryData',
       {
-        groupsByRef: "group",
+        groupsByRef: 'group',
       },
     ],
 
     [
-      "homepageLayout.rows",
+      'homepageLayout.rows',
       {
-        sourceGroupsByRef: "group",
-        sourceAlbumsByRef: "album",
+        sourceGroupsByRef: 'group',
+        sourceAlbumsByRef: 'album',
       },
     ],
 
     [
-      "flashData",
+      'flashData',
       {
-        contributorContribsByRef: "_contrib",
-        featuredTracksByRef: "track",
+        contributorContribsByRef: '_contrib',
+        featuredTracksByRef: 'track',
       },
     ],
 
     [
-      "flashActData",
+      'flashActData',
       {
-        flashesByRef: "flash",
+        flashesByRef: 'flash',
       },
     ],
   ];
@@ -1364,35 +1363,35 @@ export function filterReferenceErrors(wikiData) {
   const aggregate = openAggregate({
     message: `Errors validating between-thing references in data`,
   });
-  const boundFind = bindFind(wikiData, { mode: "error" });
+  const boundFind = bindFind(wikiData, {mode: 'error'});
   for (const [thingDataProp, propSpec] of referenceSpec) {
     const thingData = getNestedProp(wikiData, thingDataProp);
     aggregate.nest(
       {
         message: `Reference errors in ${color.green(
-          "wikiData." + thingDataProp
+          'wikiData.' + thingDataProp
         )}`,
       },
-      ({ nest }) => {
+      ({nest}) => {
         const things = Array.isArray(thingData) ? thingData : [thingData];
         for (const thing of things) {
           nest(
-            { message: `Reference errors in ${inspect(thing)}` },
-            ({ filter }) => {
+            {message: `Reference errors in ${inspect(thing)}`},
+            ({filter}) => {
               for (const [property, findFnKey] of Object.entries(propSpec)) {
                 if (!thing[property]) continue;
-                if (findFnKey === "_contrib") {
+                if (findFnKey === '_contrib') {
                   thing[property] = filter(
                     thing[property],
-                    decorateErrorWithIndex(({ who }) => {
+                    decorateErrorWithIndex(({who}) => {
                       const alias = find.artist(who, wikiData.artistAliasData, {
-                        mode: "quiet",
+                        mode: 'quiet',
                       });
                       if (alias) {
                         const original = find.artist(
                           alias.aliasedArtistRef,
                           wikiData.artistData,
-                          { mode: "quiet" }
+                          {mode: 'quiet'}
                         );
                         throw new Error(
                           `Reference ${color.red(
@@ -1407,7 +1406,7 @@ export function filterReferenceErrors(wikiData) {
                     {
                       message: `Reference errors in contributions ${color.green(
                         property
-                      )} (${color.green("find.artist")})`,
+                      )} (${color.green('find.artist')})`,
                     }
                   );
                   continue;
@@ -1421,7 +1420,7 @@ export function filterReferenceErrors(wikiData) {
                     {
                       message: `Reference errors in property ${color.green(
                         property
-                      )} (${color.green("find." + findFnKey)})`,
+                      )} (${color.green('find.' + findFnKey)})`,
                     }
                   );
                 } else {
@@ -1429,9 +1428,9 @@ export function filterReferenceErrors(wikiData) {
                     {
                       message: `Reference error in property ${color.green(
                         property
-                      )} (${color.green("find." + findFnKey)})`,
+                      )} (${color.green('find.' + findFnKey)})`,
                     },
-                    ({ call }) => {
+                    ({call}) => {
                       try {
                         call(findFn, value);
                       } catch (error) {
@@ -1460,14 +1459,14 @@ export function filterReferenceErrors(wikiData) {
 // main wiki build process.
 export async function quickLoadAllFromYAML(
   dataPath,
-  { showAggregate: customShowAggregate = showAggregate } = {}
+  {showAggregate: customShowAggregate = showAggregate} = {}
 ) {
   const showAggregate = customShowAggregate;
 
   let wikiData;
 
   {
-    const { aggregate, result } = await loadAndProcessDataDocuments({
+    const {aggregate, result} = await loadAndProcessDataDocuments({
       dataPath,
     });
 
diff --git a/src/file-size-preloader.js b/src/file-size-preloader.js
index e34cee5..363fb4c 100644
--- a/src/file-size-preloader.js
+++ b/src/file-size-preloader.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // Very simple, bare-bones file size loader which takes a bunch of file
 // paths, gets their filesizes, and resolves a promise when it's done.
 //
@@ -19,8 +19,8 @@
 // This only processes files one at a time because I'm lazy and stat calls
 // are very, very fast.
 
-import { stat } from "fs/promises";
-import { logWarn } from "./util/cli.js";
+import {stat} from 'fs/promises';
+import {logWarn} from './util/cli.js';
 
 export default class FileSizePreloader {
   #paths = [];
diff --git a/src/gen-thumbs.js b/src/gen-thumbs.js
index cd7196d..82ddb34 100644
--- a/src/gen-thumbs.js
+++ b/src/gen-thumbs.js
@@ -1,7 +1,6 @@
 #!/usr/bin/env node
+/** @format */
 
-// @format
-//
 // Ok, so the d8te is 3 March 2021, and the music wiki was initially released
 // on 15 November 2019. That is 474 days or 11376 hours. In my opinion, and
 // pro8a8ly the opinions of at least one other person, that is WAY TOO LONG
@@ -74,18 +73,18 @@
 // unused). This is just to make the code more porta8le and sta8le, long-term,
 // since it avoids a lot of otherwise implic8ted maintenance.
 
-"use strict";
+'use strict';
 
-const CACHE_FILE = "thumbnail-cache.json";
+const CACHE_FILE = 'thumbnail-cache.json';
 const WARNING_DELAY_TIME = 10000;
 
-import { spawn } from "child_process";
-import { createHash } from "crypto";
-import * as path from "path";
+import {spawn} from 'child_process';
+import {createHash} from 'crypto';
+import * as path from 'path';
 
-import { readdir, readFile, writeFile } from "fs/promises"; // Whatcha know! Nice.
+import {readdir, readFile, writeFile} from 'fs/promises'; // Whatcha know! Nice.
 
-import { createReadStream } from "fs"; // Still gotta import from 8oth tho, for createReadStream.
+import {createReadStream} from 'fs'; // Still gotta import from 8oth tho, for createReadStream.
 
 import {
   logError,
@@ -93,15 +92,15 @@ import {
   logWarn,
   parseOptions,
   progressPromiseAll,
-} from "./util/cli.js";
+} from './util/cli.js';
 
-import { commandExists, isMain, promisifyProcess } from "./util/node-utils.js";
+import {commandExists, isMain, promisifyProcess} from './util/node-utils.js';
 
-import { delay, queue } from "./util/sugar.js";
+import {delay, queue} from './util/sugar.js';
 
 function traverse(
   startDirPath,
-  { filterFile = () => true, filterDir = () => true } = {}
+  {filterFile = () => true, filterDir = () => true} = {}
 ) {
   const recursive = (names, subDirPath) =>
     Promise.all(
@@ -116,24 +115,24 @@ function traverse(
       )
     ).then((pathArrays) => pathArrays.flatMap((x) => x));
 
-  return readdir(startDirPath).then((names) => recursive(names, ""));
+  return readdir(startDirPath).then((names) => recursive(names, ''));
 }
 
 function readFileMD5(filePath) {
   return new Promise((resolve, reject) => {
-    const md5 = createHash("md5");
+    const md5 = createHash('md5');
     const stream = createReadStream(filePath);
-    stream.on("data", (data) => md5.update(data));
-    stream.on("end", (data) => resolve(md5.digest("hex")));
-    stream.on("error", (err) => reject(err));
+    stream.on('data', (data) => md5.update(data));
+    stream.on('end', (data) => resolve(md5.digest('hex')));
+    stream.on('error', (err) => reject(err));
   });
 }
 
 async function getImageMagickVersion(spawnConvert) {
-  const proc = spawnConvert(["--version"], false);
+  const proc = spawnConvert(['--version'], false);
 
-  let allData = "";
-  proc.stdout.on("data", (data) => {
+  let allData = '';
+  proc.stdout.on('data', (data) => {
     allData += data.toString();
   });
 
@@ -145,7 +144,7 @@ async function getImageMagickVersion(spawnConvert) {
 
   const match = allData.match(/Version: (.*)/i);
   if (!match) {
-    return "unknown version";
+    return 'unknown version';
   }
 
   return match[1];
@@ -153,13 +152,13 @@ async function getImageMagickVersion(spawnConvert) {
 
 async function getSpawnConvert() {
   let fn, description, version;
-  if (await commandExists("convert")) {
-    fn = (args) => spawn("convert", args);
-    description = "convert";
-  } else if (await commandExists("magick")) {
+  if (await commandExists('convert')) {
+    fn = (args) => spawn('convert', args);
+    description = 'convert';
+  } else if (await commandExists('magick')) {
     fn = (args, prefix = true) =>
-      spawn("magick", prefix ? ["convert", ...args] : args);
-    description = "magick convert";
+      spawn('magick', prefix ? ['convert', ...args] : args);
+    description = 'magick convert';
   } else {
     return [`no convert or magick binary`, null];
   }
@@ -173,28 +172,28 @@ async function getSpawnConvert() {
   return [`${description} (${version})`, fn];
 }
 
-function generateImageThumbnails(filePath, { spawnConvert }) {
+function generateImageThumbnails(filePath, {spawnConvert}) {
   const dirname = path.dirname(filePath);
   const extname = path.extname(filePath);
   const basename = path.basename(filePath, extname);
-  const output = (name) => path.join(dirname, basename + name + ".jpg");
+  const output = (name) => path.join(dirname, basename + name + '.jpg');
 
-  const convert = (name, { size, quality }) =>
+  const convert = (name, {size, quality}) =>
     spawnConvert([
       filePath,
-      "-strip",
-      "-resize",
+      '-strip',
+      '-resize',
       `${size}x${size}>`,
-      "-interlace",
-      "Plane",
-      "-quality",
+      '-interlace',
+      'Plane',
+      '-quality',
       `${quality}%`,
       output(name),
     ]);
 
   return Promise.all([
-    promisifyProcess(convert(".medium", { size: 400, quality: 95 }), false),
-    promisifyProcess(convert(".small", { size: 250, quality: 85 }), false),
+    promisifyProcess(convert('.medium', {size: 400, quality: 95}), false),
+    promisifyProcess(convert('.small', {size: 250, quality: 85}), false),
   ]);
 
   return new Promise((resolve, reject) => {
@@ -208,10 +207,10 @@ function generateImageThumbnails(filePath, { spawnConvert }) {
 
 export default async function genThumbs(
   mediaPath,
-  { queueSize = 0, quiet = false } = {}
+  {queueSize = 0, quiet = false} = {}
 ) {
   if (!mediaPath) {
-    throw new Error("Expected mediaPath to be passed");
+    throw new Error('Expected mediaPath to be passed');
   }
 
   const quietInfo = quiet ? () => null : logInfo;
@@ -221,16 +220,16 @@ export default async function genThumbs(
     // thumbnail-cache.json is 8eing passed through, for some reason.
 
     const ext = path.extname(name);
-    if (ext !== ".jpg" && ext !== ".png") return false;
+    if (ext !== '.jpg' && ext !== '.png') return false;
 
     const rest = path.basename(name, ext);
-    if (rest.endsWith(".medium") || rest.endsWith(".small")) return false;
+    if (rest.endsWith('.medium') || rest.endsWith('.small')) return false;
 
     return true;
   };
 
   const filterDir = (name) => {
-    if (name === ".git") return false;
+    if (name === '.git') return false;
     return true;
   };
 
@@ -242,7 +241,7 @@ export default async function genThumbs(
     logInfo`You can find info to help install ImageMagick on Linux, Windows, or macOS`;
     logInfo`from its official website: ${`https://imagemagick.org/script/download.php`}`;
     logInfo`If you have trouble working ImageMagick and would like some help, feel free`;
-    logInfo`to drop a message in the HSMusic Discord server! ${"https://hsmusic.wiki/discord/"}`;
+    logInfo`to drop a message in the HSMusic Discord server! ${'https://hsmusic.wiki/discord/'}`;
     return false;
   } else {
     logInfo`Found ImageMagick binary: ${convertInfo}`;
@@ -256,7 +255,7 @@ export default async function genThumbs(
     quietInfo`Cache file successfully read.`;
   } catch (error) {
     cache = {};
-    if (error.code === "ENOENT") {
+    if (error.code === 'ENOENT') {
       firstRun = true;
     } else {
       failedReadingCache = true;
@@ -283,7 +282,7 @@ export default async function genThumbs(
     await delay(WARNING_DELAY_TIME);
   }
 
-  const imagePaths = await traverse(mediaPath, { filterFile, filterDir });
+  const imagePaths = await traverse(mediaPath, {filterFile, filterDir});
 
   const imageToMD5Entries = await progressPromiseAll(
     `Generating MD5s of image files`,
@@ -292,7 +291,7 @@ export default async function genThumbs(
         (imagePath) => () =>
           readFileMD5(path.join(mediaPath, imagePath)).then(
             (md5) => [imagePath, md5],
-            (error) => [imagePath, { error }]
+            (error) => [imagePath, {error}]
           )
       ),
       queueSize
@@ -385,24 +384,24 @@ export default async function genThumbs(
 if (isMain(import.meta.url)) {
   (async function () {
     const miscOptions = await parseOptions(process.argv.slice(2), {
-      "media-path": {
-        type: "value",
+      'media-path': {
+        type: 'value',
       },
-      "queue-size": {
-        type: "value",
+      'queue-size': {
+        type: 'value',
         validate(size) {
-          if (parseInt(size) !== parseFloat(size)) return "an integer";
-          if (parseInt(size) < 0) return "a counting number or zero";
+          if (parseInt(size) !== parseFloat(size)) return 'an integer';
+          if (parseInt(size) < 0) return 'a counting number or zero';
           return true;
         },
       },
-      queue: { alias: "queue-size" },
+      queue: {alias: 'queue-size'},
     });
 
-    const mediaPath = miscOptions["media-path"] || process.env.HSMUSIC_MEDIA;
-    const queueSize = +(miscOptions["queue-size"] ?? 0);
+    const mediaPath = miscOptions['media-path'] || process.env.HSMUSIC_MEDIA;
+    const queueSize = +(miscOptions['queue-size'] ?? 0);
 
-    await genThumbs(mediaPath, { queueSize });
+    await genThumbs(mediaPath, {queueSize});
   })().catch((err) => {
     console.error(err);
   });
diff --git a/src/listing-spec.js b/src/listing-spec.js
index f561cef..d5341a5 100644
--- a/src/listing-spec.js
+++ b/src/listing-spec.js
@@ -1,6 +1,6 @@
-// @format
+/** @format */
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
 import {
   chunkByProperties,
@@ -8,55 +8,55 @@ import {
   getTotalDuration,
   sortAlphabetically,
   sortChronologically,
-} from "./util/wiki-data.js";
+} from './util/wiki-data.js';
 
 const listingSpec = [
   {
-    directory: "albums/by-name",
-    stringsKey: "listAlbums.byName",
+    directory: 'albums/by-name',
+    stringsKey: 'listAlbums.byName',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return sortAlphabetically(wikiData.albumData.slice());
     },
 
-    row(album, { link, language }) {
-      return language.$("listingPage.listAlbums.byName.item", {
+    row(album, {link, language}) {
+      return language.$('listingPage.listAlbums.byName.item', {
         album: link.album(album),
-        tracks: language.countTracks(album.tracks.length, { unit: true }),
+        tracks: language.countTracks(album.tracks.length, {unit: true}),
       });
     },
   },
 
   {
-    directory: "albums/by-tracks",
-    stringsKey: "listAlbums.byTracks",
+    directory: 'albums/by-tracks',
+    stringsKey: 'listAlbums.byTracks',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.albumData
         .slice()
         .sort((a, b) => b.tracks.length - a.tracks.length);
     },
 
-    row(album, { link, language }) {
-      return language.$("listingPage.listAlbums.byTracks.item", {
+    row(album, {link, language}) {
+      return language.$('listingPage.listAlbums.byTracks.item', {
         album: link.album(album),
-        tracks: language.countTracks(album.tracks.length, { unit: true }),
+        tracks: language.countTracks(album.tracks.length, {unit: true}),
       });
     },
   },
 
   {
-    directory: "albums/by-duration",
-    stringsKey: "listAlbums.byDuration",
+    directory: 'albums/by-duration',
+    stringsKey: 'listAlbums.byDuration',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.albumData
-        .map((album) => ({ album, duration: getTotalDuration(album.tracks) }))
+        .map((album) => ({album, duration: getTotalDuration(album.tracks)}))
         .sort((a, b) => b.duration - a.duration);
     },
 
-    row({ album, duration }, { link, language }) {
-      return language.$("listingPage.listAlbums.byDuration.item", {
+    row({album, duration}, {link, language}) {
+      return language.$('listingPage.listAlbums.byDuration.item', {
         album: link.album(album),
         duration: language.formatDuration(duration),
       });
@@ -64,17 +64,17 @@ const listingSpec = [
   },
 
   {
-    directory: "albums/by-date",
-    stringsKey: "listAlbums.byDate",
+    directory: 'albums/by-date',
+    stringsKey: 'listAlbums.byDate',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return sortChronologically(
         wikiData.albumData.filter((album) => album.date)
       );
     },
 
-    row(album, { link, language }) {
-      return language.$("listingPage.listAlbums.byDate.item", {
+    row(album, {link, language}) {
+      return language.$('listingPage.listAlbums.byDate.item', {
         album: link.album(album),
         date: language.formatDate(album.date),
       });
@@ -82,10 +82,10 @@ const listingSpec = [
   },
 
   {
-    directory: "albums/by-date-added",
-    stringsKey: "listAlbums.byDateAdded",
+    directory: 'albums/by-date-added',
+    stringsKey: 'listAlbums.byDateAdded',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return chunkByProperties(
         wikiData.albumData
           .filter((a) => a.dateAddedToWiki)
@@ -93,18 +93,18 @@ const listingSpec = [
             if (a.dateAddedToWiki < b.dateAddedToWiki) return -1;
             if (a.dateAddedToWiki > b.dateAddedToWiki) return 1;
           }),
-        ["dateAddedToWiki"]
+        ['dateAddedToWiki']
       );
     },
 
-    html(chunks, { link, language }) {
+    html(chunks, {link, language}) {
       return fixWS`
                 <dl>
                     ${chunks
                       .map(
-                        ({ dateAddedToWiki, chunk: albums }) => fixWS`
+                        ({dateAddedToWiki, chunk: albums}) => fixWS`
                         <dt>${language.$(
-                          "listingPage.listAlbums.byDateAdded.date",
+                          'listingPage.listAlbums.byDateAdded.date',
                           {
                             date: language.formatDate(dateAddedToWiki),
                           }
@@ -113,36 +113,36 @@ const listingSpec = [
                             ${albums
                               .map((album) =>
                                 language.$(
-                                  "listingPage.listAlbums.byDateAdded.album",
+                                  'listingPage.listAlbums.byDateAdded.album',
                                   {
                                     album: link.album(album),
                                   }
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </ul></dd>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 </dl>
             `;
     },
   },
 
   {
-    directory: "artists/by-name",
-    stringsKey: "listArtists.byName",
+    directory: 'artists/by-name',
+    stringsKey: 'listArtists.byName',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return sortAlphabetically(wikiData.artistData.slice()).map((artist) => ({
         artist,
         contributions: getArtistNumContributions(artist),
       }));
     },
 
-    row({ artist, contributions }, { link, language }) {
-      return language.$("listingPage.listArtists.byName.item", {
+    row({artist, contributions}, {link, language}) {
+      return language.$('listingPage.listArtists.byName.item', {
         artist: link.artist(artist),
         contributions: language.countContributions(contributions, {
           unit: true,
@@ -152,10 +152,10 @@ const listingSpec = [
   },
 
   {
-    directory: "artists/by-contribs",
-    stringsKey: "listArtists.byContribs",
+    directory: 'artists/by-contribs',
+    stringsKey: 'listArtists.byContribs',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return {
         toTracks: wikiData.artistData
           .map((artist) => ({
@@ -165,7 +165,7 @@ const listingSpec = [
               (artist.tracksAsArtist?.length ?? 0),
           }))
           .sort((a, b) => b.contributions - a.contributions)
-          .filter(({ contributions }) => contributions),
+          .filter(({contributions}) => contributions),
 
         toArtAndFlashes: wikiData.artistData
           .map((artist) => ({
@@ -180,7 +180,7 @@ const listingSpec = [
                 : 0),
           }))
           .sort((a, b) => b.contributions - a.contributions)
-          .filter(({ contributions }) => contributions),
+          .filter(({contributions}) => contributions),
 
         // This is a kinda naughty hack, 8ut like, it's the only place
         // we'd 8e passing wikiData to html() otherwise, so like....
@@ -189,54 +189,54 @@ const listingSpec = [
       };
     },
 
-    html({ toTracks, toArtAndFlashes, showAsFlashes }, { link, language }) {
+    html({toTracks, toArtAndFlashes, showAsFlashes}, {link, language}) {
       return fixWS`
                 <div class="content-columns">
                     <div class="column">
                         <h2>${language.$(
-                          "listingPage.misc.trackContributors"
+                          'listingPage.misc.trackContributors'
                         )}</h2>
                         <ul>
                             ${toTracks
-                              .map(({ artist, contributions }) =>
+                              .map(({artist, contributions}) =>
                                 language.$(
-                                  "listingPage.listArtists.byContribs.item",
+                                  'listingPage.listArtists.byContribs.item',
                                   {
                                     artist: link.artist(artist),
                                     contributions: language.countContributions(
                                       contributions,
-                                      { unit: true }
+                                      {unit: true}
                                     ),
                                   }
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                          </ul>
                     </div>
                     <div class="column">
                         <h2>${language.$(
-                          "listingPage.misc" +
+                          'listingPage.misc' +
                             (showAsFlashes
-                              ? ".artAndFlashContributors"
-                              : ".artContributors")
+                              ? '.artAndFlashContributors'
+                              : '.artContributors')
                         )}</h2>
                         <ul>
                             ${toArtAndFlashes
-                              .map(({ artist, contributions }) =>
+                              .map(({artist, contributions}) =>
                                 language.$(
-                                  "listingPage.listArtists.byContribs.item",
+                                  'listingPage.listArtists.byContribs.item',
                                   {
                                     artist: link.artist(artist),
                                     contributions: language.countContributions(
                                       contributions,
-                                      { unit: true }
+                                      {unit: true}
                                     ),
                                   }
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </ul>
                     </div>
                 </div>
@@ -245,10 +245,10 @@ const listingSpec = [
   },
 
   {
-    directory: "artists/by-commentary",
-    stringsKey: "listArtists.byCommentary",
+    directory: 'artists/by-commentary',
+    stringsKey: 'listArtists.byCommentary',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.artistData
         .map((artist) => ({
           artist,
@@ -256,23 +256,23 @@ const listingSpec = [
             (artist.tracksAsCommentator?.length ?? 0) +
             (artist.albumsAsCommentator?.length ?? 0),
         }))
-        .filter(({ entries }) => entries)
+        .filter(({entries}) => entries)
         .sort((a, b) => b.entries - a.entries);
     },
 
-    row({ artist, entries }, { link, language }) {
-      return language.$("listingPage.listArtists.byCommentary.item", {
+    row({artist, entries}, {link, language}) {
+      return language.$('listingPage.listArtists.byCommentary.item', {
         artist: link.artist(artist),
-        entries: language.countCommentaryEntries(entries, { unit: true }),
+        entries: language.countCommentaryEntries(entries, {unit: true}),
       });
     },
   },
 
   {
-    directory: "artists/by-duration",
-    stringsKey: "listArtists.byDuration",
+    directory: 'artists/by-duration',
+    stringsKey: 'listArtists.byDuration',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.artistData
         .map((artist) => ({
           artist,
@@ -281,12 +281,12 @@ const listingSpec = [
             ...(artist.tracksAsContributor ?? []),
           ]),
         }))
-        .filter(({ duration }) => duration > 0)
+        .filter(({duration}) => duration > 0)
         .sort((a, b) => b.duration - a.duration);
     },
 
-    row({ artist, duration }, { link, language }) {
-      return language.$("listingPage.listArtists.byDuration.item", {
+    row({artist, duration}, {link, language}) {
+      return language.$('listingPage.listArtists.byDuration.item', {
         artist: link.artist(artist),
         duration: language.formatDuration(duration),
       });
@@ -294,10 +294,10 @@ const listingSpec = [
   },
 
   {
-    directory: "artists/by-latest",
-    stringsKey: "listArtists.byLatest",
+    directory: 'artists/by-latest',
+    stringsKey: 'listArtists.byLatest',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       const reversedTracks = sortChronologically(
         wikiData.trackData.filter((t) => t.date)
       ).reverse();
@@ -318,10 +318,10 @@ const listingSpec = [
                 [
                   ...(track.artistContribs ?? []),
                   ...(track.contributorContribs ?? []),
-                ].some(({ who }) => who === artist)
+                ].some(({who}) => who === artist)
               )?.date,
             }))
-            .filter(({ date }) => date)
+            .filter(({date}) => date)
         ).reverse(),
 
         toArtAndFlashes: sortChronologically(
@@ -331,7 +331,7 @@ const listingSpec = [
                 [
                   ...(thing.coverArtistContribs ?? []),
                   ...((!thing.album && thing.contributorContribs) || []),
-                ].some(({ who }) => who === artist)
+                ].some(({who}) => who === artist)
               );
               return (
                 thing && {
@@ -339,7 +339,7 @@ const listingSpec = [
                   directory: artist.directory,
                   name: artist.name,
                   date: thing.coverArtistContribs?.some(
-                    ({ who }) => who === artist
+                    ({who}) => who === artist
                   )
                     ? thing.coverArtDate
                     : thing.date,
@@ -357,18 +357,18 @@ const listingSpec = [
       };
     },
 
-    html({ toTracks, toArtAndFlashes, showAsFlashes }, { link, language }) {
+    html({toTracks, toArtAndFlashes, showAsFlashes}, {link, language}) {
       return fixWS`
                 <div class="content-columns">
                     <div class="column">
                         <h2>${language.$(
-                          "listingPage.misc.trackContributors"
+                          'listingPage.misc.trackContributors'
                         )}</h2>
                         <ul>
                             ${toTracks
-                              .map(({ artist, date }) =>
+                              .map(({artist, date}) =>
                                 language.$(
-                                  "listingPage.listArtists.byLatest.item",
+                                  'listingPage.listArtists.byLatest.item',
                                   {
                                     artist: link.artist(artist),
                                     date: language.formatDate(date),
@@ -376,21 +376,21 @@ const listingSpec = [
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </ul>
                     </div>
                     <div class="column">
                         <h2>${language.$(
-                          "listingPage.misc" +
+                          'listingPage.misc' +
                             (showAsFlashes
-                              ? ".artAndFlashContributors"
-                              : ".artContributors")
+                              ? '.artAndFlashContributors'
+                              : '.artContributors')
                         )}</h2>
                         <ul>
                             ${toArtAndFlashes
-                              .map(({ artist, date }) =>
+                              .map(({artist, date}) =>
                                 language.$(
-                                  "listingPage.listArtists.byLatest.item",
+                                  'listingPage.listArtists.byLatest.item',
                                   {
                                     artist: link.artist(artist),
                                     date: language.formatDate(date),
@@ -398,7 +398,7 @@ const listingSpec = [
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </ul>
                     </div>
                 </div>
@@ -407,35 +407,35 @@ const listingSpec = [
   },
 
   {
-    directory: "groups/by-name",
-    stringsKey: "listGroups.byName",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI,
-    data: ({ wikiData }) => sortAlphabetically(wikiData.groupData.slice()),
+    directory: 'groups/by-name',
+    stringsKey: 'listGroups.byName',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI,
+    data: ({wikiData}) => sortAlphabetically(wikiData.groupData.slice()),
 
-    row(group, { link, language }) {
-      return language.$("listingPage.listGroups.byCategory.group", {
+    row(group, {link, language}) {
+      return language.$('listingPage.listGroups.byCategory.group', {
         group: link.groupInfo(group),
         gallery: link.groupGallery(group, {
-          text: language.$("listingPage.listGroups.byCategory.group.gallery"),
+          text: language.$('listingPage.listGroups.byCategory.group.gallery'),
         }),
       });
     },
   },
 
   {
-    directory: "groups/by-category",
-    stringsKey: "listGroups.byCategory",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI,
-    data: ({ wikiData }) => wikiData.groupCategoryData,
+    directory: 'groups/by-category',
+    stringsKey: 'listGroups.byCategory',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI,
+    data: ({wikiData}) => wikiData.groupCategoryData,
 
-    html(groupCategoryData, { link, language }) {
+    html(groupCategoryData, {link, language}) {
       return fixWS`
                 <dl>
                     ${groupCategoryData
                       .map(
                         (category) => fixWS`
                         <dt>${language.$(
-                          "listingPage.listGroups.byCategory.category",
+                          'listingPage.listGroups.byCategory.category',
                           {
                             category: link.groupInfo(category.groups[0], {
                               text: category.name,
@@ -446,53 +446,53 @@ const listingSpec = [
                             ${category.groups
                               .map((group) =>
                                 language.$(
-                                  "listingPage.listGroups.byCategory.group",
+                                  'listingPage.listGroups.byCategory.group',
                                   {
                                     group: link.groupInfo(group),
                                     gallery: link.groupGallery(group, {
                                       text: language.$(
-                                        "listingPage.listGroups.byCategory.group.gallery"
+                                        'listingPage.listGroups.byCategory.group.gallery'
                                       ),
                                     }),
                                   }
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </ul></dd>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 </dl>
             `;
     },
   },
 
   {
-    directory: "groups/by-albums",
-    stringsKey: "listGroups.byAlbums",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI,
+    directory: 'groups/by-albums',
+    stringsKey: 'listGroups.byAlbums',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI,
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.groupData
-        .map((group) => ({ group, albums: group.albums.length }))
+        .map((group) => ({group, albums: group.albums.length}))
         .sort((a, b) => b.albums - a.albums);
     },
 
-    row({ group, albums }, { link, language }) {
-      return language.$("listingPage.listGroups.byAlbums.item", {
+    row({group, albums}, {link, language}) {
+      return language.$('listingPage.listGroups.byAlbums.item', {
         group: link.groupInfo(group),
-        albums: language.countAlbums(albums, { unit: true }),
+        albums: language.countAlbums(albums, {unit: true}),
       });
     },
   },
 
   {
-    directory: "groups/by-tracks",
-    stringsKey: "listGroups.byTracks",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI,
+    directory: 'groups/by-tracks',
+    stringsKey: 'listGroups.byTracks',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI,
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.groupData
         .map((group) => ({
           group,
@@ -504,20 +504,20 @@ const listingSpec = [
         .sort((a, b) => b.tracks - a.tracks);
     },
 
-    row({ group, tracks }, { link, language }) {
-      return language.$("listingPage.listGroups.byTracks.item", {
+    row({group, tracks}, {link, language}) {
+      return language.$('listingPage.listGroups.byTracks.item', {
         group: link.groupInfo(group),
-        tracks: language.countTracks(tracks, { unit: true }),
+        tracks: language.countTracks(tracks, {unit: true}),
       });
     },
   },
 
   {
-    directory: "groups/by-duration",
-    stringsKey: "listGroups.byDuration",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI,
+    directory: 'groups/by-duration',
+    stringsKey: 'listGroups.byDuration',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI,
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.groupData
         .map((group) => ({
           group,
@@ -528,8 +528,8 @@ const listingSpec = [
         .sort((a, b) => b.duration - a.duration);
     },
 
-    row({ group, duration }, { link, language }) {
-      return language.$("listingPage.listGroups.byDuration.item", {
+    row({group, duration}, {link, language}) {
+      return language.$('listingPage.listGroups.byDuration.item', {
         group: link.groupInfo(group),
         duration: language.formatDuration(duration),
       });
@@ -537,11 +537,11 @@ const listingSpec = [
   },
 
   {
-    directory: "groups/by-latest-album",
-    stringsKey: "listGroups.byLatest",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI,
+    directory: 'groups/by-latest-album',
+    stringsKey: 'listGroups.byLatest',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI,
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return sortChronologically(
         wikiData.groupData
           .map((group) => {
@@ -572,8 +572,8 @@ const listingSpec = [
       ).reverse();
     },
 
-    row({ group, date }, { link, language }) {
-      return language.$("listingPage.listGroups.byLatest.item", {
+    row({group, date}, {link, language}) {
+      return language.$('listingPage.listGroups.byLatest.item', {
         group: link.groupInfo(group),
         date: language.formatDate(date),
       });
@@ -581,33 +581,33 @@ const listingSpec = [
   },
 
   {
-    directory: "tracks/by-name",
-    stringsKey: "listTracks.byName",
+    directory: 'tracks/by-name',
+    stringsKey: 'listTracks.byName',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return sortAlphabetically(wikiData.trackData.slice());
     },
 
-    row(track, { link, language }) {
-      return language.$("listingPage.listTracks.byName.item", {
+    row(track, {link, language}) {
+      return language.$('listingPage.listTracks.byName.item', {
         track: link.track(track),
       });
     },
   },
 
   {
-    directory: "tracks/by-album",
-    stringsKey: "listTracks.byAlbum",
-    data: ({ wikiData }) => wikiData.albumData,
+    directory: 'tracks/by-album',
+    stringsKey: 'listTracks.byAlbum',
+    data: ({wikiData}) => wikiData.albumData,
 
-    html(albumData, { link, language }) {
+    html(albumData, {link, language}) {
       return fixWS`
                 <dl>
                     ${albumData
                       .map(
                         (album) => fixWS`
                         <dt>${language.$(
-                          "listingPage.listTracks.byAlbum.album",
+                          'listingPage.listTracks.byAlbum.album',
                           {
                             album: link.album(album),
                           }
@@ -616,42 +616,42 @@ const listingSpec = [
                             ${album.tracks
                               .map((track) =>
                                 language.$(
-                                  "listingPage.listTracks.byAlbum.track",
+                                  'listingPage.listTracks.byAlbum.track',
                                   {
                                     track: link.track(track),
                                   }
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </ol></dd>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 </dl>
             `;
     },
   },
 
   {
-    directory: "tracks/by-date",
-    stringsKey: "listTracks.byDate",
+    directory: 'tracks/by-date',
+    stringsKey: 'listTracks.byDate',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return chunkByProperties(
         sortChronologically(wikiData.trackData.filter((t) => t.date)),
-        ["album", "date"]
+        ['album', 'date']
       );
     },
 
-    html(chunks, { link, language }) {
+    html(chunks, {link, language}) {
       return fixWS`
                 <dl>
                     ${chunks
                       .map(
-                        ({ album, date, chunk: tracks }) => fixWS`
+                        ({album, date, chunk: tracks}) => fixWS`
                         <dt>${language.$(
-                          "listingPage.listTracks.byDate.album",
+                          'listingPage.listTracks.byDate.album',
                           {
                             album: link.album(album),
                             date: language.formatDate(date),
@@ -662,41 +662,41 @@ const listingSpec = [
                               .map((track) =>
                                 track.aka
                                   ? `<li class="rerelease">${language.$(
-                                      "listingPage.listTracks.byDate.track.rerelease",
+                                      'listingPage.listTracks.byDate.track.rerelease',
                                       {
                                         track: link.track(track),
                                       }
                                     )}</li>`
                                   : `<li>${language.$(
-                                      "listingPage.listTracks.byDate.track",
+                                      'listingPage.listTracks.byDate.track',
                                       {
                                         track: link.track(track),
                                       }
                                     )}</li>`
                               )
-                              .join("\n")}
+                              .join('\n')}
                         </ul></dd>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 </dl>
             `;
     },
   },
 
   {
-    directory: "tracks/by-duration",
-    stringsKey: "listTracks.byDuration",
+    directory: 'tracks/by-duration',
+    stringsKey: 'listTracks.byDuration',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.trackData
-        .map((track) => ({ track, duration: track.duration }))
-        .filter(({ duration }) => duration > 0)
+        .map((track) => ({track, duration: track.duration}))
+        .filter(({duration}) => duration > 0)
         .sort((a, b) => b.duration - a.duration);
     },
 
-    row({ track, duration }, { link, language }) {
-      return language.$("listingPage.listTracks.byDuration.item", {
+    row({track, duration}, {link, language}) {
+      return language.$('listingPage.listTracks.byDuration.item', {
         track: link.track(track),
         duration: language.formatDuration(duration),
       });
@@ -704,10 +704,10 @@ const listingSpec = [
   },
 
   {
-    directory: "tracks/by-duration-in-album",
-    stringsKey: "listTracks.byDurationInAlbum",
+    directory: 'tracks/by-duration-in-album',
+    stringsKey: 'listTracks.byDurationInAlbum',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.albumData.map((album) => ({
         album,
         tracks: album.tracks
@@ -716,14 +716,14 @@ const listingSpec = [
       }));
     },
 
-    html(albums, { link, language }) {
+    html(albums, {link, language}) {
       return fixWS`
                 <dl>
                     ${albums
                       .map(
-                        ({ album, tracks }) => fixWS`
+                        ({album, tracks}) => fixWS`
                         <dt>${language.$(
-                          "listingPage.listTracks.byDurationInAlbum.album",
+                          'listingPage.listTracks.byDurationInAlbum.album',
                           {
                             album: link.album(album),
                           }
@@ -732,7 +732,7 @@ const listingSpec = [
                             ${tracks
                               .map((track) =>
                                 language.$(
-                                  "listingPage.listTracks.byDurationInAlbum.track",
+                                  'listingPage.listTracks.byDurationInAlbum.track',
                                   {
                                     track: link.track(track),
                                     duration: language.formatDuration(
@@ -742,32 +742,32 @@ const listingSpec = [
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </dd></ul>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 </dl>
             `;
     },
   },
 
   {
-    directory: "tracks/by-times-referenced",
-    stringsKey: "listTracks.byTimesReferenced",
+    directory: 'tracks/by-times-referenced',
+    stringsKey: 'listTracks.byTimesReferenced',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.trackData
         .map((track) => ({
           track,
           timesReferenced: track.referencedByTracks.length,
         }))
-        .filter(({ timesReferenced }) => timesReferenced > 0)
+        .filter(({timesReferenced}) => timesReferenced > 0)
         .sort((a, b) => b.timesReferenced - a.timesReferenced);
     },
 
-    row({ track, timesReferenced }, { link, language }) {
-      return language.$("listingPage.listTracks.byTimesReferenced.item", {
+    row({track, timesReferenced}, {link, language}) {
+      return language.$('listingPage.listTracks.byTimesReferenced.item', {
         track: link.track(track),
         timesReferenced: language.countTimesReferenced(timesReferenced, {
           unit: true,
@@ -777,25 +777,25 @@ const listingSpec = [
   },
 
   {
-    directory: "tracks/in-flashes/by-album",
-    stringsKey: "listTracks.inFlashes.byAlbum",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableFlashesAndGames,
+    directory: 'tracks/in-flashes/by-album',
+    stringsKey: 'listTracks.inFlashes.byAlbum',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableFlashesAndGames,
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return chunkByProperties(
         wikiData.trackData.filter((t) => t.featuredInFlashes?.length > 0),
-        ["album"]
+        ['album']
       );
     },
 
-    html(chunks, { link, language }) {
+    html(chunks, {link, language}) {
       return fixWS`
                 <dl>
                     ${chunks
                       .map(
-                        ({ album, chunk: tracks }) => fixWS`
+                        ({album, chunk: tracks}) => fixWS`
                         <dt>${language.$(
-                          "listingPage.listTracks.inFlashes.byAlbum.album",
+                          'listingPage.listTracks.inFlashes.byAlbum.album',
                           {
                             album: link.album(album),
                             date: language.formatDate(album.date),
@@ -805,7 +805,7 @@ const listingSpec = [
                             ${tracks
                               .map((track) =>
                                 language.$(
-                                  "listingPage.listTracks.inFlashes.byAlbum.track",
+                                  'listingPage.listTracks.inFlashes.byAlbum.track',
                                   {
                                     track: link.track(track),
                                     flashes: language.formatConjunctionList(
@@ -815,30 +815,30 @@ const listingSpec = [
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </dd></ul>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 </dl>
             `;
     },
   },
 
   {
-    directory: "tracks/in-flashes/by-flash",
-    stringsKey: "listTracks.inFlashes.byFlash",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableFlashesAndGames,
-    data: ({ wikiData }) => wikiData.flashData,
+    directory: 'tracks/in-flashes/by-flash',
+    stringsKey: 'listTracks.inFlashes.byFlash',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableFlashesAndGames,
+    data: ({wikiData}) => wikiData.flashData,
 
-    html(flashData, { link, language }) {
+    html(flashData, {link, language}) {
       return fixWS`
                 <dl>
                     ${sortChronologically(flashData.slice())
                       .map(
                         (flash) => fixWS`
                         <dt>${language.$(
-                          "listingPage.listTracks.inFlashes.byFlash.flash",
+                          'listingPage.listTracks.inFlashes.byFlash.flash',
                           {
                             flash: link.flash(flash),
                             date: language.formatDate(flash.date),
@@ -848,7 +848,7 @@ const listingSpec = [
                             ${flash.featuredTracks
                               .map((track) =>
                                 language.$(
-                                  "listingPage.listTracks.inFlashes.byFlash.track",
+                                  'listingPage.listTracks.inFlashes.byFlash.track',
                                   {
                                     track: link.track(track),
                                     album: link.album(track.album),
@@ -856,37 +856,37 @@ const listingSpec = [
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </ul></dd>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 </dl>
             `;
     },
   },
 
   {
-    directory: "tracks/with-lyrics",
-    stringsKey: "listTracks.withLyrics",
+    directory: 'tracks/with-lyrics',
+    stringsKey: 'listTracks.withLyrics',
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.albumData
         .map((album) => ({
           album,
           tracks: album.tracks.filter((t) => t.lyrics),
         }))
-        .filter(({ tracks }) => tracks.length > 0);
+        .filter(({tracks}) => tracks.length > 0);
     },
 
-    html(chunks, { link, language }) {
+    html(chunks, {link, language}) {
       return fixWS`
                 <dl>
                     ${chunks
                       .map(
-                        ({ album, tracks }) => fixWS`
+                        ({album, tracks}) => fixWS`
                         <dt>${language.$(
-                          "listingPage.listTracks.withLyrics.album",
+                          'listingPage.listTracks.withLyrics.album',
                           {
                             album: link.album(album),
                             date: language.formatDate(album.date),
@@ -896,74 +896,74 @@ const listingSpec = [
                             ${tracks
                               .map((track) =>
                                 language.$(
-                                  "listingPage.listTracks.withLyrics.track",
+                                  'listingPage.listTracks.withLyrics.track',
                                   {
                                     track: link.track(track),
                                   }
                                 )
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </dd></ul>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 </dl>
             `;
     },
   },
 
   {
-    directory: "tags/by-name",
-    stringsKey: "listTags.byName",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableArtTagUI,
+    directory: 'tags/by-name',
+    stringsKey: 'listTags.byName',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableArtTagUI,
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return sortAlphabetically(
         wikiData.artTagData.filter((tag) => !tag.isContentWarning)
-      ).map((tag) => ({ tag, timesUsed: tag.taggedInThings?.length }));
+      ).map((tag) => ({tag, timesUsed: tag.taggedInThings?.length}));
     },
 
-    row({ tag, timesUsed }, { link, language }) {
-      return language.$("listingPage.listTags.byName.item", {
+    row({tag, timesUsed}, {link, language}) {
+      return language.$('listingPage.listTags.byName.item', {
         tag: link.tag(tag),
-        timesUsed: language.countTimesUsed(timesUsed, { unit: true }),
+        timesUsed: language.countTimesUsed(timesUsed, {unit: true}),
       });
     },
   },
 
   {
-    directory: "tags/by-uses",
-    stringsKey: "listTags.byUses",
-    condition: ({ wikiData }) => wikiData.wikiInfo.enableArtTagUI,
+    directory: 'tags/by-uses',
+    stringsKey: 'listTags.byUses',
+    condition: ({wikiData}) => wikiData.wikiInfo.enableArtTagUI,
 
-    data({ wikiData }) {
+    data({wikiData}) {
       return wikiData.artTagData
         .filter((tag) => !tag.isContentWarning)
-        .map((tag) => ({ tag, timesUsed: tag.taggedInThings?.length }))
+        .map((tag) => ({tag, timesUsed: tag.taggedInThings?.length}))
         .sort((a, b) => b.timesUsed - a.timesUsed);
     },
 
-    row({ tag, timesUsed }, { link, language }) {
-      return language.$("listingPage.listTags.byUses.item", {
+    row({tag, timesUsed}, {link, language}) {
+      return language.$('listingPage.listTags.byUses.item', {
         tag: link.tag(tag),
-        timesUsed: language.countTimesUsed(timesUsed, { unit: true }),
+        timesUsed: language.countTimesUsed(timesUsed, {unit: true}),
       });
     },
   },
 
   {
-    directory: "random",
-    stringsKey: "other.randomPages",
+    directory: 'random',
+    stringsKey: 'other.randomPages',
 
-    data: ({ wikiData }) => ({
+    data: ({wikiData}) => ({
       officialAlbumData: wikiData.officialAlbumData,
       fandomAlbumData: wikiData.fandomAlbumData,
     }),
 
     html: (
-      { officialAlbumData, fandomAlbumData },
-      { getLinkThemeString, language }
+      {officialAlbumData, fandomAlbumData},
+      {getLinkThemeString, language}
     ) => fixWS`
             <p>Choose a link to go to a random page in that category or album! If your browser doesn't support relatively modern JavaScript or you've disabled it, these links won't work - sorry.</p>
             <p class="js-hide-once-data">(Data files are downloading in the background! Please wait for data to load.)</p>
@@ -980,14 +980,14 @@ const listingSpec = [
                 </ul></dd>
                 ${[
                   {
-                    name: "Official",
+                    name: 'Official',
                     albumData: officialAlbumData,
-                    code: "official",
+                    code: 'official',
                   },
                   {
-                    name: "Fandom",
+                    name: 'Fandom',
                     albumData: fandomAlbumData,
-                    code: "fandom",
+                    code: 'fandom',
                   },
                 ]
                   .map(
@@ -1009,10 +1009,10 @@ const listingSpec = [
                         }</a></li>
                     `
                       )
-                      .join("\n")}</ul></dd>
+                      .join('\n')}</ul></dd>
                 `
                   )
-                  .join("\n")}
+                  .join('\n')}
             </dl>
         `,
   },
@@ -1023,29 +1023,29 @@ const filterListings = (directoryPrefix) =>
 
 const listingTargetSpec = [
   {
-    title: ({ language }) => language.$("listingPage.target.album"),
-    listings: filterListings("album"),
+    title: ({language}) => language.$('listingPage.target.album'),
+    listings: filterListings('album'),
   },
   {
-    title: ({ language }) => language.$("listingPage.target.artist"),
-    listings: filterListings("artist"),
+    title: ({language}) => language.$('listingPage.target.artist'),
+    listings: filterListings('artist'),
   },
   {
-    title: ({ language }) => language.$("listingPage.target.group"),
-    listings: filterListings("group"),
+    title: ({language}) => language.$('listingPage.target.group'),
+    listings: filterListings('group'),
   },
   {
-    title: ({ language }) => language.$("listingPage.target.track"),
-    listings: filterListings("track"),
+    title: ({language}) => language.$('listingPage.target.track'),
+    listings: filterListings('track'),
   },
   {
-    title: ({ language }) => language.$("listingPage.target.tag"),
-    listings: filterListings("tag"),
+    title: ({language}) => language.$('listingPage.target.tag'),
+    listings: filterListings('tag'),
   },
   {
-    title: ({ language }) => language.$("listingPage.target.other"),
-    listings: [listingSpec.find((l) => l.directory === "random")],
+    title: ({language}) => language.$('listingPage.target.other'),
+    listings: [listingSpec.find((l) => l.directory === 'random')],
   },
 ];
 
-export { listingSpec, listingTargetSpec };
+export {listingSpec, listingTargetSpec};
diff --git a/src/misc-templates.js b/src/misc-templates.js
index f0e0edf..27f7018 100644
--- a/src/misc-templates.js
+++ b/src/misc-templates.js
@@ -1,37 +1,37 @@
-// @format
-//
+/** @format */
+
 // Miscellaneous utility functions which are useful across page specifications.
 // These are made available right on a page spec's ({wikiData, language, ...})
 // args object!
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
-import * as html from "./util/html.js";
+import * as html from './util/html.js';
 
-import { Track, Album } from "./data/things.js";
+import {Track, Album} from './data/things.js';
 
-import { getColors } from "./util/colors.js";
+import {getColors} from './util/colors.js';
 
-import { unique } from "./util/sugar.js";
+import {unique} from './util/sugar.js';
 
 import {
   getTotalDuration,
   sortAlbumsTracksChronologically,
   sortChronologically,
-} from "./util/wiki-data.js";
+} from './util/wiki-data.js';
 
-const BANDCAMP_DOMAINS = ["bc.s3m.us", "music.solatrux.com"];
+const BANDCAMP_DOMAINS = ['bc.s3m.us', 'music.solatrux.com'];
 
-const MASTODON_DOMAINS = ["types.pl"];
+const MASTODON_DOMAINS = ['types.pl'];
 
 // "Additional Files" listing
 
-export function generateAdditionalFilesShortcut(additionalFiles, { language }) {
-  if (!additionalFiles?.length) return "";
+export function generateAdditionalFilesShortcut(additionalFiles, {language}) {
+  if (!additionalFiles?.length) return '';
 
-  return language.$("releaseInfo.additionalFiles.shortcut", {
+  return language.$('releaseInfo.additionalFiles.shortcut', {
     anchorLink: `<a href="#additional-files">${language.$(
-      "releaseInfo.additionalFiles.shortcut.anchorLink"
+      'releaseInfo.additionalFiles.shortcut.anchorLink'
     )}</a>`,
     titles: language.formatUnitList(additionalFiles.map((g) => g.title)),
   });
@@ -39,15 +39,15 @@ export function generateAdditionalFilesShortcut(additionalFiles, { language }) {
 
 export function generateAdditionalFilesList(
   additionalFiles,
-  { language, getFileSize, linkFile }
+  {language, getFileSize, linkFile}
 ) {
-  if (!additionalFiles?.length) return "";
+  if (!additionalFiles?.length) return '';
 
   const fileCount = additionalFiles.flatMap((g) => g.files).length;
 
   return fixWS`
         <p id="additional-files">${language.$(
-          "releaseInfo.additionalFiles.heading",
+          'releaseInfo.additionalFiles.heading',
           {
             additionalFiles: language.countAdditionalFiles(fileCount, {
               unit: true,
@@ -57,14 +57,14 @@ export function generateAdditionalFilesList(
         <dl>
             ${additionalFiles
               .map(
-                ({ title, description, files }) => fixWS`
+                ({title, description, files}) => fixWS`
                 <dt>${
                   description
                     ? language.$(
-                        "releaseInfo.additionalFiles.entry.withDescription",
-                        { title, description }
+                        'releaseInfo.additionalFiles.entry.withDescription',
+                        {title, description}
                       )
-                    : language.$("releaseInfo.additionalFiles.entry", { title })
+                    : language.$('releaseInfo.additionalFiles.entry', {title})
                 }</dt>
                 <dd><ul>
                     ${files
@@ -72,7 +72,7 @@ export function generateAdditionalFilesList(
                         const size = getFileSize(file);
                         return size
                           ? `<li>${language.$(
-                              "releaseInfo.additionalFiles.file.withSize",
+                              'releaseInfo.additionalFiles.file.withSize',
                               {
                                 file: linkFile(file),
                                 size: language.formatFileSize(
@@ -81,17 +81,17 @@ export function generateAdditionalFilesList(
                               }
                             )}</li>`
                           : `<li>${language.$(
-                              "releaseInfo.additionalFiles.file",
+                              'releaseInfo.additionalFiles.file',
                               {
                                 file: linkFile(file),
                               }
                             )}</li>`;
                       })
-                      .join("\n")}
+                      .join('\n')}
                 </ul></dd>
             `
               )
-              .join("\n")}
+              .join('\n')}
         </dl>
     `;
 }
@@ -100,22 +100,22 @@ export function generateAdditionalFilesList(
 
 export function getArtistString(
   artists,
-  { iconifyURL, link, language, showIcons = false, showContrib = false }
+  {iconifyURL, link, language, showIcons = false, showContrib = false}
 ) {
   return language.formatConjunctionList(
-    artists.map(({ who, what }) => {
-      const { urls, directory, name } = who;
+    artists.map(({who, what}) => {
+      const {urls, directory, name} = who;
       return [
         link.artist(who),
         showContrib && what && `(${what})`,
         showIcons &&
           urls?.length &&
           `<span class="icons">(${language.formatUnitList(
-            urls.map((url) => iconifyURL(url, { language }))
+            urls.map((url) => iconifyURL(url, {language}))
           )})</span>`,
       ]
         .filter(Boolean)
-        .join(" ");
+        .join(' ');
     })
   );
 }
@@ -125,7 +125,7 @@ export function getArtistString(
 export function generateChronologyLinks(
   currentThing,
   {
-    dateKey = "date",
+    dateKey = 'date',
     contribKey,
     getThings,
     headingString,
@@ -135,28 +135,28 @@ export function generateChronologyLinks(
     wikiData,
   }
 ) {
-  const { albumData } = wikiData;
+  const {albumData} = wikiData;
 
   const contributions = currentThing[contribKey];
   if (!contributions) {
-    return "";
+    return '';
   }
 
   if (contributions.length > 8) {
     return `<div class="chronology">${language.$(
-      "misc.chronology.seeArtistPages"
+      'misc.chronology.seeArtistPages'
     )}</div>`;
   }
 
   return contributions
-    .map(({ who: artist }) => {
+    .map(({who: artist}) => {
       const thingsUnsorted = unique(getThings(artist)).filter(
         (t) => t[dateKey]
       );
 
       // Kinda a hack, but we automatically detect which is (probably) the
       // right function to use here.
-      const args = [thingsUnsorted, { getDate: (t) => t[dateKey] }];
+      const args = [thingsUnsorted, {getDate: (t) => t[dateKey]}];
       const things = thingsUnsorted.every(
         (t) => t instanceof Album || t instanceof Track
       )
@@ -165,7 +165,7 @@ export function generateChronologyLinks(
 
       const index = things.indexOf(currentThing);
 
-      if (index === -1) return "";
+      if (index === -1) return '';
 
       // TODO: This can pro8a8ly 8e made to use generatePreviousNextLinks?
       // We'd need to make generatePreviousNextLinks use toAnythingMan tho.
@@ -175,21 +175,21 @@ export function generateChronologyLinks(
         previous &&
           linkAnythingMan(previous, {
             color: false,
-            text: language.$("misc.nav.previous"),
+            text: language.$('misc.nav.previous'),
           }),
         next &&
           linkAnythingMan(next, {
             color: false,
-            text: language.$("misc.nav.next"),
+            text: language.$('misc.nav.next'),
           }),
       ].filter(Boolean);
 
       if (!parts.length) {
-        return "";
+        return '';
       }
 
       const stringOpts = {
-        index: language.formatIndex(index + 1, { language }),
+        index: language.formatIndex(index + 1, {language}),
         artist: link.artist(artist),
       };
 
@@ -201,27 +201,27 @@ export function generateChronologyLinks(
                 )}</span>
                 ${
                   parts.length &&
-                  `<span class="buttons">(${parts.join(", ")})</span>`
+                  `<span class="buttons">(${parts.join(', ')})</span>`
                 }
             </div>
         `;
     })
     .filter(Boolean)
-    .join("\n");
+    .join('\n');
 }
 
 // Content warning tags
 
-export function getRevealStringFromWarnings(warnings, { language }) {
+export function getRevealStringFromWarnings(warnings, {language}) {
   return (
-    language.$("misc.contentWarnings", { warnings }) +
+    language.$('misc.contentWarnings', {warnings}) +
     `<br><span class="reveal-interaction">${language.$(
-      "misc.contentWarnings.reveal"
+      'misc.contentWarnings.reveal'
     )}</span>`
   );
 }
 
-export function getRevealStringFromTags(tags, { language }) {
+export function getRevealStringFromTags(tags, {language}) {
   return (
     tags &&
     tags.some((tag) => tag.isContentWarning) &&
@@ -229,7 +229,7 @@ export function getRevealStringFromTags(tags, { language }) {
       language.formatUnitList(
         tags.filter((tag) => tag.isContentWarning).map((tag) => tag.name)
       ),
-      { language }
+      {language}
     )
   );
 }
@@ -247,7 +247,7 @@ export function generateCoverLink({
   alt,
   tags = [],
 }) {
-  const { wikiInfo } = wikiData;
+  const {wikiInfo} = wikiData;
 
   if (!src && path) {
     src = to(...path);
@@ -262,22 +262,22 @@ export function generateCoverLink({
             ${img({
               src,
               alt,
-              thumb: "medium",
-              id: "cover-art",
+              thumb: 'medium',
+              id: 'cover-art',
               link: true,
               square: true,
-              reveal: getRevealStringFromTags(tags, { language }),
+              reveal: getRevealStringFromTags(tags, {language}),
             })}
             ${
               wikiInfo.enableArtTagUI &&
               tags.filter((tag) => !tag.isContentWarning).length &&
               fixWS`
                 <p class="tags">
-                    ${language.$("releaseInfo.artTags")}
+                    ${language.$('releaseInfo.artTags')}
                     ${tags
                       .filter((tag) => !tag.isContentWarning)
                       .map(link.tag)
-                      .join(",\n")}
+                      .join(',\n')}
                 </p>
             `
             }
@@ -288,9 +288,9 @@ export function generateCoverLink({
 // CSS & color shenanigans
 
 export function getThemeString(color, additionalVariables = []) {
-  if (!color) return "";
+  if (!color) return '';
 
-  const { primary, dim, bg } = getColors(color);
+  const {primary, dim, bg} = getColors(color);
 
   const variables = [
     `--primary-color: ${primary}`,
@@ -299,19 +299,19 @@ export function getThemeString(color, additionalVariables = []) {
     ...additionalVariables,
   ].filter(Boolean);
 
-  if (!variables.length) return "";
+  if (!variables.length) return '';
 
   return (
-    `:root {\n` + variables.map((line) => `    ` + line + ";\n").join("") + `}`
+    `:root {\n` + variables.map((line) => `    ` + line + ';\n').join('') + `}`
   );
 }
-export function getAlbumStylesheet(album, { to }) {
+export function getAlbumStylesheet(album, {to}) {
   return [
     album.wallpaperArtistContribs.length &&
       fixWS`
             body::before {
                 background-image: url("${to(
-                  "media.albumWallpaper",
+                  'media.albumWallpaper',
                   album.directory,
                   album.wallpaperFileExtension
                 )}");
@@ -326,31 +326,31 @@ export function getAlbumStylesheet(album, { to }) {
         `,
   ]
     .filter(Boolean)
-    .join("\n");
+    .join('\n');
 }
 
 // Divided track lists
 
 export function generateTrackListDividedByGroups(
   tracks,
-  { getTrackItem, language, wikiData }
+  {getTrackItem, language, wikiData}
 ) {
-  const { divideTrackListsByGroups: groups } = wikiData.wikiInfo;
+  const {divideTrackListsByGroups: groups} = wikiData.wikiInfo;
 
   if (!groups?.length) {
     return html.tag(
-      "ul",
+      'ul',
       tracks.map((t) => getTrackItem(t))
     );
   }
 
   const lists = Object.fromEntries(
-    groups.map((group) => [group.directory, { group, tracks: [] }])
+    groups.map((group) => [group.directory, {group, tracks: []}])
   );
   const other = [];
 
   for (const track of tracks) {
-    const { album } = track;
+    const {album} = track;
     const group = groups.find((g) => g.albums.includes(album));
     if (group) {
       lists[group.directory].tracks.push(track);
@@ -361,26 +361,26 @@ export function generateTrackListDividedByGroups(
 
   const ddul = (tracks) => fixWS`
         <dd><ul>
-            ${tracks.map((t) => getTrackItem(t)).join("\n")}
+            ${tracks.map((t) => getTrackItem(t)).join('\n')}
         </ul></dd>
     `;
 
   return html.tag(
-    "dl",
+    'dl',
     Object.values(lists)
-      .filter(({ tracks }) => tracks.length)
-      .flatMap(({ group, tracks }) => [
+      .filter(({tracks}) => tracks.length)
+      .flatMap(({group, tracks}) => [
         html.tag(
-          "dt",
-          language.formatString("trackList.group", { group: group.name })
+          'dt',
+          language.formatString('trackList.group', {group: group.name})
         ),
         ddul(tracks),
       ])
       .concat(
         other.length
           ? [
-              `<dt>${language.formatString("trackList.group", {
-                group: language.formatString("trackList.group.other"),
+              `<dt>${language.formatString('trackList.group', {
+                group: language.formatString('trackList.group.other'),
               })}</dt>`,
               ddul(other),
             ]
@@ -391,7 +391,7 @@ export function generateTrackListDividedByGroups(
 
 // Fancy lookin' links
 
-export function fancifyURL(url, { language, album = false } = {}) {
+export function fancifyURL(url, {language, album = false} = {}) {
   let local = Symbol();
   let domain;
   try {
@@ -403,80 +403,80 @@ export function fancifyURL(url, { language, album = false } = {}) {
   }
   return fixWS`<a href="${url}" class="nowrap">${
     domain === local
-      ? language.$("misc.external.local")
-      : domain.includes("bandcamp.com")
-      ? language.$("misc.external.bandcamp")
+      ? language.$('misc.external.local')
+      : domain.includes('bandcamp.com')
+      ? language.$('misc.external.bandcamp')
       : BANDCAMP_DOMAINS.includes(domain)
-      ? language.$("misc.external.bandcamp.domain", { domain })
+      ? language.$('misc.external.bandcamp.domain', {domain})
       : MASTODON_DOMAINS.includes(domain)
-      ? language.$("misc.external.mastodon.domain", { domain })
-      : domain.includes("youtu")
+      ? language.$('misc.external.mastodon.domain', {domain})
+      : domain.includes('youtu')
       ? album
-        ? url.includes("list=")
-          ? language.$("misc.external.youtube.playlist")
-          : language.$("misc.external.youtube.fullAlbum")
-        : language.$("misc.external.youtube")
-      : domain.includes("soundcloud")
-      ? language.$("misc.external.soundcloud")
-      : domain.includes("tumblr.com")
-      ? language.$("misc.external.tumblr")
-      : domain.includes("twitter.com")
-      ? language.$("misc.external.twitter")
-      : domain.includes("deviantart.com")
-      ? language.$("misc.external.deviantart")
-      : domain.includes("wikipedia.org")
-      ? language.$("misc.external.wikipedia")
-      : domain.includes("poetryfoundation.org")
-      ? language.$("misc.external.poetryFoundation")
-      : domain.includes("instagram.com")
-      ? language.$("misc.external.instagram")
-      : domain.includes("patreon.com")
-      ? language.$("misc.external.patreon")
+        ? url.includes('list=')
+          ? language.$('misc.external.youtube.playlist')
+          : language.$('misc.external.youtube.fullAlbum')
+        : language.$('misc.external.youtube')
+      : domain.includes('soundcloud')
+      ? language.$('misc.external.soundcloud')
+      : domain.includes('tumblr.com')
+      ? language.$('misc.external.tumblr')
+      : domain.includes('twitter.com')
+      ? language.$('misc.external.twitter')
+      : domain.includes('deviantart.com')
+      ? language.$('misc.external.deviantart')
+      : domain.includes('wikipedia.org')
+      ? language.$('misc.external.wikipedia')
+      : domain.includes('poetryfoundation.org')
+      ? language.$('misc.external.poetryFoundation')
+      : domain.includes('instagram.com')
+      ? language.$('misc.external.instagram')
+      : domain.includes('patreon.com')
+      ? language.$('misc.external.patreon')
       : domain
   }</a>`;
 }
 
-export function fancifyFlashURL(url, flash, { language }) {
-  const link = fancifyURL(url, { language });
+export function fancifyFlashURL(url, flash, {language}) {
+  const link = fancifyURL(url, {language});
   return `<span class="nowrap">${
-    url.includes("homestuck.com")
+    url.includes('homestuck.com')
       ? isNaN(Number(flash.page))
-        ? language.$("misc.external.flash.homestuck.secret", { link })
-        : language.$("misc.external.flash.homestuck.page", {
+        ? language.$('misc.external.flash.homestuck.secret', {link})
+        : language.$('misc.external.flash.homestuck.page', {
             link,
             page: flash.page,
           })
-      : url.includes("bgreco.net")
-      ? language.$("misc.external.flash.bgreco", { link })
-      : url.includes("youtu")
-      ? language.$("misc.external.flash.youtube", { link })
+      : url.includes('bgreco.net')
+      ? language.$('misc.external.flash.bgreco', {link})
+      : url.includes('youtu')
+      ? language.$('misc.external.flash.youtube', {link})
       : link
   }</span>`;
 }
 
-export function iconifyURL(url, { language, to }) {
+export function iconifyURL(url, {language, to}) {
   const domain = new URL(url).hostname;
-  const [id, msg] = domain.includes("bandcamp.com")
-    ? ["bandcamp", language.$("misc.external.bandcamp")]
+  const [id, msg] = domain.includes('bandcamp.com')
+    ? ['bandcamp', language.$('misc.external.bandcamp')]
     : BANDCAMP_DOMAINS.includes(domain)
-    ? ["bandcamp", language.$("misc.external.bandcamp.domain", { domain })]
+    ? ['bandcamp', language.$('misc.external.bandcamp.domain', {domain})]
     : MASTODON_DOMAINS.includes(domain)
-    ? ["mastodon", language.$("misc.external.mastodon.domain", { domain })]
-    : domain.includes("youtu")
-    ? ["youtube", language.$("misc.external.youtube")]
-    : domain.includes("soundcloud")
-    ? ["soundcloud", language.$("misc.external.soundcloud")]
-    : domain.includes("tumblr.com")
-    ? ["tumblr", language.$("misc.external.tumblr")]
-    : domain.includes("twitter.com")
-    ? ["twitter", language.$("misc.external.twitter")]
-    : domain.includes("deviantart.com")
-    ? ["deviantart", language.$("misc.external.deviantart")]
-    : domain.includes("instagram.com")
-    ? ["instagram", language.$("misc.external.bandcamp")]
-    : ["globe", language.$("misc.external.domain", { domain })];
+    ? ['mastodon', language.$('misc.external.mastodon.domain', {domain})]
+    : domain.includes('youtu')
+    ? ['youtube', language.$('misc.external.youtube')]
+    : domain.includes('soundcloud')
+    ? ['soundcloud', language.$('misc.external.soundcloud')]
+    : domain.includes('tumblr.com')
+    ? ['tumblr', language.$('misc.external.tumblr')]
+    : domain.includes('twitter.com')
+    ? ['twitter', language.$('misc.external.twitter')]
+    : domain.includes('deviantart.com')
+    ? ['deviantart', language.$('misc.external.deviantart')]
+    : domain.includes('instagram.com')
+    ? ['instagram', language.$('misc.external.bandcamp')]
+    : ['globe', language.$('misc.external.domain', {domain})];
   return fixWS`<a href="${url}" class="icon"><svg><title>${msg}</title><use href="${to(
-    "shared.staticFile",
+    'shared.staticFile',
     `icons.svg#icon-${id}`
   )}"></use></svg></a>`;
 }
@@ -490,23 +490,23 @@ export function getGridHTML({
   entries,
   srcFn,
   linkFn,
-  noSrcTextFn = () => "",
-  altFn = () => "",
+  noSrcTextFn = () => '',
+  altFn = () => '',
   detailsFn = null,
   lazy = true,
 }) {
   return entries
-    .map(({ large, item }, i) =>
+    .map(({large, item}, i) =>
       linkFn(item, {
-        class: ["grid-item", "box", large && "large-grid-item"],
+        class: ['grid-item', 'box', large && 'large-grid-item'],
         text: fixWS`
                 ${img({
                   src: srcFn(item),
                   alt: altFn(item),
-                  thumb: "small",
-                  lazy: typeof lazy === "number" ? i >= lazy : lazy,
+                  thumb: 'small',
+                  lazy: typeof lazy === 'number' ? i >= lazy : lazy,
                   square: true,
-                  reveal: getRevealStringFromTags(item.artTags, { language }),
+                  reveal: getRevealStringFromTags(item.artTags, {language}),
                   noSrcText: noSrcTextFn(item),
                 })}
                 <span>${item.name}</span>
@@ -514,7 +514,7 @@ export function getGridHTML({
             `,
       })
     )
-    .join("\n");
+    .join('\n');
 }
 
 export function getAlbumGridHTML({
@@ -531,24 +531,19 @@ export function getAlbumGridHTML({
     detailsFn:
       details &&
       ((album) =>
-        language.$("misc.albumGrid.details", {
-          tracks: language.countTracks(album.tracks.length, { unit: true }),
+        language.$('misc.albumGrid.details', {
+          tracks: language.countTracks(album.tracks.length, {unit: true}),
           time: language.formatDuration(getTotalDuration(album.tracks)),
         })),
     noSrcTextFn: (album) =>
-      language.$("misc.albumGrid.noCoverArt", {
+      language.$('misc.albumGrid.noCoverArt', {
         album: album.name,
       }),
     ...props,
   });
 }
 
-export function getFlashGridHTML({
-  getFlashCover,
-  getGridHTML,
-  link,
-  ...props
-}) {
+export function getFlashGridHTML({getFlashCover, getGridHTML, link, ...props}) {
   return getGridHTML({
     srcFn: getFlashCover,
     linkFn: link.flash,
@@ -561,23 +556,23 @@ export function getFlashGridHTML({
 export function generateInfoGalleryLinks(
   currentThing,
   isGallery,
-  { link, language, linkKeyGallery, linkKeyInfo }
+  {link, language, linkKeyGallery, linkKeyInfo}
 ) {
   return [
     link[linkKeyInfo](currentThing, {
-      class: isGallery ? "" : "current",
-      text: language.$("misc.nav.info"),
+      class: isGallery ? '' : 'current',
+      text: language.$('misc.nav.info'),
     }),
     link[linkKeyGallery](currentThing, {
-      class: isGallery ? "current" : "",
-      text: language.$("misc.nav.gallery"),
+      class: isGallery ? 'current' : '',
+      text: language.$('misc.nav.gallery'),
     }),
-  ].join(", ");
+  ].join(', ');
 }
 
 export function generatePreviousNextLinks(
   current,
-  { data, link, linkKey, language }
+  {data, link, linkKey, language}
 ) {
   const linkFn = link[linkKey];
 
@@ -589,51 +584,51 @@ export function generatePreviousNextLinks(
     previous &&
       linkFn(previous, {
         attributes: {
-          id: "previous-button",
+          id: 'previous-button',
           title: previous.name,
         },
-        text: language.$("misc.nav.previous"),
+        text: language.$('misc.nav.previous'),
         color: false,
       }),
     next &&
       linkFn(next, {
         attributes: {
-          id: "next-button",
+          id: 'next-button',
           title: next.name,
         },
-        text: language.$("misc.nav.next"),
+        text: language.$('misc.nav.next'),
         color: false,
       }),
   ]
     .filter(Boolean)
-    .join(", ");
+    .join(', ');
 }
 
 // Footer stuff
 
 export function getFooterLocalizationLinks(
   pathname,
-  { defaultLanguage, languages, paths, language, to }
+  {defaultLanguage, languages, paths, language, to}
 ) {
-  const { toPath } = paths;
-  const keySuffix = toPath[0].replace(/^localized\./, ".");
+  const {toPath} = paths;
+  const keySuffix = toPath[0].replace(/^localized\./, '.');
   const toArgs = toPath.slice(1);
 
   const links = Object.entries(languages)
-    .filter(([code, language]) => code !== "default" && !language.hidden)
+    .filter(([code, language]) => code !== 'default' && !language.hidden)
     .map(([code, language]) => language)
-    .sort(({ name: a }, { name: b }) => (a < b ? -1 : a > b ? 1 : 0))
+    .sort(({name: a}, {name: b}) => (a < b ? -1 : a > b ? 1 : 0))
     .map((language) =>
       html.tag(
-        "span",
+        'span',
         html.tag(
-          "a",
+          'a',
           {
             href:
               language === defaultLanguage
-                ? to("localizedDefaultLanguage" + keySuffix, ...toArgs)
+                ? to('localizedDefaultLanguage' + keySuffix, ...toArgs)
                 : to(
-                    "localizedWithBaseDirectory" + keySuffix,
+                    'localizedWithBaseDirectory' + keySuffix,
                     language.code,
                     ...toArgs
                   ),
@@ -644,8 +639,8 @@ export function getFooterLocalizationLinks(
     );
 
   return html.tag(
-    "div",
-    { class: "footer-localization-links" },
-    language.$("misc.uiLanguage", { languages: links.join("\n") })
+    'div',
+    {class: 'footer-localization-links'},
+    language.$('misc.uiLanguage', {languages: links.join('\n')})
   );
 }
diff --git a/src/page/album-commentary.js b/src/page/album-commentary.js
index b469838..719bd65 100644
--- a/src/page/album-commentary.js
+++ b/src/page/album-commentary.js
@@ -1,34 +1,34 @@
-// @format
-//
+/** @format */
+
 // Album commentary page and index specifications.
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
-import { filterAlbumsByCommentary } from "../util/wiki-data.js";
+import {filterAlbumsByCommentary} from '../util/wiki-data.js';
 
 // Page exports
 
-export function condition({ wikiData }) {
+export function condition({wikiData}) {
   return filterAlbumsByCommentary(wikiData.albumData).length;
 }
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return filterAlbumsByCommentary(wikiData.albumData);
 }
 
-export function write(album, { wikiData }) {
-  const { wikiInfo } = wikiData;
+export function write(album, {wikiData}) {
+  const {wikiInfo} = wikiData;
 
   const entries = [album, ...album.tracks]
     .filter((x) => x.commentary)
     .map((x) => x.commentary);
-  const words = entries.join(" ").split(" ").length;
+  const words = entries.join(' ').split(' ').length;
 
   const page = {
-    type: "page",
-    path: ["albumCommentary", album.directory],
+    type: 'page',
+    path: ['albumCommentary', album.directory],
     page: ({
       getAlbumStylesheet,
       getLinkThemeString,
@@ -38,30 +38,30 @@ export function write(album, { wikiData }) {
       to,
       transformMultiline,
     }) => ({
-      title: language.$("albumCommentaryPage.title", { album: album.name }),
+      title: language.$('albumCommentaryPage.title', {album: album.name}),
       stylesheet: getAlbumStylesheet(album),
       theme: getThemeString(album.color),
 
       main: {
         content: fixWS`
                     <div class="long-content">
-                        <h1>${language.$("albumCommentaryPage.title", {
+                        <h1>${language.$('albumCommentaryPage.title', {
                           album: link.album(album),
                         })}</h1>
-                        <p>${language.$("albumCommentaryPage.infoLine", {
+                        <p>${language.$('albumCommentaryPage.infoLine', {
                           words: `<b>${language.formatWordCount(words, {
                             unit: true,
                           })}</b>`,
                           entries: `<b>${language.countCommentaryEntries(
                             entries.length,
-                            { unit: true }
+                            {unit: true}
                           )}</b>`,
                         })}</p>
                         ${
                           album.commentary &&
                           fixWS`
                             <h3>${language.$(
-                              "albumCommentaryPage.entry.title.albumCommentary"
+                              'albumCommentaryPage.entry.title.albumCommentary'
                             )}</h3>
                             <blockquote>
                                 ${transformMultiline(album.commentary)}
@@ -73,7 +73,7 @@ export function write(album, { wikiData }) {
                           .map(
                             (track) => fixWS`
                             <h3 id="${track.directory}">${language.$(
-                              "albumCommentaryPage.entry.title.trackCommentary",
+                              'albumCommentaryPage.entry.title.trackCommentary',
                               {
                                 track: link.track(track),
                               }
@@ -85,22 +85,22 @@ export function write(album, { wikiData }) {
                             </blockquote>
                         `
                           )
-                          .join("\n")}
+                          .join('\n')}
                     </div>
                 `,
       },
 
       nav: {
-        linkContainerClasses: ["nav-links-hierarchy"],
+        linkContainerClasses: ['nav-links-hierarchy'],
         links: [
-          { toHome: true },
+          {toHome: true},
           {
-            path: ["localized.commentaryIndex"],
-            title: language.$("commentaryIndex.title"),
+            path: ['localized.commentaryIndex'],
+            title: language.$('commentaryIndex.title'),
           },
           {
-            html: language.$("albumCommentaryPage.nav.album", {
-              album: link.albumCommentary(album, { class: "current" }),
+            html: language.$('albumCommentaryPage.nav.album', {
+              album: link.albumCommentary(album, {class: 'current'}),
             }),
           },
         ],
@@ -111,7 +111,7 @@ export function write(album, { wikiData }) {
   return [page];
 }
 
-export function writeTargetless({ wikiData }) {
+export function writeTargetless({wikiData}) {
   const data = filterAlbumsByCommentary(wikiData.albumData)
     .map((album) => ({
       album,
@@ -119,44 +119,41 @@ export function writeTargetless({ wikiData }) {
         .filter((x) => x.commentary)
         .map((x) => x.commentary),
     }))
-    .map(({ album, entries }) => ({
+    .map(({album, entries}) => ({
       album,
       entries,
-      words: entries.join(" ").split(" ").length,
+      words: entries.join(' ').split(' ').length,
     }));
 
-  const totalEntries = data.reduce(
-    (acc, { entries }) => acc + entries.length,
-    0
-  );
-  const totalWords = data.reduce((acc, { words }) => acc + words, 0);
+  const totalEntries = data.reduce((acc, {entries}) => acc + entries.length, 0);
+  const totalWords = data.reduce((acc, {words}) => acc + words, 0);
 
   const page = {
-    type: "page",
-    path: ["commentaryIndex"],
-    page: ({ link, language }) => ({
-      title: language.$("commentaryIndex.title"),
+    type: 'page',
+    path: ['commentaryIndex'],
+    page: ({link, language}) => ({
+      title: language.$('commentaryIndex.title'),
 
       main: {
         content: fixWS`
                     <div class="long-content">
-                        <h1>${language.$("commentaryIndex.title")}</h1>
-                        <p>${language.$("commentaryIndex.infoLine", {
+                        <h1>${language.$('commentaryIndex.title')}</h1>
+                        <p>${language.$('commentaryIndex.infoLine', {
                           words: `<b>${language.formatWordCount(totalWords, {
                             unit: true,
                           })}</b>`,
                           entries: `<b>${language.countCommentaryEntries(
                             totalEntries,
-                            { unit: true }
+                            {unit: true}
                           )}</b>`,
                         })}</p>
-                        <p>${language.$("commentaryIndex.albumList.title")}</p>
+                        <p>${language.$('commentaryIndex.albumList.title')}</p>
                         <ul>
                             ${data
                               .map(
-                                ({ album, entries, words }) => fixWS`
+                                ({album, entries, words}) => fixWS`
                                     <li>${language.$(
-                                      "commentaryIndex.albumList.item",
+                                      'commentaryIndex.albumList.item',
                                       {
                                         album: link.albumCommentary(album),
                                         words: language.formatWordCount(words, {
@@ -165,19 +162,19 @@ export function writeTargetless({ wikiData }) {
                                         entries:
                                           language.countCommentaryEntries(
                                             entries.length,
-                                            { unit: true }
+                                            {unit: true}
                                           ),
                                       }
                                     )}</li>
                                 `
                               )
-                              .join("\n")}
+                              .join('\n')}
                         </ul>
                     </div>
                 `,
       },
 
-      nav: { simple: true },
+      nav: {simple: true},
     }),
   };
 
diff --git a/src/page/album.js b/src/page/album.js
index f015976..6c8bb84 100644
--- a/src/page/album.js
+++ b/src/page/album.js
@@ -1,33 +1,33 @@
-// @format
-//
+/** @format */
+
 // Album page specification.
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
-import * as html from "../util/html.js";
+import * as html from '../util/html.js';
 
-import { bindOpts, compareArrays } from "../util/sugar.js";
+import {bindOpts, compareArrays} from '../util/sugar.js';
 
 import {
   getAlbumCover,
   getAlbumListTag,
   getTotalDuration,
-} from "../util/wiki-data.js";
+} from '../util/wiki-data.js';
 
 // Page exports
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.albumData;
 }
 
-export function write(album, { wikiData }) {
-  const { wikiInfo } = wikiData;
+export function write(album, {wikiData}) {
+  const {wikiInfo} = wikiData;
 
   const unbound_trackToListItem = (
     track,
-    { getArtistString, getLinkThemeString, link, language }
+    {getArtistString, getLinkThemeString, link, language}
   ) => {
     const itemOpts = {
       duration: language.formatDuration(track.duration ?? 0),
@@ -37,13 +37,13 @@ export function write(album, { wikiData }) {
       compareArrays(
         track.artistContribs.map((c) => c.who),
         album.artistContribs.map((c) => c.who),
-        { checkOrder: false }
+        {checkOrder: false}
       )
-        ? language.$("trackList.item.withDuration", itemOpts)
-        : language.$("trackList.item.withDuration.withArtists", {
+        ? language.$('trackList.item.withDuration', itemOpts)
+        : language.$('trackList.item.withDuration.withArtists', {
             ...itemOpts,
             by: `<span class="by">${language.$(
-              "trackList.item.withArtists.by",
+              'trackList.item.withArtists.by',
               {
                 artists: getArtistString(track.artistContribs),
               }
@@ -60,8 +60,8 @@ export function write(album, { wikiData }) {
   const listTag = getAlbumListTag(album);
 
   const data = {
-    type: "data",
-    path: ["album", album.directory],
+    type: 'data',
+    path: ['album', album.directory],
     data: ({
       serializeContribs,
       serializeCover,
@@ -97,8 +97,8 @@ export function write(album, { wikiData }) {
   };
 
   const page = {
-    type: "page",
-    path: ["album", album.directory],
+    type: 'page',
+    path: ['album', album.directory],
     page: ({
       fancifyURL,
       generateAdditionalFilesShortcut,
@@ -126,7 +126,7 @@ export function write(album, { wikiData }) {
       const cover = getAlbumCover(album);
 
       return {
-        title: language.$("albumPage.title", { album: album.name }),
+        title: language.$('albumPage.title', {album: album.name}),
         stylesheet: getAlbumStylesheet(album),
         theme: getThemeString(album.color, [
           `--album-directory: ${album.directory}`,
@@ -135,12 +135,12 @@ export function write(album, { wikiData }) {
         banner: album.bannerArtistContribs.length && {
           dimensions: album.bannerDimensions,
           path: [
-            "media.albumBanner",
+            'media.albumBanner',
             album.directory,
             album.bannerFileExtension,
           ],
-          alt: language.$("misc.alt.albumBanner"),
-          position: "top",
+          alt: language.$('misc.alt.albumBanner'),
+          position: 'top',
         },
 
         main: {
@@ -149,17 +149,17 @@ export function write(album, { wikiData }) {
                           cover &&
                           generateCoverLink({
                             src: cover,
-                            alt: language.$("misc.alt.albumCover"),
+                            alt: language.$('misc.alt.albumCover'),
                             tags: album.artTags,
                           })
                         }
-                        <h1>${language.$("albumPage.title", {
+                        <h1>${language.$('albumPage.title', {
                           album: album.name,
                         })}</h1>
                         <p>
                             ${[
                               album.artistContribs.length &&
-                                language.$("releaseInfo.by", {
+                                language.$('releaseInfo.by', {
                                   artists: getArtistString(
                                     album.artistContribs,
                                     {
@@ -169,7 +169,7 @@ export function write(album, { wikiData }) {
                                   ),
                                 }),
                               album.coverArtistContribs.length &&
-                                language.$("releaseInfo.coverArtBy", {
+                                language.$('releaseInfo.coverArtBy', {
                                   artists: getArtistString(
                                     album.coverArtistContribs,
                                     {
@@ -179,7 +179,7 @@ export function write(album, { wikiData }) {
                                   ),
                                 }),
                               album.wallpaperArtistContribs.length &&
-                                language.$("releaseInfo.wallpaperArtBy", {
+                                language.$('releaseInfo.wallpaperArtBy', {
                                   artists: getArtistString(
                                     album.wallpaperArtistContribs,
                                     {
@@ -189,7 +189,7 @@ export function write(album, { wikiData }) {
                                   ),
                                 }),
                               album.bannerArtistContribs.length &&
-                                language.$("releaseInfo.bannerArtBy", {
+                                language.$('releaseInfo.bannerArtBy', {
                                   artists: getArtistString(
                                     album.bannerArtistContribs,
                                     {
@@ -199,23 +199,23 @@ export function write(album, { wikiData }) {
                                   ),
                                 }),
                               album.date &&
-                                language.$("releaseInfo.released", {
+                                language.$('releaseInfo.released', {
                                   date: language.formatDate(album.date),
                                 }),
                               album.coverArtDate &&
                                 +album.coverArtDate !== +album.date &&
-                                language.$("releaseInfo.artReleased", {
+                                language.$('releaseInfo.artReleased', {
                                   date: language.formatDate(album.coverArtDate),
                                 }),
-                              language.$("releaseInfo.duration", {
+                              language.$('releaseInfo.duration', {
                                 duration: language.formatDuration(
                                   albumDuration,
-                                  { approximate: album.tracks.length > 1 }
+                                  {approximate: album.tracks.length > 1}
                                 ),
                               }),
                             ]
                               .filter(Boolean)
-                              .join("<br>\n")}
+                              .join('<br>\n')}
                         </p>
                         ${
                           (hasAdditionalFiles || hasCommentaryEntries) &&
@@ -224,26 +224,26 @@ export function write(album, { wikiData }) {
                               hasAdditionalFiles &&
                                 generateAdditionalFilesShortcut(
                                   album.additionalFiles,
-                                  { language }
+                                  {language}
                                 ),
                               hasCommentaryEntries &&
-                                language.$("releaseInfo.viewCommentary", {
+                                language.$('releaseInfo.viewCommentary', {
                                   link: link.albumCommentary(album, {
                                     text: language.$(
-                                      "releaseInfo.viewCommentary.link"
+                                      'releaseInfo.viewCommentary.link'
                                     ),
                                   }),
                                 }),
                             ]
                               .filter(Boolean)
-                              .join("<br>\n")}</p>`
+                              .join('<br>\n')}</p>`
                         }
                         ${
                           album.urls?.length &&
-                          `<p>${language.$("releaseInfo.listenOn", {
+                          `<p>${language.$('releaseInfo.listenOn', {
                             links: language.formatDisjunctionList(
                               album.urls.map((url) =>
-                                fancifyURL(url, { album: true })
+                                fancifyURL(url, {album: true})
                               )
                             ),
                           })}</p>`
@@ -263,32 +263,32 @@ export function write(album, { wikiData }) {
                                       tracks,
                                     }) => fixWS`
                                     <dt>${language.$(
-                                      "trackList.section.withDuration",
+                                      'trackList.section.withDuration',
                                       {
                                         duration: language.formatDuration(
                                           getTotalDuration(tracks),
-                                          { approximate: tracks.length > 1 }
+                                          {approximate: tracks.length > 1}
                                         ),
                                         section: name,
                                       }
                                     )}</dt>
                                     <dd><${
-                                      listTag === "ol"
+                                      listTag === 'ol'
                                         ? `ol start="${startIndex + 1}"`
                                         : listTag
                                     }>
                                         ${tracks
                                           .map(trackToListItem)
-                                          .join("\n")}
+                                          .join('\n')}
                                     </${listTag}></dd>
                                 `
                                   )
-                                  .join("\n")}
+                                  .join('\n')}
                             </dl>
                         `
                             : fixWS`
                             <${listTag}>
-                                ${album.tracks.map(trackToListItem).join("\n")}
+                                ${album.tracks.map(trackToListItem).join('\n')}
                             </${listTag}>
                         `
                         }
@@ -297,14 +297,14 @@ export function write(album, { wikiData }) {
                           fixWS`
                             <p>
                                 ${[
-                                  language.$("releaseInfo.addedToWiki", {
+                                  language.$('releaseInfo.addedToWiki', {
                                     date: language.formatDate(
                                       album.dateAddedToWiki
                                     ),
                                   }),
                                 ]
                                   .filter(Boolean)
-                                  .join("<br>\n")}
+                                  .join('<br>\n')}
                             </p>
                         `
                         }
@@ -315,21 +315,21 @@ export function write(album, { wikiData }) {
                             getFileSize: (file) =>
                               getSizeOfAdditionalFile(
                                 urls
-                                  .from("media.root")
+                                  .from('media.root')
                                   .to(
-                                    "media.albumAdditionalFile",
+                                    'media.albumAdditionalFile',
                                     album.directory,
                                     file
                                   )
                               ),
                             linkFile: (file) =>
-                              link.albumAdditionalFile({ album, file }),
+                              link.albumAdditionalFile({album, file}),
                           })
                         }
                         ${
                           album.commentary &&
                           fixWS`
-                            <p>${language.$("releaseInfo.artistCommentary")}</p>
+                            <p>${language.$('releaseInfo.artistCommentary')}</p>
                             <blockquote>
                                 ${transformMultiline(album.commentary)}
                             </blockquote>
@@ -348,16 +348,16 @@ export function write(album, { wikiData }) {
         }),
 
         nav: {
-          linkContainerClasses: ["nav-links-hierarchy"],
+          linkContainerClasses: ['nav-links-hierarchy'],
           links: [
-            { toHome: true },
+            {toHome: true},
             {
-              html: language.$("albumPage.nav.album", {
-                album: link.album(album, { class: "current" }),
+              html: language.$('albumPage.nav.album', {
+                album: link.album(album, {class: 'current'}),
               }),
             },
           ],
-          bottomRowContent: generateAlbumNavLinks(album, null, { language }),
+          bottomRowContent: generateAlbumNavLinks(album, null, {language}),
           content: generateAlbumChronologyLinks(album, null, {
             generateChronologyLinks,
           }),
@@ -380,14 +380,7 @@ export function write(album, { wikiData }) {
 export function generateAlbumSidebar(
   album,
   currentTrack,
-  {
-    fancifyURL,
-    getLinkThemeString,
-    link,
-    language,
-    transformMultiline,
-    wikiData,
-  }
+  {fancifyURL, getLinkThemeString, link, language, transformMultiline, wikiData}
 ) {
   const listTag = getAlbumListTag(album);
 
@@ -400,41 +393,41 @@ export function generateAlbumSidebar(
     }];
     */
 
-  const { trackGroups } = album;
+  const {trackGroups} = album;
 
   const trackToListItem = (track) =>
     html.tag(
-      "li",
-      { class: track === currentTrack && "current" },
-      language.$("albumSidebar.trackList.item", {
+      'li',
+      {class: track === currentTrack && 'current'},
+      language.$('albumSidebar.trackList.item', {
         track: link.track(track),
       })
     );
 
   const nameOrDefault = (isDefaultTrackGroup, name) =>
     isDefaultTrackGroup
-      ? language.$("albumSidebar.trackList.fallbackGroupName")
+      ? language.$('albumSidebar.trackList.fallbackGroupName')
       : name;
 
   const trackListPart = fixWS`
         <h1>${link.album(album)}</h1>
         ${trackGroups
-          .map(({ name, color, startIndex, tracks, isDefaultTrackGroup }) =>
+          .map(({name, color, startIndex, tracks, isDefaultTrackGroup}) =>
             html.tag(
-              "details",
+              'details',
               {
                 // Leave side8ar track groups collapsed on al8um homepage,
                 // since there's already a view of all the groups expanded
                 // in the main content area.
                 open: currentTrack && tracks.includes(currentTrack),
-                class: tracks.includes(currentTrack) && "current",
+                class: tracks.includes(currentTrack) && 'current',
               },
               [
                 html.tag(
-                  "summary",
-                  { style: getLinkThemeString(color) },
-                  listTag === "ol"
-                    ? language.$("albumSidebar.trackList.group.withRange", {
+                  'summary',
+                  {style: getLinkThemeString(color)},
+                  listTag === 'ol'
+                    ? language.$('albumSidebar.trackList.group.withRange', {
                         group: `<span class="group-name">${nameOrDefault(
                           isDefaultTrackGroup,
                           name
@@ -443,7 +436,7 @@ export function generateAlbumSidebar(
                           startIndex + tracks.length
                         }`,
                       })
-                    : language.$("albumSidebar.trackList.group", {
+                    : language.$('albumSidebar.trackList.group', {
                         group: `<span class="group-name">${nameOrDefault(
                           isDefaultTrackGroup,
                           name
@@ -452,20 +445,20 @@ export function generateAlbumSidebar(
                 ),
                 fixWS`
                     <${
-                      listTag === "ol"
+                      listTag === 'ol'
                         ? `ol start="${startIndex + 1}"`
                         : listTag
                     }>
-                        ${tracks.map(trackToListItem).join("\n")}
+                        ${tracks.map(trackToListItem).join('\n')}
                     </${listTag}>
                 `,
               ]
             )
           )
-          .join("\n")}
+          .join('\n')}
     `;
 
-  const { groups } = album;
+  const {groups} = album;
 
   const groupParts = groups
     .map((group) => {
@@ -473,17 +466,17 @@ export function generateAlbumSidebar(
       const index = albums.indexOf(album);
       const next = index >= 0 && albums[index + 1];
       const previous = index > 0 && albums[index - 1];
-      return { group, next, previous };
+      return {group, next, previous};
     })
     .map(
-      ({ group, next, previous }) => fixWS`
-        <h1>${language.$("albumSidebar.groupBox.title", {
+      ({group, next, previous}) => fixWS`
+        <h1>${language.$('albumSidebar.groupBox.title', {
           group: link.groupInfo(group),
         })}</h1>
         ${!currentTrack && transformMultiline(group.descriptionShort)}
         ${
           group.urls?.length &&
-          `<p>${language.$("releaseInfo.visitOn", {
+          `<p>${language.$('releaseInfo.visitOn', {
             links: language.formatDisjunctionList(
               group.urls.map((url) => fancifyURL(url))
             ),
@@ -495,7 +488,7 @@ export function generateAlbumSidebar(
             ${
               next &&
               `<p class="group-chronology-link">${language.$(
-                "albumSidebar.groupBox.next",
+                'albumSidebar.groupBox.next',
                 {
                   album: link.album(next),
                 }
@@ -504,7 +497,7 @@ export function generateAlbumSidebar(
             ${
               previous &&
               `<p class="group-chronology-link">${language.$(
-                "albumSidebar.groupBox.previous",
+                'albumSidebar.groupBox.previous',
                 {
                   album: link.album(previous),
                 }
@@ -517,7 +510,7 @@ export function generateAlbumSidebar(
 
   if (groupParts.length) {
     if (currentTrack) {
-      const combinedGroupPart = groupParts.join("\n<hr>\n");
+      const combinedGroupPart = groupParts.join('\n<hr>\n');
       return {
         multiple: [trackListPart, combinedGroupPart],
       };
@@ -536,9 +529,9 @@ export function generateAlbumSidebar(
 export function generateAlbumSecondaryNav(
   album,
   currentTrack,
-  { link, language, getLinkThemeString }
+  {link, language, getLinkThemeString}
 ) {
-  const { groups } = album;
+  const {groups} = album;
 
   if (!groups.length) {
     return null;
@@ -550,56 +543,56 @@ export function generateAlbumSecondaryNav(
       const index = albums.indexOf(album);
       const next = index >= 0 && albums[index + 1];
       const previous = index > 0 && albums[index - 1];
-      return { group, next, previous };
+      return {group, next, previous};
     })
-    .map(({ group, next, previous }) => {
+    .map(({group, next, previous}) => {
       const previousNext =
         !currentTrack &&
         [
           previous &&
             link.album(previous, {
               color: false,
-              text: language.$("misc.nav.previous"),
+              text: language.$('misc.nav.previous'),
             }),
           next &&
             link.album(next, {
               color: false,
-              text: language.$("misc.nav.next"),
+              text: language.$('misc.nav.next'),
             }),
         ].filter(Boolean);
-      return html.tag("span", { style: getLinkThemeString(group.color) }, [
-        language.$("albumSidebar.groupBox.title", {
+      return html.tag('span', {style: getLinkThemeString(group.color)}, [
+        language.$('albumSidebar.groupBox.title', {
           group: link.groupInfo(group),
         }),
-        previousNext?.length && `(${previousNext.join(",\n")})`,
+        previousNext?.length && `(${previousNext.join(',\n')})`,
       ]);
     });
 
   return {
-    classes: ["dot-between-spans"],
-    content: groupParts.join("\n"),
+    classes: ['dot-between-spans'],
+    content: groupParts.join('\n'),
   };
 }
 
 export function generateAlbumNavLinks(
   album,
   currentTrack,
-  { generatePreviousNextLinks, language }
+  {generatePreviousNextLinks, language}
 ) {
   if (album.tracks.length <= 1) {
-    return "";
+    return '';
   }
 
   const previousNextLinks =
     currentTrack &&
     generatePreviousNextLinks(currentTrack, {
       data: album.tracks,
-      linkKey: "track",
+      linkKey: 'track',
     });
   const randomLink = `<a href="#" data-random="track-in-album" id="random-button">${
     currentTrack
-      ? language.$("trackPage.nav.random")
-      : language.$("albumPage.nav.randomTrack")
+      ? language.$('trackPage.nav.random')
+      : language.$('albumPage.nav.randomTrack')
   }</a>`;
 
   return previousNextLinks
@@ -610,44 +603,44 @@ export function generateAlbumNavLinks(
 export function generateAlbumChronologyLinks(
   album,
   currentTrack,
-  { generateChronologyLinks }
+  {generateChronologyLinks}
 ) {
   return html.tag(
-    "div",
+    'div',
     {
       [html.onlyIfContent]: true,
-      class: "nav-chronology-links",
+      class: 'nav-chronology-links',
     },
     [
       currentTrack &&
         generateChronologyLinks(currentTrack, {
-          contribKey: "artistContribs",
+          contribKey: 'artistContribs',
           getThings: (artist) => [
             ...artist.tracksAsArtist,
             ...artist.tracksAsContributor,
           ],
-          headingString: "misc.chronology.heading.track",
+          headingString: 'misc.chronology.heading.track',
         }),
       currentTrack &&
         generateChronologyLinks(currentTrack, {
-          contribKey: "contributorContribs",
+          contribKey: 'contributorContribs',
           getThings: (artist) => [
             ...artist.tracksAsArtist,
             ...artist.tracksAsContributor,
           ],
-          headingString: "misc.chronology.heading.track",
+          headingString: 'misc.chronology.heading.track',
         }),
       generateChronologyLinks(currentTrack || album, {
-        contribKey: "coverArtistContribs",
-        dateKey: "coverArtDate",
+        contribKey: 'coverArtistContribs',
+        dateKey: 'coverArtDate',
         getThings: (artist) => [
           ...artist.albumsAsCoverArtist,
           ...artist.tracksAsCoverArtist,
         ],
-        headingString: "misc.chronology.heading.coverArt",
+        headingString: 'misc.chronology.heading.coverArt',
       }),
     ]
       .filter(Boolean)
-      .join("\n")
+      .join('\n')
   );
 }
diff --git a/src/page/artist-alias.js b/src/page/artist-alias.js
index 8a45503..46ad1a3 100644
--- a/src/page/artist-alias.js
+++ b/src/page/artist-alias.js
@@ -1,21 +1,21 @@
-// @format
-//
+/** @format */
+
 // Artist alias redirect pages.
 // (Makes old permalinks bring visitors to the up-to-date page.)
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.artistAliasData;
 }
 
-export function write(aliasArtist, { wikiData }) {
+export function write(aliasArtist, {wikiData}) {
   // This function doesn't actually use wikiData, 8ut, um, consistency?
 
-  const { aliasedArtist } = aliasArtist;
+  const {aliasedArtist} = aliasArtist;
 
   const redirect = {
-    type: "redirect",
-    fromPath: ["artist", aliasArtist.directory],
-    toPath: ["artist", aliasedArtist.directory],
+    type: 'redirect',
+    fromPath: ['artist', aliasArtist.directory],
+    toPath: ['artist', aliasedArtist.directory],
     title: () => aliasedArtist.name,
   };
 
diff --git a/src/page/artist.js b/src/page/artist.js
index 314d2aa..6b26b0f 100644
--- a/src/page/artist.js
+++ b/src/page/artist.js
@@ -1,16 +1,16 @@
-// @format
-//
+/** @format */
+
 // Artist page specification.
 //
 // NB: See artist-alias.js for artist alias redirect pages.
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
-import * as html from "../util/html.js";
+import * as html from '../util/html.js';
 
-import { bindOpts, unique } from "../util/sugar.js";
+import {bindOpts, unique} from '../util/sugar.js';
 
 import {
   chunkByProperties,
@@ -19,18 +19,18 @@ import {
   sortByDate,
   sortByDirectory,
   sortChronologically,
-} from "../util/wiki-data.js";
+} from '../util/wiki-data.js';
 
 // Page exports
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.artistData;
 }
 
-export function write(artist, { wikiData }) {
-  const { groupData, wikiInfo } = wikiData;
+export function write(artist, {wikiData}) {
+  const {groupData, wikiInfo} = wikiData;
 
-  const { name, urls, contextNotes } = artist;
+  const {name, urls, contextNotes} = artist;
 
   const artThingsAll = sortAlbumsTracksChronologically(
     unique([
@@ -39,7 +39,7 @@ export function write(artist, { wikiData }) {
       ...(artist.albumsAsBannerArtist ?? []),
       ...(artist.tracksAsCoverArtist ?? []),
     ]),
-    { getDate: (o) => o.coverArtDate }
+    {getDate: (o) => o.coverArtDate}
   );
 
   const artThingsGallery = sortAlbumsTracksChronologically(
@@ -47,7 +47,7 @@ export function write(artist, { wikiData }) {
       ...(artist.albumsAsCoverArtist ?? []),
       ...(artist.tracksAsCoverArtist ?? []),
     ],
-    { getDate: (o) => o.coverArtDate }
+    {getDate: (o) => o.coverArtDate}
   );
 
   const commentaryThings = sortAlbumsTracksChronologically([
@@ -58,17 +58,17 @@ export function write(artist, { wikiData }) {
   const hasGallery = artThingsGallery.length > 0;
 
   const getArtistsAndContrib = (thing, key) => ({
-    artists: thing[key]?.filter(({ who }) => who !== artist),
-    contrib: thing[key]?.find(({ who }) => who === artist),
+    artists: thing[key]?.filter(({who}) => who !== artist),
+    contrib: thing[key]?.find(({who}) => who === artist),
     thing,
     key,
   });
 
   const artListChunks = chunkByProperties(
     artThingsAll.flatMap((thing) =>
-      ["coverArtistContribs", "wallpaperArtistContribs", "bannerArtistContribs"]
+      ['coverArtistContribs', 'wallpaperArtistContribs', 'bannerArtistContribs']
         .map((key) => getArtistsAndContrib(thing, key))
-        .filter(({ contrib }) => contrib)
+        .filter(({contrib}) => contrib)
         .map((props) => ({
           album: thing.album || thing,
           track: thing.album ? thing : null,
@@ -76,7 +76,7 @@ export function write(artist, { wikiData }) {
           ...props,
         }))
     ),
-    ["date", "album"]
+    ['date', 'album']
   );
 
   const commentaryListChunks = chunkByProperties(
@@ -84,7 +84,7 @@ export function write(artist, { wikiData }) {
       album: thing.album || thing,
       track: thing.album ? thing : null,
     })),
-    ["album"]
+    ['album']
   );
 
   const allTracks = sortAlbumsTracksChronologically(
@@ -101,19 +101,19 @@ export function write(artist, { wikiData }) {
         date: +track.date,
         album: track.album,
         duration: track.duration,
-        artists: track.artistContribs.some(({ who }) => who === artist)
-          ? track.artistContribs.filter(({ who }) => who !== artist)
-          : track.contributorContribs.filter(({ who }) => who !== artist),
+        artists: track.artistContribs.some(({who}) => who === artist)
+          ? track.artistContribs.filter(({who}) => who !== artist)
+          : track.contributorContribs.filter(({who}) => who !== artist),
         contrib: {
           who: artist,
           whatArray: [
-            track.artistContribs.find(({ who }) => who === artist)?.what,
-            track.contributorContribs.find(({ who }) => who === artist)?.what,
+            track.artistContribs.find(({who}) => who === artist)?.what,
+            track.contributorContribs.find(({who}) => who === artist)?.what,
           ].filter(Boolean),
         },
       })),
-      ["date", "album"]
-    ).map(({ date, album, chunk }) => ({
+      ['date', 'album']
+    ).map(({date, album, chunk}) => ({
       date,
       album,
       chunk,
@@ -132,7 +132,7 @@ export function write(artist, { wikiData }) {
         group,
         contributions: usedGroups.filter((g) => g === group).length,
       }))
-      .filter(({ contributions }) => contributions > 0)
+      .filter(({contributions}) => contributions > 0)
       .sort((a, b) => b.contributions - a.contributions);
   };
 
@@ -151,10 +151,10 @@ export function write(artist, { wikiData }) {
         // want to show the full list of other contri8utors inline.
         // (It can often 8e very, very large!)
         artists: [],
-        contrib: flash.contributorContribs.find(({ who }) => who === artist),
+        contrib: flash.contributorContribs.find(({who}) => who === artist),
       })),
-      ["act"]
-    ).map(({ act, chunk }) => ({
+      ['act']
+    ).map(({act, chunk}) => ({
       act,
       chunk,
       dateFirst: chunk[0].date,
@@ -171,11 +171,11 @@ export function write(artist, { wikiData }) {
     contrib,
   }) =>
     original
-      ? language.$("artistPage.creditList.entry.rerelease", { entry })
+      ? language.$('artistPage.creditList.entry.rerelease', {entry})
       : artists.length
       ? contrib.what || contrib.whatArray?.length
         ? language.$(
-            "artistPage.creditList.entry.withArtists.withContribution",
+            'artistPage.creditList.entry.withArtists.withContribution',
             {
               entry,
               artists: getArtistString(artists),
@@ -184,12 +184,12 @@ export function write(artist, { wikiData }) {
                 : contrib.what,
             }
           )
-        : language.$("artistPage.creditList.entry.withArtists", {
+        : language.$('artistPage.creditList.entry.withArtists', {
             entry,
             artists: getArtistString(artists),
           })
       : contrib.what || contrib.whatArray?.length
-      ? language.$("artistPage.creditList.entry.withContribution", {
+      ? language.$('artistPage.creditList.entry.withContribution', {
           entry,
           contribution: contrib.whatArray
             ? language.formatUnitList(contrib.whatArray)
@@ -199,16 +199,16 @@ export function write(artist, { wikiData }) {
 
   const unbound_generateTrackList = (
     chunks,
-    { getArtistString, link, language }
+    {getArtistString, link, language}
   ) => fixWS`
         <dl>
             ${chunks
               .map(
-                ({ date, album, chunk, duration }) => fixWS`
+                ({date, album, chunk, duration}) => fixWS`
                 <dt>${
                   date && duration
                     ? language.$(
-                        "artistPage.creditList.album.withDate.withDuration",
+                        'artistPage.creditList.album.withDate.withDuration',
                         {
                           album: link.album(album),
                           date: language.formatDate(date),
@@ -218,27 +218,27 @@ export function write(artist, { wikiData }) {
                         }
                       )
                     : date
-                    ? language.$("artistPage.creditList.album.withDate", {
+                    ? language.$('artistPage.creditList.album.withDate', {
                         album: link.album(album),
                         date: language.formatDate(date),
                       })
                     : duration
-                    ? language.$("artistPage.creditList.album.withDuration", {
+                    ? language.$('artistPage.creditList.album.withDuration', {
                         album: link.album(album),
                         duration: language.formatDuration(duration, {
                           approximate: true,
                         }),
                       })
-                    : language.$("artistPage.creditList.album", {
+                    : language.$('artistPage.creditList.album', {
                         album: link.album(album),
                       })
                 }</dt>
                 <dd><ul>
                     ${chunk
-                      .map(({ track, ...props }) => ({
+                      .map(({track, ...props}) => ({
                         original: track.originalReleaseTrack,
                         entry: language.$(
-                          "artistPage.creditList.entry.track.withDuration",
+                          'artistPage.creditList.entry.track.withDuration',
                           {
                             track: link.track(track),
                             duration: language.formatDuration(
@@ -248,10 +248,10 @@ export function write(artist, { wikiData }) {
                         ),
                         ...props,
                       }))
-                      .map(({ original, ...opts }) =>
+                      .map(({original, ...opts}) =>
                         html.tag(
-                          "li",
-                          { class: original && "rerelease" },
+                          'li',
+                          {class: original && 'rerelease'},
                           generateEntryAccents({
                             getArtistString,
                             language,
@@ -260,18 +260,18 @@ export function write(artist, { wikiData }) {
                           })
                         )
                       )
-                      .join("\n")}
+                      .join('\n')}
                 </ul></dd>
             `
               )
-              .join("\n")}
+              .join('\n')}
         </dl>
     `;
 
   const unbound_serializeArtistsAndContrib =
-    (key, { serializeContribs, serializeLink }) =>
+    (key, {serializeContribs, serializeLink}) =>
     (thing) => {
-      const { artists, contrib } = getArtistsAndContrib(thing, key);
+      const {artists, contrib} = getArtistsAndContrib(thing, key);
       const ret = {};
       ret.link = serializeLink(thing);
       if (contrib.what) ret.contribution = contrib.what;
@@ -279,21 +279,21 @@ export function write(artist, { wikiData }) {
       return ret;
     };
 
-  const unbound_serializeTrackListChunks = (chunks, { serializeLink }) =>
-    chunks.map(({ date, album, chunk, duration }) => ({
+  const unbound_serializeTrackListChunks = (chunks, {serializeLink}) =>
+    chunks.map(({date, album, chunk, duration}) => ({
       album: serializeLink(album),
       date,
       duration,
-      tracks: chunk.map(({ track }) => ({
+      tracks: chunk.map(({track}) => ({
         link: serializeLink(track),
         duration: track.duration,
       })),
     }));
 
   const data = {
-    type: "data",
-    path: ["artist", artist.directory],
-    data: ({ serializeContribs, serializeLink }) => {
+    type: 'data',
+    path: ['artist', artist.directory],
+    data: ({serializeContribs, serializeLink}) => {
       const serializeArtistsAndContrib = bindOpts(
         unbound_serializeArtistsAndContrib,
         {
@@ -312,22 +312,22 @@ export function write(artist, { wikiData }) {
       return {
         albums: {
           asCoverArtist: artist.albumsAsCoverArtist?.map(
-            serializeArtistsAndContrib("coverArtistContribs")
+            serializeArtistsAndContrib('coverArtistContribs')
           ),
           asWallpaperArtist: artist.albumsAsWallpaperArtist?.map(
-            serializeArtistsAndContrib("wallpaperArtistContribs")
+            serializeArtistsAndContrib('wallpaperArtistContribs')
           ),
           asBannerArtist: artist.albumsAsBannerArtist?.map(
-            serializeArtistsAndContrib("bannerArtistContribs")
+            serializeArtistsAndContrib('bannerArtistContribs')
           ),
         },
         flashes: wikiInfo.enableFlashesAndGames
           ? {
               asContributor: artist.flashesAsContributor
                 ?.map((flash) =>
-                  getArtistsAndContrib(flash, "contributorContribs")
+                  getArtistsAndContrib(flash, 'contributorContribs')
                 )
-                .map(({ contrib, thing: flash }) => ({
+                .map(({contrib, thing: flash}) => ({
                   link: serializeLink(flash),
                   contribution: contrib.what,
                 })),
@@ -335,10 +335,10 @@ export function write(artist, { wikiData }) {
           : null,
         tracks: {
           asArtist: artist.tracksAsArtist.map(
-            serializeArtistsAndContrib("artistContribs")
+            serializeArtistsAndContrib('artistContribs')
           ),
           asContributor: artist.tracksAsContributor.map(
-            serializeArtistsAndContrib("contributorContribs")
+            serializeArtistsAndContrib('contributorContribs')
           ),
           chunked: serializeTrackListChunks(trackListChunks),
         },
@@ -347,8 +347,8 @@ export function write(artist, { wikiData }) {
   };
 
   const infoPage = {
-    type: "page",
-    path: ["artist", artist.directory],
+    type: 'page',
+    path: ['artist', artist.directory],
     page: ({
       fancifyURL,
       generateCoverLink,
@@ -367,7 +367,7 @@ export function write(artist, { wikiData }) {
       });
 
       return {
-        title: language.$("artistPage.title", { artist: name }),
+        title: language.$('artistPage.title', {artist: name}),
 
         main: {
           content: fixWS`
@@ -375,16 +375,16 @@ export function write(artist, { wikiData }) {
                           artist.hasAvatar &&
                           generateCoverLink({
                             src: getArtistAvatar(artist),
-                            alt: language.$("misc.alt.artistAvatar"),
+                            alt: language.$('misc.alt.artistAvatar'),
                           })
                         }
-                        <h1>${language.$("artistPage.title", {
+                        <h1>${language.$('artistPage.title', {
                           artist: name,
                         })}</h1>
                         ${
                           contextNotes &&
                           fixWS`
-                            <p>${language.$("releaseInfo.note")}</p>
+                            <p>${language.$('releaseInfo.note')}</p>
                             <blockquote>
                                 ${transformMultiline(contextNotes)}
                             </blockquote>
@@ -393,41 +393,41 @@ export function write(artist, { wikiData }) {
                         }
                         ${
                           urls?.length &&
-                          `<p>${language.$("releaseInfo.visitOn", {
+                          `<p>${language.$('releaseInfo.visitOn', {
                             links: language.formatDisjunctionList(
-                              urls.map((url) => fancifyURL(url, { language }))
+                              urls.map((url) => fancifyURL(url, {language}))
                             ),
                           })}</p>`
                         }
                         ${
                           hasGallery &&
-                          `<p>${language.$("artistPage.viewArtGallery", {
+                          `<p>${language.$('artistPage.viewArtGallery', {
                             link: link.artistGallery(artist, {
                               text: language.$(
-                                "artistPage.viewArtGallery.link"
+                                'artistPage.viewArtGallery.link'
                               ),
                             }),
                           })}</p>`
                         }
-                        <p>${language.$("misc.jumpTo.withLinks", {
+                        <p>${language.$('misc.jumpTo.withLinks', {
                           links: language.formatUnitList(
                             [
                               allTracks.length &&
                                 `<a href="#tracks">${language.$(
-                                  "artistPage.trackList.title"
+                                  'artistPage.trackList.title'
                                 )}</a>`,
                               artThingsAll.length &&
                                 `<a href="#art">${language.$(
-                                  "artistPage.artList.title"
+                                  'artistPage.artList.title'
                                 )}</a>`,
                               wikiInfo.enableFlashesAndGames &&
                                 flashes.length &&
                                 `<a href="#flashes">${language.$(
-                                  "artistPage.flashList.title"
+                                  'artistPage.flashList.title'
                                 )}</a>`,
                               commentaryThings.length &&
                                 `<a href="#commentary">${language.$(
-                                  "artistPage.commentaryList.title"
+                                  'artistPage.commentaryList.title'
                                 )}</a>`,
                             ].filter(Boolean)
                           ),
@@ -436,22 +436,22 @@ export function write(artist, { wikiData }) {
                           allTracks.length &&
                           fixWS`
                             <h2 id="tracks">${language.$(
-                              "artistPage.trackList.title"
+                              'artistPage.trackList.title'
                             )}</h2>
                             <p>${language.$(
-                              "artistPage.contributedDurationLine",
+                              'artistPage.contributedDurationLine',
                               {
                                 artist: artist.name,
                                 duration: language.formatDuration(
                                   totalDuration,
-                                  { approximate: true, unit: true }
+                                  {approximate: true, unit: true}
                                 ),
                               }
                             )}</p>
-                            <p>${language.$("artistPage.musicGroupsLine", {
+                            <p>${language.$('artistPage.musicGroupsLine', {
                               groups: language.formatUnitList(
-                                musicGroups.map(({ group, contributions }) =>
-                                  language.$("artistPage.groupsLine.item", {
+                                musicGroups.map(({group, contributions}) =>
+                                  language.$('artistPage.groupsLine.item', {
                                     group: link.groupInfo(group),
                                     contributions:
                                       language.countContributions(
@@ -468,25 +468,25 @@ export function write(artist, { wikiData }) {
                           artThingsAll.length &&
                           fixWS`
                             <h2 id="art">${language.$(
-                              "artistPage.artList.title"
+                              'artistPage.artList.title'
                             )}</h2>
                             ${
                               hasGallery &&
                               `<p>${language.$(
-                                "artistPage.viewArtGallery.orBrowseList",
+                                'artistPage.viewArtGallery.orBrowseList',
                                 {
                                   link: link.artistGallery(artist, {
                                     text: language.$(
-                                      "artistPage.viewArtGallery.link"
+                                      'artistPage.viewArtGallery.link'
                                     ),
                                   }),
                                 }
                               )}</p>`
                             }
-                            <p>${language.$("artistPage.artGroupsLine", {
+                            <p>${language.$('artistPage.artGroupsLine', {
                               groups: language.formatUnitList(
-                                artGroups.map(({ group, contributions }) =>
-                                  language.$("artistPage.groupsLine.item", {
+                                artGroups.map(({group, contributions}) =>
+                                  language.$('artistPage.groupsLine.item', {
                                     group: link.groupInfo(group),
                                     contributions:
                                       language.countContributions(
@@ -499,9 +499,9 @@ export function write(artist, { wikiData }) {
                             <dl>
                                 ${artListChunks
                                   .map(
-                                    ({ date, album, chunk }) => fixWS`
+                                    ({date, album, chunk}) => fixWS`
                                     <dt>${language.$(
-                                      "artistPage.creditList.album.withDate",
+                                      'artistPage.creditList.album.withDate',
                                       {
                                         album: link.album(album),
                                         date: language.formatDate(date),
@@ -518,20 +518,20 @@ export function write(artist, { wikiData }) {
                                             }) => ({
                                               entry: track
                                                 ? language.$(
-                                                    "artistPage.creditList.entry.track",
+                                                    'artistPage.creditList.entry.track',
                                                     {
                                                       track: link.track(track),
                                                     }
                                                   )
                                                 : `<i>${language.$(
-                                                    "artistPage.creditList.entry.album." +
+                                                    'artistPage.creditList.entry.album.' +
                                                       {
                                                         wallpaperArtistContribs:
-                                                          "wallpaperArt",
+                                                          'wallpaperArt',
                                                         bannerArtistContribs:
-                                                          "bannerArt",
+                                                          'bannerArt',
                                                         coverArtistContribs:
-                                                          "coverArt",
+                                                          'coverArt',
                                                       }[key]
                                                   )}</i>`,
                                               ...props,
@@ -545,11 +545,11 @@ export function write(artist, { wikiData }) {
                                             })
                                           )
                                           .map((row) => `<li>${row}</li>`)
-                                          .join("\n")}
+                                          .join('\n')}
                                     </ul></dd>
                                 `
                                   )
-                                  .join("\n")}
+                                  .join('\n')}
                             </dl>
                         `
                         }
@@ -558,7 +558,7 @@ export function write(artist, { wikiData }) {
                           flashes.length &&
                           fixWS`
                             <h2 id="flashes">${language.$(
-                              "artistPage.flashList.title"
+                              'artistPage.flashList.title'
                             )}</h2>
                             <dl>
                                 ${flashListChunks
@@ -570,7 +570,7 @@ export function write(artist, { wikiData }) {
                                       dateLast,
                                     }) => fixWS`
                                     <dt>${language.$(
-                                      "artistPage.creditList.flashAct.withDateRange",
+                                      'artistPage.creditList.flashAct.withDateRange',
                                       {
                                         act: link.flash(chunk[0].flash, {
                                           text: act.name,
@@ -583,9 +583,9 @@ export function write(artist, { wikiData }) {
                                     )}</dt>
                                     <dd><ul>
                                         ${chunk
-                                          .map(({ flash, ...props }) => ({
+                                          .map(({flash, ...props}) => ({
                                             entry: language.$(
-                                              "artistPage.creditList.entry.flash",
+                                              'artistPage.creditList.entry.flash',
                                               {
                                                 flash: link.flash(flash),
                                               }
@@ -600,11 +600,11 @@ export function write(artist, { wikiData }) {
                                             })
                                           )
                                           .map((row) => `<li>${row}</li>`)
-                                          .join("\n")}
+                                          .join('\n')}
                                     </ul></dd>
                                 `
                                   )
-                                  .join("\n")}
+                                  .join('\n')}
                             </dl>
                         `
                         }
@@ -612,38 +612,38 @@ export function write(artist, { wikiData }) {
                           commentaryThings.length &&
                           fixWS`
                             <h2 id="commentary">${language.$(
-                              "artistPage.commentaryList.title"
+                              'artistPage.commentaryList.title'
                             )}</h2>
                             <dl>
                                 ${commentaryListChunks
                                   .map(
-                                    ({ album, chunk }) => fixWS`
+                                    ({album, chunk}) => fixWS`
                                     <dt>${language.$(
-                                      "artistPage.creditList.album",
+                                      'artistPage.creditList.album',
                                       {
                                         album: link.album(album),
                                       }
                                     )}</dt>
                                     <dd><ul>
                                         ${chunk
-                                          .map(({ album, track, ...props }) =>
+                                          .map(({album, track, ...props}) =>
                                             track
                                               ? language.$(
-                                                  "artistPage.creditList.entry.track",
+                                                  'artistPage.creditList.entry.track',
                                                   {
                                                     track: link.track(track),
                                                   }
                                                 )
                                               : `<i>${language.$(
-                                                  "artistPage.creditList.entry.album.commentary"
+                                                  'artistPage.creditList.entry.album.commentary'
                                                 )}</i>`
                                           )
                                           .map((row) => `<li>${row}</li>`)
-                                          .join("\n")}
+                                          .join('\n')}
                                     </ul></dd>
                                 `
                                   )
-                                  .join("\n")}
+                                  .join('\n')}
                             </dl>
                         `
                         }
@@ -661,8 +661,8 @@ export function write(artist, { wikiData }) {
   };
 
   const galleryPage = hasGallery && {
-    type: "page",
-    path: ["artistGallery", artist.directory],
+    type: 'page',
+    path: ['artistGallery', artist.directory],
     page: ({
       generateInfoGalleryLinks,
       getAlbumCover,
@@ -672,26 +672,26 @@ export function write(artist, { wikiData }) {
       language,
       to,
     }) => ({
-      title: language.$("artistGalleryPage.title", { artist: name }),
+      title: language.$('artistGalleryPage.title', {artist: name}),
 
       main: {
-        classes: ["top-index"],
+        classes: ['top-index'],
         content: fixWS`
-                    <h1>${language.$("artistGalleryPage.title", {
+                    <h1>${language.$('artistGalleryPage.title', {
                       artist: name,
                     })}</h1>
                     <p class="quick-info">${language.$(
-                      "artistGalleryPage.infoLine",
+                      'artistGalleryPage.infoLine',
                       {
                         coverArts: language.countCoverArts(
                           artThingsGallery.length,
-                          { unit: true }
+                          {unit: true}
                         ),
                       }
                     )}</p>
                     <div class="grid-listing">
                         ${getGridHTML({
-                          entries: artThingsGallery.map((item) => ({ item })),
+                          entries: artThingsGallery.map((item) => ({item})),
                           srcFn: (thing) =>
                             thing.album
                               ? getTrackCover(thing)
@@ -723,30 +723,30 @@ function generateNavForArtist(
   artist,
   isGallery,
   hasGallery,
-  { generateInfoGalleryLinks, link, language, wikiData }
+  {generateInfoGalleryLinks, link, language, wikiData}
 ) {
-  const { wikiInfo } = wikiData;
+  const {wikiInfo} = wikiData;
 
   const infoGalleryLinks =
     hasGallery &&
     generateInfoGalleryLinks(artist, isGallery, {
       link,
       language,
-      linkKeyGallery: "artistGallery",
-      linkKeyInfo: "artist",
+      linkKeyGallery: 'artistGallery',
+      linkKeyInfo: 'artist',
     });
 
   return {
-    linkContainerClasses: ["nav-links-hierarchy"],
+    linkContainerClasses: ['nav-links-hierarchy'],
     links: [
-      { toHome: true },
+      {toHome: true},
       wikiInfo.enableListings && {
-        path: ["localized.listingIndex"],
-        title: language.$("listingIndex.title"),
+        path: ['localized.listingIndex'],
+        title: language.$('listingIndex.title'),
       },
       {
-        html: language.$("artistPage.nav.artist", {
-          artist: link.artist(artist, { class: "current" }),
+        html: language.$('artistPage.nav.artist', {
+          artist: link.artist(artist, {class: 'current'}),
         }),
       },
       hasGallery && {
diff --git a/src/page/flash.js b/src/page/flash.js
index 864f44d..340927d 100644
--- a/src/page/flash.js
+++ b/src/page/flash.js
@@ -1,29 +1,29 @@
-// @format
-//
+/** @format */
+
 // Flash page and index specifications.
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
-import * as html from "../util/html.js";
+import * as html from '../util/html.js';
 
-import { getFlashLink } from "../util/wiki-data.js";
+import {getFlashLink} from '../util/wiki-data.js';
 
 // Page exports
 
-export function condition({ wikiData }) {
+export function condition({wikiData}) {
   return wikiData.wikiInfo.enableFlashesAndGames;
 }
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.flashData;
 }
 
-export function write(flash, { wikiData }) {
+export function write(flash, {wikiData}) {
   const page = {
-    type: "page",
-    path: ["flash", flash.directory],
+    type: 'page',
+    path: ['flash', flash.directory],
     page: ({
       fancifyFlashURL,
       generateChronologyLinks,
@@ -36,26 +36,26 @@ export function write(flash, { wikiData }) {
       language,
       transformInline,
     }) => ({
-      title: language.$("flashPage.title", { flash: flash.name }),
+      title: language.$('flashPage.title', {flash: flash.name}),
       theme: getThemeString(flash.color, [
         `--flash-directory: ${flash.directory}`,
       ]),
 
       main: {
         content: fixWS`
-                    <h1>${language.$("flashPage.title", {
+                    <h1>${language.$('flashPage.title', {
                       flash: flash.name,
                     })}</h1>
                     ${generateCoverLink({
                       src: getFlashCover(flash),
-                      alt: language.$("misc.alt.flashArt"),
+                      alt: language.$('misc.alt.flashArt'),
                     })}
-                    <p>${language.$("releaseInfo.released", {
+                    <p>${language.$('releaseInfo.released', {
                       date: language.formatDate(flash.date),
                     })}</p>
                     ${
                       (flash.page || flash.urls?.length) &&
-                      `<p>${language.$("releaseInfo.playOn", {
+                      `<p>${language.$('releaseInfo.playOn', {
                         links: language.formatDisjunctionList(
                           [
                             flash.page && getFlashLink(flash),
@@ -69,15 +69,15 @@ export function write(flash, { wikiData }) {
                       fixWS`
                         <p>Tracks featured in <i>${flash.name.replace(
                           /\.$/,
-                          ""
+                          ''
                         )}</i>:</p>
                         <ul>
                             ${flash.featuredTracks
                               .map((track) =>
-                                language.$("trackList.item.withArtists", {
+                                language.$('trackList.item.withArtists', {
                                   track: link.track(track),
                                   by: `<span class="by">${language.$(
-                                    "trackList.item.withArtists.by",
+                                    'trackList.item.withArtists.by',
                                     {
                                       artists: getArtistString(
                                         track.artistContribs
@@ -87,14 +87,14 @@ export function write(flash, { wikiData }) {
                                 })
                               )
                               .map((row) => `<li>${row}</li>`)
-                              .join("\n")}
+                              .join('\n')}
                         </ul>
                     `
                     }
                     ${
                       flash.contributorContribs.length &&
                       fixWS`
-                        <p>${language.$("releaseInfo.contributors")}</p>
+                        <p>${language.$('releaseInfo.contributors')}</p>
                         <ul>
                             ${flash.contributorContribs
                               .map(
@@ -104,14 +104,14 @@ export function write(flash, { wikiData }) {
                                     showIcons: true,
                                   })}</li>`
                               )
-                              .join("\n")}
+                              .join('\n')}
                         </ul>
                     `
                     }
                 `,
       },
 
-      sidebarLeft: generateSidebarForFlash(flash, { link, language, wikiData }),
+      sidebarLeft: generateSidebarForFlash(flash, {link, language, wikiData}),
       nav: generateNavForFlash(flash, {
         generateChronologyLinks,
         generatePreviousNextLinks,
@@ -125,32 +125,32 @@ export function write(flash, { wikiData }) {
   return [page];
 }
 
-export function writeTargetless({ wikiData }) {
-  const { flashActData } = wikiData;
+export function writeTargetless({wikiData}) {
+  const {flashActData} = wikiData;
 
   const page = {
-    type: "page",
-    path: ["flashIndex"],
-    page: ({ getFlashGridHTML, getLinkThemeString, link, language }) => ({
-      title: language.$("flashIndex.title"),
+    type: 'page',
+    path: ['flashIndex'],
+    page: ({getFlashGridHTML, getLinkThemeString, link, language}) => ({
+      title: language.$('flashIndex.title'),
 
       main: {
-        classes: ["flash-index"],
+        classes: ['flash-index'],
         content: fixWS`
-                    <h1>${language.$("flashIndex.title")}</h1>
+                    <h1>${language.$('flashIndex.title')}</h1>
                     <div class="long-content">
-                        <p class="quick-info">${language.$("misc.jumpTo")}</p>
+                        <p class="quick-info">${language.$('misc.jumpTo')}</p>
                         <ul class="quick-info">
                             ${flashActData
                               .filter((act) => act.jump)
                               .map(
-                                ({ anchor, jump, jumpColor }) => fixWS`
+                                ({anchor, jump, jumpColor}) => fixWS`
                                 <li><a href="#${anchor}" style="${getLinkThemeString(
                                   jumpColor
                                 )}">${jump}</a></li>
                             `
                               )
-                              .join("\n")}
+                              .join('\n')}
                         </ul>
                     </div>
                     ${flashActData
@@ -171,11 +171,11 @@ export function writeTargetless({ wikiData }) {
                         </div>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 `,
       },
 
-      nav: { simple: true },
+      nav: {simple: true},
     }),
   };
 
@@ -186,32 +186,26 @@ export function writeTargetless({ wikiData }) {
 
 function generateNavForFlash(
   flash,
-  {
-    generateChronologyLinks,
-    generatePreviousNextLinks,
-    link,
-    language,
-    wikiData,
-  }
+  {generateChronologyLinks, generatePreviousNextLinks, link, language, wikiData}
 ) {
-  const { flashData, wikiInfo } = wikiData;
+  const {flashData, wikiInfo} = wikiData;
 
   const previousNextLinks = generatePreviousNextLinks(flash, {
     data: flashData,
-    linkKey: "flash",
+    linkKey: 'flash',
   });
 
   return {
-    linkContainerClasses: ["nav-links-hierarchy"],
+    linkContainerClasses: ['nav-links-hierarchy'],
     links: [
-      { toHome: true },
+      {toHome: true},
       {
-        path: ["localized.flashIndex"],
-        title: language.$("flashIndex.title"),
+        path: ['localized.flashIndex'],
+        title: language.$('flashIndex.title'),
       },
       {
-        html: language.$("flashPage.nav.flash", {
-          flash: link.flash(flash, { class: "current" }),
+        html: language.$('flashPage.nav.flash', {
+          flash: link.flash(flash, {class: 'current'}),
         }),
       },
     ],
@@ -221,8 +215,8 @@ function generateNavForFlash(
     content: fixWS`
             <div>
                 ${generateChronologyLinks(flash, {
-                  headingString: "misc.chronology.heading.flash",
-                  contribKey: "contributorContribs",
+                  headingString: 'misc.chronology.heading.flash',
+                  contribKey: 'contributorContribs',
                   getThings: (artist) => artist.flashesAsContributor,
                 })}
             </div>
@@ -230,21 +224,21 @@ function generateNavForFlash(
   };
 }
 
-function generateSidebarForFlash(flash, { link, language, wikiData }) {
+function generateSidebarForFlash(flash, {link, language, wikiData}) {
   // all hard-coded, sorry :(
   // this doesnt have a super portable implementation/design...yet!!
 
-  const { flashActData } = wikiData;
+  const {flashActData} = wikiData;
 
-  const act6 = flashActData.findIndex((act) => act.name.startsWith("Act 6"));
+  const act6 = flashActData.findIndex((act) => act.name.startsWith('Act 6'));
   const postCanon = flashActData.findIndex((act) =>
-    act.name.includes("Post Canon")
+    act.name.includes('Post Canon')
   );
   const outsideCanon =
     postCanon +
     flashActData
       .slice(postCanon)
-      .findIndex((act) => !act.name.includes("Post Canon"));
+      .findIndex((act) => !act.name.includes('Post Canon'));
   const actIndex = flashActData.indexOf(flash.act);
   const side =
     actIndex < 0 ? 0 : actIndex < act6 ? 1 : actIndex <= outsideCanon ? 2 : 3;
@@ -252,18 +246,18 @@ function generateSidebarForFlash(flash, { link, language, wikiData }) {
 
   return {
     content: fixWS`
-            <h1>${link.flashIndex("", {
-              text: language.$("flashIndex.title"),
+            <h1>${link.flashIndex('', {
+              text: language.$('flashIndex.title'),
             })}</h1>
             <dl>
                 ${flashActData
                   .filter(
                     (act) =>
-                      act.name.startsWith("Act 1") ||
-                      act.name.startsWith("Act 6 Act 1") ||
-                      act.name.startsWith("Hiveswap") ||
+                      act.name.startsWith('Act 1') ||
+                      act.name.startsWith('Act 6 Act 1') ||
+                      act.name.startsWith('Hiveswap') ||
                       // Sorry not sorry -Yiffy
-                      (({ index = flashActData.indexOf(act) } = {}) =>
+                      (({index = flashActData.indexOf(act)} = {}) =>
                         index < act6
                           ? side === 1
                           : index < outsideCanon
@@ -271,43 +265,43 @@ function generateSidebarForFlash(flash, { link, language, wikiData }) {
                           : true)()
                   )
                   .flatMap((act) => [
-                    (act.name.startsWith("Act 1") &&
+                    (act.name.startsWith('Act 1') &&
                       html.tag(
-                        "dt",
-                        { class: ["side", side === 1 && "current"] },
+                        'dt',
+                        {class: ['side', side === 1 && 'current']},
                         link.flash(act.flashes[0], {
-                          color: "#4ac925",
+                          color: '#4ac925',
                           text: `Side 1 (Acts 1-5)`,
                         })
                       )) ||
-                      (act.name.startsWith("Act 6 Act 1") &&
+                      (act.name.startsWith('Act 6 Act 1') &&
                         html.tag(
-                          "dt",
-                          { class: ["side", side === 2 && "current"] },
+                          'dt',
+                          {class: ['side', side === 2 && 'current']},
                           link.flash(act.flashes[0], {
-                            color: "#1076a2",
+                            color: '#1076a2',
                             text: `Side 2 (Acts 6-7)`,
                           })
                         )) ||
-                      (act.name.startsWith("Hiveswap Act 1") &&
+                      (act.name.startsWith('Hiveswap Act 1') &&
                         html.tag(
-                          "dt",
-                          { class: ["side", side === 3 && "current"] },
+                          'dt',
+                          {class: ['side', side === 3 && 'current']},
                           link.flash(act.flashes[0], {
-                            color: "#008282",
+                            color: '#008282',
                             text: `Outside Canon (Misc. Games)`,
                           })
                         )),
-                    (({ index = flashActData.indexOf(act) } = {}) =>
+                    (({index = flashActData.indexOf(act)} = {}) =>
                       index < act6
                         ? side === 1
                         : index < outsideCanon
                         ? side === 2
                         : true)() &&
                       html.tag(
-                        "dt",
-                        { class: act === currentAct && "current" },
-                        link.flash(act.flashes[0], { text: act.name })
+                        'dt',
+                        {class: act === currentAct && 'current'},
+                        link.flash(act.flashes[0], {text: act.name})
                       ),
                     act === currentAct &&
                       fixWS`
@@ -315,17 +309,17 @@ function generateSidebarForFlash(flash, { link, language, wikiData }) {
                             ${act.flashes
                               .map((f) =>
                                 html.tag(
-                                  "li",
-                                  { class: f === flash && "current" },
+                                  'li',
+                                  {class: f === flash && 'current'},
                                   link.flash(f)
                                 )
                               )
-                              .join("\n")}
+                              .join('\n')}
                         </ul></dd>
                     `,
                   ])
                   .filter(Boolean)
-                  .join("\n")}
+                  .join('\n')}
             </dl>
         `,
   };
diff --git a/src/page/group.js b/src/page/group.js
index b6f9978..c7de2fb 100644
--- a/src/page/group.js
+++ b/src/page/group.js
@@ -1,25 +1,25 @@
-// @format
-//
+/** @format */
+
 // Group page specifications.
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
-import * as html from "../util/html.js";
+import * as html from '../util/html.js';
 
-import { getTotalDuration, sortChronologically } from "../util/wiki-data.js";
+import {getTotalDuration, sortChronologically} from '../util/wiki-data.js';
 
 // Page exports
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.groupData;
 }
 
-export function write(group, { wikiData }) {
-  const { listingSpec, wikiInfo } = wikiData;
+export function write(group, {wikiData}) {
+  const {listingSpec, wikiInfo} = wikiData;
 
-  const { albums } = group;
+  const {albums} = group;
   const tracks = albums.flatMap((album) => album.tracks);
   const totalDuration = getTotalDuration(tracks);
 
@@ -29,8 +29,8 @@ export function write(group, { wikiData }) {
   }));
 
   const infoPage = {
-    type: "page",
-    path: ["groupInfo", group.directory],
+    type: 'page',
+    path: ['groupInfo', group.directory],
     page: ({
       generateInfoGalleryLinks,
       generatePreviousNextLinks,
@@ -41,57 +41,57 @@ export function write(group, { wikiData }) {
       language,
       transformMultiline,
     }) => ({
-      title: language.$("groupInfoPage.title", { group: group.name }),
+      title: language.$('groupInfoPage.title', {group: group.name}),
       theme: getThemeString(group.color),
 
       main: {
         content: fixWS`
-                    <h1>${language.$("groupInfoPage.title", {
+                    <h1>${language.$('groupInfoPage.title', {
                       group: group.name,
                     })}</h1>
                     ${
                       group.urls?.length &&
-                      `<p>${language.$("releaseInfo.visitOn", {
+                      `<p>${language.$('releaseInfo.visitOn', {
                         links: language.formatDisjunctionList(
-                          group.urls.map((url) => fancifyURL(url, { language }))
+                          group.urls.map((url) => fancifyURL(url, {language}))
                         ),
                       })}</p>`
                     }
                     <blockquote>
                         ${transformMultiline(group.description)}
                     </blockquote>
-                    <h2>${language.$("groupInfoPage.albumList.title")}</h2>
-                    <p>${language.$("groupInfoPage.viewAlbumGallery", {
+                    <h2>${language.$('groupInfoPage.albumList.title')}</h2>
+                    <p>${language.$('groupInfoPage.viewAlbumGallery', {
                       link: link.groupGallery(group, {
-                        text: language.$("groupInfoPage.viewAlbumGallery.link"),
+                        text: language.$('groupInfoPage.viewAlbumGallery.link'),
                       }),
                     })}</p>
                     <ul>
                         ${albumLines
-                          .map(({ album, otherGroup }) => {
+                          .map(({album, otherGroup}) => {
                             const item = album.date
-                              ? language.$("groupInfoPage.albumList.item", {
+                              ? language.$('groupInfoPage.albumList.item', {
                                   year: album.date.getFullYear(),
                                   album: link.album(album),
                                 })
                               : language.$(
-                                  "groupInfoPage.albumList.item.withoutYear",
+                                  'groupInfoPage.albumList.item.withoutYear',
                                   {
                                     album: link.album(album),
                                   }
                                 );
                             return html.tag(
-                              "li",
+                              'li',
                               otherGroup
                                 ? language.$(
-                                    "groupInfoPage.albumList.item.withAccent",
+                                    'groupInfoPage.albumList.item.withAccent',
                                     {
                                       item,
                                       accent: html.tag(
-                                        "span",
-                                        { class: "other-group-accent" },
+                                        'span',
+                                        {class: 'other-group-accent'},
                                         language.$(
-                                          "groupInfoPage.albumList.item.otherGroupAccent",
+                                          'groupInfoPage.albumList.item.otherGroupAccent',
                                           {
                                             group: link.groupInfo(otherGroup, {
                                               color: false,
@@ -104,7 +104,7 @@ export function write(group, { wikiData }) {
                                 : item
                             );
                           })
-                          .join("\n")}
+                          .join('\n')}
                     </ul>
                 `,
       },
@@ -127,8 +127,8 @@ export function write(group, { wikiData }) {
   };
 
   const galleryPage = {
-    type: "page",
-    path: ["groupGallery", group.directory],
+    type: 'page',
+    path: ['groupGallery', group.directory],
     page: ({
       generateInfoGalleryLinks,
       generatePreviousNextLinks,
@@ -138,17 +138,17 @@ export function write(group, { wikiData }) {
       link,
       language,
     }) => ({
-      title: language.$("groupGalleryPage.title", { group: group.name }),
+      title: language.$('groupGalleryPage.title', {group: group.name}),
       theme: getThemeString(group.color),
 
       main: {
-        classes: ["top-index"],
+        classes: ['top-index'],
         content: fixWS`
-                    <h1>${language.$("groupGalleryPage.title", {
+                    <h1>${language.$('groupGalleryPage.title', {
                       group: group.name,
                     })}</h1>
                     <p class="quick-info">${language.$(
-                      "groupGalleryPage.infoLine",
+                      'groupGalleryPage.infoLine',
                       {
                         tracks: `<b>${language.countTracks(tracks.length, {
                           unit: true,
@@ -165,16 +165,16 @@ export function write(group, { wikiData }) {
                       wikiInfo.enableGroupUI &&
                       wikiInfo.enableListings &&
                       html.tag(
-                        "p",
-                        { class: "quick-info" },
-                        language.$("groupGalleryPage.anotherGroupLine", {
+                        'p',
+                        {class: 'quick-info'},
+                        language.$('groupGalleryPage.anotherGroupLine', {
                           link: link.listing(
                             listingSpec.find(
-                              (l) => l.directory === "groups/by-category"
+                              (l) => l.directory === 'groups/by-category'
                             ),
                             {
                               text: language.$(
-                                "groupGalleryPage.anotherGroupLine.link"
+                                'groupGalleryPage.anotherGroupLine.link'
                               ),
                             }
                           ),
@@ -222,45 +222,45 @@ export function write(group, { wikiData }) {
 function generateGroupSidebar(
   currentGroup,
   isGallery,
-  { getLinkThemeString, link, language, wikiData }
+  {getLinkThemeString, link, language, wikiData}
 ) {
-  const { groupCategoryData, wikiInfo } = wikiData;
+  const {groupCategoryData, wikiInfo} = wikiData;
 
   if (!wikiInfo.enableGroupUI) {
     return null;
   }
 
-  const linkKey = isGallery ? "groupGallery" : "groupInfo";
+  const linkKey = isGallery ? 'groupGallery' : 'groupInfo';
 
   return {
     content: fixWS`
-            <h1>${language.$("groupSidebar.title")}</h1>
+            <h1>${language.$('groupSidebar.title')}</h1>
             ${groupCategoryData
               .map((category) =>
                 html.tag(
-                  "details",
+                  'details',
                   {
                     open: category === currentGroup.category,
-                    class: category === currentGroup.category && "current",
+                    class: category === currentGroup.category && 'current',
                   },
                   [
                     html.tag(
-                      "summary",
-                      { style: getLinkThemeString(category.color) },
-                      language.$("groupSidebar.groupList.category", {
+                      'summary',
+                      {style: getLinkThemeString(category.color)},
+                      language.$('groupSidebar.groupList.category', {
                         category: `<span class="group-name">${category.name}</span>`,
                       })
                     ),
                     html.tag(
-                      "ul",
+                      'ul',
                       category.groups.map((group) =>
                         html.tag(
-                          "li",
+                          'li',
                           {
-                            class: group === currentGroup && "current",
+                            class: group === currentGroup && 'current',
                             style: getLinkThemeString(group.color),
                           },
-                          language.$("groupSidebar.groupList.item", {
+                          language.$('groupSidebar.groupList.item', {
                             group: link[linkKey](group),
                           })
                         )
@@ -269,7 +269,7 @@ function generateGroupSidebar(
                   ]
                 )
               )
-              .join("\n")}
+              .join('\n')}
             </dl>
         `,
   };
@@ -286,18 +286,18 @@ function generateGroupNav(
     wikiData,
   }
 ) {
-  const { groupData, wikiInfo } = wikiData;
+  const {groupData, wikiInfo} = wikiData;
 
   if (!wikiInfo.enableGroupUI) {
-    return { simple: true };
+    return {simple: true};
   }
 
-  const urlKey = isGallery ? "localized.groupGallery" : "localized.groupInfo";
-  const linkKey = isGallery ? "groupGallery" : "groupInfo";
+  const urlKey = isGallery ? 'localized.groupGallery' : 'localized.groupInfo';
+  const linkKey = isGallery ? 'groupGallery' : 'groupInfo';
 
   const infoGalleryLinks = generateInfoGalleryLinks(currentGroup, isGallery, {
-    linkKeyGallery: "groupGallery",
-    linkKeyInfo: "groupInfo",
+    linkKeyGallery: 'groupGallery',
+    linkKeyInfo: 'groupInfo',
   });
 
   const previousNextLinks = generatePreviousNextLinks(currentGroup, {
@@ -306,16 +306,16 @@ function generateGroupNav(
   });
 
   return {
-    linkContainerClasses: ["nav-links-hierarchy"],
+    linkContainerClasses: ['nav-links-hierarchy'],
     links: [
-      { toHome: true },
+      {toHome: true},
       wikiInfo.enableListings && {
-        path: ["localized.listingIndex"],
-        title: language.$("listingIndex.title"),
+        path: ['localized.listingIndex'],
+        title: language.$('listingIndex.title'),
       },
       {
-        html: language.$("groupPage.nav.group", {
-          group: link[linkKey](currentGroup, { class: "current" }),
+        html: language.$('groupPage.nav.group', {
+          group: link[linkKey](currentGroup, {class: 'current'}),
         }),
       },
       {
diff --git a/src/page/homepage.js b/src/page/homepage.js
index 8d20acc..7701a73 100644
--- a/src/page/homepage.js
+++ b/src/page/homepage.js
@@ -1,23 +1,23 @@
-// @format
-//
+/** @format */
+
 // Homepage specification.
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
-import * as html from "../util/html.js";
+import * as html from '../util/html.js';
 
-import { getNewAdditions, getNewReleases } from "../util/wiki-data.js";
+import {getNewAdditions, getNewReleases} from '../util/wiki-data.js';
 
 // Page exports
 
-export function writeTargetless({ wikiData }) {
-  const { newsData, staticPageData, homepageLayout, wikiInfo } = wikiData;
+export function writeTargetless({wikiData}) {
+  const {newsData, staticPageData, homepageLayout, wikiInfo} = wikiData;
 
   const page = {
-    type: "page",
-    path: ["home"],
+    type: 'page',
+    path: ['home'],
     page: ({
       getAlbumGridHTML,
       getLinkThemeString,
@@ -35,7 +35,7 @@ export function writeTargetless({ wikiData }) {
       },
 
       main: {
-        classes: ["top-index"],
+        classes: ['top-index'],
         content: fixWS`
                     <h1>${wikiInfo.name}</h1>
                     ${homepageLayout.rows
@@ -46,21 +46,21 @@ export function writeTargetless({ wikiData }) {
                         )}">
                             <h2>${row.name}</h2>
                             ${
-                              row.type === "albums" &&
+                              row.type === 'albums' &&
                               fixWS`
                                 <div class="grid-listing">
                                     ${getAlbumGridHTML({
                                       entries: (row.sourceGroupByRef ===
-                                      "new-releases"
+                                      'new-releases'
                                         ? getNewReleases(
                                             row.countAlbumsFromGroup,
-                                            { wikiData }
+                                            {wikiData}
                                           )
                                         : row.sourceGroupByRef ===
-                                          "new-additions"
+                                          'new-additions'
                                         ? getNewAdditions(
                                             row.countAlbumsFromGroup,
-                                            { wikiData }
+                                            {wikiData}
                                           )
                                         : (row.sourceGroup?.albums ?? [])
                                             .slice()
@@ -70,7 +70,7 @@ export function writeTargetless({ wikiData }) {
                                                 album.isListedOnHomepage
                                             )
                                             .slice(0, row.countAlbumsFromGroup)
-                                            .map((album) => ({ item: album }))
+                                            .map((album) => ({item: album}))
                                       ).concat(
                                         row.sourceAlbums.map((album) => ({
                                           item: album,
@@ -85,11 +85,11 @@ export function writeTargetless({ wikiData }) {
                                             ${row.actionLinks
                                               .map((action) =>
                                                 transformInline(action).replace(
-                                                  "<a",
+                                                  '<a',
                                                   '<a class="box grid-item"'
                                                 )
                                               )
-                                              .join("\n")}
+                                              .join('\n')}
                                         </div>
                                     `
                                     }
@@ -99,7 +99,7 @@ export function writeTargetless({ wikiData }) {
                         </section>
                     `
                       )
-                      .join("\n")}
+                      .join('\n')}
                 `,
       },
 
@@ -117,21 +117,21 @@ export function writeTargetless({ wikiData }) {
         // And no, I will not make [[news]] into part of transformMultiline
         // (even though that would 8e hilarious).
         content: transformMultiline(
-          homepageLayout.sidebarContent.replace("[[news]]", "__GENERATE_NEWS__")
+          homepageLayout.sidebarContent.replace('[[news]]', '__GENERATE_NEWS__')
         ).replace(
-          "<p>__GENERATE_NEWS__</p>",
+          '<p>__GENERATE_NEWS__</p>',
           wikiInfo.enableNews
             ? fixWS`
-                        <h1>${language.$("homepage.news.title")}</h1>
+                        <h1>${language.$('homepage.news.title')}</h1>
                         ${newsData
                           .slice(0, 3)
                           .map((entry, i) =>
                             html.tag(
-                              "article",
+                              'article',
                               {
                                 class: [
-                                  "news-entry",
-                                  i === 0 && "first-news-entry",
+                                  'news-entry',
+                                  i === 0 && 'first-news-entry',
                                 ],
                               },
                               fixWS`
@@ -143,42 +143,42 @@ export function writeTargetless({ wikiData }) {
                                   entry.contentShort !== entry.content &&
                                   link.newsEntry(entry, {
                                     text: language.$(
-                                      "homepage.news.entry.viewRest"
+                                      'homepage.news.entry.viewRest'
                                     ),
                                   })
                                 }
                             `
                             )
                           )
-                          .join("\n")}
+                          .join('\n')}
                     `
             : `<p><i>News requested in content description but this feature isn't enabled</i></p>`
         ),
       },
 
       nav: {
-        linkContainerClasses: ["nav-links-index"],
+        linkContainerClasses: ['nav-links-index'],
         links: [
-          link.home("", { text: wikiInfo.nameShort, class: "current", to }),
+          link.home('', {text: wikiInfo.nameShort, class: 'current', to}),
 
           wikiInfo.enableListings &&
-            link.listingIndex("", {
-              text: language.$("listingIndex.title"),
+            link.listingIndex('', {
+              text: language.$('listingIndex.title'),
               to,
             }),
 
           wikiInfo.enableNews &&
-            link.newsIndex("", { text: language.$("newsIndex.title"), to }),
+            link.newsIndex('', {text: language.$('newsIndex.title'), to}),
 
           wikiInfo.enableFlashesAndGames &&
-            link.flashIndex("", { text: language.$("flashIndex.title"), to }),
+            link.flashIndex('', {text: language.$('flashIndex.title'), to}),
 
           ...staticPageData
             .filter((page) => page.showInNavigationBar)
-            .map((page) => link.staticPage(page, { text: page.nameShort })),
+            .map((page) => link.staticPage(page, {text: page.nameShort})),
         ]
           .filter(Boolean)
-          .map((html) => ({ html })),
+          .map((html) => ({html})),
       },
     }),
   };
diff --git a/src/page/index.js b/src/page/index.js
index 3d7e6fa..149503f 100644
--- a/src/page/index.js
+++ b/src/page/index.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // NB: This is the index for the page/ directory and contains exports for all
 // other modules here! It's not the page spec for the homepage - see
 // homepage.js for that.
@@ -41,15 +41,15 @@
 // These functions should be referenced only from adjacent modules, as they
 // pertain only to site page generation.
 
-export * as album from "./album.js";
-export * as albumCommentary from "./album-commentary.js";
-export * as artist from "./artist.js";
-export * as artistAlias from "./artist-alias.js";
-export * as flash from "./flash.js";
-export * as group from "./group.js";
-export * as homepage from "./homepage.js";
-export * as listing from "./listing.js";
-export * as news from "./news.js";
-export * as static from "./static.js";
-export * as tag from "./tag.js";
-export * as track from "./track.js";
+export * as album from './album.js';
+export * as albumCommentary from './album-commentary.js';
+export * as artist from './artist.js';
+export * as artistAlias from './artist-alias.js';
+export * as flash from './flash.js';
+export * as group from './group.js';
+export * as homepage from './homepage.js';
+export * as listing from './listing.js';
+export * as news from './news.js';
+export * as static from './static.js';
+export * as tag from './tag.js';
+export * as track from './track.js';
diff --git a/src/page/listing.js b/src/page/listing.js
index 69fa491..90415de 100644
--- a/src/page/listing.js
+++ b/src/page/listing.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // Listing page specification.
 //
 // The targets here are a bit different than for most pages: rather than data
@@ -12,36 +12,36 @@
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
-import * as html from "../util/html.js";
+import * as html from '../util/html.js';
 
-import { getTotalDuration } from "../util/wiki-data.js";
+import {getTotalDuration} from '../util/wiki-data.js';
 
 // Page exports
 
-export function condition({ wikiData }) {
+export function condition({wikiData}) {
   return wikiData.wikiInfo.enableListings;
 }
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.listingSpec;
 }
 
-export function write(listing, { wikiData }) {
-  if (listing.condition && !listing.condition({ wikiData })) {
+export function write(listing, {wikiData}) {
+  if (listing.condition && !listing.condition({wikiData})) {
     return null;
   }
 
-  const { wikiInfo } = wikiData;
+  const {wikiInfo} = wikiData;
 
-  const data = listing.data ? listing.data({ wikiData }) : null;
+  const data = listing.data ? listing.data({wikiData}) : null;
 
   const page = {
-    type: "page",
-    path: ["listing", listing.directory],
+    type: 'page',
+    path: ['listing', listing.directory],
     page: (opts) => {
-      const { getLinkThemeString, link, language } = opts;
+      const {getLinkThemeString, link, language} = opts;
       const titleKey = `listingPage.${listing.stringsKey}.title`;
 
       return {
@@ -63,7 +63,7 @@ export function write(listing, { wikiData }) {
                                 ${data
                                   .map((item) => listing.row(item, opts))
                                   .map((row) => `<li>${row}</li>`)
-                                  .join("\n")}
+                                  .join('\n')}
                             </ul>
                         `
                         }
@@ -80,14 +80,14 @@ export function write(listing, { wikiData }) {
         },
 
         nav: {
-          linkContainerClasses: ["nav-links-hierarchy"],
+          linkContainerClasses: ['nav-links-hierarchy'],
           links: [
-            { toHome: true },
+            {toHome: true},
             {
-              path: ["localized.listingIndex"],
-              title: language.$("listingIndex.title"),
+              path: ['localized.listingIndex'],
+              title: language.$('listingIndex.title'),
             },
-            { toCurrentPage: true },
+            {toCurrentPage: true},
           ],
         },
       };
@@ -97,21 +97,21 @@ export function write(listing, { wikiData }) {
   return [page];
 }
 
-export function writeTargetless({ wikiData }) {
-  const { albumData, trackData, wikiInfo } = wikiData;
+export function writeTargetless({wikiData}) {
+  const {albumData, trackData, wikiInfo} = wikiData;
 
   const totalDuration = getTotalDuration(trackData);
 
   const page = {
-    type: "page",
-    path: ["listingIndex"],
-    page: ({ getLinkThemeString, language, link }) => ({
-      title: language.$("listingIndex.title"),
+    type: 'page',
+    path: ['listingIndex'],
+    page: ({getLinkThemeString, language, link}) => ({
+      title: language.$('listingIndex.title'),
 
       main: {
         content: fixWS`
-                    <h1>${language.$("listingIndex.title")}</h1>
-                    <p>${language.$("listingIndex.infoLine", {
+                    <h1>${language.$('listingIndex.title')}</h1>
+                    <p>${language.$('listingIndex.infoLine', {
                       wiki: wikiInfo.name,
                       tracks: `<b>${language.countTracks(trackData.length, {
                         unit: true,
@@ -125,7 +125,7 @@ export function writeTargetless({ wikiData }) {
                       })}</b>`,
                     })}</p>
                     <hr>
-                    <p>${language.$("listingIndex.exploreList")}</p>
+                    <p>${language.$('listingIndex.exploreList')}</p>
                     ${generateLinkIndexForListings(null, false, {
                       link,
                       language,
@@ -143,7 +143,7 @@ export function writeTargetless({ wikiData }) {
         }),
       },
 
-      nav: { simple: true },
+      nav: {simple: true},
     }),
   };
 
@@ -154,11 +154,11 @@ export function writeTargetless({ wikiData }) {
 
 function generateSidebarForListings(
   currentListing,
-  { getLinkThemeString, link, language, wikiData }
+  {getLinkThemeString, link, language, wikiData}
 ) {
   return fixWS`
-        <h1>${link.listingIndex("", {
-          text: language.$("listingIndex.title"),
+        <h1>${link.listingIndex('', {
+          text: language.$('listingIndex.title'),
         })}</h1>
         ${generateLinkIndexForListings(currentListing, true, {
           getLinkThemeString,
@@ -172,24 +172,24 @@ function generateSidebarForListings(
 function generateLinkIndexForListings(
   currentListing,
   forSidebar,
-  { getLinkThemeString, link, language, wikiData }
+  {getLinkThemeString, link, language, wikiData}
 ) {
-  const { listingTargetSpec, wikiInfo } = wikiData;
+  const {listingTargetSpec, wikiInfo} = wikiData;
 
   const filteredByCondition = listingTargetSpec
-    .map(({ listings, ...rest }) => ({
+    .map(({listings, ...rest}) => ({
       ...rest,
-      listings: listings.filter(({ condition: c }) => !c || c({ wikiData })),
+      listings: listings.filter(({condition: c}) => !c || c({wikiData})),
     }))
-    .filter(({ listings }) => listings.length > 0);
+    .filter(({listings}) => listings.length > 0);
 
   const genUL = (listings) =>
     html.tag(
-      "ul",
+      'ul',
       listings.map((listing) =>
         html.tag(
-          "li",
-          { class: [listing === currentListing && "current"] },
+          'li',
+          {class: [listing === currentListing && 'current']},
           link.listing(listing, {
             text: language.$(`listingPage.${listing.stringsKey}.title.short`),
           })
@@ -199,30 +199,30 @@ function generateLinkIndexForListings(
 
   if (forSidebar) {
     return filteredByCondition
-      .map(({ title, listings }) =>
+      .map(({title, listings}) =>
         html.tag(
-          "details",
+          'details',
           {
             open: !forSidebar || listings.includes(currentListing),
-            class: listings.includes(currentListing) && "current",
+            class: listings.includes(currentListing) && 'current',
           },
           [
             html.tag(
-              "summary",
-              { style: getLinkThemeString(wikiInfo.color) },
-              html.tag("span", { class: "group-name" }, title({ language }))
+              'summary',
+              {style: getLinkThemeString(wikiInfo.color)},
+              html.tag('span', {class: 'group-name'}, title({language}))
             ),
             genUL(listings),
           ]
         )
       )
-      .join("\n");
+      .join('\n');
   } else {
     return html.tag(
-      "dl",
-      filteredByCondition.flatMap(({ title, listings }) => [
-        html.tag("dt", title({ language })),
-        html.tag("dd", genUL(listings)),
+      'dl',
+      filteredByCondition.flatMap(({title, listings}) => [
+        html.tag('dt', title({language})),
+        html.tag('dd', genUL(listings)),
       ])
     );
   }
diff --git a/src/page/news.js b/src/page/news.js
index b988e34..bf581e4 100644
--- a/src/page/news.js
+++ b/src/page/news.js
@@ -1,40 +1,40 @@
-// @format
-//
+/** @format */
+
 // News entry & index page specifications.
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
 // Page exports
 
-export function condition({ wikiData }) {
+export function condition({wikiData}) {
   return wikiData.wikiInfo.enableNews;
 }
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.newsData;
 }
 
-export function write(entry, { wikiData }) {
+export function write(entry, {wikiData}) {
   const page = {
-    type: "page",
-    path: ["newsEntry", entry.directory],
+    type: 'page',
+    path: ['newsEntry', entry.directory],
     page: ({
       generatePreviousNextLinks,
       link,
       language,
       transformMultiline,
     }) => ({
-      title: language.$("newsEntryPage.title", { entry: entry.name }),
+      title: language.$('newsEntryPage.title', {entry: entry.name}),
 
       main: {
         content: fixWS`
                     <div class="long-content">
-                        <h1>${language.$("newsEntryPage.title", {
+                        <h1>${language.$('newsEntryPage.title', {
                           entry: entry.name,
                         })}</h1>
-                        <p>${language.$("newsEntryPage.published", {
+                        <p>${language.$('newsEntryPage.published', {
                           date: language.formatDate(entry.date),
                         })}</p>
                         ${transformMultiline(entry.content)}
@@ -54,19 +54,19 @@ export function write(entry, { wikiData }) {
   return [page];
 }
 
-export function writeTargetless({ wikiData }) {
-  const { newsData } = wikiData;
+export function writeTargetless({wikiData}) {
+  const {newsData} = wikiData;
 
   const page = {
-    type: "page",
-    path: ["newsIndex"],
-    page: ({ link, language, transformMultiline }) => ({
-      title: language.$("newsIndex.title"),
+    type: 'page',
+    path: ['newsIndex'],
+    page: ({link, language, transformMultiline}) => ({
+      title: language.$('newsIndex.title'),
 
       main: {
         content: fixWS`
                     <div class="long-content news-index">
-                        <h1>${language.$("newsIndex.title")}</h1>
+                        <h1>${language.$('newsIndex.title')}</h1>
                         ${newsData
                           .map(
                             (entry) => fixWS`
@@ -79,19 +79,19 @@ export function writeTargetless({ wikiData }) {
                                   entry.contentShort !== entry.content &&
                                   `<p>${link.newsEntry(entry, {
                                     text: language.$(
-                                      "newsIndex.entry.viewRest"
+                                      'newsIndex.entry.viewRest'
                                     ),
                                   })}</p>`
                                 }
                             </article>
                         `
                           )
-                          .join("\n")}
+                          .join('\n')}
                     </div>
                 `,
       },
 
-      nav: { simple: true },
+      nav: {simple: true},
     }),
   };
 
@@ -102,9 +102,9 @@ export function writeTargetless({ wikiData }) {
 
 function generateNewsEntryNav(
   entry,
-  { generatePreviousNextLinks, link, language, wikiData }
+  {generatePreviousNextLinks, link, language, wikiData}
 ) {
-  const { wikiInfo, newsData } = wikiData;
+  const {wikiInfo, newsData} = wikiData;
 
   // The newsData list is sorted reverse chronologically (newest ones first),
   // so the way we find next/previous entries is flipped from normal.
@@ -112,21 +112,21 @@ function generateNewsEntryNav(
     link,
     language,
     data: newsData.slice().reverse(),
-    linkKey: "newsEntry",
+    linkKey: 'newsEntry',
   });
 
   return {
-    linkContainerClasses: ["nav-links-hierarchy"],
+    linkContainerClasses: ['nav-links-hierarchy'],
     links: [
-      { toHome: true },
+      {toHome: true},
       {
-        path: ["localized.newsIndex"],
-        title: language.$("newsEntryPage.nav.news"),
+        path: ['localized.newsIndex'],
+        title: language.$('newsEntryPage.nav.news'),
       },
       {
-        html: language.$("newsEntryPage.nav.entry", {
+        html: language.$('newsEntryPage.nav.entry', {
           date: language.formatDate(entry.date),
-          entry: link.newsEntry(entry, { class: "current" }),
+          entry: link.newsEntry(entry, {class: 'current'}),
         }),
       },
       previousNextLinks && {
diff --git a/src/page/static.js b/src/page/static.js
index ecd77c9..f4a8197 100644
--- a/src/page/static.js
+++ b/src/page/static.js
@@ -1,24 +1,24 @@
-// @format
-//
+/** @format */
+
 // Static content page specification. (These are static pages coded into the
 // wiki data folder, used for a variety of purposes, e.g. wiki info,
 // changelog, and so on.)
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
 // Page exports
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.staticPageData;
 }
 
-export function write(staticPage, { wikiData }) {
+export function write(staticPage, {wikiData}) {
   const page = {
-    type: "page",
-    path: ["staticPage", staticPage.directory],
-    page: ({ language, transformMultiline }) => ({
+    type: 'page',
+    path: ['staticPage', staticPage.directory],
+    page: ({language, transformMultiline}) => ({
       title: staticPage.name,
       stylesheet: staticPage.stylesheet,
 
@@ -31,7 +31,7 @@ export function write(staticPage, { wikiData }) {
                 `,
       },
 
-      nav: { simple: true },
+      nav: {simple: true},
     }),
   };
 
diff --git a/src/page/tag.js b/src/page/tag.js
index efd95e1..4b2322d 100644
--- a/src/page/tag.js
+++ b/src/page/tag.js
@@ -1,34 +1,34 @@
-// @format
-//
+/** @format */
+
 // Art tag page specification.
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
 // Page exports
 
-export function condition({ wikiData }) {
+export function condition({wikiData}) {
   return wikiData.wikiInfo.enableArtTagUI;
 }
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.artTagData.filter((tag) => !tag.isContentWarning);
 }
 
-export function write(tag, { wikiData }) {
-  const { wikiInfo } = wikiData;
-  const { taggedInThings: things } = tag;
+export function write(tag, {wikiData}) {
+  const {wikiInfo} = wikiData;
+  const {taggedInThings: things} = tag;
 
   // Display things featuring this art tag in reverse chronological order,
   // sticking the most recent additions near the top!
   const thingsReversed = things.slice().reverse();
 
-  const entries = thingsReversed.map((item) => ({ item }));
+  const entries = thingsReversed.map((item) => ({item}));
 
   const page = {
-    type: "page",
-    path: ["tag", tag.directory],
+    type: 'page',
+    path: ['tag', tag.directory],
     page: ({
       generatePreviousNextLinks,
       getAlbumCover,
@@ -39,14 +39,14 @@ export function write(tag, { wikiData }) {
       language,
       to,
     }) => ({
-      title: language.$("tagPage.title", { tag: tag.name }),
+      title: language.$('tagPage.title', {tag: tag.name}),
       theme: getThemeString(tag.color),
 
       main: {
-        classes: ["top-index"],
+        classes: ['top-index'],
         content: fixWS`
-                    <h1>${language.$("tagPage.title", { tag: tag.name })}</h1>
-                    <p class="quick-info">${language.$("tagPage.infoLine", {
+                    <h1>${language.$('tagPage.title', {tag: tag.name})}</h1>
+                    <p class="quick-info">${language.$('tagPage.infoLine', {
                       coverArts: language.countCoverArts(things.length, {
                         unit: true,
                       }),
@@ -83,24 +83,24 @@ export function write(tag, { wikiData }) {
 
 function generateTagNav(
   tag,
-  { generatePreviousNextLinks, link, language, wikiData }
+  {generatePreviousNextLinks, link, language, wikiData}
 ) {
   const previousNextLinks = generatePreviousNextLinks(tag, {
     data: wikiData.artTagData.filter((tag) => !tag.isContentWarning),
-    linkKey: "tag",
+    linkKey: 'tag',
   });
 
   return {
-    linkContainerClasses: ["nav-links-hierarchy"],
+    linkContainerClasses: ['nav-links-hierarchy'],
     links: [
-      { toHome: true },
+      {toHome: true},
       wikiData.wikiInfo.enableListings && {
-        path: ["localized.listingIndex"],
-        title: language.$("listingIndex.title"),
+        path: ['localized.listingIndex'],
+        title: language.$('listingIndex.title'),
       },
       {
-        html: language.$("tagPage.nav.tag", {
-          tag: link.tag(tag, { class: "current" }),
+        html: language.$('tagPage.nav.tag', {
+          tag: link.tag(tag, {class: 'current'}),
         }),
       },
       /*
diff --git a/src/page/track.js b/src/page/track.js
index 5827197..a9758ec 100644
--- a/src/page/track.js
+++ b/src/page/track.js
@@ -1,37 +1,37 @@
-// @format
-//
+/** @format */
+
 // Track page specification.
 
 // Imports
 
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 
 import {
   generateAlbumChronologyLinks,
   generateAlbumNavLinks,
   generateAlbumSecondaryNav,
   generateAlbumSidebar,
-} from "./album.js";
+} from './album.js';
 
-import * as html from "../util/html.js";
+import * as html from '../util/html.js';
 
-import { bindOpts } from "../util/sugar.js";
+import {bindOpts} from '../util/sugar.js';
 
 import {
   getTrackCover,
   getAlbumListTag,
   sortChronologically,
-} from "../util/wiki-data.js";
+} from '../util/wiki-data.js';
 
 // Page exports
 
-export function targets({ wikiData }) {
+export function targets({wikiData}) {
   return wikiData.trackData;
 }
 
-export function write(track, { wikiData }) {
-  const { groupData, wikiInfo } = wikiData;
-  const { album, referencedByTracks, referencedTracks, otherReleases } = track;
+export function write(track, {wikiData}) {
+  const {groupData, wikiInfo} = wikiData;
+  const {album, referencedByTracks, referencedTracks, otherReleases} = track;
 
   const listTag = getAlbumListTag(album);
 
@@ -50,12 +50,12 @@ export function write(track, { wikiData }) {
     );
   }
 
-  const unbound_getTrackItem = (track, { getArtistString, link, language }) =>
+  const unbound_getTrackItem = (track, {getArtistString, link, language}) =>
     html.tag(
-      "li",
-      language.$("trackList.item.withArtists", {
+      'li',
+      language.$('trackList.item.withArtists', {
         track: link.track(track),
-        by: `<span class="by">${language.$("trackList.item.withArtists.by", {
+        by: `<span class="by">${language.$('trackList.item.withArtists.by', {
           artists: getArtistString(track.artistContribs),
         })}</span>`,
       })
@@ -63,46 +63,46 @@ export function write(track, { wikiData }) {
 
   const unbound_generateTrackList = (
     tracks,
-    { getArtistString, link, language }
+    {getArtistString, link, language}
   ) =>
     html.tag(
-      "ul",
+      'ul',
       tracks.map((track) =>
-        unbound_getTrackItem(track, { getArtistString, link, language })
+        unbound_getTrackItem(track, {getArtistString, link, language})
       )
     );
 
   const hasCommentary =
     track.commentary || otherReleases.some((t) => t.commentary);
-  const generateCommentary = ({ link, language, transformMultiline }) =>
+  const generateCommentary = ({link, language, transformMultiline}) =>
     transformMultiline(
       [
         track.commentary,
         ...otherReleases.map((track) =>
           track.commentary
-            ?.split("\n")
-            .filter((line) => line.replace(/<\/b>/g, "").includes(":</i>"))
+            ?.split('\n')
+            .filter((line) => line.replace(/<\/b>/g, '').includes(':</i>'))
             .map(
               (line) => fixWS`
                     ${line}
                     ${language.$(
-                      "releaseInfo.artistCommentary.seeOriginalRelease",
+                      'releaseInfo.artistCommentary.seeOriginalRelease',
                       {
                         original: link.track(track),
                       }
                     )}
                 `
             )
-            .join("\n")
+            .join('\n')
         ),
       ]
         .filter(Boolean)
-        .join("\n")
+        .join('\n')
     );
 
   const data = {
-    type: "data",
-    path: ["track", track.directory],
+    type: 'data',
+    path: ['track', track.directory],
     data: ({
       serializeContribs,
       serializeCover,
@@ -145,19 +145,19 @@ export function write(track, { wikiData }) {
         // they don't get parsed and displayed, generally speaking), so
         // override the link argument so that artist "links" just show
         // their names.
-        link: { artist: (artist) => artist.name },
+        link: {artist: (artist) => artist.name},
       });
-    if (!hasArtists && !hasCoverArtists) return "";
+    if (!hasArtists && !hasCoverArtists) return '';
     return language.formatString(
-      "trackPage.socialEmbed.body" +
-        [hasArtists && ".withArtists", hasCoverArtists && ".withCoverArtists"]
+      'trackPage.socialEmbed.body' +
+        [hasArtists && '.withArtists', hasCoverArtists && '.withCoverArtists']
           .filter(Boolean)
-          .join(""),
+          .join(''),
       Object.fromEntries(
         [
-          hasArtists && ["artists", getArtistString(track.artistContribs)],
+          hasArtists && ['artists', getArtistString(track.artistContribs)],
           hasCoverArtists && [
-            "coverArtists",
+            'coverArtists',
             getArtistString(track.coverArtistContribs),
           ],
         ].filter(Boolean)
@@ -166,8 +166,8 @@ export function write(track, { wikiData }) {
   };
 
   const page = {
-    type: "page",
-    path: ["track", track.directory],
+    type: 'page',
+    path: ['track', track.directory],
     page: ({
       absoluteTo,
       fancifyURL,
@@ -196,24 +196,23 @@ export function write(track, { wikiData }) {
       const cover = getTrackCover(track);
 
       return {
-        title: language.$("trackPage.title", { track: track.name }),
-        stylesheet: getAlbumStylesheet(album, { to }),
+        title: language.$('trackPage.title', {track: track.name}),
+        stylesheet: getAlbumStylesheet(album, {to}),
         theme: getThemeString(track.color, [
           `--album-directory: ${album.directory}`,
           `--track-directory: ${track.directory}`,
         ]),
 
         socialEmbed: {
-          heading: language.$("trackPage.socialEmbed.heading", {
+          heading: language.$('trackPage.socialEmbed.heading', {
             album: track.album.name,
           }),
-          headingLink: absoluteTo("localized.album", album.directory),
-          title: language.$("trackPage.socialEmbed.title", {
+          headingLink: absoluteTo('localized.album', album.directory),
+          title: language.$('trackPage.socialEmbed.title', {
             track: track.name,
           }),
-          description: getSocialEmbedDescription({ getArtistString, language }),
-          image:
-            "/" + getTrackCover(track, { to: urls.from("shared.root").to }),
+          description: getSocialEmbedDescription({getArtistString, language}),
+          image: '/' + getTrackCover(track, {to: urls.from('shared.root').to}),
           color: track.color,
         },
 
@@ -234,23 +233,23 @@ export function write(track, { wikiData }) {
                           cover &&
                           generateCoverLink({
                             src: cover,
-                            alt: language.$("misc.alt.trackCover"),
+                            alt: language.$('misc.alt.trackCover'),
                             tags: track.artTags,
                           })
                         }
-                        <h1>${language.$("trackPage.title", {
+                        <h1>${language.$('trackPage.title', {
                           track: track.name,
                         })}</h1>
                         <p>
                             ${[
-                              language.$("releaseInfo.by", {
+                              language.$('releaseInfo.by', {
                                 artists: getArtistString(track.artistContribs, {
                                   showContrib: true,
                                   showIcons: true,
                                 }),
                               }),
                               track.coverArtistContribs.length &&
-                                language.$("releaseInfo.coverArtBy", {
+                                language.$('releaseInfo.coverArtBy', {
                                   artists: getArtistString(
                                     track.coverArtistContribs,
                                     {
@@ -260,45 +259,45 @@ export function write(track, { wikiData }) {
                                   ),
                                 }),
                               track.date &&
-                                language.$("releaseInfo.released", {
+                                language.$('releaseInfo.released', {
                                   date: language.formatDate(track.date),
                                 }),
                               track.coverArtDate &&
                                 +track.coverArtDate !== +track.date &&
-                                language.$("releaseInfo.artReleased", {
+                                language.$('releaseInfo.artReleased', {
                                   date: language.formatDate(track.coverArtDate),
                                 }),
                               track.duration &&
-                                language.$("releaseInfo.duration", {
+                                language.$('releaseInfo.duration', {
                                   duration: language.formatDuration(
                                     track.duration
                                   ),
                                 }),
                             ]
                               .filter(Boolean)
-                              .join("<br>\n")}
+                              .join('<br>\n')}
                         </p>
                         <p>${
                           track.urls?.length
-                            ? language.$("releaseInfo.listenOn", {
+                            ? language.$('releaseInfo.listenOn', {
                                 links: language.formatDisjunctionList(
                                   track.urls.map((url) =>
-                                    fancifyURL(url, { language })
+                                    fancifyURL(url, {language})
                                   )
                                 ),
                               })
-                            : language.$("releaseInfo.listenOn.noLinks")
+                            : language.$('releaseInfo.listenOn.noLinks')
                         }</p>
                         ${
                           otherReleases.length &&
                           fixWS`
-                            <p>${language.$("releaseInfo.alsoReleasedAs")}</p>
+                            <p>${language.$('releaseInfo.alsoReleasedAs')}</p>
                             <ul>
                                 ${otherReleases
                                   .map(
                                     (track) => fixWS`
                                     <li>${language.$(
-                                      "releaseInfo.alsoReleasedAs.item",
+                                      'releaseInfo.alsoReleasedAs.item',
                                       {
                                         track: link.track(track),
                                         album: link.album(track.album),
@@ -306,14 +305,14 @@ export function write(track, { wikiData }) {
                                     )}</li>
                                 `
                                   )
-                                  .join("\n")}
+                                  .join('\n')}
                             </ul>
                         `
                         }
                         ${
                           track.contributorContribs.length &&
                           fixWS`
-                            <p>${language.$("releaseInfo.contributors")}</p>
+                            <p>${language.$('releaseInfo.contributors')}</p>
                             <ul>
                                 ${track.contributorContribs
                                   .map(
@@ -323,18 +322,18 @@ export function write(track, { wikiData }) {
                                         showIcons: true,
                                       })}</li>`
                                   )
-                                  .join("\n")}
+                                  .join('\n')}
                             </ul>
                         `
                         }
                         ${
                           referencedTracks.length &&
                           fixWS`
-                            <p>${language.$("releaseInfo.tracksReferenced", {
+                            <p>${language.$('releaseInfo.tracksReferenced', {
                               track: `<i>${track.name}</i>`,
                             })}</p>
                             ${html.tag(
-                              "ul",
+                              'ul',
                               referencedTracks.map(getTrackItem)
                             )}
                         `
@@ -342,7 +341,7 @@ export function write(track, { wikiData }) {
                         ${
                           referencedByTracks.length &&
                           fixWS`
-                            <p>${language.$("releaseInfo.tracksThatReference", {
+                            <p>${language.$('releaseInfo.tracksThatReference', {
                               track: `<i>${track.name}</i>`,
                             })}</p>
                             ${generateTrackListDividedByGroups(
@@ -358,24 +357,24 @@ export function write(track, { wikiData }) {
                           wikiInfo.enableFlashesAndGames &&
                           flashesThatFeature.length &&
                           fixWS`
-                            <p>${language.$("releaseInfo.flashesThatFeature", {
+                            <p>${language.$('releaseInfo.flashesThatFeature', {
                               track: `<i>${track.name}</i>`,
                             })}</p>
                             <ul>
                                 ${flashesThatFeature
-                                  .map(({ flash, as }) =>
+                                  .map(({flash, as}) =>
                                     html.tag(
-                                      "li",
-                                      { class: as !== track && "rerelease" },
+                                      'li',
+                                      {class: as !== track && 'rerelease'},
                                       as === track
                                         ? language.$(
-                                            "releaseInfo.flashesThatFeature.item",
+                                            'releaseInfo.flashesThatFeature.item',
                                             {
                                               flash: link.flash(flash),
                                             }
                                           )
                                         : language.$(
-                                            "releaseInfo.flashesThatFeature.item.asDifferentRelease",
+                                            'releaseInfo.flashesThatFeature.item.asDifferentRelease',
                                             {
                                               flash: link.flash(flash),
                                               track: link.track(as),
@@ -383,14 +382,14 @@ export function write(track, { wikiData }) {
                                           )
                                     )
                                   )
-                                  .join("\n")}
+                                  .join('\n')}
                             </ul>
                         `
                         }
                         ${
                           track.lyrics &&
                           fixWS`
-                            <p>${language.$("releaseInfo.lyrics")}</p>
+                            <p>${language.$('releaseInfo.lyrics')}</p>
                             <blockquote>
                                 ${transformLyrics(track.lyrics)}
                             </blockquote>
@@ -399,7 +398,7 @@ export function write(track, { wikiData }) {
                         ${
                           hasCommentary &&
                           fixWS`
-                            <p>${language.$("releaseInfo.artistCommentary")}</p>
+                            <p>${language.$('releaseInfo.artistCommentary')}</p>
                             <blockquote>
                                 ${generateCommentary({
                                   link,
@@ -422,23 +421,23 @@ export function write(track, { wikiData }) {
         }),
 
         nav: {
-          linkContainerClasses: ["nav-links-hierarchy"],
+          linkContainerClasses: ['nav-links-hierarchy'],
           links: [
-            { toHome: true },
+            {toHome: true},
             {
-              path: ["localized.album", album.directory],
+              path: ['localized.album', album.directory],
               title: album.name,
             },
-            listTag === "ol"
+            listTag === 'ol'
               ? {
-                  html: language.$("trackPage.nav.track.withNumber", {
+                  html: language.$('trackPage.nav.track.withNumber', {
                     number: album.tracks.indexOf(track) + 1,
-                    track: link.track(track, { class: "current", to }),
+                    track: link.track(track, {class: 'current', to}),
                   }),
                 }
               : {
-                  html: language.$("trackPage.nav.track", {
-                    track: link.track(track, { class: "current", to }),
+                  html: language.$('trackPage.nav.track', {
+                    track: link.track(track, {class: 'current', to}),
                   }),
                 },
           ].filter(Boolean),
diff --git a/src/repl.js b/src/repl.js
index 5826d23..5a6334a 100644
--- a/src/repl.js
+++ b/src/repl.js
@@ -1,53 +1,53 @@
-// @format
+/** @format */
 
-import * as os from "os";
-import * as path from "path";
-import * as repl from "repl";
-import { fileURLToPath } from "url";
-import { promisify } from "util";
+import * as os from 'os';
+import * as path from 'path';
+import * as repl from 'repl';
+import {fileURLToPath} from 'url';
+import {promisify} from 'util';
 
-import { quickLoadAllFromYAML } from "./data/yaml.js";
-import { logError, parseOptions } from "./util/cli.js";
-import { showAggregate } from "./util/sugar.js";
+import {quickLoadAllFromYAML} from './data/yaml.js';
+import {logError, parseOptions} from './util/cli.js';
+import {showAggregate} from './util/sugar.js';
 
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
 
 async function main() {
   const miscOptions = await parseOptions(process.argv.slice(2), {
-    "data-path": {
-      type: "value",
+    'data-path': {
+      type: 'value',
     },
 
-    "show-traces": {
-      type: "flag",
+    'show-traces': {
+      type: 'flag',
     },
 
-    "no-history": {
-      type: "flag",
+    'no-history': {
+      type: 'flag',
     },
   });
 
-  const dataPath = miscOptions["data-path"] || process.env.HSMUSIC_DATA;
-  const showAggregateTraces = miscOptions["show-traces"] ?? false;
-  const disableHistory = miscOptions["no-history"] ?? false;
+  const dataPath = miscOptions['data-path'] || process.env.HSMUSIC_DATA;
+  const showAggregateTraces = miscOptions['show-traces'] ?? false;
+  const disableHistory = miscOptions['no-history'] ?? false;
 
   if (!dataPath) {
     logError`Expected --data-path option or HSMUSIC_DATA to be set`;
     return;
   }
 
-  console.log("HSMusic data REPL");
+  console.log('HSMusic data REPL');
 
   const wikiData = await quickLoadAllFromYAML(dataPath);
   const replServer = repl.start();
 
-  Object.assign(replServer.context, wikiData, { wikiData, WD: wikiData });
+  Object.assign(replServer.context, wikiData, {wikiData, WD: wikiData});
 
   if (disableHistory) {
     console.log(`\rInput history disabled (--no-history provided)`);
     replServer.displayPrompt(true);
   } else {
-    const historyFile = path.join(os.homedir(), ".hsmusic_repl_history");
+    const historyFile = path.join(os.homedir(), '.hsmusic_repl_history');
     replServer.setupHistory(historyFile, (err) => {
       if (err) {
         console.error(
diff --git a/src/static/client.js b/src/static/client.js
index d68358b..8342eb1 100644
--- a/src/static/client.js
+++ b/src/static/client.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // This is the JS file that gets loaded on the client! It's only really used for
 // the random track feature right now - the idea is we only use it for stuff
 // that cannot 8e done at static-site compile time, 8y its fundamentally
@@ -7,9 +7,9 @@
 //
 // Upd8: As of 04/02/2021, it's now used for info cards too! Nice.
 
-import { getColors } from "../util/colors.js";
+import {getColors} from '../util/colors.js';
 
-import { getArtistNumContributions } from "../util/wiki-data.js";
+import {getArtistNumContributions} from '../util/wiki-data.js';
 
 let albumData, artistData, flashData;
 let officialAlbumData, fandomAlbumData, artistNames;
@@ -18,25 +18,25 @@ let ready = false;
 
 // Localiz8tion nonsense ----------------------------------
 
-const language = document.documentElement.getAttribute("lang");
+const language = document.documentElement.getAttribute('lang');
 
 let list;
-if (typeof Intl === "object" && typeof Intl.ListFormat === "function") {
+if (typeof Intl === 'object' && typeof Intl.ListFormat === 'function') {
   const getFormat = (type) => {
-    const formatter = new Intl.ListFormat(language, { type });
+    const formatter = new Intl.ListFormat(language, {type});
     return formatter.format.bind(formatter);
   };
 
   list = {
-    conjunction: getFormat("conjunction"),
-    disjunction: getFormat("disjunction"),
-    unit: getFormat("unit"),
+    conjunction: getFormat('conjunction'),
+    disjunction: getFormat('disjunction'),
+    unit: getFormat('unit'),
   };
 } else {
   // Not a gr8 mock we've got going here, 8ut it's *mostly* language-free.
   // We use the same mock for every list 'cuz we don't have any of the
   // necessary CLDR info to appropri8tely distinguish 8etween them.
-  const arbitraryMock = (array) => array.join(", ");
+  const arbitraryMock = (array) => array.join(', ');
 
   list = {
     conjunction: arbitraryMock,
@@ -47,8 +47,8 @@ if (typeof Intl === "object" && typeof Intl.ListFormat === "function") {
 
 // Miscellaneous helpers ----------------------------------
 
-function rebase(href, rebaseKey = "rebaseLocalized") {
-  const relative = (document.documentElement.dataset[rebaseKey] || ".") + "/";
+function rebase(href, rebaseKey = 'rebaseLocalized') {
+  const relative = (document.documentElement.dataset[rebaseKey] || '.') + '/';
   if (relative) {
     return relative + href;
   } else {
@@ -65,16 +65,16 @@ function cssProp(el, key) {
 }
 
 function getRefDirectory(ref) {
-  return ref.split(":")[1];
+  return ref.split(':')[1];
 }
 
 function getAlbum(el) {
-  const directory = cssProp(el, "--album-directory");
+  const directory = cssProp(el, '--album-directory');
   return albumData.find((album) => album.directory === directory);
 }
 
 function getFlash(el) {
-  const directory = cssProp(el, "--flash-directory");
+  const directory = cssProp(el, '--flash-directory');
   return flashData.find((flash) => flash.directory === directory);
 }
 
@@ -88,17 +88,17 @@ const openFlash = (d) => rebase(`flash/${d}`);
 
 function getTrackListAndIndex() {
   const album = getAlbum(document.body);
-  const directory = cssProp(document.body, "--track-directory");
+  const directory = cssProp(document.body, '--track-directory');
   if (!directory && !album) return {};
-  if (!directory) return { list: album.tracks };
+  if (!directory) return {list: album.tracks};
   const trackIndex = album.tracks.findIndex(
     (track) => track.directory === directory
   );
-  return { list: album.tracks, index: trackIndex };
+  return {list: album.tracks, index: trackIndex};
 }
 
 function openRandomTrack() {
-  const { list } = getTrackListAndIndex();
+  const {list} = getTrackListAndIndex();
   if (!list) return;
   return openTrack(pick(list));
 }
@@ -106,38 +106,38 @@ function openRandomTrack() {
 function getFlashListAndIndex() {
   const list = flashData.filter((flash) => !flash.act8r8k);
   const flash = getFlash(document.body);
-  if (!flash) return { list };
+  if (!flash) return {list};
   const flashIndex = list.indexOf(flash);
-  return { list, index: flashIndex };
+  return {list, index: flashIndex};
 }
 
 // TODO: This should also use urlSpec.
 function fetchData(type, directory) {
-  return fetch(rebase(`${type}/${directory}/data.json`, "rebaseData")).then(
+  return fetch(rebase(`${type}/${directory}/data.json`, 'rebaseData')).then(
     (res) => res.json()
   );
 }
 
 // JS-based links -----------------------------------------
 
-for (const a of document.body.querySelectorAll("[data-random]")) {
-  a.addEventListener("click", (evt) => {
+for (const a of document.body.querySelectorAll('[data-random]')) {
+  a.addEventListener('click', (evt) => {
     if (!ready) {
       evt.preventDefault();
       return;
     }
 
     setTimeout(() => {
-      a.href = rebase("js-disabled");
+      a.href = rebase('js-disabled');
     });
     switch (a.dataset.random) {
-      case "album":
+      case 'album':
         return (a.href = openAlbum(pick(albumData).directory));
-      case "album-in-fandom":
+      case 'album-in-fandom':
         return (a.href = openAlbum(pick(fandomAlbumData).directory));
-      case "album-in-official":
+      case 'album-in-official':
         return (a.href = openAlbum(pick(officialAlbumData).directory));
-      case "track":
+      case 'track':
         return (a.href = openTrack(
           getRefDirectory(
             pick(
@@ -145,9 +145,9 @@ for (const a of document.body.querySelectorAll("[data-random]")) {
             )
           )
         ));
-      case "track-in-album":
+      case 'track-in-album':
         return (a.href = openTrack(getRefDirectory(pick(getAlbum(a).tracks))));
-      case "track-in-fandom":
+      case 'track-in-fandom':
         return (a.href = openTrack(
           getRefDirectory(
             pick(
@@ -158,7 +158,7 @@ for (const a of document.body.querySelectorAll("[data-random]")) {
             )
           )
         ));
-      case "track-in-official":
+      case 'track-in-official':
         return (a.href = openTrack(
           getRefDirectory(
             pick(
@@ -169,9 +169,9 @@ for (const a of document.body.querySelectorAll("[data-random]")) {
             )
           )
         ));
-      case "artist":
+      case 'artist':
         return (a.href = openArtist(pick(artistData).directory));
-      case "artist-more-than-one-contrib":
+      case 'artist-more-than-one-contrib':
         return (a.href = openArtist(
           pick(
             artistData.filter((artist) => getArtistNumContributions(artist) > 1)
@@ -181,51 +181,51 @@ for (const a of document.body.querySelectorAll("[data-random]")) {
   });
 }
 
-const next = document.getElementById("next-button");
-const previous = document.getElementById("previous-button");
-const random = document.getElementById("random-button");
+const next = document.getElementById('next-button');
+const previous = document.getElementById('previous-button');
+const random = document.getElementById('random-button');
 
 const prependTitle = (el, prepend) => {
-  const existing = el.getAttribute("title");
+  const existing = el.getAttribute('title');
   if (existing) {
-    el.setAttribute("title", prepend + " " + existing);
+    el.setAttribute('title', prepend + ' ' + existing);
   } else {
-    el.setAttribute("title", prepend);
+    el.setAttribute('title', prepend);
   }
 };
 
-if (next) prependTitle(next, "(Shift+N)");
-if (previous) prependTitle(previous, "(Shift+P)");
-if (random) prependTitle(random, "(Shift+R)");
+if (next) prependTitle(next, '(Shift+N)');
+if (previous) prependTitle(previous, '(Shift+P)');
+if (random) prependTitle(random, '(Shift+R)');
 
-document.addEventListener("keypress", (event) => {
+document.addEventListener('keypress', (event) => {
   if (event.shiftKey) {
-    if (event.charCode === "N".charCodeAt(0)) {
+    if (event.charCode === 'N'.charCodeAt(0)) {
       if (next) next.click();
-    } else if (event.charCode === "P".charCodeAt(0)) {
+    } else if (event.charCode === 'P'.charCodeAt(0)) {
       if (previous) previous.click();
-    } else if (event.charCode === "R".charCodeAt(0)) {
+    } else if (event.charCode === 'R'.charCodeAt(0)) {
       if (random && ready) random.click();
     }
   }
 });
 
-for (const reveal of document.querySelectorAll(".reveal")) {
-  reveal.addEventListener("click", (event) => {
-    if (!reveal.classList.contains("revealed")) {
-      reveal.classList.add("revealed");
+for (const reveal of document.querySelectorAll('.reveal')) {
+  reveal.addEventListener('click', (event) => {
+    if (!reveal.classList.contains('revealed')) {
+      reveal.classList.add('revealed');
       event.preventDefault();
       event.stopPropagation();
     }
   });
 }
 
-const elements1 = document.getElementsByClassName("js-hide-once-data");
-const elements2 = document.getElementsByClassName("js-show-once-data");
+const elements1 = document.getElementsByClassName('js-hide-once-data');
+const elements2 = document.getElementsByClassName('js-show-once-data');
 
-for (const element of elements1) element.style.display = "block";
+for (const element of elements1) element.style.display = 'block';
 
-fetch(rebase("data.json", "rebaseShared"))
+fetch(rebase('data.json', 'rebaseShared'))
   .then((data) => data.json())
   .then((data) => {
     albumData = data.albumData;
@@ -233,17 +233,17 @@ fetch(rebase("data.json", "rebaseShared"))
     flashData = data.flashData;
 
     officialAlbumData = albumData.filter((album) =>
-      album.groups.includes("group:official")
+      album.groups.includes('group:official')
     );
     fandomAlbumData = albumData.filter(
-      (album) => !album.groups.includes("group:official")
+      (album) => !album.groups.includes('group:official')
     );
     artistNames = artistData
       .filter((artist) => !artist.alias)
       .map((artist) => artist.name);
 
-    for (const element of elements1) element.style.display = "none";
-    for (const element of elements2) element.style.display = "block";
+    for (const element of elements1) element.style.display = 'none';
+    for (const element of elements2) element.style.display = 'block';
 
     ready = true;
   });
@@ -260,13 +260,13 @@ let endFastHoverTimeout = null;
 
 function colorLink(a, color) {
   if (color) {
-    const { primary, dim } = getColors(color);
-    a.style.setProperty("--primary-color", primary);
-    a.style.setProperty("--dim-color", dim);
+    const {primary, dim} = getColors(color);
+    a.style.setProperty('--primary-color', primary);
+    a.style.setProperty('--dim-color', dim);
   }
 }
 
-function link(a, type, { name, directory, color }) {
+function link(a, type, {name, directory, color}) {
   colorLink(a, color);
   a.innerText = name;
   a.href = getLinkHref(type, directory);
@@ -284,14 +284,14 @@ function joinElements(type, elements) {
 }
 
 const infoCard = (() => {
-  const container = document.getElementById("info-card-container");
+  const container = document.getElementById('info-card-container');
 
   let cancelShow = false;
   let hideTimeout = null;
   let showing = false;
 
-  container.addEventListener("mouseenter", cancelHide);
-  container.addEventListener("mouseleave", readyHide);
+  container.addEventListener('mouseenter', cancelHide);
+  container.addEventListener('mouseleave', readyHide);
 
   function show(type, target) {
     cancelShow = false;
@@ -307,91 +307,91 @@ const infoCard = (() => {
 
       const rect = target.getBoundingClientRect();
 
-      container.style.setProperty("--primary-color", data.color);
+      container.style.setProperty('--primary-color', data.color);
 
-      container.style.top = window.scrollY + rect.bottom + "px";
-      container.style.left = window.scrollX + rect.left + "px";
+      container.style.top = window.scrollY + rect.bottom + 'px';
+      container.style.left = window.scrollX + rect.left + 'px';
 
       // Use a short timeout to let a currently hidden (or not yet shown)
       // info card teleport to the position set a8ove. (If it's currently
       // shown, it'll transition to that position.)
       setTimeout(() => {
-        container.classList.remove("hide");
-        container.classList.add("show");
+        container.classList.remove('hide');
+        container.classList.add('show');
       }, 50);
 
       // 8asic details.
 
-      const nameLink = container.querySelector(".info-card-name a");
-      link(nameLink, "track", data);
+      const nameLink = container.querySelector('.info-card-name a');
+      link(nameLink, 'track', data);
 
-      const albumLink = container.querySelector(".info-card-album a");
-      link(albumLink, "album", data.album);
+      const albumLink = container.querySelector('.info-card-album a');
+      link(albumLink, 'album', data.album);
 
-      const artistSpan = container.querySelector(".info-card-artists span");
+      const artistSpan = container.querySelector('.info-card-artists span');
       artistSpan.innerHTML = joinElements(
-        "conjunction",
-        data.artists.map(({ artist }) => {
-          const a = document.createElement("a");
-          a.href = getLinkHref("artist", artist.directory);
+        'conjunction',
+        data.artists.map(({artist}) => {
+          const a = document.createElement('a');
+          a.href = getLinkHref('artist', artist.directory);
           a.innerText = artist.name;
           return a;
         })
       );
 
       const coverArtistParagraph = container.querySelector(
-        ".info-card-cover-artists"
+        '.info-card-cover-artists'
       );
-      const coverArtistSpan = coverArtistParagraph.querySelector("span");
+      const coverArtistSpan = coverArtistParagraph.querySelector('span');
       if (data.coverArtists.length) {
-        coverArtistParagraph.style.display = "block";
+        coverArtistParagraph.style.display = 'block';
         coverArtistSpan.innerHTML = joinElements(
-          "conjunction",
-          data.coverArtists.map(({ artist }) => {
-            const a = document.createElement("a");
-            a.href = getLinkHref("artist", artist.directory);
+          'conjunction',
+          data.coverArtists.map(({artist}) => {
+            const a = document.createElement('a');
+            a.href = getLinkHref('artist', artist.directory);
             a.innerText = artist.name;
             return a;
           })
         );
       } else {
-        coverArtistParagraph.style.display = "none";
+        coverArtistParagraph.style.display = 'none';
       }
 
       // Cover art.
 
       const [containerNoReveal, containerReveal] = [
-        container.querySelector(".info-card-art-container.no-reveal"),
-        container.querySelector(".info-card-art-container.reveal"),
+        container.querySelector('.info-card-art-container.no-reveal'),
+        container.querySelector('.info-card-art-container.reveal'),
       ];
 
       const [containerShow, containerHide] = data.cover.warnings.length
         ? [containerReveal, containerNoReveal]
         : [containerNoReveal, containerReveal];
 
-      containerHide.style.display = "none";
-      containerShow.style.display = "block";
+      containerHide.style.display = 'none';
+      containerShow.style.display = 'block';
 
-      const img = containerShow.querySelector(".info-card-art");
-      img.src = rebase(data.cover.paths.small, "rebaseMedia");
+      const img = containerShow.querySelector('.info-card-art');
+      img.src = rebase(data.cover.paths.small, 'rebaseMedia');
 
-      const imgLink = containerShow.querySelector("a");
+      const imgLink = containerShow.querySelector('a');
       colorLink(imgLink, data.color);
-      imgLink.href = rebase(data.cover.paths.original, "rebaseMedia");
+      imgLink.href = rebase(data.cover.paths.original, 'rebaseMedia');
 
       if (containerShow === containerReveal) {
-        const cw = containerShow.querySelector(".info-card-art-warnings");
+        const cw = containerShow.querySelector('.info-card-art-warnings');
         cw.innerText = list.unit(data.cover.warnings);
 
-        const reveal = containerShow.querySelector(".reveal");
-        reveal.classList.remove("revealed");
+        const reveal = containerShow.querySelector('.reveal');
+        reveal.classList.remove('revealed');
       }
     });
   }
 
   function hide() {
-    container.classList.remove("show");
-    container.classList.add("hide");
+    container.classList.remove('show');
+    container.classList.add('hide');
     cancelShow = true;
     showing = false;
   }
@@ -452,7 +452,7 @@ function makeInfoCardLinkHandlers(type) {
 }
 
 const infoCardLinkHandlers = {
-  track: makeInfoCardLinkHandlers("track"),
+  track: makeInfoCardLinkHandlers('track'),
 };
 
 function addInfoCardLinkHandlers(type) {
@@ -471,5 +471,5 @@ function addInfoCardLinkHandlers(type) {
 //     localStorage.tryInfoCards = true;
 //
 if (localStorage.tryInfoCards) {
-  addInfoCardLinkHandlers("track");
+  addInfoCardLinkHandlers('track');
 }
diff --git a/src/static/lazy-loading.js b/src/static/lazy-loading.js
index 5ca9fb3..8f3e8cf 100644
--- a/src/static/lazy-loading.js
+++ b/src/static/lazy-loading.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // Lazy loading! Roll your own. Woot.
 // This file includes a 8unch of fall8acks and stuff like that, and is written
 // with fairly Olden JavaScript(TM), so as to work on pretty much any 8rowser
@@ -27,15 +27,15 @@ function lazyLoadMain() {
   // we'd 8e mutating its value just 8y interacting with the DOM elements it
   // contains. A while loop works just fine, even though you'd think reading
   // over this code that this would 8e an infinitely hanging loop. It isn't!
-  var elements = document.getElementsByClassName("js-hide");
+  var elements = document.getElementsByClassName('js-hide');
   while (elements.length) {
-    elements[0].classList.remove("js-hide");
+    elements[0].classList.remove('js-hide');
   }
 
-  var lazyElements = document.getElementsByClassName("lazy");
+  var lazyElements = document.getElementsByClassName('lazy');
   if (window.IntersectionObserver) {
     observer = new IntersectionObserver(lazyLoad, {
-      rootMargin: "200px",
+      rootMargin: '200px',
       threshold: 1.0,
     });
     for (var i = 0; i < lazyElements.length; i++) {
@@ -44,10 +44,10 @@ function lazyLoadMain() {
   } else {
     for (var i = 0; i < lazyElements.length; i++) {
       var element = lazyElements[i];
-      var original = element.getAttribute("data-original");
-      element.setAttribute("src", original);
+      var original = element.getAttribute('data-original');
+      element.setAttribute('src', original);
     }
   }
 }
 
-document.addEventListener("DOMContentLoaded", lazyLoadMain);
+document.addEventListener('DOMContentLoaded', lazyLoadMain);
diff --git a/src/upd8.js b/src/upd8.js
index cff4313..8a5a287 100755
--- a/src/upd8.js
+++ b/src/upd8.js
@@ -1,6 +1,5 @@
 #!/usr/bin/env node
-
-// @format
+/** @format */
 
 // HEY N8RDS!
 //
@@ -33,18 +32,18 @@
 // Oh yeah, like. Just run this through some relatively recent version of
 // node.js and you'll 8e fine. ...Within the project root. O8viously.
 
-import * as path from "path";
-import { promisify } from "util";
-import { fileURLToPath } from "url";
+import * as path from 'path';
+import {promisify} from 'util';
+import {fileURLToPath} from 'url';
 
 // I made this dependency myself! A long, long time ago. It is pro8a8ly my
 // most useful li8rary ever. I'm not sure 8esides me actually uses it, though.
-import fixWS from "fix-whitespace";
+import fixWS from 'fix-whitespace';
 // Wait nevermind, I forgot a8out why-do-kids-love-the-taste-of-cinnamon-toast-
 // crunch. THAT is my 8est li8rary.
 
 // It stands for "HTML Entities", apparently. Cursed.
-import he from "he";
+import he from 'he';
 
 import {
   copyFile,
@@ -54,25 +53,25 @@ import {
   symlink,
   writeFile,
   unlink,
-} from "fs/promises";
+} from 'fs/promises';
 
-import { inspect as nodeInspect } from "util";
+import {inspect as nodeInspect} from 'util';
 
-import genThumbs from "./gen-thumbs.js";
-import { listingSpec, listingTargetSpec } from "./listing-spec.js";
-import urlSpec from "./url-spec.js";
-import * as pageSpecs from "./page/index.js";
+import genThumbs from './gen-thumbs.js';
+import {listingSpec, listingTargetSpec} from './listing-spec.js';
+import urlSpec from './url-spec.js';
+import * as pageSpecs from './page/index.js';
 
-import find, { bindFind } from "./util/find.js";
-import * as html from "./util/html.js";
-import unbound_link, { getLinkThemeString } from "./util/link.js";
-import { findFiles } from "./util/io.js";
+import find, {bindFind} from './util/find.js';
+import * as html from './util/html.js';
+import unbound_link, {getLinkThemeString} from './util/link.js';
+import {findFiles} from './util/io.js';
 
-import CacheableObject from "./data/cacheable-object.js";
+import CacheableObject from './data/cacheable-object.js';
 
-import { serializeThings } from "./data/serialize.js";
+import {serializeThings} from './data/serialize.js';
 
-import { Language } from "./data/things.js";
+import {Language} from './data/things.js';
 
 import {
   filterDuplicateDirectories,
@@ -81,7 +80,7 @@ import {
   loadAndProcessDataDocuments,
   sortWikiDataArrays,
   WIKI_INFO_FILE,
-} from "./data/yaml.js";
+} from './data/yaml.js';
 
 import {
   fancifyFlashURL,
@@ -103,7 +102,7 @@ import {
   getRevealStringFromWarnings,
   getThemeString,
   iconifyURL,
-} from "./misc-templates.js";
+} from './misc-templates.js';
 
 import {
   color,
@@ -114,9 +113,9 @@ import {
   parseOptions,
   progressPromiseAll,
   ENABLE_COLOR,
-} from "./util/cli.js";
+} from './util/cli.js';
 
-import { validateReplacerSpec, transformInline } from "./util/replacer.js";
+import {validateReplacerSpec, transformInline} from './util/replacer.js';
 
 import {
   chunkByConditions,
@@ -130,7 +129,7 @@ import {
   getKebabCase,
   getTotalDuration,
   getTrackCover,
-} from "./util/wiki-data.js";
+} from './util/wiki-data.js';
 
 import {
   serializeContribs,
@@ -139,7 +138,7 @@ import {
   serializeGroupsForTrack,
   serializeImagePaths,
   serializeLink,
-} from "./util/serialize.js";
+} from './util/serialize.js';
 
 import {
   bindOpts,
@@ -155,23 +154,23 @@ import {
   unique,
   withAggregate,
   withEntries,
-} from "./util/sugar.js";
+} from './util/sugar.js';
 
-import { generateURLs, thumb } from "./util/urls.js";
+import {generateURLs, thumb} from './util/urls.js';
 
 // Pensive emoji!
 import {
   FANDOM_GROUP_DIRECTORY,
   OFFICIAL_GROUP_DIRECTORY,
-} from "./util/magic-constants.js";
+} from './util/magic-constants.js';
 
-import FileSizePreloader from "./file-size-preloader.js";
+import FileSizePreloader from './file-size-preloader.js';
 
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
 
 const CACHEBUST = 10;
 
-const DEFAULT_STRINGS_FILE = "strings-default.json";
+const DEFAULT_STRINGS_FILE = 'strings-default.json';
 
 // Code that's common 8etween the 8uild code (i.e. upd8.js) and gener8ted
 // site code should 8e put here. Which, uh, ~~only really means this one
@@ -180,20 +179,20 @@ const DEFAULT_STRINGS_FILE = "strings-default.json";
 // Rather than hard code it, anything in this directory can 8e shared across
 // 8oth ends of the code8ase.
 // (This gets symlinked into the --data-path directory.)
-const UTILITY_DIRECTORY = "util";
+const UTILITY_DIRECTORY = 'util';
 
 // Code that's used only in the static site! CSS, cilent JS, etc.
 // (This gets symlinked into the --data-path directory.)
-const STATIC_DIRECTORY = "static";
+const STATIC_DIRECTORY = 'static';
 
 // This exists adjacent to index.html for any page with oEmbed metadata.
-const OEMBED_JSON_FILE = "oembed.json";
+const OEMBED_JSON_FILE = 'oembed.json';
 
 // Automatically copied (if present) from media directory to site root.
-const FAVICON_FILE = "favicon.ico";
+const FAVICON_FILE = 'favicon.ico';
 
 function inspect(value) {
-  return nodeInspect(value, { colors: ENABLE_COLOR });
+  return nodeInspect(value, {colors: ENABLE_COLOR});
 }
 
 // Shared varia8les! These are more efficient to access than a shared varia8le
@@ -222,38 +221,38 @@ function splitLines(text) {
 
 const replacerSpec = {
   album: {
-    find: "album",
-    link: "album",
+    find: 'album',
+    link: 'album',
   },
-  "album-commentary": {
-    find: "album",
-    link: "albumCommentary",
+  'album-commentary': {
+    find: 'album',
+    link: 'albumCommentary',
   },
   artist: {
-    find: "artist",
-    link: "artist",
+    find: 'artist',
+    link: 'artist',
   },
-  "artist-gallery": {
-    find: "artist",
-    link: "artistGallery",
+  'artist-gallery': {
+    find: 'artist',
+    link: 'artistGallery',
   },
-  "commentary-index": {
+  'commentary-index': {
     find: null,
-    link: "commentaryIndex",
+    link: 'commentaryIndex',
   },
   date: {
     find: null,
     value: (ref) => new Date(ref),
-    html: (date, { language }) =>
+    html: (date, {language}) =>
       `<time datetime="${date.toString()}">${language.formatDate(date)}</time>`,
   },
   flash: {
-    find: "flash",
-    link: "flash",
+    find: 'flash',
+    link: 'flash',
     transformName(name, node, input) {
       const nextCharacter = input[node.iEnd];
       const lastCharacter = name[name.length - 1];
-      if (![" ", "\n", "<"].includes(nextCharacter) && lastCharacter === ".") {
+      if (![' ', '\n', '<'].includes(nextCharacter) && lastCharacter === '.') {
         return name.slice(0, -1);
       } else {
         return name;
@@ -261,69 +260,69 @@ const replacerSpec = {
     },
   },
   group: {
-    find: "group",
-    link: "groupInfo",
+    find: 'group',
+    link: 'groupInfo',
   },
-  "group-gallery": {
-    find: "group",
-    link: "groupGallery",
+  'group-gallery': {
+    find: 'group',
+    link: 'groupGallery',
   },
   home: {
     find: null,
-    link: "home",
+    link: 'home',
   },
-  "listing-index": {
+  'listing-index': {
     find: null,
-    link: "listingIndex",
+    link: 'listingIndex',
   },
   listing: {
-    find: "listing",
-    link: "listing",
+    find: 'listing',
+    link: 'listing',
   },
   media: {
     find: null,
-    link: "media",
+    link: 'media',
   },
-  "news-index": {
+  'news-index': {
     find: null,
-    link: "newsIndex",
+    link: 'newsIndex',
   },
-  "news-entry": {
-    find: "newsEntry",
-    link: "newsEntry",
+  'news-entry': {
+    find: 'newsEntry',
+    link: 'newsEntry',
   },
   root: {
     find: null,
-    link: "root",
+    link: 'root',
   },
   site: {
     find: null,
-    link: "site",
+    link: 'site',
   },
   static: {
-    find: "staticPage",
-    link: "staticPage",
+    find: 'staticPage',
+    link: 'staticPage',
   },
   string: {
     find: null,
     value: (ref) => ref,
-    html: (ref, { language, args }) => language.$(ref, args),
+    html: (ref, {language, args}) => language.$(ref, args),
   },
   tag: {
-    find: "artTag",
-    link: "tag",
+    find: 'artTag',
+    link: 'tag',
   },
   track: {
-    find: "track",
-    link: "track",
+    find: 'track',
+    link: 'track',
   },
 };
 
-if (!validateReplacerSpec(replacerSpec, { find, link: unbound_link })) {
+if (!validateReplacerSpec(replacerSpec, {find, link: unbound_link})) {
   process.exit();
 }
 
-function parseAttributes(string, { to }) {
+function parseAttributes(string, {to}) {
   const attributes = Object.create(null);
   const skipWhitespace = (i) => {
     const ws = /\s/;
@@ -345,7 +344,7 @@ function parseAttributes(string, { to }) {
     const aEnd = i + string.slice(i).match(/[\s=]|$/).index;
     const attribute = string.slice(aStart, aEnd);
     i = skipWhitespace(aEnd);
-    if (string[i] === "=") {
+    if (string[i] === '=') {
       i = skipWhitespace(i + 1);
       let end, endOffset;
       if (string[i] === '"' || string[i] === "'") {
@@ -353,15 +352,15 @@ function parseAttributes(string, { to }) {
         endOffset = 1;
         i++;
       } else {
-        end = "\\s";
+        end = '\\s';
         endOffset = 0;
       }
       const vStart = i;
       const vEnd = i + string.slice(i).match(new RegExp(`${end}|$`)).index;
       const value = string.slice(vStart, vEnd);
       i = vEnd + endOffset;
-      if (attribute === "src" && value.startsWith("media/")) {
-        attributes[attribute] = to("media.path", value.slice("media/".length));
+      if (attribute === 'src' && value.startsWith('media/')) {
+        attributes[attribute] = to('media.path', value.slice('media/'.length));
       } else {
         attributes[attribute] = value;
       }
@@ -372,9 +371,9 @@ function parseAttributes(string, { to }) {
   return Object.fromEntries(
     Object.entries(attributes).map(([key, val]) => [
       key,
-      val === "true"
+      val === 'true'
         ? true
-        : val === "false"
+        : val === 'false'
         ? false
         : val === key
         ? true
@@ -386,13 +385,13 @@ function parseAttributes(string, { to }) {
 function joinLineBreaks(sourceLines) {
   const outLines = [];
 
-  let lineSoFar = "";
+  let lineSoFar = '';
   for (let i = 0; i < sourceLines.length; i++) {
     const line = sourceLines[i];
     lineSoFar += line;
-    if (!line.endsWith("<br>")) {
+    if (!line.endsWith('<br>')) {
       outLines.push(lineSoFar);
-      lineSoFar = "";
+      lineSoFar = '';
     }
   }
 
@@ -403,14 +402,14 @@ function joinLineBreaks(sourceLines) {
   return outLines;
 }
 
-function transformMultiline(text, { parseAttributes, transformInline }) {
+function transformMultiline(text, {parseAttributes, transformInline}) {
   // Heck yes, HTML magics.
 
   text = transformInline(text.trim());
 
   const outLines = [];
 
-  const indentString = " ".repeat(4);
+  const indentString = ' '.repeat(4);
 
   let levelIndents = [];
   const openLevel = (indent) => {
@@ -418,13 +417,13 @@ function transformMultiline(text, { parseAttributes, transformInline }) {
     // correct, we have to append the <ul> at the end of the existing
     // previous <li>
     const previousLine = outLines[outLines.length - 1];
-    if (previousLine?.endsWith("</li>")) {
+    if (previousLine?.endsWith('</li>')) {
       // we will re-close the <li> later
-      outLines[outLines.length - 1] = previousLine.slice(0, -5) + " <ul>";
+      outLines[outLines.length - 1] = previousLine.slice(0, -5) + ' <ul>';
     } else {
       // if the previous line isn't a list item, this is the opening of
       // the first list level, so no need for indent
-      outLines.push("<ul>");
+      outLines.push('<ul>');
     }
     levelIndents.push(indent);
   };
@@ -432,10 +431,10 @@ function transformMultiline(text, { parseAttributes, transformInline }) {
     levelIndents.pop();
     if (levelIndents.length) {
       // closing a sublist, so close the list item containing it too
-      outLines.push(indentString.repeat(levelIndents.length) + "</ul></li>");
+      outLines.push(indentString.repeat(levelIndents.length) + '</ul></li>');
     } else {
       // closing the final list level! no need for indent here
-      outLines.push("</ul>");
+      outLines.push('</ul>');
     }
   };
 
@@ -448,19 +447,19 @@ function transformMultiline(text, { parseAttributes, transformInline }) {
   let lines = splitLines(text);
   lines = joinLineBreaks(lines);
   for (let line of lines) {
-    const imageLine = line.startsWith("<img");
+    const imageLine = line.startsWith('<img');
     line = line.replace(/<img (.*?)>/g, (match, attributes) =>
       img({
         lazy: true,
         link: true,
-        thumb: "medium",
+        thumb: 'medium',
         ...parseAttributes(attributes),
       })
     );
 
     let indentThisLine = 0;
     let lineContent = line;
-    let lineTag = "p";
+    let lineTag = 'p';
 
     const listMatch = line.match(/^( *)- *(.*)$/);
     if (listMatch) {
@@ -498,7 +497,7 @@ function transformMultiline(text, { parseAttributes, transformInline }) {
       // finally, set variables for appending content line
       indentThisLine = levelIndents.length;
       lineContent = listMatch[2];
-      lineTag = "li";
+      lineTag = 'li';
     } else {
       // not a list item! close any existing list levels
       while (levelIndents.length) closeLevel();
@@ -510,27 +509,27 @@ function transformMultiline(text, { parseAttributes, transformInline }) {
         // is a quote! open a blockquote tag if it doesnt already exist
         if (!inBlockquote) {
           inBlockquote = true;
-          outLines.push("<blockquote>");
+          outLines.push('<blockquote>');
         }
         indentThisLine = 1;
         lineContent = quoteMatch[1];
       } else if (inBlockquote) {
         // not a quote! close a blockquote tag if it exists
         inBlockquote = false;
-        outLines.push("</blockquote>");
+        outLines.push('</blockquote>');
       }
 
       // let some escaped symbols display as the normal symbol, since the
       // point of escaping them is just to avoid having them be treated as
       // syntax markers!
       if (lineContent.match(/( *)\\-/)) {
-        lineContent = lineContent.replace("\\-", "-");
+        lineContent = lineContent.replace('\\-', '-');
       } else if (lineContent.match(/( *)\\>/)) {
-        lineContent = lineContent.replace("\\>", ">");
+        lineContent = lineContent.replace('\\>', '>');
       }
     }
 
-    if (lineTag === "p") {
+    if (lineTag === 'p') {
       // certain inline element tags should still be postioned within a
       // paragraph; other elements (e.g. headings) should be added as-is
       const elementMatch = line.match(/^<(.*?)[ >]/);
@@ -538,40 +537,40 @@ function transformMultiline(text, { parseAttributes, transformInline }) {
         elementMatch &&
         !imageLine &&
         ![
-          "a",
-          "abbr",
-          "b",
-          "bdo",
-          "br",
-          "cite",
-          "code",
-          "data",
-          "datalist",
-          "del",
-          "dfn",
-          "em",
-          "i",
-          "img",
-          "ins",
-          "kbd",
-          "mark",
-          "output",
-          "picture",
-          "q",
-          "ruby",
-          "samp",
-          "small",
-          "span",
-          "strong",
-          "sub",
-          "sup",
-          "svg",
-          "time",
-          "var",
-          "wbr",
+          'a',
+          'abbr',
+          'b',
+          'bdo',
+          'br',
+          'cite',
+          'code',
+          'data',
+          'datalist',
+          'del',
+          'dfn',
+          'em',
+          'i',
+          'img',
+          'ins',
+          'kbd',
+          'mark',
+          'output',
+          'picture',
+          'q',
+          'ruby',
+          'samp',
+          'small',
+          'span',
+          'strong',
+          'sub',
+          'sup',
+          'svg',
+          'time',
+          'var',
+          'wbr',
         ].includes(elementMatch[1])
       ) {
-        lineTag = "";
+        lineTag = '';
       }
     }
 
@@ -592,43 +591,43 @@ function transformMultiline(text, { parseAttributes, transformInline }) {
   // if still in a blockquote, close its tag
   if (inBlockquote) {
     inBlockquote = false;
-    outLines.push("</blockquote>");
+    outLines.push('</blockquote>');
   }
 
-  return outLines.join("\n");
+  return outLines.join('\n');
 }
 
-function transformLyrics(text, { transformInline, transformMultiline }) {
+function transformLyrics(text, {transformInline, transformMultiline}) {
   // Different from transformMultiline 'cuz it joins multiple lines together
   // with line 8reaks (<br>); transformMultiline treats each line as its own
   // complete paragraph (or list, etc).
 
   // If it looks like old data, then like, oh god.
   // Use the normal transformMultiline tool.
-  if (text.includes("<br")) {
+  if (text.includes('<br')) {
     return transformMultiline(text);
   }
 
   text = transformInline(text.trim());
 
-  let buildLine = "";
+  let buildLine = '';
   const addLine = () => outLines.push(`<p>${buildLine}</p>`);
   const outLines = [];
-  for (const line of text.split("\n")) {
+  for (const line of text.split('\n')) {
     if (line.length) {
       if (buildLine.length) {
-        buildLine += "<br>";
+        buildLine += '<br>';
       }
       buildLine += line;
     } else if (buildLine.length) {
       addLine();
-      buildLine = "";
+      buildLine = '';
     }
   }
   if (buildLine.length) {
     addLine();
   }
-  return outLines.join("\n");
+  return outLines.join('\n');
 }
 
 function stringifyThings(thingData) {
@@ -638,7 +637,7 @@ function stringifyThings(thingData) {
 function img({
   src,
   alt,
-  noSrcText = "",
+  noSrcText = '',
   thumb: thumbKey,
   reveal,
   id,
@@ -650,13 +649,13 @@ function img({
   square = false,
 }) {
   const willSquare = square;
-  const willLink = typeof link === "string" || link;
+  const willLink = typeof link === 'string' || link;
 
   const originalSrc = src;
   const thumbSrc = src && (thumbKey ? thumb[thumbKey](src) : src);
 
   const imgAttributes = html.attributes({
-    id: link ? "" : id,
+    id: link ? '' : id,
     class: className,
     alt,
     width,
@@ -701,21 +700,21 @@ function img({
     }
 
     if (willSquare) {
-      wrapped = html.tag("div", { class: "square-content" }, wrapped);
+      wrapped = html.tag('div', {class: 'square-content'}, wrapped);
       wrapped = html.tag(
-        "div",
-        { class: ["square", hide && !willLink && "js-hide"] },
+        'div',
+        {class: ['square', hide && !willLink && 'js-hide']},
         wrapped
       );
     }
 
     if (willLink) {
       wrapped = html.tag(
-        "a",
+        'a',
         {
           id,
-          class: ["box", hide && "js-hide"],
-          href: typeof link === "string" ? link : originalSrc,
+          class: ['box', hide && 'js-hide'],
+          href: typeof link === 'string' ? link : originalSrc,
         },
         wrapped
       );
@@ -727,16 +726,16 @@ function img({
 
 function validateWritePath(path, urlGroup) {
   if (!Array.isArray(path)) {
-    return { error: `Expected array, got ${path}` };
+    return {error: `Expected array, got ${path}`};
   }
 
-  const { paths } = urlGroup;
+  const {paths} = urlGroup;
 
   const definedKeys = Object.keys(paths);
   const specifiedKey = path[0];
 
   if (!definedKeys.includes(specifiedKey)) {
-    return { error: `Specified key ${specifiedKey} isn't defined` };
+    return {error: `Specified key ${specifiedKey} isn't defined`};
   }
 
   const expectedArgs = paths[specifiedKey].match(/<>/g)?.length ?? 0;
@@ -748,54 +747,54 @@ function validateWritePath(path, urlGroup) {
     };
   }
 
-  return { success: true };
+  return {success: true};
 }
 
 function validateWriteObject(obj) {
-  if (typeof obj !== "object") {
-    return { error: `Expected object, got ${typeof obj}` };
+  if (typeof obj !== 'object') {
+    return {error: `Expected object, got ${typeof obj}`};
   }
 
-  if (typeof obj.type !== "string") {
-    return { error: `Expected type to be string, got ${obj.type}` };
+  if (typeof obj.type !== 'string') {
+    return {error: `Expected type to be string, got ${obj.type}`};
   }
 
   switch (obj.type) {
-    case "legacy": {
-      if (typeof obj.write !== "function") {
-        return { error: `Expected write to be string, got ${obj.write}` };
+    case 'legacy': {
+      if (typeof obj.write !== 'function') {
+        return {error: `Expected write to be string, got ${obj.write}`};
       }
 
       break;
     }
 
-    case "page": {
+    case 'page': {
       const path = validateWritePath(obj.path, urlSpec.localized);
       if (path.error) {
-        return { error: `Path validation failed: ${path.error}` };
+        return {error: `Path validation failed: ${path.error}`};
       }
 
-      if (typeof obj.page !== "function") {
-        return { error: `Expected page to be function, got ${obj.content}` };
+      if (typeof obj.page !== 'function') {
+        return {error: `Expected page to be function, got ${obj.content}`};
       }
 
       break;
     }
 
-    case "data": {
+    case 'data': {
       const path = validateWritePath(obj.path, urlSpec.data);
       if (path.error) {
-        return { error: `Path validation failed: ${path.error}` };
+        return {error: `Path validation failed: ${path.error}`};
       }
 
-      if (typeof obj.data !== "function") {
-        return { error: `Expected data to be function, got ${obj.data}` };
+      if (typeof obj.data !== 'function') {
+        return {error: `Expected data to be function, got ${obj.data}`};
       }
 
       break;
     }
 
-    case "redirect": {
+    case 'redirect': {
       const fromPath = validateWritePath(obj.fromPath, urlSpec.localized);
       if (fromPath.error) {
         return {
@@ -805,22 +804,22 @@ function validateWriteObject(obj) {
 
       const toPath = validateWritePath(obj.toPath, urlSpec.localized);
       if (toPath.error) {
-        return { error: `Path (toPath) validation failed: ${toPath.error}` };
+        return {error: `Path (toPath) validation failed: ${toPath.error}`};
       }
 
-      if (typeof obj.title !== "function") {
-        return { error: `Expected title to be function, got ${obj.title}` };
+      if (typeof obj.title !== 'function') {
+        return {error: `Expected title to be function, got ${obj.title}`};
       }
 
       break;
     }
 
     default: {
-      return { error: `Unknown type: ${obj.type}` };
+      return {error: `Unknown type: ${obj.type}`};
     }
   }
 
-  return { success: true };
+  return {success: true};
 }
 
 /*
@@ -836,9 +835,9 @@ async function writeData(subKey, directory, data) {
 const writePage = {};
 
 writePage.to =
-  ({ baseDirectory, pageSubKey, paths }) =>
+  ({baseDirectory, pageSubKey, paths}) =>
   (targetFullKey, ...args) => {
-    const [groupKey, subKey] = targetFullKey.split(".");
+    const [groupKey, subKey] = targetFullKey.split('.');
     let path = paths.subdirectoryPrefix;
 
     let from;
@@ -847,27 +846,27 @@ writePage.to =
     // When linking to *outside* the localized area of the site, we need to
     // make sure the result is correctly relative to the 8ase directory.
     if (
-      groupKey !== "localized" &&
-      groupKey !== "localizedDefaultLanguage" &&
+      groupKey !== 'localized' &&
+      groupKey !== 'localizedDefaultLanguage' &&
       baseDirectory
     ) {
-      from = "localizedWithBaseDirectory." + pageSubKey;
+      from = 'localizedWithBaseDirectory.' + pageSubKey;
       to = targetFullKey;
-    } else if (groupKey === "localizedDefaultLanguage" && baseDirectory) {
+    } else if (groupKey === 'localizedDefaultLanguage' && baseDirectory) {
       // Special case for specifically linking *from* a page with base
       // directory *to* a page without! Used for the language switcher and
       // hopefully nothing else oh god.
-      from = "localizedWithBaseDirectory." + pageSubKey;
-      to = "localized." + subKey;
-    } else if (groupKey === "localizedDefaultLanguage") {
+      from = 'localizedWithBaseDirectory.' + pageSubKey;
+      to = 'localized.' + subKey;
+    } else if (groupKey === 'localizedDefaultLanguage') {
       // Linking to the default, except surprise, we're already IN the default
       // (no baseDirectory set).
-      from = "localized." + pageSubKey;
-      to = "localized." + subKey;
+      from = 'localized.' + pageSubKey;
+      to = 'localized.' + subKey;
     } else {
       // If we're linking inside the localized area (or there just is no
       // 8ase directory), the 8ase directory doesn't matter.
-      from = "localized." + pageSubKey;
+      from = 'localized.' + pageSubKey;
       to = targetFullKey;
     }
 
@@ -890,13 +889,13 @@ writePage.html = (
     wikiData,
   }
 ) => {
-  const { wikiInfo } = wikiData;
+  const {wikiInfo} = wikiData;
 
   let {
-    title = "",
+    title = '',
     meta = {},
-    theme = "",
-    stylesheet = "",
+    theme = '',
+    stylesheet = '',
 
     showWikiNameInTitle = true,
 
@@ -912,45 +911,45 @@ writePage.html = (
     socialEmbed = {},
   } = pageInfo;
 
-  body.style ??= "";
+  body.style ??= '';
 
   theme = theme || getThemeString(wikiInfo.color);
 
   banner ||= {};
   banner.classes ??= [];
-  banner.src ??= "";
-  banner.position ??= "";
+  banner.src ??= '';
+  banner.position ??= '';
   banner.dimensions ??= [0, 0];
 
   main.classes ??= [];
-  main.content ??= "";
+  main.content ??= '';
 
   sidebarLeft ??= {};
   sidebarRight ??= {};
 
   for (const sidebar of [sidebarLeft, sidebarRight]) {
     sidebar.classes ??= [];
-    sidebar.content ??= "";
+    sidebar.content ??= '';
     sidebar.collapse ??= true;
   }
 
   nav.classes ??= [];
-  nav.content ??= "";
-  nav.bottomRowContent ??= "";
+  nav.content ??= '';
+  nav.bottomRowContent ??= '';
   nav.links ??= [];
   nav.linkContainerClasses ??= [];
 
   secondaryNav ??= {};
-  secondaryNav.content ??= "";
-  secondaryNav.content ??= "";
+  secondaryNav.content ??= '';
+  secondaryNav.content ??= '';
 
   footer.classes ??= [];
   footer.content ??= wikiInfo.footerContent
     ? transformMultiline(wikiInfo.footerContent)
-    : "";
+    : '';
 
   footer.content +=
-    "\n" +
+    '\n' +
     getFooterLocalizationLinks(paths.pathname, {
       defaultLanguage,
       languages,
@@ -960,13 +959,13 @@ writePage.html = (
     });
 
   const canonical = wikiInfo.canonicalBase
-    ? wikiInfo.canonicalBase + (paths.pathname === "/" ? "" : paths.pathname)
-    : "";
+    ? wikiInfo.canonicalBase + (paths.pathname === '/' ? '' : paths.pathname)
+    : '';
 
   const localizedCanonical = wikiInfo.canonicalBase
-    ? Object.entries(localizedPaths).map(([code, { pathname }]) => ({
+    ? Object.entries(localizedPaths).map(([code, {pathname}]) => ({
         lang: code,
-        href: wikiInfo.canonicalBase + (pathname === "/" ? "" : pathname),
+        href: wikiInfo.canonicalBase + (pathname === '/' ? '' : pathname),
       }))
     : [];
 
@@ -976,9 +975,9 @@ writePage.html = (
   const mainHTML =
     main.content &&
     html.tag(
-      "main",
+      'main',
       {
-        id: "content",
+        id: 'content',
         class: main.classes,
       },
       main.content
@@ -987,9 +986,9 @@ writePage.html = (
   const footerHTML =
     footer.content &&
     html.tag(
-      "footer",
+      'footer',
       {
-        id: "footer",
+        id: 'footer',
         class: footer.classes,
       },
       footer.content
@@ -997,18 +996,18 @@ writePage.html = (
 
   const generateSidebarHTML = (
     id,
-    { content, multiple, classes, collapse = true, wide = false }
+    {content, multiple, classes, collapse = true, wide = false}
   ) =>
     content
       ? html.tag(
-          "div",
+          'div',
           {
             id,
             class: [
-              "sidebar-column",
-              "sidebar",
-              wide && "wide",
-              !collapse && "no-hide",
+              'sidebar-column',
+              'sidebar',
+              wide && 'wide',
+              !collapse && 'no-hide',
               ...classes,
             ],
           },
@@ -1016,28 +1015,28 @@ writePage.html = (
         )
       : multiple
       ? html.tag(
-          "div",
+          'div',
           {
             id,
             class: [
-              "sidebar-column",
-              "sidebar-multiple",
-              wide && "wide",
-              !collapse && "no-hide",
+              'sidebar-column',
+              'sidebar-multiple',
+              wide && 'wide',
+              !collapse && 'no-hide',
             ],
           },
           multiple.map((content) =>
-            html.tag("div", { class: ["sidebar", ...classes] }, content)
+            html.tag('div', {class: ['sidebar', ...classes]}, content)
           )
         )
-      : "";
+      : '';
 
-  const sidebarLeftHTML = generateSidebarHTML("sidebar-left", sidebarLeft);
-  const sidebarRightHTML = generateSidebarHTML("sidebar-right", sidebarRight);
+  const sidebarLeftHTML = generateSidebarHTML('sidebar-left', sidebarLeft);
+  const sidebarRightHTML = generateSidebarHTML('sidebar-right', sidebarRight);
 
   if (nav.simple) {
-    nav.linkContainerClasses = ["nav-links-hierarchy"];
-    nav.links = [{ toHome: true }, { toCurrentPage: true }];
+    nav.linkContainerClasses = ['nav-links-hierarchy'];
+    nav.links = [{toHome: true}, {toCurrentPage: true}];
   }
 
   const links = (nav.links || []).filter(Boolean);
@@ -1048,7 +1047,7 @@ writePage.html = (
     const prev = links[i - 1];
     const next = links[i + 1];
 
-    let { title: linkTitle } = cur;
+    let {title: linkTitle} = cur;
 
     if (cur.toHome) {
       linkTitle ??= wikiInfo.nameShort;
@@ -1058,7 +1057,7 @@ writePage.html = (
 
     let partContent;
 
-    if (typeof cur.html === "string") {
+    if (typeof cur.html === 'string') {
       if (!cur.html) {
         logWarn`Empty HTML in nav link ${JSON.stringify(cur)}`;
         console.trace();
@@ -1066,11 +1065,11 @@ writePage.html = (
       partContent = cur.html;
     } else {
       const attributes = {
-        class: (cur.toCurrentPage || i === links.length - 1) && "current",
+        class: (cur.toCurrentPage || i === links.length - 1) && 'current',
         href: cur.toCurrentPage
-          ? ""
+          ? ''
           : cur.toHome
-          ? to("localized.home")
+          ? to('localized.home')
           : cur.path
           ? to(...cur.path)
           : cur.href
@@ -1087,12 +1086,12 @@ writePage.html = (
           )})`
         );
       }
-      partContent = html.tag("a", attributes, linkTitle);
+      partContent = html.tag('a', attributes, linkTitle);
     }
 
     const part = html.tag(
-      "span",
-      { class: cur.divider === false && "no-divider" },
+      'span',
+      {class: cur.divider === false && 'no-divider'},
       partContent
     );
 
@@ -1100,35 +1099,35 @@ writePage.html = (
   }
 
   const navHTML = html.tag(
-    "nav",
+    'nav',
     {
       [html.onlyIfContent]: true,
-      id: "header",
+      id: 'header',
       class: [
         ...nav.classes,
-        links.length && "nav-has-main-links",
-        nav.content && "nav-has-content",
-        nav.bottomRowContent && "nav-has-bottom-row",
+        links.length && 'nav-has-main-links',
+        nav.content && 'nav-has-content',
+        nav.bottomRowContent && 'nav-has-bottom-row',
       ],
     },
     [
       links.length &&
         html.tag(
-          "div",
-          { class: ["nav-main-links", ...nav.linkContainerClasses] },
+          'div',
+          {class: ['nav-main-links', ...nav.linkContainerClasses]},
           navLinkParts
         ),
-      nav.content && html.tag("div", { class: "nav-content" }, nav.content),
+      nav.content && html.tag('div', {class: 'nav-content'}, nav.content),
       nav.bottomRowContent &&
-        html.tag("div", { class: "nav-bottom-row" }, nav.bottomRowContent),
+        html.tag('div', {class: 'nav-bottom-row'}, nav.bottomRowContent),
     ]
   );
 
   const secondaryNavHTML = html.tag(
-    "nav",
+    'nav',
     {
       [html.onlyIfContent]: true,
-      id: "secondary-nav",
+      id: 'secondary-nav',
       class: secondaryNav.classes,
     },
     [secondaryNav.content]
@@ -1144,12 +1143,12 @@ writePage.html = (
     banner.position &&
     bannerSrc &&
     html.tag(
-      "div",
+      'div',
       {
-        id: "banner",
+        id: 'banner',
         class: banner.classes,
       },
-      html.tag("img", {
+      html.tag('img', {
         src: bannerSrc,
         alt: banner.alt,
         width: banner.dimensions[0] || 1100,
@@ -1159,18 +1158,18 @@ writePage.html = (
 
   const layoutHTML = [
     navHTML,
-    banner.position === "top" && bannerHTML,
+    banner.position === 'top' && bannerHTML,
     secondaryNavHTML,
     html.tag(
-      "div",
-      { class: ["layout-columns", !collapseSidebars && "vertical-when-thin"] },
+      'div',
+      {class: ['layout-columns', !collapseSidebars && 'vertical-when-thin']},
       [sidebarLeftHTML, mainHTML, sidebarRightHTML]
     ),
-    banner.position === "bottom" && bannerHTML,
+    banner.position === 'bottom' && bannerHTML,
     footerHTML,
   ]
     .filter(Boolean)
-    .join("\n");
+    .join('\n');
 
   const infoCardHTML = fixWS`
         <div id="info-card-container">
@@ -1178,36 +1177,36 @@ writePage.html = (
                 <div class="info-card">
                     <div class="info-card-art-container no-reveal">
                         ${img({
-                          class: "info-card-art",
-                          src: "",
+                          class: 'info-card-art',
+                          src: '',
                           link: true,
                           square: true,
                         })}
                     </div>
                     <div class="info-card-art-container reveal">
                         ${img({
-                          class: "info-card-art",
-                          src: "",
+                          class: 'info-card-art',
+                          src: '',
                           link: true,
                           square: true,
                           reveal: getRevealStringFromWarnings(
                             '<span class="info-card-art-warnings"></span>',
-                            { language }
+                            {language}
                           ),
                         })}
                     </div>
                     <h1 class="info-card-name"><a></a></h1>
                     <p class="info-card-album">${language.$(
-                      "releaseInfo.from",
-                      { album: "<a></a>" }
+                      'releaseInfo.from',
+                      {album: '<a></a>'}
                     )}</p>
                     <p class="info-card-artists">${language.$(
-                      "releaseInfo.by",
-                      { artists: "<span></span>" }
+                      'releaseInfo.by',
+                      {artists: '<span></span>'}
                     )}</p>
                     <p class="info-card-cover-artists">${language.$(
-                      "releaseInfo.coverArtBy",
-                      { artists: "<span></span>" }
+                      'releaseInfo.coverArtBy',
+                      {artists: '<span></span>'}
                     )}</p>
                 </div>
             </div>
@@ -1216,47 +1215,47 @@ writePage.html = (
 
   const socialEmbedHTML = [
     socialEmbed.title &&
-      html.tag("meta", { property: "og:title", content: socialEmbed.title }),
+      html.tag('meta', {property: 'og:title', content: socialEmbed.title}),
     socialEmbed.description &&
-      html.tag("meta", {
-        property: "og:description",
+      html.tag('meta', {
+        property: 'og:description',
         content: socialEmbed.description,
       }),
     socialEmbed.image &&
-      html.tag("meta", { property: "og:image", content: socialEmbed.image }),
+      html.tag('meta', {property: 'og:image', content: socialEmbed.image}),
     socialEmbed.color &&
-      html.tag("meta", { name: "theme-color", content: socialEmbed.color }),
+      html.tag('meta', {name: 'theme-color', content: socialEmbed.color}),
     oEmbedJSONHref &&
-      html.tag("link", {
-        type: "application/json+oembed",
+      html.tag('link', {
+        type: 'application/json+oembed',
         href: oEmbedJSONHref,
       }),
   ]
     .filter(Boolean)
-    .join("\n");
+    .join('\n');
 
   return filterEmptyLines(fixWS`
         <!DOCTYPE html>
         <html ${html.attributes({
           lang: language.intlCode,
-          "data-language-code": language.code,
-          "data-url-key": paths.toPath[0],
+          'data-language-code': language.code,
+          'data-url-key': paths.toPath[0],
           ...Object.fromEntries(
-            paths.toPath.slice(1).map((v, i) => [["data-url-value" + i], v])
+            paths.toPath.slice(1).map((v, i) => [['data-url-value' + i], v])
           ),
-          "data-rebase-localized": to("localized.root"),
-          "data-rebase-shared": to("shared.root"),
-          "data-rebase-media": to("media.root"),
-          "data-rebase-data": to("data.root"),
+          'data-rebase-localized': to('localized.root'),
+          'data-rebase-shared': to('shared.root'),
+          'data-rebase-media': to('media.root'),
+          'data-rebase-data': to('data.root'),
         })}>
             <head>
                 <title>${
                   showWikiNameInTitle
-                    ? language.formatString("misc.pageTitle.withWikiName", {
+                    ? language.formatString('misc.pageTitle.withWikiName', {
                         title,
                         wikiName: wikiInfo.nameShort,
                       })
-                    : language.formatString("misc.pageTitle", { title })
+                    : language.formatString('misc.pageTitle', {title})
                 }</title>
                 <meta charset="utf-8">
                 <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -1266,17 +1265,17 @@ writePage.html = (
                     ([key, value]) =>
                       `<meta ${key}="${html.escapeAttributeValue(value)}">`
                   )
-                  .join("\n")}
+                  .join('\n')}
                 ${canonical && `<link rel="canonical" href="${canonical}">`}
                 ${localizedCanonical
                   .map(
-                    ({ lang, href }) =>
+                    ({lang, href}) =>
                       `<link rel="alternate" hreflang="${lang}" href="${href}">`
                   )
-                  .join("\n")}
+                  .join('\n')}
                 ${socialEmbedHTML}
                 <link rel="stylesheet" href="${to(
-                  "shared.staticFile",
+                  'shared.staticFile',
                   `site.css?${CACHEBUST}`
                 )}">
                 ${
@@ -1289,11 +1288,11 @@ writePage.html = (
                 `
                 }
                 <script src="${to(
-                  "shared.staticFile",
+                  'shared.staticFile',
                   `lazy-loading.js?${CACHEBUST}`
                 )}"></script>
             </head>
-            <body ${html.attributes({ style: body.style || "" })}>
+            <body ${html.attributes({style: body.style || ''})}>
                 <div id="page-container">
                     ${
                       mainHTML &&
@@ -1301,28 +1300,28 @@ writePage.html = (
                         <div id="skippers">
                             ${[
                               [
-                                "#content",
-                                language.$("misc.skippers.skipToContent"),
+                                '#content',
+                                language.$('misc.skippers.skipToContent'),
                               ],
                               sidebarLeftHTML && [
-                                "#sidebar-left",
+                                '#sidebar-left',
                                 sidebarRightHTML
                                   ? language.$(
-                                      "misc.skippers.skipToSidebar.left"
+                                      'misc.skippers.skipToSidebar.left'
                                     )
-                                  : language.$("misc.skippers.skipToSidebar"),
+                                  : language.$('misc.skippers.skipToSidebar'),
                               ],
                               sidebarRightHTML && [
-                                "#sidebar-right",
+                                '#sidebar-right',
                                 sidebarLeftHTML
                                   ? language.$(
-                                      "misc.skippers.skipToSidebar.right"
+                                      'misc.skippers.skipToSidebar.right'
                                     )
-                                  : language.$("misc.skippers.skipToSidebar"),
+                                  : language.$('misc.skippers.skipToSidebar'),
                               ],
                               footerHTML && [
-                                "#footer",
-                                language.$("misc.skippers.skipToFooter"),
+                                '#footer',
+                                language.$('misc.skippers.skipToFooter'),
                               ],
                             ]
                               .filter(Boolean)
@@ -1331,7 +1330,7 @@ writePage.html = (
                                 <span class="skipper"><a href="${href}">${title}</a></span>
                             `
                               )
-                              .join("\n")}
+                              .join('\n')}
                         </div>
                     `
                     }
@@ -1339,7 +1338,7 @@ writePage.html = (
                 </div>
                 ${infoCardHTML}
                 <script type="module" src="${to(
-                  "shared.staticFile",
+                  'shared.staticFile',
                   `client.js?${CACHEBUST}`
                 )}"></script>
             </body>
@@ -1347,37 +1346,37 @@ writePage.html = (
     `);
 };
 
-writePage.oEmbedJSON = (pageInfo, { language, wikiData }) => {
-  const { socialEmbed } = pageInfo;
-  const { wikiInfo } = wikiData;
-  const { canonicalBase, nameShort } = wikiInfo;
+writePage.oEmbedJSON = (pageInfo, {language, wikiData}) => {
+  const {socialEmbed} = pageInfo;
+  const {wikiInfo} = wikiData;
+  const {canonicalBase, nameShort} = wikiInfo;
 
-  if (!socialEmbed) return "";
+  if (!socialEmbed) return '';
 
   const entries = [
     socialEmbed.heading && [
-      "author_name",
-      language.$("misc.socialEmbed.heading", {
+      'author_name',
+      language.$('misc.socialEmbed.heading', {
         wikiName: nameShort,
         heading: socialEmbed.heading,
       }),
     ],
     socialEmbed.headingLink &&
       canonicalBase && [
-        "author_url",
-        canonicalBase.replace(/\/$/, "") +
-          "/" +
-          socialEmbed.headingLink.replace(/^\//, ""),
+        'author_url',
+        canonicalBase.replace(/\/$/, '') +
+          '/' +
+          socialEmbed.headingLink.replace(/^\//, ''),
       ],
   ].filter(Boolean);
 
-  if (!entries.length) return "";
+  if (!entries.length) return '';
 
   return JSON.stringify(Object.fromEntries(entries));
 };
 
-writePage.write = async ({ html, oEmbedJSON = "", paths }) => {
-  await mkdir(paths.outputDirectory, { recursive: true });
+writePage.write = async ({html, oEmbedJSON = '', paths}) => {
+  await mkdir(paths.outputDirectory, {recursive: true});
   await Promise.all(
     [
       writeFile(paths.outputFile, html),
@@ -1390,25 +1389,25 @@ writePage.write = async ({ html, oEmbedJSON = "", paths }) => {
 writePage.paths = (
   baseDirectory,
   fullKey,
-  directory = "",
-  { file = "index.html" } = {}
+  directory = '',
+  {file = 'index.html'} = {}
 ) => {
-  const [groupKey, subKey] = fullKey.split(".");
+  const [groupKey, subKey] = fullKey.split('.');
 
   const pathname =
-    groupKey === "localized" && baseDirectory
+    groupKey === 'localized' && baseDirectory
       ? urls
-          .from("shared.root")
+          .from('shared.root')
           .toDevice(
-            "localizedWithBaseDirectory." + subKey,
+            'localizedWithBaseDirectory.' + subKey,
             baseDirectory,
             directory
           )
-      : urls.from("shared.root").toDevice(fullKey, directory);
+      : urls.from('shared.root').toDevice(fullKey, directory);
 
   // Needed for the rare directory which itself contains a slash, e.g. for
   // listings, with directories like 'albums/by-name'.
-  const subdirectoryPrefix = "../".repeat(directory.split("/").length - 1);
+  const subdirectoryPrefix = '../'.repeat(directory.split('/').length - 1);
 
   const outputDirectory = path.join(outputPath, pathname);
   const outputFile = path.join(outputDirectory, file);
@@ -1445,74 +1444,74 @@ async function writeFavicon() {
 }
 
 function writeSymlinks() {
-  return progressPromiseAll("Writing site symlinks.", [
-    link(path.join(__dirname, UTILITY_DIRECTORY), "shared.utilityRoot"),
-    link(path.join(__dirname, STATIC_DIRECTORY), "shared.staticRoot"),
-    link(mediaPath, "media.root"),
+  return progressPromiseAll('Writing site symlinks.', [
+    link(path.join(__dirname, UTILITY_DIRECTORY), 'shared.utilityRoot'),
+    link(path.join(__dirname, STATIC_DIRECTORY), 'shared.staticRoot'),
+    link(mediaPath, 'media.root'),
   ]);
 
   async function link(directory, urlKey) {
-    const pathname = urls.from("shared.root").toDevice(urlKey);
+    const pathname = urls.from('shared.root').toDevice(urlKey);
     const file = path.join(outputPath, pathname);
     try {
       await unlink(file);
     } catch (error) {
-      if (error.code !== "ENOENT") {
+      if (error.code !== 'ENOENT') {
         throw error;
       }
     }
     try {
       await symlink(path.resolve(directory), file);
     } catch (error) {
-      if (error.code === "EPERM") {
-        await symlink(path.resolve(directory), file, "junction");
+      if (error.code === 'EPERM') {
+        await symlink(path.resolve(directory), file, 'junction');
       }
     }
   }
 }
 
-function writeSharedFilesAndPages({ language, wikiData }) {
-  const { groupData, wikiInfo } = wikiData;
+function writeSharedFilesAndPages({language, wikiData}) {
+  const {groupData, wikiInfo} = wikiData;
 
   const redirect = async (title, from, urlKey, directory) => {
     const target = path.relative(
       from,
-      urls.from("shared.root").to(urlKey, directory)
+      urls.from('shared.root').to(urlKey, directory)
     );
-    const content = generateRedirectPage(title, target, { language });
-    await mkdir(path.join(outputPath, from), { recursive: true });
-    await writeFile(path.join(outputPath, from, "index.html"), content);
+    const content = generateRedirectPage(title, target, {language});
+    await mkdir(path.join(outputPath, from), {recursive: true});
+    await writeFile(path.join(outputPath, from, 'index.html'), content);
   };
 
   return progressPromiseAll(
     `Writing files & pages shared across languages.`,
     [
-      groupData?.some((group) => group.directory === "fandom") &&
+      groupData?.some((group) => group.directory === 'fandom') &&
         redirect(
-          "Fandom - Gallery",
-          "albums/fandom",
-          "localized.groupGallery",
-          "fandom"
+          'Fandom - Gallery',
+          'albums/fandom',
+          'localized.groupGallery',
+          'fandom'
         ),
 
-      groupData?.some((group) => group.directory === "official") &&
+      groupData?.some((group) => group.directory === 'official') &&
         redirect(
-          "Official - Gallery",
-          "albums/official",
-          "localized.groupGallery",
-          "official"
+          'Official - Gallery',
+          'albums/official',
+          'localized.groupGallery',
+          'official'
         ),
 
       wikiInfo.enableListings &&
         redirect(
-          "Album Commentary",
-          "list/all-commentary",
-          "localized.commentaryIndex",
-          ""
+          'Album Commentary',
+          'list/all-commentary',
+          'localized.commentaryIndex',
+          ''
         ),
 
       writeFile(
-        path.join(outputPath, "data.json"),
+        path.join(outputPath, 'data.json'),
         fixWS`
             {
                 "albumData": ${stringifyThings(wikiData.albumData)},
@@ -1528,12 +1527,12 @@ function writeSharedFilesAndPages({ language, wikiData }) {
   );
 }
 
-function generateRedirectPage(title, target, { language }) {
+function generateRedirectPage(title, target, {language}) {
   return fixWS`
         <!DOCTYPE html>
         <html>
             <head>
-                <title>${language.$("redirectPage.title", { title })}</title>
+                <title>${language.$('redirectPage.title', {title})}</title>
                 <meta charset="utf-8">
                 <meta http-equiv="refresh" content="0;url=${target}">
                 <link rel="canonical" href="${target}">
@@ -1541,8 +1540,8 @@ function generateRedirectPage(title, target, { language }) {
             </head>
             <body>
                 <main>
-                    <h1>${language.$("redirectPage.title", { title })}</h1>
-                    <p>${language.$("redirectPage.infoLine", {
+                    <h1>${language.$('redirectPage.title', {title})}</h1>
+                    <p>${language.$('redirectPage.infoLine', {
                       target: `<a href="${target}">${target}</a>`,
                     })}</p>
                 </main>
@@ -1553,41 +1552,41 @@ function generateRedirectPage(title, target, { language }) {
 
 // RIP toAnythingMan (previously getHrefOfAnythingMan), 2020-05-25<>2021-05-14.
 // ........Yet the function 8reathes life anew as linkAnythingMan! ::::)
-function linkAnythingMan(anythingMan, { link, wikiData, ...opts }) {
+function linkAnythingMan(anythingMan, {link, wikiData, ...opts}) {
   return wikiData.albumData.includes(anythingMan)
     ? link.album(anythingMan, opts)
     : wikiData.trackData.includes(anythingMan)
     ? link.track(anythingMan, opts)
     : wikiData.flashData?.includes(anythingMan)
     ? link.flash(anythingMan, opts)
-    : "idk bud";
+    : 'idk bud';
 }
 
 async function processLanguageFile(file) {
-  const contents = await readFile(file, "utf-8");
+  const contents = await readFile(file, 'utf-8');
   const json = JSON.parse(contents);
 
-  const code = json["meta.languageCode"];
+  const code = json['meta.languageCode'];
   if (!code) {
     throw new Error(`Missing language code (file: ${file})`);
   }
-  delete json["meta.languageCode"];
+  delete json['meta.languageCode'];
 
-  const intlCode = json["meta.languageIntlCode"] ?? null;
-  delete json["meta.languageIntlCode"];
+  const intlCode = json['meta.languageIntlCode'] ?? null;
+  delete json['meta.languageIntlCode'];
 
-  const name = json["meta.languageName"];
+  const name = json['meta.languageName'];
   if (!name) {
     throw new Error(`Missing language name (${code})`);
   }
-  delete json["meta.languageName"];
+  delete json['meta.languageName'];
 
-  const hidden = json["meta.hidden"] ?? false;
-  delete json["meta.hidden"];
+  const hidden = json['meta.hidden'] ?? false;
+  delete json['meta.hidden'];
 
-  if (json["meta.baseDirectory"]) {
+  if (json['meta.baseDirectory']) {
     logWarn`(${code}) Language JSON still has unused meta.baseDirectory`;
-    delete json["meta.baseDirectory"];
+    delete json['meta.baseDirectory'];
   }
 
   const language = new Language();
@@ -1596,18 +1595,18 @@ async function processLanguageFile(file) {
   language.name = name;
   language.hidden = hidden;
   language.escapeHTML = (string) =>
-    he.encode(string, { useNamedReferences: true });
+    he.encode(string, {useNamedReferences: true});
   language.strings = json;
   return language;
 }
 
 // Wrapper function for running a function once for all languages.
-async function wrapLanguages(fn, { languages, writeOneLanguage = null }) {
+async function wrapLanguages(fn, {languages, writeOneLanguage = null}) {
   const k = writeOneLanguage;
-  const languagesToRun = k ? { [k]: languages[k] } : languages;
+  const languagesToRun = k ? {[k]: languages[k]} : languages;
 
   const entries = Object.entries(languagesToRun).filter(
-    ([key]) => key !== "default"
+    ([key]) => key !== 'default'
   );
 
   for (let i = 0; i < entries.length; i++) {
@@ -1629,15 +1628,15 @@ async function main() {
     // Data files for the site, including flash, artist, and al8um data,
     // and like a jillion other things too. Pretty much everything which
     // makes an individual wiki what it is goes here!
-    "data-path": {
-      type: "value",
+    'data-path': {
+      type: 'value',
     },
 
     // Static media will 8e referenced in the site here! The contents are
     // categorized; check out MEDIA_ALBUM_ART_DIRECTORY and other constants
     // near the top of this file (upd8.js).
-    "media-path": {
-      type: "value",
+    'media-path': {
+      type: 'value',
     },
 
     // String files! For the most part, this is used for translating the
@@ -1650,8 +1649,8 @@ async function main() {
     // Unlike the other options here, this one's optional - the site will
     // 8uild with the default (English) strings if this path is left
     // unspecified.
-    "lang-path": {
-      type: "value",
+    'lang-path': {
+      type: 'value',
     },
 
     // This is the output directory. It's the one you'll upload online with
@@ -1660,78 +1659,78 @@ async function main() {
     // site. Just keep in mind that the gener8ted result will contain a
     // couple symlinked directories, so if you're uploading, you're pro8a8ly
     // gonna want to resolve those yourself.
-    "out-path": {
-      type: "value",
+    'out-path': {
+      type: 'value',
     },
 
     // Thum8nail gener8tion is *usually* something you want, 8ut it can 8e
     // kinda a pain to run every time, since it does necessit8te reading
     // every media file at run time. Pass this to skip it.
-    "skip-thumbs": {
-      type: "flag",
+    'skip-thumbs': {
+      type: 'flag',
     },
 
     // Or, if you *only* want to gener8te newly upd8ted thum8nails, you can
     // pass this flag! It exits 8efore 8uilding the rest of the site.
-    "thumbs-only": {
-      type: "flag",
+    'thumbs-only': {
+      type: 'flag',
     },
 
     // Just working on data entries and not interested in actually
     // generating site HTML yet? This flag will cut execution off right
     // 8efore any site 8uilding actually happens.
-    "no-build": {
-      type: "flag",
+    'no-build': {
+      type: 'flag',
     },
 
     // Only want to 8uild one language during testing? This can chop down
     // 8uild times a pretty 8ig chunk! Just pass a single language code.
     lang: {
-      type: "value",
+      type: 'value',
     },
 
     // Working without a dev server and just using file:// URLs in your we8
     // 8rowser? This will automatically append index.html to links across
     // the site. Not recommended for production, since it isn't guaranteed
     // 100% error-free (and index.html-style links are less pretty anyway).
-    "append-index-html": {
-      type: "flag",
+    'append-index-html': {
+      type: 'flag',
     },
 
     // Want sweet, sweet trace8ack info in aggreg8te error messages? This
     // will print all the juicy details (or at least the first relevant
     // line) right to your output, 8ut also pro8a8ly give you a headache
     // 8ecause wow that is a lot of visual noise.
-    "show-traces": {
-      type: "flag",
+    'show-traces': {
+      type: 'flag',
     },
 
-    "queue-size": {
-      type: "value",
+    'queue-size': {
+      type: 'value',
       validate(size) {
-        if (parseInt(size) !== parseFloat(size)) return "an integer";
-        if (parseInt(size) < 0) return "a counting number or zero";
+        if (parseInt(size) !== parseFloat(size)) return 'an integer';
+        if (parseInt(size) < 0) return 'a counting number or zero';
         return true;
       },
     },
-    queue: { alias: "queue-size" },
+    queue: {alias: 'queue-size'},
 
     // This option is super slow and has the potential for bugs! It puts
     // CacheableObject in a mode where every instance is a Proxy which will
     // keep track of invalid property accesses.
-    "show-invalid-property-accesses": {
-      type: "flag",
+    'show-invalid-property-accesses': {
+      type: 'flag',
     },
 
     [parseOptions.handleUnknown]: () => {},
   });
 
-  dataPath = miscOptions["data-path"] || process.env.HSMUSIC_DATA;
-  mediaPath = miscOptions["media-path"] || process.env.HSMUSIC_MEDIA;
-  langPath = miscOptions["lang-path"] || process.env.HSMUSIC_LANG; // Can 8e left unset!
-  outputPath = miscOptions["out-path"] || process.env.HSMUSIC_OUT;
+  dataPath = miscOptions['data-path'] || process.env.HSMUSIC_DATA;
+  mediaPath = miscOptions['media-path'] || process.env.HSMUSIC_MEDIA;
+  langPath = miscOptions['lang-path'] || process.env.HSMUSIC_LANG; // Can 8e left unset!
+  outputPath = miscOptions['out-path'] || process.env.HSMUSIC_OUT;
 
-  const writeOneLanguage = miscOptions["lang"];
+  const writeOneLanguage = miscOptions['lang'];
 
   {
     let errored = false;
@@ -1752,16 +1751,16 @@ async function main() {
     }
   }
 
-  const appendIndexHTML = miscOptions["append-index-html"] ?? false;
+  const appendIndexHTML = miscOptions['append-index-html'] ?? false;
   if (appendIndexHTML) {
     logWarn`Appending index.html to link hrefs. (Note: not recommended for production release!)`;
     unbound_link.globalOptions.appendIndexHTML = true;
   }
 
-  const skipThumbs = miscOptions["skip-thumbs"] ?? false;
-  const thumbsOnly = miscOptions["thumbs-only"] ?? false;
-  const noBuild = miscOptions["no-build"] ?? false;
-  const showAggregateTraces = miscOptions["show-traces"] ?? false;
+  const skipThumbs = miscOptions['skip-thumbs'] ?? false;
+  const thumbsOnly = miscOptions['thumbs-only'] ?? false;
+  const noBuild = miscOptions['no-build'] ?? false;
+  const showAggregateTraces = miscOptions['show-traces'] ?? false;
 
   const niceShowAggregate = (error, ...opts) => {
     showAggregate(error, {
@@ -1780,45 +1779,45 @@ async function main() {
     logInfo`Skipping thumbnail generation.`;
   } else {
     logInfo`Begin thumbnail generation... -----+`;
-    const result = await genThumbs(mediaPath, { queueSize, quiet: true });
+    const result = await genThumbs(mediaPath, {queueSize, quiet: true});
     logInfo`Done thumbnail generation! --------+`;
     if (!result) return;
     if (thumbsOnly) return;
   }
 
   const showInvalidPropertyAccesses =
-    miscOptions["show-invalid-property-accesses"] ?? false;
+    miscOptions['show-invalid-property-accesses'] ?? false;
 
   if (showInvalidPropertyAccesses) {
     CacheableObject.DEBUG_SLOW_TRACK_INVALID_PROPERTIES = true;
   }
 
-  const { aggregate: processDataAggregate, result: wikiDataResult } =
-    await loadAndProcessDataDocuments({ dataPath });
+  const {aggregate: processDataAggregate, result: wikiDataResult} =
+    await loadAndProcessDataDocuments({dataPath});
 
   Object.assign(wikiData, wikiDataResult);
 
   {
     const logThings = (thingDataProp, label) =>
       logInfo` - ${
-        wikiData[thingDataProp]?.length ?? color.red("(Missing!)")
+        wikiData[thingDataProp]?.length ?? color.red('(Missing!)')
       } ${color.normal(color.dim(label))}`;
     try {
       logInfo`Loaded data and processed objects:`;
-      logThings("albumData", "albums");
-      logThings("trackData", "tracks");
-      logThings("artistData", "artists");
+      logThings('albumData', 'albums');
+      logThings('trackData', 'tracks');
+      logThings('artistData', 'artists');
       if (wikiData.flashData) {
-        logThings("flashData", "flashes");
-        logThings("flashActData", "flash acts");
+        logThings('flashData', 'flashes');
+        logThings('flashActData', 'flash acts');
       }
-      logThings("groupData", "groups");
-      logThings("groupCategoryData", "group categories");
-      logThings("artTagData", "art tags");
+      logThings('groupData', 'groups');
+      logThings('groupCategoryData', 'group categories');
+      logThings('artTagData', 'art tags');
       if (wikiData.newsData) {
-        logThings("newsData", "news entries");
+        logThings('newsData', 'news entries');
       }
-      logThings("staticPageData", "static pages");
+      logThings('staticPageData', 'static pages');
       if (wikiData.homepageLayout) {
         logInfo` - ${1} homepage layout (${
           wikiData.homepageLayout.rows.length
@@ -1925,7 +1924,7 @@ async function main() {
   let languages;
   if (langPath) {
     const languageDataFiles = await findFiles(langPath, {
-      filter: (f) => path.extname(f) === ".json",
+      filter: (f) => path.extname(f) === '.json',
     });
 
     const results = await progressPromiseAll(
@@ -1953,7 +1952,7 @@ async function main() {
     if (langPath) {
       logError`Check if an appropriate file exists in ${langPath}?`;
     } else {
-      logError`Be sure to specify ${"--lang"} or ${"HSMUSIC_LANG"} with the path to language files.`;
+      logError`Be sure to specify ${'--lang'} or ${'HSMUSIC_LANG'} with the path to language files.`;
     }
     return;
   } else {
@@ -1969,7 +1968,7 @@ async function main() {
     language.inheritedStrings = finalDefaultLanguage.strings;
   }
 
-  logInfo`Loaded language strings: ${Object.keys(languages).join(", ")}`;
+  logInfo`Loaded language strings: ${Object.keys(languages).join(', ')}`;
 
   if (noBuild) {
     logInfo`Not generating any site or page files this run (--no-build passed).`;
@@ -2030,19 +2029,19 @@ async function main() {
           device: path.join(
             mediaPath,
             urls
-              .from("media.root")
-              .toDevice("media.albumAdditionalFile", album.directory, file)
+              .from('media.root')
+              .toDevice('media.albumAdditionalFile', album.directory, file)
           ),
           media: urls
-            .from("media.root")
-            .to("media.albumAdditionalFile", album.directory, file),
+            .from('media.root')
+            .to('media.albumAdditionalFile', album.directory, file),
         }))
     ),
   ];
 
   const getSizeOfAdditionalFile = (mediaPath) => {
-    const { device = null } =
-      additionalFilePaths.find(({ media }) => media === mediaPath) || {};
+    const {device = null} =
+      additionalFilePaths.find(({media}) => media === mediaPath) || {};
     if (!device) return null;
     return fileSizePreloader.getSizeOfPath(device);
   };
@@ -2062,7 +2061,7 @@ async function main() {
   // performance right now 'cuz it'll w8 for file writes to 8e completed
   // 8efore moving on to more data processing. So, defaults to zero, which
   // disa8les the queue feature altogether.
-  queueSize = +(miscOptions["queue-size"] ?? 0);
+  queueSize = +(miscOptions['queue-size'] ?? 0);
 
   const buildDictionary = pageSpecs;
 
@@ -2072,11 +2071,11 @@ async function main() {
   // on some particular area(s) of the site rather than making changes
   // across all of them.
   const writeFlags = await parseOptions(process.argv.slice(2), {
-    all: { type: "flag" }, // Defaults to true if none 8elow specified.
+    all: {type: 'flag'}, // Defaults to true if none 8elow specified.
 
     // Kinda a hack t8h!
     ...Object.fromEntries(
-      Object.keys(buildDictionary).map((key) => [key, { type: "flag" }])
+      Object.keys(buildDictionary).map((key) => [key, {type: 'flag'}])
     ),
 
     [parseOptions.handleUnknown]: () => {},
@@ -2085,12 +2084,12 @@ async function main() {
   const writeAll = !Object.keys(writeFlags).length || writeFlags.all;
 
   logInfo`Writing site pages: ${
-    writeAll ? "all" : Object.keys(writeFlags).join(", ")
+    writeAll ? 'all' : Object.keys(writeFlags).join(', ')
   }`;
 
   await writeFavicon();
   await writeSymlinks();
-  await writeSharedFilesAndPages({ language: finalDefaultLanguage, wikiData });
+  await writeSharedFilesAndPages({language: finalDefaultLanguage, wikiData});
 
   const buildSteps = writeAll
     ? Object.entries(buildDictionary)
@@ -2103,33 +2102,33 @@ async function main() {
     const buildStepsWithTargets = buildSteps
       .map(([flag, pageSpec]) => {
         // Condition not met: skip this build step altogether.
-        if (pageSpec.condition && !pageSpec.condition({ wikiData })) {
+        if (pageSpec.condition && !pageSpec.condition({wikiData})) {
           return null;
         }
 
         // May still call writeTargetless if present.
         if (!pageSpec.targets) {
-          return { flag, pageSpec, targets: [] };
+          return {flag, pageSpec, targets: []};
         }
 
         if (!pageSpec.write) {
-          logError`${flag + ".targets"} is specified, but ${
-            flag + ".write"
+          logError`${flag + '.targets'} is specified, but ${
+            flag + '.write'
           } is missing!`;
           error = true;
           return null;
         }
 
-        const targets = pageSpec.targets({ wikiData });
+        const targets = pageSpec.targets({wikiData});
         if (!Array.isArray(targets)) {
           logError`${
-            flag + ".targets"
+            flag + '.targets'
           } was called, but it didn't return an array! (${typeof targets})`;
           error = true;
           return null;
         }
 
-        return { flag, pageSpec, targets };
+        return {flag, pageSpec, targets};
       })
       .filter(Boolean);
 
@@ -2149,7 +2148,7 @@ async function main() {
 
       if (
         !(
-          writes.every((obj) => typeof obj === "object") &&
+          writes.every((obj) => typeof obj === 'object') &&
           writes.every((obj) => {
             const result = validateWriteObject(obj);
             if (result.error) {
@@ -2171,19 +2170,19 @@ async function main() {
 
     // return;
 
-    writes = buildStepsWithTargets.flatMap(({ flag, pageSpec, targets }) => {
+    writes = buildStepsWithTargets.flatMap(({flag, pageSpec, targets}) => {
       const writes = targets.flatMap(
-        (target) => pageSpec.write(target, { wikiData })?.slice() || []
+        (target) => pageSpec.write(target, {wikiData})?.slice() || []
       );
 
-      if (!validateWrites(writes, flag + ".write")) {
+      if (!validateWrites(writes, flag + '.write')) {
         return [];
       }
 
       if (pageSpec.writeTargetless) {
-        const writes2 = pageSpec.writeTargetless({ wikiData });
+        const writes2 = pageSpec.writeTargetless({wikiData});
 
-        if (!validateWrites(writes2, flag + ".writeTargetless")) {
+        if (!validateWrites(writes2, flag + '.writeTargetless')) {
           return [];
         }
 
@@ -2198,9 +2197,9 @@ async function main() {
     }
   }
 
-  const pageWrites = writes.filter(({ type }) => type === "page");
-  const dataWrites = writes.filter(({ type }) => type === "data");
-  const redirectWrites = writes.filter(({ type }) => type === "redirect");
+  const pageWrites = writes.filter(({type}) => type === 'page');
+  const dataWrites = writes.filter(({type}) => type === 'data');
+  const redirectWrites = writes.filter(({type}) => type === 'redirect');
 
   if (writes.length) {
     logInfo`Total of ${writes.length} writes returned. (${pageWrites.length} page, ${dataWrites.length} data [currently skipped], ${redirectWrites.length} redirect)`;
@@ -2247,20 +2246,20 @@ async function main() {
 
   const perLanguageFn = async (language, i, entries) => {
     const baseDirectory =
-      language === finalDefaultLanguage ? "" : language.code;
+      language === finalDefaultLanguage ? '' : language.code;
 
     console.log(
       `\x1b[34;1m${`[${i + 1}/${entries.length}] ${
         language.code
-      } (-> /${baseDirectory}) `.padEnd(60, "-")}\x1b[0m`
+      } (-> /${baseDirectory}) `.padEnd(60, '-')}\x1b[0m`
     );
 
     await progressPromiseAll(
       `Writing ${language.code}`,
       queue(
         [
-          ...pageWrites.map(({ type, ...props }) => () => {
-            const { path, page } = props;
+          ...pageWrites.map(({type, ...props}) => () => {
+            const {path, page} = props;
 
             // TODO: This only supports one <>-style argument.
             const pageSubKey = path[0];
@@ -2269,13 +2268,13 @@ async function main() {
             const localizedPaths = Object.fromEntries(
               Object.entries(languages)
                 .filter(
-                  ([key, language]) => key !== "default" && !language.hidden
+                  ([key, language]) => key !== 'default' && !language.hidden
                 )
                 .map(([key, language]) => [
                   language.code,
                   writePage.paths(
-                    language === finalDefaultLanguage ? "" : language.code,
-                    "localized." + pageSubKey,
+                    language === finalDefaultLanguage ? '' : language.code,
+                    'localized.' + pageSubKey,
                     directory
                   ),
                 ])
@@ -2283,7 +2282,7 @@ async function main() {
 
             const paths = writePage.paths(
               baseDirectory,
-              "localized." + pageSubKey,
+              'localized.' + pageSubKey,
               directory
             );
 
@@ -2294,13 +2293,13 @@ async function main() {
             });
 
             const absoluteTo = (targetFullKey, ...args) => {
-              const [groupKey, subKey] = targetFullKey.split(".");
-              const from = urls.from("shared.root");
+              const [groupKey, subKey] = targetFullKey.split('.');
+              const from = urls.from('shared.root');
               return (
-                "/" +
-                (groupKey === "localized" && baseDirectory
+                '/' +
+                (groupKey === 'localized' && baseDirectory
                   ? from.to(
-                      "localizedWithBaseDirectory." + subKey,
+                      'localizedWithBaseDirectory.' + subKey,
                       baseDirectory,
                       ...args
                     )
@@ -2314,7 +2313,7 @@ async function main() {
             const bound = {};
 
             bound.link = withEntries(unbound_link, (entries) =>
-              entries.map(([key, fn]) => [key, bindOpts(fn, { to })])
+              entries.map(([key, fn]) => [key, bindOpts(fn, {to})])
             );
 
             bound.linkAnythingMan = bindOpts(linkAnythingMan, {
@@ -2326,7 +2325,7 @@ async function main() {
               to,
             });
 
-            bound.find = bindFind(wikiData, { mode: "warn" });
+            bound.find = bindFind(wikiData, {mode: 'warn'});
 
             bound.transformInline = bindOpts(transformInline, {
               find: bound.find,
@@ -2501,8 +2500,8 @@ async function main() {
               wikiData.wikiInfo.canonicalBase &&
               wikiData.wikiInfo.canonicalBase +
                 urls
-                  .from("shared.root")
-                  .to("shared.path", paths.pathname + OEMBED_JSON_FILE);
+                  .from('shared.root')
+                  .to('shared.path', paths.pathname + OEMBED_JSON_FILE);
 
             const html = writePage.html(pageInfo, {
               defaultLanguage: finalDefaultLanguage,
@@ -2522,30 +2521,27 @@ async function main() {
               paths,
             });
           }),
-          ...redirectWrites.map(
-            ({ fromPath, toPath, title: titleFn }) =>
-              () => {
-                const title = titleFn({
-                  language,
-                });
-
-                // TODO: This only supports one <>-style argument.
-                const fromPaths = writePage.paths(
-                  baseDirectory,
-                  "localized." + fromPath[0],
-                  fromPath[1]
-                );
-                const to = writePage.to({
-                  baseDirectory,
-                  pageSubKey: fromPath[0],
-                  paths: fromPaths,
-                });
-
-                const target = to("localized." + toPath[0], ...toPath.slice(1));
-                const html = generateRedirectPage(title, target, { language });
-                return writePage.write({ html, paths: fromPaths });
-              }
-          ),
+          ...redirectWrites.map(({fromPath, toPath, title: titleFn}) => () => {
+            const title = titleFn({
+              language,
+            });
+
+            // TODO: This only supports one <>-style argument.
+            const fromPaths = writePage.paths(
+              baseDirectory,
+              'localized.' + fromPath[0],
+              fromPath[1]
+            );
+            const to = writePage.to({
+              baseDirectory,
+              pageSubKey: fromPath[0],
+              paths: fromPaths,
+            });
+
+            const target = to('localized.' + toPath[0], ...toPath.slice(1));
+            const html = generateRedirectPage(title, target, {language});
+            return writePage.write({html, paths: fromPaths});
+          }),
         ],
         queueSize
       )
diff --git a/src/url-spec.js b/src/url-spec.js
index 0defced..bab97ef 100644
--- a/src/url-spec.js
+++ b/src/url-spec.js
@@ -1,18 +1,18 @@
-// @format
+/** @format */
 
-import { withEntries } from "./util/sugar.js";
+import {withEntries} from './util/sugar.js';
 
 const urlSpec = {
   data: {
-    prefix: "data/",
+    prefix: 'data/',
 
     paths: {
-      root: "",
-      path: "<>",
+      root: '',
+      path: '<>',
 
-      album: "album/<>",
-      artist: "artist/<>",
-      track: "track/<>",
+      album: 'album/<>',
+      artist: 'artist/<>',
+      track: 'track/<>',
     },
   },
 
@@ -21,64 +21,64 @@ const urlSpec = {
     // prefix: '_languageCode',
 
     paths: {
-      root: "",
-      path: "<>",
+      root: '',
+      path: '<>',
 
-      home: "",
+      home: '',
 
-      album: "album/<>/",
-      albumCommentary: "commentary/album/<>/",
+      album: 'album/<>/',
+      albumCommentary: 'commentary/album/<>/',
 
-      artist: "artist/<>/",
-      artistGallery: "artist/<>/gallery/",
+      artist: 'artist/<>/',
+      artistGallery: 'artist/<>/gallery/',
 
-      commentaryIndex: "commentary/",
+      commentaryIndex: 'commentary/',
 
-      flashIndex: "flash/",
-      flash: "flash/<>/",
+      flashIndex: 'flash/',
+      flash: 'flash/<>/',
 
-      groupInfo: "group/<>/",
-      groupGallery: "group/<>/gallery/",
+      groupInfo: 'group/<>/',
+      groupGallery: 'group/<>/gallery/',
 
-      listingIndex: "list/",
-      listing: "list/<>/",
+      listingIndex: 'list/',
+      listing: 'list/<>/',
 
-      newsIndex: "news/",
-      newsEntry: "news/<>/",
+      newsIndex: 'news/',
+      newsEntry: 'news/<>/',
 
-      staticPage: "<>/",
-      tag: "tag/<>/",
-      track: "track/<>/",
+      staticPage: '<>/',
+      tag: 'tag/<>/',
+      track: 'track/<>/',
     },
   },
 
   shared: {
     paths: {
-      root: "",
-      path: "<>",
+      root: '',
+      path: '<>',
 
-      utilityRoot: "util",
-      staticRoot: "static",
+      utilityRoot: 'util',
+      staticRoot: 'static',
 
-      utilityFile: "util/<>",
-      staticFile: "static/<>",
+      utilityFile: 'util/<>',
+      staticFile: 'static/<>',
     },
   },
 
   media: {
-    prefix: "media/",
+    prefix: 'media/',
 
     paths: {
-      root: "",
-      path: "<>",
-
-      albumCover: "album-art/<>/cover.<>",
-      albumWallpaper: "album-art/<>/bg.<>",
-      albumBanner: "album-art/<>/banner.<>",
-      trackCover: "album-art/<>/<>.<>",
-      artistAvatar: "artist-avatar/<>.<>",
-      flashArt: "flash-art/<>.<>",
-      albumAdditionalFile: "album-additional/<>/<>",
+      root: '',
+      path: '<>',
+
+      albumCover: 'album-art/<>/cover.<>',
+      albumWallpaper: 'album-art/<>/bg.<>',
+      albumBanner: 'album-art/<>/banner.<>',
+      trackCover: 'album-art/<>/<>.<>',
+      artistAvatar: 'artist-avatar/<>.<>',
+      flashArt: 'flash-art/<>.<>',
+      albumAdditionalFile: 'album-additional/<>/<>',
     },
   },
 };
@@ -87,7 +87,7 @@ const urlSpec = {
 // so it should never be referenced manually.
 urlSpec.localizedWithBaseDirectory = {
   paths: withEntries(urlSpec.localized.paths, (entries) =>
-    entries.map(([key, path]) => [key, "<>/" + path])
+    entries.map(([key, path]) => [key, '<>/' + path])
   ),
 };
 
diff --git a/src/util/cli.js b/src/util/cli.js
index 159d526..d28ef40 100644
--- a/src/util/cli.js
+++ b/src/util/cli.js
@@ -1,17 +1,17 @@
-// @format
-//
+/** @format */
+
 // Utility functions for CLI- and de8ugging-rel8ted stuff.
 //
 // A 8unch of these depend on process.stdout 8eing availa8le, so they won't
 // work within the 8rowser.
 
-const { process } = globalThis;
+const {process} = globalThis;
 
 export const ENABLE_COLOR =
   process &&
-  ((process.env.CLICOLOR_FORCE && process.env.CLICOLOR_FORCE === "1") ??
+  ((process.env.CLICOLOR_FORCE && process.env.CLICOLOR_FORCE === '1') ??
     (process.env.CLICOLOR &&
-      process.env.CLICOLOR === "1" &&
+      process.env.CLICOLOR === '1' &&
       process.stdout.hasColors &&
       process.stdout.hasColors()) ??
     (process.stdout.hasColors ? process.stdout.hasColors() : true));
@@ -20,17 +20,17 @@ const C = (n) =>
   ENABLE_COLOR ? (text) => `\x1b[${n}m${text}\x1b[0m` : (text) => text;
 
 export const color = {
-  bright: C("1"),
-  dim: C("2"),
-  normal: C("22"),
-  black: C("30"),
-  red: C("31"),
-  green: C("32"),
-  yellow: C("33"),
-  blue: C("34"),
-  magenta: C("35"),
-  cyan: C("36"),
-  white: C("37"),
+  bright: C('1'),
+  dim: C('2'),
+  normal: C('22'),
+  black: C('30'),
+  red: C('31'),
+  green: C('32'),
+  yellow: C('33'),
+  blue: C('34'),
+  magenta: C('35'),
+  cyan: C('36'),
+  white: C('37'),
 };
 
 const logColor =
@@ -51,7 +51,7 @@ const logColor =
       }
     }
     wc(`\x1b[0m`);
-    w("\n");
+    w('\n');
   };
 
 export const logInfo = logColor(2);
@@ -105,9 +105,9 @@ export async function parseOptions(options, optionDescriptorMap) {
   const result = Object.create(null);
   for (let i = 0; i < options.length; i++) {
     const option = options[i];
-    if (option.startsWith("--")) {
+    if (option.startsWith('--')) {
       // --x can be a flag or expect a value or series of values
-      let name = option.slice(2).split("=")[0]; // '--x'.split('=') = ['--x']
+      let name = option.slice(2).split('=')[0]; // '--x'.split('=') = ['--x']
       let descriptor = optionDescriptorMap[name];
       if (!descriptor) {
         if (handleUnknown) {
@@ -122,13 +122,13 @@ export async function parseOptions(options, optionDescriptorMap) {
         name = descriptor.alias;
         descriptor = optionDescriptorMap[name];
       }
-      if (descriptor.type === "flag") {
+      if (descriptor.type === 'flag') {
         result[name] = true;
-      } else if (descriptor.type === "value") {
-        let value = option.slice(2).split("=")[1];
+      } else if (descriptor.type === 'value') {
+        let value = option.slice(2).split('=')[1];
         if (!value) {
           value = options[++i];
-          if (!value || value.startsWith("-")) {
+          if (!value || value.startsWith('-')) {
             value = null;
           }
         }
@@ -137,14 +137,14 @@ export async function parseOptions(options, optionDescriptorMap) {
           process.exit(1);
         }
         result[name] = value;
-      } else if (descriptor.type === "series") {
-        if (!options.slice(i).includes(";")) {
+      } else if (descriptor.type === 'series') {
+        if (!options.slice(i).includes(';')) {
           console.error(
             `Expected a series of values concluding with ; (\\;) for --${name}`
           );
           process.exit(1);
         }
-        const endIndex = i + options.slice(i).indexOf(";");
+        const endIndex = i + options.slice(i).indexOf(';');
         result[name] = options.slice(i + 1, endIndex);
         i = endIndex;
       }
@@ -155,7 +155,7 @@ export async function parseOptions(options, optionDescriptorMap) {
           process.exit(1);
         }
       }
-    } else if (option.startsWith("-")) {
+    } else if (option.startsWith('-')) {
       // mtui doesn't use any -x=y or -x y format optionuments
       // -x will always just be a flag
       let name = option.slice(1);
@@ -173,7 +173,7 @@ export async function parseOptions(options, optionDescriptorMap) {
         name = descriptor.alias;
         descriptor = optionDescriptorMap[name];
       }
-      if (descriptor.type === "flag") {
+      if (descriptor.type === 'flag') {
         result[name] = true;
       } else {
         console.error(`Use --${name} (value) to specify ${name}`);
@@ -191,7 +191,7 @@ export const handleUnknown = Symbol();
 
 export function decorateTime(arg1, arg2) {
   const [id, functionToBeWrapped] =
-    typeof arg1 === "string" || typeof arg1 === "symbol"
+    typeof arg1 === 'string' || typeof arg1 === 'symbol'
       ? [arg1, arg2]
       : [Symbol(arg1.name), arg1];
 
@@ -202,7 +202,7 @@ export function decorateTime(arg1, arg2) {
     displayTime() {
       const averageTime = meta.timeSpent / meta.timesCalled;
       console.log(
-        `\x1b[1m${typeof id === "symbol" ? id.description : id}(...):\x1b[0m ${
+        `\x1b[1m${typeof id === 'symbol' ? id.description : id}(...):\x1b[0m ${
           meta.timeSpent
         } ms / ${meta.timesCalled} calls \x1b[2m(avg: ${averageTime} ms)\x1b[0m`
       );
@@ -236,7 +236,7 @@ decorateTime.displayTime = function () {
   ];
 
   if (keys.length) {
-    console.log(`\x1b[1mdecorateTime results: ` + "-".repeat(40) + "\x1b[0m");
+    console.log(`\x1b[1mdecorateTime results: ` + '-'.repeat(40) + '\x1b[0m');
     for (const key of keys) {
       map[key].displayTime();
     }
@@ -249,7 +249,7 @@ export function progressPromiseAll(msgOrMsgFn, array) {
   }
 
   const msgFn =
-    typeof msgOrMsgFn === "function" ? msgOrMsgFn : () => msgOrMsgFn;
+    typeof msgOrMsgFn === 'function' ? msgOrMsgFn : () => msgOrMsgFn;
 
   let done = 0,
     total = array.length;
@@ -260,9 +260,9 @@ export function progressPromiseAll(msgOrMsgFn, array) {
       Promise.resolve(promise).then((val) => {
         done++;
         // const pc = `${done}/${total}`;
-        const pc = (Math.round((done / total) * 1000) / 10 + "%").padEnd(
-          "99.9%".length,
-          " "
+        const pc = (Math.round((done / total) * 1000) / 10 + '%').padEnd(
+          '99.9%'.length,
+          ' '
         );
         if (done === total) {
           const time = Date.now() - start;
diff --git a/src/util/colors.js b/src/util/colors.js
index 8d6b24d..5848a82 100644
--- a/src/util/colors.js
+++ b/src/util/colors.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // Color and theming utility functions! Handy.
 
 // Graciously stolen from https://stackoverflow.com/a/54071699! ::::)
diff --git a/src/util/find.js b/src/util/find.js
index 6ee0583..460a4fa 100644
--- a/src/util/find.js
+++ b/src/util/find.js
@@ -1,14 +1,14 @@
-// @format
+/** @format */
 
-import { color, logError, logWarn } from "./cli.js";
+import {color, logError, logWarn} from './cli.js';
 
-import { inspect } from "util";
+import {inspect} from 'util';
 
 function warnOrThrow(mode, message) {
   switch (mode) {
-    case "error":
+    case 'error':
       throw new Error(message);
-    case "warn":
+    case 'warn':
       logWarn(message);
     default:
       return null;
@@ -26,16 +26,16 @@ function findHelper(keys, findFns = {}) {
   const byName = findFns.byName || matchName;
 
   const keyRefRegex = new RegExp(
-    String.raw`^(?:(${keys.join("|")}):(?=\S))?(.*)$`
+    String.raw`^(?:(${keys.join('|')}):(?=\S))?(.*)$`
   );
 
   // The mode argument here may be 'warn', 'error', or 'quiet'. 'error' throws
   // errors for null matches (with details about the error), while 'warn' and
   // 'quiet' both return null, with 'warn' logging details directly to the
   // console.
-  return (fullRef, data, { mode = "warn" } = {}) => {
+  return (fullRef, data, {mode = 'warn'} = {}) => {
     if (!fullRef) return null;
-    if (typeof fullRef !== "string") {
+    if (typeof fullRef !== 'string') {
       throw new Error(
         `Got a reference that is ${typeof fullRef}, not string: ${fullRef}`
       );
@@ -81,19 +81,19 @@ function findHelper(keys, findFns = {}) {
 }
 
 function matchDirectory(ref, data, mode) {
-  return data.find(({ directory }) => directory === ref);
+  return data.find(({directory}) => directory === ref);
 }
 
 function matchName(ref, data, mode) {
   const matches = data.filter(
-    ({ name }) => name.toLowerCase() === ref.toLowerCase()
+    ({name}) => name.toLowerCase() === ref.toLowerCase()
   );
 
   if (matches.length > 1) {
     return warnOrThrow(
       mode,
       `Multiple matches for reference "${ref}". Please resolve:\n` +
-        matches.map((match) => `- ${inspect(match)}\n`).join("") +
+        matches.map((match) => `- ${inspect(match)}\n`).join('') +
         `Returning null for this reference.`
     );
   }
@@ -115,19 +115,19 @@ function matchName(ref, data, mode) {
 }
 
 function matchTagName(ref, data, quiet) {
-  return matchName(ref.startsWith("cw: ") ? ref.slice(4) : ref, data, quiet);
+  return matchName(ref.startsWith('cw: ') ? ref.slice(4) : ref, data, quiet);
 }
 
 const find = {
-  album: findHelper(["album", "album-commentary"]),
-  artist: findHelper(["artist", "artist-gallery"]),
-  artTag: findHelper(["tag"], { byName: matchTagName }),
-  flash: findHelper(["flash"]),
-  group: findHelper(["group", "group-gallery"]),
-  listing: findHelper(["listing"]),
-  newsEntry: findHelper(["news-entry"]),
-  staticPage: findHelper(["static"]),
-  track: findHelper(["track"]),
+  album: findHelper(['album', 'album-commentary']),
+  artist: findHelper(['artist', 'artist-gallery']),
+  artTag: findHelper(['tag'], {byName: matchTagName}),
+  flash: findHelper(['flash']),
+  group: findHelper(['group', 'group-gallery']),
+  listing: findHelper(['listing']),
+  newsEntry: findHelper(['news-entry']),
+  staticPage: findHelper(['static']),
+  track: findHelper(['track']),
 };
 
 export default find;
@@ -140,15 +140,15 @@ export default find;
 export function bindFind(wikiData, opts1) {
   return Object.fromEntries(
     Object.entries({
-      album: "albumData",
-      artist: "artistData",
-      artTag: "artTagData",
-      flash: "flashData",
-      group: "groupData",
-      listing: "listingSpec",
-      newsEntry: "newsData",
-      staticPage: "staticPageData",
-      track: "trackData",
+      album: 'albumData',
+      artist: 'artistData',
+      artTag: 'artTagData',
+      flash: 'flashData',
+      group: 'groupData',
+      listing: 'listingSpec',
+      newsEntry: 'newsData',
+      staticPage: 'staticPageData',
+      track: 'trackData',
     }).map(([key, value]) => {
       const findFn = find[key];
       const thingData = wikiData[value];
@@ -157,7 +157,7 @@ export function bindFind(wikiData, opts1) {
         opts1
           ? (ref, opts2) =>
               opts2
-                ? findFn(ref, thingData, { ...opts1, ...opts2 })
+                ? findFn(ref, thingData, {...opts1, ...opts2})
                 : findFn(ref, thingData, opts1)
           : (ref, opts2) =>
               opts2 ? findFn(ref, thingData, opts2) : findFn(ref, thingData),
diff --git a/src/util/html.js b/src/util/html.js
index 913dc7b..f5b7bdc 100644
--- a/src/util/html.js
+++ b/src/util/html.js
@@ -1,23 +1,23 @@
-// @format
-//
+/** @format */
+
 // Some really simple functions for formatting HTML content.
 
 // COMPREHENSIVE!
 // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
 export const selfClosingTags = [
-  "area",
-  "base",
-  "br",
-  "col",
-  "embed",
-  "hr",
-  "img",
-  "input",
-  "link",
-  "meta",
-  "source",
-  "track",
-  "wbr",
+  'area',
+  'base',
+  'br',
+  'col',
+  'embed',
+  'hr',
+  'img',
+  'input',
+  'link',
+  'meta',
+  'source',
+  'track',
+  'wbr',
 ];
 
 // Pass to tag() as an attri8utes key to make tag() return a 8lank string
@@ -32,7 +32,7 @@ export function tag(tagName, ...args) {
   let content;
   let attrs;
 
-  if (typeof args[0] === "object" && !Array.isArray(args[0])) {
+  if (typeof args[0] === 'object' && !Array.isArray(args[0])) {
     attrs = args[0];
     content = args[1];
   } else {
@@ -44,7 +44,7 @@ export function tag(tagName, ...args) {
   }
 
   if (attrs?.[onlyIfContent] && !content) {
-    return "";
+    return '';
   }
 
   if (attrs) {
@@ -59,17 +59,17 @@ export function tag(tagName, ...args) {
   }
 
   if (Array.isArray(content)) {
-    content = content.filter(Boolean).join("\n");
+    content = content.filter(Boolean).join('\n');
   }
 
   if (content) {
-    if (content.includes("\n")) {
+    if (content.includes('\n')) {
       return (
         `<${openTag}>\n` +
         content
-          .split("\n")
-          .map((line) => "    " + line + "\n")
-          .join("") +
+          .split('\n')
+          .map((line) => '    ' + line + '\n')
+          .join('') +
         `</${tagName}>`
       );
     } else {
@@ -85,18 +85,18 @@ export function tag(tagName, ...args) {
 }
 
 export function escapeAttributeValue(value) {
-  return value.replaceAll('"', "&quot;").replaceAll("'", "&apos;");
+  return value.replaceAll('"', '&quot;').replaceAll("'", '&apos;');
 }
 
 export function attributes(attribs) {
   return Object.entries(attribs)
     .map(([key, val]) => {
-      if (typeof val === "undefined" || val === null) return [key, val, false];
-      else if (typeof val === "string") return [key, val, true];
-      else if (typeof val === "boolean") return [key, val, val];
-      else if (typeof val === "number") return [key, val.toString(), true];
+      if (typeof val === 'undefined' || val === null) return [key, val, false];
+      else if (typeof val === 'string') return [key, val, true];
+      else if (typeof val === 'boolean') return [key, val, val];
+      else if (typeof val === 'number') return [key, val.toString(), true];
       else if (Array.isArray(val))
-        return [key, val.filter(Boolean).join(" "), val.length > 0];
+        return [key, val.filter(Boolean).join(' '), val.length > 0];
       else
         throw new Error(
           `Attribute value for ${key} should be primitive or array, got ${typeof val}`
@@ -104,9 +104,9 @@ export function attributes(attribs) {
     })
     .filter(([key, val, keep]) => keep)
     .map(([key, val]) =>
-      typeof val === "boolean"
+      typeof val === 'boolean'
         ? `${key}`
         : `${key}="${escapeAttributeValue(val)}"`
     )
-    .join(" ");
+    .join(' ');
 }
diff --git a/src/util/io.js b/src/util/io.js
index 6ea1e22..5c1ab24 100644
--- a/src/util/io.js
+++ b/src/util/io.js
@@ -1,14 +1,14 @@
-// @format
-//
+/** @format */
+
 // Utility functions for interacting with files and other external data
 // interfacey constructs.
 
-import { readdir } from "fs/promises";
-import * as path from "path";
+import {readdir} from 'fs/promises';
+import * as path from 'path';
 
 export async function findFiles(
   dataPath,
-  { filter = (f) => true, joinParentDirectory = true } = {}
+  {filter = (f) => true, joinParentDirectory = true} = {}
 ) {
   return (await readdir(dataPath))
     .filter((file) => filter(file))
diff --git a/src/util/link.js b/src/util/link.js
index 4095b17..ee3579d 100644
--- a/src/util/link.js
+++ b/src/util/link.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // This file is essentially one level of a8straction a8ove urls.js (and the
 // urlSpec it gets its paths from). It's a 8unch of utility functions which
 // take certain types of wiki data o8jects (colloquially known as "things")
@@ -11,74 +11,74 @@
 // options availa8le in all the functions, making a common interface for
 // gener8ting just a8out any link on the site.
 
-import * as html from "./html.js";
-import { getColors } from "./colors.js";
+import * as html from './html.js';
+import {getColors} from './colors.js';
 
 export function getLinkThemeString(color) {
-  if (!color) return "";
+  if (!color) return '';
 
-  const { primary, dim } = getColors(color);
+  const {primary, dim} = getColors(color);
   return `--primary-color: ${primary}; --dim-color: ${dim}`;
 }
 
 const appendIndexHTMLRegex = /^(?!https?:\/\/).+\/$/;
 
 const linkHelper =
-  (hrefFn, { color = true, attr = null } = {}) =>
+  (hrefFn, {color = true, attr = null} = {}) =>
   (
     thing,
     {
       to,
-      text = "",
+      text = '',
       attributes = null,
-      class: className = "",
+      class: className = '',
       color: color2 = true,
-      hash = "",
+      hash = '',
     }
   ) => {
-    let href = hrefFn(thing, { to });
+    let href = hrefFn(thing, {to});
 
     if (link.globalOptions.appendIndexHTML) {
       if (appendIndexHTMLRegex.test(href)) {
-        href += "index.html";
+        href += 'index.html';
       }
     }
 
     if (hash) {
-      href += (hash.startsWith("#") ? "" : "#") + hash;
+      href += (hash.startsWith('#') ? '' : '#') + hash;
     }
 
     return html.tag(
-      "a",
+      'a',
       {
         ...(attr ? attr(thing) : {}),
         ...(attributes ? attributes : {}),
         href,
         style:
-          typeof color2 === "string"
+          typeof color2 === 'string'
             ? getLinkThemeString(color2)
             : color2 && color
             ? getLinkThemeString(thing.color)
-            : "",
+            : '',
         class: className,
       },
       text || thing.name
     );
   };
 
-const linkDirectory = (key, { expose = null, attr = null, ...conf } = {}) =>
-  linkHelper((thing, { to }) => to("localized." + key, thing.directory), {
+const linkDirectory = (key, {expose = null, attr = null, ...conf} = {}) =>
+  linkHelper((thing, {to}) => to('localized.' + key, thing.directory), {
     attr: (thing) => ({
       ...(attr ? attr(thing) : {}),
-      ...(expose ? { [expose]: thing.directory } : {}),
+      ...(expose ? {[expose]: thing.directory} : {}),
     }),
     ...conf,
   });
 
 const linkPathname = (key, conf) =>
-  linkHelper(({ directory: pathname }, { to }) => to(key, pathname), conf);
+  linkHelper(({directory: pathname}, {to}) => to(key, pathname), conf);
 const linkIndex = (key, conf) =>
-  linkHelper((_, { to }) => to("localized." + key), conf);
+  linkHelper((_, {to}) => to('localized.' + key), conf);
 
 const link = {
   globalOptions: {
@@ -90,50 +90,50 @@ const link = {
     appendIndexHTML: false,
   },
 
-  album: linkDirectory("album"),
-  albumCommentary: linkDirectory("albumCommentary"),
-  artist: linkDirectory("artist", { color: false }),
-  artistGallery: linkDirectory("artistGallery", { color: false }),
-  commentaryIndex: linkIndex("commentaryIndex", { color: false }),
-  flashIndex: linkIndex("flashIndex", { color: false }),
-  flash: linkDirectory("flash"),
-  groupInfo: linkDirectory("groupInfo"),
-  groupGallery: linkDirectory("groupGallery"),
-  home: linkIndex("home", { color: false }),
-  listingIndex: linkIndex("listingIndex"),
-  listing: linkDirectory("listing"),
-  newsIndex: linkIndex("newsIndex", { color: false }),
-  newsEntry: linkDirectory("newsEntry", { color: false }),
-  staticPage: linkDirectory("staticPage", { color: false }),
-  tag: linkDirectory("tag"),
-  track: linkDirectory("track", { expose: "data-track" }),
+  album: linkDirectory('album'),
+  albumCommentary: linkDirectory('albumCommentary'),
+  artist: linkDirectory('artist', {color: false}),
+  artistGallery: linkDirectory('artistGallery', {color: false}),
+  commentaryIndex: linkIndex('commentaryIndex', {color: false}),
+  flashIndex: linkIndex('flashIndex', {color: false}),
+  flash: linkDirectory('flash'),
+  groupInfo: linkDirectory('groupInfo'),
+  groupGallery: linkDirectory('groupGallery'),
+  home: linkIndex('home', {color: false}),
+  listingIndex: linkIndex('listingIndex'),
+  listing: linkDirectory('listing'),
+  newsIndex: linkIndex('newsIndex', {color: false}),
+  newsEntry: linkDirectory('newsEntry', {color: false}),
+  staticPage: linkDirectory('staticPage', {color: false}),
+  tag: linkDirectory('tag'),
+  track: linkDirectory('track', {expose: 'data-track'}),
 
   // TODO: This is a bit hacky. Files are just strings (not objects), so we
   // have to manually provide the album alongside the file. They also don't
   // follow the usual {name: whatever} type shape, so we have to provide that
   // ourselves.
   _albumAdditionalFileHelper: linkHelper(
-    (fakeFileObject, { to }) =>
+    (fakeFileObject, {to}) =>
       to(
-        "media.albumAdditionalFile",
+        'media.albumAdditionalFile',
         fakeFileObject.album.directory,
         fakeFileObject.name
       ),
-    { color: false }
+    {color: false}
   ),
-  albumAdditionalFile: ({ file, album }, { to }) =>
+  albumAdditionalFile: ({file, album}, {to}) =>
     link._albumAdditionalFileHelper(
       {
         name: file,
         album,
       },
-      { to }
+      {to}
     ),
 
-  media: linkPathname("media.path", { color: false }),
-  root: linkPathname("shared.path", { color: false }),
-  data: linkPathname("data.path", { color: false }),
-  site: linkPathname("localized.path", { color: false }),
+  media: linkPathname('media.path', {color: false}),
+  root: linkPathname('shared.path', {color: false}),
+  data: linkPathname('data.path', {color: false}),
+  site: linkPathname('localized.path', {color: false}),
 };
 
 export default link;
diff --git a/src/util/magic-constants.js b/src/util/magic-constants.js
index a7b2933..dbdbcfd 100644
--- a/src/util/magic-constants.js
+++ b/src/util/magic-constants.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // Magic constants only! These are hard-coded, and any use of them should be
 // considered a flaw in the codebase - areas where we use hard-coded behavior
 // to support one use of the wiki software (i.e. HSMusic, usually), rather than
@@ -8,5 +8,5 @@
 // All such uses should eventually be replaced with better code in due time
 // (TM).
 
-export const OFFICIAL_GROUP_DIRECTORY = "official";
-export const FANDOM_GROUP_DIRECTORY = "fandom";
+export const OFFICIAL_GROUP_DIRECTORY = 'official';
+export const FANDOM_GROUP_DIRECTORY = 'fandom';
diff --git a/src/util/node-utils.js b/src/util/node-utils.js
index f638e4a..df44665 100644
--- a/src/util/node-utils.js
+++ b/src/util/node-utils.js
@@ -1,10 +1,10 @@
-// @format
-//
+/** @format */
+
 // Utility functions which are only relevant to particular Node.js constructs.
 
-import { fileURLToPath } from "url";
+import {fileURLToPath} from 'url';
 
-import _commandExists from "command-exists";
+import _commandExists from 'command-exists';
 
 // This package throws an error instead of returning false when the command
 // doesn't exist, for some reason. Yay for making logic more difficult!
@@ -32,7 +32,7 @@ export function promisifyProcess(proc, showLogging = true) {
       proc.stderr.pipe(process.stderr);
     }
 
-    proc.on("exit", (code) => {
+    proc.on('exit', (code) => {
       if (code === 0) {
         resolve();
       } else {
diff --git a/src/util/replacer.js b/src/util/replacer.js
index 10603b6..6f4d0e9 100644
--- a/src/util/replacer.js
+++ b/src/util/replacer.js
@@ -1,14 +1,14 @@
-// @format
+/** @format */
 
-import { logError, logWarn } from "./cli.js";
-import { escapeRegex } from "./sugar.js";
+import {logError, logWarn} from './cli.js';
+import {escapeRegex} from './sugar.js';
 
-export function validateReplacerSpec(replacerSpec, { find, link }) {
+export function validateReplacerSpec(replacerSpec, {find, link}) {
   let success = true;
 
   for (const [
     key,
-    { link: linkKey, find: findKey, value, html },
+    {link: linkKey, find: findKey, value, html},
   ] of Object.entries(replacerSpec)) {
     if (!html && !link[linkKey]) {
       logError`The replacer spec ${key} has invalid link key ${linkKey}! Specify it in link specs or fix typo.`;
@@ -24,15 +24,15 @@ export function validateReplacerSpec(replacerSpec, { find, link }) {
 }
 
 // Syntax literals.
-const tagBeginning = "[[";
-const tagEnding = "]]";
-const tagReplacerValue = ":";
-const tagHash = "#";
-const tagArgument = "*";
-const tagArgumentValue = "=";
-const tagLabel = "|";
+const tagBeginning = '[[';
+const tagEnding = ']]';
+const tagReplacerValue = ':';
+const tagHash = '#';
+const tagArgument = '*';
+const tagArgumentValue = '=';
+const tagLabel = '|';
 
-const noPrecedingWhitespace = "(?<!\\s)";
+const noPrecedingWhitespace = '(?<!\\s)';
 
 const R_tagBeginning = escapeRegex(tagBeginning);
 
@@ -51,7 +51,7 @@ const R_tagLabel = escapeRegex(tagLabel);
 
 const regexpCache = {};
 
-const makeError = (i, message) => ({ i, type: "error", data: { message } });
+const makeError = (i, message) => ({i, type: 'error', data: {message}});
 const endOfInput = (i, comment) =>
   makeError(i, `Unexpected end of input (${comment}).`);
 
@@ -67,7 +67,7 @@ function parseOneTextNode(input, i, stopAt) {
 function parseNodes(input, i, stopAt, textOnly) {
   let nodes = [];
   let escapeNext = false;
-  let string = "";
+  let string = '';
   let iString = 0;
 
   stopped = false;
@@ -82,8 +82,8 @@ function parseNodes(input, i, stopAt, textOnly) {
     }
 
     if (string.length) {
-      nodes.push({ i: iString, iEnd: i, type: "text", data: string });
-      string = "";
+      nodes.push({i: iString, iEnd: i, type: 'text', data: string});
+      string = '';
     }
   };
 
@@ -97,7 +97,7 @@ function parseNodes(input, i, stopAt, textOnly) {
   // should 8e counted only as part of the current string/text.
   //
   // Inspired 8y this: https://stackoverflow.com/a/41470813
-  const regexpSource = `(?<!\\\\)(?:\\\\{2})*(${literalsToMatch.join("|")})`;
+  const regexpSource = `(?<!\\\\)(?:\\\\{2})*(${literalsToMatch.join('|')})`;
 
   // There are 8asically only a few regular expressions we'll ever use,
   // 8ut it's a pain to hard-code them all, so we dynamically gener8te
@@ -271,7 +271,7 @@ function parseNodes(input, i, stopAt, textOnly) {
         const value = N;
         i = stop_iParse;
 
-        args.push({ key, value });
+        args.push({key, value});
       }
 
       let label;
@@ -289,8 +289,8 @@ function parseNodes(input, i, stopAt, textOnly) {
       nodes.push({
         i: iTag,
         iEnd: i,
-        type: "tag",
-        data: { replacerKey, replacerValue, hash, args, label },
+        type: 'tag',
+        data: {replacerKey, replacerValue, hash, args, label},
       });
 
       continue;
@@ -304,23 +304,23 @@ export function parseInput(input) {
   try {
     return parseNodes(input, 0);
   } catch (errorNode) {
-    if (errorNode.type !== "error") {
+    if (errorNode.type !== 'error') {
       throw errorNode;
     }
 
     const {
       i,
-      data: { message },
+      data: {message},
     } = errorNode;
 
-    let lineStart = input.slice(0, i).lastIndexOf("\n");
+    let lineStart = input.slice(0, i).lastIndexOf('\n');
     if (lineStart >= 0) {
       lineStart += 1;
     } else {
       lineStart = 0;
     }
 
-    let lineEnd = input.slice(i).indexOf("\n");
+    let lineEnd = input.slice(i).indexOf('\n');
     if (lineEnd >= 0) {
       lineEnd += i;
     } else {
@@ -334,18 +334,18 @@ export function parseInput(input) {
     throw new SyntaxError(fixWS`
             Parse error (at pos ${i}): ${message}
             ${line}
-            ${"-".repeat(cursor) + "^"}
+            ${'-'.repeat(cursor) + '^'}
         `);
   }
 }
 
 function evaluateTag(node, opts) {
-  const { find, input, language, link, replacerSpec, to, wikiData } = opts;
+  const {find, input, language, link, replacerSpec, to, wikiData} = opts;
 
   const source = input.slice(node.i, node.iEnd);
 
   const replacerKeyImplied = !node.data.replacerKey;
-  const replacerKey = replacerKeyImplied ? "track" : node.data.replacerKey.data;
+  const replacerKey = replacerKeyImplied ? 'track' : node.data.replacerKey.data;
 
   if (!replacerSpec[replacerKey]) {
     logWarn`The link ${source} has an invalid replacer key!`;
@@ -395,7 +395,7 @@ function evaluateTag(node, opts) {
   const args =
     node.data.args &&
     Object.fromEntries(
-      node.data.args.map(({ key, value }) => [
+      node.data.args.map(({key, value}) => [
         transformNode(key, opts),
         transformNodes(value, opts),
       ])
@@ -404,7 +404,7 @@ function evaluateTag(node, opts) {
   const fn = htmlFn ? htmlFn : link[linkKey];
 
   try {
-    return fn(value, { text: label, hash, args, language, to });
+    return fn(value, {text: label, hash, args, language, to});
   } catch (error) {
     logError`The link ${source} failed to be processed: ${error}`;
     return source;
@@ -413,17 +413,17 @@ function evaluateTag(node, opts) {
 
 function transformNode(node, opts) {
   if (!node) {
-    throw new Error("Expected a node!");
+    throw new Error('Expected a node!');
   }
 
   if (Array.isArray(node)) {
-    throw new Error("Got an array - use transformNodes here!");
+    throw new Error('Got an array - use transformNodes here!');
   }
 
   switch (node.type) {
-    case "text":
+    case 'text':
       return node.data;
-    case "tag":
+    case 'tag':
       return evaluateTag(node, opts);
     default:
       throw new Error(`Unknown node type ${node.type}`);
@@ -435,19 +435,19 @@ function transformNodes(nodes, opts) {
     throw new Error(`Expected an array of nodes! Got: ${nodes}`);
   }
 
-  return nodes.map((node) => transformNode(node, opts)).join("");
+  return nodes.map((node) => transformNode(node, opts)).join('');
 }
 
 export function transformInline(
   input,
-  { replacerSpec, find, link, language, to, wikiData }
+  {replacerSpec, find, link, language, to, wikiData}
 ) {
-  if (!replacerSpec) throw new Error("Expected replacerSpec");
-  if (!find) throw new Error("Expected find");
-  if (!link) throw new Error("Expected link");
-  if (!language) throw new Error("Expected language");
-  if (!to) throw new Error("Expected to");
-  if (!wikiData) throw new Error("Expected wikiData");
+  if (!replacerSpec) throw new Error('Expected replacerSpec');
+  if (!find) throw new Error('Expected find');
+  if (!link) throw new Error('Expected link');
+  if (!language) throw new Error('Expected language');
+  if (!to) throw new Error('Expected to');
+  if (!wikiData) throw new Error('Expected wikiData');
 
   const nodes = parseInput(input);
   return transformNodes(nodes, {
diff --git a/src/util/serialize.js b/src/util/serialize.js
index ab86483..9aa8b0c 100644
--- a/src/util/serialize.js
+++ b/src/util/serialize.js
@@ -1,4 +1,4 @@
-// @format
+/** @format */
 
 export function serializeLink(thing) {
   const ret = {};
@@ -9,7 +9,7 @@ export function serializeLink(thing) {
 }
 
 export function serializeContribs(contribs) {
-  return contribs.map(({ who, what }) => {
+  return contribs.map(({who, what}) => {
     const ret = {};
     ret.artist = serializeLink(who);
     if (what) ret.contribution = what;
@@ -17,7 +17,7 @@ export function serializeContribs(contribs) {
   });
 }
 
-export function serializeImagePaths(original, { thumb }) {
+export function serializeImagePaths(original, {thumb}) {
   return {
     original,
     medium: thumb.medium(original),
@@ -28,13 +28,13 @@ export function serializeImagePaths(original, { thumb }) {
 export function serializeCover(
   thing,
   pathFunction,
-  { serializeImagePaths, urls }
+  {serializeImagePaths, urls}
 ) {
   const coverPath = pathFunction(thing, {
-    to: urls.from("media.root").to,
+    to: urls.from('media.root').to,
   });
 
-  const { artTags } = thing;
+  const {artTags} = thing;
 
   const cwTags = artTags.filter((tag) => tag.isContentWarning);
   const linkTags = artTags.filter((tag) => !tag.isContentWarning);
@@ -46,15 +46,15 @@ export function serializeCover(
   };
 }
 
-export function serializeGroupsForAlbum(album, { serializeLink }) {
+export function serializeGroupsForAlbum(album, {serializeLink}) {
   return album.groups
     .map((group) => {
       const index = group.albums.indexOf(album);
       const next = group.albums[index + 1] || null;
       const previous = group.albums[index - 1] || null;
-      return { group, index, next, previous };
+      return {group, index, next, previous};
     })
-    .map(({ group, index, next, previous }) => ({
+    .map(({group, index, next, previous}) => ({
       link: serializeLink(group),
       descriptionShort: group.descriptionShort,
       albumIndex: index,
@@ -64,7 +64,7 @@ export function serializeGroupsForAlbum(album, { serializeLink }) {
     }));
 }
 
-export function serializeGroupsForTrack(track, { serializeLink }) {
+export function serializeGroupsForTrack(track, {serializeLink}) {
   return track.album.groups.map((group) => ({
     link: serializeLink(group),
     urls: group.urls,
diff --git a/src/util/sugar.js b/src/util/sugar.js
index a4504da..0a5de48 100644
--- a/src/util/sugar.js
+++ b/src/util/sugar.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // Syntactic sugar! (Mostly.)
 // Generic functions - these are useful just a8out everywhere.
 //
@@ -8,7 +8,7 @@
 // It will likely only do exactly what I want it to, and only in the cases I
 // decided were relevant enough to 8other handling.
 
-import { color } from "./cli.js";
+import {color} from './cli.js';
 
 // Apparently JavaScript doesn't come with a function to split an array into
 // chunks! Weird. Anyway, this is an awesome place to use a generator, even
@@ -35,13 +35,13 @@ export const mapInPlace = (array, fn) =>
 
 export const filterEmptyLines = (string) =>
   string
-    .split("\n")
+    .split('\n')
     .filter((line) => line.trim())
-    .join("\n");
+    .join('\n');
 
 export const unique = (arr) => Array.from(new Set(arr));
 
-export const compareArrays = (arr1, arr2, { checkOrder = true } = {}) =>
+export const compareArrays = (arr1, arr2, {checkOrder = true} = {}) =>
   arr1.length === arr2.length &&
   (checkOrder
     ? arr1.every((x, i) => arr2[i] === x)
@@ -90,7 +90,7 @@ export function delay(ms) {
 // There's a proposal for a native JS function like this, 8ut it's not even
 // past stage 1 yet: https://github.com/tc39/proposal-regex-escaping
 export function escapeRegex(string) {
-  return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
+  return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
 }
 
 export function bindOpts(fn, bind) {
@@ -98,10 +98,10 @@ export function bindOpts(fn, bind) {
 
   const bound = function (...args) {
     const opts = args[bindIndex] ?? {};
-    return fn(...args.slice(0, bindIndex), { ...bind, ...opts });
+    return fn(...args.slice(0, bindIndex), {...bind, ...opts});
   };
 
-  Object.defineProperty(bound, "name", {
+  Object.defineProperty(bound, 'name', {
     value: fn.name ? `(options-bound) ${fn.name}` : `(options-bound)`,
   });
 
@@ -132,7 +132,7 @@ export function openAggregate({
 
   // Optional human-readable message to describe the aggregate error, if
   // constructed.
-  message = "",
+  message = '',
 
   // Value to return when a provided function throws an error. If this is a
   // function, it will be called with the arguments given to the function.
@@ -151,7 +151,7 @@ export function openAggregate({
         return fn(...args);
       } catch (error) {
         errors.push(error);
-        return typeof returnOnFail === "function"
+        return typeof returnOnFail === 'function'
           ? returnOnFail(...args)
           : returnOnFail;
       }
@@ -164,7 +164,7 @@ export function openAggregate({
         (value) => value,
         (error) => {
           errors.push(error);
-          return typeof returnOnFail === "function"
+          return typeof returnOnFail === 'function'
             ? returnOnFail(...args)
             : returnOnFail;
         }
@@ -189,21 +189,21 @@ export function openAggregate({
 
   aggregate.map = (...args) => {
     const parent = aggregate;
-    const { result, aggregate: child } = mapAggregate(...args);
+    const {result, aggregate: child} = mapAggregate(...args);
     parent.call(child.close);
     return result;
   };
 
   aggregate.mapAsync = async (...args) => {
     const parent = aggregate;
-    const { result, aggregate: child } = await mapAggregateAsync(...args);
+    const {result, aggregate: child} = await mapAggregateAsync(...args);
     parent.call(child.close);
     return result;
   };
 
   aggregate.filter = (...args) => {
     const parent = aggregate;
-    const { result, aggregate: child } = filterAggregate(...args);
+    const {result, aggregate: child} = filterAggregate(...args);
     parent.call(child.close);
     return result;
   };
@@ -219,11 +219,11 @@ export function openAggregate({
   return aggregate;
 }
 
-openAggregate.errorClassSymbol = Symbol("error class");
+openAggregate.errorClassSymbol = Symbol('error class');
 
 // Utility function for providing {errorClass} parameter to aggregate functions.
 export function aggregateThrows(errorClass) {
-  return { [openAggregate.errorClassSymbol]: errorClass };
+  return {[openAggregate.errorClassSymbol]: errorClass};
 }
 
 // Performs an ordinary array map with the given function, collating into a
@@ -236,15 +236,15 @@ export function aggregateThrows(errorClass) {
 // use aggregate.close() to throw the error. (This aggregate may be passed to a
 // parent aggregate: `parent.call(aggregate.close)`!)
 export function mapAggregate(array, fn, aggregateOpts) {
-  return _mapAggregate("sync", null, array, fn, aggregateOpts);
+  return _mapAggregate('sync', null, array, fn, aggregateOpts);
 }
 
 export function mapAggregateAsync(
   array,
   fn,
-  { promiseAll = Promise.all.bind(Promise), ...aggregateOpts } = {}
+  {promiseAll = Promise.all.bind(Promise), ...aggregateOpts} = {}
 ) {
-  return _mapAggregate("async", promiseAll, array, fn, aggregateOpts);
+  return _mapAggregate('async', promiseAll, array, fn, aggregateOpts);
 }
 
 // Helper function for mapAggregate which holds code common between sync and
@@ -257,15 +257,15 @@ export function _mapAggregate(mode, promiseAll, array, fn, aggregateOpts) {
     ...aggregateOpts,
   });
 
-  if (mode === "sync") {
+  if (mode === 'sync') {
     const result = array
       .map(aggregate.wrap(fn))
       .filter((value) => value !== failureSymbol);
-    return { result, aggregate };
+    return {result, aggregate};
   } else {
     return promiseAll(array.map(aggregate.wrapAsync(fn))).then((values) => {
       const result = values.filter((value) => value !== failureSymbol);
-      return { result, aggregate };
+      return {result, aggregate};
     });
   }
 }
@@ -278,15 +278,15 @@ export function _mapAggregate(mode, promiseAll, array, fn, aggregateOpts) {
 //
 // As with mapAggregate, the returned aggregate property is not yet closed.
 export function filterAggregate(array, fn, aggregateOpts) {
-  return _filterAggregate("sync", null, array, fn, aggregateOpts);
+  return _filterAggregate('sync', null, array, fn, aggregateOpts);
 }
 
 export async function filterAggregateAsync(
   array,
   fn,
-  { promiseAll = Promise.all.bind(Promise), ...aggregateOpts } = {}
+  {promiseAll = Promise.all.bind(Promise), ...aggregateOpts} = {}
 ) {
-  return _filterAggregate("async", promiseAll, array, fn, aggregateOpts);
+  return _filterAggregate('async', promiseAll, array, fn, aggregateOpts);
 }
 
 // Helper function for filterAggregate which holds code common between sync and
@@ -326,30 +326,30 @@ function _filterAggregate(mode, promiseAll, array, fn, aggregateOpts) {
     };
   }
 
-  if (mode === "sync") {
+  if (mode === 'sync') {
     const result = array
       .map(
         aggregate.wrap((input, index, array) => {
           const output = fn(input, index, array);
-          return { input, output };
+          return {input, output};
         })
       )
       .filter(filterFunction)
       .map(mapFunction);
 
-    return { result, aggregate };
+    return {result, aggregate};
   } else {
     return promiseAll(
       array.map(
         aggregate.wrapAsync(async (input, index, array) => {
           const output = await fn(input, index, array);
-          return { input, output };
+          return {input, output};
         })
       )
     ).then((values) => {
       const result = values.filter(filterFunction).map(mapFunction);
 
-      return { result, aggregate };
+      return {result, aggregate};
     });
   }
 }
@@ -358,22 +358,22 @@ function _filterAggregate(mode, promiseAll, array, fn, aggregateOpts) {
 // function with it, then closing the function and returning the result (if
 // there's no throw).
 export function withAggregate(aggregateOpts, fn) {
-  return _withAggregate("sync", aggregateOpts, fn);
+  return _withAggregate('sync', aggregateOpts, fn);
 }
 
 export function withAggregateAsync(aggregateOpts, fn) {
-  return _withAggregate("async", aggregateOpts, fn);
+  return _withAggregate('async', aggregateOpts, fn);
 }
 
 export function _withAggregate(mode, aggregateOpts, fn) {
-  if (typeof aggregateOpts === "function") {
+  if (typeof aggregateOpts === 'function') {
     fn = aggregateOpts;
     aggregateOpts = {};
   }
 
   const aggregate = openAggregate(aggregateOpts);
 
-  if (mode === "sync") {
+  if (mode === 'sync') {
     const result = fn(aggregate);
     aggregate.close();
     return result;
@@ -387,56 +387,56 @@ export function _withAggregate(mode, aggregateOpts, fn) {
 
 export function showAggregate(
   topError,
-  { pathToFile = (p) => p, showTraces = true } = {}
+  {pathToFile = (p) => p, showTraces = true} = {}
 ) {
-  const recursive = (error, { level }) => {
+  const recursive = (error, {level}) => {
     let header = showTraces
-      ? `[${error.constructor.name || "unnamed"}] ${
-          error.message || "(no message)"
+      ? `[${error.constructor.name || 'unnamed'}] ${
+          error.message || '(no message)'
         }`
       : error instanceof AggregateError
-      ? `[${error.message || "(no message)"}]`
-      : error.message || "(no message)";
+      ? `[${error.message || '(no message)'}]`
+      : error.message || '(no message)';
     if (showTraces) {
-      const stackLines = error.stack?.split("\n");
+      const stackLines = error.stack?.split('\n');
       const stackLine = stackLines?.find(
         (line) =>
-          line.trim().startsWith("at") &&
-          !line.includes("sugar") &&
-          !line.includes("node:") &&
-          !line.includes("<anonymous>")
+          line.trim().startsWith('at') &&
+          !line.includes('sugar') &&
+          !line.includes('node:') &&
+          !line.includes('<anonymous>')
       );
       const tracePart = stackLine
-        ? "- " +
+        ? '- ' +
           stackLine
             .trim()
             .replace(/file:\/\/(.*\.js)/, (match, pathname) =>
               pathToFile(pathname)
             )
-        : "(no stack trace)";
+        : '(no stack trace)';
       header += ` ${color.dim(tracePart)}`;
     }
-    const bar = level % 2 === 0 ? "\u2502" : color.dim("\u254e");
-    const head = level % 2 === 0 ? "\u257f" : color.dim("\u257f");
+    const bar = level % 2 === 0 ? '\u2502' : color.dim('\u254e');
+    const head = level % 2 === 0 ? '\u257f' : color.dim('\u257f');
 
     if (error instanceof AggregateError) {
       return (
         header +
-        "\n" +
+        '\n' +
         error.errors
-          .map((error) => recursive(error, { level: level + 1 }))
-          .flatMap((str) => str.split("\n"))
+          .map((error) => recursive(error, {level: level + 1}))
+          .flatMap((str) => str.split('\n'))
           .map((line, i, lines) =>
             i === 0 ? ` ${head} ${line}` : ` ${bar} ${line}`
           )
-          .join("\n")
+          .join('\n')
       );
     } else {
       return header;
     }
   };
 
-  console.error(recursive(topError, { level: 0 }));
+  console.error(recursive(topError, {level: 0}));
 }
 
 export function decorateErrorWithIndex(fn) {
diff --git a/src/util/urls.js b/src/util/urls.js
index 4e39808..ce747df 100644
--- a/src/util/urls.js
+++ b/src/util/urls.js
@@ -1,5 +1,5 @@
-// @format
-//
+/** @format */
+
 // Code that deals with URLs (really the pathnames that get referenced all
 // throughout the gener8ted HTML). Most nota8ly here is generateURLs, which
 // is in charge of pre-gener8ting a complete network of template strings
@@ -10,12 +10,12 @@
 // actual path strings. More a8stract operations using wiki data o8jects is
 // the domain of link.js.
 
-import * as path from "path";
-import { withEntries } from "./sugar.js";
+import * as path from 'path';
+import {withEntries} from './sugar.js';
 
 export function generateURLs(urlSpec) {
   const getValueForFullKey = (obj, fullKey, prop = null) => {
-    const [groupKey, subKey] = fullKey.split(".");
+    const [groupKey, subKey] = fullKey.split('.');
     if (!groupKey || !subKey) {
       throw new Error(`Expected group key and subkey (got ${fullKey})`);
     }
@@ -41,27 +41,27 @@ export function generateURLs(urlSpec) {
   // This should be called on values which are going to be passed to
   // path.relative, because relative will resolve a leading slash as the root
   // directory of the working device, which we aren't looking for here.
-  const trimLeadingSlash = (P) => (P.startsWith("/") ? P.slice(1) : P);
+  const trimLeadingSlash = (P) => (P.startsWith('/') ? P.slice(1) : P);
 
   const generateTo = (fromPath, fromGroup) => {
     const A = trimLeadingSlash(fromPath);
 
-    const rebasePrefix = "../".repeat(
-      (fromGroup.prefix || "").split("/").filter(Boolean).length
+    const rebasePrefix = '../'.repeat(
+      (fromGroup.prefix || '').split('/').filter(Boolean).length
     );
 
     const pathHelper = (toPath, toGroup) => {
       let B = trimLeadingSlash(toPath);
 
       let argIndex = 0;
-      B = B.replaceAll("<>", () => `<${argIndex++}>`);
+      B = B.replaceAll('<>', () => `<${argIndex++}>`);
 
       if (toGroup.prefix !== fromGroup.prefix) {
         // TODO: Handle differing domains in prefixes.
-        B = rebasePrefix + (toGroup.prefix || "") + B;
+        B = rebasePrefix + (toGroup.prefix || '') + B;
       }
 
-      const suffix = toPath.endsWith("/") ? "/" : "";
+      const suffix = toPath.endsWith('/') ? '/' : '';
 
       return {
         posix: path.posix.relative(A, B) + suffix,
@@ -86,7 +86,7 @@ export function generateURLs(urlSpec) {
       (delimiterMode) =>
       (key, ...args) => {
         const {
-          value: { [delimiterMode]: template },
+          value: {[delimiterMode]: template},
         } = getValueForFullKey(relative, key);
 
         let missing = 0;
@@ -110,8 +110,8 @@ export function generateURLs(urlSpec) {
       };
 
     return {
-      to: toHelper("posix"),
-      toDevice: toHelper("device"),
+      to: toHelper('posix'),
+      toDevice: toHelper('device'),
     };
   };
 
@@ -127,16 +127,16 @@ export function generateURLs(urlSpec) {
 
     const from = (key) => getValueForFullKey(map, key).value;
 
-    return { from, map };
+    return {from, map};
   };
 
   return generateFrom();
 }
 
 const thumbnailHelper = (name) => (file) =>
-  file.replace(/\.(jpg|png)$/, name + ".jpg");
+  file.replace(/\.(jpg|png)$/, name + '.jpg');
 
 export const thumb = {
-  medium: thumbnailHelper(".medium"),
-  small: thumbnailHelper(".small"),
+  medium: thumbnailHelper('.medium'),
+  small: thumbnailHelper('.small'),
 };
diff --git a/src/util/wiki-data.js b/src/util/wiki-data.js
index 7e16580..65eb7d7 100644
--- a/src/util/wiki-data.js
+++ b/src/util/wiki-data.js
@@ -1,17 +1,17 @@
-// @format
-//
+/** @format */
+
 // Utility functions for interacting with wiki data.
 
 // Generic value operations
 
 export function getKebabCase(name) {
   return name
-    .split(" ")
-    .join("-")
-    .replace(/&/g, "and")
-    .replace(/[^a-zA-Z0-9\-]/g, "")
-    .replace(/-{2,}/g, "-")
-    .replace(/^-+|-+$/g, "")
+    .split(' ')
+    .join('-')
+    .replace(/&/g, 'and')
+    .replace(/[^a-zA-Z0-9\-]/g, '')
+    .replace(/-{2,}/g, '-')
+    .replace(/^-+|-+$/g, '')
     .toLowerCase();
 }
 
@@ -81,8 +81,8 @@ export function compareCaseLessSensitive(a, b) {
   const bl = b.toLowerCase();
 
   return al === bl
-    ? a.localeCompare(b, undefined, { numeric: true })
-    : al.localeCompare(bl, undefined, { numeric: true });
+    ? a.localeCompare(b, undefined, {numeric: true})
+    : al.localeCompare(bl, undefined, {numeric: true});
 }
 
 // Subtract common prefixes and other characters which some people don't like
@@ -92,22 +92,22 @@ export function normalizeName(s) {
   // Turn (some) ligatures into expanded variant for cleaner sorting, e.g.
   // "ff" into "ff", in decompose mode, so that "ü" is represented as two
   // bytes ("u" + \u0308 combining diaeresis).
-  s = s.normalize("NFKD");
+  s = s.normalize('NFKD');
 
   // Replace one or more whitespace of any kind in a row, as well as certain
   // punctuation, with a single typical space, then trim the ends.
   s = s
     .replace(
       /[\p{Separator}\p{Dash_Punctuation}\p{Connector_Punctuation}]+/gu,
-      " "
+      ' '
     )
     .trim();
 
   // Discard anything that isn't a letter, number, or space.
-  s = s.replace(/[^\p{Letter}\p{Number} ]/gu, "");
+  s = s.replace(/[^\p{Letter}\p{Number} ]/gu, '');
 
   // Remove common English (only, for now) prefixes.
-  s = s.replace(/^(?:an?|the) /i, "");
+  s = s.replace(/^(?:an?|the) /i, '');
 
   return s;
 }
@@ -142,7 +142,7 @@ export function normalizeName(s) {
 // except when album and track directories overlap with each other.
 export function sortByDirectory(
   data,
-  { getDirectory = (o) => o.directory } = {}
+  {getDirectory = (o) => o.directory} = {}
 ) {
   return data.sort((a, b) => {
     const ad = getDirectory(a);
@@ -151,7 +151,7 @@ export function sortByDirectory(
   });
 }
 
-export function sortByName(data, { getName = (o) => o.name } = {}) {
+export function sortByName(data, {getName = (o) => o.name} = {}) {
   return data.sort((a, b) => {
     const an = getName(a);
     const bn = getName(b);
@@ -163,7 +163,7 @@ export function sortByName(data, { getName = (o) => o.name } = {}) {
   });
 }
 
-export function sortByDate(data, { getDate = (o) => o.date } = {}) {
+export function sortByDate(data, {getDate = (o) => o.date} = {}) {
   return data.sort((a, b) => {
     const ad = getDate(a);
     const bd = getDate(b);
@@ -254,9 +254,9 @@ export function sortByConditions(data, conditions) {
 // Expects thing properties:
 //  * directory (or override getDirectory)
 //  * name (or override getName)
-export function sortAlphabetically(data, { getDirectory, getName } = {}) {
-  sortByDirectory(data, { getDirectory });
-  sortByName(data, { getName });
+export function sortAlphabetically(data, {getDirectory, getName} = {}) {
+  sortByDirectory(data, {getDirectory});
+  sortByName(data, {getName});
   return data;
 }
 
@@ -266,10 +266,10 @@ export function sortAlphabetically(data, { getDirectory, getName } = {}) {
 //  * date (or override getDate)
 export function sortChronologically(
   data,
-  { getDirectory, getName, getDate } = {}
+  {getDirectory, getName, getDate} = {}
 ) {
-  sortAlphabetically(data, { getDirectory, getName });
-  sortByDate(data, { getDate });
+  sortAlphabetically(data, {getDirectory, getName});
+  sortByDate(data, {getDate});
   return data;
 }
 
@@ -281,7 +281,7 @@ export function sortChronologically(
 // release date but can be overridden) above all else.
 //
 // This function also works for data lists which contain only tracks.
-export function sortAlbumsTracksChronologically(data, { getDate } = {}) {
+export function sortAlbumsTracksChronologically(data, {getDate} = {}) {
   // Sort albums before tracks...
   sortByConditions(data, [(t) => t.album === undefined]);
 
@@ -297,7 +297,7 @@ export function sortAlbumsTracksChronologically(data, { getDate } = {}) {
   // released on the same date, they'll still be grouped together by album,
   // and tracks within an album will retain their relative positioning (i.e.
   // stay in the same order as part of the album's track listing).
-  sortByDate(data, { getDate });
+  sortByDate(data, {getDate});
 
   return data;
 }
@@ -310,17 +310,17 @@ export function filterAlbumsByCommentary(albums) {
   );
 }
 
-export function getAlbumCover(album, { to }) {
+export function getAlbumCover(album, {to}) {
   // Some albums don't have art! This function returns null in that case.
   if (album.hasCoverArt) {
-    return to("media.albumCover", album.directory, album.coverArtFileExtension);
+    return to('media.albumCover', album.directory, album.coverArtFileExtension);
   } else {
     return null;
   }
 }
 
 export function getAlbumListTag(album) {
-  return album.hasTrackNumbers ? "ol" : "ul";
+  return album.hasTrackNumbers ? 'ol' : 'ul';
 }
 
 // This gets all the track o8jects defined in every al8um, and sorts them 8y
@@ -352,8 +352,8 @@ export function getArtistNumContributions(artist) {
   );
 }
 
-export function getFlashCover(flash, { to }) {
-  return to("media.flashArt", flash.directory, flash.coverArtFileExtension);
+export function getFlashCover(flash, {to}) {
+  return to('media.flashArt', flash.directory, flash.coverArtFileExtension);
 }
 
 export function getFlashLink(flash) {
@@ -364,16 +364,16 @@ export function getTotalDuration(tracks) {
   return tracks.reduce((duration, track) => duration + track.duration, 0);
 }
 
-export function getTrackCover(track, { to }) {
+export function getTrackCover(track, {to}) {
   // Some albums don't have any track art at all, and in those, every track
   // just inherits the album's own cover art. Note that since cover art isn't
   // guaranteed on albums either, it's possible that this function returns
   // null!
   if (!track.hasCoverArt) {
-    return getAlbumCover(track.album, { to });
+    return getAlbumCover(track.album, {to});
   } else {
     return to(
-      "media.trackCover",
+      'media.trackCover',
       track.album.directory,
       track.directory,
       track.coverArtFileExtension
@@ -381,14 +381,14 @@ export function getTrackCover(track, { to }) {
   }
 }
 
-export function getArtistAvatar(artist, { to }) {
-  return to("media.artistAvatar", artist.directory, artist.avatarFileExtension);
+export function getArtistAvatar(artist, {to}) {
+  return to('media.artistAvatar', artist.directory, artist.avatarFileExtension);
 }
 
 // Big-ass homepage row functions
 
-export function getNewAdditions(numAlbums, { wikiData }) {
-  const { albumData } = wikiData;
+export function getNewAdditions(numAlbums, {wikiData}) {
+  const {albumData} = wikiData;
 
   // Sort al8ums, in descending order of priority, 8y...
   //
@@ -486,11 +486,11 @@ export function getNewAdditions(numAlbums, { wikiData }) {
   // Finally, do some quick mapping shenanigans to 8etter display the result
   // in a grid. (This should pro8a8ly 8e a separ8te, shared function, 8ut
   // whatevs.)
-  return albums.map((album) => ({ large: album.isMajorRelease, item: album }));
+  return albums.map((album) => ({large: album.isMajorRelease, item: album}));
 }
 
-export function getNewReleases(numReleases, { wikiData }) {
-  const { albumData } = wikiData;
+export function getNewReleases(numReleases, {wikiData}) {
+  const {albumData} = wikiData;
 
   const latestFirst = albumData
     .filter((album) => album.isListedOnHomepage)
@@ -503,7 +503,7 @@ export function getNewReleases(numReleases, { wikiData }) {
     .slice(0, numReleases - majorReleases.length);
 
   return [
-    ...majorReleases.map((album) => ({ large: true, item: album })),
-    ...otherReleases.map((album) => ({ large: false, item: album })),
+    ...majorReleases.map((album) => ({large: true, item: album})),
+    ...otherReleases.map((album) => ({large: false, item: album})),
   ];
 }