« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/util/wiki-data.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/wiki-data.js')
-rw-r--r--src/util/wiki-data.js638
1 files changed, 329 insertions, 309 deletions
diff --git a/src/util/wiki-data.js b/src/util/wiki-data.js
index 5aef812..f7610fd 100644
--- a/src/util/wiki-data.js
+++ b/src/util/wiki-data.js
@@ -3,63 +3,64 @@
 // 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, '')
-        .toLowerCase();
+  return name
+    .split(" ")
+    .join("-")
+    .replace(/&/g, "and")
+    .replace(/[^a-zA-Z0-9\-]/g, "")
+    .replace(/-{2,}/g, "-")
+    .replace(/^-+|-+$/g, "")
+    .toLowerCase();
 }
 
 export function chunkByConditions(array, conditions) {
-    if (array.length === 0) {
-        return [];
-    } else if (conditions.length === 0) {
-        return [array];
+  if (array.length === 0) {
+    return [];
+  } else if (conditions.length === 0) {
+    return [array];
+  }
+
+  const out = [];
+  let cur = [array[0]];
+  for (let i = 1; i < array.length; i++) {
+    const item = array[i];
+    const prev = array[i - 1];
+    let chunk = false;
+    for (const condition of conditions) {
+      if (condition(item, prev)) {
+        chunk = true;
+        break;
+      }
     }
-
-    const out = [];
-    let cur = [array[0]];
-    for (let i = 1; i < array.length; i++) {
-        const item = array[i];
-        const prev = array[i - 1];
-        let chunk = false;
-        for (const condition of conditions) {
-            if (condition(item, prev)) {
-                chunk = true;
-                break;
-            }
-        }
-        if (chunk) {
-            out.push(cur);
-            cur = [item];
-        } else {
-            cur.push(item);
-        }
+    if (chunk) {
+      out.push(cur);
+      cur = [item];
+    } else {
+      cur.push(item);
     }
-    out.push(cur);
-    return out;
+  }
+  out.push(cur);
+  return out;
 }
 
 export function chunkByProperties(array, properties) {
-    return chunkByConditions(array, properties.map(p => (a, b) => {
-        if (a[p] instanceof Date && b[p] instanceof Date)
-            return +a[p] !== +b[p];
-
-        if (a[p] !== b[p]) return true;
-
-        // Not sure if this line is still necessary with the specific check for
-        // d8tes a8ove, 8ut, uh, keeping it anyway, just in case....?
-        if (a[p] != b[p]) return true;
-
-        return false;
-    }))
-        .map(chunk => ({
-            ...Object.fromEntries(properties.map(p => [p, chunk[0][p]])),
-            chunk
-        }));
+  return chunkByConditions(
+    array,
+    properties.map((p) => (a, b) => {
+      if (a[p] instanceof Date && b[p] instanceof Date) return +a[p] !== +b[p];
+
+      if (a[p] !== b[p]) return true;
+
+      // Not sure if this line is still necessary with the specific check for
+      // d8tes a8ove, 8ut, uh, keeping it anyway, just in case....?
+      if (a[p] != b[p]) return true;
+
+      return false;
+    })
+  ).map((chunk) => ({
+    ...Object.fromEntries(properties.map((p) => [p, chunk[0][p]])),
+    chunk,
+  }));
 }
 
 // Sorting functions - all utils here are mutating, so make sure to initially
@@ -71,37 +72,42 @@ export function chunkByProperties(array, properties) {
 // handy in the sorting functions below (or if you're making your own sort).
 
 export function compareCaseLessSensitive(a, b) {
-    // Compare two strings without considering capitalization... unless they
-    // happen to be the same that way.
+  // Compare two strings without considering capitalization... unless they
+  // happen to be the same that way.
 
-    const al = a.toLowerCase();
-    const bl = b.toLowerCase();
+  const al = a.toLowerCase();
+  const bl = b.toLowerCase();
 
-    return (al === bl
-        ? a.localeCompare(b, undefined, {numeric: true})
-        : al.localeCompare(bl, undefined, {numeric: true}));
+  return al === bl
+    ? a.localeCompare(b, undefined, { numeric: true })
+    : al.localeCompare(bl, undefined, { numeric: true });
 }
 
 // Subtract common prefixes and other characters which some people don't like
 // to have considered while sorting. The words part of this is English-only for
 // now, which is totally evil.
 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');
-
-    // 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, '');
-
-    // Remove common English (only, for now) prefixes.
-    s = s.replace(/^(?:an?|the) /i, '');
-
-    return 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");
+
+  // 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, "");
+
+  // Remove common English (only, for now) prefixes.
+  s = s.replace(/^(?:an?|the) /i, "");
+
+  return s;
 }
 
 // Component sort functions - these sort by one particular property, applying
@@ -132,106 +138,103 @@ export function normalizeName(s) {
 // ...trackData]), because the initial sort places albums before tracks - and
 // sortByDirectory will handle the rest, given all directories are unique
 // except when album and track directories overlap with each other.
-export function sortByDirectory(data, {
-    getDirectory = o => o.directory
-} = {}) {
-    return data.sort((a, b) => {
-        const ad = getDirectory(a);
-        const bd = getDirectory(b);
-        return compareCaseLessSensitive(ad, bd)
-    });
+export function sortByDirectory(
+  data,
+  { getDirectory = (o) => o.directory } = {}
+) {
+  return data.sort((a, b) => {
+    const ad = getDirectory(a);
+    const bd = getDirectory(b);
+    return compareCaseLessSensitive(ad, bd);
+  });
 }
 
-export function sortByName(data, {
-    getName = o => o.name
-} = {}) {
-    return data.sort((a, b) => {
-        const an = getName(a);
-        const bn = getName(b);
-        const ann = normalizeName(an);
-        const bnn = normalizeName(bn);
-        return (
-            compareCaseLessSensitive(ann, bnn) ||
-            compareCaseLessSensitive(an, bn));
-    });
+export function sortByName(data, { getName = (o) => o.name } = {}) {
+  return data.sort((a, b) => {
+    const an = getName(a);
+    const bn = getName(b);
+    const ann = normalizeName(an);
+    const bnn = normalizeName(bn);
+    return (
+      compareCaseLessSensitive(ann, bnn) || compareCaseLessSensitive(an, bn)
+    );
+  });
 }
 
-export function sortByDate(data, {
-    getDate = o => o.date
-} = {}) {
-    return data.sort((a, b) => {
-        const ad = getDate(a);
-        const bd = getDate(b);
-
-        // It's possible for objects with and without dates to be mixed
-        // together in the same array. If that's the case, we put all items
-        // without dates at the end.
-        if (ad && bd) {
-            return ad - bd;
-        } else if (ad) {
-            return -1;
-        } else if (bd) {
-            return 1;
-        } else {
-            // If neither of the items being compared have a date, don't move
-            // them relative to each other. This is basically the same as
-            // filtering out all non-date items and then pushing them at the
-            // end after sorting the rest.
-            return 0;
-        }
-    });
+export function sortByDate(data, { getDate = (o) => o.date } = {}) {
+  return data.sort((a, b) => {
+    const ad = getDate(a);
+    const bd = getDate(b);
+
+    // It's possible for objects with and without dates to be mixed
+    // together in the same array. If that's the case, we put all items
+    // without dates at the end.
+    if (ad && bd) {
+      return ad - bd;
+    } else if (ad) {
+      return -1;
+    } else if (bd) {
+      return 1;
+    } else {
+      // If neither of the items being compared have a date, don't move
+      // them relative to each other. This is basically the same as
+      // filtering out all non-date items and then pushing them at the
+      // end after sorting the rest.
+      return 0;
+    }
+  });
 }
 
 export function sortByPositionInAlbum(data) {
-    return data.sort((a, b) => {
-        const aa = a.album;
-        const ba = b.album;
-
-        // Don't change the sort when the two tracks are from separate albums.
-        // This function doesn't change the order of albums or try to "merge"
-        // two separated chunks of tracks from the same album together.
-        if (aa !== ba) {
-            return 0;
-        }
+  return data.sort((a, b) => {
+    const aa = a.album;
+    const ba = b.album;
+
+    // Don't change the sort when the two tracks are from separate albums.
+    // This function doesn't change the order of albums or try to "merge"
+    // two separated chunks of tracks from the same album together.
+    if (aa !== ba) {
+      return 0;
+    }
 
-        // Don't change the sort when only one (or neither) item is actually
-        // a track (i.e. has an album).
-        if (!aa || !ba) {
-            return 0;
-        }
+    // Don't change the sort when only one (or neither) item is actually
+    // a track (i.e. has an album).
+    if (!aa || !ba) {
+      return 0;
+    }
 
-        const ai = aa.tracks.indexOf(a);
-        const bi = ba.tracks.indexOf(b);
+    const ai = aa.tracks.indexOf(a);
+    const bi = ba.tracks.indexOf(b);
 
-        // There's no reason this two-way reference (a track's album and the
-        // album's track list) should be broken, but if for any reason it is,
-        // don't change the sort.
-        if (ai === -1 || bi === -1) {
-            return 0;
-        }
+    // There's no reason this two-way reference (a track's album and the
+    // album's track list) should be broken, but if for any reason it is,
+    // don't change the sort.
+    if (ai === -1 || bi === -1) {
+      return 0;
+    }
 
-        return ai - bi;
-    });
+    return ai - bi;
+  });
 }
 
 // Sorts data so that items are grouped together according to whichever of a
 // set of arbitrary given conditions is true first. If no conditions are met
 // for a given item, it's moved over to the end!
 export function sortByConditions(data, conditions) {
-    data.sort((a, b) => {
-        const ai = conditions.findIndex(f => f(a));
-        const bi = conditions.findIndex(f => f(b));
-
-        if (ai >= 0 && bi >= 0) {
-            return ai - bi;
-        } else if (ai >= 0) {
-            return -1;
-        } else if (bi >= 0) {
-            return 1;
-        } else {
-            return 0;
-        }
-    });
+  data.sort((a, b) => {
+    const ai = conditions.findIndex((f) => f(a));
+    const bi = conditions.findIndex((f) => f(b));
+
+    if (ai >= 0 && bi >= 0) {
+      return ai - bi;
+    } else if (ai >= 0) {
+      return -1;
+    } else if (bi >= 0) {
+      return 1;
+    } else {
+      return 0;
+    }
+  });
 }
 
 // Composite sorting functions - these consider multiple properties, generally
@@ -249,20 +252,23 @@ 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});
-    return data;
+export function sortAlphabetically(data, { getDirectory, getName } = {}) {
+  sortByDirectory(data, { getDirectory });
+  sortByName(data, { getName });
+  return data;
 }
 
 // Expects thing properties:
 //  * directory (or override getDirectory)
 //  * name (or override getName)
 //  * date (or override getDate)
-export function sortChronologically(data, {getDirectory, getName, getDate} = {}) {
-    sortAlphabetically(data, {getDirectory, getName});
-    sortByDate(data, {getDate});
-    return data;
+export function sortChronologically(
+  data,
+  { getDirectory, getName, getDate } = {}
+) {
+  sortAlphabetically(data, { getDirectory, getName });
+  sortByDate(data, { getDate });
+  return data;
 }
 
 // Highly contextual sort functions - these are only for very specific types
@@ -273,44 +279,46 @@ export function sortChronologically(data, {getDirectory, getName, getDate} = {})
 // 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} = {}) {
-    // Sort albums before tracks...
-    sortByConditions(data, [t => t.album === undefined]);
+export function sortAlbumsTracksChronologically(data, { getDate } = {}) {
+  // Sort albums before tracks...
+  sortByConditions(data, [(t) => t.album === undefined]);
 
-    // Group tracks by album...
-    sortByDirectory(data, {
-        getDirectory: t => (t.album ? t.album.directory : t.directory)
-    });
+  // Group tracks by album...
+  sortByDirectory(data, {
+    getDirectory: (t) => (t.album ? t.album.directory : t.directory),
+  });
 
-    // Sort tracks by position in album...
-    sortByPositionInAlbum(data);
+  // Sort tracks by position in album...
+  sortByPositionInAlbum(data);
 
-    // ...and finally sort by date. If tracks from more than one album were
-    // 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});
+  // ...and finally sort by date. If tracks from more than one album were
+  // 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 });
 
-    return data;
+  return data;
 }
 
 // Specific data utilities
 
 export function filterAlbumsByCommentary(albums) {
-    return albums.filter(album => [album, ...album.tracks].some(x => x.commentary));
+  return albums.filter((album) =>
+    [album, ...album.tracks].some((x) => x.commentary)
+  );
 }
 
-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);
-    } else {
-        return null;
-    }
+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);
+  } 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
@@ -331,157 +339,169 @@ export function getAlbumListTag(album) {
 // d8s, 8ut still keep the al8um listing in a specific order, since that isn't
 // sorted 8y date.
 export function getAllTracks(albumData) {
-    return sortByDate(albumData.flatMap(album => album.tracks));
+  return sortByDate(albumData.flatMap((album) => album.tracks));
 }
 
 export function getArtistNumContributions(artist) {
-    return (
-        (artist.tracksAsAny?.length ?? 0) +
-        (artist.albumsAsCoverArtist?.length ?? 0) +
-        (artist.flashesAsContributor?.length ?? 0)
-    );
+  return (
+    (artist.tracksAsAny?.length ?? 0) +
+    (artist.albumsAsCoverArtist?.length ?? 0) +
+    (artist.flashesAsContributor?.length ?? 0)
+  );
 }
 
-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) {
-    return `https://homestuck.com/story/${flash.page}`;
+  return `https://homestuck.com/story/${flash.page}`;
 }
 
 export function getTotalDuration(tracks) {
-    return tracks.reduce((duration, track) => duration + track.duration, 0);
+  return tracks.reduce((duration, track) => duration + track.duration, 0);
 }
 
-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});
-    } else {
-        return to('media.trackCover', track.album.directory, track.directory, track.coverArtFileExtension);
-    }
+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 });
+  } else {
+    return to(
+      "media.trackCover",
+      track.album.directory,
+      track.directory,
+      track.coverArtFileExtension
+    );
+  }
 }
 
-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;
-
-    // Sort al8ums, in descending order of priority, 8y...
-    //
-    // * D8te of addition to the wiki (descending).
-    // * Major releases first.
-    // * D8te of release (descending).
-    //
-    // Major releases go first to 8etter ensure they show up in the list (and
-    // are usually at the start of the final output for a given d8 of release
-    // too).
-    const sortedAlbums = albumData.filter(album => album.isListedOnHomepage).sort((a, b) => {
-        if (a.dateAddedToWiki > b.dateAddedToWiki) return -1;
-        if (a.dateAddedToWiki < b.dateAddedToWiki) return 1;
-        if (a.isMajorRelease && !b.isMajorRelease) return -1;
-        if (!a.isMajorRelease && b.isMajorRelease) return 1;
-        if (a.date > b.date) return -1;
-        if (a.date < b.date) return 1;
+export function getNewAdditions(numAlbums, { wikiData }) {
+  const { albumData } = wikiData;
+
+  // Sort al8ums, in descending order of priority, 8y...
+  //
+  // * D8te of addition to the wiki (descending).
+  // * Major releases first.
+  // * D8te of release (descending).
+  //
+  // Major releases go first to 8etter ensure they show up in the list (and
+  // are usually at the start of the final output for a given d8 of release
+  // too).
+  const sortedAlbums = albumData
+    .filter((album) => album.isListedOnHomepage)
+    .sort((a, b) => {
+      if (a.dateAddedToWiki > b.dateAddedToWiki) return -1;
+      if (a.dateAddedToWiki < b.dateAddedToWiki) return 1;
+      if (a.isMajorRelease && !b.isMajorRelease) return -1;
+      if (!a.isMajorRelease && b.isMajorRelease) return 1;
+      if (a.date > b.date) return -1;
+      if (a.date < b.date) return 1;
     });
 
-    // When multiple al8ums are added to the wiki at a time, we want to show
-    // all of them 8efore pulling al8ums from the next (earlier) date. We also
-    // want to show a diverse selection of al8ums - with limited space, we'd
-    // rather not show only the latest al8ums, if those happen to all 8e
-    // closely rel8ted!
-    //
-    // Specifically, we're concerned with avoiding too much overlap amongst
-    // the primary (first/top-most) group. We do this 8y collecting every
-    // primary group present amongst the al8ums for a given d8 into one
-    // (ordered) array, initially sorted (inherently) 8y latest al8um from
-    // the group. Then we cycle over the array, adding one al8um from each
-    // group until all the al8ums from that release d8 have 8een added (or
-    // we've met the total target num8er of al8ums). Once we've added all the
-    // al8ums for a given group, it's struck from the array (so the groups
-    // with the most additions on one d8 will have their oldest releases
-    // collected more towards the end of the list).
-
-    const albums = [];
-
-    let i = 0;
-    outerLoop: while (i < sortedAlbums.length) {
-        // 8uild up a list of groups and their al8ums 8y order of decending
-        // release, iter8ting until we're on a different d8. (We use a map for
-        // indexing so we don't have to iter8te through the entire array each
-        // time we access one of its entries. This is 8asically unnecessary
-        // since this will never 8e an expensive enough task for that to
-        // matter.... 8ut it's nicer code. BBBB) )
-        const currentDate = sortedAlbums[i].dateAddedToWiki;
-        const groupMap = new Map();
-        const groupArray = [];
-        for (let album; (album = sortedAlbums[i]) && +album.dateAddedToWiki === +currentDate; i++) {
-            const primaryGroup = album.groups[0];
-            if (groupMap.has(primaryGroup)) {
-                groupMap.get(primaryGroup).push(album);
-            } else {
-                const entry = [album]
-                groupMap.set(primaryGroup, entry);
-                groupArray.push(entry);
-            }
+  // When multiple al8ums are added to the wiki at a time, we want to show
+  // all of them 8efore pulling al8ums from the next (earlier) date. We also
+  // want to show a diverse selection of al8ums - with limited space, we'd
+  // rather not show only the latest al8ums, if those happen to all 8e
+  // closely rel8ted!
+  //
+  // Specifically, we're concerned with avoiding too much overlap amongst
+  // the primary (first/top-most) group. We do this 8y collecting every
+  // primary group present amongst the al8ums for a given d8 into one
+  // (ordered) array, initially sorted (inherently) 8y latest al8um from
+  // the group. Then we cycle over the array, adding one al8um from each
+  // group until all the al8ums from that release d8 have 8een added (or
+  // we've met the total target num8er of al8ums). Once we've added all the
+  // al8ums for a given group, it's struck from the array (so the groups
+  // with the most additions on one d8 will have their oldest releases
+  // collected more towards the end of the list).
+
+  const albums = [];
+
+  let i = 0;
+  outerLoop: while (i < sortedAlbums.length) {
+    // 8uild up a list of groups and their al8ums 8y order of decending
+    // release, iter8ting until we're on a different d8. (We use a map for
+    // indexing so we don't have to iter8te through the entire array each
+    // time we access one of its entries. This is 8asically unnecessary
+    // since this will never 8e an expensive enough task for that to
+    // matter.... 8ut it's nicer code. BBBB) )
+    const currentDate = sortedAlbums[i].dateAddedToWiki;
+    const groupMap = new Map();
+    const groupArray = [];
+    for (
+      let album;
+      (album = sortedAlbums[i]) && +album.dateAddedToWiki === +currentDate;
+      i++
+    ) {
+      const primaryGroup = album.groups[0];
+      if (groupMap.has(primaryGroup)) {
+        groupMap.get(primaryGroup).push(album);
+      } else {
+        const entry = [album];
+        groupMap.set(primaryGroup, entry);
+        groupArray.push(entry);
+      }
+    }
+
+    // Then cycle over that sorted array, adding one al8um from each to
+    // the main array until we've run out or have met the target num8er
+    // of al8ums.
+    while (groupArray.length) {
+      let j = 0;
+      while (j < groupArray.length) {
+        const entry = groupArray[j];
+        const album = entry.shift();
+        albums.push(album);
+
+        // This is the only time we ever add anything to the main al8um
+        // list, so it's also the only place we need to check if we've
+        // met the target length.
+        if (albums.length === numAlbums) {
+          // If we've met it, 8r8k out of the outer loop - we're done
+          // here!
+          break outerLoop;
         }
 
-        // Then cycle over that sorted array, adding one al8um from each to
-        // the main array until we've run out or have met the target num8er
-        // of al8ums.
-        while (groupArray.length) {
-            let j = 0;
-            while (j < groupArray.length) {
-                const entry = groupArray[j];
-                const album = entry.shift();
-                albums.push(album);
-
-
-                // This is the only time we ever add anything to the main al8um
-                // list, so it's also the only place we need to check if we've
-                // met the target length.
-                if (albums.length === numAlbums) {
-                    // If we've met it, 8r8k out of the outer loop - we're done
-                    // here!
-                    break outerLoop;
-                }
-
-                if (entry.length) {
-                    j++;
-                } else {
-                    groupArray.splice(j, 1);
-                }
-            }
+        if (entry.length) {
+          j++;
+        } else {
+          groupArray.splice(j, 1);
         }
+      }
     }
+  }
 
-    // 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}));
+  // 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 }));
 }
 
-export function getNewReleases(numReleases, {wikiData}) {
-    const { albumData } = wikiData;
+export function getNewReleases(numReleases, { wikiData }) {
+  const { albumData } = wikiData;
 
-    const latestFirst = albumData.filter(album => album.isListedOnHomepage).reverse();
-    const majorReleases = latestFirst.filter(album => album.isMajorRelease);
-    majorReleases.splice(1);
+  const latestFirst = albumData
+    .filter((album) => album.isListedOnHomepage)
+    .reverse();
+  const majorReleases = latestFirst.filter((album) => album.isMajorRelease);
+  majorReleases.splice(1);
 
-    const otherReleases = latestFirst
-        .filter(album => !majorReleases.includes(album))
-        .slice(0, numReleases - majorReleases.length);
+  const otherReleases = latestFirst
+    .filter((album) => !majorReleases.includes(album))
+    .slice(0, numReleases - majorReleases.length);
 
-    return [
-        ...majorReleases.map(album => ({large: true, item: album})),
-        ...otherReleases.map(album => ({large: false, item: album}))
-    ];
+  return [
+    ...majorReleases.map((album) => ({ large: true, item: album })),
+    ...otherReleases.map((album) => ({ large: false, item: album })),
+  ];
 }