diff options
Diffstat (limited to 'src/data')
-rw-r--r-- | src/data/cacheable-object.js | 33 | ||||
-rw-r--r-- | src/data/patches.js | 45 | ||||
-rw-r--r-- | src/data/serialize.js | 6 | ||||
-rw-r--r-- | src/data/things.js | 691 | ||||
-rw-r--r-- | src/data/validators.js | 42 | ||||
-rw-r--r-- | src/data/yaml.js | 595 |
6 files changed, 702 insertions, 710 deletions
diff --git a/src/data/cacheable-object.js b/src/data/cacheable-object.js index 47281939..fe1817f6 100644 --- a/src/data/cacheable-object.js +++ b/src/data/cacheable-object.js @@ -1,5 +1,7 @@ -// @format -// +/** + * @format + */ + // Generally extendable class for caching properties and handling dependencies, // with a few key properties: // @@ -76,16 +78,16 @@ // function, which provides a mapping of exposed property names to whether // or not their dependencies are yet met. -import { color, ENABLE_COLOR } from "../util/cli.js"; +import {color, ENABLE_COLOR} from '../util/cli.js'; -import { inspect as nodeInspect } from "util"; +import {inspect as nodeInspect} from 'util'; function inspect(value) { - return nodeInspect(value, { colors: ENABLE_COLOR }); + return nodeInspect(value, {colors: ENABLE_COLOR}); } export default class CacheableObject { - static instance = Symbol("CacheableObject `this` instance"); + static instance = Symbol('CacheableObject `this` instance'); #propertyUpdateValues = Object.create(null); #propertyUpdateCacheInvalidators = Object.create(null); @@ -109,7 +111,7 @@ export default class CacheableObject { return new Proxy(this, { get: (obj, key) => { if (!Object.hasOwn(obj, key)) { - if (key !== "constructor") { + if (key !== 'constructor') { CacheableObject._invalidAccesses.add( `(${obj.constructor.name}).${key}` ); @@ -125,7 +127,7 @@ export default class CacheableObject { for (const [property, descriptor] of Object.entries( this.constructor.propertyDescriptors )) { - const { flags, update } = descriptor; + const {flags, update} = descriptor; if (!flags.update) { continue; @@ -149,7 +151,7 @@ export default class CacheableObject { for (const [property, descriptor] of Object.entries( this.constructor.propertyDescriptors )) { - const { flags } = descriptor; + const {flags} = descriptor; const definition = { configurable: false, @@ -173,9 +175,8 @@ export default class CacheableObject { } #getUpdateObjectDefinitionSetterFunction(property) { - const { update } = this.#getPropertyDescriptor(property); + const {update} = this.#getPropertyDescriptor(property); const validate = update?.validate; - const allowNull = update?.allowNull; return (newValue) => { const oldValue = this.#propertyUpdateValues[property]; @@ -209,10 +210,6 @@ export default class CacheableObject { }; } - #getUpdatePropertyValidateFunction(property) { - const descriptor = this.#getPropertyDescriptor(property); - } - #getPropertyDescriptor(property) { return this.constructor.propertyDescriptors[property]; } @@ -225,7 +222,7 @@ export default class CacheableObject { } #getExposeObjectDefinitionGetterFunction(property) { - const { flags } = this.#getPropertyDescriptor(property); + const {flags} = this.#getPropertyDescriptor(property); const compute = this.#getExposeComputeFunction(property); if (compute) { @@ -248,7 +245,7 @@ export default class CacheableObject { } #getExposeComputeFunction(property) { - const { flags, expose } = this.#getPropertyDescriptor(property); + const {flags, expose} = this.#getPropertyDescriptor(property); const compute = expose?.compute; const transform = expose?.transform; @@ -286,7 +283,7 @@ export default class CacheableObject { } #getExposeCheckCacheValidFunction(property) { - const { flags, expose } = this.#getPropertyDescriptor(property); + const {flags, expose} = this.#getPropertyDescriptor(property); let valid = false; diff --git a/src/data/patches.js b/src/data/patches.js index 937fb099..dc757fa9 100644 --- a/src/data/patches.js +++ b/src/data/patches.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // --> Patch export class Patch { @@ -76,7 +76,7 @@ export class Patch { inputs[inputName] = [Patch.INPUT_AVAILABLE, output[1]]; break; } - throw new Error("Unreachable"); + throw new Error('Unreachable'); } case Patch.INPUT_MANAGED_CONNECTION: { @@ -169,7 +169,7 @@ export class PatchManager extends Patch { return false; } - for (const inputNames of patch.inputNames) { + for (const inputName of patch.inputNames) { const input = patch.inputs[inputName]; if (input[0] === Patch.INPUT_MANAGED_CONNECTION) { this.dropManagedInput(input[1]); @@ -202,7 +202,7 @@ export class PatchManager extends Patch { } dropManagedInput(identifier) { - return delete this.managedInputs[key]; + return delete this.managedInputs[identifier]; } getManagedInput(identifier) { @@ -213,7 +213,7 @@ export class PatchManager extends Patch { return this.computeManagedInput(patch, outputName, memory); } - computeManagedInput(patch, outputName, memory) { + computeManagedInput(patch, outputName) { // Override this function in subclasses to alter behavior of the "wire" // used for connecting patches. @@ -255,7 +255,7 @@ export class PatchManager extends Patch { } class PatchManagerExternalInputPatch extends Patch { - constructor({ manager, ...rest }) { + constructor({manager, ...rest}) { super({ manager, inputNames: manager.inputNames, @@ -284,7 +284,7 @@ class PatchManagerExternalInputPatch extends Patch { } class PatchManagerExternalOutputPatch extends Patch { - constructor({ manager, ...rest }) { + constructor({manager, ...rest}) { super({ manager, inputNames: manager.outputNames, @@ -312,7 +312,6 @@ class PatchManagerExternalOutputPatch extends Patch { const caches = Symbol(); const common = Symbol(); -const hsmusic = Symbol(); Patch[caches] = { WireCachedPatchManager: class extends PatchManager { @@ -327,8 +326,8 @@ Patch[caches] = { computeManagedInput(patch, outputName, memory) { let cache = true; - const { previousInputs } = memory; - const { inputs } = patch; + const {previousInputs} = memory; + const {inputs} = patch; if (memory.previousInputs) { for (const inputName of patch.inputNames) { // TODO: This doesn't account for connections whose values @@ -348,7 +347,7 @@ Patch[caches] = { const outputs = patch.computeOutputs(); memory.previousOutputs = outputs; - memory.previousInputs = { ...inputs }; + memory.previousInputs = {...inputs}; return outputs[outputName]; } }, @@ -356,8 +355,8 @@ Patch[caches] = { Patch[common] = { Stringify: class extends Patch { - static inputNames = ["value"]; - static outputNames = ["value"]; + static inputNames = ['value']; + static outputNames = ['value']; compute(inputs, outputs) { if (inputs.value[0] === Patch.INPUT_AVAILABLE) { @@ -369,8 +368,8 @@ Patch[common] = { }, Echo: class extends Patch { - static inputNames = ["value"]; - static outputNames = ["value"]; + static inputNames = ['value']; + static outputNames = ['value']; compute(inputs, outputs) { if (inputs.value[0] === Patch.INPUT_AVAILABLE) { @@ -383,16 +382,16 @@ Patch[common] = { }; const PM = new Patch[caches].WireCachedPatchManager({ - inputNames: ["externalInput"], - outputNames: ["externalOutput"], + inputNames: ['externalInput'], + outputNames: ['externalOutput'], }); -const P1 = new Patch[common].Stringify({ manager: PM }); -const P2 = new Patch[common].Echo({ manager: PM }); +const P1 = new Patch[common].Stringify({manager: PM}); +const P2 = new Patch[common].Echo({manager: PM}); -PM.addExternalInput(P1, "value", "externalInput"); -PM.addManagedInput(P2, "value", P1, "value"); -PM.setExternalOutput("externalOutput", P2, "value"); +PM.addExternalInput(P1, 'value', 'externalInput'); +PM.addManagedInput(P2, 'value', P1, 'value'); +PM.setExternalOutput('externalOutput', P2, 'value'); PM.inputs.externalInput = [Patch.INPUT_CONSTANT, 123]; console.log(PM.computeOutputs()); diff --git a/src/data/serialize.js b/src/data/serialize.js index 13b20e13..a4206fd0 100644 --- a/src/data/serialize.js +++ b/src/data/serialize.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // serialize-util.js: simple interface and utility functions for converting // Things into a directly serializeable format @@ -18,7 +18,7 @@ export function toRefs(things) { } export function toContribRefs(contribs) { - return contribs?.map(({ who, what }) => ({ who: toRef(who), what })); + return contribs?.map(({who, what}) => ({who: toRef(who), what})); } // Interface diff --git a/src/data/things.js b/src/data/things.js index c86add30..e2bbfd70 100644 --- a/src/data/things.js +++ b/src/data/things.js @@ -1,9 +1,9 @@ -// @format -// +/** @format */ + // things.js: class definitions for various object types used across the wiki, // most of which correspond to an output page, such as Track, Album, Artist -import CacheableObject from "./cacheable-object.js"; +import CacheableObject from './cacheable-object.js'; import { isAdditionalFileList, @@ -16,32 +16,30 @@ import { isDimensions, isDirectory, isDuration, - isInstance, isFileExtension, isLanguageCode, isName, isNumber, isURL, isString, - isWholeNumber, oneOf, validateArrayItems, validateInstanceOf, validateReference, validateReferenceList, -} from "./validators.js"; +} from './validators.js'; -import * as S from "./serialize.js"; +import * as S from './serialize.js'; import { getKebabCase, sortAlbumsTracksChronologically, -} from "../util/wiki-data.js"; +} from '../util/wiki-data.js'; -import find from "../util/find.js"; +import find from '../util/find.js'; -import { inspect } from "util"; -import { color } from "../util/cli.js"; +import {inspect} from 'util'; +import {color} from '../util/cli.js'; // Stub classes (and their exports) at the top of the file - these are // referenced later when we actually define static class fields. We deliberately @@ -96,16 +94,16 @@ export class Language extends CacheableObject {} // Before initializing property descriptors, set additional independent // constants on the classes (which are referenced later). -Thing.referenceType = Symbol("Thing.referenceType"); +Thing.referenceType = Symbol('Thing.referenceType'); -Album[Thing.referenceType] = "album"; -Track[Thing.referenceType] = "track"; -Artist[Thing.referenceType] = "artist"; -Group[Thing.referenceType] = "group"; -ArtTag[Thing.referenceType] = "tag"; -NewsEntry[Thing.referenceType] = "news-entry"; -StaticPage[Thing.referenceType] = "static"; -Flash[Thing.referenceType] = "flash"; +Album[Thing.referenceType] = 'album'; +Track[Thing.referenceType] = 'track'; +Artist[Thing.referenceType] = 'artist'; +Group[Thing.referenceType] = 'group'; +ArtTag[Thing.referenceType] = 'tag'; +NewsEntry[Thing.referenceType] = 'news-entry'; +StaticPage[Thing.referenceType] = 'static'; +Flash[Thing.referenceType] = 'flash'; // -> Thing: base class for wiki data types, providing wiki-specific utility // functions on top of essential CacheableObject behavior. @@ -115,21 +113,21 @@ Flash[Thing.referenceType] = "flash"; // functions, so check each for how its own arguments behave! Thing.common = { name: (defaultName) => ({ - flags: { update: true, expose: true }, - update: { validate: isName, default: defaultName }, + flags: {update: true, expose: true}, + update: {validate: isName, default: defaultName}, }), color: () => ({ - flags: { update: true, expose: true }, - update: { validate: isColor }, + flags: {update: true, expose: true}, + update: {validate: isColor}, }), directory: () => ({ - flags: { update: true, expose: true }, - update: { validate: isDirectory }, + flags: {update: true, expose: true}, + update: {validate: isDirectory}, expose: { - dependencies: ["name"], - transform(directory, { name }) { + dependencies: ['name'], + transform(directory, {name}) { if (directory === null && name === null) return null; else if (directory === null) return getKebabCase(name); else return directory; @@ -138,27 +136,27 @@ Thing.common = { }), urls: () => ({ - flags: { update: true, expose: true }, - update: { validate: validateArrayItems(isURL) }, + flags: {update: true, expose: true}, + update: {validate: validateArrayItems(isURL)}, }), // A file extension! Or the default, if provided when calling this. fileExtension: (defaultFileExtension = null) => ({ - flags: { update: true, expose: true }, - update: { validate: isFileExtension }, - expose: { transform: (value) => value ?? defaultFileExtension }, + flags: {update: true, expose: true}, + update: {validate: isFileExtension}, + expose: {transform: (value) => value ?? defaultFileExtension}, }), // Straightforward flag descriptor for a variety of property purposes. // Provide a default value, true or false! flag: (defaultValue = false) => { - if (typeof defaultValue !== "boolean") { + if (typeof defaultValue !== 'boolean') { throw new TypeError(`Always set explicit defaults for flags!`); } return { - flags: { update: true, expose: true }, - update: { validate: isBoolean, default: defaultValue }, + flags: {update: true, expose: true}, + update: {validate: isBoolean, default: defaultValue}, }; }, @@ -166,23 +164,23 @@ Thing.common = { // This isn't dynamic though - it won't inherit from a date stored on // another object, for example. simpleDate: () => ({ - flags: { update: true, expose: true }, - update: { validate: isDate }, + flags: {update: true, expose: true}, + update: {validate: isDate}, }), // General string type. This should probably generally be avoided in favor // of more specific validation, but using it makes it easy to find where we // might want to improve later, and it's a useful shorthand meanwhile. simpleString: () => ({ - flags: { update: true, expose: true }, - update: { validate: isString }, + flags: {update: true, expose: true}, + update: {validate: isString}, }), // External function. These should only be used as dependencies for other // properties, so they're left unexposed. externalFunction: () => ({ - flags: { update: true }, - update: { validate: (t) => typeof t === "function" }, + flags: {update: true}, + update: {validate: (t) => typeof t === 'function'}, }), // Super simple "contributions by reference" list, used for a variety of @@ -197,14 +195,14 @@ Thing.common = { // // ...processed from YAML, spreadsheet, or any other kind of input. contribsByRef: () => ({ - flags: { update: true, expose: true }, - update: { validate: isContributionList }, + flags: {update: true, expose: true}, + update: {validate: isContributionList}, }), // Artist commentary! Generally present on tracks and albums. commentary: () => ({ - flags: { update: true, expose: true }, - update: { validate: isCommentary }, + flags: {update: true, expose: true}, + update: {validate: isCommentary}, }), // This is a somewhat more involved data structure - it's for additional @@ -223,8 +221,8 @@ Thing.common = { // ] // additionalFiles: () => ({ - flags: { update: true, expose: true }, - update: { validate: isAdditionalFileList }, + flags: {update: true, expose: true}, + update: {validate: isAdditionalFileList}, }), // A reference list! Keep in mind this is for general references to wiki @@ -236,7 +234,7 @@ Thing.common = { // string in multiple places by referencing the value saved on the class // instead. referenceList: (thingClass) => { - const { [Thing.referenceType]: referenceType } = thingClass; + const {[Thing.referenceType]: referenceType} = thingClass; if (!referenceType) { throw new Error( `The passed constructor ${thingClass.name} doesn't define Thing.referenceType!` @@ -244,14 +242,14 @@ Thing.common = { } return { - flags: { update: true, expose: true }, - update: { validate: validateReferenceList(referenceType) }, + flags: {update: true, expose: true}, + update: {validate: validateReferenceList(referenceType)}, }; }, // Corresponding function for a single reference. singleReference: (thingClass) => { - const { [Thing.referenceType]: referenceType } = thingClass; + const {[Thing.referenceType]: referenceType} = thingClass; if (!referenceType) { throw new Error( `The passed constructor ${thingClass.name} doesn't define Thing.referenceType!` @@ -259,8 +257,8 @@ Thing.common = { } return { - flags: { update: true, expose: true }, - update: { validate: validateReference(referenceType) }, + flags: {update: true, expose: true}, + update: {validate: validateReference(referenceType)}, }; }, @@ -272,7 +270,7 @@ Thing.common = { thingDataProperty, findFn ) => ({ - flags: { expose: true }, + flags: {expose: true}, expose: { dependencies: [referenceListProperty, thingDataProperty], @@ -282,7 +280,7 @@ Thing.common = { }) => refs && thingData ? refs - .map((ref) => findFn(ref, thingData, { mode: "quiet" })) + .map((ref) => findFn(ref, thingData, {mode: 'quiet'})) .filter(Boolean) : [], }, @@ -294,15 +292,14 @@ Thing.common = { thingDataProperty, findFn ) => ({ - flags: { expose: true }, + flags: {expose: true}, expose: { dependencies: [singleReferenceProperty, thingDataProperty], compute: ({ [singleReferenceProperty]: ref, [thingDataProperty]: thingData, - }) => - ref && thingData ? findFn(ref, thingData, { mode: "quiet" }) : null, + }) => (ref && thingData ? findFn(ref, thingData, {mode: 'quiet'}) : null), }, }), @@ -322,17 +319,17 @@ Thing.common = { // reference list is somehow messed up, or artistData isn't being provided // properly.) dynamicContribs: (contribsByRefProperty) => ({ - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["artistData", contribsByRefProperty], - compute: ({ artistData, [contribsByRefProperty]: contribsByRef }) => + dependencies: ['artistData', contribsByRefProperty], + compute: ({artistData, [contribsByRefProperty]: contribsByRef}) => contribsByRef && artistData ? contribsByRef - .map(({ who: ref, what }) => ({ + .map(({who: ref, what}) => ({ who: find.artist(ref, artistData), what, })) - .filter(({ who }) => who) + .filter(({who}) => who) : [], }, }), @@ -350,9 +347,9 @@ Thing.common = { thingDataProperty, findFn ) => ({ - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: [contribsByRefProperty, thingDataProperty, "artistData"], + dependencies: [contribsByRefProperty, thingDataProperty, 'artistData'], compute({ [Thing.instance]: thing, [contribsByRefProperty]: contribsByRef, @@ -362,16 +359,16 @@ Thing.common = { if (!artistData) return []; const refs = contribsByRef ?? - findFn(thing, thingData, { mode: "quiet" })?.[ + findFn(thing, thingData, {mode: 'quiet'})?.[ parentContribsByRefProperty ]; if (!refs) return []; return refs - .map(({ who: ref, what }) => ({ + .map(({who: ref, what}) => ({ who: find.artist(ref, artistData), what, })) - .filter(({ who }) => who); + .filter(({who}) => who); }, }, }), @@ -382,12 +379,12 @@ Thing.common = { // property. Naturally, the passed ref list property is of the things in the // wiki data provided, not the requesting Thing itself. reverseReferenceList: (wikiDataProperty, referencerRefListProperty) => ({ - flags: { expose: true }, + flags: {expose: true}, expose: { dependencies: [wikiDataProperty], - compute: ({ [wikiDataProperty]: wikiData, [Thing.instance]: thing }) => + compute: ({[wikiDataProperty]: wikiData, [Thing.instance]: thing}) => wikiData ? wikiData.filter((t) => t[referencerRefListProperty]?.includes(thing) @@ -400,12 +397,12 @@ Thing.common = { // is still a list - this is for matching all the objects whose single // reference (in the given property) matches this Thing. reverseSingleReference: (wikiDataProperty, referencerRefListProperty) => ({ - flags: { expose: true }, + flags: {expose: true}, expose: { dependencies: [wikiDataProperty], - compute: ({ [wikiDataProperty]: wikiData, [Thing.instance]: thing }) => + compute: ({[wikiDataProperty]: wikiData, [Thing.instance]: thing}) => wikiData?.filter((t) => t[referencerRefListProperty] === thing), }, }), @@ -413,7 +410,7 @@ Thing.common = { // General purpose wiki data constructor, for properties like artistData, // trackData, etc. wikiData: (thingClass) => ({ - flags: { update: true }, + flags: {update: true}, update: { validate: validateArrayItems(validateInstanceOf(thingClass)), }, @@ -423,21 +420,21 @@ Thing.common = { // commentary content, and finds the matching artist for each reference. // This is mostly useful for credits and listings on artist pages. commentatorArtists: () => ({ - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["artistData", "commentary"], + dependencies: ['artistData', 'commentary'], - compute: ({ artistData, commentary }) => + compute: ({artistData, commentary}) => artistData && commentary ? Array.from( new Set( Array.from( commentary - .replace(/<\/?b>/g, "") + .replace(/<\/?b>/g, '') .matchAll(/<i>(?<who>.*?):<\/i>/g) - ).map(({ groups: { who } }) => - find.artist(who, artistData, { mode: "quiet" }) + ).map(({groups: {who}}) => + find.artist(who, artistData, {mode: 'quiet'}) ) ) ) @@ -472,7 +469,7 @@ Thing.prototype[inspect.custom] = function () { return ( (this.name ? `${cname} ${color.green(`"${this.name}"`)}` : `${cname}`) + - (this.directory ? ` (${color.blue(Thing.getReference(this))})` : "") + (this.directory ? ` (${color.blue(Thing.getReference(this))})` : '') ); }; @@ -481,7 +478,7 @@ Thing.prototype[inspect.custom] = function () { Album.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Album"), + name: Thing.common.name('Unnamed Album'), color: Thing.common.color(), directory: Thing.common.directory(), urls: Thing.common.urls(), @@ -491,13 +488,13 @@ Album.propertyDescriptors = { dateAddedToWiki: Thing.common.simpleDate(), coverArtDate: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, - update: { validate: isDate }, + update: {validate: isDate}, expose: { - dependencies: ["date"], - transform: (coverArtDate, { date }) => coverArtDate ?? date ?? null, + dependencies: ['date'], + transform: (coverArtDate, {date}) => coverArtDate ?? date ?? null, }, }, @@ -511,24 +508,24 @@ Album.propertyDescriptors = { artTagsByRef: Thing.common.referenceList(ArtTag), trackGroups: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, update: { validate: validateArrayItems(validateInstanceOf(TrackGroup)), }, }, - coverArtFileExtension: Thing.common.fileExtension("jpg"), - trackCoverArtFileExtension: Thing.common.fileExtension("jpg"), + coverArtFileExtension: Thing.common.fileExtension('jpg'), + trackCoverArtFileExtension: Thing.common.fileExtension('jpg'), wallpaperStyle: Thing.common.simpleString(), - wallpaperFileExtension: Thing.common.fileExtension("jpg"), + wallpaperFileExtension: Thing.common.fileExtension('jpg'), bannerStyle: Thing.common.simpleString(), - bannerFileExtension: Thing.common.fileExtension("jpg"), + bannerFileExtension: Thing.common.fileExtension('jpg'), bannerDimensions: { - flags: { update: true, expose: true }, - update: { validate: isDimensions }, + flags: {update: true, expose: true}, + update: {validate: isDimensions}, }, hasCoverArt: Thing.common.flag(true), @@ -549,44 +546,44 @@ Album.propertyDescriptors = { // Expose only - artistContribs: Thing.common.dynamicContribs("artistContribsByRef"), - coverArtistContribs: Thing.common.dynamicContribs("coverArtistContribsByRef"), + artistContribs: Thing.common.dynamicContribs('artistContribsByRef'), + coverArtistContribs: Thing.common.dynamicContribs('coverArtistContribsByRef'), trackCoverArtistContribs: Thing.common.dynamicContribs( - "trackCoverArtistContribsByRef" + 'trackCoverArtistContribsByRef' ), wallpaperArtistContribs: Thing.common.dynamicContribs( - "wallpaperArtistContribsByRef" + 'wallpaperArtistContribsByRef' ), bannerArtistContribs: Thing.common.dynamicContribs( - "bannerArtistContribsByRef" + 'bannerArtistContribsByRef' ), commentatorArtists: Thing.common.commentatorArtists(), tracks: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["trackGroups", "trackData"], - compute: ({ trackGroups, trackData }) => + dependencies: ['trackGroups', 'trackData'], + compute: ({trackGroups, trackData}) => trackGroups && trackData ? trackGroups .flatMap((group) => group.tracksByRef ?? []) - .map((ref) => find.track(ref, trackData, { mode: "quiet" })) + .map((ref) => find.track(ref, trackData, {mode: 'quiet'})) .filter(Boolean) : [], }, }, groups: Thing.common.dynamicThingsFromReferenceList( - "groupsByRef", - "groupData", + 'groupsByRef', + 'groupData', find.group ), artTags: Thing.common.dynamicThingsFromReferenceList( - "artTagsByRef", - "artTagData", + 'artTagsByRef', + 'artTagData', find.artTag ), }; @@ -632,17 +629,17 @@ Album[S.serializeDescriptors] = { TrackGroup.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Track Group"), + name: Thing.common.name('Unnamed Track Group'), color: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, - update: { validate: isColor }, + update: {validate: isColor}, expose: { - dependencies: ["album"], + dependencies: ['album'], - transform(color, { album }) { + transform(color, {album}) { return color ?? album?.color ?? null; }, }, @@ -657,8 +654,8 @@ TrackGroup.propertyDescriptors = { // Update only album: { - flags: { update: true }, - update: { validate: validateInstanceOf(Album) }, + flags: {update: true}, + update: {validate: validateInstanceOf(Album)}, }, trackData: Thing.common.wikiData(Track), @@ -666,11 +663,11 @@ TrackGroup.propertyDescriptors = { // Expose only tracks: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["tracksByRef", "trackData"], - compute: ({ tracksByRef, trackData }) => + dependencies: ['tracksByRef', 'trackData'], + compute: ({tracksByRef, trackData}) => tracksByRef && trackData ? tracksByRef.map((ref) => find.track(ref, trackData)).filter(Boolean) : [], @@ -678,11 +675,11 @@ TrackGroup.propertyDescriptors = { }, startIndex: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["album"], - compute: ({ album, [TrackGroup.instance]: trackGroup }) => + dependencies: ['album'], + compute: ({album, [TrackGroup.instance]: trackGroup}) => album.trackGroups .slice(0, album.trackGroups.indexOf(trackGroup)) .reduce((acc, tg) => acc + tg.tracks.length, 0), @@ -717,12 +714,12 @@ Track.hasCoverArt = ( Track.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Track"), + name: Thing.common.name('Unnamed Track'), directory: Thing.common.directory(), duration: { - flags: { update: true, expose: true }, - update: { validate: isDuration }, + flags: {update: true, expose: true}, + update: {validate: isDuration}, }, urls: Thing.common.urls(), @@ -738,15 +735,15 @@ Track.propertyDescriptors = { artTagsByRef: Thing.common.referenceList(ArtTag), hasCoverArt: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, - update: { validate: isBoolean }, + update: {validate: isBoolean}, expose: { - dependencies: ["albumData", "coverArtistContribsByRef"], + dependencies: ['albumData', 'coverArtistContribsByRef'], transform: ( hasCoverArt, - { albumData, coverArtistContribsByRef, [Track.instance]: track } + {albumData, coverArtistContribsByRef, [Track.instance]: track} ) => Track.hasCoverArt( track, @@ -758,12 +755,12 @@ Track.propertyDescriptors = { }, coverArtFileExtension: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, - update: { validate: isFileExtension }, + update: {validate: isFileExtension}, expose: { - dependencies: ["albumData", "coverArtistContribsByRef"], + dependencies: ['albumData', 'coverArtistContribsByRef'], transform: ( coverArtFileExtension, { @@ -782,7 +779,7 @@ Track.propertyDescriptors = { ) ? Track.findAlbum(track, albumData)?.trackCoverArtFileExtension : Track.findAlbum(track, albumData)?.coverArtFileExtension) ?? - "jpg", + 'jpg', }, }, @@ -808,11 +805,11 @@ Track.propertyDescriptors = { commentatorArtists: Thing.common.commentatorArtists(), album: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["albumData"], - compute: ({ [Track.instance]: track, albumData }) => + dependencies: ['albumData'], + compute: ({[Track.instance]: track, albumData}) => albumData?.find((album) => album.tracks.includes(track)) ?? null, }, }, @@ -825,28 +822,28 @@ Track.propertyDescriptors = { // dataSourceAlbum is available (depending on the Track creator to optionally // provide dataSourceAlbumByRef). dataSourceAlbum: Thing.common.dynamicThingFromSingleReference( - "dataSourceAlbumByRef", - "albumData", + 'dataSourceAlbumByRef', + 'albumData', find.album ), date: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["albumData", "dateFirstReleased"], - compute: ({ albumData, dateFirstReleased, [Track.instance]: track }) => + dependencies: ['albumData', 'dateFirstReleased'], + compute: ({albumData, dateFirstReleased, [Track.instance]: track}) => dateFirstReleased ?? Track.findAlbum(track, albumData)?.date ?? null, }, }, color: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["albumData"], + dependencies: ['albumData'], - compute: ({ albumData, [Track.instance]: track }) => + compute: ({albumData, [Track.instance]: track}) => Track.findAlbum(track, albumData)?.trackGroups.find((tg) => tg.tracks.includes(track) )?.color ?? null, @@ -854,15 +851,15 @@ Track.propertyDescriptors = { }, coverArtDate: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, - update: { validate: isDate }, + update: {validate: isDate}, expose: { - dependencies: ["albumData", "dateFirstReleased"], + dependencies: ['albumData', 'dateFirstReleased'], transform: ( coverArtDate, - { albumData, dateFirstReleased, [Track.instance]: track } + {albumData, dateFirstReleased, [Track.instance]: track} ) => coverArtDate ?? dateFirstReleased ?? @@ -873,16 +870,16 @@ Track.propertyDescriptors = { }, originalReleaseTrack: Thing.common.dynamicThingFromSingleReference( - "originalReleaseTrackByRef", - "trackData", + 'originalReleaseTrackByRef', + 'trackData', find.track ), otherReleases: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["originalReleaseTrackByRef", "trackData"], + dependencies: ['originalReleaseTrackByRef', 'trackData'], compute: ({ originalReleaseTrackByRef: t1origRef, @@ -898,7 +895,7 @@ Track.propertyDescriptors = { return [ t1orig, ...trackData.filter((t2) => { - const { originalReleaseTrack: t2orig } = t2; + const {originalReleaseTrack: t2orig} = t2; return t2 !== t1 && t2orig && (t2orig === t1orig || t2orig === t1); }), ].filter(Boolean); @@ -908,27 +905,27 @@ Track.propertyDescriptors = { // Previously known as: (track).artists artistContribs: Thing.common.dynamicInheritContribs( - "artistContribsByRef", - "artistContribsByRef", - "albumData", + 'artistContribsByRef', + 'artistContribsByRef', + 'albumData', Track.findAlbum ), // Previously known as: (track).contributors - contributorContribs: Thing.common.dynamicContribs("contributorContribsByRef"), + contributorContribs: Thing.common.dynamicContribs('contributorContribsByRef'), // Previously known as: (track).coverArtists coverArtistContribs: Thing.common.dynamicInheritContribs( - "coverArtistContribsByRef", - "trackCoverArtistContribsByRef", - "albumData", + 'coverArtistContribsByRef', + 'trackCoverArtistContribsByRef', + 'albumData', Track.findAlbum ), // Previously known as: (track).references referencedTracks: Thing.common.dynamicThingsFromReferenceList( - "referencedTracksByRef", - "trackData", + 'referencedTracksByRef', + 'trackData', find.track ), @@ -941,12 +938,12 @@ Track.propertyDescriptors = { // the "Tracks - by Times Referenced" listing page (or other data // processing). referencedByTracks: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["trackData"], + dependencies: ['trackData'], - compute: ({ trackData, [Track.instance]: track }) => + compute: ({trackData, [Track.instance]: track}) => trackData ? trackData .filter((t) => !t.originalReleaseTrack) @@ -957,13 +954,13 @@ Track.propertyDescriptors = { // Previously known as: (track).flashes featuredInFlashes: Thing.common.reverseReferenceList( - "flashData", - "featuredTracks" + 'flashData', + 'featuredTracks' ), artTags: Thing.common.dynamicThingsFromReferenceList( - "artTagsByRef", - "artTagData", + 'artTagsByRef', + 'artTagData', find.artTag ), }; @@ -971,12 +968,12 @@ Track.propertyDescriptors = { Track.prototype[inspect.custom] = function () { const base = Thing.prototype[inspect.custom].apply(this); - const { album, dataSourceAlbum } = this; + const {album, dataSourceAlbum} = this; const albumName = album ? album.name : dataSourceAlbum?.name; const albumIndex = albumName && (album ? album.tracks.indexOf(this) : dataSourceAlbum.tracks.indexOf(this)); - const trackNum = albumIndex === -1 ? "#?" : `#${albumIndex + 1}`; + const trackNum = albumIndex === -1 ? '#?' : `#${albumIndex + 1}`; return albumName ? base + ` (${color.yellow(trackNum)} in ${color.green(albumName)})` @@ -986,13 +983,13 @@ Track.prototype[inspect.custom] = function () { // -> Artist Artist.filterByContrib = (thingDataProperty, contribsProperty) => ({ - flags: { expose: true }, + flags: {expose: true}, expose: { dependencies: [thingDataProperty], - compute: ({ [thingDataProperty]: thingData, [Artist.instance]: artist }) => - thingData?.filter(({ [contribsProperty]: contribs }) => + compute: ({[thingDataProperty]: thingData, [Artist.instance]: artist}) => + thingData?.filter(({[contribsProperty]: contribs}) => contribs?.some((contrib) => contrib.who === artist) ), }, @@ -1001,16 +998,16 @@ Artist.filterByContrib = (thingDataProperty, contribsProperty) => ({ Artist.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Artist"), + name: Thing.common.name('Unnamed Artist'), directory: Thing.common.directory(), urls: Thing.common.urls(), contextNotes: Thing.common.simpleString(), hasAvatar: Thing.common.flag(false), - avatarFileExtension: Thing.common.fileExtension("jpg"), + avatarFileExtension: Thing.common.fileExtension('jpg'), aliasNames: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, update: { validate: validateArrayItems(isName), }, @@ -1029,87 +1026,87 @@ Artist.propertyDescriptors = { // Expose only aliasedArtist: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["artistData", "aliasedArtistRef"], - compute: ({ artistData, aliasedArtistRef }) => + dependencies: ['artistData', 'aliasedArtistRef'], + compute: ({artistData, aliasedArtistRef}) => aliasedArtistRef && artistData - ? find.artist(aliasedArtistRef, artistData, { mode: "quiet" }) + ? find.artist(aliasedArtistRef, artistData, {mode: 'quiet'}) : null, }, }, - tracksAsArtist: Artist.filterByContrib("trackData", "artistContribs"), + tracksAsArtist: Artist.filterByContrib('trackData', 'artistContribs'), tracksAsContributor: Artist.filterByContrib( - "trackData", - "contributorContribs" + 'trackData', + 'contributorContribs' ), tracksAsCoverArtist: Artist.filterByContrib( - "trackData", - "coverArtistContribs" + 'trackData', + 'coverArtistContribs' ), tracksAsAny: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["trackData"], + dependencies: ['trackData'], - compute: ({ trackData, [Artist.instance]: artist }) => + compute: ({trackData, [Artist.instance]: artist}) => trackData?.filter((track) => [ ...track.artistContribs, ...track.contributorContribs, ...track.coverArtistContribs, - ].some(({ who }) => who === artist) + ].some(({who}) => who === artist) ), }, }, tracksAsCommentator: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["trackData"], + dependencies: ['trackData'], - compute: ({ trackData, [Artist.instance]: artist }) => - trackData.filter(({ commentatorArtists }) => + compute: ({trackData, [Artist.instance]: artist}) => + trackData.filter(({commentatorArtists}) => commentatorArtists?.includes(artist) ), }, }, - albumsAsAlbumArtist: Artist.filterByContrib("albumData", "artistContribs"), + albumsAsAlbumArtist: Artist.filterByContrib('albumData', 'artistContribs'), albumsAsCoverArtist: Artist.filterByContrib( - "albumData", - "coverArtistContribs" + 'albumData', + 'coverArtistContribs' ), albumsAsWallpaperArtist: Artist.filterByContrib( - "albumData", - "wallpaperArtistContribs" + 'albumData', + 'wallpaperArtistContribs' ), albumsAsBannerArtist: Artist.filterByContrib( - "albumData", - "bannerArtistContribs" + 'albumData', + 'bannerArtistContribs' ), albumsAsCommentator: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["albumData"], + dependencies: ['albumData'], - compute: ({ albumData, [Artist.instance]: artist }) => - albumData.filter(({ commentatorArtists }) => + compute: ({albumData, [Artist.instance]: artist}) => + albumData.filter(({commentatorArtists}) => commentatorArtists?.includes(artist) ), }, }, flashesAsContributor: Artist.filterByContrib( - "flashData", - "contributorContribs" + 'flashData', + 'contributorContribs' ), }; @@ -1143,7 +1140,7 @@ Artist[S.serializeDescriptors] = { Group.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Group"), + name: Thing.common.name('Unnamed Group'), directory: Thing.common.directory(), description: Thing.common.simpleString(), @@ -1158,42 +1155,42 @@ Group.propertyDescriptors = { // Expose only descriptionShort: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["description"], - compute: ({ description }) => description.split('<hr class="split">')[0], + dependencies: ['description'], + compute: ({description}) => description.split('<hr class="split">')[0], }, }, albums: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["albumData"], - compute: ({ albumData, [Group.instance]: group }) => + dependencies: ['albumData'], + compute: ({albumData, [Group.instance]: group}) => albumData?.filter((album) => album.groups.includes(group)) ?? [], }, }, color: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["groupCategoryData"], + dependencies: ['groupCategoryData'], - compute: ({ groupCategoryData, [Group.instance]: group }) => + compute: ({groupCategoryData, [Group.instance]: group}) => groupCategoryData.find((category) => category.groups.includes(group)) ?.color ?? null, }, }, category: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["groupCategoryData"], - compute: ({ groupCategoryData, [Group.instance]: group }) => + dependencies: ['groupCategoryData'], + compute: ({groupCategoryData, [Group.instance]: group}) => groupCategoryData.find((category) => category.groups.includes(group)) ?? null, }, @@ -1203,7 +1200,7 @@ Group.propertyDescriptors = { GroupCategory.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Group Category"), + name: Thing.common.name('Unnamed Group Category'), color: Thing.common.color(), groupsByRef: Thing.common.referenceList(Group), @@ -1215,8 +1212,8 @@ GroupCategory.propertyDescriptors = { // Expose only groups: Thing.common.dynamicThingsFromReferenceList( - "groupsByRef", - "groupData", + 'groupsByRef', + 'groupData', find.group ), }; @@ -1226,7 +1223,7 @@ GroupCategory.propertyDescriptors = { ArtTag.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Art Tag"), + name: Thing.common.name('Unnamed Art Tag'), directory: Thing.common.directory(), color: Thing.common.color(), isContentWarning: Thing.common.flag(false), @@ -1240,16 +1237,16 @@ ArtTag.propertyDescriptors = { // Previously known as: (tag).things taggedInThings: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["albumData", "trackData"], - compute: ({ albumData, trackData, [ArtTag.instance]: artTag }) => + dependencies: ['albumData', 'trackData'], + compute: ({albumData, trackData, [ArtTag.instance]: artTag}) => sortAlbumsTracksChronologically( [...albumData, ...trackData].filter((thing) => thing.artTags?.includes(artTag) ), - { getDate: (o) => o.coverArtDate } + {getDate: (o) => o.coverArtDate} ), }, }, @@ -1260,7 +1257,7 @@ ArtTag.propertyDescriptors = { NewsEntry.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed News Entry"), + name: Thing.common.name('Unnamed News Entry'), directory: Thing.common.directory(), date: Thing.common.simpleDate(), @@ -1269,12 +1266,12 @@ NewsEntry.propertyDescriptors = { // Expose only contentShort: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["content"], + dependencies: ['content'], - compute: ({ content }) => content.split('<hr class="split">')[0], + compute: ({content}) => content.split('<hr class="split">')[0], }, }, }; @@ -1284,15 +1281,15 @@ NewsEntry.propertyDescriptors = { StaticPage.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Static Page"), + name: Thing.common.name('Unnamed Static Page'), nameShort: { - flags: { update: true, expose: true }, - update: { validate: isName }, + flags: {update: true, expose: true}, + update: {validate: isName}, expose: { - dependencies: ["name"], - transform: (value, { name }) => value ?? name, + dependencies: ['name'], + transform: (value, {name}) => value ?? name, }, }, @@ -1310,7 +1307,7 @@ HomepageLayout.propertyDescriptors = { sidebarContent: Thing.common.simpleString(), rows: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, update: { validate: validateArrayItems(validateInstanceOf(HomepageLayoutRow)), @@ -1321,13 +1318,13 @@ HomepageLayout.propertyDescriptors = { HomepageLayoutRow.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Homepage Row"), + name: Thing.common.name('Unnamed Homepage Row'), type: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, update: { - validate(value) { + validate() { throw new Error(`'type' property validator must be overridden`); }, }, @@ -1350,10 +1347,10 @@ HomepageLayoutAlbumsRow.propertyDescriptors = { // Update & expose type: { - flags: { update: true, expose: true }, + flags: {update: true, expose: true}, update: { validate(value) { - if (value !== "albums") { + if (value !== 'albums') { throw new TypeError(`Expected 'albums'`); } @@ -1366,25 +1363,25 @@ HomepageLayoutAlbumsRow.propertyDescriptors = { sourceAlbumsByRef: Thing.common.referenceList(Album), countAlbumsFromGroup: { - flags: { update: true, expose: true }, - update: { validate: isCountingNumber }, + flags: {update: true, expose: true}, + update: {validate: isCountingNumber}, }, actionLinks: { - flags: { update: true, expose: true }, - update: { validate: validateArrayItems(isString) }, + flags: {update: true, expose: true}, + update: {validate: validateArrayItems(isString)}, }, // Expose only sourceGroup: Thing.common.dynamicThingFromSingleReference( - "sourceGroupByRef", - "groupData", + 'sourceGroupByRef', + 'groupData', find.group ), sourceAlbums: Thing.common.dynamicThingsFromReferenceList( - "sourceAlbumsByRef", - "albumData", + 'sourceAlbumsByRef', + 'albumData', find.album ), }; @@ -1394,18 +1391,18 @@ HomepageLayoutAlbumsRow.propertyDescriptors = { Flash.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Flash"), + name: Thing.common.name('Unnamed Flash'), directory: { - flags: { update: true, expose: true }, - update: { validate: isDirectory }, + flags: {update: true, expose: true}, + update: {validate: isDirectory}, // Flashes expose directory differently from other Things! Their // default directory is dependent on the page number (or ID), not // the name. expose: { - dependencies: ["page"], - transform(directory, { page }) { + dependencies: ['page'], + transform(directory, {page}) { if (directory === null && page === null) return null; else if (directory === null) return page; else return directory; @@ -1414,8 +1411,8 @@ Flash.propertyDescriptors = { }, page: { - flags: { update: true, expose: true }, - update: { validate: oneOf(isString, isNumber) }, + flags: {update: true, expose: true}, + update: {validate: oneOf(isString, isNumber)}, expose: { transform: (value) => (value === null ? null : value.toString()), @@ -1424,7 +1421,7 @@ Flash.propertyDescriptors = { date: Thing.common.simpleDate(), - coverArtFileExtension: Thing.common.fileExtension("jpg"), + coverArtFileExtension: Thing.common.fileExtension('jpg'), contributorContribsByRef: Thing.common.contribsByRef(), @@ -1440,32 +1437,32 @@ Flash.propertyDescriptors = { // Expose only - contributorContribs: Thing.common.dynamicContribs("contributorContribsByRef"), + contributorContribs: Thing.common.dynamicContribs('contributorContribsByRef'), featuredTracks: Thing.common.dynamicThingsFromReferenceList( - "featuredTracksByRef", - "trackData", + 'featuredTracksByRef', + 'trackData', find.track ), act: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["flashActData"], + dependencies: ['flashActData'], - compute: ({ flashActData, [Flash.instance]: flash }) => + compute: ({flashActData, [Flash.instance]: flash}) => flashActData.find((act) => act.flashes.includes(flash)) ?? null, }, }, color: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["flashActData"], + dependencies: ['flashActData'], - compute: ({ flashActData, [Flash.instance]: flash }) => + compute: ({flashActData, [Flash.instance]: flash}) => flashActData.find((act) => act.flashes.includes(flash))?.color ?? null, }, }, @@ -1485,7 +1482,7 @@ Flash[S.serializeDescriptors] = { FlashAct.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Flash Act"), + name: Thing.common.name('Unnamed Flash Act'), color: Thing.common.color(), anchor: Thing.common.simpleString(), jump: Thing.common.simpleString(), @@ -1500,8 +1497,8 @@ FlashAct.propertyDescriptors = { // Expose only flashes: Thing.common.dynamicThingsFromReferenceList( - "flashesByRef", - "flashData", + 'flashesByRef', + 'flashData', find.flash ), }; @@ -1511,16 +1508,16 @@ FlashAct.propertyDescriptors = { WikiInfo.propertyDescriptors = { // Update & expose - name: Thing.common.name("Unnamed Wiki"), + name: Thing.common.name('Unnamed Wiki'), // Displayed in nav bar. nameShort: { - flags: { update: true, expose: true }, - update: { validate: isName }, + flags: {update: true, expose: true}, + update: {validate: isName}, expose: { - dependencies: ["name"], - transform: (value, { name }) => value ?? name, + dependencies: ['name'], + transform: (value, {name}) => value ?? name, }, }, @@ -1532,13 +1529,13 @@ WikiInfo.propertyDescriptors = { footerContent: Thing.common.simpleString(), defaultLanguage: { - flags: { update: true, expose: true }, - update: { validate: isLanguageCode }, + flags: {update: true, expose: true}, + update: {validate: isLanguageCode}, }, canonicalBase: { - flags: { update: true, expose: true }, - update: { validate: isURL }, + flags: {update: true, expose: true}, + update: {validate: isURL}, }, divideTrackListsByGroupsByRef: Thing.common.referenceList(Group), @@ -1557,8 +1554,8 @@ WikiInfo.propertyDescriptors = { // Expose only divideTrackListsByGroups: Thing.common.dynamicThingsFromReferenceList( - "divideTrackListsByGroupsByRef", - "groupData", + 'divideTrackListsByGroupsByRef', + 'groupData', find.group ), }; @@ -1566,10 +1563,10 @@ WikiInfo.propertyDescriptors = { // -> Language const intlHelper = (constructor, opts) => ({ - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["code", "intlCode"], - compute: ({ code, intlCode }) => { + dependencies: ['code', 'intlCode'], + compute: ({code, intlCode}) => { const constructCode = intlCode ?? code; if (!constructCode) return null; return Reflect.construct(constructor, [constructCode, opts]); @@ -1584,8 +1581,8 @@ Language.propertyDescriptors = { // from other languages (similar to how "Directory" operates in many data // objects). code: { - flags: { update: true, expose: true }, - update: { validate: isLanguageCode }, + flags: {update: true, expose: true}, + update: {validate: isLanguageCode}, }, // Human-readable name. This should be the language's own native name, not @@ -1596,11 +1593,11 @@ Language.propertyDescriptors = { // Usually this will be the same as the language's general code, but it // may be overridden to provide Intl constructors an alternative value. intlCode: { - flags: { update: true, expose: true }, - update: { validate: isLanguageCode }, + flags: {update: true, expose: true}, + update: {validate: isLanguageCode}, expose: { - dependencies: ["code"], - transform: (intlCode, { code }) => intlCode ?? code, + dependencies: ['code'], + transform: (intlCode, {code}) => intlCode ?? code, }, }, @@ -1617,13 +1614,13 @@ Language.propertyDescriptors = { // Mapping of translation keys to values (strings). Generally, don't // access this object directly - use methods instead. strings: { - flags: { update: true, expose: true }, - update: { validate: (t) => typeof t === "object" }, + flags: {update: true, expose: true}, + update: {validate: (t) => typeof t === 'object'}, expose: { - dependencies: ["inheritedStrings"], - transform(strings, { inheritedStrings }) { + dependencies: ['inheritedStrings'], + transform(strings, {inheritedStrings}) { if (strings || inheritedStrings) { - return { ...(inheritedStrings ?? {}), ...(strings ?? {}) }; + return {...(inheritedStrings ?? {}), ...(strings ?? {})}; } else { return null; } @@ -1634,8 +1631,8 @@ Language.propertyDescriptors = { // May be provided to specify "default" strings, generally (but not // necessarily) inherited from another Language object. inheritedStrings: { - flags: { update: true, expose: true }, - update: { validate: (t) => typeof t === "object" }, + flags: {update: true, expose: true}, + update: {validate: (t) => typeof t === 'object'}, }, // Update only @@ -1644,20 +1641,20 @@ Language.propertyDescriptors = { // Expose only - intl_date: intlHelper(Intl.DateTimeFormat, { full: true }), + intl_date: intlHelper(Intl.DateTimeFormat, {full: true}), intl_number: intlHelper(Intl.NumberFormat), - intl_listConjunction: intlHelper(Intl.ListFormat, { type: "conjunction" }), - intl_listDisjunction: intlHelper(Intl.ListFormat, { type: "disjunction" }), - intl_listUnit: intlHelper(Intl.ListFormat, { type: "unit" }), - intl_pluralCardinal: intlHelper(Intl.PluralRules, { type: "cardinal" }), - intl_pluralOrdinal: intlHelper(Intl.PluralRules, { type: "ordinal" }), + intl_listConjunction: intlHelper(Intl.ListFormat, {type: 'conjunction'}), + intl_listDisjunction: intlHelper(Intl.ListFormat, {type: 'disjunction'}), + intl_listUnit: intlHelper(Intl.ListFormat, {type: 'unit'}), + intl_pluralCardinal: intlHelper(Intl.PluralRules, {type: 'cardinal'}), + intl_pluralOrdinal: intlHelper(Intl.PluralRules, {type: 'ordinal'}), validKeys: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["strings", "inheritedStrings"], - compute: ({ strings, inheritedStrings }) => + dependencies: ['strings', 'inheritedStrings'], + compute: ({strings, inheritedStrings}) => Array.from( new Set([ ...Object.keys(inheritedStrings ?? {}), @@ -1668,12 +1665,12 @@ Language.propertyDescriptors = { }, strings_htmlEscaped: { - flags: { expose: true }, + flags: {expose: true}, expose: { - dependencies: ["strings", "inheritedStrings", "escapeHTML"], - compute({ strings, inheritedStrings, escapeHTML }) { + dependencies: ['strings', 'inheritedStrings', 'escapeHTML'], + compute({strings, inheritedStrings, escapeHTML}) { if (!(strings || inheritedStrings) || !escapeHTML) return null; - const allStrings = { ...(inheritedStrings ?? {}), ...(strings ?? {}) }; + const allStrings = {...(inheritedStrings ?? {}), ...(strings ?? {})}; return Object.fromEntries( Object.entries(allStrings).map(([k, v]) => [k, escapeHTML(v)]) ); @@ -1683,12 +1680,12 @@ Language.propertyDescriptors = { }; const countHelper = (stringKey, argName = stringKey) => - function (value, { unit = false } = {}) { + function (value, {unit = false} = {}) { return this.$( unit ? `count.${stringKey}.withUnit.` + this.getUnitForm(value) : `count.${stringKey}`, - { [argName]: this.formatNumber(value) } + {[argName]: this.formatNumber(value)} ); }; @@ -1704,7 +1701,7 @@ Object.assign(Language.prototype, { }, getUnitForm(value) { - this.assertIntlAvailable("intl_pluralCardinal"); + this.assertIntlAvailable('intl_pluralCardinal'); return this.intl_pluralCardinal.select(value); }, @@ -1738,7 +1735,7 @@ Object.assign(Language.prototype, { // like, who cares, dude?) Also, this is an array, 8ecause it's handy // for the iterating we're a8out to do. const processedArgs = Object.entries(args).map(([k, v]) => [ - k.replace(/[A-Z]/g, "_$&").toUpperCase(), + k.replace(/[A-Z]/g, '_$&').toUpperCase(), v, ]); @@ -1758,55 +1755,55 @@ Object.assign(Language.prototype, { }, formatDate(date) { - this.assertIntlAvailable("intl_date"); + this.assertIntlAvailable('intl_date'); return this.intl_date.format(date); }, formatDateRange(startDate, endDate) { - this.assertIntlAvailable("intl_date"); + this.assertIntlAvailable('intl_date'); return this.intl_date.formatRange(startDate, endDate); }, - formatDuration(secTotal, { approximate = false, unit = false } = {}) { + formatDuration(secTotal, {approximate = false, unit = false} = {}) { if (secTotal === 0) { - return this.formatString("count.duration.missing"); + return this.formatString('count.duration.missing'); } const hour = Math.floor(secTotal / 3600); const min = Math.floor((secTotal - hour * 3600) / 60); const sec = Math.floor(secTotal - hour * 3600 - min * 60); - const pad = (val) => val.toString().padStart(2, "0"); + const pad = (val) => val.toString().padStart(2, '0'); - const stringSubkey = unit ? ".withUnit" : ""; + const stringSubkey = unit ? '.withUnit' : ''; const duration = hour > 0 - ? this.formatString("count.duration.hours" + stringSubkey, { + ? this.formatString('count.duration.hours' + stringSubkey, { hours: hour, minutes: pad(min), seconds: pad(sec), }) - : this.formatString("count.duration.minutes" + stringSubkey, { + : this.formatString('count.duration.minutes' + stringSubkey, { minutes: min, seconds: pad(sec), }); return approximate - ? this.formatString("count.duration.approximate", { duration }) + ? this.formatString('count.duration.approximate', {duration}) : duration; }, formatIndex(value) { - this.assertIntlAvailable("intl_pluralOrdinal"); + this.assertIntlAvailable('intl_pluralOrdinal'); return this.formatString( - "count.index." + this.intl_pluralOrdinal.select(value), - { index: value } + 'count.index.' + this.intl_pluralOrdinal.select(value), + {index: value} ); }, formatNumber(value) { - this.assertIntlAvailable("intl_number"); + this.assertIntlAvailable('intl_number'); return this.intl_number.format(value); }, @@ -1817,70 +1814,70 @@ Object.assign(Language.prototype, { const words = value > 1000 - ? this.formatString("count.words.thousand", { words: num }) - : this.formatString("count.words", { words: num }); + ? this.formatString('count.words.thousand', {words: num}) + : this.formatString('count.words', {words: num}); return this.formatString( - "count.words.withUnit." + this.getUnitForm(value), - { words } + 'count.words.withUnit.' + this.getUnitForm(value), + {words} ); }, // Conjunction list: A, B, and C formatConjunctionList(array) { - this.assertIntlAvailable("intl_listConjunction"); + this.assertIntlAvailable('intl_listConjunction'); return this.intl_listConjunction.format(array); }, // Disjunction lists: A, B, or C formatDisjunctionList(array) { - this.assertIntlAvailable("intl_listDisjunction"); + this.assertIntlAvailable('intl_listDisjunction'); return this.intl_listDisjunction.format(array); }, // Unit lists: A, B, C formatUnitList(array) { - this.assertIntlAvailable("intl_listUnit"); + this.assertIntlAvailable('intl_listUnit'); return this.intl_listUnit.format(array); }, // File sizes: 42.5 kB, 127.2 MB, 4.13 GB, 998.82 TB formatFileSize(bytes) { - if (!bytes) return ""; + if (!bytes) return ''; bytes = parseInt(bytes); - if (isNaN(bytes)) return ""; + if (isNaN(bytes)) return ''; const round = (exp) => Math.round(bytes / 10 ** (exp - 1)) / 10; if (bytes >= 10 ** 12) { - return this.formatString("count.fileSize.terabytes", { + return this.formatString('count.fileSize.terabytes', { terabytes: round(12), }); } else if (bytes >= 10 ** 9) { - return this.formatString("count.fileSize.gigabytes", { + return this.formatString('count.fileSize.gigabytes', { gigabytes: round(9), }); } else if (bytes >= 10 ** 6) { - return this.formatString("count.fileSize.megabytes", { + return this.formatString('count.fileSize.megabytes', { megabytes: round(6), }); } else if (bytes >= 10 ** 3) { - return this.formatString("count.fileSize.kilobytes", { + return this.formatString('count.fileSize.kilobytes', { kilobytes: round(3), }); } else { - return this.formatString("count.fileSize.bytes", { bytes }); + return this.formatString('count.fileSize.bytes', {bytes}); } }, // TODO: These are hard-coded. Is there a better way? - countAdditionalFiles: countHelper("additionalFiles", "files"), - countAlbums: countHelper("albums"), - countCommentaryEntries: countHelper("commentaryEntries", "entries"), - countContributions: countHelper("contributions"), - countCoverArts: countHelper("coverArts"), - countTimesReferenced: countHelper("timesReferenced"), - countTimesUsed: countHelper("timesUsed"), - countTracks: countHelper("tracks"), + countAdditionalFiles: countHelper('additionalFiles', 'files'), + countAlbums: countHelper('albums'), + countCommentaryEntries: countHelper('commentaryEntries', 'entries'), + countContributions: countHelper('contributions'), + countCoverArts: countHelper('coverArts'), + countTimesReferenced: countHelper('timesReferenced'), + countTimesUsed: countHelper('timesUsed'), + countTracks: countHelper('tracks'), }); diff --git a/src/data/validators.js b/src/data/validators.js index eab26896..8d922399 100644 --- a/src/data/validators.js +++ b/src/data/validators.js @@ -1,13 +1,13 @@ -// @format +/** @format */ -import { withAggregate } from "../util/sugar.js"; +import {withAggregate} from '../util/sugar.js'; -import { color, ENABLE_COLOR, decorateTime } from "../util/cli.js"; +import {color, ENABLE_COLOR} from '../util/cli.js'; -import { inspect as nodeInspect } from "util"; +import {inspect as nodeInspect} from 'util'; function inspect(value) { - return nodeInspect(value, { colors: ENABLE_COLOR }); + return nodeInspect(value, {colors: ENABLE_COLOR}); } // Basic types (primitives) @@ -24,11 +24,11 @@ function isType(value, type) { } export function isBoolean(value) { - return isType(value, "boolean"); + return isType(value, 'boolean'); } export function isNumber(value) { - return isType(value, "number"); + return isType(value, 'number'); } export function isPositive(number) { @@ -86,7 +86,7 @@ export function isWholeNumber(number) { } export function isString(value) { - return isType(value, "string"); + return isType(value, 'string'); } export function isStringNonEmpty(value) { @@ -116,7 +116,7 @@ export function isDate(value) { } export function isObject(value) { - isType(value, "object"); + isType(value, 'object'); // Note: Please remember that null is always a valid value for properties // held by a CacheableObject. This assertion is exclusively for use in other @@ -127,7 +127,7 @@ export function isObject(value) { } export function isArray(value) { - if (typeof value !== "object" || value === null || !Array.isArray(value)) + if (typeof value !== 'object' || value === null || !Array.isArray(value)) throw new TypeError(`Expected an array, got ${value}`); return true; @@ -156,7 +156,7 @@ export function validateArrayItems(itemValidator) { return (array) => { isArray(array); - withAggregate({ message: "Errors validating array items" }, ({ wrap }) => { + withAggregate({message: 'Errors validating array items'}, ({wrap}) => { array.forEach(wrap(fn)); }); @@ -173,7 +173,7 @@ export function validateInstanceOf(constructor) { export function isColor(color) { isStringNonEmpty(color); - if (color.startsWith("#")) { + if (color.startsWith('#')) { if (![1 + 3, 1 + 4, 1 + 6, 1 + 8].includes(color.length)) throw new TypeError( `Expected #rgb, #rgba, #rrggbb, or #rrggbbaa, got length ${color.length}` @@ -192,7 +192,7 @@ export function isCommentary(commentary) { return isString(commentary); } -const isArtistRef = validateReference("artist"); +const isArtistRef = validateReference('artist'); export function validateProperties(spec) { const specEntries = Object.entries(spec); @@ -205,8 +205,8 @@ export function validateProperties(spec) { throw new TypeError(`Expected an object, got array`); withAggregate( - { message: `Errors validating object properties` }, - ({ call }) => { + {message: `Errors validating object properties`}, + ({call}) => { for (const [specKey, specValidator] of specEntries) { call(() => { const value = object[specKey]; @@ -229,7 +229,7 @@ export function validateProperties(spec) { throw new Error( `Unknown keys present (${ unknownKeys.length - }): [${unknownKeys.join(", ")}]` + }): [${unknownKeys.join(', ')}]` ); }); } @@ -273,7 +273,7 @@ export function isDimensions(dimensions) { export function isDirectory(directory) { isStringNonEmpty(directory); - if (directory.match(/[^a-zA-Z0-9_\-]/)) + if (directory.match(/[^a-zA-Z0-9_-]/)) throw new TypeError( `Expected only letters, numbers, dash, and underscore, got "${directory}"` ); @@ -291,7 +291,7 @@ export function isDuration(duration) { export function isFileExtension(string) { isStringNonEmpty(string); - if (string[0] === ".") + if (string[0] === '.') throw new TypeError(`Expected no dot (.) at the start of file extension`); if (string.match(/[^a-zA-Z0-9_]/)) @@ -321,7 +321,7 @@ export function isURL(string) { return true; } -export function validateReference(type = "track") { +export function validateReference(type = 'track') { return (ref) => { isStringNonEmpty(ref); @@ -332,7 +332,7 @@ export function validateReference(type = "track") { if (!match) throw new TypeError(`Malformed reference`); const { - groups: { typePart, directoryPart }, + groups: {typePart, directoryPart}, } = match; if (typePart && typePart !== type) @@ -348,7 +348,7 @@ export function validateReference(type = "track") { }; } -export function validateReferenceList(type = "") { +export function validateReferenceList(type = '') { return validateArrayItems(validateReference(type)); } diff --git a/src/data/yaml.js b/src/data/yaml.js index 5058bb39..a4255764 100644 --- a/src/data/yaml.js +++ b/src/data/yaml.js @@ -1,13 +1,13 @@ -// @format -// +/** @format */ + // yaml.js - specification for HSMusic YAML data file format and utilities for // loading and processing YAML files and documents -import * as path from "path"; -import yaml from "js-yaml"; +import * as path from 'path'; +import yaml from 'js-yaml'; -import { readFile } from "fs/promises"; -import { inspect as nodeInspect } from "util"; +import {readFile} from 'fs/promises'; +import {inspect as nodeInspect} from 'util'; import { Album, @@ -19,16 +19,15 @@ import { GroupCategory, HomepageLayout, HomepageLayoutAlbumsRow, - HomepageLayoutRow, NewsEntry, StaticPage, Thing, Track, TrackGroup, WikiInfo, -} from "./things.js"; +} from './things.js'; -import { color, ENABLE_COLOR, logInfo, logWarn } from "../util/cli.js"; +import {color, ENABLE_COLOR, logInfo, logWarn} from '../util/cli.js'; import { decorateErrorWithIndex, @@ -36,36 +35,36 @@ import { openAggregate, showAggregate, withAggregate, -} from "../util/sugar.js"; +} from '../util/sugar.js'; import { sortAlbumsTracksChronologically, sortAlphabetically, sortChronologically, -} from "../util/wiki-data.js"; +} from '../util/wiki-data.js'; -import find, { bindFind } from "../util/find.js"; -import { findFiles } from "../util/io.js"; +import find, {bindFind} from '../util/find.js'; +import {findFiles} from '../util/io.js'; // --> General supporting stuff function inspect(value) { - return nodeInspect(value, { colors: ENABLE_COLOR }); + return nodeInspect(value, {colors: ENABLE_COLOR}); } // --> YAML data repository structure constants -export const WIKI_INFO_FILE = "wiki-info.yaml"; -export const BUILD_DIRECTIVE_DATA_FILE = "build-directives.yaml"; -export const HOMEPAGE_LAYOUT_DATA_FILE = "homepage.yaml"; -export const ARTIST_DATA_FILE = "artists.yaml"; -export const FLASH_DATA_FILE = "flashes.yaml"; -export const NEWS_DATA_FILE = "news.yaml"; -export const ART_TAG_DATA_FILE = "tags.yaml"; -export const GROUP_DATA_FILE = "groups.yaml"; -export const STATIC_PAGE_DATA_FILE = "static-pages.yaml"; +export const WIKI_INFO_FILE = 'wiki-info.yaml'; +export const BUILD_DIRECTIVE_DATA_FILE = 'build-directives.yaml'; +export const HOMEPAGE_LAYOUT_DATA_FILE = 'homepage.yaml'; +export const ARTIST_DATA_FILE = 'artists.yaml'; +export const FLASH_DATA_FILE = 'flashes.yaml'; +export const NEWS_DATA_FILE = 'news.yaml'; +export const ART_TAG_DATA_FILE = 'tags.yaml'; +export const GROUP_DATA_FILE = 'groups.yaml'; +export const STATIC_PAGE_DATA_FILE = 'static-pages.yaml'; -export const DATA_ALBUM_DIRECTORY = "album"; +export const DATA_ALBUM_DIRECTORY = 'album'; // --> Document processing functions @@ -119,7 +118,7 @@ function makeProcessDocument( ); const decorateErrorWithName = (fn) => { - const nameField = propertyFieldMapping["name"]; + const nameField = propertyFieldMapping['name']; if (!nameField) return fn; return (document) => { @@ -168,8 +167,8 @@ function makeProcessDocument( const thing = Reflect.construct(thingClass, []); withAggregate( - { message: `Errors applying ${color.green(thingClass.name)} properties` }, - ({ call }) => { + {message: `Errors applying ${color.green(thingClass.name)} properties`}, + ({call}) => { for (const [property, value] of Object.entries(sourceProperties)) { call(() => (thing[property] = value)); } @@ -184,7 +183,7 @@ makeProcessDocument.UnknownFieldsError = class UnknownFieldsError extends ( Error ) { constructor(fields) { - super(`Unknown fields present: ${fields.join(", ")}`); + super(`Unknown fields present: ${fields.join(', ')}`); this.fields = fields; } }; @@ -192,72 +191,72 @@ makeProcessDocument.UnknownFieldsError = class UnknownFieldsError extends ( export const processAlbumDocument = makeProcessDocument(Album, { fieldTransformations: { Artists: parseContributors, - "Cover Artists": parseContributors, - "Default Track Cover Artists": parseContributors, - "Wallpaper Artists": parseContributors, - "Banner 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), + '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, + 'Banner Dimensions': parseDimensions, - "Additional Files": parseAdditionalFiles, + 'Additional Files': parseAdditionalFiles, }, propertyFieldMapping: { - name: "Album", + name: 'Album', - color: "Color", - directory: "Directory", - urls: "URLs", + color: 'Color', + directory: 'Directory', + urls: 'URLs', - artistContribsByRef: "Artists", - coverArtistContribsByRef: "Cover Artists", - trackCoverArtistContribsByRef: "Default Track Cover Artists", + artistContribsByRef: 'Artists', + coverArtistContribsByRef: 'Cover Artists', + trackCoverArtistContribsByRef: 'Default Track Cover Artists', - coverArtFileExtension: "Cover Art File Extension", - trackCoverArtFileExtension: "Track Art File Extension", + coverArtFileExtension: 'Cover Art File Extension', + trackCoverArtFileExtension: 'Track Art File Extension', - wallpaperArtistContribsByRef: "Wallpaper Artists", - wallpaperStyle: "Wallpaper Style", - wallpaperFileExtension: "Wallpaper File Extension", + wallpaperArtistContribsByRef: 'Wallpaper Artists', + wallpaperStyle: 'Wallpaper Style', + wallpaperFileExtension: 'Wallpaper File Extension', - bannerArtistContribsByRef: "Banner Artists", - bannerStyle: "Banner Style", - bannerFileExtension: "Banner File Extension", - bannerDimensions: "Banner Dimensions", + bannerArtistContribsByRef: 'Banner Artists', + bannerStyle: 'Banner Style', + bannerFileExtension: 'Banner File Extension', + bannerDimensions: 'Banner Dimensions', - date: "Date", - trackArtDate: "Default Track Cover Art Date", - coverArtDate: "Cover Art Date", - dateAddedToWiki: "Date Added", + date: 'Date', + trackArtDate: 'Default Track Cover Art Date', + coverArtDate: 'Cover Art Date', + dateAddedToWiki: 'Date Added', - hasCoverArt: "Has Cover Art", - hasTrackArt: "Has Track Art", - hasTrackNumbers: "Has Track Numbers", - isMajorRelease: "Major Release", - isListedOnHomepage: "Listed on Homepage", + hasCoverArt: 'Has Cover Art', + hasTrackArt: 'Has Track Art', + hasTrackNumbers: 'Has Track Numbers', + isMajorRelease: 'Major Release', + isListedOnHomepage: 'Listed on Homepage', - groupsByRef: "Groups", - artTagsByRef: "Art Tags", - commentary: "Commentary", + groupsByRef: 'Groups', + artTagsByRef: 'Art Tags', + commentary: 'Commentary', - additionalFiles: "Additional Files", + additionalFiles: 'Additional Files', }, }); export const processTrackGroupDocument = makeProcessDocument(TrackGroup, { fieldTransformations: { - "Date Originally Released": (value) => new Date(value), + 'Date Originally Released': (value) => new Date(value), }, propertyFieldMapping: { - name: "Group", - color: "Color", - dateOriginallyReleased: "Date Originally Released", + name: 'Group', + color: 'Color', + dateOriginallyReleased: 'Date Originally Released', }, }); @@ -265,60 +264,60 @@ export const processTrackDocument = makeProcessDocument(Track, { fieldTransformations: { Duration: getDurationInSeconds, - "Date First Released": (value) => new Date(value), - "Cover Art Date": (value) => new Date(value), + 'Date First Released': (value) => new Date(value), + 'Cover Art Date': (value) => new Date(value), Artists: parseContributors, Contributors: parseContributors, - "Cover Artists": parseContributors, + 'Cover Artists': parseContributors, - "Additional Files": parseAdditionalFiles, + 'Additional Files': parseAdditionalFiles, }, propertyFieldMapping: { - name: "Track", + name: 'Track', - directory: "Directory", - duration: "Duration", - urls: "URLs", + directory: 'Directory', + duration: 'Duration', + urls: 'URLs', - coverArtDate: "Cover Art Date", - coverArtFileExtension: "Cover Art File Extension", - dateFirstReleased: "Date First Released", - hasCoverArt: "Has Cover Art", - hasURLs: "Has URLs", + coverArtDate: 'Cover Art Date', + coverArtFileExtension: 'Cover Art File Extension', + dateFirstReleased: 'Date First Released', + hasCoverArt: 'Has Cover Art', + hasURLs: 'Has URLs', - referencedTracksByRef: "Referenced Tracks", - artistContribsByRef: "Artists", - contributorContribsByRef: "Contributors", - coverArtistContribsByRef: "Cover Artists", - artTagsByRef: "Art Tags", - originalReleaseTrackByRef: "Originally Released As", + referencedTracksByRef: 'Referenced Tracks', + artistContribsByRef: 'Artists', + contributorContribsByRef: 'Contributors', + coverArtistContribsByRef: 'Cover Artists', + artTagsByRef: 'Art Tags', + originalReleaseTrackByRef: 'Originally Released As', - commentary: "Commentary", - lyrics: "Lyrics", + commentary: 'Commentary', + lyrics: 'Lyrics', - additionalFiles: "Additional Files", + additionalFiles: 'Additional Files', }, - ignoredFields: ["Sampled Tracks"], + ignoredFields: ['Sampled Tracks'], }); export const processArtistDocument = makeProcessDocument(Artist, { propertyFieldMapping: { - name: "Artist", + name: 'Artist', - directory: "Directory", - urls: "URLs", - hasAvatar: "Has Avatar", - avatarFileExtension: "Avatar File Extension", + directory: 'Directory', + urls: 'URLs', + hasAvatar: 'Has Avatar', + avatarFileExtension: 'Avatar File Extension', - aliasNames: "Aliases", + aliasNames: 'Aliases', - contextNotes: "Context Notes", + contextNotes: 'Context Notes', }, - ignoredFields: ["Dead URLs"], + ignoredFields: ['Dead URLs'], }); export const processFlashDocument = makeProcessDocument(Flash, { @@ -329,26 +328,26 @@ export const processFlashDocument = makeProcessDocument(Flash, { }, propertyFieldMapping: { - name: "Flash", + name: 'Flash', - directory: "Directory", - page: "Page", - date: "Date", - coverArtFileExtension: "Cover Art File Extension", + directory: 'Directory', + page: 'Page', + date: 'Date', + coverArtFileExtension: 'Cover Art File Extension', - featuredTracksByRef: "Featured Tracks", - contributorContribsByRef: "Contributors", - urls: "URLs", + featuredTracksByRef: 'Featured Tracks', + contributorContribsByRef: 'Contributors', + urls: 'URLs', }, }); export const processFlashActDocument = makeProcessDocument(FlashAct, { propertyFieldMapping: { - name: "Act", - color: "Color", - anchor: "Anchor", - jump: "Jump", - jumpColor: "Jump Color", + name: 'Act', + color: 'Color', + anchor: 'Anchor', + jump: 'Jump', + jumpColor: 'Jump Color', }, }); @@ -358,66 +357,66 @@ export const processNewsEntryDocument = makeProcessDocument(NewsEntry, { }, propertyFieldMapping: { - name: "Name", - directory: "Directory", - date: "Date", - content: "Content", + name: 'Name', + directory: 'Directory', + date: 'Date', + content: 'Content', }, }); export const processArtTagDocument = makeProcessDocument(ArtTag, { propertyFieldMapping: { - name: "Tag", - directory: "Directory", - color: "Color", - isContentWarning: "Is CW", + name: 'Tag', + directory: 'Directory', + color: 'Color', + isContentWarning: 'Is CW', }, }); export const processGroupDocument = makeProcessDocument(Group, { propertyFieldMapping: { - name: "Group", - directory: "Directory", - description: "Description", - urls: "URLs", + name: 'Group', + directory: 'Directory', + description: 'Description', + urls: 'URLs', }, }); export const processGroupCategoryDocument = makeProcessDocument(GroupCategory, { propertyFieldMapping: { - name: "Category", - color: "Color", + name: 'Category', + color: 'Color', }, }); export const processStaticPageDocument = makeProcessDocument(StaticPage, { propertyFieldMapping: { - name: "Name", - nameShort: "Short Name", - directory: "Directory", + name: 'Name', + nameShort: 'Short Name', + directory: 'Directory', - content: "Content", - stylesheet: "Style", + content: 'Content', + stylesheet: 'Style', - showInNavigationBar: "Show in Navigation Bar", + showInNavigationBar: 'Show in Navigation Bar', }, }); export const processWikiInfoDocument = makeProcessDocument(WikiInfo, { propertyFieldMapping: { - name: "Name", - nameShort: "Short Name", - color: "Color", - description: "Description", - footerContent: "Footer Content", - defaultLanguage: "Default Language", - canonicalBase: "Canonical Base", - divideTrackListsByGroupsByRef: "Divide Track Lists By Groups", - enableFlashesAndGames: "Enable Flashes & Games", - enableListings: "Enable Listings", - enableNews: "Enable News", - enableArtTagUI: "Enable Art Tag UI", - enableGroupUI: "Enable Group UI", + name: 'Name', + nameShort: 'Short Name', + color: 'Color', + description: 'Description', + footerContent: 'Footer Content', + defaultLanguage: 'Default Language', + canonicalBase: 'Canonical Base', + divideTrackListsByGroupsByRef: 'Divide Track Lists By Groups', + enableFlashesAndGames: 'Enable Flashes & Games', + enableListings: 'Enable Listings', + enableNews: 'Enable News', + enableArtTagUI: 'Enable Art Tag UI', + enableGroupUI: 'Enable Group UI', }, }); @@ -425,10 +424,10 @@ export const processHomepageLayoutDocument = makeProcessDocument( HomepageLayout, { propertyFieldMapping: { - sidebarContent: "Sidebar Content", + sidebarContent: 'Sidebar Content', }, - ignoredFields: ["Homepage"], + ignoredFields: ['Homepage'], } ); @@ -437,9 +436,9 @@ export function makeProcessHomepageLayoutRowDocument(rowClass, spec) { ...spec, propertyFieldMapping: { - name: "Row", - color: "Color", - type: "Type", + name: 'Row', + color: 'Color', + type: 'Type', ...spec.propertyFieldMapping, }, }); @@ -448,16 +447,16 @@ export function makeProcessHomepageLayoutRowDocument(rowClass, spec) { export const homepageLayoutRowTypeProcessMapping = { albums: makeProcessHomepageLayoutRowDocument(HomepageLayoutAlbumsRow, { propertyFieldMapping: { - sourceGroupByRef: "Group", - countAlbumsFromGroup: "Count", - sourceAlbumsByRef: "Albums", - actionLinks: "Actions", + sourceGroupByRef: 'Group', + countAlbumsFromGroup: 'Count', + sourceAlbumsByRef: 'Albums', + actionLinks: 'Actions', }, }), }; export function processHomepageLayoutRowDocument(document) { - const type = document["Type"]; + const type = document['Type']; const match = Object.entries(homepageLayoutRowTypeProcessMapping).find( ([key]) => key === type @@ -473,15 +472,15 @@ export function processHomepageLayoutRowDocument(document) { // --> Utilities shared across document parsing functions export function getDurationInSeconds(string) { - if (typeof string === "number") { + if (typeof string === 'number') { return string; } - if (typeof string !== "string") { + if (typeof string !== 'string') { throw new TypeError(`Expected a string or number, got ${string}`); } - const parts = string.split(":").map((n) => parseInt(n)); + const parts = string.split(':').map((n) => parseInt(n)); if (parts.length === 3) { return parts[0] * 3600 + parts[1] * 60 + parts[2]; } else if (parts.length === 2) { @@ -499,16 +498,16 @@ export function parseAdditionalFiles(array) { } return array.map((item) => ({ - title: item["Title"], - description: item["Description"] ?? null, - files: item["Files"], + title: item['Title'], + description: item['Description'] ?? null, + files: item['Files'], })); } export function parseCommentary(text) { if (text) { - const lines = String(text).split("\n"); - if (!lines[0].replace(/<\/b>/g, "").includes(":</i>")) { + const lines = String(text).split('\n'); + if (!lines[0].replace(/<\/b>/g, '').includes(':</i>')) { return { error: `An entry is missing commentary citation: "${lines[0].slice( 0, @@ -527,7 +526,7 @@ export function parseContributors(contributors) { return null; } - if (contributors.length === 1 && contributors[0].startsWith("<i>")) { + if (contributors.length === 1 && contributors[0].startsWith('<i>')) { const arr = []; arr.textContent = contributors[0]; return arr; @@ -542,17 +541,17 @@ export function parseContributors(contributors) { } const who = match[1]; const what = match[3] || null; - return { who, what }; + return {who, what}; }); - const badContributor = contributors.find((val) => typeof val === "string"); + const badContributor = contributors.find((val) => typeof val === 'string'); if (badContributor) { return { error: `An entry has an incorrectly formatted contributor, "${badContributor}".`, }; } - if (contributors.length === 1 && contributors[0].who === "none") { + if (contributors.length === 1 && contributors[0].who === 'none') { return null; } @@ -584,7 +583,7 @@ export const documentModes = { // processDocument function. Obviously, each specified data file should only // contain one YAML document (an error will be thrown otherwise). Calls save // with an array of processed documents (wiki objects). - onePerFile: Symbol("Document mode: onePerFile"), + onePerFile: Symbol('Document mode: onePerFile'), // headerAndEntries: One or more documents per file; the first document is // treated as a "header" and represents data which pertains to all following @@ -599,12 +598,12 @@ export const documentModes = { // aggregate. However, if the processHeaderDocument function fails, all // following documents in the same file will be ignored as well (i.e. an // entire file will be excempt from the save() function's input). - headerAndEntries: Symbol("Document mode: headerAndEntries"), + headerAndEntries: Symbol('Document mode: headerAndEntries'), // allInOne: One or more documents, all contained in one file. Expects file // string (or function) and processDocument function. Calls save with an // array of processed documents (wiki objects). - allInOne: Symbol("Document mode: allInOne"), + allInOne: Symbol('Document mode: allInOne'), // oneDocumentTotal: Just a single document, represented in one file. // Expects file string (or function) and processDocument function. Calls @@ -614,7 +613,7 @@ export const documentModes = { // function won't be called at all, generally resulting in an altogether // missing property from the global wikiData object. This should be caught // and handled externally. - oneDocumentTotal: Symbol("Document mode: oneDocumentTotal"), + oneDocumentTotal: Symbol('Document mode: oneDocumentTotal'), }; // dataSteps: Top-level array of "steps" for loading YAML document files. @@ -662,7 +661,7 @@ export const dataSteps = [ return; } - return { wikiInfo }; + return {wikiInfo}; }, }, @@ -671,7 +670,7 @@ export const dataSteps = [ files: async (dataPath) => ( await findFiles(path.join(dataPath, DATA_ALBUM_DIRECTORY), { - filter: (f) => path.extname(f) === ".yaml", + filter: (f) => path.extname(f) === '.yaml', joinParentDirectory: false, }) ).map((file) => path.join(DATA_ALBUM_DIRECTORY, file)), @@ -679,7 +678,7 @@ export const dataSteps = [ documentMode: documentModes.headerAndEntries, processHeaderDocument: processAlbumDocument, processEntryDocument(document) { - return "Group" in document + return 'Group' in document ? processTrackGroupDocument(document) : processTrackDocument(document); }, @@ -688,7 +687,7 @@ export const dataSteps = [ const albumData = []; const trackData = []; - for (const { header: album, entries } of results) { + for (const {header: album, entries} of results) { // We can't mutate an array once it's set as a property // value, so prepare the tracks and track groups that will // show up in a track list all the way before actually @@ -699,7 +698,7 @@ export const dataSteps = [ const albumRef = Thing.getReference(album); - function closeCurrentTrackGroup() { + const closeCurrentTrackGroup = () => { if (currentTracksByRef) { let trackGroup; @@ -715,7 +714,7 @@ export const dataSteps = [ trackGroup.tracksByRef = currentTracksByRef; trackGroups.push(trackGroup); } - } + }; for (const entry of entries) { if (entry instanceof TrackGroup) { @@ -743,7 +742,7 @@ export const dataSteps = [ albumData.push(album); } - return { albumData, trackData }; + return {albumData, trackData}; }, }, @@ -771,7 +770,7 @@ export const dataSteps = [ ); }); - return { artistData, artistAliasData }; + return {artistData, artistAliasData}; }, }, @@ -782,7 +781,7 @@ export const dataSteps = [ documentMode: documentModes.allInOne, processDocument(document) { - return "Act" in document + return 'Act' in document ? processFlashActDocument(document) : processFlashDocument(document); }, @@ -798,7 +797,7 @@ export const dataSteps = [ for (const thing of results) { if (thing instanceof FlashAct) { if (flashAct) { - Object.assign(flashAct, { flashesByRef }); + Object.assign(flashAct, {flashesByRef}); } flashAct = thing; @@ -809,13 +808,13 @@ export const dataSteps = [ } if (flashAct) { - Object.assign(flashAct, { flashesByRef }); + Object.assign(flashAct, {flashesByRef}); } const flashData = results.filter((x) => x instanceof Flash); const flashActData = results.filter((x) => x instanceof FlashAct); - return { flashData, flashActData }; + return {flashData, flashActData}; }, }, @@ -825,7 +824,7 @@ export const dataSteps = [ documentMode: documentModes.allInOne, processDocument(document) { - return "Category" in document + return 'Category' in document ? processGroupCategoryDocument(document) : processGroupDocument(document); }, @@ -841,7 +840,7 @@ export const dataSteps = [ for (const thing of results) { if (thing instanceof GroupCategory) { if (groupCategory) { - Object.assign(groupCategory, { groupsByRef }); + Object.assign(groupCategory, {groupsByRef}); } groupCategory = thing; @@ -852,7 +851,7 @@ export const dataSteps = [ } if (groupCategory) { - Object.assign(groupCategory, { groupsByRef }); + Object.assign(groupCategory, {groupsByRef}); } const groupData = results.filter((x) => x instanceof Group); @@ -860,7 +859,7 @@ export const dataSteps = [ (x) => x instanceof GroupCategory ); - return { groupData, groupCategoryData }; + return {groupData, groupCategoryData}; }, }, @@ -877,9 +876,9 @@ export const dataSteps = [ return; } - const { header: homepageLayout, entries: rows } = results[0]; - Object.assign(homepageLayout, { rows }); - return { homepageLayout }; + const {header: homepageLayout, entries: rows} = results[0]; + Object.assign(homepageLayout, {rows}); + return {homepageLayout}; }, }, @@ -895,7 +894,7 @@ export const dataSteps = [ sortChronologically(newsData); newsData.reverse(); - return { newsData }; + return {newsData}; }, }, @@ -909,7 +908,7 @@ export const dataSteps = [ save(artTagData) { sortAlphabetically(artTagData); - return { artTagData }; + return {artTagData}; }, }, @@ -921,12 +920,12 @@ export const dataSteps = [ processDocument: processStaticPageDocument, save(staticPageData) { - return { staticPageData }; + return {staticPageData}; }, }, ]; -export async function loadAndProcessDataDocuments({ dataPath }) { +export async function loadAndProcessDataDocuments({dataPath}) { const processDataAggregate = openAggregate({ message: `Errors processing data files`, }); @@ -938,7 +937,7 @@ export async function loadAndProcessDataDocuments({ dataPath }) { return fn(x, index, array); } catch (error) { error.message += - (error.message.includes("\n") ? "\n" : " ") + + (error.message.includes('\n') ? '\n' : ' ') + `(file: ${color.bright( color.blue(path.relative(dataPath, x.file)) )})`; @@ -949,9 +948,9 @@ export async function loadAndProcessDataDocuments({ dataPath }) { for (const dataStep of dataSteps) { await processDataAggregate.nestAsync( - { message: `Errors during data step: ${dataStep.title}` }, - async ({ call, callAsync, map, mapAsync, nest }) => { - const { documentMode } = dataStep; + {message: `Errors during data step: ${dataStep.title}`}, + async ({call, callAsync, map, mapAsync, nest}) => { + const {documentMode} = dataStep; if (!Object.values(documentModes).includes(documentMode)) { throw new Error(`Invalid documentMode: ${documentMode.toString()}`); @@ -969,12 +968,12 @@ export async function loadAndProcessDataDocuments({ dataPath }) { const file = path.join( dataPath, - typeof dataStep.file === "function" + typeof dataStep.file === 'function' ? await callAsync(dataStep.file, dataPath) : dataStep.file ); - const readResult = await callAsync(readFile, file, "utf-8"); + const readResult = await callAsync(readFile, file, 'utf-8'); if (!readResult) { return; @@ -992,14 +991,14 @@ export async function loadAndProcessDataDocuments({ dataPath }) { let processResults; if (documentMode === documentModes.oneDocumentTotal) { - nest({ message: `Errors processing document` }, ({ call }) => { + nest({message: `Errors processing document`}, ({call}) => { processResults = call(dataStep.processDocument, yamlResult); }); } else { - const { result, aggregate } = mapAggregate( + const {result, aggregate} = mapAggregate( yamlResult, decorateErrorWithIndex(dataStep.processDocument), - { message: `Errors processing documents` } + {message: `Errors processing documents`} ); processResults = result; call(aggregate.close); @@ -1023,7 +1022,7 @@ export async function loadAndProcessDataDocuments({ dataPath }) { } const files = ( - typeof dataStep.files === "function" + typeof dataStep.files === 'function' ? await callAsync(dataStep.files, dataPath) : dataStep.files ).map((file) => path.join(dataPath, file)); @@ -1031,35 +1030,35 @@ export async function loadAndProcessDataDocuments({ dataPath }) { const readResults = await mapAsync( files, (file) => - readFile(file, "utf-8").then((contents) => ({ file, contents })), - { message: `Errors reading data files` } + readFile(file, 'utf-8').then((contents) => ({file, contents})), + {message: `Errors reading data files`} ); const yamlResults = map( readResults, - decorateErrorWithFile(({ file, contents }) => ({ + decorateErrorWithFile(({file, contents}) => ({ file, documents: yaml.loadAll(contents), })), - { message: `Errors parsing data files as valid YAML` } + {message: `Errors parsing data files as valid YAML`} ); let processResults; if (documentMode === documentModes.headerAndEntries) { nest( - { message: `Errors processing data files as valid documents` }, - ({ call, map }) => { + {message: `Errors processing data files as valid documents`}, + ({call, map}) => { processResults = []; - yamlResults.forEach(({ file, documents }) => { + yamlResults.forEach(({file, documents}) => { const [headerDocument, ...entryDocuments] = documents; const header = call( - decorateErrorWithFile(({ document }) => + decorateErrorWithFile(({document}) => dataStep.processHeaderDocument(document) ), - { file, document: headerDocument } + {file, document: headerDocument} ); // Don't continue processing files whose header @@ -1070,13 +1069,13 @@ export async function loadAndProcessDataDocuments({ dataPath }) { } const entries = map( - entryDocuments.map((document) => ({ file, document })), + entryDocuments.map((document) => ({file, document})), decorateErrorWithFile( - decorateErrorWithIndex(({ document }) => + decorateErrorWithIndex(({document}) => dataStep.processEntryDocument(document) ) ), - { message: `Errors processing entry documents` } + {message: `Errors processing entry documents`} ); // Entries may be incomplete (i.e. any errored @@ -1084,7 +1083,7 @@ export async function loadAndProcessDataDocuments({ dataPath }) { // represented here) - this is intentional! By // principle, partial output is preferred over // erroring an entire file. - processResults.push({ header, entries }); + processResults.push({header, entries}); }); } ); @@ -1092,11 +1091,11 @@ export async function loadAndProcessDataDocuments({ dataPath }) { if (documentMode === documentModes.onePerFile) { nest( - { message: `Errors processing data files as valid documents` }, - ({ call, map }) => { + {message: `Errors processing data files as valid documents`}, + ({call}) => { processResults = []; - yamlResults.forEach(({ file, documents }) => { + yamlResults.forEach(({file, documents}) => { if (documents.length > 1) { call( decorateErrorWithFile(() => { @@ -1109,10 +1108,10 @@ export async function loadAndProcessDataDocuments({ dataPath }) { } const result = call( - decorateErrorWithFile(({ document }) => + decorateErrorWithFile(({document}) => dataStep.processDocument(document) ), - { file, document: documents[0] } + {file, document: documents[0]} ); if (!result) { @@ -1155,40 +1154,40 @@ export function linkWikiDataArrays(wikiData) { const WD = wikiData; - assignWikiData([WD.wikiInfo], "groupData"); + assignWikiData([WD.wikiInfo], 'groupData'); assignWikiData( WD.albumData, - "artistData", - "artTagData", - "groupData", - "trackData" + 'artistData', + 'artTagData', + 'groupData', + 'trackData' ); WD.albumData.forEach((album) => - assignWikiData(album.trackGroups, "trackData") + assignWikiData(album.trackGroups, 'trackData') ); assignWikiData( WD.trackData, - "albumData", - "artistData", - "artTagData", - "flashData", - "trackData" + 'albumData', + 'artistData', + 'artTagData', + 'flashData', + 'trackData' ); assignWikiData( WD.artistData, - "albumData", - "artistData", - "flashData", - "trackData" + 'albumData', + 'artistData', + 'flashData', + 'trackData' ); - assignWikiData(WD.groupData, "albumData", "groupCategoryData"); - assignWikiData(WD.groupCategoryData, "groupData"); - assignWikiData(WD.flashData, "artistData", "flashActData", "trackData"); - assignWikiData(WD.flashActData, "flashData"); - assignWikiData(WD.artTagData, "albumData", "trackData"); - assignWikiData(WD.homepageLayout.rows, "albumData", "groupData"); + assignWikiData(WD.groupData, 'albumData', 'groupCategoryData'); + assignWikiData(WD.groupCategoryData, 'groupData'); + assignWikiData(WD.flashData, 'artistData', 'flashActData', 'trackData'); + assignWikiData(WD.flashActData, 'flashData'); + assignWikiData(WD.artTagData, 'albumData', 'trackData'); + assignWikiData(WD.homepageLayout.rows, 'albumData', 'groupData'); } export function sortWikiDataArrays(wikiData) { @@ -1213,28 +1212,28 @@ export function sortWikiDataArrays(wikiData) { // build, for example). export function filterDuplicateDirectories(wikiData) { const deduplicateSpec = [ - "albumData", - "artTagData", - "flashData", - "groupData", - "newsData", - "trackData", + 'albumData', + 'artTagData', + 'flashData', + 'groupData', + 'newsData', + 'trackData', ]; - const aggregate = openAggregate({ message: `Duplicate directories found` }); + const aggregate = openAggregate({message: `Duplicate directories found`}); for (const thingDataProp of deduplicateSpec) { const thingData = wikiData[thingDataProp]; aggregate.nest( { message: `Duplicate directories found in ${color.green( - "wikiData." + thingDataProp + 'wikiData.' + thingDataProp )}`, }, - ({ call }) => { + ({call}) => { const directoryPlaces = Object.create(null); const duplicateDirectories = []; for (const thing of thingData) { - const { directory } = thing; + const {directory} = thing; if (directory in directoryPlaces) { directoryPlaces[directory].push(thing); duplicateDirectories.push(directory); @@ -1253,7 +1252,7 @@ export function filterDuplicateDirectories(wikiData) { call(() => { throw new Error( `Duplicate directory ${color.green(directory)}:\n` + - places.map((thing) => ` - ` + inspect(thing)).join("\n") + places.map((thing) => ` - ` + inspect(thing)).join('\n') ); }); } @@ -1292,64 +1291,64 @@ export function filterDuplicateDirectories(wikiData) { export function filterReferenceErrors(wikiData) { const referenceSpec = [ [ - "wikiInfo", + 'wikiInfo', { - divideTrackListsByGroupsByRef: "group", + divideTrackListsByGroupsByRef: 'group', }, ], [ - "albumData", + 'albumData', { - artistContribsByRef: "_contrib", - coverArtistContribsByRef: "_contrib", - trackCoverArtistContribsByRef: "_contrib", - wallpaperArtistContribsByRef: "_contrib", - bannerArtistContribsByRef: "_contrib", - groupsByRef: "group", - artTagsByRef: "artTag", + artistContribsByRef: '_contrib', + coverArtistContribsByRef: '_contrib', + trackCoverArtistContribsByRef: '_contrib', + wallpaperArtistContribsByRef: '_contrib', + bannerArtistContribsByRef: '_contrib', + groupsByRef: 'group', + artTagsByRef: 'artTag', }, ], [ - "trackData", + 'trackData', { - artistContribsByRef: "_contrib", - contributorContribsByRef: "_contrib", - coverArtistContribsByRef: "_contrib", - referencedTracksByRef: "track", - artTagsByRef: "artTag", - originalReleaseTrackByRef: "track", + artistContribsByRef: '_contrib', + contributorContribsByRef: '_contrib', + coverArtistContribsByRef: '_contrib', + referencedTracksByRef: 'track', + artTagsByRef: 'artTag', + originalReleaseTrackByRef: 'track', }, ], [ - "groupCategoryData", + 'groupCategoryData', { - groupsByRef: "group", + groupsByRef: 'group', }, ], [ - "homepageLayout.rows", + 'homepageLayout.rows', { - sourceGroupsByRef: "group", - sourceAlbumsByRef: "album", + sourceGroupsByRef: 'group', + sourceAlbumsByRef: 'album', }, ], [ - "flashData", + 'flashData', { - contributorContribsByRef: "_contrib", - featuredTracksByRef: "track", + contributorContribsByRef: '_contrib', + featuredTracksByRef: 'track', }, ], [ - "flashActData", + 'flashActData', { - flashesByRef: "flash", + flashesByRef: 'flash', }, ], ]; @@ -1364,35 +1363,35 @@ export function filterReferenceErrors(wikiData) { const aggregate = openAggregate({ message: `Errors validating between-thing references in data`, }); - const boundFind = bindFind(wikiData, { mode: "error" }); + const boundFind = bindFind(wikiData, {mode: 'error'}); for (const [thingDataProp, propSpec] of referenceSpec) { const thingData = getNestedProp(wikiData, thingDataProp); aggregate.nest( { message: `Reference errors in ${color.green( - "wikiData." + thingDataProp + 'wikiData.' + thingDataProp )}`, }, - ({ nest }) => { + ({nest}) => { const things = Array.isArray(thingData) ? thingData : [thingData]; for (const thing of things) { nest( - { message: `Reference errors in ${inspect(thing)}` }, - ({ filter }) => { + {message: `Reference errors in ${inspect(thing)}`}, + ({filter}) => { for (const [property, findFnKey] of Object.entries(propSpec)) { if (!thing[property]) continue; - if (findFnKey === "_contrib") { + if (findFnKey === '_contrib') { thing[property] = filter( thing[property], - decorateErrorWithIndex(({ who }) => { + decorateErrorWithIndex(({who}) => { const alias = find.artist(who, wikiData.artistAliasData, { - mode: "quiet", + mode: 'quiet', }); if (alias) { const original = find.artist( alias.aliasedArtistRef, wikiData.artistData, - { mode: "quiet" } + {mode: 'quiet'} ); throw new Error( `Reference ${color.red( @@ -1407,7 +1406,7 @@ export function filterReferenceErrors(wikiData) { { message: `Reference errors in contributions ${color.green( property - )} (${color.green("find.artist")})`, + )} (${color.green('find.artist')})`, } ); continue; @@ -1421,7 +1420,7 @@ export function filterReferenceErrors(wikiData) { { message: `Reference errors in property ${color.green( property - )} (${color.green("find." + findFnKey)})`, + )} (${color.green('find.' + findFnKey)})`, } ); } else { @@ -1429,9 +1428,9 @@ export function filterReferenceErrors(wikiData) { { message: `Reference error in property ${color.green( property - )} (${color.green("find." + findFnKey)})`, + )} (${color.green('find.' + findFnKey)})`, }, - ({ call }) => { + ({call}) => { try { call(findFn, value); } catch (error) { @@ -1460,14 +1459,14 @@ export function filterReferenceErrors(wikiData) { // main wiki build process. export async function quickLoadAllFromYAML( dataPath, - { showAggregate: customShowAggregate = showAggregate } = {} + {showAggregate: customShowAggregate = showAggregate} = {} ) { const showAggregate = customShowAggregate; let wikiData; { - const { aggregate, result } = await loadAndProcessDataDocuments({ + const {aggregate, result} = await loadAndProcessDataDocuments({ dataPath, }); |