« get me outta code hell

error-free basic full build - 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>2022-02-24 22:59:04 -0400
committer(quasar) nebula <qznebula@protonmail.com>2022-02-24 22:59:04 -0400
commit5057b24b53f3866cf9f34a6fdfa0d1345c0d8be0 (patch)
tree80c4d41078d1b23d6f3d8279c1470e84f8a7615a
parent71de050864a42afcaeb409c11b9c792653cf2f1b (diff)
error-free basic full build
-rw-r--r--src/data/things.js99
-rw-r--r--src/listing-spec.js2
-rw-r--r--src/page/artist.js8
-rw-r--r--src/page/flash.js17
-rw-r--r--src/page/group.js2
-rw-r--r--src/page/homepage.js34
-rw-r--r--src/page/news.js6
-rwxr-xr-xsrc/upd8.js151
8 files changed, 193 insertions, 126 deletions
diff --git a/src/data/things.js b/src/data/things.js
index 90d09b9c..330419c7 100644
--- a/src/data/things.js
+++ b/src/data/things.js
@@ -243,7 +243,7 @@ Thing.common = {
 
         expose: {
             dependencies: [singleReferenceProperty, wikiDataProperty],
-            compute: ({ [singleReferenceProperyt]: ref, [wikiDataProperty]: wikiData }) => (
+            compute: ({ [singleReferenceProperty]: ref, [wikiDataProperty]: wikiData }) => (
                 (ref && wikiData
                     ? findFn(ref, {wikiData: {[wikiDataProperty]: wikiData}})
                     : [])
@@ -603,9 +603,9 @@ Track.propertyDescriptors = {
         flags: {expose: true},
 
         expose: {
-            dependencies: ['albumData', 'trackData'],
+            dependencies: ['albumData'],
 
-            compute: ({ albumData, trackData, [Track.instance]: track }) => (
+            compute: ({ albumData, [Track.instance]: track }) => (
                 (Track.findAlbum(track, albumData)?.trackGroups
                     .find(tg => tg.tracks.includes(track))?.color)
                 ?? null
@@ -743,6 +743,7 @@ Group.propertyDescriptors = {
     // Update only
 
     albumData: Thing.common.wikiData(Album),
+    groupCategoryData: Thing.common.wikiData(GroupCategory),
 
     // Expose only
 
@@ -764,6 +765,27 @@ Group.propertyDescriptors = {
                 albumData?.filter(album => album.groups.includes(group)) ?? [])
         }
     },
+
+    color: {
+        flags: {expose: true},
+
+        expose: {
+            dependencies: ['groupCategoryData'],
+
+            compute: ({ groupCategoryData, [Group.instance]: group }) => (
+                groupCategoryData.find(category => category.groups.includes(group))?.color ?? null)
+        }
+    },
+
+    category: {
+        flags: {expose: true},
+
+        expose: {
+            dependencies: ['groupCategoryData'],
+            compute: ({ groupCategoryData, [Group.instance]: group }) => (
+                groupCategoryData?.filter(category => category.groups.includes(group)) ?? [])
+        }
+    },
 };
 
 GroupCategory.propertyDescriptors = {
@@ -773,6 +795,14 @@ GroupCategory.propertyDescriptors = {
     color: Thing.common.color(),
 
     groupsByRef: Thing.common.referenceList(Group),
+
+    // Update only
+
+    groupData: Thing.common.wikiData(Group),
+
+    // Expose only
+
+    groups: Thing.common.dynamicThingsFromReferenceList('groupsByRef', 'groupData', find.group),
 };
 
 // -> ArtTag
@@ -823,9 +853,7 @@ NewsEntry.propertyDescriptors = {
         expose: {
             dependencies: ['content'],
 
-            compute({ content }) {
-                return body.split('<hr class="split">')[0];
-            }
+            compute: ({ content }) => content.split('<hr class="split">')[0]
         }
     },
 };
@@ -885,6 +913,14 @@ HomepageLayoutRow.propertyDescriptors = {
     },
 
     color: Thing.common.color(),
+
+    // Update only
+
+    // These aren't necessarily used by every HomepageLayoutRow subclass, but
+    // for convenience of providing this data, every row accepts all wiki data
+    // arrays depended upon by any subclass's behavior.
+    albumData: Thing.common.wikiData(Album),
+    groupData: Thing.common.wikiData(Group),
 };
 
 HomepageLayoutAlbumsRow.propertyDescriptors = {
@@ -917,6 +953,11 @@ HomepageLayoutAlbumsRow.propertyDescriptors = {
         flags: {update: true, expose: true},
         update: {validate: validateArrayItems(isString)}
     },
+
+    // Expose only
+
+    sourceGroup: Thing.common.dynamicThingFromSingleReference('sourceGroupByRef', 'groupData', find.group),
+    sourceAlbums: Thing.common.dynamicThingsFromReferenceList('sourceAlbumsByRef', 'albumData', find.album),
 };
 
 // -> Flash
@@ -951,7 +992,7 @@ Flash.propertyDescriptors = {
         update: {validate: oneOf(isString, isNumber)},
 
         expose: {
-            transform: value => value.toString()
+            transform: value => (value === null ? null : value.toString())
         }
     },
 
@@ -967,6 +1008,40 @@ Flash.propertyDescriptors = {
     contributorContribsByRef: Thing.common.contribsByRef(),
 
     urls: Thing.common.urls(),
+
+    // Update only
+
+    artistData: Thing.common.wikiData(Artist),
+    trackData: Thing.common.wikiData(Track),
+    flashActData: Thing.common.wikiData(FlashAct),
+
+    // Expose only
+
+    contributorContribs: Thing.common.dynamicContribs('contributorContribsByRef'),
+
+    featuredTracks: Thing.common.dynamicThingsFromReferenceList('featuredTracksByRef', 'trackData', find.track),
+
+    act: {
+        flags: {expose: true},
+
+        expose: {
+            dependencies: ['flashActData'],
+
+            compute: ({ flashActData, [Flash.instance]: flash }) => (
+                flashActData.find(act => act.flashes.includes(flash)) ?? null)
+        }
+    },
+
+    color: {
+        flags: {expose: true},
+
+        expose: {
+            dependencies: ['flashActData'],
+
+            compute: ({ flashActData, [Flash.instance]: flash }) => (
+                flashActData.find(act => act.flashes.includes(flash))?.color ?? null)
+        }
+    },
 };
 
 FlashAct.propertyDescriptors = {
@@ -979,6 +1054,14 @@ FlashAct.propertyDescriptors = {
     jumpColor: Thing.common.color(),
 
     flashesByRef: Thing.common.referenceList(Flash),
+
+    // Update only
+
+    flashData: Thing.common.wikiData(Flash),
+
+    // Expose only
+
+    flashes: Thing.common.dynamicThingsFromReferenceList('flashesByRef', 'flashData', find.flash),
 };
 
 // -> WikiInfo
@@ -989,7 +1072,7 @@ WikiInfo.propertyDescriptors = {
     name: Thing.common.name('Unnamed Wiki'),
 
     // Displayed in nav bar.
-    shortName: {
+    nameShort: {
         flags: {update: true, expose: true},
         update: {validate: isName},
 
diff --git a/src/listing-spec.js b/src/listing-spec.js
index 97498ba6..6603e963 100644
--- a/src/listing-spec.js
+++ b/src/listing-spec.js
@@ -673,7 +673,7 @@ const listingSpec = [
                             date: strings.count.date(flash.date)
                         })}</dt>
                         <dd><ul>
-                            ${(flash.tracks
+                            ${(flash.featuredTracks
                                 .map(track => strings('listingPage.listTracks.inFlashes.byFlash.track', {
                                     track: link.track(track),
                                     album: link.album(track.album)
diff --git a/src/page/artist.js b/src/page/artist.js
index b89107fc..d0128050 100644
--- a/src/page/artist.js
+++ b/src/page/artist.js
@@ -32,11 +32,7 @@ export function targets({wikiData}) {
 export function write(artist, {wikiData}) {
     const { groupData, wikiInfo } = wikiData;
 
-    const {
-        name,
-        urls = [],
-        note = ''
-    } = artist;
+    const { name, urls, note } = artist;
 
     const artThingsAll = sortByDate(unique([
         ...artist.albumsAsCoverArtist ?? [],
@@ -305,7 +301,7 @@ export function write(artist, {wikiData}) {
                             </blockquote>
                             <hr>
                         `}
-                        ${urls.length && `<p>${strings('releaseInfo.visitOn', {
+                        ${urls?.length && `<p>${strings('releaseInfo.visitOn', {
                             links: strings.list.or(urls.map(url => fancifyURL(url, {strings})))
                         })}</p>`}
                         ${hasGallery && `<p>${strings('artistPage.viewArtGallery', {
diff --git a/src/page/flash.js b/src/page/flash.js
index 9d88bc79..8f6e4f85 100644
--- a/src/page/flash.js
+++ b/src/page/flash.js
@@ -49,13 +49,13 @@ export function write(flash, {wikiData}) {
                         alt: strings('misc.alt.flashArt')
                     })}
                     <p>${strings('releaseInfo.released', {date: strings.count.date(flash.date)})}</p>
-                    ${(flash.page || flash.urls.length) && `<p>${strings('releaseInfo.playOn', {
+                    ${(flash.page || flash.urls?.length) && `<p>${strings('releaseInfo.playOn', {
                         links: strings.list.or([
                             flash.page && getFlashLink(flash),
-                            ...flash.urls
+                            ...flash.urls ?? []
                         ].map(url => fancifyFlashURL(url, flash)))
                     })}</p>`}
-                    ${flash.tracks.length && fixWS`
+                    ${flash.tracks?.length && fixWS`
                         <p>Tracks featured in <i>${flash.name.replace(/\.$/, '')}</i>:</p>
                         <ul>
                             ${(flash.tracks
@@ -71,17 +71,10 @@ export function write(flash, {wikiData}) {
                                 .join('\n'))}
                         </ul>
                     `}
-                    ${flash.contributors.textContent && fixWS`
-                        <p>
-                            ${strings('releaseInfo.contributors')}
-                            <br>
-                            ${transformInline(flash.contributors.textContent)}
-                        </p>
-                    `}
-                    ${flash.contributors.length && fixWS`
+                    ${flash.contributorContribs.length && fixWS`
                         <p>${strings('releaseInfo.contributors')}</p>
                         <ul>
-                            ${flash.contributors
+                            ${flash.contributorContribs
                                 .map(contrib => `<li>${getArtistString([contrib], {
                                     showContrib: true,
                                     showIcons: true
diff --git a/src/page/group.js b/src/page/group.js
index 47828965..c51f0e38 100644
--- a/src/page/group.js
+++ b/src/page/group.js
@@ -52,7 +52,7 @@ export function write(group, {wikiData}) {
             main: {
                 content: fixWS`
                     <h1>${strings('groupInfoPage.title', {group: group.name})}</h1>
-                    ${group.urls.length && `<p>${
+                    ${group.urls?.length && `<p>${
                         strings('releaseInfo.visitOn', {
                             links: strings.list.or(group.urls.map(url => fancifyURL(url, {strings})))
                         })
diff --git a/src/page/homepage.js b/src/page/homepage.js
index e40392a1..37dac2c8 100644
--- a/src/page/homepage.js
+++ b/src/page/homepage.js
@@ -16,7 +16,7 @@ import {
 // Page exports
 
 export function writeTargetless({wikiData}) {
-    const { newsData, staticPageData, homepageInfo, wikiInfo } = wikiData;
+    const { newsData, staticPageData, homepageLayout, wikiInfo } = wikiData;
 
     const page = {
         type: 'page',
@@ -40,27 +40,24 @@ export function writeTargetless({wikiData}) {
                 classes: ['top-index'],
                 content: fixWS`
                     <h1>${wikiInfo.name}</h1>
-                    ${homepageInfo.rows.map((row, i) => fixWS`
+                    ${homepageLayout.rows?.map((row, i) => fixWS`
                         <section class="row" style="${getLinkThemeString(row.color)}">
                             <h2>${row.name}</h2>
                             ${row.type === 'albums' && fixWS`
                                 <div class="grid-listing">
                                     ${getAlbumGridHTML({
                                         entries: (
-                                            row.group === 'new-releases' ? getNewReleases(row.groupCount, {wikiData}) :
-                                            row.group === 'new-additions' ? getNewAdditions(row.groupCount, {wikiData}) :
-                                            ((find.group(row.group, {wikiData})?.albums || [])
+                                            row.sourceGroupByRef === 'new-releases' ? getNewReleases(row.countAlbumsFromGroup, {wikiData}) :
+                                            row.sourceGroupByRef === 'new-additions' ? getNewAdditions(row.countAlbumsFromGroup, {wikiData}) :
+                                            ((row.sourceGroup?.albums ?? [])
                                                 .slice()
                                                 .reverse()
-                                                .slice(0, row.groupCount)
+                                                .slice(0, row.countAlbumsFromGroup)
                                                 .map(album => ({item: album})))
-                                        ).concat(row.albums
-                                            .map(album => find.album(album, {wikiData}))
-                                            .map(album => ({item: album}))
-                                        ),
+                                        ).concat(row.sourceAlbums.map(album => ({item: album}))),
                                         lazy: i > 0
                                     })}
-                                    ${row.actions.length && fixWS`
+                                    ${row.actions?.length && fixWS`
                                         <div class="grid-actions">
                                             ${row.actions.map(action => transformInline(action)
                                                 .replace('<a', '<a class="box grid-item"')).join('\n')}
@@ -73,7 +70,7 @@ export function writeTargetless({wikiData}) {
                 `
             },
 
-            sidebarLeft: homepageInfo.sidebar && {
+            sidebarLeft: homepageLayout.sidebarContent && {
                 wide: true,
                 collapse: false,
                 // This is a pretty filthy hack! 8ut otherwise, the [[news]] part
@@ -86,15 +83,15 @@ export function writeTargetless({wikiData}) {
                 //
                 // And no, I will not make [[news]] into part of transformMultiline
                 // (even though that would 8e hilarious).
-                content: (transformMultiline(homepageInfo.sidebar.replace('[[news]]', '__GENERATE_NEWS__'))
+                content: (transformMultiline(homepageLayout.sidebarContent.replace('[[news]]', '__GENERATE_NEWS__'))
                     .replace('<p>__GENERATE_NEWS__</p>', wikiInfo.enableNews ? fixWS`
                         <h1>${strings('homepage.news.title')}</h1>
                         ${newsData.slice(0, 3).map((entry, i) => html.tag('article',
                             {class: ['news-entry', i === 0 && 'first-news-entry']},
                             fixWS`
                                 <h2><time>${strings.count.date(entry.date)}</time> ${link.newsEntry(entry)}</h2>
-                                ${transformMultiline(entry.bodyShort)}
-                                ${entry.bodyShort !== entry.body && link.newsEntry(entry, {
+                                ${transformMultiline(entry.contentShort)}
+                                ${entry.contentShort !== entry.content && link.newsEntry(entry, {
                                     text: strings('homepage.news.entry.viewRest')
                                 })}
                             `)).join('\n')}
@@ -105,15 +102,16 @@ export function writeTargetless({wikiData}) {
                 content: fixWS`
                     <h2 class="dot-between-spans">
                         ${[
-                            link.home('', {text: wikiInfo.shortName, class: 'current', to}),
+                            link.home('', {text: wikiInfo.nameShort, class: 'current', to}),
                             wikiInfo.enableListings &&
                             link.listingIndex('', {text: strings('listingIndex.title'), to}),
                             wikiInfo.enableNews &&
                             link.newsIndex('', {text: strings('newsIndex.title'), to}),
                             wikiInfo.enableFlashesAndGames &&
                             link.flashIndex('', {text: strings('flashIndex.title'), to}),
-                            ...staticPageData.filter(page => page.listed).map(page =>
-                                link.staticPage(page, {text: page.shortName}))
+                            ...(staticPageData
+                                .filter(page => page.showInNavigationBar)
+                                .map(page => link.staticPage(page, {text: page.nameShort})))
                         ].filter(Boolean).map(link => `<span>${link}</span>`).join('\n')}
                     </h2>
                 `
diff --git a/src/page/news.js b/src/page/news.js
index 75ae9e48..8dc65981 100644
--- a/src/page/news.js
+++ b/src/page/news.js
@@ -31,7 +31,7 @@ export function write(entry, {wikiData}) {
                     <div class="long-content">
                         <h1>${strings('newsEntryPage.title', {entry: entry.name})}</h1>
                         <p>${strings('newsEntryPage.published', {date: strings.count.date(entry.date)})}</p>
-                        ${transformMultiline(entry.body)}
+                        ${transformMultiline(entry.content)}
                     </div>
                 `
             },
@@ -68,8 +68,8 @@ export function writeTargetless({wikiData}) {
                         ${newsData.map(entry => fixWS`
                             <article id="${entry.directory}">
                                 <h2><time>${strings.count.date(entry.date)}</time> ${link.newsEntry(entry)}</h2>
-                                ${transformMultiline(entry.bodyShort)}
-                                ${entry.bodyShort !== entry.body && `<p>${link.newsEntry(entry, {
+                                ${transformMultiline(entry.contentShort)}
+                                ${entry.contentShort !== entry.content && `<p>${link.newsEntry(entry, {
                                     text: strings('newsIndex.entry.viewRest')
                                 })}</p>`}
                             </article>
diff --git a/src/upd8.js b/src/upd8.js
index 70aec6c8..4a755abf 100755
--- a/src/upd8.js
+++ b/src/upd8.js
@@ -1213,58 +1213,10 @@ const processStaticPageDocument = makeProcessDocument(StaticPage, {
     }
 });
 
-async function processStaticPageDataFile(file) {
-    let contents;
-    try {
-        contents = await readFile(file, 'utf-8');
-    } catch (error) {
-        if (error.code === 'ENOENT') {
-            return [];
-        } else {
-            return {error: `Could not read ${file} (${error.code}).`};
-        }
-    }
-
-    const contentLines = splitLines(contents);
-    const sections = Array.from(getSections(contentLines));
-
-    return sections.map(section => {
-        const name = getBasicField(section, 'Name');
-        if (!name) {
-            return {error: 'Expected "Name" field!'};
-        }
-
-        const shortName = getBasicField(section, 'Short Name') || name;
-
-        let directory = getBasicField(section, 'Directory');
-        if (!directory) {
-            return {error: 'Expected "Directory" field!'};
-        }
-
-        let content = getMultilineField(section, 'Content');
-        if (!content) {
-            return {error: 'Expected "Content" field!'};
-        }
-
-        let stylesheet = getMultilineField(section, 'Style') || '';
-
-        let listed = getBooleanField(section, 'Listed') ?? true;
-
-        return {
-            name,
-            shortName,
-            directory,
-            content,
-            stylesheet,
-            listed
-        };
-    });
-}
-
 const processWikiInfoDocument = makeProcessDocument(WikiInfo, {
     propertyFieldMapping: {
         name: 'Name',
-        shortName: 'Short Name',
+        nameShort: 'Short Name',
         color: 'Color',
         description: 'Description',
         footerContent: 'Footer Content',
@@ -1772,7 +1724,7 @@ writePage.html = (pageFn, {
         let { title: linkTitle } = cur;
 
         if (cur.toHome) {
-            linkTitle ??= wikiInfo.shortName;
+            linkTitle ??= wikiInfo.nameShort;
         } else if (cur.toCurrentPage) {
             linkTitle ??= title;
         }
@@ -2783,46 +2735,91 @@ async function main() {
     // which require it - they'll expose dynamically computed properties as a
     // result (many of which are required for page HTML generation).
 
-    for (const album of WD.albumData) {
-        album.artistData = WD.artistData;
-        album.groupData = WD.groupData;
-        album.trackData = WD.trackData;
+    function linkDataArrays() {
+        for (const album of WD.albumData) {
+            album.artistData = WD.artistData;
+            album.groupData = WD.groupData;
+            album.trackData = WD.trackData;
 
-        for (const trackGroup of album.trackGroups) {
-            trackGroup.trackData = WD.trackData;
+            for (const trackGroup of album.trackGroups) {
+                trackGroup.trackData = WD.trackData;
+            }
         }
-    }
 
-    for (const track of WD.trackData) {
-        track.albumData = WD.albumData;
-        track.artistData = WD.artistData;
-        track.artTagData = WD.artTagData;
-        track.flashData = WD.flashData;
-        track.trackData = WD.trackData;
-    }
+        for (const track of WD.trackData) {
+            track.albumData = WD.albumData;
+            track.artistData = WD.artistData;
+            track.artTagData = WD.artTagData;
+            track.flashData = WD.flashData;
+            track.trackData = WD.trackData;
+        }
 
-    for (const artist of WD.artistData) {
-        artist.artistData = WD.artistData;
-    }
+        for (const artist of WD.artistData) {
+            artist.artistData = WD.artistData;
+        }
 
-    for (const group of WD.groupData) {
-        group.albumData = WD.albumData;
-    }
+        for (const group of WD.groupData) {
+            group.albumData = WD.albumData;
+            group.groupCategoryData = WD.groupCategoryData;
+        }
+
+        for (const category of WD.groupCategoryData) {
+            category.groupData = WD.groupData;
+        }
 
-    for (const artTag of WD.artTagData) {
-        artTag.albumData = WD.albumData;
-        artTag.trackData = WD.trackData;
+        for (const flash of WD.flashData) {
+            flash.artistData = WD.artistData;
+            flash.trackData = WD.trackData;
+            flash.flashActData = WD.flashActData;
+        }
+
+        for (const act of WD.flashActData) {
+            act.flashData = WD.flashData;
+        }
+
+        for (const artTag of WD.artTagData) {
+            artTag.albumData = WD.albumData;
+            artTag.trackData = WD.trackData;
+        }
+
+        for (const row of WD.homepageLayout.rows) {
+            row.albumData = WD.albumData;
+            row.groupData = WD.groupData;
+        }
     }
 
     // Extra organization stuff needed for listings and the like.
 
-    Object.assign(wikiData, {
-        albumData: sortByDate(WD.albumData.slice()),
-        trackData: sortByDate(WD.trackData.slice())
-    });
+    function sortDataArrays() {
+        Object.assign(wikiData, {
+            albumData: sortByDate(WD.albumData.slice()),
+            trackData: sortByDate(WD.trackData.slice())
+        });
+    }
+
+    // Now post-process data in three steps...
+
+    // 1. Link data arrays so that all essential references between objects are
+    // are complete, so properties (like dates!) are inherited where that's
+    // appropriate.
+    linkDataArrays();
+
+    // 2. Sort data arrays so that they're all in order! This may use properties
+    // which are only available after the initial linking.
+    sortDataArrays();
+
+    // 3. Re-link data arrays, so that every object has the new, sorted
+    // versions. Note that the sorting step deliberately creates new arrays
+    // (mutating slices instead of the original arrays) - this is so that the
+    // object caching system understands that it's working with a new ordering.
+    // We still need to actually provide those updated arrays over again!
+    linkDataArrays();
 
     // 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);
+    // console.log(WD.homepageLayout.rows[0].countAlbumsFromGroup);
+    // console.log(WD.albumData.map(a => `${a.name} (${a.date.toDateString()})`).join('\n'));
+    // console.log(WD.groupData.find(g => g.name === 'Fandom').albums.map(a => `${a.name} (${a.date.toDateString()})`).join('\n'));
     // return;
 
     // Update languages o8ject with the wiki-specified default language!