« get me outta code hell

Merge branch 'preview' into track-data-cleanup - 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-09-23 20:19:12 -0300
committer(quasar) nebula <qznebula@protonmail.com>2023-09-23 20:19:12 -0300
commit61a3fad9cde8fa3809ccf26465f69dd050e6c669 (patch)
tree5c5c90d452c5aa7c023174bddc1a08f3f0acd75f
parent64476074cbc7375afe2388ddd6e9e3275c25f3bd (diff)
parenta53d53e1f95d84b23cffb9a5d43f1a70a412b6fe (diff)
Merge branch 'preview' into track-data-cleanup
-rw-r--r--README.md30
-rw-r--r--src/content/dependencies/linkThing.js6
-rw-r--r--src/content/dependencies/listTracksWithExtra.js12
-rw-r--r--src/gen-thumbs.js8
-rw-r--r--src/page/artist-alias.js6
-rw-r--r--src/strings-default.json9
-rw-r--r--tap-snapshots/test/snapshot/linkThing.js.test.cjs39
-rw-r--r--test/snapshot/linkThing.js87
8 files changed, 171 insertions, 26 deletions
diff --git a/README.md b/README.md
index 62dd64d..a7fc582 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ $ git clone https://github.com/hsmusic/hsmusic-wiki code
 Cloning into 'code'...
 $ git clone https://github.com/hsmusic/hsmusic-data data
 Cloning into 'data'...
-$ git clone https://nebula.ed1.club/git/hsmusic-media media
+$ git clone https://github.com/hsmusic/hsmusic-media media
 Cloning into 'media'...
 ```
 
@@ -106,7 +106,7 @@ HSMusic works using a number of repositories in tandem:
   - This repo covers albums, tracks, artists, groups, and a variety of other things which make up the content of a music wiki.
   - The data repo also contains all the metadata which makes one wiki unique from another: layout info, static pages (like "About & Credits"), whether or not certain site features are enabled (like "Flashes & Games" or UI for browsing groups), and so on.
   - All data is written and accessed in the YAML format, and every file follows a specific structure described within this (code) repository. See below and the `src/data` directory for details.
-- [`hsmusic-media`][ed1-media]: The media repository, holding all album, track, and layout media used across the site in one place. Media and organization directly corresponds to entries in the data repository; generally the data and media repositories go together and are swapped out for another together.
+- [`hsmusic-media`][github-media]: The media repository, holding all album, track, and layout media used across the site in one place. Media and organization directly corresponds to entries in the data repository; generally the data and media repositories go together and are swapped out for another together.
 - *Language repo:* The language repository, holding up-to-date strings and other localization info for HSMusic. NB: This repo isn't currently online as its structure and tooling haven't been polished or properly put together yet, but it's not required for building the site.
   - Strings and language info are stored in top-level JSON files within this repository. They're based off the `src/strings-default.json` file within the code repo (and don't need to provide translations for all strings to be used for site building).
 
@@ -139,16 +139,18 @@ The source code for HSMusic is divided across a number of source files, loosely
 
 - `src/`
   - `data/`
-    - `cacheable-object.js`: Backbone of how data objects (colloquially "things") store, share, and compute their properties
-    - `things.js`: Descriptors for all "thing" classes used across the wiki: albums, tracks, artists, groups, etc
-    - `validators.js`: Convenient error-throwing utilities which help ensure properties set on things follow the right format
-    - `yaml.js`: Mappings from YAML documents (the format used in `hsmusic-data`) to things (actual data objects), and a full set of utilities used to actually load that data from scratch
-  - `page/`
-    - All page templates (HTML content and layout metadata) are kept in source files under this directory
-  - `static/`
-    - Purely client-side files are kept here, e.g. site CSS, icon SVGs, and client-side JS
-  - `util/`
-    - Common utilities which generally may be accessed from both Node.js or the client (web browser)
+    - `things/`: Descriptors for individual types of data objects used across the wiki, notably including:
+      - `cacheable-object.js`: Backbone of how data objects (colloquially "things") store, share, and compute their properties
+      - `thing.js`: Common superclass for most data objects, with a bunch of utilities and common behavior
+      - `validators.js`: Convenient error-throwing utilities which help ensure properties set on data objects follow the right format
+    - `yaml.js`: Mappings from YAML documents (the format used in `hsmusic-data`) to things (actual data objects), and a full suite of utilities used to actually load that data from scratch
+  - `content/`: Functions which generate HTML content; these go from bite-sized, commonly reused utilities (like `linkTemplate`) all the way up to entire page definitions (like `generateArtistInfoPage`)
+  - `page/`: Definitions for page paths, mapping data objects to paths served over an HTTP server (or written to an output folder) and to the functions which actually generate those pages' content
+  - `write/`: Common utilities and output methods for controlling what hsmusic does to turn data and media into something you actually visit; these each define a variety of command-line arguments and are basically the interchangeable  "second half" of upd8.js
+    - `live-dev-server.js`: Gets the site available for viewing as quickly as possible, generating and serving pages as they are requested from a local HTTP server; reacts live to code changes in the `content` directory
+    - `static-build.js`: Builds the entire site at once, writing all the output to one self-contained folder which can be uploaded to a static file server
+  - `static/`: Purely client-side files are kept here, e.g. site CSS, icon SVGs, and client-side JS
+  - `util/`: Common utilities which generally may be accessed from both Node.js or the client (web browser)
   - `upd8.js`: Main entry point which controls and directs site generation from start to finish
   - `gen-thumbs.js`: Standalone utility also called every time HSMusic is run (unless `--skip-thumbs` is provided) which keeps a persistent cache of media MD5s and (re)generates thumbnails for new or updated image files
   - `repl.js`: Standalone utility for loading all wiki data and providing a convenient REPL to run filters and transformations on data objects right from the Node.js command line
@@ -165,16 +167,16 @@ hsmusic is a relatively generic music wiki software, so you're more than encoura
 
 As mentioned, part of the focus of the hsmusic.wiki release, as well as most development since, has been to create a more modular and developer-friendly repository. So, on the curious chance anyone would like to contribute code to the repo, that's more possible now than it used to be!
 
-Still, for larger additions, we encourage you to [drop the main dev an email][feedback] or, better yet, [pop by the Discord][discord] before writing all the implementation code: besides code tips which might make your life a bit easier (questions are welcome), we also love to discuss feature designs and values while they're still being brainstormed! That way, nobody has to tell you there are fundamental ideas or implementation details that should be rebuilt from the ground up - the last thing we want is anyone putting hours into code that has to be replaced by another implementation before it ever ends up part of the wiki!
+Still, for larger additions, we encourage you to [drop the main devs an email][feedback] or, better yet, [pop by the Discord][discord] before writing all the implementation code: besides code tips which might make your life a bit easier (questions are welcome), we also love to discuss feature designs and values while they're still being brainstormed! That way, nobody has to tell you there are fundamental ideas or implementation details that should be rebuilt from the ground up - the last thing we want is anyone putting hours into code that has to be replaced by another implementation before it ever ends up part of the wiki!
 
 As ever, feedback is always welcome, and may be shared via the usual links. Thank you for checking the repository out!
 
-  [ed1-media]: https://nebula.ed1.club/git/hsmusic-media/
   [discord]: https://hsmusic.wiki/discord/
   [fandom]: https://homestuck-and-mspa-music.fandom.com/wiki/Homestuck_and_MSPA_Music_Wiki
   [feedback]: https://hsmusic.wiki/feedback/
   [github]: https://github.com/hsmusic/hsmusic-wiki
   [github-code]: https://github.com/hsmusic/hsmusic-wiki
   [github-data]: https://github.com/hsmusic/hsmusic-data
+  [github-media]: https://github.com/hsmusic/hsmusic-media
   [hsmusic]: https://hsmusic.wiki
   [nsnd]: https://homestuck.net/music/references.html
diff --git a/src/content/dependencies/linkThing.js b/src/content/dependencies/linkThing.js
index 643bf4b..e661ca7 100644
--- a/src/content/dependencies/linkThing.js
+++ b/src/content/dependencies/linkThing.js
@@ -1,6 +1,6 @@
 export default {
   contentDependencies: ['linkTemplate'],
-  extraDependencies: ['html'],
+  extraDependencies: ['html', 'language'],
 
   relations(relation) {
     return {
@@ -41,7 +41,7 @@ export default {
     hash: {type: 'string'},
   },
 
-  generate(data, relations, slots, {html}) {
+  generate(data, relations, slots, {html, language}) {
     const path = [data.pathKey, data.directory];
 
     const name =
@@ -51,7 +51,7 @@ export default {
 
     const content =
       (html.isBlank(slots.content)
-        ? name
+        ? language.sanitize(name)
         : slots.content);
 
     let color = null;
diff --git a/src/content/dependencies/listTracksWithExtra.js b/src/content/dependencies/listTracksWithExtra.js
index 73d25e3..c9f80f3 100644
--- a/src/content/dependencies/listTracksWithExtra.js
+++ b/src/content/dependencies/listTracksWithExtra.js
@@ -65,10 +65,14 @@ export default {
         stitchArrays({
           albumLink: relations.albumLinks,
           date: data.dates,
-        }).map(({albumLink, date}) => ({
-            album: albumLink,
-            date: language.formatDate(date),
-          })),
+        }).map(({albumLink, date}) =>
+            (date
+              ? {
+                  stringsKey: 'withDate',
+                  album: albumLink,
+                  date: language.formatDate(date),
+                }
+              : {album: albumLink})),
 
       chunkRows:
         relations.trackLinks
diff --git a/src/gen-thumbs.js b/src/gen-thumbs.js
index e99b960..3d441bc 100644
--- a/src/gen-thumbs.js
+++ b/src/gen-thumbs.js
@@ -251,7 +251,11 @@ async function getImageMagickVersion(binary) {
     allData += data.toString();
   });
 
-  await promisifyProcess(proc, false);
+  try {
+    await promisifyProcess(proc, false);
+  } catch (error) {
+    return null;
+  }
 
   if (!allData.match(/ImageMagick/i)) {
     return null;
@@ -283,7 +287,7 @@ async function getSpawnMagick(tool) {
   }
 
   if (fn === null && await commandExists('magick')) {
-    version = await getImageMagickVersion(fn);
+    version = await getImageMagickVersion('magick');
     if (version !== null) {
       fn = (args) => spawn('magick', [tool, ...args]);
       description = `magick ${tool}`;
diff --git a/src/page/artist-alias.js b/src/page/artist-alias.js
index 1da2af4..d230522 100644
--- a/src/page/artist-alias.js
+++ b/src/page/artist-alias.js
@@ -7,6 +7,12 @@ export function targets({wikiData}) {
 export function pathsForTarget(aliasArtist) {
   const {aliasedArtist} = aliasArtist;
 
+  // Don't generate a redirect page if this aliased name resolves to the same
+  // directory as the original artist! See issue #280.
+  if (aliasArtist.directory === aliasedArtist.directory) {
+    return [];
+  }
+
   return [
     {
       type: 'redirect',
diff --git a/src/strings-default.json b/src/strings-default.json
index b5e39e9..0ad7a51 100644
--- a/src/strings-default.json
+++ b/src/strings-default.json
@@ -437,15 +437,18 @@
   "listingPage.listTracks.inFlashes.byFlash.chunk.item": "{TRACK} (from {ALBUM})",
   "listingPage.listTracks.withLyrics.title": "Tracks - with Lyrics",
   "listingPage.listTracks.withLyrics.title.short": "...with Lyrics",
-  "listingPage.listTracks.withLyrics.chunk.title": "{ALBUM} ({DATE})",
+  "listingPage.listTracks.withLyrics.chunk.title": "{ALBUM}",
+  "listingPage.listTracks.withLyrics.chunk.title.withDate": "{ALBUM} ({DATE})",
   "listingPage.listTracks.withLyrics.chunk.item": "{TRACK}",
   "listingPage.listTracks.withSheetMusicFiles.title": "Tracks - with Sheet Music Files",
   "listingPage.listTracks.withSheetMusicFiles.title.short": "...with Sheet Music Files",
-  "listingPage.listTracks.withSheetMusicFiles.chunk.title": "{ALBUM} ({DATE})",
+  "listingPage.listTracks.withSheetMusicFiles.chunk.title": "{ALBUM}",
+  "listingPage.listTracks.withSheetMusicFiles.chunk.title.withDate": "{ALBUM} ({DATE})",
   "listingPage.listTracks.withSheetMusicFiles.chunk.item": "{TRACK}",
   "listingPage.listTracks.withMidiProjectFiles.title": "Tracks - with MIDI & Project Files",
   "listingPage.listTracks.withMidiProjectFiles.title.short": "...with MIDI & Project Files",
-  "listingPage.listTracks.withMidiProjectFiles.chunk.title": "{ALBUM} ({DATE})",
+  "listingPage.listTracks.withMidiProjectFiles.chunk.title": "{ALBUM}",
+  "listingPage.listTracks.withMidiProjectFiles.chunk.title.withDate": "{ALBUM} ({DATE})",
   "listingPage.listTracks.withMidiProjectFiles.chunk.item": "{TRACK}",
   "listingPage.listTags.byName.title": "Tags - by Name",
   "listingPage.listTags.byName.title.short": "...by Name",
diff --git a/tap-snapshots/test/snapshot/linkThing.js.test.cjs b/tap-snapshots/test/snapshot/linkThing.js.test.cjs
new file mode 100644
index 0000000..1a98cfd
--- /dev/null
+++ b/tap-snapshots/test/snapshot/linkThing.js.test.cjs
@@ -0,0 +1,39 @@
+/* IMPORTANT
+ * This snapshot file is auto-generated, but designed for humans.
+ * It should be checked into source control and tracked carefully.
+ * Re-generate by setting TAP_SNAPSHOT=1 and running tests.
+ * Make sure to inspect the output below.  Do not ignore changes!
+ */
+'use strict'
+exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > basic behavior 1`] = `
+<a href="track/foo/" style="--primary-color: #abcdef; --dim-color: #818181">Cool track!</a>
+`
+
+exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > color 1`] = `
+<a href="track/showtime-piano-refrain/">Showtime (Piano Refrain)</a>
+<a href="track/showtime-piano-refrain/" style="--primary-color: #38f43d; --dim-color: #389f33">Showtime (Piano Refrain)</a>
+<a href="track/showtime-piano-refrain/" style="--primary-color: #aaccff; --dim-color: #828282">Showtime (Piano Refrain)</a>
+`
+
+exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > nested links in content stripped 1`] = `
+<a href="foo/"><b>Oooo! Very spooky.</b></a>
+`
+
+exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > preferShortName 1`] = `
+<a href="tag/five-oceanfalls/">Five</a>
+`
+
+exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > tags in name escaped 1`] = `
+<a href="track/foo/">&lt;a href=&quot;SNOOPING&quot;&gt;AS USUAL&lt;/a&gt; I SEE</a>
+<a href="track/bar/">&lt;b&gt;boldface&lt;/b&gt;</a>
+<a href="album/exile/">&gt;Exile&lt;</a>
+<a href="track/heart/">&lt;3</a>
+`
+
+exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > tooltip & content 1`] = `
+<a href="album/beyond-canon/">Beyond Canon</a>
+<a href="album/beyond-canon/" title="Beyond Canon">Beyond Canon</a>
+<a href="album/beyond-canon/" title="Beyond Canon">Next</a>
+<a href="album/beyond-canon/" title="Apple">Banana</a>
+<a href="album/beyond-canon/">Banana</a>
+`
diff --git a/test/snapshot/linkThing.js b/test/snapshot/linkThing.js
new file mode 100644
index 0000000..195d8c0
--- /dev/null
+++ b/test/snapshot/linkThing.js
@@ -0,0 +1,87 @@
+import t from 'tap';
+import * as html from '#html';
+import {testContentFunctions} from '#test-lib';
+
+testContentFunctions(t, 'linkThing (snapshot)', async (t, evaluate) => {
+  await evaluate.load();
+
+  const quickSnapshot = (message, oneOrMultiple) =>
+    evaluate.snapshot(message,
+      (Array.isArray(oneOrMultiple)
+        ? {name: 'linkThing', multiple: oneOrMultiple}
+        : {name: 'linkThing', ...oneOrMultiple}));
+
+  quickSnapshot('basic behavior', {
+    args: ['localized.track', {
+      directory: 'foo',
+      color: '#abcdef',
+      name: `Cool track!`,
+    }],
+  });
+
+  quickSnapshot('preferShortName', {
+    args: ['localized.tag', {
+      directory: 'five-oceanfalls',
+      name: 'Five (Oceanfalls)',
+      nameShort: 'Five',
+    }],
+    slots: {preferShortName: true},
+  });
+
+  quickSnapshot('tooltip & content', {
+    args: ['localized.album', {
+      directory: 'beyond-canon',
+      name: 'Beyond Canon',
+    }],
+    multiple: [
+      {slots: {tooltip: false}},
+      {slots: {tooltip: true}},
+      {slots: {tooltip: true, content: 'Next'}},
+      {slots: {tooltip: 'Apple', content: 'Banana'}},
+      {slots: {content: 'Banana'}},
+    ],
+  });
+
+  quickSnapshot('color', {
+    args: ['localized.track', {
+      directory: 'showtime-piano-refrain',
+      name: 'Showtime (Piano Refrain)',
+      color: '#38f43d',
+    }],
+    multiple: [
+      {slots: {color: false}},
+      {slots: {color: true}},
+      {slots: {color: '#aaccff'}},
+    ],
+  });
+
+  quickSnapshot('tags in name escaped', [
+    {args: ['localized.track', {
+      directory: 'foo',
+      name: `<a href="SNOOPING">AS USUAL</a> I SEE`,
+    }]},
+    {args: ['localized.track', {
+      directory: 'bar',
+      name: `<b>boldface</b>`,
+    }]},
+    {args: ['localized.album', {
+      directory: 'exile',
+      name: '>Exile<',
+    }]},
+    {args: ['localized.track', {
+      directory: 'heart',
+      name: '<3',
+    }]},
+  ]);
+
+  quickSnapshot('nested links in content stripped', {
+    args: ['localized.staticPage', {directory: 'foo', name: 'Foo'}],
+    slots: {
+      content:
+        html.tag('b', {[html.joinChildren]: ''}, [
+          html.tag('a', {href: 'bar'}, `Oooo!`),
+          ` Very spooky.`,
+        ]),
+    },
+  });
+});