« get me outta code hell

Merge branch 'staging' into preview - 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>2023-02-05 09:36:50 -0400
committer(quasar) nebula <qznebula@protonmail.com>2023-02-05 09:36:50 -0400
commitf36f93b702729f14021746d56b192b25ac3ed1b7 (patch)
tree5bd2de31bf42627e76feff3823b6d1ff809c8a49
parentdb93dfae3aa8a627451ee643e2c02291e4f5dead (diff)
parentf71136f0a03d7f3c36651918af2dc18527dd47be (diff)
Merge branch 'staging' into preview
-rw-r--r--src/data/yaml.js62
-rw-r--r--src/page/album.js8
-rw-r--r--src/page/group.js25
-rw-r--r--src/util/io.js4
4 files changed, 72 insertions, 27 deletions
diff --git a/src/data/yaml.js b/src/data/yaml.js
index f269d447..9c3a4b88 100644
--- a/src/data/yaml.js
+++ b/src/data/yaml.js
@@ -4,7 +4,7 @@
 import * as path from 'path';
 import yaml from 'js-yaml';
 
-import {readFile} from 'fs/promises';
+import {readFile, stat} from 'fs/promises';
 import {inspect as nodeInspect} from 'util';
 
 import T from './things/index.js';
@@ -241,7 +241,7 @@ export const processTrackSectionDocument = makeProcessDocument(T.TrackSectionHel
 
 export const processTrackDocument = makeProcessDocument(T.Track, {
   fieldTransformations: {
-    'Duration': getDurationInSeconds,
+    'Duration': parseDuration,
 
     'Date First Released': (value) => new Date(value),
     'Cover Art Date': (value) => new Date(value),
@@ -447,13 +447,9 @@ export function processHomepageLayoutRowDocument(document) {
 
 // --> Utilities shared across document parsing functions
 
-export function getDurationInSeconds(string) {
-  if (typeof string === 'number') {
-    return string;
-  }
-
+export function parseDuration(string) {
   if (typeof string !== 'string') {
-    throw new TypeError(`Expected a string or number, got ${string}`);
+    return string;
   }
 
   const parts = string.split(':').map((n) => parseInt(n));
@@ -467,7 +463,6 @@ export function getDurationInSeconds(string) {
 }
 
 export function parseAdditionalFiles(array) {
-  if (!array) return null;
   if (!Array.isArray(array)) {
     // Error will be caught when validating against whatever this value is
     return array;
@@ -493,8 +488,11 @@ export function parseCommentary(text) {
 }
 
 export function parseContributors(contributors) {
-  if (!contributors) {
-    return null;
+  // If this isn't something we can parse, just return it as-is.
+  // The Thing object's validators will handle the data error better
+  // than we're able to here.
+  if (!Array.isArray(contributors)) {
+    return contributors;
   }
 
   if (contributors.length === 1 && contributors[0].startsWith('<i>')) {
@@ -528,8 +526,11 @@ export function parseContributors(contributors) {
 }
 
 function parseDimensions(string) {
-  if (!string) {
-    return null;
+  // It's technically possible to pass an array like [30, 40] through here.
+  // That's not really an issue because if it isn't of the appropriate shape,
+  // the Thing object's validators will handle the error.
+  if (typeof string !== 'string') {
+    return string;
   }
 
   const parts = string.split(/[x,* ]+/g);
@@ -940,6 +941,30 @@ export async function loadAndProcessDataDocuments({dataPath}) {
               ? await callAsync(dataStep.file, dataPath)
               : dataStep.file);
 
+          const statResult = await callAsync(() =>
+            stat(file).then(
+              () => true,
+              error => {
+                if (error.code === 'ENOENT') {
+                  return false;
+                } else {
+                  throw error;
+                }
+              }));
+
+          if (statResult === false) {
+            const saveResult = call(dataStep.save, {
+              [documentModes.allInOne]: [],
+              [documentModes.oneDocumentTotal]: {},
+            }[documentMode]);
+
+            if (!saveResult) return;
+
+            Object.assign(wikiDataResult, saveResult);
+
+            return;
+          }
+
           const readResult = await callAsync(readFile, file, 'utf-8');
 
           if (!readResult) {
@@ -987,7 +1012,16 @@ export async function loadAndProcessDataDocuments({dataPath}) {
 
         let files = (
           typeof dataStep.files === 'function'
-            ? await callAsync(dataStep.files, dataPath)
+            ? await callAsync(() =>
+                dataStep.files(dataPath).then(
+                  files => files,
+                  error => {
+                    if (error.code === 'ENOENT') {
+                      return [];
+                    } else {
+                      throw error;
+                    }
+                  }))
             : dataStep.files
         );
 
diff --git a/src/page/album.js b/src/page/album.js
index 7a7f35bc..897e5110 100644
--- a/src/page/album.js
+++ b/src/page/album.js
@@ -55,7 +55,7 @@ export function write(album, {wikiData}) {
   const displayTrackSections =
     album.trackSections &&
       (album.trackSections.length > 1 ||
-        !album.trackSections[0].isDefaultTrackSection);
+        !album.trackSections[0]?.isDefaultTrackSection);
 
   const listTag = getAlbumListTag(album);
 
@@ -294,6 +294,7 @@ export function write(album, {wikiData}) {
                 })),
 
             displayTrackSections &&
+            !empty(album.trackSections) &&
               html.tag('dl',
                 {class: 'album-group-list'},
                 album.trackSections.flatMap(({
@@ -316,6 +317,7 @@ export function write(album, {wikiData}) {
                 ])),
 
             !displayTrackSections &&
+            !empty(album.tracks) &&
               html.tag(listTag,
                 album.tracks.map(trackToListItem)),
 
@@ -749,6 +751,10 @@ export function generateAlbumNavLinks(album, currentTrack, {
     randomLink,
   ].filter(Boolean);
 
+  if (empty(allLinks)) {
+    return '';
+  }
+
   return `(${language.formatUnitList(allLinks)})`;
 }
 
diff --git a/src/page/group.js b/src/page/group.js
index 37ebb789..9a48c1d8 100644
--- a/src/page/group.js
+++ b/src/page/group.js
@@ -18,8 +18,7 @@ export function targets({wikiData}) {
 export function write(group, {wikiData}) {
   const {listingSpec, wikiInfo} = wikiData;
 
-  const {albums} = group;
-  const tracks = albums.flatMap((album) => album.tracks);
+  const tracks = group.albums.flatMap((album) => album.tracks);
   const totalDuration = getTotalDuration(tracks, {originalReleasesOnly: true});
 
   const albumLines = group.albums.map((album) => ({
@@ -62,7 +61,7 @@ export function write(group, {wikiData}) {
               transformMultiline(group.description)),
 
           ...html.fragment(
-            group.albums && [
+            !empty(group.albums) && [
               html.tag('h2',
                 {class: ['content-heading']},
                 language.$('groupInfoPage.albumList.title')),
@@ -120,7 +119,7 @@ export function write(group, {wikiData}) {
     }),
   };
 
-  const galleryPage = {
+  const galleryPage = !empty(group.albums) && {
     type: 'page',
     path: ['groupGallery', group.directory],
     page: ({
@@ -159,7 +158,7 @@ export function write(group, {wikiData}) {
                   unit: true,
                 })),
               albums: html.tag('b',
-                language.countAlbums(albums.length, {
+                language.countAlbums(group.albums.length, {
                   unit: true,
                 })),
               time: html.tag('b',
@@ -216,7 +215,7 @@ export function write(group, {wikiData}) {
     }),
   };
 
-  return [infoPage, galleryPage];
+  return [infoPage, galleryPage].filter(Boolean);
 }
 
 // Utility functions
@@ -234,8 +233,6 @@ function generateGroupSidebar(currentGroup, isGallery, {
     return null;
   }
 
-  const linkKey = isGallery ? 'groupGallery' : 'groupInfo';
-
   return {
     content: [
       html.tag('h1',
@@ -254,15 +251,21 @@ function generateGroupSidebar(currentGroup, isGallery, {
                 category: `<span class="group-name">${category.name}</span>`,
               })),
             html.tag('ul',
-              category.groups.map((group) =>
-                html.tag('li',
+              category.groups.map((group) => {
+                const linkKey = (
+                  isGallery && !empty(group.albums)
+                    ? 'groupGallery'
+                    : 'groupInfo');
+
+                return html.tag('li',
                   {
                     class: group === currentGroup && 'current',
                     style: getLinkThemeString(group.color),
                   },
                   language.$('groupSidebar.groupList.item', {
                     group: link[linkKey](group),
-                  })))),
+                  }));
+              })),
           ])),
     ],
   };
diff --git a/src/util/io.js b/src/util/io.js
index 6cc89b56..12e87f4d 100644
--- a/src/util/io.js
+++ b/src/util/io.js
@@ -12,7 +12,9 @@ export async function findFiles(dataPath, {
   try {
     files = await readdir(dataPath);
   } catch (error) {
-    throw new AggregateError([error], `Failed to list files from ${dataPath}`);
+    throw Object.assign(
+      new AggregateError([error], `Failed to list files from ${dataPath}`),
+      {code: error.code});
   }
 
   return files