« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/page
diff options
context:
space:
mode:
Diffstat (limited to 'src/page')
-rw-r--r--src/page/album.js704
-rw-r--r--src/page/artist.js680
-rw-r--r--src/page/index.js54
-rw-r--r--src/page/static.js30
-rw-r--r--src/page/track.js550
5 files changed, 86 insertions, 1932 deletions
diff --git a/src/page/album.js b/src/page/album.js
index 9ee57c09..111cab82 100644
--- a/src/page/album.js
+++ b/src/page/album.js
@@ -1,66 +1,62 @@
 // Album page specification.
 
-import {
-  bindOpts,
-  compareArrays,
-  empty,
-} from '../util/sugar.js';
-
-import {
-  getAlbumCover,
-  getAlbumListTag,
-  getTotalDuration,
-} from '../util/wiki-data.js';
-
 export const description = `per-album info & track artwork gallery pages`;
 
 export function targets({wikiData}) {
   return wikiData.albumData;
 }
 
-export function write(album, {wikiData}) {
-  const unbound_trackToListItem = (track, {
-    getArtistString,
-    getLinkThemeString,
-    html,
-    language,
-    link,
-  }) => {
-    const itemOpts = {
-      duration: language.formatDuration(track.duration ?? 0),
-      track: link.track(track),
-    };
+export function pathsForTarget(album) {
+  const hasGalleryPage = album.tracks.some(t => t.hasUniqueCoverArt);
+  const hasCommentaryPage = !!album.commentary || album.tracks.some(t => t.commentary);
 
-    return html.tag('li',
-      {style: getLinkThemeString(track.color)},
-      compareArrays(
-        track.artistContribs.map((c) => c.who),
-        album.artistContribs.map((c) => c.who),
-        {checkOrder: false}
-      )
-        ? language.$('trackList.item.withDuration', itemOpts)
-        : language.$('trackList.item.withDuration.withArtists', {
-            ...itemOpts,
-            by: html.tag('span',
-              {class: 'by'},
-              language.$('trackList.item.withArtists.by', {
-                artists: getArtistString(track.artistContribs),
-              })),
-          }));
-  };
+  return [
+    {
+      type: 'page',
+      path: ['album', album.directory],
 
-  const hasAdditionalFiles = !empty(album.additionalFiles);
-  const numAdditionalFiles = album.additionalFiles.flatMap((g) => g.files).length;
+      contentFunction: {
+        name: 'generateAlbumInfoPage',
+        args: [album],
+      },
+    },
 
-  const albumDuration = getTotalDuration(album.tracks);
+    /*
+    hasGalleryPage && {
+      type: 'page',
+      path: ['albumGallery', album.directory],
 
-  const displayTrackSections =
-    album.trackSections &&
-      (album.trackSections.length > 1 ||
-        !album.trackSections[0]?.isDefaultTrackSection);
+      contentFunction: {
+        name: 'generateAlbumGalleryPage',
+        args: [album],
+      },
+    },
+
+    hasCommentaryPage && {
+      type: 'page',
+      path: ['albumCommentary', album.directory],
+
+      contentFunction: {
+        name: 'generateAlbumCommentaryPage',
+        args: [album],
+      },
+    },
 
-  const listTag = getAlbumListTag(album);
+    {
+      type: 'data',
+      path: ['album', album.directory],
 
+      contentFunction: {
+        name: 'generateAlbumDataFile',
+        args: [album],
+      },
+    },
+    */
+  ];
+}
+
+/*
+export function write(album, {wikiData}) {
   const getSocialEmbedDescription = ({
     getArtistString: _getArtistString,
     language,
@@ -123,297 +119,6 @@ export function write(album, {wikiData}) {
     }),
   };
 
-  const infoPage = {
-    type: 'page',
-    path: ['album', album.directory],
-    page: ({
-      absoluteTo,
-      fancifyURL,
-      generateAdditionalFilesShortcut,
-      generateAdditionalFilesList,
-      generateChronologyLinks,
-      generateContentHeading,
-      generateNavigationLinks,
-      getAlbumCover,
-      getAlbumStylesheet,
-      getArtistString,
-      getLinkThemeString,
-      getSizeOfAdditionalFile,
-      getThemeString,
-      html,
-      link,
-      language,
-      transformMultiline,
-      urls,
-    }) => {
-      const trackToListItem = bindOpts(unbound_trackToListItem, {
-        getArtistString,
-        getLinkThemeString,
-        html,
-        language,
-        link,
-      });
-
-      return {
-        title: language.$('albumPage.title', {album: album.name}),
-        stylesheet: getAlbumStylesheet(album),
-
-        themeColor: album.color,
-        theme:
-          getThemeString(album.color, {
-            additionalVariables: [
-              `--album-directory: ${album.directory}`,
-            ],
-          }),
-
-        socialEmbed: {
-          heading:
-            (empty(album.groups)
-              ? ''
-              : language.$('albumPage.socialEmbed.heading', {
-                  group: album.groups[0].name,
-                })),
-          headingLink:
-            (empty(album.groups)
-              ? null
-              : absoluteTo('localized.album', album.groups[0].directory)),
-          title: language.$('albumPage.socialEmbed.title', {
-            album: album.name,
-          }),
-          description: getSocialEmbedDescription({getArtistString, language}),
-          image: '/' + getAlbumCover(album, {to: urls.from('shared.root').to}),
-          color: album.color,
-        },
-
-        banner: !empty(album.bannerArtistContribs) && {
-          dimensions: album.bannerDimensions,
-          path: [
-            'media.albumBanner',
-            album.directory,
-            album.bannerFileExtension,
-          ],
-          alt: language.$('misc.alt.albumBanner'),
-          position: 'top',
-        },
-
-        cover: {
-          src: getAlbumCover(album),
-          alt: language.$('misc.alt.albumCover'),
-          artTags: album.artTags,
-        },
-
-        main: {
-          headingMode: 'sticky',
-
-          content: [
-            html.tag('p',
-              {
-                [html.onlyIfContent]: true,
-                [html.joinChildren]: '<br>',
-              },
-              [
-                !empty(album.artistContribs) &&
-                  language.$('releaseInfo.by', {
-                    artists: getArtistString(album.artistContribs, {
-                      showContrib: true,
-                      showIcons: true,
-                    }),
-                  }),
-
-                !empty(album.coverArtistContribs) &&
-                  language.$('releaseInfo.coverArtBy', {
-                    artists: getArtistString(album.coverArtistContribs, {
-                      showContrib: true,
-                      showIcons: true,
-                    }),
-                  }),
-
-                !empty(album.wallpaperArtistContribs) &&
-                  language.$('releaseInfo.wallpaperArtBy', {
-                    artists: getArtistString(album.wallpaperArtistContribs, {
-                      showContrib: true,
-                      showIcons: true,
-                    }),
-                  }),
-
-                !empty(album.bannerArtistContribs) &&
-                  language.$('releaseInfo.bannerArtBy', {
-                    artists: getArtistString(album.bannerArtistContribs, {
-                      showContrib: true,
-                      showIcons: true,
-                    }),
-                  }),
-
-                album.date &&
-                  language.$('releaseInfo.released', {
-                    date: language.formatDate(album.date),
-                  }),
-
-                album.hasCoverArt &&
-                album.coverArtDate &&
-                +album.coverArtDate !== +album.date &&
-                  language.$('releaseInfo.artReleased', {
-                    date: language.formatDate(album.coverArtDate),
-                  }),
-
-                albumDuration > 0 &&
-                  language.$('releaseInfo.duration', {
-                    duration: language.formatDuration(albumDuration, {
-                      approximate: album.tracks.length > 1,
-                    }),
-                  }),
-              ]),
-
-            html.tag('p',
-              {
-                [html.onlyIfContent]: true,
-                [html.joinChildren]: '<br>',
-              },
-              [
-                hasAdditionalFiles &&
-                  generateAdditionalFilesShortcut(album.additionalFiles),
-
-                checkGalleryPage(album) &&
-                  language.$('releaseInfo.viewGallery', {
-                    link: link.albumGallery(album, {
-                      text: language.$('releaseInfo.viewGallery.link'),
-                    }),
-                  }),
-
-                checkCommentaryPage(album) &&
-                  language.$('releaseInfo.viewCommentary', {
-                    link: link.albumCommentary(album, {
-                      text: language.$('releaseInfo.viewCommentary.link'),
-                    }),
-                  }),
-              ]),
-
-            !empty(album.urls) &&
-              html.tag('p',
-                language.$('releaseInfo.listenOn', {
-                  links: language.formatDisjunctionList(
-                    album.urls.map(url => fancifyURL(url, {album: true}))
-                  ),
-                })),
-
-            displayTrackSections &&
-            !empty(album.trackSections) &&
-              html.tag('dl',
-                {class: 'album-group-list'},
-                album.trackSections.flatMap(({
-                  name,
-                  startIndex,
-                  tracks,
-                }) => [
-                  html.tag('dt',
-                    {class: ['content-heading']},
-                    language.$('trackList.section.withDuration', {
-                      duration: language.formatDuration(getTotalDuration(tracks), {
-                        approximate: tracks.length > 1,
-                      }),
-                      section: name,
-                    })),
-                  html.tag('dd',
-                    html.tag(listTag,
-                      listTag === 'ol' ? {start: startIndex + 1} : {},
-                      tracks.map(trackToListItem))),
-                ])),
-
-            !displayTrackSections &&
-            !empty(album.tracks) &&
-              html.tag(listTag,
-                album.tracks.map(trackToListItem)),
-
-            html.tag('p',
-              {
-                [html.onlyIfContent]: true,
-                [html.joinChildren]: '<br>',
-              },
-              [
-                album.dateAddedToWiki &&
-                  language.$('releaseInfo.addedToWiki', {
-                    date: language.formatDate(
-                      album.dateAddedToWiki
-                    ),
-                  })
-              ]),
-
-            ...html.fragment(
-              hasAdditionalFiles && [
-                generateContentHeading({
-                  id: 'additional-files',
-                  title: language.$('releaseInfo.additionalFiles.heading', {
-                    additionalFiles: language.countAdditionalFiles(numAdditionalFiles, {
-                      unit: true,
-                    }),
-                  }),
-                }),
-
-                generateAlbumAdditionalFilesList(album, album.additionalFiles, {
-                  generateAdditionalFilesList,
-                  getSizeOfAdditionalFile,
-                  link,
-                  urls,
-                }),
-              ]),
-
-            ...html.fragment(
-              album.commentary && [
-                generateContentHeading({
-                  id: 'artist-commentary',
-                  title: language.$('releaseInfo.artistCommentary'),
-                }),
-
-                html.tag('blockquote', transformMultiline(album.commentary)),
-              ]),
-          ],
-        },
-
-        sidebarLeft: generateAlbumSidebar(album, null, {
-          fancifyURL,
-          getLinkThemeString,
-          html,
-          link,
-          language,
-          transformMultiline,
-          wikiData,
-        }),
-
-        nav: {
-          linkContainerClasses: ['nav-links-hierarchy'],
-          links: [
-            {toHome: true},
-            {
-              html: language.$('albumPage.nav.album', {
-                album: link.album(album, {class: 'current'}),
-              }),
-            },
-            {
-              divider: false,
-              html: generateAlbumNavLinks(album, null, {
-                generateNavigationLinks,
-                html,
-                language,
-                link,
-              }),
-            }
-          ],
-          content: generateAlbumChronologyLinks(album, null, {
-            generateChronologyLinks,
-            html,
-          }),
-        },
-
-        secondaryNav: generateAlbumSecondaryNav(album, null, {
-          getLinkThemeString,
-          html,
-          language,
-          link,
-        }),
-      };
-    },
-  };
-
   // TODO: only gen if there are any tracks with art
   const galleryPage = {
     type: 'page',
@@ -494,153 +199,6 @@ export function write(album, {wikiData}) {
       }),
     }),
   };
-
-  return [
-    infoPage,
-    galleryPage,
-    data,
-  ];
-}
-
-// Utility functions
-
-export function generateAlbumSidebar(album, currentTrack, {
-  fancifyURL,
-  getLinkThemeString,
-  html,
-  language,
-  link,
-  transformMultiline,
-}) {
-  const isAlbumPage = !currentTrack;
-  const isTrackPage = !!currentTrack;
-
-  const listTag = getAlbumListTag(album);
-
-  const {trackSections} = album;
-
-  const trackToListItem = (track) =>
-    html.tag('li',
-      {class: track === currentTrack && 'current'},
-      language.$('albumSidebar.trackList.item', {
-        track: link.track(track),
-      }));
-
-  const nameOrDefault = (isDefaultTrackSection, name) =>
-    isDefaultTrackSection
-      ? language.$('albumSidebar.trackList.fallbackSectionName')
-      : name;
-
-  const trackListPart = [
-    html.tag('h1', link.album(album)),
-    ...trackSections.map(({name, color, startIndex, tracks, isDefaultTrackSection}) => {
-      const groupName =
-        html.tag('span',
-          {class: 'group-name'},
-          nameOrDefault(
-            isDefaultTrackSection,
-            name
-          ));
-      return html.tag('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: isTrackPage && tracks.includes(currentTrack),
-          class: tracks.includes(currentTrack) && 'current',
-        },
-        [
-          html.tag(
-            'summary',
-            {style: getLinkThemeString(color)},
-            html.tag('span', [
-              listTag === 'ol' &&
-                language.$('albumSidebar.trackList.group.withRange', {
-                  group: groupName,
-                  range: `${startIndex + 1}&ndash;${
-                    startIndex + tracks.length
-                  }`,
-                }),
-              listTag === 'ul' &&
-                language.$('albumSidebar.trackList.group', {
-                  group: groupName,
-                }),
-            ])),
-          html.tag(listTag,
-            listTag === 'ol' ? {start: startIndex + 1} : {},
-            tracks.map(trackToListItem)),
-        ]);
-    }),
-  ];
-
-  const {groups} = album;
-
-  const groupParts = groups
-    .map((group) => {
-      const albums = group.albums.filter((album) => album.date);
-      const index = albums.indexOf(album);
-      const next = index >= 0 && albums[index + 1];
-      const previous = index > 0 && albums[index - 1];
-      return {group, next, previous};
-    })
-    // This is a map and not a flatMap because the distinction between which
-    // group sets of elements belong to matters. That means this variable is an
-    // array of arrays, and we'll need to treat it as such later!
-    .map(({group, next, previous}) => [
-      html.tag('h1', language.$('albumSidebar.groupBox.title', {
-        group: link.groupInfo(group),
-      })),
-
-      isAlbumPage &&
-        transformMultiline(group.descriptionShort),
-
-      !empty(group.urls) &&
-        html.tag('p', language.$('releaseInfo.visitOn', {
-          links: language.formatDisjunctionList(
-            group.urls.map((url) => fancifyURL(url))
-          ),
-        })),
-
-      ...html.fragment(
-        isAlbumPage && [
-          next &&
-            html.tag('p',
-              {class: 'group-chronology-link'},
-              language.$('albumSidebar.groupBox.next', {
-                album: link.album(next),
-              })),
-
-          previous &&
-            html.tag('p',
-              {class: 'group-chronology-link'},
-              language.$('albumSidebar.groupBox.previous', {
-                album: link.album(previous),
-              })),
-        ]),
-    ]);
-
-  if (empty(groupParts)) {
-    return {
-      stickyMode: 'column',
-      content: trackListPart,
-    };
-  } else if (isTrackPage) {
-    const combinedGroupPart = {
-      classes: ['no-sticky-header'],
-      content: groupParts
-        .map(groupPart => groupPart.filter(Boolean).join('\n'))
-        .join('\n<hr>\n'),
-    };
-    return {
-      stickyMode: 'column',
-      multiple: [trackListPart, combinedGroupPart],
-    };
-  } else {
-    return {
-      stickyMode: 'last',
-      multiple: [...groupParts, trackListPart],
-    };
-  }
 }
 
 export function generateAlbumSecondaryNav(album, currentTrack, {
@@ -696,174 +254,4 @@ export function generateAlbumSecondaryNav(album, currentTrack, {
     content: groupParts,
   };
 }
-
-function checkGalleryPage(album) {
-  return album.tracks.some(t => t.hasUniqueCoverArt);
-}
-
-function checkCommentaryPage(album) {
-  return !!album.commentary || album.tracks.some(t => t.commentary);
-}
-
-export function generateAlbumNavLinks(album, currentTrack, {
-  generateNavigationLinks,
-  html,
-  language,
-  link,
-
-  currentExtra = null,
-  showTrackNavigation = true,
-  showExtraLinks = null,
-}) {
-  const isTrackPage = !!currentTrack;
-
-  showExtraLinks ??= currentTrack ? false : true;
-
-  const extraLinks = showExtraLinks ? [
-    checkGalleryPage(album) &&
-      link.albumGallery(album, {
-        class: [currentExtra === 'gallery' && 'current'],
-        text: language.$('albumPage.nav.gallery'),
-      }),
-
-    checkCommentaryPage(album) &&
-      link.albumCommentary(album, {
-        class: [currentExtra === 'commentary' && 'current'],
-        text: language.$('albumPage.nav.commentary'),
-      }),
-  ].filter(Boolean) : [];
-
-  const previousNextLinks =
-    showTrackNavigation &&
-    album.tracks.length > 1 &&
-      generateNavigationLinks(currentTrack, {
-        data: album.tracks,
-        linkKey: 'track',
-        returnAsArray: true,
-      })
-
-  const randomLink =
-    showTrackNavigation &&
-    album.tracks.length > 1 &&
-      html.tag('a',
-        {
-          href: '#',
-          'data-random': 'track-in-album',
-          id: 'random-button'
-        },
-        (isTrackPage
-          ? language.$('trackPage.nav.random')
-          : language.$('albumPage.nav.randomTrack')));
-
-  const allLinks = [
-    ...previousNextLinks || [],
-    ...extraLinks || [],
-    randomLink,
-  ].filter(Boolean);
-
-  if (empty(allLinks)) {
-    return '';
-  }
-
-  return `(${language.formatUnitList(allLinks)})`;
-}
-
-export function generateAlbumExtrasPageNav(album, currentExtra, {
-  html,
-  language,
-  link,
-}) {
-  return {
-    linkContainerClasses: ['nav-links-hierarchy'],
-    links: [
-      {toHome: true},
-      {
-        html: language.$('albumPage.nav.album', {
-          album: link.album(album, {class: 'current'}),
-        }),
-      },
-      {
-        divider: false,
-        html: generateAlbumNavLinks(album, null, {
-          currentExtra,
-          showTrackNavigation: false,
-          showExtraLinks: true,
-
-          html,
-          language,
-          link,
-        }),
-      }
-    ],
-  };
-}
-
-export function generateAlbumChronologyLinks(album, currentTrack, {
-  generateChronologyLinks,
-  html,
-}) {
-  return html.tag(
-    'div',
-    {
-      [html.onlyIfContent]: true,
-      class: 'nav-chronology-links',
-    },
-    [
-      ...html.fragment(
-        currentTrack && [
-          ...html.fragment(
-            generateChronologyLinks(currentTrack, {
-              contribKey: 'artistContribs',
-              getThings: (artist) => [
-                ...artist.tracksAsArtist,
-                ...artist.tracksAsContributor,
-              ],
-              headingString: 'misc.chronology.heading.track',
-            })),
-
-          ...html.fragment(
-            generateChronologyLinks(currentTrack, {
-              contribKey: 'contributorContribs',
-              getThings: (artist) => [
-                ...artist.tracksAsArtist,
-                ...artist.tracksAsContributor,
-              ],
-              headingString: 'misc.chronology.heading.track',
-            })),
-        ]),
-
-      ...html.fragment(
-        generateChronologyLinks(currentTrack || album, {
-          contribKey: 'coverArtistContribs',
-          dateKey: 'coverArtDate',
-          getThings: (artist) => [
-            ...artist.albumsAsCoverArtist,
-            ...artist.tracksAsCoverArtist,
-          ],
-          headingString: 'misc.chronology.heading.coverArt',
-        })),
-    ]);
-}
-
-export function generateAlbumAdditionalFilesList(album, additionalFiles, {
-  fileSize = true,
-
-  generateAdditionalFilesList,
-  getSizeOfAdditionalFile,
-  link,
-  urls,
-}) {
-  return generateAdditionalFilesList(additionalFiles, {
-    getFileSize:
-      (fileSize
-        ? (file) =>
-            // TODO: Kinda near the metal here...
-            getSizeOfAdditionalFile(
-              urls
-                .from('media.root')
-                .to('media.albumAdditionalFile', album.directory, file))
-        : () => null),
-    linkFile: (file) =>
-      link.albumAdditionalFile({album, file}),
-  });
-}
+*/
diff --git a/src/page/artist.js b/src/page/artist.js
index 4ef44d32..ad365161 100644
--- a/src/page/artist.js
+++ b/src/page/artist.js
@@ -2,682 +2,22 @@
 //
 // NB: See artist-alias.js for artist alias redirect pages.
 
-import {
-  bindOpts,
-  empty,
-  unique,
-} from '../util/sugar.js';
-
-import {
-  chunkByProperties,
-  getTotalDuration,
-  sortAlbumsTracksChronologically,
-  sortFlashesChronologically,
-} from '../util/wiki-data.js';
-
 export const description = `per-artist info & artwork gallery pages`;
 
 export function targets({wikiData}) {
   return wikiData.artistData;
 }
 
-export function write(artist, {wikiData}) {
-  const {groupData, wikiInfo} = wikiData;
-
-  const {name, urls, contextNotes} = artist;
-
-  const artThingsAll = sortAlbumsTracksChronologically(
-    unique([
-      ...(artist.albumsAsCoverArtist ?? []),
-      ...(artist.albumsAsWallpaperArtist ?? []),
-      ...(artist.albumsAsBannerArtist ?? []),
-      ...(artist.tracksAsCoverArtist ?? []),
-    ]),
-    {getDate: (o) => o.coverArtDate});
-
-  const artThingsGallery = sortAlbumsTracksChronologically(
-    [
-      ...(artist.albumsAsCoverArtist ?? []),
-      ...(artist.tracksAsCoverArtist ?? []),
-    ],
-    {latestFirst: true, getDate: (o) => o.coverArtDate});
-
-  const commentaryThings = sortAlbumsTracksChronologically([
-    ...(artist.albumsAsCommentator ?? []),
-    ...(artist.tracksAsCommentator ?? []),
-  ]);
-
-  const hasGallery = !empty(artThingsGallery);
-
-  const getArtistsAndContrib = (thing, key) => ({
-    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']
-        .map((key) => getArtistsAndContrib(thing, key))
-        .filter(({contrib}) => contrib)
-        .map((props) => ({
-          album: thing.album || thing,
-          track: thing.album ? thing : null,
-          date: thing.date,
-          ...props,
-        }))),
-    ['date', 'album']);
-
-  const commentaryListChunks = chunkByProperties(
-    commentaryThings.map((thing) => ({
-      album: thing.album || thing,
-      track: thing.album ? thing : null,
-    })),
-    ['album']);
-
-  const allTracks = sortAlbumsTracksChronologically(
-    unique([
-      ...(artist.tracksAsArtist ?? []),
-      ...(artist.tracksAsContributor ?? []),
-    ]));
-
-  const chunkTracks = (tracks) =>
-    chunkByProperties(
-      tracks.map((track) => ({
-        track,
-        date: +track.date,
-        album: track.album,
-        duration: track.duration,
-        originalReleaseTrack: track.originalReleaseTrack,
-        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,
-          ].filter(Boolean),
-        },
-      })),
-      ['date', 'album'])
-    .map(({date, album, chunk}) => ({
-      date,
-      album,
-      chunk,
-      duration: getTotalDuration(chunk, {originalReleasesOnly: true}),
-    }));
-
-  const trackListChunks = chunkTracks(allTracks);
-  const totalDuration = getTotalDuration(allTracks.filter(t => !t.originalReleaseTrack));
-
-  const countGroups = (things) => {
-    const usedGroups = things.flatMap(
-      (thing) => thing.groups || thing.album?.groups || []);
-    return groupData
-      .map((group) => ({
-        group,
-        contributions: usedGroups.filter(g => g === group).length,
-      }))
-      .filter(({contributions}) => contributions > 0)
-      .sort((a, b) => b.contributions - a.contributions);
-  };
-
-  const musicGroups = countGroups(allTracks);
-  const artGroups = countGroups(artThingsAll);
-
-  let flashes, flashListChunks;
-  if (wikiInfo.enableFlashesAndGames) {
-    flashes = sortFlashesChronologically(artist.flashesAsContributor.slice());
-    flashListChunks = chunkByProperties(
-      flashes.map((flash) => ({
-        act: flash.act,
-        flash,
-        date: flash.date,
-        // Manual artists/contrib properties here, 8ecause we don't
-        // 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),
-      })),
-      ['act']
-    ).map(({act, chunk}) => ({
-      act,
-      chunk,
-      dateFirst: chunk[0].date,
-      dateLast: chunk[chunk.length - 1].date,
-    }));
-  }
-
-  const generateEntryAccents = ({
-    getArtistString,
-    language,
-    original,
-    entry,
-    artists,
-    contrib,
-  }) =>
-    original
-      ? language.$('artistPage.creditList.entry.rerelease', {entry})
-      : !empty(artists)
-      ? contrib.what || contrib.whatArray?.length
-        ? language.$('artistPage.creditList.entry.withArtists.withContribution', {
-            entry,
-            artists: getArtistString(artists),
-            contribution: contrib.whatArray
-              ? language.formatUnitList(contrib.whatArray)
-              : contrib.what,
-          })
-        : language.$('artistPage.creditList.entry.withArtists', {
-            entry,
-            artists: getArtistString(artists),
-          })
-      : contrib.what || contrib.whatArray?.length
-      ? language.$('artistPage.creditList.entry.withContribution', {
-          entry,
-          contribution: contrib.whatArray
-            ? language.formatUnitList(contrib.whatArray)
-            : contrib.what,
-        })
-      : entry;
-
-  const unbound_generateTrackList = (chunks, {
-    getArtistString,
-    html,
-    language,
-    link,
-  }) =>
-    html.tag('dl',
-      chunks.flatMap(({date, album, chunk, duration}) => [
-        html.tag('dt',
-          date && duration ?
-            language.$('artistPage.creditList.album.withDate.withDuration', {
-              album: link.album(album),
-              date: language.formatDate(date),
-              duration: language.formatDuration(duration, {
-                approximate: true,
-              }),
-            }) :
-
-          date ?
-            language.$('artistPage.creditList.album.withDate', {
-              album: link.album(album),
-              date: language.formatDate(date),
-            }) :
-
-          duration ?
-            language.$('artistPage.creditList.album.withDuration', {
-              album: link.album(album),
-              duration: language.formatDuration(duration, {
-                approximate: true,
-              }),
-            }) :
-
-          language.$('artistPage.creditList.album', {
-            album: link.album(album),
-          })),
-
-        html.tag('dd',
-          html.tag('ul',
-            chunk
-              .map(({track, ...props}) => ({
-                original: track.originalReleaseTrack,
-                entry: language.$('artistPage.creditList.entry.track.withDuration', {
-                  track: link.track(track),
-                  duration: language.formatDuration(track.duration ?? 0),
-                }),
-                ...props,
-              }))
-              .map(({original, ...opts}) =>
-                html.tag('li',
-                  {class: original && 'rerelease'},
-                  generateEntryAccents({
-                    getArtistString,
-                    language,
-                    original,
-                    ...opts,
-                  })
-                )
-              ))),
-      ]));
-
-  const unbound_serializeArtistsAndContrib =
-    (key, {serializeContribs, serializeLink}) =>
-    (thing) => {
-      const {artists, contrib} = getArtistsAndContrib(thing, key);
-      const ret = {};
-      ret.link = serializeLink(thing);
-      if (contrib.what) ret.contribution = contrib.what;
-      if (!empty(artists)) ret.otherArtists = serializeContribs(artists);
-      return ret;
-    };
-
-  const unbound_serializeTrackListChunks = (chunks, {serializeLink}) =>
-    chunks.map(({date, album, chunk, duration}) => ({
-      album: serializeLink(album),
-      date,
-      duration,
-      tracks: chunk.map(({track}) => ({
-        link: serializeLink(track),
-        duration: track.duration,
-      })),
-    }));
-
-  const jumpTo = {
-    tracks: !empty(allTracks),
-    art: !empty(artThingsAll),
-    flashes: wikiInfo.enableFlashesAndGames && !empty(flashes),
-    commentary: !empty(commentaryThings),
-  };
-
-  const showJumpTo = Object.values(jumpTo).includes(true);
-
-  const data = {
-    type: 'data',
-    path: ['artist', artist.directory],
-    data: ({serializeContribs, serializeLink}) => {
-      const serializeArtistsAndContrib = bindOpts(unbound_serializeArtistsAndContrib, {
-        serializeContribs,
-        serializeLink,
-      });
-
-      const serializeTrackListChunks = bindOpts(unbound_serializeTrackListChunks, {
-        serializeLink,
-      });
-
-      return {
-        albums: {
-          asCoverArtist: artist.albumsAsCoverArtist
-            .map(serializeArtistsAndContrib('coverArtistContribs')),
-          asWallpaperArtist: artist.albumsAsWallpaperArtist
-            .map(serializeArtistsAndContrib('wallpaperArtistContribs')),
-          asBannerArtist: artist.albumsAsBannerArtis
-            .map(serializeArtistsAndContrib('bannerArtistContribs')),
-        },
-        flashes: wikiInfo.enableFlashesAndGames
-          ? {
-              asContributor: artist.flashesAsContributor
-                .map(flash => getArtistsAndContrib(flash, 'contributorContribs'))
-                .map(({contrib, thing: flash}) => ({
-                  link: serializeLink(flash),
-                  contribution: contrib.what,
-                })),
-            }
-          : null,
-        tracks: {
-          asArtist: artist.tracksAsArtist
-            .map(serializeArtistsAndContrib('artistContribs')),
-          asContributor: artist.tracksAsContributo
-            .map(serializeArtistsAndContrib('contributorContribs')),
-          chunked: serializeTrackListChunks(trackListChunks),
-        },
-      };
-    },
-  };
-
-  const infoPage = {
-    type: 'page',
-    path: ['artist', artist.directory],
-    page: ({
-      fancifyURL,
-      generateInfoGalleryLinks,
-      getArtistAvatar,
-      getArtistString,
-      html,
-      link,
-      language,
-      transformMultiline,
-    }) => {
-      const generateTrackList = bindOpts(unbound_generateTrackList, {
-        getArtistString,
-        html,
-        language,
-        link,
-      });
-
-      return {
-        title: language.$('artistPage.title', {artist: name}),
-
-        cover: artist.hasAvatar && {
-          src: getArtistAvatar(artist),
-          alt: language.$('misc.alt.artistAvatar'),
-        },
-
-        main: {
-          headingMode: 'sticky',
-
-          content: [
-            ...html.fragment(
-              contextNotes && [
-                html.tag('p',
-                  language.$('releaseInfo.note')),
-
-                html.tag('blockquote',
-                  transformMultiline(contextNotes)),
-
-                html.tag('hr'),
-              ]),
-
-            !empty(urls) &&
-              html.tag('p',
-                language.$('releaseInfo.visitOn', {
-                  links: language.formatDisjunctionList(
-                    urls.map((url) => fancifyURL(url, {language}))
-                  ),
-                })),
-
-            hasGallery &&
-              html.tag('p',
-                language.$('artistPage.viewArtGallery', {
-                  link: link.artistGallery(artist, {
-                    text: language.$('artistPage.viewArtGallery.link'),
-                  }),
-                })),
-
-            showJumpTo &&
-              html.tag('p',
-                language.$('misc.jumpTo.withLinks', {
-                  links: language.formatUnitList(
-                    [
-                      jumpTo.tracks &&
-                        html.tag('a',
-                          {href: '#tracks'},
-                          language.$('artistPage.trackList.title')),
-
-                      jumpTo.art &&
-                        html.tag('a',
-                          {href: '#art'},
-                          language.$('artistPage.artList.title')),
-
-                      jumpTo.flashes &&
-                        html.tag('a',
-                          {href: '#flashes'},
-                          language.$('artistPage.flashList.title')),
-
-                      jumpTo.commentary &&
-                        html.tag('a',
-                          {href: '#commentary'},
-                          language.$('artistPage.commentaryList.title')),
-                    ].filter(Boolean)),
-                })),
-
-            ...html.fragment(
-              !empty(allTracks) && [
-                html.tag('h2',
-                  {id: 'tracks', class: ['content-heading']},
-                  language.$('artistPage.trackList.title')),
-
-                totalDuration > 0 &&
-                  html.tag('p',
-                    language.$('artistPage.contributedDurationLine', {
-                      artist: artist.name,
-                      duration: language.formatDuration(
-                        totalDuration,
-                        {
-                          approximate: true,
-                          unit: true,
-                        }
-                      ),
-                    })),
-
-                !empty(musicGroups) &&
-                  html.tag('p',
-                    language.$('artistPage.musicGroupsLine', {
-                      groups: language.formatUnitList(
-                        musicGroups.map(({group, contributions}) =>
-                          language.$('artistPage.groupsLine.item', {
-                            group: link.groupInfo(group),
-                            contributions:
-                              language.countContributions(
-                                contributions
-                              ),
-                          })
-                        )
-                      ),
-                    })),
-
-                generateTrackList(trackListChunks),
-              ]),
-
-            ...html.fragment(
-              !empty(artThingsAll) && [
-                html.tag('h2',
-                  {id: 'art', class: ['content-heading']},
-                  language.$('artistPage.artList.title')),
-
-                hasGallery &&
-                  html.tag('p',
-                    language.$('artistPage.viewArtGallery.orBrowseList', {
-                      link: link.artistGallery(artist, {
-                        text: language.$('artistPage.viewArtGallery.link'),
-                      })
-                    })),
-
-                !empty(artGroups) &&
-                  html.tag('p',
-                    language.$('artistPage.artGroupsLine', {
-                    groups: language.formatUnitList(
-                      artGroups.map(({group, contributions}) =>
-                        language.$('artistPage.groupsLine.item', {
-                          group: link.groupInfo(group),
-                          contributions:
-                            language.countContributions(
-                              contributions
-                            ),
-                        })
-                      )
-                    ),
-                  })),
-
-                html.tag('dl',
-                  artListChunks.flatMap(({date, album, chunk}) => [
-                    html.tag('dt', language.$('artistPage.creditList.album.withDate', {
-                      album: link.album(album),
-                      date: language.formatDate(date),
-                    })),
-
-                    html.tag('dd',
-                      html.tag('ul',
-                        chunk
-                          .map(({track, key, ...props}) => ({
-                            ...props,
-                            entry:
-                              track
-                                ? language.$('artistPage.creditList.entry.track', {
-                                    track: link.track(track),
-                                  })
-                                : html.tag('i',
-                                    language.$('artistPage.creditList.entry.album.' + {
-                                      wallpaperArtistContribs:
-                                        'wallpaperArt',
-                                      bannerArtistContribs:
-                                        'bannerArt',
-                                      coverArtistContribs:
-                                        'coverArt',
-                                    }[key])),
-                          }))
-                          .map((opts) => generateEntryAccents({
-                            getArtistString,
-                            language,
-                            ...opts,
-                          }))
-                          .map(row => html.tag('li', row)))),
-                  ])),
-              ]),
-
-            ...html.fragment(
-              wikiInfo.enableFlashesAndGames &&
-              !empty(flashes) && [
-                html.tag('h2',
-                  {id: 'flashes', class: ['content-heading']},
-                  language.$('artistPage.flashList.title')),
-
-                html.tag('dl',
-                  flashListChunks.flatMap(({
-                    act,
-                    chunk,
-                    dateFirst,
-                    dateLast,
-                  }) => [
-                    html.tag('dt',
-                      language.$('artistPage.creditList.flashAct.withDateRange', {
-                        act: link.flash(chunk[0].flash, {
-                          text: act.name,
-                        }),
-                        dateRange: language.formatDateRange(
-                          dateFirst,
-                          dateLast
-                        ),
-                      })),
-
-                    html.tag('dd',
-                      html.tag('ul',
-                        chunk
-                          .map(({flash, ...props}) => ({
-                            ...props,
-                            entry: language.$('artistPage.creditList.entry.flash', {
-                              flash: link.flash(flash),
-                            }),
-                          }))
-                          .map(opts => generateEntryAccents({
-                            getArtistString,
-                            language,
-                            ...opts,
-                          }))
-                          .map(row => html.tag('li', row)))),
-                  ])),
-              ]),
-
-            ...html.fragment(
-              !empty(commentaryThings) && [
-                html.tag('h2',
-                  {id: 'commentary', class: ['content-heading']},
-                  language.$('artistPage.commentaryList.title')),
-
-                html.tag('dl',
-                  commentaryListChunks.flatMap(({album, chunk}) => [
-                    html.tag('dt',
-                      language.$('artistPage.creditList.album', {
-                        album: link.album(album),
-                      })),
-
-                    html.tag('dd',
-                      html.tag('ul',
-                        chunk
-                          .map(({track}) => track
-                            ? language.$('artistPage.creditList.entry.track', {
-                                track: link.track(track),
-                              })
-                            : html.tag('i',
-                                language.$('artistPage.creditList.entry.album.commentary')))
-                          .map(row => html.tag('li', row)))),
-                  ])),
-              ]),
-          ],
-        },
-
-        nav: generateNavForArtist(artist, false, hasGallery, {
-          generateInfoGalleryLinks,
-          link,
-          language,
-          wikiData,
-        }),
-      };
-    },
-  };
-
-  const galleryPage = hasGallery && {
-    type: 'page',
-    path: ['artistGallery', artist.directory],
-    page: ({
-      generateInfoGalleryLinks,
-      getAlbumCover,
-      getGridHTML,
-      getTrackCover,
-      html,
-      link,
-      language,
-    }) => ({
-      title: language.$('artistGalleryPage.title', {artist: name}),
-
-      main: {
-        classes: ['top-index'],
-        headingMode: 'static',
-
-        content: [
-          html.tag('p',
-            {class: 'quick-info'},
-            language.$('artistGalleryPage.infoLine', {
-              coverArts: language.countCoverArts(artThingsGallery.length, {
-                unit: true,
-              }),
-            })),
-
-          html.tag('div',
-            {class: 'grid-listing'},
-            getGridHTML({
-              entries: artThingsGallery.map((item) => ({item})),
-              srcFn: (thing) =>
-                thing.album
-                  ? getTrackCover(thing)
-                  : getAlbumCover(thing),
-              linkFn: (thing, opts) =>
-                thing.album
-                  ? link.track(thing, opts)
-                  : link.album(thing, opts),
-            })),
-        ],
-      },
-
-      nav: generateNavForArtist(artist, true, hasGallery, {
-        generateInfoGalleryLinks,
-        link,
-        language,
-        wikiData,
-      }),
-    }),
-  };
-
-  return [data, infoPage, galleryPage].filter(Boolean);
-}
-
-// Utility functions
-
-function generateNavForArtist(artist, isGallery, hasGallery, {
-  generateInfoGalleryLinks,
-  language,
-  link,
-  wikiData,
-}) {
-  const {wikiInfo} = wikiData;
+export function pathsForTarget(artist) {
+  return [
+    {
+      type: 'page',
+      path: ['artist', artist.directory],
 
-  const infoGalleryLinks =
-    hasGallery &&
-    generateInfoGalleryLinks(artist, isGallery, {
-      link,
-      language,
-      linkKeyGallery: 'artistGallery',
-      linkKeyInfo: 'artist',
-    });
-
-  return {
-    linkContainerClasses: ['nav-links-hierarchy'],
-    links: [
-      {toHome: true},
-      wikiInfo.enableListings && {
-        path: ['localized.listingIndex'],
-        title: language.$('listingIndex.title'),
-      },
-      {
-        html: language.$('artistPage.nav.artist', {
-          artist: link.artist(artist, {class: 'current'}),
-        }),
+      contentFunction: {
+        name: 'generateArtistInfoPage',
+        args: [artist],
       },
-      hasGallery && {
-        divider: false,
-        html: `(${infoGalleryLinks})`,
-      },
-    ],
-  };
+    },
+  ];
 }
diff --git a/src/page/index.js b/src/page/index.js
index f580cbea..77ebfb6f 100644
--- a/src/page/index.js
+++ b/src/page/index.js
@@ -2,52 +2,20 @@
 // other modules here! It's not the page spec for the homepage - see
 // homepage.js for that.
 //
-// Each module published in this list should follow a particular format,
-// including any of the following exports:
+// (TODO: The docs here from initial draft were totally outdated.
+//        We don't have docs for the new setup yet.
+//        Write those!!)
 //
-// condition({wikiData})
-//     Returns a boolean indicating whether to process targets/writes (true) or
-//     skip this page spec altogether (false). This is usually used for
-//     selectively toggling pages according to site feature flags, though it may
-//     also be used to e.g. skip out if no targets would be found (preventing
-//     writeTargetless from generating an empty index page).
-//
-// targets({wikiData})
-//     Gets the objects which this page's write() function should be called on.
-//     Usually this will simply mean returning the appropriate thingData array,
-//     but it may also apply filter/map/etc if useful.
-//
-// write(thing, {wikiData})
-//     Provides descriptors for any page and data writes associated with the
-//     given thing (which will be a value from the targets() array). This
-//     includes page (HTML) writes, data (JSON) writes, etc. Notably, this
-//     function does not perform any file operations itself; it only describes
-//     the operations which will be processed elsewhere, once for each
-//     translation language.  The write function also immediately transforms
-//     any data which will be reused across writes of the same page, so that
-//     this data is effectively cached (rather than recalculated for each
-//     language/write).
-//
-// writeTargetless({wikiData})
-//     Provides descriptors for page/data/etc writes which will be used
-//     without concern for targets. This is usually used for writing index pages
-//     which should be generated just once (rather than corresponding to
-//     targets).
-//
-// As these modules are effectively the HTML templates for all site layout,
-// common patterns may also be exported alongside the special exports above.
-// 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 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 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 tag from './tag.js';
 export * as track from './track.js';
diff --git a/src/page/static.js b/src/page/static.js
index 8572db4e..82330dec 100644
--- a/src/page/static.js
+++ b/src/page/static.js
@@ -8,26 +8,16 @@ export function targets({wikiData}) {
   return wikiData.staticPageData;
 }
 
-export function write(staticPage) {
-  const page = {
-    type: 'page',
-    path: ['staticPage', staticPage.directory],
-    page: ({
-      transformMultiline,
-    }) => ({
-      title: staticPage.name,
-      stylesheet: staticPage.stylesheet,
+export function pathsForTarget(staticPage) {
+  return [
+    {
+      type: 'page',
+      path: ['staticPage', staticPage.directory],
 
-      main: {
-        classes: ['long-content'],
-        headingMode: 'sticky',
-
-        content: transformMultiline(staticPage.content),
+      contentFunction: {
+        name: 'generateStaticPage',
+        args: [staticPage],
       },
-
-      nav: {simple: true},
-    }),
-  };
-
-  return [page];
+    },
+  ];
 }
diff --git a/src/page/track.js b/src/page/track.js
index b6b03f35..e75b6958 100644
--- a/src/page/track.js
+++ b/src/page/track.js
@@ -1,553 +1,21 @@
 // Track page specification.
 
-import {
-  generateAlbumChronologyLinks,
-  generateAlbumNavLinks,
-  generateAlbumSecondaryNav,
-  generateAlbumSidebar,
-  generateAlbumAdditionalFilesList as unbound_generateAlbumAdditionalFilesList,
-} from './album.js';
-
-import {
-  bindOpts,
-  empty,
-} from '../util/sugar.js';
-
-import {
-  getTrackCover,
-  getAlbumListTag,
-  sortFlashesChronologically,
-} from '../util/wiki-data.js';
-
 export const description = `per-track info pages`;
 
 export function targets({wikiData}) {
   return wikiData.trackData;
 }
 
-export function write(track, {wikiData}) {
-  const {wikiInfo} = wikiData;
-
-  const {
-    album,
-    contributorContribs,
-    referencedByTracks,
-    referencedTracks,
-    sampledByTracks,
-    sampledTracks,
-    otherReleases,
-  } = track;
-
-  const listTag = getAlbumListTag(album);
-
-  let flashesThatFeature;
-  if (wikiInfo.enableFlashesAndGames) {
-    flashesThatFeature = sortFlashesChronologically(
-      [track, ...otherReleases].flatMap((track) =>
-        track.featuredInFlashes.map((flash) => ({
-          flash,
-          as: track,
-          directory: flash.directory,
-          name: flash.name,
-          date: flash.date,
-        }))
-      )
-    );
-  }
-
-  const unbound_getTrackItem = (track, {
-    getArtistString,
-    html,
-    language,
-    link,
-  }) =>
-    html.tag('li',
-      language.$('trackList.item.withArtists', {
-        track: link.track(track),
-        by: html.tag('span',
-          {class: 'by'},
-          language.$('trackList.item.withArtists.by', {
-            artists: getArtistString(track.artistContribs),
-          })),
-      }));
-
-  const hasCommentary =
-    track.commentary || otherReleases.some((t) => t.commentary);
+export function pathsForTarget(track) {
+  return [
+    {
+      type: 'page',
+      path: ['track', track.directory],
 
-  const hasAdditionalFiles = !empty(track.additionalFiles);
-  const hasSheetMusicFiles = !empty(track.sheetMusicFiles);
-  const hasMidiProjectFiles = !empty(track.midiProjectFiles);
-  const numAdditionalFiles = album.additionalFiles.flatMap((g) => g.files).length;
-
-  const generateCommentary = ({language, link, transformMultiline}) =>
-    transformMultiline([
-      track.commentary,
-      ...otherReleases.map((track) =>
-        track.commentary
-          ?.split('\n')
-          .filter((line) => line.replace(/<\/b>/g, '').includes(':</i>'))
-          .flatMap(line => [
-            line,
-            language.$('releaseInfo.artistCommentary.seeOriginalRelease', {
-              original: link.track(track),
-            }),
-          ])
-          .join('\n')
-      ),
-    ].filter(Boolean).join('\n'));
-
-  const data = {
-    type: 'data',
-    path: ['track', track.directory],
-    data: ({
-      serializeContribs,
-      serializeCover,
-      serializeGroupsForTrack,
-      serializeLink,
-    }) => ({
-      name: track.name,
-      directory: track.directory,
-      dates: {
-        released: track.date,
-        originallyReleased: track.originalDate,
-        coverArtAdded: track.coverArtDate,
+      contentFunction: {
+        name: 'generateTrackInfoPage',
+        args: [track],
       },
-      duration: track.duration,
-      color: track.color,
-      cover: serializeCover(track, getTrackCover),
-      artistsContribs: serializeContribs(track.artistContribs),
-      contributorContribs: serializeContribs(track.contributorContribs),
-      coverArtistContribs: serializeContribs(track.coverArtistContribs || []),
-      album: serializeLink(track.album),
-      groups: serializeGroupsForTrack(track),
-      references: track.references.map(serializeLink),
-      referencedBy: track.referencedBy.map(serializeLink),
-      alsoReleasedAs: otherReleases.map((track) => ({
-        track: serializeLink(track),
-        album: serializeLink(track.album),
-      })),
-    }),
-  };
-
-  const getSocialEmbedDescription = ({
-    getArtistString: _getArtistString,
-    language,
-  }) => {
-    const hasArtists = !empty(track.artistContribs);
-    const hasCoverArtists = !empty(track.coverArtistContribs);
-    const getArtistString = (contribs) =>
-      _getArtistString(contribs, {
-        // We don't want to put actual HTML tags in social embeds (sadly
-        // 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},
-      });
-    if (!hasArtists && !hasCoverArtists) return '';
-    return language.formatString(
-      'trackPage.socialEmbed.body' +
-        [hasArtists && '.withArtists', hasCoverArtists && '.withCoverArtists']
-          .filter(Boolean)
-          .join(''),
-      Object.fromEntries(
-        [
-          hasArtists && ['artists', getArtistString(track.artistContribs)],
-          hasCoverArtists && [
-            'coverArtists',
-            getArtistString(track.coverArtistContribs),
-          ],
-        ].filter(Boolean)
-      )
-    );
-  };
-
-  const page = {
-    type: 'page',
-    path: ['track', track.directory],
-    page: ({
-      absoluteTo,
-      fancifyURL,
-      generateAdditionalFilesList,
-      generateAdditionalFilesShortcut,
-      generateChronologyLinks,
-      generateContentHeading,
-      generateNavigationLinks,
-      generateTrackListDividedByGroups,
-      getAlbumStylesheet,
-      getArtistString,
-      getLinkThemeString,
-      getSizeOfAdditionalFile,
-      getThemeString,
-      getTrackCover,
-      html,
-      link,
-      language,
-      transformLyrics,
-      transformMultiline,
-      to,
-      urls,
-    }) => {
-      const getTrackItem = bindOpts(unbound_getTrackItem, {
-        getArtistString,
-        html,
-        language,
-        link,
-      });
-
-      const generateAlbumAdditionalFilesList = bindOpts(unbound_generateAlbumAdditionalFilesList, {
-        [bindOpts.bindIndex]: 2,
-        generateAdditionalFilesList,
-        getSizeOfAdditionalFile,
-        link,
-        urls,
-      });
-
-      return {
-        title: language.$('trackPage.title', {track: track.name}),
-        stylesheet: getAlbumStylesheet(album, {to}),
-
-        themeColor: track.color,
-        theme:
-          getThemeString(track.color, {
-            additionalVariables: [
-              `--album-directory: ${album.directory}`,
-              `--track-directory: ${track.directory}`,
-            ]
-          }),
-
-        socialEmbed: {
-          heading: language.$('trackPage.socialEmbed.heading', {
-            album: track.album.name,
-          }),
-          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}),
-          color: track.color,
-        },
-
-        // disabled for now! shifting banner position per height of page is disorienting
-        /*
-        banner: !empty(album.bannerArtistContribs) && {
-          classes: ['dim'],
-          dimensions: album.bannerDimensions,
-          path: ['media.albumBanner', album.directory, album.bannerFileExtension],
-          alt: language.$('misc.alt.albumBanner'),
-          position: 'bottom'
-        },
-        */
-
-        cover: {
-          src: getTrackCover(track),
-          alt: language.$('misc.alt.trackCover'),
-          artTags: track.artTags,
-        },
-
-        main: {
-          headingMode: 'sticky',
-
-          content: [
-            html.tag('p',
-              {
-                [html.onlyIfContent]: true,
-                [html.joinChildren]: '<br>',
-              },
-              [
-                !empty(track.artistContribs) &&
-                  language.$('releaseInfo.by', {
-                    artists: getArtistString(track.artistContribs, {
-                      showContrib: true,
-                      showIcons: true,
-                    }),
-                  }),
-
-                !empty(track.coverArtistContribs) &&
-                  language.$('releaseInfo.coverArtBy', {
-                    artists: getArtistString(track.coverArtistContribs, {
-                      showContrib: true,
-                      showIcons: true,
-                    }),
-                  }),
-
-                track.date &&
-                  language.$('releaseInfo.released', {
-                    date: language.formatDate(track.date),
-                  }),
-
-                track.hasCoverArt &&
-                track.coverArtDate &&
-                +track.coverArtDate !== +track.date &&
-                  language.$('releaseInfo.artReleased', {
-                    date: language.formatDate(track.coverArtDate),
-                  }),
-
-                track.duration &&
-                  language.$('releaseInfo.duration', {
-                    duration: language.formatDuration(
-                      track.duration
-                    ),
-                  }),
-              ]),
-
-            html.tag('p',
-              {
-                [html.onlyIfContent]: true,
-                [html.joinChildren]: '<br>',
-              },
-              [
-                hasSheetMusicFiles &&
-                  language.$('releaseInfo.sheetMusicFiles.shortcut', {
-                    link: html.tag('a',
-                      {href: '#sheet-music-files'},
-                      language.$('releaseInfo.sheetMusicFiles.shortcut.link')),
-                  }),
-
-                hasMidiProjectFiles &&
-                  language.$('releaseInfo.midiProjectFiles.shortcut', {
-                    link: html.tag('a',
-                      {href: '#midi-project-files'},
-                      language.$('releaseInfo.midiProjectFiles.shortcut.link')),
-                  }),
-
-                hasAdditionalFiles &&
-                  generateAdditionalFilesShortcut(track.additionalFiles),
-              ]),
-
-            html.tag('p',
-              (empty(track.urls)
-                ? language.$('releaseInfo.listenOn.noLinks')
-                : language.$('releaseInfo.listenOn', {
-                    links: language.formatDisjunctionList(
-                      track.urls.map(url => fancifyURL(url, {language}))),
-                  }))),
-
-            ...html.fragment(
-              !empty(otherReleases) && [
-                generateContentHeading({
-                  id: 'also-released-as',
-                  title: language.$('releaseInfo.alsoReleasedAs'),
-                }),
-
-                html.tag('ul', otherReleases.map(track =>
-                  html.tag('li', language.$('releaseInfo.alsoReleasedAs.item', {
-                    track: link.track(track),
-                    album: link.album(track.album),
-                  })))),
-              ]),
-
-            ...html.fragment(
-              !empty(contributorContribs) && [
-                generateContentHeading({
-                  id: 'contributors',
-                  title: language.$('releaseInfo.contributors'),
-                }),
-
-                html.tag('ul', contributorContribs.map(contrib =>
-                  html.tag('li', getArtistString([contrib], {
-                    showContrib: true,
-                    showIcons: true,
-                  })))),
-              ]),
-
-            ...html.fragment(
-              !empty(referencedTracks) && [
-                generateContentHeading({
-                  id: 'references',
-                  title:
-                    language.$('releaseInfo.tracksReferenced', {
-                      track: html.tag('i', track.name),
-                    }),
-                }),
-
-                html.tag('ul', referencedTracks.map(getTrackItem)),
-              ]),
-
-            ...html.fragment(
-              !empty(referencedByTracks) && [
-                generateContentHeading({
-                  id: 'referenced-by',
-                  title:
-                    language.$('releaseInfo.tracksThatReference', {
-                      track: html.tag('i', track.name),
-                    }),
-                }),
-
-                generateTrackListDividedByGroups(referencedByTracks, {
-                  getTrackItem,
-                  wikiData,
-                }),
-              ]),
-
-            ...html.fragment(
-              !empty(sampledTracks) && [
-                generateContentHeading({
-                  id: 'samples',
-                  title:
-                    language.$('releaseInfo.tracksSampled', {
-                      track: html.tag('i', track.name),
-                    }),
-                }),
-
-                html.tag('ul', sampledTracks.map(getTrackItem)),
-              ]),
-
-            ...html.fragment(
-              !empty(sampledByTracks) && [
-                generateContentHeading({
-                  id: 'sampled-by',
-                  title:
-                    language.$('releaseInfo.tracksThatSample', {
-                      track: html.tag('i', track.name),
-                    })
-                }),
-
-                html.tag('ul', sampledByTracks.map(getTrackItem)),
-              ]),
-
-            ...html.fragment(
-              wikiInfo.enableFlashesAndGames &&
-              !empty(flashesThatFeature) && [
-                generateContentHeading({
-                  id: 'featured-in',
-                  title:
-                    language.$('releaseInfo.flashesThatFeature', {
-                      track: html.tag('i', track.name),
-                    }),
-                }),
-
-                html.tag('ul', flashesThatFeature.map(({flash, as}) =>
-                  html.tag('li',
-                    {class: as !== track && 'rerelease'},
-                    (as === track
-                      ? language.$('releaseInfo.flashesThatFeature.item', {
-                        flash: link.flash(flash),
-                      })
-                      : language.$('releaseInfo.flashesThatFeature.item.asDifferentRelease', {
-                        flash: link.flash(flash),
-                        track: link.track(as),
-                      }))))),
-              ]),
-
-            ...html.fragment(
-              track.lyrics && [
-                generateContentHeading({
-                  id: 'lyrics',
-                  title: language.$('releaseInfo.lyrics'),
-                }),
-
-                html.tag('blockquote', transformLyrics(track.lyrics)),
-              ]),
-
-            ...html.fragment(
-              hasSheetMusicFiles && [
-                generateContentHeading({
-                  id: 'sheet-music-files',
-                  title: language.$('releaseInfo.sheetMusicFiles.heading'),
-                }),
-
-                generateAlbumAdditionalFilesList(album, track.sheetMusicFiles, {
-                  fileSize: false,
-                }),
-              ]),
-
-            ...html.fragment(
-              hasMidiProjectFiles && [
-                generateContentHeading({
-                  id: 'midi-project-files',
-                  title: language.$('releaseInfo.midiProjectFiles.heading'),
-                }),
-
-                generateAlbumAdditionalFilesList(album, track.midiProjectFiles),
-              ]),
-
-            ...html.fragment(
-              hasAdditionalFiles && [
-                generateContentHeading({
-                  id: 'additional-files',
-                  title: language.$('releaseInfo.additionalFiles.heading', {
-                    additionalFiles: language.countAdditionalFiles(numAdditionalFiles, {
-                      unit: true,
-                    }),
-                  })
-                }),
-
-                generateAlbumAdditionalFilesList(album, track.additionalFiles),
-              ]),
-
-            ...html.fragment(
-              hasCommentary && [
-                generateContentHeading({
-                  id: 'artist-commentary',
-                  title: language.$('releaseInfo.artistCommentary'),
-                }),
-
-                html.tag('blockquote', generateCommentary({
-                  link,
-                  language,
-                  transformMultiline,
-                })),
-              ]),
-          ],
-        },
-
-        sidebarLeft: generateAlbumSidebar(album, track, {
-          fancifyURL,
-          getLinkThemeString,
-          html,
-          language,
-          link,
-          transformMultiline,
-          wikiData,
-        }),
-
-        nav: {
-          linkContainerClasses: ['nav-links-hierarchy'],
-          links: [
-            {toHome: true},
-            {
-              path: ['localized.album', album.directory],
-              title: album.name,
-            },
-            listTag === 'ol' &&
-              {
-                html: language.$('trackPage.nav.track.withNumber', {
-                  number: album.tracks.indexOf(track) + 1,
-                  track: link.track(track, {class: 'current', to}),
-                }),
-              },
-            listTag === 'ul' &&
-              {
-                html: language.$('trackPage.nav.track', {
-                  track: link.track(track, {class: 'current', to}),
-                }),
-              },
-          ].filter(Boolean),
-
-          content: generateAlbumChronologyLinks(album, track, {
-            generateChronologyLinks,
-            html,
-          }),
-
-          bottomRowContent:
-            album.tracks.length > 1 &&
-              generateAlbumNavLinks(album, track, {
-                generateNavigationLinks,
-                html,
-                language,
-              }),
-        },
-
-        secondaryNav: generateAlbumSecondaryNav(album, track, {
-          getLinkThemeString,
-          html,
-          language,
-          link,
-        }),
-      };
     },
-  };
-
-  return [data, page];
+  ];
 }