« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/data/things.js74
-rw-r--r--src/data/validators.js7
-rw-r--r--src/misc-templates.js2
-rw-r--r--src/page/album.js27
-rw-r--r--src/page/track.js58
-rwxr-xr-xsrc/upd8.js13
-rw-r--r--src/util/find.js31
7 files changed, 157 insertions, 55 deletions
diff --git a/src/data/things.js b/src/data/things.js
index 4f4c2908..90d09b9c 100644
--- a/src/data/things.js
+++ b/src/data/things.js
@@ -20,6 +20,7 @@ import {
     isNumber,
     isURL,
     isString,
+    isWholeNumber,
     oneOf,
     validateArrayItems,
     validateInstanceOf,
@@ -270,7 +271,7 @@ Thing.common = {
         expose: {
             dependencies: ['artistData', contribsByRefProperty],
             compute: ({ artistData, [contribsByRefProperty]: contribsByRef }) => (
-                (contribsByRef && artistData
+                ((contribsByRef && artistData)
                     ? (contribsByRef
                         .map(({ who: ref, what }) => ({
                             who: find.artist(ref, {wikiData: {artistData}}),
@@ -329,7 +330,10 @@ Thing.common = {
             dependencies: [wikiDataProperty],
 
             compute: ({ [wikiDataProperty]: wikiData, [Thing.instance]: thing }) => (
-                wikiData?.filter(t => t[referencerRefListProperty]?.includes(thing)))
+                (wikiData
+                    ? wikiData.filter(t => t[referencerRefListProperty]?.includes(thing))
+                    : [])
+            )
         }
     }),
 
@@ -466,7 +470,25 @@ TrackGroup.propertyDescriptors = {
     // Update & expose
 
     name: Thing.common.name('Unnamed Track Group'),
-    color: Thing.common.color(),
+
+    color: {
+        flags: {update: true, expose: true},
+
+        update: {validate: isColor},
+
+        expose: {
+            dependencies: ['album'],
+
+            transform(color, { album }) {
+                return color ?? album?.color ?? null;
+            }
+        }
+    },
+
+    startIndex: {
+        flags: {update: true, expose: true},
+        update: {validate: isWholeNumber}
+    },
 
     dateOriginallyReleased: Thing.common.simpleDate(),
 
@@ -476,6 +498,11 @@ TrackGroup.propertyDescriptors = {
 
     // Update only
 
+    album: {
+        flags: {update: true},
+        update: {validate: validateInstanceOf(Album)}
+    },
+
     trackData: Thing.common.wikiData(Track),
 
     // Expose only
@@ -544,6 +571,7 @@ Track.propertyDescriptors = {
     albumData: Thing.common.wikiData(Album),
     artistData: Thing.common.wikiData(Artist),
     artTagData: Thing.common.wikiData(ArtTag),
+    flashData: Thing.common.wikiData(Flash),
     trackData: Thing.common.wikiData(Track),
 
     // Expose only
@@ -565,12 +593,26 @@ Track.propertyDescriptors = {
             dependencies: ['albumData', 'dateFirstReleased'],
             compute: ({ albumData, dateFirstReleased, [Track.instance]: track }) => (
                 dateFirstReleased ??
-                Track.findAlbum(track)?.date ??
+                Track.findAlbum(track, albumData)?.date ??
                 null
             )
         }
     },
 
+    color: {
+        flags: {expose: true},
+
+        expose: {
+            dependencies: ['albumData', 'trackData'],
+
+            compute: ({ albumData, trackData, [Track.instance]: track }) => (
+                (Track.findAlbum(track, albumData)?.trackGroups
+                    .find(tg => tg.tracks.includes(track))?.color)
+                ?? null
+            )
+        }
+    },
+
     coverArtDate: {
         flags: {update: true, expose: true},
 
@@ -588,6 +630,30 @@ Track.propertyDescriptors = {
         }
     },
 
+    otherReleases: {
+        flags: {expose: true},
+
+        expose: {
+            dependencies: ['originalReleaseTrackByRef', 'trackData'],
+
+            compute: ({ originalReleaseTrackByRef: ref1, trackData, [Track.instance]: t1 }) => (
+                (ref1 && trackData
+                    ? [
+                        find.track(ref1, {wikiData: {trackData}}),
+                        ...trackData.filter(t2 => {
+                            const { originalReleaseTrackByRef: ref2 } = t2;
+                            return (
+                                t2 !== t1 &&
+                                ref2 &&
+                                (
+                                    find.track(ref2, {wikiData: {trackData}}) ===
+                                    find.track(ref1, {wikiData: {trackData}})))
+                        })]
+                    : [])
+            )
+        }
+    },
+
     // Previously known as: (track).artists
     artistContribs: Thing.common.dynamicInheritContribs('artistContribsByRef', 'artistContribsByRef', 'albumData', Track.findAlbum),
 
diff --git a/src/data/validators.js b/src/data/validators.js
index 83922229..ca10833e 100644
--- a/src/data/validators.js
+++ b/src/data/validators.js
@@ -81,6 +81,13 @@ export function isCountingNumber(number) {
     return true;
 }
 
+export function isWholeNumber(number) {
+    isInteger(number);
+    isPositiveOrZero(number);
+
+    return true;
+}
+
 export function isString(value) {
     return isType(value, 'string');
 }
diff --git a/src/misc-templates.js b/src/misc-templates.js
index 1fc05f0f..3ad1f233 100644
--- a/src/misc-templates.js
+++ b/src/misc-templates.js
@@ -35,7 +35,7 @@ export function getArtistString(artists, {
         return [
             link.artist(who),
             showContrib && what && `(${what})`,
-            showIcons && urls.length && `<span class="icons">(${
+            showIcons && urls?.length && `<span class="icons">(${
                 strings.list.unit(urls.map(url => iconifyURL(url, {strings})))
             })</span>`
         ].filter(Boolean).join(' ');
diff --git a/src/page/album.js b/src/page/album.js
index 6e8d6dbc..1ebfdecc 100644
--- a/src/page/album.js
+++ b/src/page/album.js
@@ -164,9 +164,11 @@ export function write(album, {wikiData}) {
                                 strings('releaseInfo.released', {
                                     date: strings.count.date(album.date)
                                 }),
-                                +album.coverArtDate !== +album.date && strings('releaseInfo.artReleased', {
-                                    date: strings.count.date(album.coverArtDate)
-                                }),
+                                (album.coverArtDate &&
+                                    +album.coverArtDate !== +album.date &&
+                                    strings('releaseInfo.artReleased', {
+                                        date: strings.count.date(album.coverArtDate)
+                                    })),
                                 strings('releaseInfo.duration', {
                                     duration: strings.count.duration(albumDuration, {approximate: album.tracks.length > 1})
                                 })
@@ -179,7 +181,7 @@ export function write(album, {wikiData}) {
                                 })
                             })
                         }</p>`}
-                        ${album.urls.length && `<p>${
+                        ${album.urls?.length && `<p>${
                             strings('releaseInfo.listenOn', {
                                 links: strings.list.or(album.urls.map(url => fancifyURL(url, {album: true})))
                             })
@@ -263,12 +265,16 @@ export function generateAlbumSidebar(album, currentTrack, {
 }) {
     const listTag = getAlbumListTag(album);
 
+    /*
     const trackGroups = album.trackGroups || [{
         name: strings('albumSidebar.trackList.fallbackGroupName'),
         color: album.color,
         startIndex: 0,
         tracks: album.tracks
     }];
+    */
+
+    const { trackGroups } = album;
 
     const trackToListItem = track => html.tag('li',
         {class: track === currentTrack && 'current'},
@@ -276,9 +282,14 @@ export function generateAlbumSidebar(album, currentTrack, {
             track: link.track(track)
         }));
 
+    const nameOrDefault = (isDefaultTrackGroup, name) =>
+        (isDefaultTrackGroup
+            ? strings('albumSidebar.trackList.fallbackGroupName')
+            : name);
+
     const trackListPart = fixWS`
         <h1>${link.album(album)}</h1>
-        ${trackGroups.map(({ name, color, startIndex, tracks }) =>
+        ${trackGroups.map(({ name, color, startIndex, tracks, isDefaultTrackGroup }) =>
             html.tag('details', {
                 // Leave side8ar track groups collapsed on al8um homepage,
                 // since there's already a view of all the groups expanded
@@ -290,11 +301,11 @@ export function generateAlbumSidebar(album, currentTrack, {
                     {style: getLinkThemeString(color)},
                     (listTag === 'ol'
                         ? strings('albumSidebar.trackList.group.withRange', {
-                            group: `<span class="group-name">${name}</span>`,
+                            group: `<span class="group-name">${nameOrDefault(isDefaultTrackGroup, name)}</span>`,
                             range: `${startIndex + 1}&ndash;${startIndex + tracks.length}`
                         })
                         : strings('albumSidebar.trackList.group', {
-                            group: `<span class="group-name">${name}</span>`
+                            group: `<span class="group-name">${nameOrDefault(isDefaultTrackGroup, name)}</span>`
                         }))
                 ),
                 fixWS`
@@ -319,7 +330,7 @@ export function generateAlbumSidebar(album, currentTrack, {
             })
         }</h1>
         ${!currentTrack && transformMultiline(group.descriptionShort)}
-        ${group.urls.length && `<p>${
+        ${group.urls?.length && `<p>${
             strings('releaseInfo.visitOn', {
                 links: strings.list.or(group.urls.map(url => fancifyURL(url)))
             })
diff --git a/src/page/track.js b/src/page/track.js
index b3cec414..a6ec722e 100644
--- a/src/page/track.js
+++ b/src/page/track.js
@@ -35,23 +35,20 @@ export function targets({wikiData}) {
 
 export function write(track, {wikiData}) {
     const { groupData, wikiInfo } = wikiData;
-    const { album } = track;
+    const { album, referencedByTracks, referencedTracks, otherReleases } = track;
 
-    const tracksThatReference = track.referencedBy;
     const useDividedReferences = groupData.some(group => group.directory === OFFICIAL_GROUP_DIRECTORY);
-    const ttrFanon = (useDividedReferences &&
-        tracksThatReference.filter(t => t.album.groups.every(group => group.directory !== OFFICIAL_GROUP_DIRECTORY)));
-    const ttrOfficial = (useDividedReferences &&
-        tracksThatReference.filter(t => t.album.groups.some(group => group.directory === OFFICIAL_GROUP_DIRECTORY)));
+    const rbtFanon = (useDividedReferences &&
+        referencedByTracks.filter(t => t.album.groups.every(group => group.directory !== OFFICIAL_GROUP_DIRECTORY)));
+    const rbtOfficial = (useDividedReferences &&
+        referencedByTracks.filter(t => t.album.groups.some(group => group.directory === OFFICIAL_GROUP_DIRECTORY)));
 
-    const tracksReferenced = track.references;
-    const otherReleases = track.otherReleases;
     const listTag = getAlbumListTag(album);
 
     let flashesThatFeature;
     if (wikiInfo.enableFlashesAndGames) {
         flashesThatFeature = sortByDate([track, ...otherReleases]
-            .flatMap(track => track.flashes.map(flash => ({flash, as: track}))));
+            .flatMap(track => track.featuredInFlashes.map(flash => ({flash, as: track}))));
     }
 
     const unbound_generateTrackList = (tracks, {getArtistString, link, strings}) => html.tag('ul',
@@ -59,7 +56,7 @@ export function write(track, {wikiData}) {
             const line = strings('trackList.item.withArtists', {
                 track: link.track(track),
                 by: `<span class="by">${strings('trackList.item.withArtists.by', {
-                    artists: getArtistString(track.artists)
+                    artists: getArtistString(track.artistContribs)
                 })}</span>`
             });
             return (track.aka
@@ -172,13 +169,13 @@ export function write(track, {wikiData}) {
                         <p>
                             ${[
                                 strings('releaseInfo.by', {
-                                    artists: getArtistString(track.artists, {
+                                    artists: getArtistString(track.artistContribs, {
                                         showContrib: true,
                                         showIcons: true
                                     })
                                 }),
                                 track.coverArtists && strings('releaseInfo.coverArtBy', {
-                                    artists: getArtistString(track.coverArtists, {
+                                    artists: getArtistString(track.coverArtistContribs, {
                                         showContrib: true,
                                         showIcons: true
                                     })
@@ -186,16 +183,18 @@ export function write(track, {wikiData}) {
                                 album.directory !== UNRELEASED_TRACKS_DIRECTORY && strings('releaseInfo.released', {
                                     date: strings.count.date(track.date)
                                 }),
-                                +track.coverArtDate !== +track.date && strings('releaseInfo.artReleased', {
-                                    date: strings.count.date(track.coverArtDate)
-                                }),
+                                (track.coverArtDate &&
+                                    +track.coverArtDate !== +track.date &&
+                                    strings('releaseInfo.artReleased', {
+                                        date: strings.count.date(track.coverArtDate)
+                                    })),
                                 track.duration && strings('releaseInfo.duration', {
                                     duration: strings.count.duration(track.duration)
                                 })
                             ].filter(Boolean).join('<br>\n')}
                         </p>
                         <p>${
-                            (track.urls.length
+                            (track.urls?.length
                                 ? strings('releaseInfo.listenOn', {
                                     links: strings.list.or(track.urls.map(url => fancifyURL(url, {strings})))
                                 })
@@ -212,17 +211,10 @@ export function write(track, {wikiData}) {
                                 `).join('\n')}
                             </ul>
                         `}
-                        ${track.contributors.textContent && fixWS`
-                            <p>
-                                ${strings('releaseInfo.contributors')}
-                                <br>
-                                ${transformInline(track.contributors.textContent)}
-                            </p>
-                        `}
-                        ${track.contributors.length && fixWS`
+                        ${track.contributorContribs.length && fixWS`
                             <p>${strings('releaseInfo.contributors')}</p>
                             <ul>
-                                ${(track.contributors
+                                ${(track.contributorContribs
                                     .map(contrib => `<li>${getArtistString([contrib], {
                                         showContrib: true,
                                         showIcons: true
@@ -230,25 +222,25 @@ export function write(track, {wikiData}) {
                                     .join('\n'))}
                             </ul>
                         `}
-                        ${tracksReferenced.length && fixWS`
+                        ${referencedTracks.length && fixWS`
                             <p>${strings('releaseInfo.tracksReferenced', {track: `<i>${track.name}</i>`})}</p>
-                            ${generateTrackList(tracksReferenced)}
+                            ${generateTrackList(referencedTracks)}
                         `}
-                        ${tracksThatReference.length && fixWS`
+                        ${referencedByTracks.length && fixWS`
                             <p>${strings('releaseInfo.tracksThatReference', {track: `<i>${track.name}</i>`})}</p>
                             ${useDividedReferences && fixWS`
                                 <dl>
-                                    ${ttrOfficial.length && fixWS`
+                                    ${rbtOfficial.length && fixWS`
                                         <dt>${strings('trackPage.referenceList.official')}</dt>
-                                        <dd>${generateTrackList(ttrOfficial)}</dd>
+                                        <dd>${generateTrackList(rbtOfficial)}</dd>
                                     `}
-                                    ${ttrFanon.length && fixWS`
+                                    ${rbtFanon.length && fixWS`
                                         <dt>${strings('trackPage.referenceList.fandom')}</dt>
-                                        <dd>${generateTrackList(ttrFanon)}</dd>
+                                        <dd>${generateTrackList(rbtFanon)}</dd>
                                     `}
                                 </dl>
                             `}
-                            ${!useDividedReferences && generateTrackList(tracksThatReference)}
+                            ${!useDividedReferences && generateTrackList(referencedByTracks)}
                         `}
                         ${wikiInfo.enableFlashesAndGames && flashesThatFeature.length && fixWS`
                             <p>${strings('releaseInfo.flashesThatFeature', {track: `<i>${track.name}</i>`})}</p>
diff --git a/src/upd8.js b/src/upd8.js
index 2769d42a..70aec6c8 100755
--- a/src/upd8.js
+++ b/src/upd8.js
@@ -906,6 +906,8 @@ function processAlbumEntryDocuments(documents) {
     let currentTracksByRef = null;
     let currentTrackGroupDoc = null;
 
+    let trackIndex = 0;
+
     function closeCurrentTrackGroup() {
         if (currentTracksByRef) {
             let trackGroup;
@@ -917,6 +919,7 @@ function processAlbumEntryDocuments(documents) {
                 trackGroup.isDefaultTrackGroup = true;
             }
 
+            trackGroup.startIndex = trackIndex;
             trackGroup.tracksByRef = currentTracksByRef;
             trackGroups.push(trackGroup);
         }
@@ -930,6 +933,8 @@ function processAlbumEntryDocuments(documents) {
             continue;
         }
 
+        trackIndex++;
+
         const track = processTrackDocument(doc);
         tracks.push(track);
 
@@ -2384,6 +2389,7 @@ async function main() {
                                 trackGroup.isDefaultTrackGroup = true;
                             }
 
+                            trackGroup.album = album;
                             trackGroup.tracksByRef = currentTracksByRef;
                             trackGroups.push(trackGroup);
                         }
@@ -2773,7 +2779,6 @@ async function main() {
         return;
     }
 
-
     // Data linking! Basically, provide (portions of) wikiData to the Things
     // which require it - they'll expose dynamically computed properties as a
     // result (many of which are required for page HTML generation).
@@ -2792,6 +2797,7 @@ async function main() {
         track.albumData = WD.albumData;
         track.artistData = WD.artistData;
         track.artTagData = WD.artTagData;
+        track.flashData = WD.flashData;
         track.trackData = WD.trackData;
     }
 
@@ -2815,7 +2821,8 @@ async function main() {
         trackData: sortByDate(WD.trackData.slice())
     });
 
-    // console.log(WD.trackData.find(t => t.name === 'Aggrievance').artTags[0].taggedInThings.map(thing => thing.name));
+    // const track = WD.trackData.find(t => t.name === 'Under the Sun');
+    // console.log(track.album.trackGroups.find(tg => tg.tracks.includes(track)).color, track.color);
     // return;
 
     // Update languages o8ject with the wiki-specified default language!
@@ -3163,7 +3170,7 @@ async function main() {
             return true;
         };
 
-        return;
+        // return;
 
         writes = buildStepsWithTargets.flatMap(({ flag, pageSpec, targets }) => {
             const writes = targets.flatMap(target =>
diff --git a/src/util/find.js b/src/util/find.js
index 872b3b7a..e8e04a5b 100644
--- a/src/util/find.js
+++ b/src/util/find.js
@@ -4,6 +4,12 @@ import {
 } from './cli.js';
 
 function findHelper(keys, dataProp, findFns = {}) {
+    // Note: This cache explicitly *doesn't* support mutable data arrays. If the
+    // data array is modified, make sure it's actually a new array object, not
+    // the original, or the cache here will break and act as though the data
+    // hasn't changed!
+    const cache = new WeakMap();
+
     const byDirectory = findFns.byDirectory || matchDirectory;
     const byName = findFns.byName || matchName;
 
@@ -15,6 +21,23 @@ function findHelper(keys, dataProp, findFns = {}) {
             throw new Error(`Got a reference that is ${typeof fullRef}, not string: ${fullRef}`);
         }
 
+        const data = wikiData[dataProp];
+
+        if (!data) {
+            throw new Error(`Expected data to be present`);
+        }
+
+        let cacheForThisData = cache.get(data);
+        const cachedValue = cacheForThisData?.[fullRef];
+        if (cachedValue) {
+            globalThis.NUM_CACHE = (globalThis.NUM_CACHE || 0) + 1;
+            return cachedValue;
+        }
+        if (!cacheForThisData) {
+            cacheForThisData = Object.create(null);
+            cache.set(data, cacheForThisData);
+        }
+
         const match = fullRef.match(keyRefRegex);
         if (!match) {
             throw new Error(`Malformed link reference: "${fullRef}"`);
@@ -23,12 +46,6 @@ function findHelper(keys, dataProp, findFns = {}) {
         const key = match[1];
         const ref = match[2];
 
-        const data = wikiData[dataProp];
-
-        if (!data) {
-            throw new Error(`Expected data to be present`);
-        }
-
         const found = (key
             ? byDirectory(ref, data, quiet)
             : byName(ref, data, quiet));
@@ -37,6 +54,8 @@ function findHelper(keys, dataProp, findFns = {}) {
             logWarn`Didn't match anything for ${fullRef}!`;
         }
 
+        cacheForThisData[fullRef] = found;
+
         return found;
     };
 }