diff options
Diffstat (limited to 'src/data')
-rw-r--r-- | src/data/cacheable-object.js (renamed from src/data/things/cacheable-object.js) | 0 | ||||
-rw-r--r-- | src/data/composite.js (renamed from src/data/things/composite.js) | 0 | ||||
-rw-r--r-- | src/data/thing.js (renamed from src/data/things/thing.js) | 20 | ||||
-rw-r--r-- | src/data/things/album.js | 158 | ||||
-rw-r--r-- | src/data/things/art-tag.js | 17 | ||||
-rw-r--r-- | src/data/things/artist.js | 23 | ||||
-rw-r--r-- | src/data/things/flash.js | 62 | ||||
-rw-r--r-- | src/data/things/group.js | 21 | ||||
-rw-r--r-- | src/data/things/homepage-layout.js | 39 | ||||
-rw-r--r-- | src/data/things/index.js | 4 | ||||
-rw-r--r-- | src/data/things/language.js | 11 | ||||
-rw-r--r-- | src/data/things/news-entry.js | 28 | ||||
-rw-r--r-- | src/data/things/static-page.js | 27 | ||||
-rw-r--r-- | src/data/things/track.js | 148 | ||||
-rw-r--r-- | src/data/things/wiki-info.js | 40 | ||||
-rw-r--r-- | src/data/validators.js (renamed from src/data/things/validators.js) | 0 | ||||
-rw-r--r-- | src/data/yaml.js | 156 |
17 files changed, 375 insertions, 379 deletions
diff --git a/src/data/things/cacheable-object.js b/src/data/cacheable-object.js index 1e7c7aa8..1e7c7aa8 100644 --- a/src/data/things/cacheable-object.js +++ b/src/data/cacheable-object.js diff --git a/src/data/things/composite.js b/src/data/composite.js index 113f0a4f..113f0a4f 100644 --- a/src/data/things/composite.js +++ b/src/data/composite.js diff --git a/src/data/things/thing.js b/src/data/thing.js index e1f488ee..ae8e71af 100644 --- a/src/data/things/thing.js +++ b/src/data/thing.js @@ -3,10 +3,9 @@ import {inspect} from 'node:util'; +import CacheableObject from '#cacheable-object'; import {colors} from '#cli'; -import CacheableObject from './cacheable-object.js'; - export default class Thing extends CacheableObject { static referenceType = Symbol.for('Thing.referenceType'); static friendlyName = Symbol.for('Thing.friendlyName'); @@ -45,28 +44,21 @@ export default class Thing extends CacheableObject { const superspec = thingClass[Thing.yamlDocumentSpec]; const { - fieldTransformations, - propertyFieldMapping, + fields, ignoredFields, invalidFieldCombinations, ...restOfSubspec } = subspec; - const newFields = - Object.values(subspec.propertyFieldMapping ?? {}); + const newFields = Object.keys(fields ?? {}); return { ...superspec, ...restOfSubspec, - fieldTransformations: { - ...superspec.fieldTransformations, - ...fieldTransformations, - }, - - propertyFieldMapping: { - ...superspec.propertyFieldMapping, - ...propertyFieldMapping, + fields: { + ...superspec.fields ?? {}, + ...fields, }, ignoredFields: diff --git a/src/data/things/album.js b/src/data/things/album.js index 02d34544..3a05ac83 100644 --- a/src/data/things/album.js +++ b/src/data/things/album.js @@ -1,6 +1,9 @@ import {input} from '#composite'; import find from '#find'; +import Thing from '#thing'; import {isDate} from '#validators'; +import {parseAdditionalFiles, parseContributors, parseDate, parseDimensions} + from '#yaml'; import {exposeDependency, exposeUpdateValueOrContinue} from '#composite/control-flow'; @@ -27,14 +30,6 @@ import { import {withTracks, withTrackSections} from '#composite/things/album'; -import { - parseAdditionalFiles, - parseContributors, - parseDimensions, -} from '#yaml'; - -import Thing from './thing.js'; - export class Album extends Thing { static [Thing.referenceType] = 'album'; @@ -205,58 +200,85 @@ export class Album extends Thing { }); static [Thing.yamlDocumentSpec] = { - fieldTransformations: { - 'Artists': parseContributors, - 'Cover Artists': parseContributors, - 'Default Track Cover Artists': parseContributors, - 'Wallpaper Artists': parseContributors, - 'Banner Artists': parseContributors, - - 'Date': (value) => new Date(value), - 'Date Added': (value) => new Date(value), - 'Cover Art Date': (value) => new Date(value), - 'Default Track Cover Art Date': (value) => new Date(value), - - 'Banner Dimensions': parseDimensions, - - 'Additional Files': parseAdditionalFiles, - }, - - propertyFieldMapping: { - name: 'Album', - directory: 'Directory', - date: 'Date', - color: 'Color', - urls: 'URLs', - - hasTrackNumbers: 'Has Track Numbers', - isListedOnHomepage: 'Listed on Homepage', - isListedInGalleries: 'Listed in Galleries', - - coverArtDate: 'Cover Art Date', - trackArtDate: 'Default Track Cover Art Date', - dateAddedToWiki: 'Date Added', - - coverArtFileExtension: 'Cover Art File Extension', - trackCoverArtFileExtension: 'Track Art File Extension', - - wallpaperArtistContribs: 'Wallpaper Artists', - wallpaperStyle: 'Wallpaper Style', - wallpaperFileExtension: 'Wallpaper File Extension', - - bannerArtistContribs: 'Banner Artists', - bannerStyle: 'Banner Style', - bannerFileExtension: 'Banner File Extension', - bannerDimensions: 'Banner Dimensions', - - commentary: 'Commentary', - additionalFiles: 'Additional Files', - - artistContribs: 'Artists', - coverArtistContribs: 'Cover Artists', - trackCoverArtistContribs: 'Default Track Cover Artists', - groups: 'Groups', - artTags: 'Art Tags', + fields: { + 'Album': {property: 'name'}, + 'Directory': {property: 'directory'}, + + 'Date': { + property: 'date', + transform: parseDate, + }, + + 'Color': {property: 'color'}, + 'URLs': {property: 'urls'}, + + 'Has Track Numbers': {property: 'hasTrackNumbers'}, + 'Listed on Homepage': {property: 'isListedOnHomepage'}, + 'Listed in Galleries': {property: 'isListedInGalleries'}, + + 'Cover Art Date': { + property: 'coverArtDate', + transform: parseDate, + }, + + 'Default Track Cover Art Date': { + property: 'trackArtDate', + transform: parseDate, + }, + + 'Date Added': { + property: 'dateAddedToWiki', + transform: parseDate, + }, + + 'Cover Art File Extension': {property: 'coverArtFileExtension'}, + 'Track Art File Extension': {property: 'trackCoverArtFileExtension'}, + + 'Wallpaper Artists': { + property: 'wallpaperArtistContribs', + transform: parseContributors, + }, + + 'Wallpaper Style': {property: 'wallpaperStyle'}, + 'Wallpaper File Extension': {property: 'wallpaperFileExtension'}, + + 'Banner Artists': { + property: 'bannerArtistContribs', + transform: parseContributors, + }, + + 'Banner Style': {property: 'bannerStyle'}, + 'Banner File Extension': {property: 'bannerFileExtension'}, + + 'Banner Dimensions': { + property: 'bannerDimensions', + transform: parseDimensions, + }, + + 'Commentary': {property: 'commentary'}, + + 'Additional Files': { + property: 'additionalFiles', + transform: parseAdditionalFiles, + }, + + 'Artists': { + property: 'artistContribs', + transform: parseContributors, + }, + + 'Cover Artists': { + property: 'coverArtistContribs', + transform: parseContributors, + }, + + 'Default Track Cover Artists': { + property: 'trackCoverArtistContribs', + transform: parseContributors, + }, + + 'Groups': {property: 'groups'}, + 'Art Tags': {property: 'artTags'}, }, ignoredFields: ['Review Points'], @@ -274,14 +296,14 @@ export class TrackSectionHelper extends Thing { }) static [Thing.yamlDocumentSpec] = { - fieldTransformations: { - 'Date Originally Released': (value) => new Date(value), - }, - - propertyFieldMapping: { - name: 'Section', - color: 'Color', - dateOriginallyReleased: 'Date Originally Released', + fields: { + 'Section': {property: 'name'}, + 'Color': {property: 'color'}, + + 'Date Originally Released': { + property: 'dateOriginallyReleased', + transform: parseDate, + }, }, }; } diff --git a/src/data/things/art-tag.js b/src/data/things/art-tag.js index c0b4a6d6..af6677f0 100644 --- a/src/data/things/art-tag.js +++ b/src/data/things/art-tag.js @@ -1,6 +1,7 @@ import {input} from '#composite'; -import {sortAlbumsTracksChronologically} from '#wiki-data'; +import Thing from '#thing'; import {isName} from '#validators'; +import {sortAlbumsTracksChronologically} from '#wiki-data'; import {exposeUpdateValueOrContinue} from '#composite/control-flow'; @@ -12,8 +13,6 @@ import { wikiData, } from '#composite/wiki-properties'; -import Thing from './thing.js'; - export class ArtTag extends Thing { static [Thing.referenceType] = 'tag'; static [Thing.friendlyName] = `Art Tag`; @@ -65,13 +64,13 @@ export class ArtTag extends Thing { }); static [Thing.yamlDocumentSpec] = { - propertyFieldMapping: { - name: 'Tag', - nameShort: 'Short Name', - directory: 'Directory', + fields: { + 'Tag': {property: 'name'}, + 'Short Name': {property: 'nameShort'}, + 'Directory': {property: 'directory'}, - color: 'Color', - isContentWarning: 'Is CW', + 'Color': {property: 'color'}, + 'Is CW': {property: 'isContentWarning'}, }, }; } diff --git a/src/data/things/artist.js b/src/data/things/artist.js index 42090557..502510a8 100644 --- a/src/data/things/artist.js +++ b/src/data/things/artist.js @@ -1,8 +1,11 @@ import {input} from '#composite'; import find from '#find'; import {unique} from '#sugar'; +import Thing from '#thing'; import {isName, validateArrayItems} from '#validators'; +import {withReverseContributionList} from '#composite/wiki-data'; + import { contentString, directory, @@ -16,10 +19,6 @@ import { wikiData, } from '#composite/wiki-properties'; -import {withReverseContributionList} from '#composite/wiki-data'; - -import Thing from './thing.js'; - export class Artist extends Thing { static [Thing.referenceType] = 'artist'; @@ -242,16 +241,16 @@ export class Artist extends Thing { }); static [Thing.yamlDocumentSpec] = { - propertyFieldMapping: { - name: 'Artist', - directory: 'Directory', - urls: 'URLs', - contextNotes: 'Context Notes', + fields: { + 'Artist': {property: 'name'}, + 'Directory': {property: 'directory'}, + 'URLs': {property: 'urls'}, + 'Context Notes': {property: 'contextNotes'}, - hasAvatar: 'Has Avatar', - avatarFileExtension: 'Avatar File Extension', + 'Has Avatar': {property: 'hasAvatar'}, + 'Avatar File Extension': {property: 'avatarFileExtension'}, - aliasNames: 'Aliases', + 'Aliases': {property: 'aliasNames'}, }, ignoredFields: ['Dead URLs', 'Review Points'], diff --git a/src/data/things/flash.js b/src/data/things/flash.js index d7e8bb46..7e859bfa 100644 --- a/src/data/things/flash.js +++ b/src/data/things/flash.js @@ -1,13 +1,8 @@ import {input} from '#composite'; import find from '#find'; - -import { - anyOf, - isColor, - isDirectory, - isNumber, - isString, -} from '#validators'; +import Thing from '#thing'; +import {anyOf, isColor, isDirectory, isNumber, isString} from '#validators'; +import {parseDate, parseContributors} from '#yaml'; import {exposeDependency, exposeUpdateValueOrContinue} from '#composite/control-flow'; @@ -28,10 +23,6 @@ import { import {withFlashAct} from '#composite/things/flash'; -import {parseContributors} from '#yaml'; - -import Thing from './thing.js'; - export class Flash extends Thing { static [Thing.referenceType] = 'flash'; @@ -137,24 +128,25 @@ export class Flash extends Thing { }); static [Thing.yamlDocumentSpec] = { - fieldTransformations: { - 'Date': (value) => new Date(value), - - 'Contributors': parseContributors, - }, - - propertyFieldMapping: { - name: 'Flash', - directory: 'Directory', - page: 'Page', - color: 'Color', - urls: 'URLs', + fields: { + 'Flash': {property: 'name'}, + 'Directory': {property: 'directory'}, + 'Page': {property: 'page'}, + 'Color': {property: 'color'}, + 'URLs': {property: 'urls'}, + + 'Date': { + property: 'date', + transform: parseDate, + }, - date: 'Date', - coverArtFileExtension: 'Cover Art File Extension', + 'Cover Art File Extension': {property: 'coverArtFileExtension'}, - featuredTracks: 'Featured Tracks', - contributorContribs: 'Contributors', + 'Featured Tracks': {property: 'featuredTracks'}, + 'Contributors': { + property: 'contributorContribs', + transform: parseContributors, + }, }, ignoredFields: ['Review Points'], @@ -199,15 +191,15 @@ export class FlashAct extends Thing { }); static [Thing.yamlDocumentSpec] = { - propertyFieldMapping: { - name: 'Act', - directory: 'Directory', + fields: { + 'Act': {property: 'name'}, + 'Directory': {property: 'directory'}, - color: 'Color', - listTerminology: 'List Terminology', + 'Color': {property: 'color'}, + 'List Terminology': {property: 'listTerminology'}, - jump: 'Jump', - jumpColor: 'Jump Color', + 'Jump': {property: 'jump'}, + 'Jump Color': {property: 'jumpColor'}, }, ignoredFields: ['Review Points'], diff --git a/src/data/things/group.js b/src/data/things/group.js index a9708fb4..fe04dfaa 100644 --- a/src/data/things/group.js +++ b/src/data/things/group.js @@ -1,5 +1,6 @@ import {input} from '#composite'; import find from '#find'; +import Thing from '#thing'; import { color, @@ -11,8 +12,6 @@ import { wikiData, } from '#composite/wiki-properties'; -import Thing from './thing.js'; - export class Group extends Thing { static [Thing.referenceType] = 'group'; @@ -87,13 +86,13 @@ export class Group extends Thing { }); static [Thing.yamlDocumentSpec] = { - propertyFieldMapping: { - name: 'Group', - directory: 'Directory', - description: 'Description', - urls: 'URLs', + fields: { + 'Group': {property: 'name'}, + 'Directory': {property: 'directory'}, + 'Description': {property: 'description'}, + 'URLs': {property: 'urls'}, - featuredAlbums: 'Featured Albums', + 'Featured Albums': {property: 'featuredAlbums'}, }, ignoredFields: ['Review Points'], @@ -126,9 +125,9 @@ export class GroupCategory extends Thing { }); static [Thing.yamlDocumentSpec] = { - propertyFieldMapping: { - name: 'Category', - color: 'Color', + fields: { + 'Category': {property: 'name'}, + 'Color': {property: 'color'}, }, }; } diff --git a/src/data/things/homepage-layout.js b/src/data/things/homepage-layout.js index b4fb97db..bd0970fe 100644 --- a/src/data/things/homepage-layout.js +++ b/src/data/things/homepage-layout.js @@ -1,5 +1,6 @@ import {input} from '#composite'; import find from '#find'; +import Thing from '#thing'; import { anyOf, @@ -14,16 +15,8 @@ import { import {exposeDependency} from '#composite/control-flow'; import {withResolvedReference} from '#composite/wiki-data'; - -import { - color, - contentString, - name, - referenceList, - wikiData, -} from '#composite/wiki-properties'; - -import Thing from './thing.js'; +import {color, contentString, name, referenceList, wikiData} + from '#composite/wiki-properties'; export class HomepageLayout extends Thing { static [Thing.friendlyName] = `Homepage Layout`; @@ -48,9 +41,9 @@ export class HomepageLayout extends Thing { }); static [Thing.yamlDocumentSpec] = { - propertyFieldMapping: { - sidebarContent: 'Sidebar Content', - navbarLinks: 'Navbar Links', + fields: { + 'Sidebar Content': {property: 'sidebarContent'}, + 'Navbar Links': {property: 'navbarLinks'}, }, ignoredFields: ['Homepage'], @@ -93,10 +86,10 @@ export class HomepageLayoutRow extends Thing { }); static [Thing.yamlDocumentSpec] = { - propertyFieldMapping: { - name: 'Row', - color: 'Color', - type: 'Type', + fields: { + 'Row': {property: 'name'}, + 'Color': {property: 'color'}, + 'Type': {property: 'type'}, }, }; } @@ -181,12 +174,12 @@ export class HomepageLayoutAlbumsRow extends HomepageLayoutRow { }); static [Thing.yamlDocumentSpec] = Thing.extendDocumentSpec(HomepageLayoutRow, { - propertyFieldMapping: { - displayStyle: 'Display Style', - sourceGroup: 'Group', - countAlbumsFromGroup: 'Count', - sourceAlbums: 'Albums', - actionLinks: 'Actions', + fields: { + 'Display Style': {property: 'displayStyle'}, + 'Group': {property: 'sourceGroup'}, + 'Count': {property: 'countAlbumsFromGroup'}, + 'Albums': {property: 'sourceAlbums'}, + 'Actions': {property: 'actionLinks'}, }, }); } diff --git a/src/data/things/index.js b/src/data/things/index.js index d1143b0a..9a36eaae 100644 --- a/src/data/things/index.js +++ b/src/data/things/index.js @@ -6,7 +6,7 @@ import {compositeFrom} from '#composite'; import * as serialize from '#serialize'; import {openAggregate, showAggregate} from '#sugar'; -import Thing from './thing.js'; +import Thing from '#thing'; import * as albumClasses from './album.js'; import * as artTagClasses from './art-tag.js'; @@ -20,8 +20,6 @@ import * as staticPageClasses from './static-page.js'; import * as trackClasses from './track.js'; import * as wikiInfoClasses from './wiki-info.js'; -export {default as Thing} from './thing.js'; - const allClassLists = { 'album.js': albumClasses, 'art-tag.js': artTagClasses, diff --git a/src/data/things/language.js b/src/data/things/language.js index b7841ace..c576a316 100644 --- a/src/data/things/language.js +++ b/src/data/things/language.js @@ -1,8 +1,10 @@ import { Temporal, toTemporalInstant } from '@js-temporal/polyfill'; +import CacheableObject from '#cacheable-object'; import * as html from '#html'; import {empty, withAggregate} from '#sugar'; import {isLanguageCode} from '#validators'; +import Thing from '#thing'; import { getExternalLinkStringOfStyleFromDescriptors, @@ -12,14 +14,7 @@ import { isExternalLinkStyle, } from '#external-links'; -import { - externalFunction, - flag, - name, -} from '#composite/wiki-properties'; - -import CacheableObject from './cacheable-object.js'; -import Thing from './thing.js'; +import {externalFunction, flag, name} from '#composite/wiki-properties'; export class Language extends Thing { static [Thing.getPropertyDescriptors] = () => ({ diff --git a/src/data/things/news-entry.js b/src/data/things/news-entry.js index 06dad629..5a022449 100644 --- a/src/data/things/news-entry.js +++ b/src/data/things/news-entry.js @@ -1,11 +1,8 @@ -import { - contentString, - directory, - name, - simpleDate, -} from '#composite/wiki-properties'; +import Thing from '#thing'; +import {parseDate} from '#yaml'; -import Thing from './thing.js'; +import {contentString, directory, name, simpleDate} + from '#composite/wiki-properties'; export class NewsEntry extends Thing { static [Thing.referenceType] = 'news-entry'; @@ -34,15 +31,16 @@ export class NewsEntry extends Thing { }); static [Thing.yamlDocumentSpec] = { - fieldTransformations: { - 'Date': (value) => new Date(value), - }, + fields: { + 'Name': {property: 'name'}, + 'Directory': {property: 'directory'}, + + 'Date': { + property: 'date', + transform: parseDate, + }, - propertyFieldMapping: { - name: 'Name', - directory: 'Directory', - date: 'Date', - content: 'Content', + 'Content': {property: 'content'}, }, }; } diff --git a/src/data/things/static-page.js b/src/data/things/static-page.js index 00c0b09c..7f8b7c91 100644 --- a/src/data/things/static-page.js +++ b/src/data/things/static-page.js @@ -1,13 +1,8 @@ +import Thing from '#thing'; import {isName} from '#validators'; -import { - contentString, - directory, - name, - simpleString, -} from '#composite/wiki-properties'; - -import Thing from './thing.js'; +import {contentString, directory, name, simpleString} + from '#composite/wiki-properties'; export class StaticPage extends Thing { static [Thing.referenceType] = 'static'; @@ -35,14 +30,14 @@ export class StaticPage extends Thing { }); static [Thing.yamlDocumentSpec] = { - propertyFieldMapping: { - name: 'Name', - nameShort: 'Short Name', - directory: 'Directory', - - stylesheet: 'Style', - script: 'Script', - content: 'Content', + fields: { + 'Name': {property: 'name'}, + 'Short Name': {property: 'nameShort'}, + 'Directory': {property: 'directory'}, + + 'Style': {property: 'stylesheet'}, + 'Script': {property: 'script'}, + 'Content': {property: 'content'}, }, ignoredFields: ['Review Points'], diff --git a/src/data/things/track.js b/src/data/things/track.js index 3621510b..9f44bd8d 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -1,15 +1,20 @@ import {inspect} from 'node:util'; +import CacheableObject from '#cacheable-object'; import {colors} from '#cli'; import {input} from '#composite'; import find from '#find'; +import Thing from '#thing'; +import {isColor, isContributionList, isDate, isFileExtension} + from '#validators'; import { - isColor, - isContributionList, - isDate, - isFileExtension, -} from '#validators'; + parseAdditionalFiles, + parseAdditionalNames, + parseContributors, + parseDate, + parseDuration, +} from '#yaml'; import {withPropertyFromObject} from '#composite/data'; import {withResolvedContribs} from '#composite/wiki-data'; @@ -55,16 +60,6 @@ import { withPropertyFromAlbum, } from '#composite/things/track'; -import { - parseAdditionalFiles, - parseAdditionalNames, - parseContributors, - parseDuration, -} from '#yaml'; - -import CacheableObject from './cacheable-object.js'; -import Thing from './thing.js'; - export class Track extends Thing { static [Thing.referenceType] = 'track'; @@ -340,54 +335,83 @@ export class Track extends Thing { }); static [Thing.yamlDocumentSpec] = { - fieldTransformations: { - 'Additional Names': parseAdditionalNames, - 'Duration': parseDuration, - - 'Date First Released': (value) => new Date(value), - 'Cover Art Date': (value) => new Date(value), - 'Has Cover Art': (value) => - (value === true ? false : - value === false ? true : - value), - - 'Artists': parseContributors, - 'Contributors': parseContributors, - 'Cover Artists': parseContributors, - - 'Additional Files': parseAdditionalFiles, - 'Sheet Music Files': parseAdditionalFiles, - 'MIDI Project Files': parseAdditionalFiles, - }, + fields: { + 'Track': {property: 'name'}, + 'Directory': {property: 'directory'}, + + 'Additional Names': { + property: 'additionalNames', + transform: parseAdditionalNames, + }, + + 'Duration': { + property: 'duration', + transform: parseDuration, + }, + + 'Color': {property: 'color'}, + 'URLs': {property: 'urls'}, + + 'Date First Released': { + property: 'dateFirstReleased', + transform: parseDate, + }, + + 'Cover Art Date': { + property: 'coverArtDate', + transform: parseDate, + }, + + 'Cover Art File Extension': {property: 'coverArtFileExtension'}, + + 'Has Cover Art': { + property: 'disableUniqueCoverArt', + transform: value => + (typeof value === 'boolean' + ? !value + : value), + }, + + 'Always Reference By Directory': {property: 'alwaysReferenceByDirectory'}, + + 'Lyrics': {property: 'lyrics'}, + 'Commentary': {property: 'commentary'}, + + 'Additional Files': { + property: 'additionalFiles', + transform: parseAdditionalFiles, + }, + + 'Sheet Music Files': { + property: 'sheetMusicFiles', + transform: parseAdditionalFiles, + }, + + 'MIDI Project Files': { + property: 'midiProjectFiles', + transform: parseAdditionalFiles, + }, + + 'Originally Released As': {property: 'originalReleaseTrack'}, + 'Referenced Tracks': {property: 'referencedTracks'}, + 'Sampled Tracks': {property: 'sampledTracks'}, + + 'Artists': { + property: 'artistContribs', + transform: parseContributors, + }, + + 'Contributors': { + property: 'contributorContribs', + transform: parseContributors, + }, + + 'Cover Artists': { + property: 'coverArtistContribs', + transform: parseContributors, + }, - propertyFieldMapping: { - name: 'Track', - directory: 'Directory', - additionalNames: 'Additional Names', - duration: 'Duration', - color: 'Color', - urls: 'URLs', - - dateFirstReleased: 'Date First Released', - coverArtDate: 'Cover Art Date', - coverArtFileExtension: 'Cover Art File Extension', - disableUniqueCoverArt: 'Has Cover Art', // This gets transformed to flip true/false. - - alwaysReferenceByDirectory: 'Always Reference By Directory', - - lyrics: 'Lyrics', - commentary: 'Commentary', - additionalFiles: 'Additional Files', - sheetMusicFiles: 'Sheet Music Files', - midiProjectFiles: 'MIDI Project Files', - - originalReleaseTrack: 'Originally Released As', - referencedTracks: 'Referenced Tracks', - sampledTracks: 'Sampled Tracks', - artistContribs: 'Artists', - contributorContribs: 'Contributors', - coverArtistContribs: 'Cover Artists', - artTags: 'Art Tags', + 'Art Tags': {property: 'artTags'}, }, ignoredFields: ['Review Points'], diff --git a/src/data/things/wiki-info.js b/src/data/things/wiki-info.js index 80793550..fd6c239c 100644 --- a/src/data/things/wiki-info.js +++ b/src/data/things/wiki-info.js @@ -1,16 +1,10 @@ import {input} from '#composite'; import find from '#find'; +import Thing from '#thing'; import {isColor, isLanguageCode, isName, isURL} from '#validators'; -import { - contentString, - flag, - name, - referenceList, - wikiData, -} from '#composite/wiki-properties'; - -import Thing from './thing.js'; +import {contentString, flag, name, referenceList, wikiData} + from '#composite/wiki-properties'; export class WikiInfo extends Thing { static [Thing.friendlyName] = `Wiki Info`; @@ -76,20 +70,20 @@ export class WikiInfo extends Thing { }); static [Thing.yamlDocumentSpec] = { - propertyFieldMapping: { - name: 'Name', - nameShort: 'Short Name', - color: 'Color', - description: 'Description', - footerContent: 'Footer Content', - defaultLanguage: 'Default Language', - canonicalBase: 'Canonical Base', - divideTrackListsByGroups: 'Divide Track Lists By Groups', - enableFlashesAndGames: 'Enable Flashes & Games', - enableListings: 'Enable Listings', - enableNews: 'Enable News', - enableArtTagUI: 'Enable Art Tag UI', - enableGroupUI: 'Enable Group UI', + fields: { + 'Name': {property: 'name'}, + 'Short Name': {property: 'nameShort'}, + 'Color': {property: 'color'}, + 'Description': {property: 'description'}, + 'Footer Content': {property: 'footerContent'}, + 'Default Language': {property: 'defaultLanguage'}, + 'Canonical Base': {property: 'canonicalBase'}, + 'Divide Track Lists By Groups': {property: 'divideTrackListsByGroups'}, + 'Enable Flashes & Games': {property: 'enableFlashesAndGames'}, + 'Enable Listings': {property: 'enableListings'}, + 'Enable News': {property: 'enableNews'}, + 'Enable Art Tag UI': {property: 'enableArtTagUI'}, + 'Enable Group UI': {property: 'enableGroupUI'}, }, }; } diff --git a/src/data/things/validators.js b/src/data/validators.js index efe76fe0..efe76fe0 100644 --- a/src/data/things/validators.js +++ b/src/data/validators.js diff --git a/src/data/yaml.js b/src/data/yaml.js index a232970b..19f56292 100644 --- a/src/data/yaml.js +++ b/src/data/yaml.js @@ -12,8 +12,8 @@ import CacheableObject, {CacheableObjectPropertyValueError} import {colors, ENABLE_COLOR, logInfo, logWarn} from '#cli'; import find, {bindFind} from '#find'; import {traverse} from '#node-utils'; - -import T, {Thing} from '#things'; +import Thing from '#thing'; +import T from '#things'; import { annotateErrorWithFile, @@ -28,6 +28,7 @@ import { showAggregate, typeAppearance, withAggregate, + withEntries, } from '#sugar'; import { @@ -65,76 +66,68 @@ export const DATA_STATIC_PAGE_DIRECTORY = 'static-page'; // makeProcessDocument is a factory function: the returned function will take a // document and apply the configuration passed to makeProcessDocument in order // to construct a Thing subclass. -function makeProcessDocument( - thingConstructor, - { - // Optional early step for transforming field values before providing them - // to the Thing's update() method. This is useful when the input format - // (i.e. values in the document) differ from the format the actual Thing - // expects. - // - // Each key and value are a field name (not an update() property) and a - // function which takes the value for that field and returns the value which - // will be passed on to update(). - // - fieldTransformations = {}, - - // Mapping of Thing.update() source properties to field names. - // - // Note this is property -> field, not field -> property. This is a - // shorthand convenience because properties are generally typical - // camel-cased JS properties, while fields may contain whitespace and be - // more easily represented as quoted strings. - // - propertyFieldMapping, - - // Completely ignored fields. These won't throw an unknown field error if - // they're present in a document, but they won't be used for Thing property - // generation, either. Useful for stuff that's present in data files but not - // yet implemented as part of a Thing's data model! - // - ignoredFields = [], - - // List of fields which are invalid when coexisting in a document. - // Data objects are generally allowing with regards to what properties go - // together, allowing for properties to be set separately from each other - // instead of complaining about invalid or unused-data cases. But it's - // useful to see these kinds of errors when actually validating YAML files! - // - // Each item of this array should itself be an object with a descriptive - // message and a list of fields. Of those fields, none should ever coexist - // with any other. For example: - // - // [ - // {message: '...', fields: ['A', 'B', 'C']}, - // {message: '...', fields: ['C', 'D']}, - // ] - // - // ...means A can't coexist with B or C, B can't coexist with A or C, and - // C can't coexist iwth A, B, or D - but it's okay for D to coexist with - // A or B. - // - invalidFieldCombinations = [], - } -) { +// +function makeProcessDocument(thingConstructor, { + // The bulk of configuration happens here in the spec's `fields` property. + // Each key is a field that's expected on the source document; fields that + // don't match one of these keys will cause an error. Values are object + // entries describing what to do with the field. + // + // A field entry's `property` tells what property the value for this field + // will be put into, on the respective Thing (subclass) instance. + // + // A field entry's `transform` optionally allows converting the raw value in + // YAML into some other format before providing setting it on the Thing + // instance. + // + fields: fieldSpecs = {}, + + // Completely ignored fields. These won't throw an unknown field error if + // they're present in a document, but they won't be used for Thing property + // generation, either. Useful for stuff that's present in data files but not + // yet implemented as part of a Thing's data model! + // + ignoredFields = [], + + // List of fields which are invalid when coexisting in a document. + // Data objects are generally allowing with regards to what properties go + // together, allowing for properties to be set separately from each other + // instead of complaining about invalid or unused-data cases. But it's + // useful to see these kinds of errors when actually validating YAML files! + // + // Each item of this array should itself be an object with a descriptive + // message and a list of fields. Of those fields, none should ever coexist + // with any other. For example: + // + // [ + // {message: '...', fields: ['A', 'B', 'C']}, + // {message: '...', fields: ['C', 'D']}, + // ] + // + // ...means A can't coexist with B or C, B can't coexist with A or C, and + // C can't coexist iwth A, B, or D - but it's okay for D to coexist with + // A or B. + // + invalidFieldCombinations = [], +}) { if (!thingConstructor) { throw new Error(`Missing Thing class`); } - if (!propertyFieldMapping) { - throw new Error(`Expected propertyFieldMapping to be provided`); + if (!fieldSpecs) { + throw new Error(`Expected fields to be provided`); } - const knownFields = Object.values(propertyFieldMapping); + const knownFields = Object.keys(fieldSpecs); - // Invert the property-field mapping, since it'll come in handy for - // assigning update() source values later. - const fieldPropertyMapping = Object.fromEntries( - Object.entries(propertyFieldMapping) - .map(([property, field]) => [field, property])); + const propertyToField = + withEntries(fieldSpecs, entries => entries + .map(([field, {property}]) => [property, field])); + // TODO: Is this function even necessary?? + // Aren't we doing basically the same work in the function it's decorating??? const decorateErrorWithName = (fn) => { - const nameField = propertyFieldMapping['name']; + const nameField = propertyToField.name; if (!nameField) return fn; return (document) => { @@ -151,7 +144,7 @@ function makeProcessDocument( }; return decorateErrorWithName((document) => { - const nameField = propertyFieldMapping['name']; + const nameField = propertyToField.name; const namePart = (nameField ? (document[nameField] @@ -192,7 +185,8 @@ function makeProcessDocument( const fieldCombinationErrors = []; for (const {message, fields} of invalidFieldCombinations) { - const fieldsPresent = presentFields.filter(field => fields.includes(field)); + const fieldsPresent = + presentFields.filter(field => fields.includes(field)); if (fieldsPresent.length >= 2) { const filteredDocument = @@ -201,7 +195,8 @@ function makeProcessDocument( fieldsPresent, {preserveOriginalOrder: true}); - fieldCombinationErrors.push(new FieldCombinationError(filteredDocument, message)); + fieldCombinationErrors.push( + new FieldCombinationError(filteredDocument, message)); for (const field of Object.keys(filteredDocument)) { skippedFields.add(field); @@ -220,8 +215,8 @@ function makeProcessDocument( // This variable would like to certify itself as "not into capitalism". let propertyValue = - (Object.hasOwn(fieldTransformations, field) - ? fieldTransformations[field](documentValue) + (fieldSpecs[field].transform + ? fieldSpecs[field].transform(documentValue) : documentValue); // Completely blank items in a YAML list are read as null. @@ -247,19 +242,13 @@ function makeProcessDocument( fieldValues[field] = propertyValue; } - const sourceProperties = {}; - - for (const [field, value] of Object.entries(fieldValues)) { - const property = fieldPropertyMapping[field]; - sourceProperties[property] = value; - } - const thing = Reflect.construct(thingConstructor, []); const fieldValueErrors = []; - for (const [property, value] of Object.entries(sourceProperties)) { - const field = propertyFieldMapping[property]; + for (const [field, value] of Object.entries(fieldValues)) { + const {property} = fieldSpecs[field]; + try { thing[property] = value; } catch (caughtError) { @@ -382,6 +371,10 @@ export class SkippedFieldsSummaryError extends Error { // --> Utilities shared across document parsing functions +export function parseDate(date) { + return new Date(date); +} + export function parseDuration(string) { if (typeof string !== 'string') { return string; @@ -779,7 +772,7 @@ export const getDataSteps = () => [ case 'albums': return T.HomepageLayoutAlbumsRow; default: - throw new TypeError(`No processDocument function for row type ${type}!`); + throw new TypeError(`No processDocument function for row type ${document['Type']}!`); } }, @@ -1574,9 +1567,12 @@ export function filterReferenceErrors(wikiData) { return false; }, fn); + const {fields} = thing.constructor[Thing.yamlDocumentSpec]; + const field = - thing.constructor[Thing.yamlDocumentSpec] - .propertyFieldMapping[property]; + Object.entries(fields ?? {}) + .find(([field, fieldSpec]) => fieldSpec.property === property) + ?.[0]; const fieldPropertyMessage = (field |